Architecture of the Daily Work Area Popup

High-level architecture and technology choices for SAFER.

Architecture: Daily Work Area Popup

Summary

The smallest useful design moves the daily popup gate from app launch semantics to screen popup semantics. The relevant work area screen owns the eligibility check when it becomes active. A tiny local persistence helper stores the last local date on which the popup was actually shown.

No server change, navigation redesign, or popup UI redesign is needed.

Inputs From PRD

Architecture-shaping inputs:

  • The popup must appear once per local calendar day when the relevant screen is reached.
  • App launch alone must not mark the popup as shown.
  • The shown date must update only after the popup is actually presented.
  • Missing, corrupt, or unreadable local state should allow the popup.
  • Existing popup content and dismissal behavior remain unchanged.
  • The change should be small and testable without real wall-clock dependence.

Existing Context

The mobile app is an Expo/React Native app with bottom-tab navigation. Existing small local state uses @react-native-async-storage/async-storage, including profile and session state.

The current repository does not expose an obvious daily work area popup implementation by name. The architecture therefore defines the intended ownership and contracts for the future code change rather than assuming exact current filenames.

System Boundaries

Owned by this design:

  • Daily work area popup eligibility.
  • Local shown-date persistence.
  • Work area screen focus-time decision to show the popup.

Not owned by this design:

  • App startup services.
  • Background telemetry/session behavior.
  • Weather, activity, prediction, and sync pipelines.
  • Popup visual design and copy.
  • Server persistence.

Module Design

Daily Popup State Helper

Responsibility:

  • Read and write the local date on which the daily work area popup was last shown.
  • Convert the current time into a stable local date key.
  • Treat missing, corrupt, or unreadable state as eligible.

Interface:

  • getLocalDateKey(now?: Date): string
  • hasDailyWorkAreaPopupShownToday(now?: Date): Promise<boolean>
  • markDailyWorkAreaPopupShown(now?: Date): Promise<void>

Implementation:

  • Store one AsyncStorage string key, for example heatsafe_daily_work_area_popup_shown_date_v1.
  • Store dates as YYYY-MM-DD local-date keys.
  • Keep parsing strict: any unexpected stored value is treated as not shown.
  • Accept an optional Date in helper functions so tests can inject time without mocking the global clock.

Depends on:

  • @react-native-async-storage/async-storage.

Used by:

  • The relevant work area screen.

Verification surface:

  • Date-key generation.
  • Missing/corrupt storage behavior.
  • Read/write behavior.
  • Failure-open behavior when storage read fails.

Work Area Screen Popup Gate

Responsibility:

  • Check popup eligibility when the screen becomes active.
  • Present the existing popup only if it has not been shown for the current local day.
  • Mark the popup as shown after presentation begins.
  • Avoid duplicate presentation if focus or render effects run repeatedly.

Interface:

  • Existing screen focus or mount lifecycle.
  • Existing popup open/present function.
  • Existing popup dismissal controls.

Implementation:

  • Prefer the existing navigation focus hook if this is a tab or navigated screen.
  • Keep an in-memory guard for the current screen instance while the async eligibility check is in flight.
  • Do not call the state helper from app startup.
  • Do not update the persisted date until the code path actually opens the popup.

Depends on:

  • Daily popup state helper.
  • Existing popup presentation component or state.
  • React Navigation focus lifecycle when applicable.

Used by:

  • Mobile users visiting the work area screen.

Verification surface:

  • Screen first-focus behavior.
  • Re-focus behavior on the same day.
  • Next-day behavior.
  • Duplicate focus/rerender guard.

Data And Control Flow

  1. User opens the app.
  2. App startup services run as they do today.
  3. App startup does not read or write the daily work area popup shown date.
  4. User navigates to the relevant work area screen.
  5. The screen focus handler asks the state helper whether the popup has shown for the current local date.
  6. If the helper returns true, the screen does nothing.
  7. If the helper returns false, the screen opens the existing popup and then writes today’s local date as shown.
  8. Later visits to the same screen on the same local date read the stored date and suppress the popup.
  9. A visit on a later local date is eligible again.

State And Persistence

State:

  • One persisted local date string representing the last day the popup was shown.
  • One transient in-memory guard in the screen to prevent duplicate presentation while an eligibility check is pending or just completed.

Persistence lifecycle:

  • Created the first time the popup is shown.
  • Overwritten when the popup is shown on a later local date.
  • Not cleared as part of normal app use.

Failure behavior:

  • Read failure: allow the popup to show.
  • Invalid stored value: allow the popup to show.
  • Write failure after popup presentation: do not crash; a later screen visit may show the popup again.

Error Handling And Failure Modes

  • AsyncStorage read error: treat as not shown and continue.
  • AsyncStorage write error: log only if existing logging patterns make that natural; do not block the popup.
  • Multiple focus events: the in-memory guard prevents duplicate popup opens.
  • Date changes while the app stays open: the next screen focus should compare against the new local date.
  • Device time changes: behavior follows the current device local date because this is user-facing local-day behavior.

Interfaces And Contracts

  • Callers provide no user data or server data.
  • The helper returns only a boolean eligibility answer and writes only a date key.
  • The popup screen does not expose launch state, session state, or app-open timestamps through this contract.
  • The shown-date key means “popup was presented for this local date,” not “app opened on this local date.”

Verification Surface

  • Unit tests for the date-key and AsyncStorage helper.
  • Component or screen tests for focus-driven popup presentation.
  • Regression checks proving app launch does not write the shown date.
  • Manual smoke test on the relevant work area screen.

Alternatives Considered

  • Keep app-launch tracking and add exceptions: rejected because app launch is not the correct event and would preserve the bug-prone coupling.
  • Store a full timestamp instead of a local date key: rejected for v0 because the requirement is daily local behavior, and a date key is simpler to inspect and test.
  • Server-side tracking: rejected because this is per-device UI prompt state and does not require sync.

Deferred Decisions

None.

Architecture Risks

  • The actual popup code may already have a state helper with a different name; implementation should reuse the existing local pattern if present rather than duplicating storage keys.
  • If the work area screen is mounted but not focused, a mount-only effect may still be too early; implementation should use the lifecycle that best corresponds to “user reached the screen.”
  • If writing the shown date happens before popup presentation state is committed, a crash or interrupted render could suppress the popup incorrectly.