From 37222dd70f38987aa7bf595d55e53c1293efec5d Mon Sep 17 00:00:00 2001 From: 0-o0 <83802075+0-o0@users.noreply.github.com> Date: Thu, 15 May 2025 17:43:08 +0800 Subject: [PATCH 1/3] FIX: Preventing Issues with Rapid Delete/Recover Button Clicks --- app/assets/javascripts/discourse/app/models/post.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/app/models/post.js b/app/assets/javascripts/discourse/app/models/post.js index cad4e3c88f9de..a504592ad4d5a 100644 --- a/app/assets/javascripts/discourse/app/models/post.js +++ b/app/assets/javascripts/discourse/app/models/post.js @@ -489,6 +489,7 @@ export default class Post extends RestModel { deleted_by: null, user_deleted: false, can_delete: false, + isRecovering: true, }); return ajax(`/posts/${this.id}/recover`, { @@ -501,11 +502,12 @@ export default class Post extends RestModel { user_deleted: false, can_delete: true, version: data.version, + isRecovering: false, }); }) .catch((error) => { popupAjaxError(error); - this.setProperties(initProperties); + this.setProperties({...initProperties, isRecovering: false}); }); } @@ -568,10 +570,13 @@ export default class Post extends RestModel { } destroy(deletedBy, opts) { + this.set("isDeleting", true); return this.setDeletedState(deletedBy).then(() => { return ajax("/posts/" + this.id, { data: { context: window.location.pathname, ...opts }, type: "DELETE", + }).finally(() => { + this.set("isDeleting", false); }); }); } From be72e2f26a4f2d5d17809f254ace0368b8573596 Mon Sep 17 00:00:00 2001 From: 0-o0 <83802075+0-o0@users.noreply.github.com> Date: Thu, 15 May 2025 17:45:38 +0800 Subject: [PATCH 2/3] FIX: Preventing Issues with Rapid Delete/Recover Button Clicks --- .../discourse/app/components/post/menu/buttons/delete.gjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/app/components/post/menu/buttons/delete.gjs b/app/assets/javascripts/discourse/app/components/post/menu/buttons/delete.gjs index 5f434aeff3c42..b93351d215482 100644 --- a/app/assets/javascripts/discourse/app/components/post/menu/buttons/delete.gjs +++ b/app/assets/javascripts/discourse/app/components/post/menu/buttons/delete.gjs @@ -112,7 +112,7 @@ export default class PostMenuDeleteButton extends Component { } get disabled() { - return !this.activeAction; + return !this.activeAction || this.args.post.isDeleting || this.args.post.isRecovering; } get #activeMode() { From 852ccbd7b2475f4749b4e7cb72c5092e1f27e762 Mon Sep 17 00:00:00 2001 From: 0-o0 <83802075+0-o0@users.noreply.github.com> Date: Wed, 21 May 2025 03:05:54 +0800 Subject: [PATCH 3/3] FIX: issuses with fast consecutive clicks on delete/restore buttons --- .../javascripts/discourse/app/models/post.js | 66 ++++++++++++------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/app/assets/javascripts/discourse/app/models/post.js b/app/assets/javascripts/discourse/app/models/post.js index a504592ad4d5a..ede0ba772d101 100644 --- a/app/assets/javascripts/discourse/app/models/post.js +++ b/app/assets/javascripts/discourse/app/models/post.js @@ -211,6 +211,8 @@ export default class Post extends RestModel { @trackedPostProperty user_custom_fields; @trackedPostProperty has_post_localizations; @trackedPostProperty post_localizations; + @trackedPostProperty isDeleting = false; + @trackedPostProperty isRecovering = false; @alias("can_edit") canEdit; // for compatibility with existing code @equal("trust_level", 0) new_user; @@ -476,7 +478,13 @@ export default class Post extends RestModel { } // Recover a deleted post - recover() { + async recover() { + if (this.isRecovering) { + return; + } + + this.set("isRecovering", true); + const initProperties = this.getProperties( "deleted_at", "deleted_by", @@ -489,26 +497,29 @@ export default class Post extends RestModel { deleted_by: null, user_deleted: false, can_delete: false, - isRecovering: true, }); - return ajax(`/posts/${this.id}/recover`, { - type: "PUT", - }) - .then((data) => { - this.setProperties({ - cooked: data.cooked, - raw: data.raw, - user_deleted: false, - can_delete: true, - version: data.version, - isRecovering: false, - }); - }) - .catch((error) => { - popupAjaxError(error); - this.setProperties({...initProperties, isRecovering: false}); + try { + const data = await ajax(`/posts/${this.id}/recover`, { + type: "PUT", + }); + + this.setProperties({ + cooked: data.cooked, + raw: data.raw, + user_deleted: false, + can_delete: true, + version: data.version, }); + + return data; + } catch (error) { + popupAjaxError(error); + this.setProperties(initProperties); + throw error; + } finally { + this.set("isRecovering", false); + } } /** @@ -569,18 +580,23 @@ export default class Post extends RestModel { } } - destroy(deletedBy, opts) { + async destroy(deletedBy, opts) { + if (this.isDeleting) { + return; + } + this.set("isDeleting", true); - return this.setDeletedState(deletedBy).then(() => { - return ajax("/posts/" + this.id, { + + try { + await this.setDeletedState(deletedBy); + return await ajax("/posts/" + this.id, { data: { context: window.location.pathname, ...opts }, type: "DELETE", - }).finally(() => { - this.set("isDeleting", false); }); - }); + } finally { + this.set("isDeleting", false); + } } - /** * Updates a post from another's attributes. This will normally happen when a post is loading but * is already found in an identity map.