feat(website): Sync state changes across tabs#4174
feat(website): Sync state changes across tabs#4174ravern merged 10 commits intonusmodifications:masterfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@TheMythologist is attempting to deploy a commit to the modsbot's projects Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Thanks for looking into this! The change looks good to me with a minor nit.
Do let one of the other active maintainers @ravern, @jloh02, or @leslieyip02 review it too before merging.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #4174 +/- ##
==========================================
+ Coverage 54.52% 56.81% +2.28%
==========================================
Files 274 298 +24
Lines 6076 6942 +866
Branches 1455 1676 +221
==========================================
+ Hits 3313 3944 +631
- Misses 2763 2998 +235 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Hi @zwliew I finally got to working on this again, could you help me take a look at the changes and review this PR once more? Thanks! |
jloh02
left a comment
There was a problem hiding this comment.
hi i noticed a bug. steps to reproduce
- Open 2 tabs on https://nusmods-website-mmnouxt0t-modsbots-projects.vercel.app/timetable/sem-1
- Add a course on one tab
- Other tab has the screenshot as below
| } | ||
|
|
||
| // `FETCH_` request actions should not be synced to other tabs | ||
| if (action.type.toString().startsWith('FETCH_')) { |
There was a problem hiding this comment.
hook.js:608 TypeError: Cannot read properties of undefined (reading 'find')
at u (modules.ts:30:30)
at d (modules.ts:35:16)
at timetables.ts:168:21
at mapValues.js:38:34
at _createBaseFor.js:17:11
at e.exports (_baseForOwn.js:13:20)
at e.exports (mapValues.js:37:3)
at ue (timetables.ts:167:10)
at timetables.ts:127:14
The problem is that in getModuleSemesterData() of modules.ts, the semesterData field of module is undefined for a module that was synced over from another tab.
Perhaps a fix for this is to whitelist the FETCH_MODULE_REQUEST and FETCH_MODULE_SUCCESS actions, which are there to add module info (like semesterData) for newly added modules.
As an aside, I would be more comfortable with switching over to an action whitelist rather than a blacklist.
|
@jloh02 @zwliew Finally got to fixing the issue. The reason for the weird filtering by requests starting with "FETCH_" was ping-pong responses between tabs whenever any fetch request was started, resulting in actions with type |
|
Thanks for the recent fix! Just as a first pass, I'm going to enable an AI code review tool. But we (humans) will still be the ones reviewing and approving later on. @greptile |
Greptile OverviewConfidence Score: 4/5
|
| Filename | Overview |
|---|---|
| website/src/middlewares/state-sync-middleware.ts | Added new middleware to sync Redux state across browser tabs using redux-state-sync, with proper filtering for redux-persist actions and non-serializable functions |
| website/src/middlewares/requests-middleware.ts | Added guard to prevent re-processing API request actions that already have requestStatus, preventing ping-pong sync loops between tabs |
| website/src/bootstrapping/configure-store.ts | Integrated the new state sync middleware into the Redux store configuration |
| website/package.json | Added dependencies for redux-state-sync, @types/redux-state-sync, and @jest/globals to support cross-tab state synchronization |
Sequence Diagram
sequenceDiagram
participant Tab1 as Tab 1
participant BC as BroadcastChannel
participant Tab2 as Tab 2
participant Redux1 as Redux Store (Tab 1)
participant Redux2 as Redux Store (Tab 2)
Tab1->>Redux1: Dispatch Action (e.g., Add Module)
Redux1->>Redux1: requestsMiddleware filters actions with requestStatus
Redux1->>Redux1: stateSyncMiddleware predicate checks action
Note over Redux1: Filters out PERSIST/PURGE/REHYDRATE<br/>and function actions
Redux1->>BC: Broadcast action to other tabs
Redux1->>Redux1: Update local state
BC->>Tab2: Receive broadcasted action
Tab2->>Redux2: Dispatch received action
Redux2->>Redux2: requestsMiddleware skips re-processing<br/>(prevents ping-pong)
Redux2->>Redux2: Update state with synced action
Note over Tab1,Tab2: Both tabs now have synchronized state
Additional Comments (1)
|
|
I can't recall why I initially locked the version of That being said, the current version that is installed (3.7.0) is pretty old. I did some local testing by fixing broadcast-channel to the latest version (7.3.0) and it works fine, looking at the changelog nothing that is used by broadcast-channel is affected. Should I fix it to the latest version? @ravern |
There was a problem hiding this comment.
LGTM overall :D
Thank you for implementing this, and apologies for the insanely long review time.
Regarding the broadcast-channel, since it is a dep of a dep, I'd rather just leave it at whatever version redux-state-sync specifies. Unless there's a security flaw that warrants a version bump.
Fix the build errors, and I'll be happy to approve and merge this in!
|
@ravern thanks for the review! No worries, it took me just as long to work on it as well 😅 Regarding the build failures, what specifically is failing? If you're referring to the typecheck failures, those files are not touched by me so I'm not sure what you mean. I tried |
|
For the build failure, I was referring to the CircleCI workflow, which is publicly-accessible. This is the one that's failing: https://app.circleci.com/pipelines/github/nusmodifications/nusmods/16305/workflows/f5019a8b-cee9-49c4-9d6e-efb647bc40ba/jobs/96459 |
|
Yeah it doesn't look like any files you've touched. I will take over this PR to fix the unrelated issues + get it merged! |
The regenerated yarn.lock pulled in @types/react@19 as a nested dependency of @types/react-router, causing TypeScript build errors. Reset to master's lockfile and reinstall to keep correct deduplication. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Context
Previously, changes to a redux state were not reflected in other tabs. This causes 2 main issues:
Implementation
Uses
redux-state-syncto sync redux states across tabs, which uses a polyfilledBroadcastChannelunder the hood.Other Information
Another option I looked at was
redux-persist-crosstab, which is unmaintained and incompatible with the current version ofredux-persist.Additionally, I had to forcefully resolve
broadcast-channelto version5.3.0.