dailyWorkAreaStore.ts
Purpose
This module manages the persistent record of user-defined work area circles and the daily prompt gate that determines whether the user has been asked to set their work area today. All data is stored in AsyncStorage under a single versioned key.
Its responsibilities include:
- Saving a new work area circle for a given local calendar day
- Loading the work area circle for a given day
- Recording that the daily work area prompt has been handled for a given day
- Checking whether the prompt has been handled today
- Finding the most recent prior work area within a configurable lookback window
- Providing a single composite gate-state query used by
DailyWorkAreaContext - Validating all coordinate and radius inputs before any write
- Normalizing and deduplicating persisted state on every load
Invariants
Local Day Keys
All date-based operations use toLocalDayKey(date) to derive a string key of the form YYYY-MM-DD in the device’s local timezone.
Passing a Date object or ISO string is accepted; both are converted to the local day before storage or lookup.
One Circle Per Day
saveDailyWorkAreaCircle replaces any existing circle for the same day rather than appending.
After write, circles are re-sorted in descending dayKey order so the most recent entry is always first.
Deduplication on Load
normalizeState deduplicates circles by dayKey on every load, keeping the entry with the latest createdAt value when duplicates exist.
handledDayKeys is also deduplicated and sorted on load.
Coordinate and Radius Validation
saveDailyWorkAreaCircle validates all three fields before reading state:
| Field | Constraint |
|---|---|
centerLat | Finite, in [−90, 90] |
centerLon | Finite, in [−180, 180] |
radiusKm | Finite, positive |
Violations throw a RangeError.
Graceful Corrupt State
All AsyncStorage reads and JSON parses are wrapped in try/catch.
Any read or parse failure returns EMPTY_STATE rather than propagating, ensuring the module never crashes due to corrupted persisted data.
maxAgeDays Validation
findMostRecentPriorWorkArea requires maxAgeDays to be a positive integer.
Invalid values throw a RangeError.
Constants
| Constant | Value | Description |
|---|---|---|
STORE_KEY | "heatsafe_daily_work_area_v1" | AsyncStorage key |
DEFAULT_WORK_AREA_LOOKBACK_DAYS | 7 | Default prior work area search window (days) |
Exports
DailyWorkAreaCircle
type DailyWorkAreaCircle = {
dayKey: string;
centerLat: number;
centerLon: number;
radiusKm: number;
createdAt: string;
};
saveDailyWorkAreaCircle(...)
async function saveDailyWorkAreaCircle(
circle: Omit<DailyWorkAreaCircle, "dayKey" | "createdAt">,
date?: Date | string,
): Promise<DailyWorkAreaCircle>
Saves a work area circle for the local day derived from date (default: now). Returns the saved circle including the generated dayKey and createdAt. Throws RangeError on invalid inputs.
loadDailyWorkAreaCircle(...)
async function loadDailyWorkAreaCircle(date?: Date | string): Promise<DailyWorkAreaCircle | null>
Returns the circle saved for the local day of date, or null if none exists.
markDailyWorkAreaHandled(...)
async function markDailyWorkAreaHandled(date?: Date | string): Promise<void>
Records that the daily work area prompt has been handled for date’s local day, without saving a circle.
hasHandledDailyWorkAreaPrompt(...)
async function hasHandledDailyWorkAreaPrompt(date?: Date | string): Promise<boolean>
Returns true if the prompt has been handled for date’s local day.
findMostRecentPriorWorkArea(...)
async function findMostRecentPriorWorkArea(
date?: Date | string,
maxAgeDays?: number,
): Promise<DailyWorkAreaCircle | null>
Returns the most recent circle from a prior day (not today) within maxAgeDays, or null if none is found. Throws RangeError if maxAgeDays is not a positive integer.
getDailyWorkAreaGateState(...)
async function getDailyWorkAreaGateState(
date?: Date | string,
maxAgeDays?: number,
): Promise<{
dayKey: string;
handledToday: boolean;
todayCircle: DailyWorkAreaCircle | null;
fallbackCircle: DailyWorkAreaCircle | null;
}>
Composite query returning all gate-state fields in a single call. Runs hasHandledDailyWorkAreaPrompt, loadDailyWorkAreaCircle, and findMostRecentPriorWorkArea in parallel.