Skip to content

Fix Problem Switcher Not Update#929

Closed
def-WA2025 wants to merge 1 commit intoXMOJ-Script-dev:masterfrom
def-WA2025:fix-problem-switcher-not-update
Closed

Fix Problem Switcher Not Update#929
def-WA2025 wants to merge 1 commit intoXMOJ-Script-dev:masterfrom
def-WA2025:fix-problem-switcher-not-update

Conversation

@def-WA2025
Copy link
Member

@def-WA2025 def-WA2025 commented Mar 10, 2026

What does this PR aim to accomplish?

Fix *EX problems not display.

How does this PR accomplish the above?

Update variable contestProblemList when the page reload.


By submitting this pull request, I confirm the following:

  1. I have read and understood the contributor's guide, as well as this entire template. I understand which branch to base my commits and Pull Requests against.
  2. I have commented on my proposed changes within the code and I have tested my changes.
  3. I am willing to help maintain this change if there are issues with it later.
  4. It is compatible with the GNU General Public License v3.0
  5. I have squashed any insignificant commits. (git rebase)
  6. I have checked that another pull request for this purpose does not exist.
  7. I have considered and confirmed that this submission will be valuable to others.
  8. I accept that this submission may not be used, and the pull request can be closed at the will of the maintainer.
  9. I give this submission freely and claim no ownership to its content.

  • I have read the above and my PR is ready for review. Check this box to confirm

Summary by Sourcery

Bug Fixes:

  • Refresh and persist the contest problem list on each page load so newly available problems are correctly displayed in the problem switcher.

Summary by cubic

Refreshes the contest problem list on each page load so the Problem Switcher always shows the latest problems, including EX problems. Replaces stale cached data with the newest list.

  • Bug Fixes
    • Always fetch and parse the contest problemset, then overwrite UserScript-Contest-<cid>-ProblemList in localStorage.
    • Update ContestProblemList on load to prevent stale entries and ensure EX problems appear.

Written for commit 5b1123b. Summary will update on new commits.

@sourcery-ai
Copy link

sourcery-ai bot commented Mar 10, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Ensure the contest problem list used by the problem switcher is refreshed from the contest page on each load instead of relying on possibly stale localStorage data, so EX problems and other updates are shown correctly.

Sequence diagram for refreshed contest problem list on page load

sequenceDiagram
    actor User
    participant Browser
    participant XMOJScript
    participant LocalStorage
    participant XMOJServer

    User->>Browser: Open contest problem page
    Browser->>XMOJScript: Execute main

    XMOJScript->>LocalStorage: getItem(UserScript-Contest-cid-ProblemList)
    LocalStorage-->>XMOJScript: ContestProblemList (ignored)

    XMOJScript->>XMOJScript: Set ContestProblemList to null

    XMOJScript->>XMOJServer: GET contest.php?cid=cid
    XMOJServer-->>XMOJScript: HTML contest page

    XMOJScript->>XMOJScript: Parse HTML and extract problem rows
    XMOJScript->>XMOJScript: Build problemList array

    XMOJScript->>LocalStorage: setItem(UserScript-Contest-cid-ProblemList, JSON.stringify(problemList))
    XMOJScript->>XMOJScript: ContestProblemList = JSON.stringify(problemList)

    XMOJScript->>Browser: Render problemSwitcher using updated ContestProblemList
Loading

Flow diagram for updated ContestProblemList refresh logic

