Skip to content

Clear sync state on user logout#2707

Merged
fredrikekelund merged 38 commits into
trunkfrom
gcsecsey/stu-166-clear-sync-on-logout
Jun 3, 2026
Merged

Clear sync state on user logout#2707
fredrikekelund merged 38 commits into
trunkfrom
gcsecsey/stu-166-clear-sync-on-logout

Conversation

@gcsecsey

@gcsecsey gcsecsey commented Mar 4, 2026

Copy link
Copy Markdown
Member

Related issues

Fixes STU-166

How AI was used in this PR

I used AI to research the existing logout flow, then implemented and iterated on the changes using it.

Proposed Changes

When a user logged out, sync-related Redux state was not cleared, and the stale data remained in memory. If a different user logged in, they could see leftover sync UI state from the previous session.

This PR adds a userLoggedOut Redux action that is dispatched either when logging out explicitly or when an invalid token is detected. Multiple slices and a store listener react to this:

  • syncOperations slice resets pullStates and pushStates to empty
  • sync slice clears the remote file tree cache
  • connectedSites slice resets modal and selection state
  • A store listener cancels active main-process sync operations, stops renderer-side push/pull pollers, and resets authenticated RTK Query caches

Testing Instructions

  • Log in to a WordPress.com account and connect a remote site
  • Start a push or pull operation
  • Log out
  • Log in again with the same user or a different one
  • Check that the Sync tab shows the logged out state
  • Log in again (same or different account) and check that initial pull/push states are shown
CleanShot.2026-03-04.at.16.41.21.mp4

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

Dispatch a shared userLoggedOut action on logout and invalid token detection
that resets sync slices, cancels active operations, stops pollers, and clears
authenticated RTK Query caches.
@gcsecsey gcsecsey self-assigned this Mar 4, 2026
@gcsecsey gcsecsey requested a review from a team March 4, 2026 16:54
@gcsecsey gcsecsey marked this pull request as ready for review March 4, 2026 16:54

@katinthehatsite katinthehatsite left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for making the changes @gcsecsey - nice work!

I found some issues on my end when testing this PR. This is what I saw on my end: I started the pull operation and waited until the downloading backup step. Then, I logged out and noticed that some of the buttons were stuck in the "Importing" state:

Image

(see the top right corner)

Then, when I log in as a different user, I see the following issue on the import screen:

Image

When importSite fails during a sync pull, the pull Redux state stays at
'importing' while the import context fires an error event. Since
canCancelPull only allows cancellation for 'in-progress' and
'downloading' states, the user gets stuck with a disabled dismiss button.

Add an isError flag to ImportProgressState and set it on import errors.
In the sync UI, detect when a pull is in 'importing' state but the
import has failed, and treat it as a pull error so the user can dismiss
it and retry.
@gcsecsey

Copy link
Copy Markdown
Member Author

Thanks for making the changes @gcsecsey - nice work!

I found some issues on my end when testing this PR. This is what I saw on my end: I started the pull operation and waited until the downloading backup step. Then, I logged out and noticed that some of the buttons were stuck in the "Importing" state:

Image (see the top right corner)

Then, when I log in as a different user, I see the following issue on the import screen:

Image

Thanks for the review @katinthehatsite, great catch! This was caused by the import/export also being tracked in the ImportExportProvider context, not just Redux. I set this to also be reset on logout.

I also encountered a stuck state when trying to pull a site that caused a db import issue:

image

I updated the component to treat this state as a pull error as well, to unblock this state. However, I'm not sure if this is the best approach to handle this. I think ideally we'd show the pull operation as completed, and separately also show the db import error in a dialog, similarly to how it's done when importing, right?

@wpmobilebot

wpmobilebot commented Mar 13, 2026

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing a73a48e vs trunk

app-size

Metric trunk a73a48e Diff Change
App Size (Mac) 1334.61 MB 1334.62 MB +0.01 MB ⚪ 0.0%

site-editor

Metric trunk a73a48e Diff Change
load 1660 ms 1640 ms 20 ms ⚪ 0.0%

site-startup

Metric trunk a73a48e Diff Change
siteCreation 9053 ms 9006 ms 47 ms ⚪ 0.0%
siteStartup 4410 ms 4409 ms 1 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

