/**
* JS bridge into the Android WebView app (window.NaviAndroid, added by
* MainActivity's NaviBridge). Web push APIs don't exist inside a WebView,
* so the app shows notifications natively; this composable is the only
* place that touches the bridge surface.
*/
/** The bridge object or null — never throws in non-WebView contexts. */
export function nativeBridge() {
return typeof window !== 'undefined' ? window.NaviAndroid ?? null : null
}
/** Bridge present with the full surface. */
export function bridgeAvailable() {
const b = nativeBridge()
return !!b && typeof b.notify === 'function' && typeof b.enableBackgroundMode === 'function'
}
const FLAG_KEY = 'navi_push_android'
/** Whether the user turned native push on (persisted locally, no server side). */
export function nativePushFlag() {
try {
return localStorage.getItem(FLAG_KEY) === '1'
} catch {
return false
}
}
function setNativePushFlag(on) {
try {
if (on) localStorage.setItem(FLAG_KEY, '1')
else localStorage.removeItem(FLAG_KEY)
} catch { /* storage unavailable — state stays in-memory */ }
}
/**
* Run the bridge's permission request through the 'navi-perm-result'
* CustomEvent the app posts back with {detail: granted}.
*/
export function requestPermissionViaBridge() {
const bridge = nativeBridge()
if (!bridge || typeof bridge.requestNotificationPermission !== 'function') {
return Promise.resolve(false)
}
if (bridge.hasNotificationPermission?.()) return Promise.resolve(true)
return new Promise((resolve) => {
const onResult = (e) => {
clearTimeout(timer)
window.removeEventListener('navi-perm-result', onResult)
resolve(!!(e && e.detail))
}
// Safety valve: if the activity never answers, fail closed.
const timer = setTimeout(() => {
window.removeEventListener('navi-perm-result', onResult)
resolve(false)
}, 30000)
window.addEventListener('navi-perm-result', onResult)
bridge.requestNotificationPermission()
})
}
/**
* Raise a native turn-complete notification via the bridge — only when the
* user turned native push on and the app window is not visible (the
* foreground session already shows everything on screen).
*/
export function notifyNativeTurn(title, body, url) {
const bridge = nativeBridge()
if (!bridge || typeof bridge.notify !== 'function') return
if (typeof document !== 'undefined' && !document.hidden) return
let enabled
try {
enabled = localStorage.getItem(FLAG_KEY) === '1'
} catch {
return
}
if (!enabled) return
try {
bridge.notify(String(title), String(body), String(url ?? '/'))
} catch (err) {
console.warn('[nativeBridge] notify failed', err)
}
}