Skip to content

fix: QA batch - streak strip date math, OTP login race, route blanking, UI polish - #168

Merged
thomasluizon merged 2 commits into
mainfrom
fix/qa-batch-2026-06-12
Jun 12, 2026
Merged

fix: QA batch - streak strip date math, OTP login race, route blanking, UI polish#168
thomasluizon merged 2 commits into
mainfrom
fix/qa-batch-2026-06-12

Conversation

@thomasluizon

@thomasluizon thomasluizon commented Jun 12, 2026

Copy link
Copy Markdown
Owner

What

Nine user-reported issues fixed in one batch. Paired backend PR: thomasluizon/orbit-api#203.

Bugs

  1. Streak week strip showed the last-active day as "missed" until the next completion. The derivation compared subDays(new Date(), n) (carries time-of-day) against parseISO(lastActiveDate) (midnight), so date <= lastActiveDate excluded the last-active day itself. Yesterday looked empty even with 28 logs, then "filled in" after today''s first log. The duplicated derivation now lives in @orbit/shared as buildStreakWeekDays (midnight-anchored, 7 unit tests); both apps consume it. Paired with on-read recalc in the API PR.
  2. Mobile email-OTP login sometimes bounced back to /login. login() ran queryClient.clear() before writing tokens; clearing refetches root-mounted queries (profile is mounted on the login screen), those fired with the old/empty token, hit the 401 path, and clearSessionAndResetAuth() wiped the brand-new session. Tokens are now persisted first, and apiClient retries a 401 with the newest stored token before resetting the session (covers requests that were in flight across the login).
  3. Web: navigating to the Today view blanked the whole screen. RouteTransitionShell used AnimatePresence mode="wait" - the heavy Today mount stalled the main thread right after the old page faded to opacity 0, leaving nothing on screen until the mount finished. Switched to mode="popLayout" (new route mounts immediately) and tab routes are now prefetched (WebNav uses router.push, which never prefetches - every tab switch paid a full RSC round trip).
  4. Web: bad-habit sub-habits showed a gray circle. deriveRowState gated bad (and overdue) behind !isChild; mobile applies both to child rows. Gates removed for parity.
  5. Mobile: calendar selected-day highlight rendered square. Selected-day fill now carries an explicit half-size radius in the same style object.

UX changes

  1. Mobile search bar no longer renders an input-inside-an-input (AppTextInput''s built-in field chrome is neutralized inside the search pill) and gains the web-style Cancel button outside the field.
  2. Celebrations (first-habit streak + all-done) drop the Continue button on both platforms - they auto-dismiss (2.5s / 3.5s) and tap-anywhere still closes early.
  3. Achievements: 2-column grid with uniform card heights and always-visible descriptions (locked + unlocked) on both platforms - descriptions already existed in i18n, they were just never rendered (web had them sr-only).
  4. AI summary generation cap raised to 300 (paired PR); display still clamps to 3 lines past AI_SUMMARY_CLAMP_CHARS = 140 with the see-more/see-less toggle.

Tests

  • New: packages/shared streak-week suite (7), web habit-list bad-child state, web achievements description visibility, mobile api-client token-rotation retry, mobile auth-store ordering.
  • Full validation: lint + type-check clean; web 1478/1478, mobile + shared suites green.

🤖 Generated with Claude Code

…g, UI polish

- streak: extract buildStreakWeekDays into @orbit/shared (midnight-anchored
  comparisons so the lastActiveDate day no longer renders as missed) + tests
- auth(mobile): persist tokens before clearing the query cache on login;
  retry 401s with the newest stored token before resetting the session
- web: deriveRowState applies bad/overdue dot state to sub-habit rows (mobile parity)
- web: route transitions use AnimatePresence popLayout (no blank gap on heavy
  mounts) and tab routes are prefetched
- mobile: calendar selected-day highlight uses an explicit circular radius
- mobile: today search restyled to a single field with Cancel outside (web parity)
- celebrations: remove Continue button from streak + all-done overlays
- achievements: 2-column grid, uniform card height, always-visible descriptions
- summary: clamp threshold follows shared AI_SUMMARY_MAX_CHARS (300)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
orbit-ui-mobile-web Ignored Ignored Jun 12, 2026 4:25pm

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nine QA bugs fixed in a clean, well-structured batch. A few highlights worth calling out:

Streak date math — the buildStreakWeekDays extraction into @orbit/shared is the right move: midnight-anchored via startOfDay instead of raw new Date() comparisons, 7 targeted unit tests covering the tricky boundary (lastActive day was the bug), and both platforms now share identical logic. The future status was also cleanly removed since the 7-day window ends today.

OTP login race — the two-part fix is correct. Persisting tokens before queryClient.clear() eliminates the root cause; the apiClient token-rotation retry (compare tokenUsed to the latest stored token before triggering the refresh flow) covers in-flight requests during the narrow window. The tests nail both ordering invariants.

deriveRowState parity fix — removing the !isChild gate for bad/overdue is the right call; the mobile behavior was already correct, and the web test now locks in the expected state value on child rows via data-state.

footerStyle in useCelebrationEntrance — still consumed by streak-freeze, goal-completed, and level-up overlays, so no dead code issue. The two components that dropped their Continue buttons cleaned up their destructuring correctly.

Overall: solid bug-batch PR with tests for every non-trivial fix, both platforms updated, shared utility well-tested. Good to merge.

…ver 140 chars

The display clamp threshold was raised to 300 together with the backend
generation cap, which removed the collapse affordance for every summary the
backend can now produce. The two limits are independent: generation stays at
300, display clamps at 140 (AI_SUMMARY_CLAMP_CHARS).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Second commit holds up: extracts the magic 140 into AI_SUMMARY_CLAMP_CHARS in packages/shared, both platforms import it, and the JSDoc clearly documents the independence from the API's 300-char generation cap. Full PR is solid — nine targeted bug fixes with tests for every non-trivial path, cross-platform parity throughout, and CI green. Good to merge.

@thomasluizon
thomasluizon merged commit 8c9906f into main Jun 12, 2026
7 checks passed
@thomasluizon
thomasluizon deleted the fix/qa-batch-2026-06-12 branch June 12, 2026 16:30
@sonarqubecloud

Copy link
Copy Markdown

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