Skip to content

Commit 6f3874c

Browse files
authored
Merge pull request #993 from pkvach/fix/js-prevent-multiple-submit
js: isso.js: Disable Postbox submit button on click, enable after response
2 parents ca91601 + 27f55e1 commit 6f3874c

File tree

7 files changed

+87
-22
lines changed

7 files changed

+87
-22
lines changed

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Bugfixes & Improvements
4949
- Change logging to include datetime and loglevel (`#1023`_, ix5)
5050
- Make 'text' field in 'comments' table NOT NULL and handling data migration (`#1019`_, pkvach)
5151
- Python 3.12 support (`#1015`_, ix5)
52+
- Disable Postbox submit button on click, enable after response (`#993`_, pkvach)
5253

5354
.. _#951: https://github.com/posativ/isso/pull/951
5455
.. _#967: https://github.com/posativ/isso/pull/967
@@ -62,6 +63,7 @@ Bugfixes & Improvements
6263
.. _#1023: https://github.com/isso-comments/isso/pull/1023
6364
.. _#1019: https://github.com/isso-comments/isso/pull/1019
6465
.. _#1015: https://github.com/isso-comments/isso/pull/1015
66+
.. _#993: https://github.com/isso-comments/isso/pull/993
6567

6668
0.13.1.dev0 (2023-02-05)
6769
------------------------

