fix(book-club): app-wide 401 re-login, poll deadline TZ, empty-state refresh, action degradation#19
Conversation
…ty-state refresh, action degradation Second batch of book-club review follow-ups (the ones that needed a design call): - AuthInterceptor: a 401 on an authenticated request now clears the session token, so SessionStore.authState flips and the nav host routes every screen back to login instead of leaving the user on a permanent retryable error. App-wide (not Book-Club-specific) — this is the central handler the dead isAuthExpired() implied. - DateFormat.isPast(): pin the server timezone contract. An ISO-8601 instant (the bridge sends deadlines like closes_at as UTC ...Z via isoUtc()) is now compared as an absolute Instant, so poll open/closed gating is correct regardless of device timezone. Only a bare wall-clock DATETIME falls back to device-local (best effort). - BookClubHomeScreen: the "no clubs yet" EmptyState fills the viewport and isn't scrollable, so the PullToRefreshBox gesture never fired on it. Gave it an explicit Refresh button (new book_club_refresh string, all 4 locales) so a user who joins a club on the web can reload. (ErrorState already had Retry.) - ClubDetailViewModel: an action (join/vote/rsvp/progress) that fails with 404 for a plugin deactivated mid-session now confirmGone()+reloads, so the screen degrades to the friendly "gone" EmptyState the same way the initial-load path already does. Verified: :app:compileDebugKotlin passes.
|
Warning Review limit reached
Next review available in: 32 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (11)
✨ 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 |
…progress-dialog timing The three low-severity UI items deferred from the review: - Snackbar: keyed the show LaunchedEffect on a monotonic nonce (bumped on every post) instead of the resolved message string, so two identical consecutive messages (e.g. voting twice → "Vote saved") both surface instead of the second being silently swallowed. Both the home and detail screens. - openWeb(): a blank URL or a device with no browser (ActivityNotFoundException) was a silent no-op; now shows a Toast (new book_club_no_browser string, 4 locales). - ProgressDialog: no longer dismissed the instant onSave fires — it stays open showing its `saving` state until the network round-trip clears progressBookId, so the loading state is actually reachable and a fast save no longer flashes shut. Verified: :app:compileDebugKotlin passes.
The four deferred book-club review items (the ones that needed a design call).
:app:compileDebugKotlinpasses.AuthInterceptornow clears the session token on a 401 to an authenticated request, soSessionStore.authStateflips and the nav host routes every screen (Book Club and everything else) back to login instead of stranding the user on a permanent retryable error. This is the central handler the deadisAuthExpired()implied — app-wide, not Book-Club-specific.DateFormat.isPast()now compares an ISO-8601 instant as an absoluteInstant. The bridge already sends deadlines (closes_at) as UTC…Zvia itsisoUtc()helper, so poll open/closed gating is now provably correct regardless of device timezone; a bare wall-clock DATETIME still falls back to a best-effort device-local comparison.EmptyStatefills the viewport and isn't scrollable, so thePullToRefreshBoxgesture never fired on it. Gave it an explicit Refresh button (newbook_club_refreshstring in all 4 locales).ErrorStatealready had Retry; the plugin-gone state is terminal by design.confirmGone()+ reloads, so the screen degrades to the friendly "gone" EmptyState the same way the initial-load path already does.Not included (genuinely low-value micro-polish, left for an interactive UX pass): snackbar de-dup on two identical consecutive messages,
openWebtoast on a device with no browser, progress-dialog dismiss-before-save timing.