Add push notification functionality with tests and digest scheduler
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 2m14s

- Implemented push subscription API with tests for subscribing and unsubscribing users.
- Created web push notification tests triggered by child actions.
- Added digest scheduler to send email digests to users at 9 PM local time.
- Developed utility functions for creating and validating digest action tokens.
- Integrated web push sender to handle sending notifications to users.
- Added service worker for handling push notifications in the frontend.
- Created a push opt-in component for user notification preferences.
- Implemented tests for the push opt-in component to ensure correct behavior.
- Updated frontend services to manage push subscriptions and permissions.
This commit is contained in:
2026-04-15 21:56:10 -04:00
parent 0d50a324a3
commit ad2bdf4c4f
47 changed files with 3177 additions and 197 deletions

View File

@@ -4,6 +4,7 @@ const hasLocalStorage =
typeof localStorage !== 'undefined' && typeof localStorage.getItem === 'function'
const AUTH_SYNC_EVENT_KEY = 'authSyncEvent'
const PARENT_AUTH_KEY = 'parentAuth'
const PARENT_RETURN_URL_KEY = 'parentReturnUrl'
const PARENT_AUTH_EXPIRY_NON_PERSISTENT = 60_000 // 1 minute
const PARENT_AUTH_EXPIRY_PERSISTENT = 172_800_000 // 2 days
@@ -71,6 +72,33 @@ export function enforceParentExpiry() {
runExpiryCheck()
}
export function setPendingReturnUrl(url: string) {
if (!url.startsWith('/parent')) return
if (hasLocalStorage) {
localStorage.setItem(PARENT_RETURN_URL_KEY, url)
}
}
export function consumePendingReturnUrl(): string | null {
if (!hasLocalStorage) return null
const url = localStorage.getItem(PARENT_RETURN_URL_KEY)
if (url) {
localStorage.removeItem(PARENT_RETURN_URL_KEY)
}
return url
}
export function hasPendingReturnUrl(): boolean {
if (!hasLocalStorage) return false
return localStorage.getItem(PARENT_RETURN_URL_KEY) !== null
}
export function clearPendingReturnUrl() {
if (hasLocalStorage) {
localStorage.removeItem(PARENT_RETURN_URL_KEY)
}
}
export function authenticateParent(persistent: boolean) {
const duration = persistent ? PARENT_AUTH_EXPIRY_PERSISTENT : PARENT_AUTH_EXPIRY_NON_PERSISTENT
parentAuthExpiresAt.value = Date.now() + duration