Skip to content

feat(gamification): tappable streak badge → streak page (#108) - #129

Merged
thomasluizon merged 5 commits into
mainfrom
issue-108
Jun 5, 2026
Merged

feat(gamification): tappable streak badge → streak page (#108)#129
thomasluizon merged 5 commits into
mainfrom
issue-108

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

Closes #108

Stacked on #128 (mobile type-check fix). Base branch is chore/fix-mobile-type-check; rebase onto main once #128 merges.

What

Make the Today-header StreakBadge interactive on both web and mobile so a tap routes to the existing /streak page.

How

  • Web (apps/web/components/gamification/streak-badge.tsx): render the badge as a <button> (implicit button role) calling router.push('/streak') via next/navigation, preserving the aria-label, frozen/active stroke logic, and hairline pill styling. Mirrors the existing /streak navigation on the profile page.
  • Mobile (apps/mobile/components/gamification/streak-badge.tsx): wrap the badge in a Pressable with accessibilityRole="button" and onPress → router.push('/streak') (expo-router). The press calls event.stopPropagation() so the Today header's outer <Pressable onPress={onGoToToday}> does not also fire — same pattern as the sibling NotificationBell.
  • today-shell.tsx (web + mobile) and the page/index containers are intentionally untouched — the interaction is self-contained in the badge.

Tests / validation

  • Badge tests on both platforms assert the button role + that activating the badge navigates to /streak; the mobile test also asserts propagation is stopped.
  • npm run type-check GREEN (all 3 workspaces), npm run lint GREEN.
  • npm test: web 1449 passed (149 files), mobile 366 passed (82 files).
  • No browser E2E run — manual check.

Backend

Paired API PR (auto-activate streak freeze on a missed day): thomasluizon/orbit-api#179

@vercel

vercel Bot commented Jun 4, 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 5, 2026 12:06am

@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.

Clean, focused PR. Both badges correctly swapped to interactive elements (<button> on web, <Pressable> on mobile) with proper router navigation and no regressions. The mobile stopPropagation pattern mirrors the existing NotificationBell and habit-emoji-selector precedents, and the doc comment explains the non-obvious propagation invariant. Cross-platform parity is fully satisfied, tests cover the new behavior on both platforms, and production code is free of any, console.log, and hardcoded strings.

@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 (auto-freeze UI redesign) is clean and well-structured — both platforms updated, i18n parity maintained, tests rewritten to match the new model, and the manual activation flow deleted end-to-end. One stale-code issue blocks approval.

Dead export in packages/shared/src/types/gamification.ts (lines 81–88)

This PR deletes every caller of StreakFreezeResponse:

  • useActivateStreakFreeze removed from both apps/web/hooks/use-gamification.ts and apps/mobile/hooks/use-gamification.ts
  • The streakFreeze endpoint constant removed from packages/shared/src/api/endpoints.ts

But streakFreezeResponseSchema and the inferred StreakFreezeResponse type were not removed. A full-repo grep confirms zero references to either symbol anywhere in the monorepo outside their own definition. Delete both:

// packages/shared/src/types/gamification.ts — remove lines 81-88:
export const streakFreezeResponseSchema = z.object({
  freezesRemainingThisMonth: z.number(),
  frozenDate: z.string(),
  currentStreak: z.number(),
  streakFreezesAccumulated: z.number().default(0),
})

export type StreakFreezeResponse = z.infer<typeof streakFreezeResponseSchema>

Everything else — the isFrozenToday ref-based celebration trigger (identical pattern on both platforms), the ChargeGauge pip animations (CSS + Animated.timing respectively), the Pro gate, and the nextFreezeDays arithmetic — is correct.

@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.

The changes-requested item from the prior review — dead StreakFreezeResponse / streakFreezeResponseSchema in packages/shared/src/types/gamification.ts — is fully addressed by the third commit. A full-repo grep confirms zero references remain. The rest of the PR (tappable badge on both platforms, auto-freeze UI redesign with Pro gate, deleted manual-activation flow end-to-end, i18n parity maintained, badge tests on both platforms) was already approved in the earlier pass. No new issues introduced. Approving.

@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.

Fourth commit is a clean polish pass. The cryptic AUTO chip (which read as a technical flag rather than user communication) is removed from both platforms in perfect parity, the now-dead auto.chip i18n key is deleted from both locales, the explainer is tightened to a single plain sentence, and the tests are updated to match. No new issues introduced.

The full PR is solid: tappable StreakBadge on both platforms, complete manual-activation removal end-to-end, auto-freeze UI with charge gauge + Pro gate, celebration wired to the isFrozenToday transition, dead StreakFreezeResponse type and endpoint constant cleaned up, i18n parity throughout.

@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.

Fifth commit is a clean unification pass. The two tier threshold ladders had genuinely drifted (web mapped 7–29 as 'strong', mobile as 'steady'; web had no 365-day tier branch, mobile reused a motivational-text key as a tier label) — extracting getStreakTierLabelKey into packages/shared fixes all of that in one shot. Both platforms now render the tier in a matching SettingsGroupRow + primary dot, the dead reserved CSS hooks are gone per the delete-unused-code standard, the useMemo wrapper is correctly dropped per React 19 compiler rules, all four tier keys are present in both locales, and the shared test covers every boundary value. The full PR is solid end to end.

Base automatically changed from chore/fix-mobile-type-check to main June 5, 2026 00:05
thomasluizon and others added 5 commits June 4, 2026 21:06
Make the Today-header StreakBadge interactive on both platforms so a tap
routes to the existing /streak page.

- Web: render the badge as a <button> (implicit button role) that calls
  router.push('/streak') via next/navigation, preserving the aria-label,
  frozen/active stroke logic, and hairline pill styling.
- Mobile: wrap the badge in a Pressable with accessibilityRole="button"
  and onPress → router.push('/streak') (expo-router). The press stops
  propagation so the Today header's outer go-to-today Pressable does not
  also fire — matching the sibling NotificationBell behavior.

Badge tests on both platforms assert the button role and that activating
the badge navigates to /streak; the mobile test also asserts propagation
is stopped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…108)

Remove the manual streak-freeze activation flow across web and mobile and
redesign the freeze section for the auto-activation model.

- Delete useActivateStreakFreeze (web + mobile), the activate-freeze
  Server Action, and the streakFreeze endpoint constant. Keep
  gamificationKeys.streak() for refetching.
- Redesign the freeze section (Linear-tactical): SectionLabel + AUTO chip,
  auto explainer, a banked charge gauge (frozen pips, mount fade/scale),
  Used-this-month and Next-freeze rows, and an auto-protected days list.
- Gate the section behind Pro (profile.hasProAccess); free users get a
  quiet hairline Pro line linking to /upgrade.
- Rewire StreakFreezeCelebration to fire on the isFrozenToday false->true
  transition (auto-applied freeze) instead of manual-activation success.
- Replace manual i18n keys under streakDisplay.freeze with the auto-model
  copy in both en and pt-BR; update streak page + hook tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The manual streak-freeze activation hook and endpoint were removed in this PR,
leaving streakFreezeResponseSchema and StreakFreezeResponse with zero references.
Deletes both per the delete-unused-code standard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The floating AUTO eyebrow chip had no referent and read as a technical
flag instead of communication. Removed it from the web and mobile streak
freeze sections and deleted the now-dead auto.chip i18n key. Rewrote the
explainer to a single sentence carrying the automatic behavior, dropping
the confusing "Nothing to tap" line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Web rendered the streak tier only as an invisible hero-tint CSS class
(overridden by inline styles), while mobile showed it as an explicit Stats
row -- and the two threshold ladders had drifted apart. Add a shared
getStreakTierLabelKey as the single source of truth, render the tier as an
explicit Stats row on both platforms, and remove the dead web tint code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thomasluizon
thomasluizon merged commit d905eca into main Jun 5, 2026
6 checks passed
@thomasluizon
thomasluizon deleted the issue-108 branch June 5, 2026 00:06

@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.

No new commits since the fifth-round approval. Full PR is solid end to end: tappable StreakBadge on both platforms, manual freeze activation removed end-to-end (hook, Server Action, endpoint constant, i18n keys all deleted), auto-freeze UI with charge-gauge + Pro gate at parity, isFrozenToday-transition celebration, dead StreakFreezeResponse type and endpoint constant cleaned up, AUTO chip removed, and getStreakTierLabelKey extracted into packages/shared as the single source of truth for tier thresholds (all four tier keys present in both locales, boundary values covered by the shared test). Production code is free of any, console.log, and hardcoded user-facing strings throughout.

@sonarqubecloud

sonarqubecloud Bot commented Jun 5, 2026

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.

Streak: auto-activate freeze on inactive day + tap streak icon to open streak page

1 participant