From 69320d5df4deb80bc15cb1e90706e00b06856d40 Mon Sep 17 00:00:00 2001 From: Eggbertx Date: Wed, 8 Feb 2023 12:40:36 -0800 Subject: [PATCH] Add lock/unlock option to post dropdown --- LICENSE | 2 +- frontend/js/api/management.js | 56 ++++++++++++++++++++++++++++++++ frontend/js/dom/postdropdown.js | 9 +++++ frontend/js/management/manage.js | 13 ++++++-- frontend/js/vars.js | 3 +- frontend/package.json | 2 +- html/error/404.html | 2 +- html/error/500.html | 2 +- html/error/502.html | 2 +- pkg/gcsql/threads.go | 3 ++ templates/post.html | 2 +- version | 2 +- 12 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 frontend/js/api/management.js diff --git a/LICENSE b/LICENSE index d72d753d..d4316d3e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2022, Gochan development group +Copyright (c) 2013-2023, Gochan development group All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/frontend/js/api/management.js b/frontend/js/api/management.js new file mode 100644 index 00000000..64b78a6b --- /dev/null +++ b/frontend/js/api/management.js @@ -0,0 +1,56 @@ +import $ from "jquery"; + +/** + * Returns true if the post has a lock icon without making a GET request + * @param {JQuery} $elem the jQuery element of the post + */ +export function isThreadLocked($elem) { + return $elem.find("span.status-icons img.locked-icon").length == 1; +} + +/** + * Sends a POST request to the server to lock or unlock a thread + * @param {string} board The board dir of the thread to be (un)locked, e.g. "test2" + * @param {number} op The post number of the top post in the thread + * @param {boolean} lock If true, the thread will be locked, otherwise it will be unlocked + */ +export async function updateThreadLock(board, op, lock) { + const data = { + board: board, + thread: op, + json: 1 + }; + if(lock) { + data.lock = "Not locked"; + } else { + data.unlock = "Locked"; + } + $.post({ + url: webroot + "manage/threadattrs", + data: data + }).then((_data) => { + alert("Thread " + (lock?"locked":"unlocked") + " successfully"); + const $lockOpt = $(`select#op${op} option`) + .filter((_i, el) => el.textContent == "Lock thread" || el.textContent == "Unlock thread"); + if(lock) { + $(`div#op${op} span.status-icons`).append( + $("").attr({ + class: "locked-icon", + src: webroot + "static/lock.png", + alt: "Thread locked", + title: "Thread locked" + }) + ); + $lockOpt.text("Unlock thread"); + } else { + $(`div#op${op} img.locked-icon`).remove(); + $lockOpt.text("Lock thread"); + } + }).catch((data, status, xhr) => { + if(data.responseJSON !== undefined && data.responseJSON.message !== undefined) { + alert(`Error updating thread /${board}/${op} lock status: ${data.responseJSON.message}`); + } else { + alert("Unable to send request: " + xhr); + } + }); +} \ No newline at end of file diff --git a/frontend/js/dom/postdropdown.js b/frontend/js/dom/postdropdown.js index 09474a9d..9b1d2e0a 100644 --- a/frontend/js/dom/postdropdown.js +++ b/frontend/js/dom/postdropdown.js @@ -6,6 +6,7 @@ import { currentBoard } from "../postinfo"; import { getCookie } from "../cookies"; import { alertLightbox, promptLightbox } from "./lightbox"; import { banFile, getPostInfo } from "../management/manage"; +import { updateThreadLock } from "../api/management"; const idRe = /^((reply)|(op))(\d+)/; @@ -154,6 +155,14 @@ function handleActions(action, postIDStr) { deletePost(postID, board); break; // manage stuff + case "Lock thread": + console.log(`Locking /${board}/${postID}`); + updateThreadLock(board, postID, true); + break; + case "Unlock thread": + console.log(`Unlocking /${board}/${postID}`); + updateThreadLock(board, postID, false); + break; case "Posts from this IP": getPostInfo(postID).then(info => { window.open(`${webroot}manage/ipsearch?limit=100&ip=${info.ip}`); diff --git a/frontend/js/management/manage.js b/frontend/js/management/manage.js index 6873139f..4dac9346 100755 --- a/frontend/js/management/manage.js +++ b/frontend/js/management/manage.js @@ -9,6 +9,7 @@ import { alertLightbox } from "../dom/lightbox"; import { $topbar, TopBarButton } from '../dom/topbar'; import "./sections"; import "./filebans"; +import { isThreadLocked } from '../api/management'; /** * @type {StaffInfo} @@ -49,14 +50,22 @@ function dropdownHasItem(dropdown, item) { function setupManagementEvents() { $("select.post-actions").each((_i, el) => { - let $el = $(el); + const $el = $(el); + const $post = $(el.parentElement); + const isLocked = isThreadLocked($post); if(!dropdownHasItem(el, "Staff Actions")) { $el.append(``); } + if($post.hasClass("op-post")) { + if(isLocked) { + $el.append(""); + } else { + $el.append(""); + } + } if(!dropdownHasItem(el, "Posts from this IP")) { $el.append(""); } - let $post = $(el.parentElement); let filenameOrig = $post.find("div.file-info a.file-orig").text(); if(filenameOrig != "" && !dropdownHasItem(el, "Ban filename")) { $el.append( diff --git a/frontend/js/vars.js b/frontend/js/vars.js index bcb9dfaf..a7f7c103 100755 --- a/frontend/js/vars.js +++ b/frontend/js/vars.js @@ -4,7 +4,8 @@ export default (window.$ = window.jQuery = jquery); // overwrite jQuery's deferred exception hook, because otherwise the sourcemap // is useless if AJAX is involved jquery.Deferred.exceptionHook = function(err) { - throw err; + // throw err; + return err; }; diff --git a/frontend/package.json b/frontend/package.json index 016dc3f4..67e1ee7f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "gochan.js", - "version": "3.4.1", + "version": "3.5.0", "description": "", "type": "module", "source": "js/gochan.js", diff --git a/html/error/404.html b/html/error/404.html index 3dff4546..af247288 100755 --- a/html/error/404.html +++ b/html/error/404.html @@ -7,6 +7,6 @@

404: File not found

The requested file could not be found on this server.

-
Site powered by Gochan v3.4.1 +
Site powered by Gochan v3.5.0 \ No newline at end of file diff --git a/html/error/500.html b/html/error/500.html index d24b888a..e49ebfa8 100755 --- a/html/error/500.html +++ b/html/error/500.html @@ -7,6 +7,6 @@

Error 500: Internal Server error

The server encountered an error while trying to serve the page, and we apologize for the inconvenience. The system administrator will try to fix things as soon they get around to it, whenever that is. Hopefully soon.

-
Site powered by Gochan v3.4.1 +
Site powered by Gochan v3.5.0 \ No newline at end of file diff --git a/html/error/502.html b/html/error/502.html index 653c02e7..80cea937 100644 --- a/html/error/502.html +++ b/html/error/502.html @@ -7,6 +7,6 @@

Error 502: Bad gateway

The server encountered an error while trying to serve the page, and we apologize for the inconvenience. The system administrator will try to fix things as soon they get around to it, whenever that is. Hopefully soon.

-
Site powered by Gochan v3.4.1 +
Site powered by Gochan v3.5.0 \ No newline at end of file diff --git a/pkg/gcsql/threads.go b/pkg/gcsql/threads.go index ebb3a53a..f86f8388 100644 --- a/pkg/gcsql/threads.go +++ b/pkg/gcsql/threads.go @@ -55,6 +55,9 @@ func GetPostThread(opID int) (*Thread, error) { &thread.ID, &thread.BoardID, &thread.Locked, &thread.Stickied, &thread.Anchored, &thread.Cyclical, &thread.LastBump, &thread.DeletedAt, &thread.IsDeleted, )) + if errors.Is(err, sql.ErrNoRows) { + err = ErrThreadDoesNotExist + } return thread, err } diff --git a/templates/post.html b/templates/post.html index 5f9923b2..8771332b 100644 --- a/templates/post.html +++ b/templates/post.html @@ -16,7 +16,7 @@ {{- if ne .post.Tripcode ""}}!{{.post.Tripcode}}{{end}} {{formatTimestamp .post.Timestamp -}} No. {{.post.ID}} - {{- if $.thread.Locked -}}Thread locked{{end -}} + {{- if $.thread.Locked -}}Thread locked{{end -}} {{if $.is_board_page -}} [View] diff --git a/version b/version index 8cf6caf5..e5b82034 100644 --- a/version +++ b/version @@ -1 +1 @@ -3.4.1 \ No newline at end of file +3.5.0 \ No newline at end of file