Files
orca/mobile/src/notifications/push-payload.ts
T
Jinwoo Hong 341b13cf67 Restore mobile push and fix cold-start dismissals (#20068)
* Restore mobile push for delivery validation

* fix(mobile): register push task before headless startup

* Add authenticated mobile push test and fix iOS release entitlements

* Mock push-test transport in notification consent tests

* Fix slept workspace test for structured remount result

* Fix mobile notification review findings

* Pad Android notification icon to prevent square cropping

* fix(mobile): present visible Android data pushes in foreground

* test: use deterministic clock for teardown deadline

* fix(mobile): present foreground pushes through Expo public APIs

* fix(mobile): check push eligibility before foreground scheduling

* fix(mobile): register push from shared host connection lifecycle
2026-09-12 01:03:57 -04:00

44 lines
1.7 KiB
TypeScript

// Why two shapes: APNs nests Orca's fields under `orca` beside `aps`, while FCM
// carries them flat in `data` as strings. Both reach JS as the notification's
// `content.data`, so the reader accepts either and coerces the numeric fields.
export type OrcaPushPayload = {
readonly kind?: 'alert' | 'dismiss'
readonly hostFingerprint: string
readonly notificationId?: string
readonly notificationSeq?: number
readonly notificationEpoch?: string
readonly paneKey?: string
readonly worktreeId?: string
}
function readString(value: unknown): string | undefined {
return typeof value === 'string' && value.length > 0 ? value : undefined
}
function readSeq(value: unknown): number | undefined {
const raw = typeof value === 'number' ? value : Number(readString(value))
return Number.isFinite(raw) ? raw : undefined
}
export function readOrcaPushPayload(data: unknown): OrcaPushPayload | null {
if (!data || typeof data !== 'object') {
return null
}
const nested = (data as { orca?: unknown }).orca
const record = (nested && typeof nested === 'object' ? nested : data) as Record<string, unknown>
// The fingerprint is what makes this a gateway push; locally scheduled data never has one.
const hostFingerprint = readString(record.hostFingerprint)
if (!hostFingerprint) {
return null
}
return {
hostFingerprint,
...(record.kind === 'dismiss' || record.kind === 'alert' ? { kind: record.kind } : {}),
notificationId: readString(record.notificationId),
notificationSeq: readSeq(record.notificationSeq),
notificationEpoch: readString(record.notificationEpoch),
paneKey: readString(record.paneKey),
worktreeId: readString(record.worktreeId)
}
}