@gcsecsey gcsecsey requested review from a team and katinthehatsite March 13, 2026 15:10
Comment thread apps/studio/src/stores/index.ts Outdated
Comment thread apps/studio/src/stores/index.ts Outdated
@gcsecsey gcsecsey requested a review from fredrikekelund March 17, 2026 10:54

@katinthehatsite katinthehatsite left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for making the changes @gcsecsey ! I was testing the flow a bit more but now I am getting strange session expired messages:

Image

If I do manage to log in, I am getting the errors related to not being logged in while doing the push or pull:

Image

I did not debug the code for this but wanted to point this out early in the review.

@gcsecsey

Copy link
Copy Markdown
Member Author

Thanks for making the changes @gcsecsey ! I was testing the flow a bit more but now I am getting strange session expired messages:

Image If I do manage to log in, I am getting the errors related to not being logged in while doing the push or pull: Image I did not debug the code for this but wanted to point this out early in the review.

Thanks for testing again @katinthehatsite! I could reproduce this issue, and found that it was caused by the wpcomClient being cleared after the logout event being dispatched, causing refetches to run into the error.

I also found that even though I added the listener earlier for clearing the state, these weren't firing because the listener iterated on the updated (empty) state.

I fixed both of these issues now and the whole pull flow seems to be working well on my end:

CleanShot.2026-03-19.at.13.54.07.mp4

I'm still working to fix an issue with cancelling pushes, then I'll ask for another review.

…tomattic/studio into gcsecsey/stu-166-clear-sync-on-logout
…logout

Extracted PUSH_POLLERS/PULL_POLLERS maps and stop functions into a shared
sync-pollers module. Cancel thunks now call stopPushPoller/stopPullPoller
directly, so in-flight poll requests see signal.aborted and exit silently
instead of showing error modals.
@gcsecsey

Copy link
Copy Markdown
Member Author

@katinthehatsite @fredrikekelund I think this is ready for another review. I fixed these issues for the push flow;

I reverted emptying the connected sites cache on logout to avoid resetting the connected site list when logging back in:

CleanShot.2026-03-19.at.14.02.16.mp4

I also updated the pollers to avoid receiving errors when logging out at a later step in the push process, when the changes are being processed on the server:

CleanShot.2026-03-19.at.14.40.35.mp4

@fredrikekelund fredrikekelund left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I took another look at the Linear issue, and it doesn't appear like these changes fully address the issue.

If I have two users with access to the same WP.com site, initiate a push to the site with my first account, log out in Studio, then log in with my second account (which has also connected the same WP.com site to my local site), then I still see Push cancelled in the interface. One step better than seeing an error message, but the UI should really be completely cleared.

Comment thread apps/studio/src/components/auth-provider.tsx Outdated
Comment thread apps/studio/src/stores/sync/sync-operations-slice.ts

@katinthehatsite katinthehatsite left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I tried testing the changes again but I am still seeing the error with pushing a site when I log in and try to do the push operation:

Image

I tried with a couple of different users but I keep on running into the same error.

@katinthehatsite katinthehatsite left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Log in again (same or different account) and check that initial pull/push states are shown

I am re-testing this again and when I log back in, I am seeing Push cancelled even though it was in progress when I logged out:

Image

Just to confirm, is this the expected behaviour?

@katinthehatsite katinthehatsite left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And another sidenote is that file it shows Push cancelled on the Sync tab, I am seeing in Import/Export tab that the sync is in progress:

Image

@gcsecsey

Copy link
Copy Markdown
Member Author

Just to confirm, is this the expected behaviour?

Good catch, sorry for missing this earlier. No, I think it'd be better behavior to completely clear the state in this case, and not show the success message.

And another sidenote is that file it shows Push cancelled on the Sync tab, I am seeing in Import/Export tab that the sync is in progress:

Thanks for catching, this is also clearly a bug. I'll also address this one.

@gcsecsey

Copy link
Copy Markdown
Member Author

I fixed both issues, now the state is completely reset on logout:

CleanShot.2026-04-15.at.17.22.46.mp4

@gcsecsey

Copy link
Copy Markdown
Member Author

@katinthehatsite @fredrikekelund could you please give this another look when you have some time? Thanks! 🙏

@fredrikekelund

Copy link
Copy Markdown
Contributor

@gcsecsey, yep, I'll take another look later today. Sorry for the delay

@fredrikekelund fredrikekelund left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This issue is really surprisingly complex… 🤔 I raised a point about how we handle active import operations when a user logs out. I think it's OK if this PR doesn't have a perfect solution to that problem, but we should at least be explicit in the code and acknowledge that we don't/can't abort those operations.