flowchart TD
    A[Page load in contest problem view] --> B[Execute main]
    B --> C[Read ContestProblemList from LocalStorage]
    C --> D[Set ContestProblemList to null]
    D --> E[Fetch contest.php with cid]
    E --> F{Status 200 and no private or not started message}
    F -- Yes --> G[Parse HTML with DOMParser]
    G --> H[Select #problemset tbody rows]
    H --> I[Iterate rows and build problemList array]
    I --> J[Save problemList to LocalStorage as JSON]
    J --> K[Set ContestProblemList to JSON string]
    K --> L[Create and display problemSwitcher with fresh data]
    F -- No --> M[Skip update, do not build problemList]
Loading

File-Level Changes

Change Details Files
Always refetch and rebuild the contest problem list from the contest page on load, then persist it to localStorage for the problem switcher.
  • Remove the conditional that only fetched the contest page when no cached problem list existed in localStorage.
  • Unconditionally fetch the contest page HTML for the current cid and parse the problem table rows into a problemList array.
  • Overwrite the localStorage entry for the contest problem list with the freshly built problemList JSON on each load.
  • Assign ContestProblemList from the newly built problemList JSON to ensure the problem switcher uses the updated data.
XMOJ.user.js

Possibly linked issues

  • #[Bug] Problem Switcher Not Update: They match: the PR refetches and updates contestProblemList to fix the problem switcher not updating for *EX problems.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@def-WA2025 def-WA2025 mentioned this pull request Mar 10, 2026
2 tasks
Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • You’re now discarding any cached ContestProblemList and always re-fetching; consider either preserving the original cache check with an explicit cache invalidation strategy or adding a clear reason in code comments for bypassing localStorage.
  • The line let ContestProblemList = localStorage.getItem(...); immediately followed by ContestProblemList = null; is redundant and confusing—either remove the getItem call or restore logic that uses its value.
  • The new fetch path assumes a successful response; consider adding basic error handling (e.g., non-200 status or network failure) to avoid the switcher silently breaking when the contest page cannot be retrieved.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- You’re now discarding any cached `ContestProblemList` and always re-fetching; consider either preserving the original cache check with an explicit cache invalidation strategy or adding a clear reason in code comments for bypassing localStorage.
- The line `let ContestProblemList = localStorage.getItem(...);` immediately followed by `ContestProblemList = null;` is redundant and confusing—either remove the `getItem` call or restore logic that uses its value.
- The new fetch path assumes a successful response; consider adding basic error handling (e.g., non-200 status or network failure) to avoid the switcher silently breaking when the contest page cannot be retrieved.

## Individual Comments

### Comment 1
<location path="XMOJ.user.js" line_range="2272-2274" />
<code_context>
-                                }
-                                localStorage.setItem("UserScript-Contest-" + SearchParams.get("cid") + "-ProblemList", JSON.stringify(problemList));
-                                ContestProblemList = JSON.stringify(problemList);
+                        ContestProblemList = null;
+                        const contestReq = await fetch("https://www.xmoj.tech/contest.php?cid=" + SearchParams.get("cid"));
+                        const res = await contestReq.text();
</code_context>
<issue_to_address>
**suggestion:** Avoid resetting `ContestProblemList` to `null` if you intend to keep using the cached value from `localStorage`.

This assignment discards the value read from `localStorage`, and the cache is never used afterward. If you always want fresh data, remove the initial `localStorage.getItem(...)` call. If you want to preserve caching, drop this assignment so the stored value can still be used.

