ultisuite-client/lib/native/push.ts
R3D347HR4Y d6d18f911b
Some checks failed
E2E / Playwright e2e (push) Has been cancelled
Lots of stuff and mobile app
2026-06-17 00:13:28 +02:00

82 lines
2.5 KiB
TypeScript

/**
* App-side push notifications.
*
* The native push plugin (FCM on Android / APNs on iOS) obtains the device
* token and either caches it in the secure store under "push_token" or emits a
* `ulti://push-token` event. After login we register it with the backend; on
* logout we unregister it.
*
* Backend contract:
* POST /api/v1/devices/register { platform, app, push_token, device_id? } -> { id }
* POST /api/v1/devices/unregister { push_token }
*/
import { apiClient } from "@/lib/api/client"
import { invoke, listen } from "@/lib/native/bridge"
import { isTauriRuntime, SUITE_APP } from "@/lib/platform"
import { readPushToken, writePushToken } from "@/lib/native/secure-store"
type PushRegistration = { platform: string; token: string | null }
function normalizePlatform(os: string): "ios" | "android" | null {
if (os === "ios") return "ios"
if (os === "android") return "android"
return null
}
let started = false
async function registerToken(platform: "ios" | "android", token: string) {
await writePushToken(token)
await apiClient.post<{ id: string }>("/devices/register", {
platform,
app: SUITE_APP,
push_token: token,
})
}
/**
* Call after a successful login. Idempotent. Asks the native layer for the push
* token and registers it; also subscribes to late token deliveries.
*/
export async function registerPushAfterLogin(): Promise<void> {
if (!isTauriRuntime()) return
// Late token delivery (e.g. permission granted after first launch).
if (!started) {
started = true
await listen("ulti://push-token", (payload) => {
const token = typeof payload === "string" ? payload : null
const platform = normalizePlatform(
(navigator?.platform || "").toLowerCase().includes("android")
? "android"
: "ios"
)
if (token && platform) void registerToken(platform, token)
})
}
try {
const reg = await invoke<PushRegistration>("plugin:ulti-core|push_register")
if (!reg) return
const platform = normalizePlatform(reg.platform)
if (platform && reg.token) {
await registerToken(platform, reg.token)
}
} catch {
/* push optional — never block login */
}
}
/** Call before clearing the session. */
export async function unregisterPushOnLogout(): Promise<void> {
if (!isTauriRuntime()) return
try {
const token = await readPushToken()
if (token) {
await apiClient.post("/devices/unregister", { push_token: token })
}
} catch {
/* best effort */
}
}