Fixed stale sidebar member counts after member mutations#28191
Conversation
ref https://linear.app/tryghost/issue/BER-3510/investigate-and-fix-stale-member-count-in-sidebar Member count changes can flow through several admin paths, so imports and Ember member changes now use the existing query invalidation path instead of leaving the sidebar count stale.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds a typed useImportMembers mutation, import types/type-guards, and a FormData builder; centralizes membersPath and member-count query helpers; introduces an internal useBrowseMembersInfiniteQuery and wraps it with useBrowseMembersInfinite to sync a sidebar member-count cache entry. Refactors ImportMembersModal to call useImportMembers.mutateAsync and handle typed responses/errors. Updates tests to cover API request shape, cache invalidation, sidebar count sync/no-sync cases, Ember bridge invalidation, modal integration, and expanded error handling. Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f4b34b7481
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ref https://linear.app/tryghost/issue/BER-3510/ The members list can receive a fresher unfiltered total than the lightweight sidebar count query, so the shared members hook now reconciles the existing sidebar cache without page-level wiring.
ref https://linear.app/tryghost/issue/BER-3510/ The sidebar should not need to know that member count is implemented with a lightweight members browse query, so the framework members API now owns the count-specific hook and cache key details.
ref https://linear.app/tryghost/issue/BER-3510/ The behavior coverage is still focused on invalidation, cache reconciliation, and import response handling, but repeated setup has been moved into small helpers so the PR carries less low-signal test diff.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1544451f57
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ref https://linear.app/tryghost/issue/BER-3510/ Member CSV uploads are non-idempotent, so transient network failures must not replay the same import request.
ref https://linear.app/tryghost/issue/BER-3510/ The sidebar count reconciliation guard for placeholder members data is intentional, so the members API tests cover it without expanding the scoped background import behavior.
7ee85dc to
54903c8
Compare
rob-ghost
left a comment
There was a problem hiding this comment.
If you've tested and it works I'm happy, I just had a couple of code structure opinions but nothing blocking.
ref https://linear.app/tryghost/issue/BER-3510/ Applies PR review feedback and a follow-up hardening pass; no behaviour change for the documented server contract. - dropped the `stats?: undefined` discriminator and moved import_label nullability onto the response types so the import union reads honestly - discriminate the upload response on meta.stats, keeping the guard defensive (returns false on a malformed payload rather than throwing) - replaced the confusing "unexpected response" throw in the import modal with an exhaustive complete-vs-accepted branch that trusts the documented 2xx contract - extracted the cache sync into a named useSyncMemberCountFromList hook with named guard conditions, patching only the `total` the sidebar reads and stamping it with the list's fetch time so a newer count fetch still wins - the import modal test now keeps the real type-guards via importActual instead of duplicating their logic
ref https://linear.app/tryghost/issue/BER-3510/ The previous comments ascribed intent to the cache ("the sidebar may not be mounted", "didn't ask for"); reworded to plainly state we only update the count query when it already exists.
Summary
MembersResponseTypeinvalidation path.useMemberCount()hook that owns the lightweight/members/?limit=1sidebar count query.useBrowseMembersInfinite()so a fresh unfiltered members-list total updates the existing sidebar count cache when both queries share a query client.Root Cause
Some member-changing paths updated local screen state or used raw upload calls without going through the shared query invalidation mechanics. That left the sidebar's lightweight member-count query stale while the main Members screen could show a newer total.
There was also no shared owner for the sidebar count query shape, so sidebar code knew that member count was implemented as a
limit=1members browse request.Implementation Notes
MembersResponseTypequeries through the existing admin-x-framework mechanics.useMemberCount()from@tryghost/admin-x-framework/api/members, keeping thelimit=1query details inside the framework API.useBrowseMembersInfinite()updates only the existing sidebar count cache entry, and only when the list query is successful, unfiltered, unsearched, non-placeholder/non-previous data, and at least as fresh as the sidebar cache.Validation
pnpm --filter @tryghost/admin-x-framework exec eslint src/api/members.ts test/unit/api/members.test.tsxpnpm exec vitest run test/unit/api/members.test.tsx --coverage=falsepnpm exec tsc --noEmitpnpm --filter @tryghost/admin-x-framework buildpnpm --filter @tryghost/posts exec eslint src/views/members/members.tsxpnpm exec vitest run test/unit/views/members --coverage=falsepnpm --filter @tryghost/admin exec eslint src/layout/app-sidebar/nav-content.tsxref https://linear.app/tryghost/issue/BER-3510/investigate-and-fix-stale-member-count-in-sidebar