Skip to content

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-story API)
  • 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}

FieldTypeDescription
currentStreaknumberCurrent consecutive day count
lastActivityDatestringLast activity date in YYYY-MM-DD (UTC)

Files

FileRole
lib/firebase/updateStreak.tsCore logic — Firestore transaction, called server-side
app/api/update-streak/route.tsPOST endpoint called by the client after login
lib/hooks/useStreak.tsClient hook — listens to users/{uid} in real time
components/providers/AuthProvider.tsxCalls /api/update-streak after popup and redirect sign-in
app/api/save-story/route.tsAlso calls updateStreak when a story is saved
app/(dashboard)/dashboard/page.tsxDisplays 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 time

Notes

  • Dates are UTC (new Date().toISOString().slice(0, 10)) — consistent across timezones
  • callUpdateStreak failures 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

Released under the MIT License.