git.iko.soy/parseable-android
on 3b7ea33 help sign in
197 lines 7.1 kB history raw
1package com.parseable.android.ui.navigation
3import androidx.compose.runtime.Composable
4import androidx.navigation.NavHostController
5import androidx.navigation.NavType
6import androidx.navigation.compose.NavHost
7import androidx.navigation.compose.composable
8import androidx.navigation.navArgument
9import androidx.navigation.navDeepLink
10import androidx.compose.runtime.LaunchedEffect
11import com.parseable.android.data.repository.ParseableRepository
12import com.parseable.android.ui.screens.alerts.AlertsScreen
13import com.parseable.android.ui.screens.login.LoginScreen
14import com.parseable.android.ui.screens.logviewer.LogViewerScreen
15import com.parseable.android.ui.screens.logviewer.StreamInfoScreen
16import com.parseable.android.ui.screens.about.AboutScreen
17import com.parseable.android.ui.screens.settings.SettingsScreen
18import com.parseable.android.ui.screens.streams.StreamsScreen
19import java.net.URLDecoder
20import java.net.URLEncoder
22object Routes {
23 const val LOGIN = "login"
24 const val STREAMS = "streams"
25 const val LOG_VIEWER = "log_viewer/{streamName}"
26 const val STREAM_INFO = "stream_info/{streamName}"
27 const val ALERTS = "alerts"
28 const val SETTINGS = "settings"
29 const val ABOUT = "about"
31 fun login(sessionExpired: Boolean = false) =
32 if (sessionExpired) "login?sessionExpired=true" else LOGIN
33 fun logViewer(streamName: String) =
34 "log_viewer/${URLEncoder.encode(streamName, "UTF-8")}"
35 fun streamInfo(streamName: String) =
36 "stream_info/${URLEncoder.encode(streamName, "UTF-8")}"
39@Composable
40fun ParseableNavGraph(
41 navController: NavHostController,
42 startDestination: String,
43 repository: ParseableRepository,
44) {
45 NavHost(
46 navController = navController,
47 startDestination = startDestination,
48 ) {
49 composable(
50 route = "${Routes.LOGIN}?sessionExpired={sessionExpired}",
51 arguments = listOf(navArgument("sessionExpired") {
52 type = NavType.BoolType
53 defaultValue = false
54 }),
55 ) { backStackEntry ->
56 val sessionExpired = backStackEntry.arguments?.getBoolean("sessionExpired") ?: false
57 LoginScreen(
58 onLoginSuccess = {
59 navController.navigate(Routes.STREAMS) {
60 popUpTo(Routes.LOGIN) { inclusive = true }
61 }
62 },
63 sessionExpired = sessionExpired,
64 )
65 }
67 composable(
68 route = Routes.STREAMS,
69 deepLinks = listOf(navDeepLink { uriPattern = "parseable://streams" }),
70 ) {
71 if (!repository.isConfigured) {
72 LaunchedEffect(Unit) {
73 navController.navigate(Routes.LOGIN) {
74 popUpTo(0) { inclusive = true }
75 }
76 }
77 return@composable
78 }
79 StreamsScreen(
80 onStreamClick = { streamName ->
81 navController.navigate(Routes.logViewer(streamName)) {
82 launchSingleTop = true
83 }
84 },
85 onAlertsClick = {
86 navController.navigate(Routes.ALERTS) {
87 launchSingleTop = true
88 }
89 },
90 onSettingsClick = {
91 navController.navigate(Routes.SETTINGS) {
92 launchSingleTop = true
93 }
94 },
95 onLogout = {
96 navController.navigate(Routes.LOGIN) {
97 popUpTo(0) { inclusive = true }
98 }
99 },
100 )
101 }
103 composable(
104 route = Routes.LOG_VIEWER,
105 arguments = listOf(navArgument("streamName") { type = NavType.StringType }),
106 deepLinks = listOf(navDeepLink { uriPattern = "parseable://stream/{streamName}" }),
107 ) { backStackEntry ->
108 // Guard: redirect to login if the API client isn't configured
109 if (!repository.isConfigured) {
110 LaunchedEffect(Unit) {
111 navController.navigate(Routes.LOGIN) {
112 popUpTo(0) { inclusive = true }
113 }
114 }
115 return@composable
116 }
117 val streamName = URLDecoder.decode(
118 backStackEntry.arguments?.getString("streamName") ?: "", "UTF-8"
119 )
120 LogViewerScreen(
121 streamName = streamName,
122 onBack = { navController.popBackStack() },
123 onStreamInfo = { name ->
124 navController.navigate(Routes.streamInfo(name)) {
125 launchSingleTop = true
126 }
127 },
128 )
129 }
131 composable(
132 route = Routes.STREAM_INFO,
133 arguments = listOf(navArgument("streamName") { type = NavType.StringType }),
134 ) { backStackEntry ->
135 val streamName = URLDecoder.decode(
136 backStackEntry.arguments?.getString("streamName") ?: "", "UTF-8"
137 )
138 StreamInfoScreen(
139 streamName = streamName,
140 onBack = { navController.popBackStack() },
141 onStreamDeleted = {
142 navController.popBackStack(Routes.STREAMS, inclusive = false)
143 },
144 )
145 }
147 composable(
148 route = Routes.ALERTS,
149 deepLinks = listOf(navDeepLink { uriPattern = "parseable://alerts" }),
150 ) {
151 if (!repository.isConfigured) {
152 LaunchedEffect(Unit) {
153 navController.navigate(Routes.LOGIN) {
154 popUpTo(0) { inclusive = true }
155 }
156 }
157 return@composable
158 }
159 AlertsScreen(
160 onBack = { navController.popBackStack() },
161 )
162 }
164 composable(Routes.SETTINGS) {
165 if (!repository.isConfigured) {
166 LaunchedEffect(Unit) {
167 navController.navigate(Routes.LOGIN) {
168 popUpTo(0) { inclusive = true }
169 }
170 }
171 return@composable
172 }
173 SettingsScreen(
174 onBack = { navController.popBackStack() },
175 onAboutClick = {
176 navController.navigate(Routes.ABOUT) {
177 launchSingleTop = true
178 }
179 },
180 )
181 }
183 composable(Routes.ABOUT) {
184 if (!repository.isConfigured) {
185 LaunchedEffect(Unit) {
186 navController.navigate(Routes.LOGIN) {
187 popUpTo(0) { inclusive = true }
188 }
189 }
190 return@composable
191 }
192 AboutScreen(
193 onBack = { navController.popBackStack() },
194 )
195 }
196 }