Skip to content

UX improvements: inline undo banner, haptics, technical-details preference, sign-in toast#19

Open
jameshaworthcs wants to merge 15 commits into
mainfrom
ux-improvements
Open

UX improvements: inline undo banner, haptics, technical-details preference, sign-in toast#19
jameshaworthcs wants to merge 15 commits into
mainfrom
ux-improvements

Conversation

@jameshaworthcs

@jameshaworthcs jameshaworthcs commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Summary

UX-polish branch on top of the Expo SDK 56 upgrade:

  • Inline undo banner (state-machine) replacing the undo modal
  • Haptics across autocheckin / history / settings / report / options flows
  • Opt-in "Show technical details" preference, synced with the web account
  • Tappable toasts + a contact-support toast when sign-in closes early
  • Liquid Glass keyboard "Done" capsule and sheet keyboard-layout fix

Fixes & tests

  • Fixed an undo refresh-block leak and the technical-details optimistic-save rollback
  • Added hook/component tests (undo state machine, technical-details, UndoBanner, sign-in) — 127 passing

Version bumped 1.2.15 → 1.2.16.

jameshaworthcs and others added 14 commits May 15, 2026 20:53
Cancelling or dismissing the auth browser used to leave the modal in a
silent or never-rendered error state. Now the modal closes cleanly and an
info toast appears explaining the cancellation; tapping it opens the
support page (authenticated transfer-token URL with an unauthenticated
fallback).

Toast component now wraps in TouchableOpacity when an onPress is
supplied, and useToast forwards an optional onPress that hides the toast
before invoking the handler.
- Report Code: selection haptic on changing the selected code; light
  impact + success/error around submit.
- Undo: light impact on banner tap; medium impact on modal confirm +
  success/error on completion.
- History: success haptic on copy; light impact + success/error on
  hide/restore.
- Options sheet: selection haptic on non-push rows and the Session
  Info toggle. Push rows already get one from the sheet transition.
- AutoCheckin: light impact + success/error on Join, Leave, and Open.
- Settings: light impact + success on Log Out; warning on opening
  Delete Account; medium impact + success/error on delete submit.
Adds a Display section toggle in Settings that gates the live-update fetch
timing in the checkout indicator, mirroring the web's "Show technical
details" preference. The pref is persisted to local storage for instant
first paint and synced to the user's account via /api/settings/technicalDetails
when signed in. Live-classes poll responses include the server-authoritative
value in userInfo, so changes made on the web propagate to the app within
the next 2.5s poll without an extra request.
Tapping Undo now cross-fades the thank-you banner into a "Remove your
code?" yes/no, then spinner, then a persistent "Code removed." with a
dismiss — no modal hop, no toast-only error. Mirrors the web change in
v1.9.122.

useLiveClasses owns the interactive state machine
(confirming/undoing/undone/error) and patches any buffered poll snapshot
on success so a stale flush can't revert the card. The purge in
applyDataToUI now stringifies keys before comparing — session.rejectID
is the DB activityID and arrives as a number, which previously meant
the undone entry was deleted on the very next refresh and never
rendered.
Replace the opaque accessory bar above the keyboard with a transparent
host and a 52pt circular Liquid Glass capsule (expo-glass-effect's
GlassView) on iOS 26, with a systemChromeMaterial BlurView fallback for
older iOS. The capsule contains a tinted checkmark SF Symbol so the
control reads as a real iOS submit affordance rather than a "Done"
label on a black slab.

Pass accessibilityLabel="Submit code" from CodeInput so VoiceOver
announces the action, not just "Done".
Two issues caused the report form to appear with either a large empty
gap below the action buttons, or with the bottom of the buttons clipped
behind the sheet's paddingBottom:

1. KeyboardAvoidingView (behavior="padding") already lifts the sheet
   above the keyboard, but automaticallyAdjustKeyboardInsets on the
   inner ScrollView was double-counting that by adding another
   keyboardHeight of contentInset, prompting iOS to over-scroll the
   focused field to the top of an inflated content area.

2. The sheet shrinks by keyboardHeight when the keyboard rises, but
   kept its full insets.bottom paddingBottom for the home indicator —
   which is hidden behind the keyboard and so unused. Reclaiming that
   inset in the maxHeight clamp lets the sheet grow upward by that
   amount, giving the ScrollView viewport enough room for the form.