isso/js/app/isso.js

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,34 +92,50 @@ var Postbox = function(parent) {
9292

9393
// submit form, initialize optional fields with `null` and reset form.
9494
// If replied to a comment, remove form completely.
95-
$("[type=submit]", el).on("click", function() {
95+
$("[type=submit]", el).on("click", function(event) {
9696
edit();
9797
if (! el.validate()) {
9898
return;
9999
}
100100

101+
const submitButton = event.target;
102+
submitButton.disabled = true; // Disable the submit button to prevent double posting
103+
101104
var author = $("[name=author]", el).value || null,
102105
email = $("[name=email]", el).value || null,
103106
website = $("[name=website]", el).value || null;
104107

105-
localStorage.setItem("isso-author", JSON.stringify(author));
106-
localStorage.setItem("isso-email", JSON.stringify(email));
107-
localStorage.setItem("isso-website", JSON.stringify(website));
108-
109-
api.create($("#isso-thread").getAttribute("data-isso-id"), {
110-
author: author, email: email, website: website,
111-
text: $(".isso-textarea", el).value,
112-
parent: parent || null,
113-
title: $("#isso-thread").getAttribute("data-title") || null,
114-
notification: $("[name=notification]", el).checked() ? 1 : 0,
115-
}).then(function(comment) {
116-
$(".isso-textarea", el).value = "";
117-
insert({ comment, scrollIntoView: true, offset: 0 });
118-
119-
if (parent !== null) {
120-
el.onsuccess();
121-
}
122-
});
108+
try {
109+
localStorage.setItem("isso-author", JSON.stringify(author));
110+
localStorage.setItem("isso-email", JSON.stringify(email));
111+
localStorage.setItem("isso-website", JSON.stringify(website));
112+
113+
api.create($("#isso-thread").getAttribute("data-isso-id"), {
114+
author: author, email: email, website: website,
115+
text: $(".isso-textarea", el).value,
116+
parent: parent || null,
117+
title: $("#isso-thread").getAttribute("data-title") || null,
118+
notification: $("[name=notification]", el).checked() ? 1 : 0,
119+
}).then(
120+
function(comment) {
121+
$(".isso-textarea", el).value = "";
122+
insert({ comment, scrollIntoView: true, offset: 0 });
123+
124+
if (parent !== null) {
125+
el.onsuccess();
126+
}
127+
128+
submitButton.disabled = false;
129+
},
130+
function(err) {
131+
console.error(err);
132+
submitButton.disabled = false;
133+
}
134+
);
135+
} catch (err) {
136+
console.error(err);
137+
submitButton.disabled = false;
138+
}
123139
});
124140

125141
return el;

isso/js/tests/integration/highlight-comments.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ test('Linked should be highlighted', async () => {
4040

4141
// Cleanup
4242
// Need to click once to surface "confirm" and then again to confirm
43+
await page.waitForSelector('#isso-1 > .isso-text-wrapper > .isso-comment-footer > .isso-delete');
4344
await expect(page).toClick('#isso-1 > .isso-text-wrapper > .isso-comment-footer > .isso-delete');
4445
await expect(page).toClick('#isso-1 > .isso-text-wrapper > .isso-comment-footer > .isso-delete');
4546
});

isso/js/tests/integration/puppet.test.js

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ test("should fill Postbox with valid data and receive 201 reply", async () => {
148148
await expect(elm.replace(/<time.*?>/, '<time>'))
149149
.toMatchSnapshot();
150150

151+
await expect(page).not.toMatchElement('.isso-post-action > [type=submit]:disabled');
152+
151153
await page.waitForSelector('#isso-1 > .isso-text-wrapper > .isso-comment-footer > .isso-edit');
152154

153155
// Edit comment
@@ -243,7 +245,10 @@ test("should execute GET/PUT/POST/DELETE requests correctly", async () => {
243245

244246
await expect(page).toMatchElement(
245247
'#isso-1 .isso-text',
246-
{ text: 'New comment body' },
248+
{
249+
text: 'New comment body',
250+
timeout: 1000,
251+
},
247252
);
248253

249254
// Delete comment via DELETE
@@ -268,3 +273,42 @@ test("should execute GET/PUT/POST/DELETE requests correctly", async () => {
268273
{ text: 'New comment body' },
269274
);
270275
});
276+
277+
test("Postbox submit button should be disabled on submit click and enabled after response", async () => {
278+
const delay = (duration) => new Promise(resolve => setTimeout(resolve, duration));
279+
280+
await page.setRequestInterception(true);
281+
let createHandler = async (request) => {
282+
if (request.url().startsWith(ISSO_ENDPOINT + '/new')) {
283+
delay(800).then(() => request.abort());
284+
} else {
285+
request.continue();
286+
}
287+
};
288+
await page.on('request', createHandler);
289+
290+
await page.goto(
291+
ISSO_ENDPOINT + '/demo',
292+
{waitUntil: 'load'}
293+
);
294+
295+
// Fill the textarea with the comment
296+
await expect(page).toFill(
297+
'.isso-textarea',
298+
'A comment with *italics* and [a link](http://link.com)'
299+
);
300+
301+
// Click the submit button
302+
const submitButtonSelector = '.isso-post-action > [type=submit]';
303+
await expect(page).toClick(submitButtonSelector);
304+
305+
await page.waitForSelector(submitButtonSelector + ":disabled", {timeout: 1000});
306+
await expect(page).toMatchElement(submitButtonSelector + ":disabled", {timeout: 1000});
307+
308+
await page.waitForSelector(submitButtonSelector + ":enabled", {timeout: 1000});
309+
await expect(page).toMatchElement(submitButtonSelector + ":enabled", {timeout: 1000});
310+
311+
// Disable request interception and remove the request handler
312+
await page.setRequestInterception(false);
313+
await page.off('request', createHandler);
314+
});
-96 Bytes
Loading
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
78ea677962d3f80607995d9b88d81df2b7787832023cb7f1d64811829d0fc10a
1+
a10040e07c4285acea4a36d41ee2ff9877b70fd399d908e6eaf7a4dfad100333

isso/js/tests/screenshots/screenshots.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,14 @@ test('Screenshot with inserted comment', async () => {
6666
page.waitForResponse(async (response) =>
6767
// response.ok means code of 200-300
6868
response.url().includes('/new') && response.ok(),
69-
{ timout: 500 }
69+
{ timeout: 500 }
7070
),
7171
// Then, click submit button
7272
expect(page).toClick('.isso-post-action > input[type=submit]'),
7373
]);
7474

75+
await page.waitForSelector('.isso-post-action > [type=submit]:enabled');
76+
await page.waitForSelector('#isso-1');
7577
const rendered_comment = await page.$('#isso-1');
7678
await rendered_comment.screenshot({
7779
path: SCREENSHOTS_PATH + '/comment.png'

0 commit comments

Comments
 (0)