Reading Streak
Overview
A daily streak counter that rewards users for logging in every day. The streak increments once per calendar day (UTC) on login and is displayed on the dashboard.
How It Works
Trigger
- Streak updates on every login (Google Sign-In)
- Also updates when a story is saved (bonus trigger via
save-storyAPI) - Deduplication: if the user already logged in today, the streak is not incremented again
Logic
if lastActivityDate == today → no change
if lastActivityDate == yesterday → currentStreak + 1
else → reset to 1 (streak broken)Data stored on users/{uid}
| Field | Type | Description |
|---|---|---|
currentStreak | number | Current consecutive day count |
lastActivityDate | string | Last activity date in YYYY-MM-DD (UTC) |
Files
| File | Role |
|---|---|
lib/firebase/updateStreak.ts | Core logic — Firestore transaction, called server-side |
app/api/update-streak/route.ts | POST endpoint called by the client after login |
lib/hooks/useStreak.ts | Client hook — listens to users/{uid} in real time |
components/providers/AuthProvider.tsx | Calls /api/update-streak after popup and redirect sign-in |
app/api/save-story/route.ts | Also calls updateStreak when a story is saved |
app/(dashboard)/dashboard/page.tsx | Displays currentStreak in the 🔥 stat card |
Flow
User taps "Sign in with Google"
→ AuthProvider.signInWithGoogle()
→ Real browser (desktop or mobile Chrome/Safari):
signInWithPopup → callUpdateStreak(uid)
→ WebView (Facebook, Instagram, Line, etc.):
signInWithRedirect → on return: getRedirectResult → callUpdateStreak(uid)
→ POST /api/update-streak { userId }
→ updateStreak(userId) [Admin SDK transaction]
→ reads users/{uid}
→ increments or resets currentStreak
→ writes currentStreak + lastActivityDate
→ useStreak hook picks up change via onSnapshot
→ Dashboard 🔥 stat updates in real timeNotes
- Dates are UTC (
new Date().toISOString().slice(0, 10)) — consistent across timezones callUpdateStreakfailures are silently swallowed — streak is non-critical, won't block login- Streak resets to 1 (not 0) on the first day back after a break