On launch, sessions are hydrated synchronously from the persisted
response cache, which can still hold a session that finished while the
app was closed. The toast effect fired against that stale data — and the
in-memory dedup set is reset on each mount, so it no longer suppressed
the repeat — surfacing a "<session> has N codes" toast for a session
that is no longer visible.

Gate the toast on lastUpdateTime so it only runs once a live fetch has
landed (by which point a finished session has already dropped out of the
list), and prune the dismissed set to currently-active IDs so it stays
bounded and a session that becomes active again can re-toast.
Apply npm audit fix to clear the critical and all high-severity
advisories (46 -> 16). Remaining 16 moderate are dev/build-toolchain
deps gated behind an Expo SDK 56 breaking upgrade (tracked on
chore/expo-sdk-56-upgrade-wip).
* chore(deps): WIP Expo SDK 54->56 upgrade (npm audit --force)

Upgrades expo to 56 and aligns native modules via 'expo install --fix'.
INCOMPLETE - requires code migration before merge:
- expo-router no longer compatible with @react-navigation (8 files)
- expo-router/unstable-native-tabs API changed (Icon/Label/VectorIcon)
- StyleSheet.absoluteFillObject removed
- missing peer dep react-native-worklets
- needs simulator/device testing
Clears remaining audit moderates that are gated behind SDK 56.

* chore(deps): complete Expo SDK 56 upgrade

Finishes the SDK 54->56 migration started in the WIP commit and clears
the npm audit.

- npm audit: 0 vulnerabilities via a uuid>=11.1.1 override scoped to
  xcode (the only consumer; it uses uuid.v4() only)
- native-tabs: migrate Icon/Label/VectorIcon to NativeTabs.Trigger.*,
  androidSrc -> src (SDK 56 API)
- StyleSheet.absoluteFillObject (removed) -> absoluteFill
- useColorScheme() now returns 'unspecified' (RN 0.85); normalize to
  'dark' ? 'dark' : 'light' across themed lookups
- register react-native-worklets jest resolver so the reanimated 4 mock
  loads (fixes 3 test suites)
- IconSymbol: key the SF-symbol map by string (expo-symbols name type
  widened to a platform-object union)
- tsconfig types:["jest"] (TS 6.0 no longer auto-includes ambient @types)
- resolve all eslint-plugin-react-hooks v7 (React Compiler) findings:
  Animated.Value refs -> useState lazy init, Confetti particles -> state
  with shape baked in, scoped disables for Reanimated/worklet idioms

tsc, expo lint, and prettier clean; npm audit clean; 87 tests pass.

* style: apply prettier to KeyboardDoneAccessory

Pre-existing formatting drift unrelated to the SDK upgrade; brings the
repo to a clean prettier --check (CI prettier.yml).

* fix(router): migrate @react-navigation imports to expo-router entry points

SDK 56's Metro resolver rejects any @react-navigation/* import from app
code, failing the bundle (and eas submit) at app/(tabs)/_layout.tsx.

Repoint all such imports to expo-router's own entry points:
- nav/theme hooks (useRoute, useFocusEffect, useNavigation,
  ThemeProvider, DarkTheme, DefaultTheme) -> 'expo-router'
- BottomTabBarHeightContext -> 'expo-router/js-tabs'

Runtime API is unchanged; only the module specifiers move.
- useLiveClasses: confirmUndo's error paths now unblock refresh, and the
  poll purge releases the 'undo' blocker when a session ends mid-confirm/
  undoing. Previously an undo network error (or a class ending while the
  inline confirm was open) left 'undo' stranded in the blocker set and
  froze live updates until app restart. retryUndo re-blocks on return to
  the confirm step.
- useShowTechnicalDetails: roll back the optimistic local/cache update when
  the save POST fails so storage, in-memory cache, and the Switch don't
  diverge from the server.
- CheckOutIcon: pass the color (string | OpaqueColorValue) straight to the
  SVG fill instead of .toString(), which flattened PlatformColor handles to
  a non-renderable string.
Adds the repo's first hook tests (renderHook), covering the two fixes from the
prior commit plus the surrounding logic:

- useLiveClasses.undo: full begin/confirm/cancel/retry/dismiss state machine,
  applyUndoToPending buffered-snapshot patching, and the fix #1 regressions —
  confirmUndo error (rejected + thrown) unblocks refresh, retryUndo re-blocks,
  'undone' survives the session ending while a stale 'error' is purged. The
  three error/purge tests fail against the pre-fix hook.
- useShowTechnicalDetails: hydration, optimistic save, the fix #2 rollback on
  failure (transport + body-level), accountSync relay, listener broadcast, and
  server reconciliation on login. Module-singleton state is isolated via
  resetModules + the /pure entry so renderHook shares the hook's React.

110 tests pass (was 87).
- UndoBanner: renders every step (idle/confirming/undoing/undone/error) with
  correct copy, fallbacks, button wiring, the cross-fade step swap, and the
  polite live region. Reduced motion is forced so swaps are synchronous and the
  vector icon is stubbed to avoid an async font setState.
- useAuth: native (WebBrowser) sign-in flow — the 'Sign-in cancelled' toast on
  cancel/dismiss/success-without-token, its contact-support onPress opening the
  support URL, and suppression of the toast when a token is returned. The web
  popup path (no DOM here) is documented as not covered.

127 tests pass (was 110).
The real @expo/vector-icons Icon loads its font asynchronously and setState()s
after render, surfacing as "not wrapped in act(...)" warnings in every test that
renders one (VerificationContent, ReportContent, and others). Mock the icon
entrypoints once in jest.setup.ts with a host-element stub that preserves
accessibility props, and drop the now-redundant local mock in UndoBanner.test.

Test suite is now warning-free; 127 tests pass.
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jameshaworthcs, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 55 minutes and 36 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 734c4c82-3331-4d2d-897c-370808b77ce8

📥 Commits

Reviewing files that changed from the base of the PR and between 9e24dc8 and 06097ef.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (45)
  • app.json
  • app/(tabs)/_layout.tsx
  • app/(tabs)/autocheckin/index.tsx
  • app/(tabs)/checkout/index.tsx
  • app/(tabs)/history/index.tsx
  • app/(tabs)/settings/index.tsx
  • app/_layout.tsx
  • components/ConnectionErrorOverlay.tsx
  • components/ForceUpdateOverlay.tsx
  • components/KeyboardDoneAccessory.tsx
  • components/ParallaxScrollView.tsx
  • components/TabScreenScrollView.tsx
  • components/UnavailableOverlay.tsx
  • components/checkout/AutoSplashModal.tsx
  • components/checkout/CodeDisplay.tsx
  • components/checkout/CodeInput.tsx
  • components/checkout/Confetti.tsx
  • components/checkout/SheetHost.tsx
  • components/checkout/SubmissionProgress.tsx
  • components/checkout/UndoBanner.tsx
  • components/checkout/UndoModal.tsx
  • components/checkout/__tests__/UndoBanner.test.tsx
  • components/checkout/sheet/OptionsContent.tsx
  • components/checkout/sheet/ReportContent.tsx
  • components/checkout/sheet/useSheetLifecycle.ts
  • components/ui/CheckOutIcon.tsx
  • components/ui/IconSymbol.tsx
  • constants/storageKeys.ts
  • contexts/ConnectionContext.tsx
  • hooks/__tests__/useAuth.signin.test.ts
  • hooks/__tests__/useLiveClasses.undo.test.ts
  • hooks/__tests__/useShowTechnicalDetails.test.ts
  • hooks/useAuth.ts
  • hooks/useColorScheme.web.ts
  • hooks/useConnectionMonitor.ts
  • hooks/useLiveClasses.ts
  • hooks/useShowTechnicalDetails.ts
  • hooks/useThemeColor.ts
  • hooks/useToast.ts
  • hooks/useVersionCheck.ts
  • jest.setup.ts
  • package.json
  • styles/settings.styles.ts
  • tsconfig.json
  • types/liveClasses.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ux-improvements

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jameshaworthcs jameshaworthcs changed the title UX improvements: inline undo, haptics, technical-details preference, sign-in toast UX improvements: inline undo banner, haptics, technical-details preference, sign-in toast Jun 4, 2026
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.

1 participant