Note Posting & Streak Calculation Architecture
TIP
Interactive Architecture Tour: Open Live Tour (Note Posting Flow)
This document describes the end-to-end processing pipeline for study notes, timezone-aware streak evaluation, and dynamic user level computation in Scripture Habit.
1. Streak Calculation Engine (api_internal/lib/streak-engine.ts)
To accurately evaluate consistency across timezones and personal study routines, the system applies a hybrid calendar and grace-period engine.
Core Rules
Upon note submission, the engine evaluates the user's configured timeZone (defaults to 'UTC'):
Local Date Resolution
Converts server timestamps to the user's localized calendar date string (e.g.,'2026-05-22') via the Node.jsIntlAPI.Same-Day Duplicate Guard
If the user'slastPostDatematches today's local date, the note is committed without incrementing the streak count (streakUpdated = false).Streak Continuity Evaluation
On a new calendar day, the streak increments by +1 if either condition holds:- Consecutive Calendar Day:
lastPostDateequals yesterday in the local timezone. - 36-Hour Grace Window: Time elapsed since the previous post is $\le$ 36 hours (
hoursSinceLastPost <= 36).
If neither condition is met, the streak resets to
1.- Consecutive Calendar Day:
All-Time Record Updating
When the updated streak surpasseshighestStreak, the record updates automatically.
Practical Scenarios
- Late-Night Routine: Posting Monday at 8:00 AM and Tuesday at 10:00 PM (38 hours later) preserves the streak because calendar days are consecutive.
- Transcontinental Travel: Crossing timezones and skipping a local calendar day is covered by the 36-hour physical window.
const isTargetDay = lastPostDate === yesterday;
const withinGracePeriod = lastTimeMillis > 0 && hoursSinceLastPost <= 36;
if (isTargetDay || withinGracePeriod) {
newStreak += 1;
isConsecutive = true;
} else {
newStreak = 1; // Reset streak
}2. Note Posting Transaction Flow
To guarantee state consistency, note submissions execute within an atomic Firestore transaction (db.runTransaction()):
- Membership Verification: Confirms group authorization via
userData.groupIds. - Streak Calculation: Resolves new streak counts and total study days via
StreakEngine. - Message Publishing: Writes the note payload into
groups/{id}/messages. - Master Note Archive: Saves a copy into
users/{uid}/notesfor the private library. - User Profile Update: Updates
totalNotes,lastPostAt,streakCount, anddaysStudiedCount. - Group Statistics Increment: Atomically increments counters via
FieldValue.increment.
3. Post-Transaction Background Tasks
To maintain low commit latency, non-blocking tasks execute asynchronously after the transaction commits:
- Push Notifications: Retrieves member FCM tokens and broadcasts multicast notifications.
- Unity Score Recomputation: Asynchronously recalculates the group's daily participation score.
4. Database Operation Costs (5-Member Group Example)
Operational read/write costs for a single note submission within a 5-member group (1 author + 4 peers):
Writes (Total: 8)
- Within Transaction (7 writes):
- User Profile (
/users/{uid}) - Master Note Archive (
/users/{uid}/notes/{noteId}) - Chat Message (
/groups/{gid}/messages/{messageId}) - Group Metadata (
/groups/{gid}) - Member Status (
/groups/{gid}/members/{uid}) - User Group State (
/users/{uid}/groupStates/{gid}) - Latest Message Cache (
/groups/{gid}/messages_latest/latest)
- User Profile (
- Background Async (1 write): 8. Group Unity Status (
/groups/{gid})
(Achieving a milestone adds +1 transaction write for the system announcement.)
Reads (Total: 7)
- Within Transaction (2 reads): User Profile, Latest Message Cache
- Background Async (5 reads): Group Metadata (1 read), Member FCM Tokens & Locales (4 reads)
5. Dynamic Level Computation
User level is derived dynamically from cumulative study days (daysStudiedCount):
$$\text{Level} = \lfloor \frac{\text{daysStudiedCount}}{7} \rfloor + 1$$
- Weekly Progression: Every 7 unique study days advances the user by 1 level.
- On-the-Fly Derivation: Computing levels on the client during render avoids database write overhead.
6. Sequence Diagram
Posting Sequence Breakdown
Payload Dispatch
The client submits reflection text, scripture references, and group visibility scopes to/api/messages/post-note.Timezone-Aware Evaluation
StreakEngineevaluates local date continuity and the 36-hour grace window against the user's historical records.Atomic Commit & Client Feedback
Personal archives, chat messages, and group rosters commit in a single transaction. Upon success, the client triggers celebration feedback.
7. Milestone Celebrations
To prevent demotivation when streaks break, the app celebrates Total Study Days (daysStudiedCount) as its primary retention driver:
- Regular Days: Publishes a standard note notification to the group feed.
- Milestones: Automatically publishes an official celebration card:
- Initial Milestone: Day 10
- Regular Cadence: Every 25 days thereafter (Day 25, 50, 75, 100, 125...).
See Milestone Celebrations & Retention Psychology for UX rationale.