feat: refactor navigation structure by replacing AppNavigation with MainApp and updating ListDetailScreen and ListsScreen for improved functionality

This commit is contained in:
2025-11-11 19:54:47 +01:00
parent c53e234efe
commit 607a0c4fcb
4 changed files with 146 additions and 21 deletions
@@ -10,7 +10,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.collabtable.app.data.preferences.PreferencesManager import com.collabtable.app.data.preferences.PreferencesManager
import com.collabtable.app.ui.navigation.AppNavigation import com.collabtable.app.ui.navigation.MainApp
import com.collabtable.app.ui.theme.CollabTableTheme import com.collabtable.app.ui.theme.CollabTableTheme
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@@ -34,7 +34,7 @@ class MainActivity : ComponentActivity() {
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background, color = MaterialTheme.colorScheme.background,
) { ) {
AppNavigation() MainApp()
} }
} }
} }
@@ -0,0 +1,131 @@
package com.collabtable.app.ui.navigation
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import androidx.navigation.NavType
import com.collabtable.app.data.preferences.PreferencesManager
import com.collabtable.app.ui.screens.ListDetailScreen
import com.collabtable.app.ui.screens.ListsScreen
import com.collabtable.app.ui.screens.LogsScreen
import com.collabtable.app.ui.screens.ServerSetupScreen
import com.collabtable.app.ui.screens.SettingsScreen
private object Routes {
const val Setup = "server_setup"
const val MainRoot = "main_root"
const val Tables = "tables"
const val Settings = "settings"
const val ListDetail = "list/{listId}"
const val Logs = "logs"
}
@Composable
fun MainApp() {
val context = LocalContext.current
val prefs = remember { PreferencesManager.getInstance(context) }
// Show setup flow until completed; then show main app with bottom nav
if (prefs.isFirstRun()) {
val navController = rememberNavController()
NavHost(navController, startDestination = Routes.Setup) {
composable(Routes.Setup) {
ServerSetupScreen(
onSetupComplete = {
// Mark not first run and trigger recomposition to show main UI
prefs.setIsFirstRun(false)
},
)
}
}
return
}
val navController = rememberNavController()
val backStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = backStackEntry?.destination?.route
Scaffold(
bottomBar = {
NavigationBar {
NavigationBarItem(
selected = currentRoute == Routes.Tables || currentRoute?.startsWith("list/") == true,
onClick = {
navController.navigate(Routes.Tables) {
popUpTo(navController.graph.findStartDestination().id) { saveState = true }
launchSingleTop = true
restoreState = true
}
},
icon = { androidx.compose.material3.Icon(Icons.Default.Home, contentDescription = null) },
label = { Text("Tables") },
)
NavigationBarItem(
selected = currentRoute == Routes.Settings,
onClick = {
navController.navigate(Routes.Settings) {
popUpTo(navController.graph.findStartDestination().id) { saveState = true }
launchSingleTop = true
restoreState = true
}
},
icon = { androidx.compose.material3.Icon(Icons.Default.Settings, contentDescription = null) },
label = { Text("Settings") },
)
}
},
) { innerPadding ->
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
NavHost(
navController = navController,
startDestination = Routes.Tables,
) {
composable(Routes.Tables) {
ListsScreen(
onNavigateToList = { listId -> navController.navigate("list/$listId") },
onNavigateToSettings = { navController.navigate(Routes.Settings) },
onNavigateToLogs = { navController.navigate(Routes.Logs) },
)
}
composable(Routes.Settings) {
SettingsScreen(
onNavigateBack = { navController.popBackStack() },
onNavigateToLogs = { navController.navigate(Routes.Logs) },
onLeaveServer = {
// Reset to setup flow by toggling the flag and rebuilding graph via recomposition
prefs.setIsFirstRun(true)
},
)
}
composable(Routes.Logs) {
LogsScreen(onNavigateBack = { navController.popBackStack() })
}
composable(
route = Routes.ListDetail,
arguments = listOf(navArgument("listId") { type = NavType.StringType }),
) { backStackEntry ->
val listId = backStackEntry.arguments?.getString("listId") ?: return@composable
ListDetailScreen(listId = listId, onNavigateBack = { navController.popBackStack() })
}
}
}
}
}
@@ -188,8 +188,8 @@ fun ListDetailScreen(
val localCtx = LocalContext.current val localCtx = LocalContext.current
val localPrefs = remember { PreferencesManager.getInstance(localCtx) } val localPrefs = remember { PreferencesManager.getInstance(localCtx) }
ConnectionStatusAction(prefs = localPrefs) ConnectionStatusAction(prefs = localPrefs)
IconButton(onClick = { showManageColumnsDialog = true }) { IconButton(onClick = { showAddItemDialog = true }) {
Icon(Icons.Default.Settings, contentDescription = "Manage Columns") Icon(Icons.Default.Add, contentDescription = stringResource(R.string.add_item))
} }
}, },
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
@@ -198,13 +198,7 @@ fun ListDetailScreen(
), ),
) )
}, },
floatingActionButton = { floatingActionButton = {},
FloatingActionButton(
onClick = { showAddItemDialog = true },
) {
Icon(Icons.Default.Add, contentDescription = stringResource(R.string.add_item))
}
},
) { padding -> ) { padding ->
// Apply filtering, sorting, and grouping to items // Apply filtering, sorting, and grouping to items
fun valueFor(item: ItemWithValues, field: Field?): String { fun valueFor(item: ItemWithValues, field: Field?): String {
@@ -527,6 +521,7 @@ fun ListDetailScreen(
}, },
scrollState = horizontalScrollState, scrollState = horizontalScrollState,
isLast = (field.id == stableFields.lastOrNull()?.id), isLast = (field.id == stableFields.lastOrNull()?.id),
onHeaderClick = { showManageColumnsDialog = true },
) )
} }
} }
@@ -741,6 +736,7 @@ fun FieldHeader(
onWidthChange: (Float) -> Unit, onWidthChange: (Float) -> Unit,
scrollState: androidx.compose.foundation.ScrollState, scrollState: androidx.compose.foundation.ScrollState,
isLast: Boolean, isLast: Boolean,
onHeaderClick: () -> Unit,
) { ) {
val density = LocalDensity.current val density = LocalDensity.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
@@ -767,7 +763,9 @@ fun FieldHeader(
Text( Text(
text = field.name, text = field.name,
style = MaterialTheme.typography.labelLarge, style = MaterialTheme.typography.labelLarge,
modifier = Modifier.weight(1f), modifier = Modifier
.weight(1f)
.clickable { onHeaderClick() },
) )
// Resize handle // Resize handle
@@ -23,7 +23,6 @@ import androidx.compose.material.icons.filled.DragHandle
import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.List import androidx.compose.material.icons.filled.List
import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Sort import androidx.compose.material.icons.filled.Sort
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
@@ -69,6 +68,7 @@ import org.burnoutcrew.reorderable.reorderable
@Composable @Composable
fun ListsScreen( fun ListsScreen(
onNavigateToList: (String) -> Unit, onNavigateToList: (String) -> Unit,
// Settings now accessed via bottom navigation; keep param for backward compat (unused)
onNavigateToSettings: () -> Unit, onNavigateToSettings: () -> Unit,
onNavigateToLogs: () -> Unit = {}, onNavigateToLogs: () -> Unit = {},
) { ) {
@@ -108,10 +108,10 @@ fun ListsScreen(
val context = LocalContext.current val context = LocalContext.current
val prefsLocal = remember { PreferencesManager.getInstance(context) } val prefsLocal = remember { PreferencesManager.getInstance(context) }
ConnectionStatusAction(prefs = prefsLocal) ConnectionStatusAction(prefs = prefsLocal)
IconButton(onClick = onNavigateToSettings) { IconButton(onClick = { showCreateDialog = true }) {
Icon( Icon(
Icons.Default.Settings, Icons.Default.Add,
contentDescription = stringResource(R.string.settings), contentDescription = stringResource(R.string.create_list),
) )
} }
}, },
@@ -122,11 +122,7 @@ fun ListsScreen(
), ),
) )
}, },
floatingActionButton = { floatingActionButton = {},
FloatingActionButton(onClick = { showCreateDialog = true }) {
Icon(Icons.Default.Add, contentDescription = stringResource(R.string.create_list))
}
},
) { padding -> ) { padding ->
Box( Box(
modifier = modifier =