From 35217d137ddf332c227c1a10d7bc5ed0ae8aeb5a Mon Sep 17 00:00:00 2001 From: Syer10 Date: Fri, 23 Apr 2021 20:47:35 -0400 Subject: [PATCH] Use side buttons instead of tabs for source browsing --- src/main/kotlin/ca/gosyer/data/DataModule.kt | 1 - .../gosyer/ui/base/components/ErrorScreen.kt | 1 - .../ca/gosyer/ui/base/components/Toolbar.kt | 11 +- .../ca/gosyer/ui/library/LibraryScreen.kt | 2 - .../kotlin/ca/gosyer/ui/main/MainViewModel.kt | 29 +--- .../kotlin/ca/gosyer/ui/manga/MangaMenu.kt | 10 +- .../ca/gosyer/ui/sources/SourcesMenu.kt | 68 +++++--- .../gosyer/ui/sources/SourcesMenuViewModel.kt | 8 +- .../ui/sources/components/SourceTabs.kt | 147 ------------------ 9 files changed, 66 insertions(+), 211 deletions(-) delete mode 100644 src/main/kotlin/ca/gosyer/ui/sources/components/SourceTabs.kt diff --git a/src/main/kotlin/ca/gosyer/data/DataModule.kt b/src/main/kotlin/ca/gosyer/data/DataModule.kt index 8a432f8e..4dadb4d6 100644 --- a/src/main/kotlin/ca/gosyer/data/DataModule.kt +++ b/src/main/kotlin/ca/gosyer/data/DataModule.kt @@ -20,7 +20,6 @@ import ca.gosyer.data.server.interactions.LibraryInteractionHandler import ca.gosyer.data.server.interactions.MangaInteractionHandler import ca.gosyer.data.server.interactions.SourceInteractionHandler import ca.gosyer.data.ui.UiPreferences -import io.ktor.client.HttpClient import toothpick.ktp.binding.bind import toothpick.ktp.binding.module diff --git a/src/main/kotlin/ca/gosyer/ui/base/components/ErrorScreen.kt b/src/main/kotlin/ca/gosyer/ui/base/components/ErrorScreen.kt index beca5491..86e018ef 100644 --- a/src/main/kotlin/ca/gosyer/ui/base/components/ErrorScreen.kt +++ b/src/main/kotlin/ca/gosyer/ui/base/components/ErrorScreen.kt @@ -18,7 +18,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.sp import kotlin.random.Random - @Composable fun ErrorScreen(errorMessage: String? = null) { Box(Modifier.fillMaxSize()) { diff --git a/src/main/kotlin/ca/gosyer/ui/base/components/Toolbar.kt b/src/main/kotlin/ca/gosyer/ui/base/components/Toolbar.kt index 0b944ea6..8bd75f25 100644 --- a/src/main/kotlin/ca/gosyer/ui/base/components/Toolbar.kt +++ b/src/main/kotlin/ca/gosyer/ui/base/components/Toolbar.kt @@ -23,13 +23,19 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import ca.gosyer.ui.main.Routing import com.github.zsoltk.compose.router.BackStack import compose.icons.FontAwesomeIcons import compose.icons.fontawesomeicons.Regular import compose.icons.fontawesomeicons.regular.WindowClose @Composable -fun Toolbar(router: BackStack, name: String, closable: Boolean, search: ((String) -> Unit)? = null) { +fun Toolbar( + name: String, + router: BackStack? = null, + closable: Boolean, + search: ((String) -> Unit)? = null +) { val searchText = remember { mutableStateOf("") } Surface(Modifier.fillMaxWidth().height(32.dp), elevation = 2.dp) { Row(Modifier.fillMaxSize(), horizontalArrangement = Arrangement.SpaceBetween) { @@ -46,7 +52,7 @@ fun Toolbar(router: BackStack, name: String, closable: Boolean, search: ( if (closable) { IconButton( onClick = { - router.pop() + router?.pop() } ) { Icon(FontAwesomeIcons.Regular.WindowClose, "close", Modifier.size(32.dp)) @@ -54,5 +60,4 @@ fun Toolbar(router: BackStack, name: String, closable: Boolean, search: ( } } } - } \ No newline at end of file diff --git a/src/main/kotlin/ca/gosyer/ui/library/LibraryScreen.kt b/src/main/kotlin/ca/gosyer/ui/library/LibraryScreen.kt index 7cf533ef..336e69ed 100644 --- a/src/main/kotlin/ca/gosyer/ui/library/LibraryScreen.kt +++ b/src/main/kotlin/ca/gosyer/ui/library/LibraryScreen.kt @@ -30,10 +30,8 @@ import ca.gosyer.ui.base.components.LoadingScreen import ca.gosyer.ui.base.components.Pager import ca.gosyer.ui.base.components.PagerState import ca.gosyer.ui.base.vm.viewModel -import ca.gosyer.ui.main.Routing import ca.gosyer.ui.manga.openMangaMenu import ca.gosyer.util.compose.ThemedWindow -import com.github.zsoltk.compose.router.BackStack fun openLibraryMenu() { ThemedWindow { diff --git a/src/main/kotlin/ca/gosyer/ui/main/MainViewModel.kt b/src/main/kotlin/ca/gosyer/ui/main/MainViewModel.kt index d4806dd6..0d1919ce 100644 --- a/src/main/kotlin/ca/gosyer/ui/main/MainViewModel.kt +++ b/src/main/kotlin/ca/gosyer/ui/main/MainViewModel.kt @@ -7,38 +7,11 @@ package ca.gosyer.ui.main import ca.gosyer.data.ui.UiPreferences -import ca.gosyer.data.ui.model.Screen import ca.gosyer.ui.base.vm.ViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.launch import javax.inject.Inject class MainViewModel @Inject constructor( uiPreferences: UiPreferences ): ViewModel() { - private val _menu = MutableStateFlow( - uiPreferences.startScreen().get().toMenu() - ) - val menu = _menu.asStateFlow() - - fun switchMenu(menu: Menu) { - scope.launch { - _menu.value = menu - } - } - - fun Screen.toMenu() = when (this) { - Screen.Library -> Menu.Library - Screen.Sources -> Menu.Sources - Screen.Extensions -> Menu.Extensions - } -} - -enum class Menu { - Library, - Sources, - Extensions, - Manga, - Categories + val startScreen = uiPreferences.startScreen().get() } \ No newline at end of file diff --git a/src/main/kotlin/ca/gosyer/ui/manga/MangaMenu.kt b/src/main/kotlin/ca/gosyer/ui/manga/MangaMenu.kt index 3f01abb0..2104e8fd 100644 --- a/src/main/kotlin/ca/gosyer/ui/manga/MangaMenu.kt +++ b/src/main/kotlin/ca/gosyer/ui/manga/MangaMenu.kt @@ -67,9 +67,7 @@ fun MangaMenu(mangaId: Long, backStack: BackStack? = null) { val serverUrl by vm.serverUrl.collectAsState() Column(Modifier.background(MaterialTheme.colors.background)) { - if (backStack != null) { - Toolbar(backStack, "Manga", true) - } + Toolbar("Manga", backStack, backStack != null) Surface(Modifier.height(40.dp).fillMaxWidth()) { Row { @@ -81,10 +79,10 @@ fun MangaMenu(mangaId: Long, backStack: BackStack? = null) { manga?.let { manga -> Box { val state = rememberLazyListState() + val items = remember(manga, chapters) { + listOf(MangaMenu.MangaMenuManga(manga)) + chapters.map { MangaMenu.MangaMenuChapter(it) } + } LazyColumn(state = state) { - val items = remember(manga, chapters) { - listOf(MangaMenu.MangaMenuManga(manga)) + chapters.map { MangaMenu.MangaMenuChapter(it) } - } items(items) { when (it) { is MangaMenu.MangaMenuManga -> MangaItem(it.manga, serverUrl) diff --git a/src/main/kotlin/ca/gosyer/ui/sources/SourcesMenu.kt b/src/main/kotlin/ca/gosyer/ui/sources/SourcesMenu.kt index 7b2c1899..34741b8c 100644 --- a/src/main/kotlin/ca/gosyer/ui/sources/SourcesMenu.kt +++ b/src/main/kotlin/ca/gosyer/ui/sources/SourcesMenu.kt @@ -6,22 +6,34 @@ package ca.gosyer.ui.sources -import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material.MaterialTheme +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.requiredHeight +import androidx.compose.foundation.layout.requiredWidth +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.Card +import androidx.compose.material.Icon +import androidx.compose.material.Surface +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Home import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import ca.gosyer.data.models.Manga +import androidx.compose.ui.unit.dp import ca.gosyer.data.models.Source +import ca.gosyer.ui.base.components.KtorImage +import ca.gosyer.ui.base.components.Toolbar import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.manga.openMangaMenu import ca.gosyer.ui.sources.components.SourceHomeScreen import ca.gosyer.ui.sources.components.SourceScreen -import ca.gosyer.ui.sources.components.SourceTopBar import ca.gosyer.util.compose.ThemedWindow +import com.github.zsoltk.compose.savedinstancestate.BundleScope fun openSourcesMenu() { ThemedWindow(title = "TachideskJUI - Sources") { @@ -40,22 +52,38 @@ fun SourcesMenu(onMangaClick: (Long) -> Unit) { val selectedSourceTab by vm.selectedSourceTab.collectAsState() val serverUrl by vm.serverUrl.collectAsState() - Column(Modifier.fillMaxSize().background(MaterialTheme.colors.background)) { - SourceTopBar( - openHome = { - vm.selectTab(null) - }, - sources = sourceTabs, - tabSelected = selectedSourceTab, - onTabSelected = vm::selectTab, - onTabClosed = vm::closeTab - ) + Surface { + Column { + Toolbar(selectedSourceTab?.name ?: "Sources", closable = false) + Row { + LazyColumn(Modifier.fillMaxHeight().width(64.dp)) { + items(sourceTabs) { source -> + Card( + Modifier + .clickable { + vm.selectTab(source) + } + .requiredHeight(64.dp) + .requiredWidth(64.dp), + ) { + if (source != null) { + KtorImage(source.iconUrl(serverUrl),) + } else { + Icon(Icons.Default.Home, "Home") + } + } + } + } - val selectedSource: Source? = selectedSourceTab - if (selectedSource != null) { - SourceScreen(selectedSource, onMangaClick) - } else { - SourceHomeScreen(isLoading, sources, serverUrl, vm::addTab) + val selectedSource: Source? = selectedSourceTab + BundleScope(selectedSource?.name ?: "home") { + if (selectedSource != null) { + SourceScreen(selectedSource, onMangaClick) + } else { + SourceHomeScreen(isLoading, sources, serverUrl, vm::addTab) + } + } + } } } } diff --git a/src/main/kotlin/ca/gosyer/ui/sources/SourcesMenuViewModel.kt b/src/main/kotlin/ca/gosyer/ui/sources/SourcesMenuViewModel.kt index 83f50347..1e8dd680 100644 --- a/src/main/kotlin/ca/gosyer/ui/sources/SourcesMenuViewModel.kt +++ b/src/main/kotlin/ca/gosyer/ui/sources/SourcesMenuViewModel.kt @@ -35,7 +35,7 @@ class SourcesMenuViewModel @Inject constructor( private val _sources = MutableStateFlow(emptyList()) val sources = _sources.asStateFlow() - private val _sourceTabs = MutableStateFlow(mapOf(null to null)) + private val _sourceTabs = MutableStateFlow>(listOf(null)) val sourceTabs = _sourceTabs.asStateFlow() private val _selectedSourceTab = MutableStateFlow(null) @@ -65,12 +65,14 @@ class SourcesMenuViewModel @Inject constructor( } fun addTab(source: Source) { - _sourceTabs.value += source.id to source + if (source !in _sourceTabs.value) { + _sourceTabs.value += source + } selectTab(source) } fun closeTab(source: Source) { - _sourceTabs.value -= source.id + _sourceTabs.value -= source if (selectedSourceTab.value?.id == source.id) { _selectedSourceTab.value = null } diff --git a/src/main/kotlin/ca/gosyer/ui/sources/components/SourceTabs.kt b/src/main/kotlin/ca/gosyer/ui/sources/components/SourceTabs.kt deleted file mode 100644 index 6bd06a5e..00000000 --- a/src/main/kotlin/ca/gosyer/ui/sources/components/SourceTabs.kt +++ /dev/null @@ -1,147 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -package ca.gosyer.ui.sources.components - -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.material.MaterialTheme -import androidx.compose.material.ScrollableTabRow -import androidx.compose.material.Surface -import androidx.compose.material.Tab -import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Close -import androidx.compose.material.icons.filled.Home -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import ca.gosyer.data.models.Source - -@Composable -fun SourceTopBar( - openHome: () -> Unit, - sources: Map, - tabSelected: Source?, - onTabSelected: (Source?) -> Unit, - onTabClosed: (Source) -> Unit, - modifier: Modifier = Modifier -) { - Surface( - elevation = 2.dp, - color = MaterialTheme.colors.surface - ) { - SourceTabBar( - modifier = modifier, - onHomeClicked = openHome - ) { tabBarModifier -> - SourceTabs( - modifier = tabBarModifier, - sources = sources, - tabSelected = tabSelected, - onTabSelected = { newTab -> onTabSelected(newTab) }, - onTabClosed = onTabClosed - ) - } - } - -} - -@Composable -fun SourceTabBar( - modifier: Modifier = Modifier, - onHomeClicked: () -> Unit, - children: @Composable (Modifier) -> Unit -) { - Row(modifier) { - // Separate Row as the children shouldn't have the padding - Box(Modifier.padding(4.dp).align(Alignment.CenterVertically)) { - Image( - modifier = Modifier - .clickable(onClick = onHomeClicked), - imageVector = Icons.Filled.Home, - contentDescription = null, - colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface) - ) - } - children( - Modifier - .weight(1f) - .align(Alignment.CenterVertically) - ) - - } - -} - -@Composable -fun SourceTabs( - modifier: Modifier = Modifier, - sources: Map, - tabSelected: Source?, - onTabSelected: (Source?) -> Unit, - onTabClosed: (Source) -> Unit -) { - ScrollableTabRow( - selectedTabIndex = 1,//sources.indexOf(tabSelected).let { if (it == -1) 1 else it }, - modifier = modifier, - contentColor = MaterialTheme.colors.onSurface, - indicator = { - - }, - divider = { - - }, - backgroundColor = MaterialTheme.colors.surface - ) { - sources.forEach { (sourceId, source) -> - val selected = sourceId == tabSelected?.id - - var rowModifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp) - if (selected) { - rowModifier = - Modifier - .border(BorderStroke(2.dp, MaterialTheme.colors.onSurface), RectangleShape) - .then(rowModifier) - } - - Tab( - modifier = Modifier.background(MaterialTheme.colors.surface), - selected = selected, - onClick = { onTabSelected(source) } - ) { - Row(rowModifier, verticalAlignment = Alignment.CenterVertically) { - Text( - text = source?.name?.toUpperCase() ?: "Sources", - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - if (source != null) { - Image( - modifier = Modifier - .clickable { - onTabClosed(source) - }, - imageVector = Icons.Filled.Close, - contentDescription = null, - colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface) - ) - } - } - } - } - } -} \ No newline at end of file