git.iko.soy/opencode-android
on 5244821 help sign in
71 lines 2.8 kB history raw
1package soy.iko.opencode.ui
3import androidx.activity.compose.BackHandler
4import androidx.compose.animation.AnimatedContent
5import androidx.compose.animation.slideInHorizontally
6import androidx.compose.animation.slideOutHorizontally
7import androidx.compose.animation.togetherWith
8import androidx.compose.foundation.layout.fillMaxSize
9import androidx.compose.foundation.layout.padding
10import androidx.compose.material3.Scaffold
11import androidx.compose.material3.SnackbarDuration
12import androidx.compose.material3.SnackbarHost
13import androidx.compose.material3.SnackbarHostState
14import androidx.compose.runtime.Composable
15import androidx.compose.runtime.LaunchedEffect
16import androidx.compose.runtime.remember
17import androidx.compose.ui.Modifier
18import soy.iko.opencode.core.Event
19import soy.iko.opencode.core.Screen
20import soy.iko.opencode.core.UiState
21import soy.iko.opencode.ui.screens.ChatScreen
22import soy.iko.opencode.ui.screens.ConnectScreen
23import soy.iko.opencode.ui.screens.SessionsScreen
25@Composable
26fun OpencodeApp(state: UiState, dispatch: (Event) -> Unit) {
27 val snackbar = remember { SnackbarHostState() }
29 LaunchedEffect(state.error) {
30 state.error?.let {
31 snackbar.showSnackbar(
32 message = it,
33 actionLabel = "Dismiss",
34 duration = SnackbarDuration.Short,
35 )
36 // Always clear the error from state after showing.
37 dispatch(Event.DismissError)
38 }
39 }
41 BackHandler(enabled = state.screen != Screen.Connect) {
42 when (state.screen) {
43 Screen.Chat -> dispatch(Event.NavigateToSessions)
44 Screen.Sessions -> dispatch(Event.NavigateToConnect)
45 Screen.Connect -> Unit
46 }
47 }
49 Scaffold(snackbarHost = { SnackbarHost(snackbar) }) { padding ->
50 AnimatedContent(
51 targetState = state.screen,
52 modifier = Modifier.fillMaxSize().padding(padding),
53 transitionSpec = {
54 // Compare screen ordinals to decide slide direction: forward navigation
55 // (Connect → Sessions → Chat) slides in from the right, back slides from the left.
56 if (targetState.ordinal > initialState.ordinal) {
57 slideInHorizontally { it } togetherWith slideOutHorizontally { -it }
58 } else {
59 slideInHorizontally { -it } togetherWith slideOutHorizontally { it }
60 }
61 },
62 label = "screen",
63 ) { screen ->
64 when (screen) {
65 Screen.Connect -> ConnectScreen(state, dispatch)
66 Screen.Sessions -> SessionsScreen(state, dispatch)
67 Screen.Chat -> ChatScreen(state, dispatch)
68 }
69 }
70 }