diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/folderlist/FolderListScreen.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/folderlist/FolderListScreen.kt index 2e9fe2f..f2cdeda 100644 --- a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/folderlist/FolderListScreen.kt +++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/folderlist/FolderListScreen.kt @@ -36,10 +36,14 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vaultdrop.mobile.R import com.vaultdrop.mobile.data.local.entity.FolderEntity +import com.vaultdrop.mobile.ui.navigation.FloatingNavBar +import com.vaultdrop.mobile.ui.navigation.NavTab @OptIn(ExperimentalMaterial3Api::class) @Composable fun FolderListScreen( + selectedTab: NavTab, + onTabSelected: (NavTab) -> Unit, onOpenFolder: (String) -> Unit, viewModel: FolderListViewModel = hiltViewModel(), ) { @@ -56,6 +60,9 @@ fun FolderListScreen( }, ) }, + bottomBar = { + FloatingNavBar(selected = selectedTab, onSelect = onTabSelected) + }, ) { padding -> FolderListContent( uiState = uiState, diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/FloatingNavBar.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/FloatingNavBar.kt new file mode 100644 index 0000000..190d395 --- /dev/null +++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/FloatingNavBar.kt @@ -0,0 +1,121 @@ +@file:OptIn(ExperimentalMaterial3Api::class) + +package com.vaultdrop.mobile.ui.navigation + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Folder +import androidx.compose.material.icons.filled.Search +import androidx.compose.material.icons.filled.Settings +import androidx.compose.material.icons.outlined.Folder +import androidx.compose.material.icons.outlined.Search +import androidx.compose.material.icons.outlined.Settings +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.vaultdrop.mobile.R + +enum class NavTab(val route: String) { + FILES(Routes.FILES), + SEARCH(Routes.SEARCH), + SETTINGS(Routes.SETTINGS), +} + +private data class TabItem( + val tab: NavTab, + val filled: ImageVector, + val outlined: ImageVector, + val labelRes: Int, +) + +private val TAB_ITEMS = listOf( + TabItem(NavTab.FILES, Icons.Filled.Folder, Icons.Outlined.Folder, R.string.nav_files), + TabItem(NavTab.SEARCH, Icons.Filled.Search, Icons.Outlined.Search, R.string.nav_search), + TabItem(NavTab.SETTINGS, Icons.Filled.Settings, Icons.Outlined.Settings, R.string.nav_settings), +) + +@Composable +fun FloatingNavBar( + selected: NavTab, + onSelect: (NavTab) -> Unit, + modifier: Modifier = Modifier, +) { + Box( + modifier = modifier + .fillMaxWidth() + .padding(start = 20.dp, end = 20.dp, bottom = 16.dp), + contentAlignment = Alignment.Center, + ) { + Surface( + shape = RoundedCornerShape(30.dp), + color = MaterialTheme.colorScheme.surface, + shadowElevation = 8.dp, + modifier = Modifier.widthIn(max = 400.dp).fillMaxWidth(), + ) { + Row( + modifier = Modifier.padding(vertical = 8.dp, horizontal = 8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + TAB_ITEMS.forEach { item -> + val isActive = item.tab == selected + val icon = if (isActive) item.filled else item.outlined + val tint = if (isActive) MaterialTheme.colorScheme.primary + else MaterialTheme.colorScheme.onSurfaceVariant + + Surface( + onClick = { onSelect(item.tab) }, + shape = RoundedCornerShape(20.dp), + color = if (isActive) MaterialTheme.colorScheme.primaryContainer + else MaterialTheme.colorScheme.surface, + modifier = Modifier.weight(1f), + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp), + contentAlignment = Alignment.Center, + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Icon( + imageVector = icon, + contentDescription = null, + tint = tint, + modifier = Modifier.size(22.dp), + ) + Text( + text = stringResource(item.labelRes), + color = tint, + fontSize = 11.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + fontWeight = if (isActive) FontWeight.Bold else FontWeight.Medium, + modifier = Modifier.padding(top = 2.dp), + ) + } + } + } + } + } + } + } +} diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/NavGraph.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/NavGraph.kt index f4effa2..4d2abf9 100644 --- a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/NavGraph.kt +++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/NavGraph.kt @@ -1,34 +1,74 @@ package com.vaultdrop.mobile.ui.navigation import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.navigation.NavType 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 com.vaultdrop.mobile.ui.folderdetail.FolderDetailScreen import com.vaultdrop.mobile.ui.folderlist.FolderListScreen +import com.vaultdrop.mobile.ui.search.SearchScreen +import com.vaultdrop.mobile.ui.settings.SettingsScreen object Routes { const val FILES = "files" + const val SEARCH = "search" + const val SETTINGS = "settings" const val FOLDER_DETAIL = "folder/{folderResourceId}" const val ARG_FOLDER = "folderResourceId" fun folder(folderResourceId: String): String = "folder/$folderResourceId" } +private fun String?.toNavTab(): NavTab = when (this) { + Routes.FILES -> NavTab.FILES + Routes.SEARCH -> NavTab.SEARCH + Routes.SETTINGS -> NavTab.SETTINGS + else -> NavTab.FILES +} + @Composable fun NavGraph() { val navController = rememberNavController() + + val backStackEntry by navController.currentBackStackEntryAsState() + val selectedTab = backStackEntry?.destination?.route.toNavTab() + + val onTabSelected: (NavTab) -> Unit = { tab -> + if (tab.route != selectedTab.route) { + navController.navigate(tab.route) { + popUpTo(Routes.FILES) { inclusive = true } + launchSingleTop = true + } + } + } + NavHost( navController = navController, startDestination = Routes.FILES, ) { composable(Routes.FILES) { FolderListScreen( + selectedTab = selectedTab, + onTabSelected = onTabSelected, onOpenFolder = { id -> navController.navigate(Routes.folder(id)) }, ) } + composable(Routes.SEARCH) { + SearchScreen( + selectedTab = selectedTab, + onTabSelected = onTabSelected, + ) + } + composable(Routes.SETTINGS) { + SettingsScreen( + selectedTab = selectedTab, + onTabSelected = onTabSelected, + ) + } composable( route = Routes.FOLDER_DETAIL, arguments = listOf( @@ -45,4 +85,4 @@ fun NavGraph() { ) } } -} \ No newline at end of file +} diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/search/SearchScreen.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/search/SearchScreen.kt new file mode 100644 index 0000000..edbd8d9 --- /dev/null +++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/search/SearchScreen.kt @@ -0,0 +1,42 @@ +package com.vaultdrop.mobile.ui.search + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import com.vaultdrop.mobile.R +import com.vaultdrop.mobile.ui.navigation.FloatingNavBar +import com.vaultdrop.mobile.ui.navigation.NavTab + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SearchScreen( + selectedTab: NavTab, + onTabSelected: (NavTab) -> Unit, +) { + Scaffold( + topBar = { + TopAppBar(title = { Text(stringResource(R.string.search)) }) + }, + bottomBar = { + FloatingNavBar(selected = selectedTab, onSelect = onTabSelected) + }, + ) { padding -> + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Text( + text = stringResource(R.string.search), + textAlign = TextAlign.Center, + ) + } + } +} diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/settings/SettingsScreen.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/settings/SettingsScreen.kt new file mode 100644 index 0000000..e063c46 --- /dev/null +++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/settings/SettingsScreen.kt @@ -0,0 +1,42 @@ +package com.vaultdrop.mobile.ui.settings + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import com.vaultdrop.mobile.R +import com.vaultdrop.mobile.ui.navigation.FloatingNavBar +import com.vaultdrop.mobile.ui.navigation.NavTab + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SettingsScreen( + selectedTab: NavTab, + onTabSelected: (NavTab) -> Unit, +) { + Scaffold( + topBar = { + TopAppBar(title = { Text(stringResource(R.string.settings)) }) + }, + bottomBar = { + FloatingNavBar(selected = selectedTab, onSelect = onTabSelected) + }, + ) { padding -> + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Text( + text = stringResource(R.string.settings), + textAlign = TextAlign.Center, + ) + } + } +} diff --git a/mobile-kotlin/app/src/main/res/values-en/strings.xml b/mobile-kotlin/app/src/main/res/values-en/strings.xml index 7931844..dddd31f 100644 --- a/mobile-kotlin/app/src/main/res/values-en/strings.xml +++ b/mobile-kotlin/app/src/main/res/values-en/strings.xml @@ -14,6 +14,13 @@ Refresh Back + + Files + Search + Settings + Search + Settings + Sign in Sign in to your account diff --git a/mobile-kotlin/app/src/main/res/values/strings.xml b/mobile-kotlin/app/src/main/res/values/strings.xml index d7fdb10..8349df7 100644 --- a/mobile-kotlin/app/src/main/res/values/strings.xml +++ b/mobile-kotlin/app/src/main/res/values/strings.xml @@ -15,6 +15,13 @@ Actualiser Retour + + Fichiers + Recherche + Réglages + Recherche + Réglages + Connexion Connecte-toi à ton compte