diff --git a/src/main/kotlin/ca/gosyer/ui/main/components/DownloadsExtraInfo.kt b/src/main/kotlin/ca/gosyer/ui/main/components/DownloadsExtraInfo.kt new file mode 100644 index 00000000..a8dd91b0 --- /dev/null +++ b/src/main/kotlin/ca/gosyer/ui/main/components/DownloadsExtraInfo.kt @@ -0,0 +1,48 @@ +package ca.gosyer.ui.main.components + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.ContentAlpha +import androidx.compose.material.LocalContentColor +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import ca.gosyer.data.download.DownloadService +import ca.gosyer.ui.base.resources.stringResource +import ca.gosyer.ui.base.vm.viewModel +import ca.gosyer.ui.downloads.DownloadsMenuViewModel + +@Composable +fun DownloadsExtraInfo() { + val vm = viewModel() + val status by vm.serviceStatus.collectAsState() + val list by vm.downloadQueue.collectAsState() + val text = when (status) { + DownloadService.Status.STARTING -> stringResource("downloads_loading") + DownloadService.Status.RUNNING -> { + if (list.isNotEmpty()) { + stringResource("downloads_remaining", list.size) + } else null + } + DownloadService.Status.STOPPED -> null + } + if (text != null) { + Text( + text, + style = MaterialTheme.typography.body2, + color = LocalContentColor.current.copy(alpha = ContentAlpha.disabled) + ) + } else if (status == DownloadService.Status.STOPPED) { + Surface(onClick = vm::restartDownloader, shape = RoundedCornerShape(4.dp)) { + Text( + stringResource("downloads_stopped"), + style = MaterialTheme.typography.body2, + color = Color.Red.copy(alpha = ContentAlpha.disabled) + ) + } + } +} diff --git a/src/main/kotlin/ca/gosyer/ui/main/components/SideMenu.kt b/src/main/kotlin/ca/gosyer/ui/main/components/SideMenu.kt new file mode 100644 index 00000000..30e2238e --- /dev/null +++ b/src/main/kotlin/ca/gosyer/ui/main/components/SideMenu.kt @@ -0,0 +1,70 @@ +package ca.gosyer.ui.main.components + +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.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Close +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import ca.gosyer.build.BuildConfig +import ca.gosyer.ui.base.components.MenuController +import ca.gosyer.ui.main.TopLevelMenus + +@Composable +fun SideMenu(modifier: Modifier, controller: MenuController) { + Surface(modifier then Modifier.fillMaxHeight(), elevation = 2.dp) { + Box(Modifier.fillMaxSize()) { + Column(Modifier.fillMaxSize().padding(horizontal = 4.dp)) { + Row( + Modifier.fillMaxWidth().height(60.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + BuildConfig.NAME, + fontSize = 24.sp, + modifier = Modifier + ) + IconButton(controller::closeSideMenu) { + Icon(Icons.Rounded.Close, contentDescription = null) + } + } + Spacer(Modifier.height(20.dp)) + remember { TopLevelMenus.values().filter { it.top } }.forEach { topLevelMenu -> + SideMenuItem( + topLevelMenu.isSelected(controller.backStack), + topLevelMenu, + controller::newRoot + ) + } + Box(Modifier.fillMaxSize()) { + Column(Modifier.align(Alignment.BottomStart).padding(bottom = 8.dp)) { + remember { TopLevelMenus.values().filterNot { it.top } }.forEach { topLevelMenu -> + SideMenuItem( + topLevelMenu.isSelected(controller.backStack), + topLevelMenu, + controller::newRoot + ) + } + } + } + } + } + } +} diff --git a/src/main/kotlin/ca/gosyer/ui/main/components/SideMenuItem.kt b/src/main/kotlin/ca/gosyer/ui/main/components/SideMenuItem.kt new file mode 100644 index 00000000..acf41d65 --- /dev/null +++ b/src/main/kotlin/ca/gosyer/ui/main/components/SideMenuItem.kt @@ -0,0 +1,91 @@ +package ca.gosyer.ui.main.components + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp +import ca.gosyer.ui.base.components.combinedMouseClickable +import ca.gosyer.ui.base.resources.stringResource +import ca.gosyer.ui.main.Routes +import ca.gosyer.ui.main.TopLevelMenus + +@Composable +fun SideMenuItem(selected: Boolean, topLevelMenu: TopLevelMenus, newRoot: (Routes) -> Unit) { + SideMenuItem( + selected, + stringResource(topLevelMenu.textKey), + topLevelMenu.menu, + topLevelMenu.selectedIcon, + topLevelMenu.unselectedIcon, + topLevelMenu.openInNewWindow, + topLevelMenu.extraInfo, + newRoot + ) +} + +@Composable +private fun SideMenuItem( + selected: Boolean, + text: String, + menu: Routes, + selectedIcon: ImageVector, + unselectedIcon: ImageVector, + onMiddleClick: () -> Unit, + extraInfo: (@Composable () -> Unit)? = null, + onClick: (Routes) -> Unit +) { + Card( + Modifier.fillMaxWidth(), + backgroundColor = if (!selected) { + Color.Transparent + } else { + MaterialTheme.colors.primary.copy(0.30F) + }, + elevation = 0.dp, + shape = RoundedCornerShape(8.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth() + .height(40.dp) + .combinedMouseClickable( + onClick = { onClick(menu) }, + onMiddleClick = { onMiddleClick() } + ) + ) { + Spacer(Modifier.width(16.dp)) + Image( + if (selected) { + selectedIcon + } else { + unselectedIcon + }, + text, + Modifier.size(20.dp), + colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface) + ) + Spacer(Modifier.width(16.dp)) + Column { + Text(text, color = MaterialTheme.colors.onSurface) + if (extraInfo != null) { + extraInfo() + } + } + } + } +}