Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Update.json
Original file line number Diff line number Diff line change
Expand Up @@ -3288,6 +3288,17 @@
}
],
"Notes": "No release notes were provided for this release."
},
"2.7.3": {
"UpdateDate": 1770436447016,
"Prerelease": true,
"UpdateContents": [
{
"PR": 901,
"Description": "Add isSubmitting flags to prevent multiple submissions"
}
],
"Notes": "No release notes were provided for this release."
}
}
}
29 changes: 27 additions & 2 deletions XMOJ.user.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// ==UserScript==
// @name XMOJ
// @version 2.7.2
// @version 2.7.3
// @description XMOJ增强脚本
// @author @XMOJ-Script-dev, @langningchen and the community
// @namespace https://github/langningchen
Expand Down Expand Up @@ -2667,11 +2667,17 @@ async function main() {
ErrorMessage.innerText = "提交失败!请关闭脚本后重试!";
Submit.disabled = false;
Submit.value = "提交";
isSubmitting = false;
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isSubmitting is only reset in the non-redirect branch of the PassCheck fetch(...).then(...) chain. If the fetch rejects (network error/timeout) or an exception occurs before reaching this branch, Submit can remain disabled and isSubmitting stays true, preventing retry. Add explicit error handling (e.g., .catch(...) or try/catch/finally) in the PassCheck submission flow to restore UI state and clear the flag.

Suggested change
}
}
}).catch((error) => {
if (UtilityEnabled("DebugMode")) {
console.error("Submission request failed:", error);
}
ErrorElement.style.display = "block";
ErrorMessage.style.color = "red";
ErrorMessage.innerText = "提交失败!请检查网络后重试!";
}).finally(() => {
Submit.disabled = false;
Submit.value = "提交";
isSubmitting = false;

Copilot uses AI. Check for mistakes.
})
});

let isSubmitting = false;
Submit.addEventListener("click", async () => {
Comment on lines 2668 to 2676
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isSubmitting is assigned in the PassCheck handler before its let isSubmitting = false; declaration later in the same scope. While it will usually work due to event timing, it’s confusing and easy to break (e.g., if the handler ever runs earlier) and can trigger TDZ issues if refactored. Declare isSubmitting before adding the PassCheck listener (or otherwise ensure the variable is declared above all uses).

Copilot uses AI. Check for mistakes.
if (isSubmitting) {
return;
}
isSubmitting = true;
PassCheck.style.display = "none";
ErrorElement.style.display = "none";
document.querySelector("#Submit").disabled = true;
Expand Down Expand Up @@ -2721,6 +2727,7 @@ async function main() {
freopenCodeField.setSize("100%", "auto");
document.querySelector("#Submit").disabled = false;
document.querySelector("#Submit").value = "提交";
isSubmitting = false;
return false;
} else if (RegExp("//.*freopen").test(Source)) {
PassCheck.style.display = "";
Expand All @@ -2729,6 +2736,7 @@ async function main() {
ErrorMessage.innerText = "请不要注释freopen语句";
document.querySelector("#Submit").disabled = false;
document.querySelector("#Submit").value = "提交";
isSubmitting = false;
return false;
}
}
Expand All @@ -2739,6 +2747,7 @@ async function main() {
ErrorMessage.innerText = "源代码为空";
document.querySelector("#Submit").disabled = false;
document.querySelector("#Submit").value = "提交";
isSubmitting = false;
return false;
}
if (UtilityEnabled("CompileError")) {
Expand All @@ -2761,6 +2770,7 @@ async function main() {
ErrorMessage.innerText = "编译错误:\n" + Response.stderr.trim();
document.querySelector("#Submit").disabled = false;
document.querySelector("#Submit").value = "提交";
isSubmitting = false;
return false;
} else {
PassCheck.click();
Comment on lines 2770 to 2776
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the CompileError check flow, JSON.parse(ResponseData.responseText) is not guarded. If cppinsights returns a non-JSON error page or an empty response, this throws and the handler exits without re-enabling the Submit button or clearing isSubmitting, effectively locking submissions until reload. Wrap the parse in try/catch and ensure UI + isSubmitting are reset on parse/network errors.

Copilot uses AI. Check for mistakes.
Expand Down Expand Up @@ -4378,22 +4388,30 @@ int main()
}
}
});
let isSubmittingPost = false;
SubmitElement.addEventListener("click", async () => {
if (isSubmittingPost) {
return;
}
isSubmittingPost = true;
ErrorElement.style.display = "none";
Comment on lines +4391 to 4397
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isSubmittingPost is set to true before calling RequestAPI(...), but it’s only reset inside the success callback. Since RequestAPI currently only registers onload (no onerror/ontimeout) and JSON parse errors can also prevent the callback from running, a failed request can leave isSubmittingPost stuck true (and the UI disabled) until a page reload. Ensure RequestAPI invokes a failure path (onerror/ontimeout/parse failure) and/or reset the flag in a guaranteed finally-style path.

Copilot uses AI. Check for mistakes.
let Title = TitleElement.value;
let Content = ContentElement.value;
let ProblemID = parseInt(SearchParams.get("pid"));
if (Title === "") {
TitleElement.classList.add("is-invalid");
isSubmittingPost = false;
return;
}
if (Content === "") {
ContentElement.classList.add("is-invalid");
isSubmittingPost = false;
return;
}
if (document.querySelector("#Board input:checked") === null) {
ErrorElement.innerText = "请选择要发布的板块";
ErrorElement.style.display = "block";
isSubmittingPost = false;
return;
}
SubmitElement.disabled = true;
Expand All @@ -4407,6 +4425,7 @@ int main()
}, (ResponseData) => {
SubmitElement.disabled = false;
SubmitElement.children[0].style.display = "none";
isSubmittingPost = false;
if (ResponseData.Success == true) {
location.href = "https://www.xmoj.tech/discuss3/thread.php?tid=" + ResponseData.Data.PostID;
} else {
Expand Down Expand Up @@ -4897,7 +4916,12 @@ int main()
}
});
});
let isSubmittingReply = false;
SubmitElement.addEventListener("click", async () => {
if (isSubmittingReply) {
return;
}
isSubmittingReply = true;
ErrorElement.style.display = "none";
SubmitElement.disabled = true;
SubmitElement.children[0].style.display = "inline-block";
Comment on lines +4919 to 4927
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isSubmittingReply is set to true before RequestAPI("NewReply", ...), but it’s only cleared in the callback. If the request errors/ times out, or if RequestAPI fails to parse/dispatch the callback, the flag can remain true (and combined with disabled can prevent retries without a reload). Add a guaranteed reset path by handling onerror/ontimeout in RequestAPI and/or clearing the flag in an always-run cleanup path.

Copilot uses AI. Check for mistakes.
Expand All @@ -4908,6 +4932,7 @@ int main()
}, async (ResponseData) => {
SubmitElement.disabled = false;
SubmitElement.children[0].style.display = "none";
isSubmittingReply = false;
if (ResponseData.Success == true) {
RefreshReply();
ContentElement.value = "";
Expand Down Expand Up @@ -4944,4 +4969,4 @@ int main()

main().then(r => {
console.log("XMOJ-Script loaded successfully!");
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "xmoj-script",
"version": "2.7.2",
"version": "2.7.3",
"description": "an improvement script for xmoj.tech",
"main": "AddonScript.js",
"scripts": {
Expand Down