project-transition-state.ts55 lines · main
| 1 | export const FALLBACK_LONG_RUNNING_STATE_THRESHOLD_MINUTES = 10 |
| 2 | // Persist long enough for same-browser reloads, but not so long that a later transition reuses stale state. |
| 3 | export const MAX_PERSISTED_TRANSITION_AGE_HOURS = 24 |
| 4 | |
| 5 | const MS_PER_MINUTE = 60 * 1000 |
| 6 | const MS_PER_HOUR = 60 * MS_PER_MINUTE |
| 7 | |
| 8 | export const minutesToMilliseconds = (minutes: number) => minutes * MS_PER_MINUTE |
| 9 | export const hoursToMilliseconds = (hours: number) => hours * MS_PER_HOUR |
| 10 | |
| 11 | export const getPersistedTransitionStartTime = ( |
| 12 | storageKey: string, |
| 13 | now = Date.now(), |
| 14 | maxAgeMs = Number.POSITIVE_INFINITY |
| 15 | ) => { |
| 16 | if (typeof window === 'undefined') return now |
| 17 | |
| 18 | const existingValue = window.localStorage.getItem(storageKey) |
| 19 | |
| 20 | if (existingValue !== null) { |
| 21 | const parsedStartTime = Number(existingValue) |
| 22 | const elapsedMs = now - parsedStartTime |
| 23 | |
| 24 | if ( |
| 25 | Number.isFinite(parsedStartTime) && |
| 26 | parsedStartTime > 0 && |
| 27 | elapsedMs >= 0 && |
| 28 | elapsedMs <= maxAgeMs |
| 29 | ) { |
| 30 | return parsedStartTime |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | window.localStorage.setItem(storageKey, String(now)) |
| 35 | return now |
| 36 | } |
| 37 | |
| 38 | export const clearPersistedTransitionStartTime = (storageKey: string) => { |
| 39 | if (typeof window === 'undefined') return |
| 40 | |
| 41 | window.localStorage.removeItem(storageKey) |
| 42 | } |
| 43 | |
| 44 | export const getRemainingTransitionTimeMs = ({ |
| 45 | startTimeMs, |
| 46 | thresholdMs, |
| 47 | now = Date.now(), |
| 48 | }: { |
| 49 | startTimeMs: number |
| 50 | thresholdMs: number |
| 51 | now?: number |
| 52 | }) => { |
| 53 | const elapsedMs = now - startTimeMs |
| 54 | return Math.max(thresholdMs - elapsedMs, 0) |
| 55 | } |