M5: push (FCM + ntfy) + offline catch-up + background notifications
Server (gateway-plugin): - push.py: FCM (HTTP v1 service-account / legacy key) + ntfy backends; NtfyBackend.server_url for app discovery - adapter.py: push on offline broadcast + high-priority push when live; fcm.register; server_caps.push_ntfy_server; outbox now ALWAYS appends so a reconnecting app catches up on live-delivered frames (fixes empty chat after notification tap / activity recreation) - protocol.py / ws_server.py / outbox.py / plugin.yaml: M5 frames + env vars App (Kotlin CMP): - Protocol.kt: notification / fcm.register / sync frames + push caps - GatewayClient.kt: hello carries push creds; auto-sync on reconnect - IrisController.kt: banners, deep-link, notifyMessageIfBackgrounded (system notification on a regular reply when backgrounded) - Android: PlatformPush, AppBridge, IrisNotifications, NtfyListenerService, IrisFirebaseMessagingService, AndroidPush, AndroidSecureStore - Desktop: DesktopPush, DesktopSecureStore - build files + manifest (permissions, services, deep-links) Tests: 35-test tests/gateway/test_android.py suite passes (incl. new regression test_live_delivered_frame_still_parked_for_sync). E2E verified on device: push fire, reconnect sync, background notification, and message replay after ChatStore reset.
This commit is contained in:
1 parent
913ee91024
commit
2ecfe1c05c
28 files changed
+1739
-51
No files matched your search
@@ -6,6 +6,7 @@ import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -13,6 +14,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import iris.data.SecureStore
|
||||
import iris.net.GatewayClient
|
||||
import iris.platform.setActiveController
|
||||
import iris.state.IrisController
|
||||
import iris.ui.screens.ChatScreen
|
||||
import iris.ui.screens.ConnectScreen
|
||||
@@ -33,10 +35,25 @@ private val IrisDark = darkColorScheme(
|
||||
* settings, and media (docs/10-android-app.md).
|
||||
*/
|
||||
@Composable
|
||||
fun IrisApp(store: SecureStore) {
|
||||
fun IrisApp(
|
||||
store: SecureStore,
|
||||
deepLinkChatId: String? = null,
|
||||
deepLinkThreadId: String? = null,
|
||||
) {
|
||||
val controller = remember(store) { IrisController(store) }
|
||||
DisposableEffect(controller) {
|
||||
onDispose { controller.dispose() }
|
||||
setActiveController(controller)
|
||||
onDispose {
|
||||
setActiveController(null)
|
||||
controller.dispose()
|
||||
}
|
||||
}
|
||||
// M5: open the chat referenced by a push notification tap (applied now or
|
||||
// on the next hello.ack, whichever is later).
|
||||
LaunchedEffect(deepLinkChatId, deepLinkThreadId) {
|
||||
if (!deepLinkChatId.isNullOrBlank()) {
|
||||
controller.openDeepLink(deepLinkChatId, deepLinkThreadId)
|
||||
}
|
||||
}
|
||||
val state by controller.client.state.collectAsState()
|
||||
|
||||
|
||||
Reference in new issue
Block a user