```suggestion
                        const contestReq = await fetch("https://www.xmoj.tech/contest.php?cid=" + SearchParams.get("cid"));
                        const res = await contestReq.text();
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +2272 to +2274
ContestProblemList = null;
const contestReq = await fetch("https://www.xmoj.tech/contest.php?cid=" + SearchParams.get("cid"));
const res = await contestReq.text();
Copy link

Choose a reason for hiding this comment

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

suggestion: Avoid resetting ContestProblemList to null if you intend to keep using the cached value from localStorage.

This assignment discards the value read from localStorage, and the cache is never used afterward. If you always want fresh data, remove the initial localStorage.getItem(...) call. If you want to preserve caching, drop this assignment so the stored value can still be used.

Suggested change
ContestProblemList = null;
const contestReq = await fetch("https://www.xmoj.tech/contest.php?cid=" + SearchParams.get("cid"));
const res = await contestReq.text();
const contestReq = await fetch("https://www.xmoj.tech/contest.php?cid=" + SearchParams.get("cid"));
const res = await contestReq.text();

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="XMOJ.user.js">

<violation number="1" location="XMOJ.user.js:2272">
P1: Do not clear `ContestProblemList` before refresh; it removes the cache fallback and can crash when refresh fails.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment on lines +2272 to 2288
ContestProblemList = null;
const contestReq = await fetch("https://www.xmoj.tech/contest.php?cid=" + SearchParams.get("cid"));
const res = await contestReq.text();
if (contestReq.status === 200 && res.indexOf("比赛尚未开始或私有,不能查看题目。") === -1) {
const parser = new DOMParser();
const dom = parser.parseFromString(res, "text/html");
const rows = (dom.querySelector("#problemset > tbody")).rows;
let problemList = [];
for (let i = 0; i < rows.length; i++) {
problemList.push({
"title": rows[i].children[2].innerText,
"url": rows[i].children[2].children[0].href
});
}
localStorage.setItem("UserScript-Contest-" + SearchParams.get("cid") + "-ProblemList", JSON.stringify(problemList));
ContestProblemList = JSON.stringify(problemList);
}
Copy link

@cubic-dev-ai cubic-dev-ai bot Mar 10, 2026

Choose a reason for hiding this comment

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

P1: Do not clear ContestProblemList before refresh; it removes the cache fallback and can crash when refresh fails.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At XMOJ.user.js, line 2272:

<comment>Do not clear `ContestProblemList` before refresh; it removes the cache fallback and can crash when refresh fails.</comment>

<file context>
@@ -2269,23 +2269,22 @@ async function main() {
-                                }
-                                localStorage.setItem("UserScript-Contest-" + SearchParams.get("cid") + "-ProblemList", JSON.stringify(problemList));
-                                ContestProblemList = JSON.stringify(problemList);
+                        ContestProblemList = null;
+                        const contestReq = await fetch("https://www.xmoj.tech/contest.php?cid=" + SearchParams.get("cid"));
+                        const res = await contestReq.text();
</file context>
Suggested change
ContestProblemList = null;
const contestReq = await fetch("https://www.xmoj.tech/contest.php?cid=" + SearchParams.get("cid"));
const res = await contestReq.text();
if (contestReq.status === 200 && res.indexOf("比赛尚未开始或私有,不能查看题目。") === -1) {
const parser = new DOMParser();
const dom = parser.parseFromString(res, "text/html");
const rows = (dom.querySelector("#problemset > tbody")).rows;
let problemList = [];
for (let i = 0; i < rows.length; i++) {
problemList.push({
"title": rows[i].children[2].innerText,
"url": rows[i].children[2].children[0].href
});
}
localStorage.setItem("UserScript-Contest-" + SearchParams.get("cid") + "-ProblemList", JSON.stringify(problemList));
ContestProblemList = JSON.stringify(problemList);
}
const cachedContestProblemList = ContestProblemList;
const contestReq = await fetch("https://www.xmoj.tech/contest.php?cid=" + SearchParams.get("cid"));
const res = await contestReq.text();
if (contestReq.status === 200 && res.indexOf("比赛尚未开始或私有,不能查看题目。") === -1) {
const parser = new DOMParser();
const dom = parser.parseFromString(res, "text/html");
const rows = (dom.querySelector("#problemset > tbody")).rows;
let problemList = [];
for (let i = 0; i < rows.length; i++) {
problemList.push({
"title": rows[i].children[2].innerText,
"url": rows[i].children[2].children[0].href
});
}
localStorage.setItem("UserScript-Contest-" + SearchParams.get("cid") + "-ProblemList", JSON.stringify(problemList));
ContestProblemList = JSON.stringify(problemList);
} else {
ContestProblemList = cachedContestProblemList;
}
Fix with Cubic

@boomzero
Copy link
Member

boomzero commented Mar 10, 2026

ty for PR though base to extern-contrib pls

@def-WA2025
Copy link
Member Author

Hmm so how can I submit to extern-contrib (

@def-WA2025
Copy link
Member Author

Because this is my first contribution to the project

@boomzero
Copy link
Member

Oh sorry, if you have wrote access, you should make changes in this repo, not your fork, and base your PR to dev

@boomzero
Copy link
Member

I will close this PR for now, please resubmit it

@boomzero
Copy link
Member

Also it’s terribly late, so tomorrow pls

@def-WA2025
Copy link
Member Author

tks

@def-WA2025 def-WA2025 closed this Mar 10, 2026
@boomzero
Copy link
Member

直接清Cache 不好吧,加一个刷新按钮?

@boomzero
Copy link
Member

正常提交更改方式是在这个仓库里新建一个分支,把自己的更改提交到那个分支,在PR到dev

注意新分支应基于最新dev

@def-WA2025
Copy link
Member Author

正常提交更改方式是在这个仓库里新建一个分支,把自己的更改提交到那个分支,在PR到dev

注意新分支应基于最新dev

OK

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants