package iris import androidx.compose.foundation.background import androidx.compose.material3.Surface 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 import androidx.compose.ui.Modifier 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 import iris.ui.theme.IrisColors import iris.ui.theme.IrisTheme /** * Root composable shared by the Android and Desktop shells. * * M1: routes between the Connect screen (unpaired / auth failed) and the * Chat screen (paired). Later milestones add the channel list, search, * settings, and media (docs/10-android-app.md). */ @Composable fun IrisApp( store: SecureStore, deepLinkChatId: String? = null, deepLinkThreadId: String? = null, ) { val controller = remember(store) { IrisController(store) } DisposableEffect(controller) { 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() IrisTheme { Surface(modifier = Modifier.background(IrisColors.background)) { val s = state when (s) { GatewayClient.State.Disconnected -> ConnectScreen(controller, prefillUrl = store.serverUrl, prefillToken = store.token) is GatewayClient.State.AuthFailed -> ConnectScreen( controller, prefillUrl = store.serverUrl, prefillToken = store.token, initialError = "Pairing rejected: ${s.message}", ) // Connecting / Reconnecting / Connected all render the chat; the header // StatusChip + connection banner show the link state without // blocking the view (M7's full-screen spinner is gone). else -> ChatScreen(controller) } } } }