Also, I'd like to clarify how the sync operation state gets updated when displayNotification=true in the cancel{Push,Pull}Thunk functions.

Comment thread apps/studio/src/components/content-tab-import-export.tsx Outdated
Comment thread apps/studio/src/hooks/use-import-export.tsx
Comment thread apps/studio/src/stores/sync/sync-operations-slice.ts Outdated
Comment thread apps/studio/src/stores/sync/sync-operations-slice.ts Outdated
gcsecsey added 4 commits May 6, 2026 10:31
…tomattic/studio into gcsecsey/stu-166-clear-sync-on-logout
Replace per-operation cancel thunk dispatches with a single
abortAllSyncOperations() helper. The slice's addCase(userLoggedOut)
already resets pullStates/pushStates, so the listener only needs to
abort in-flight side effects (pollers, push upload aborts, main-process
operations) — no state dispatches required.

This removes the displayNotification param from cancelPush/cancelPull,
which was overloaded to gate both the toast and the post-logout state
re-population. The thunks are now purely user-initiated cancel paths.
An active pull's import phase is driven by main-process events that
keep emitting after logout, so resetting importState here was futile —
it would just get repopulated. Snapshot the pre-logout pull-active
flag via a ref (pullStates is reset synchronously on userLoggedOut, so
the post-logout selector value is already false), and only clear when
no pull was active.

The hook now reads from the Redux store, so the test wrapper needs a
Provider too.
Conflicts in apps/studio/src/hooks/use-import-export.tsx and the deletion
of its test file: trunk's #3129 refactored the hook to depend on the CLI
for import/export, removing Sentry, useSiteDetails, error formatting,
and the BackupArchiveInfo path. Took trunk's simpler structure and
re-applied the logout cleanup block (useAuth + useRootSelector +
useRef-based pre-logout pull-active snapshot driving the
setImportState/setExportState reset).

Dropped the isError flag on ImportProgressState and the matching
pullImportFailed workaround in sync-connected-sites.tsx: trunk's pull
thunk now propagates importSite errors via pullStates.failed, so the
"dismiss button stuck disabled" bug those addressed is no longer
reachable. Accepted trunk's deletion of use-import-export.test.tsx as
part of the legacy-test cleanup.

@fredrikekelund fredrikekelund left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is looking pretty good, but there's an issue where the sidebar spinner doesn't disappear after logging out

Comment thread apps/studio/src/index.ts
Comment thread apps/studio/__mocks__/electron.ts
Comment thread apps/studio/src/modules/sync/components/sync-connected-sites.tsx Outdated
Comment thread apps/studio/src/modules/sync/components/sync-connected-sites.tsx Outdated
@gcsecsey

gcsecsey commented Jun 1, 2026

Copy link
Copy Markdown
Member Author

@fredrikekelund I rebased the PR and followed up on your comments, except for the issue where the sidebar spinner doesn't disappear after logging out. I'll look into that tomorrow and ping you again when it's ready for another review.

@gcsecsey

gcsecsey commented Jun 2, 2026

Copy link
Copy Markdown
Member Author

@fredrikekelund @katinthehatsite I think this is ready for another review.

While working on fixing the spinner state, I found that it's a symptom of not having the same abort callbacks for pulls as for pushes.

I updated the behavior so that logging out during the download phase will stop without the error dialog from before, and will clear the spinner too. During the import phase, the import is local, so we let it finish, keeping the spinner visible until it completes. I think that’s the right tradeoff, but happy to revisit. WDYT?

@fredrikekelund fredrikekelund left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for getting this over the finish line, @gcsecsey. I pushed some final tweaks that moved selector logic into the Redux slice, but fundamentally, this was already looking good.

I think you already know this, but I'll say it anyway: long lead times between reviews and changes slow down the process as a whole, because reviewers loose context after a couple of days. RSM got in the way of this one, this is arguably a low priority fix, and I dragged my feet reviewing this on a few occasions, too. Nevertheless, something we can all keep in mind in the future 🙂

@fredrikekelund fredrikekelund merged commit 091b276 into trunk Jun 3, 2026
12 checks passed
@fredrikekelund fredrikekelund deleted the gcsecsey/stu-166-clear-sync-on-logout branch June 3, 2026 10:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants