mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-10 06:42:05 +01:00
Improve Source display, in library badge, add comfortable and list display
This commit is contained in:
@@ -8,10 +8,15 @@ package ca.gosyer.data.catalog
|
||||
|
||||
import ca.gosyer.core.prefs.Preference
|
||||
import ca.gosyer.core.prefs.PreferenceStore
|
||||
import ca.gosyer.data.library.model.DisplayMode
|
||||
import java.util.Locale
|
||||
|
||||
class CatalogPreferences(private val preferenceStore: PreferenceStore) {
|
||||
fun languages(): Preference<Set<String>> {
|
||||
return preferenceStore.getStringSet("enabled_langs", setOf("en", Locale.getDefault().language))
|
||||
}
|
||||
|
||||
fun displayMode(): Preference<DisplayMode> {
|
||||
return preferenceStore.getJsonObject("display_mode", DisplayMode.CompactGrid, DisplayMode.serializer())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
<string name="move_to_latest">Latest</string>
|
||||
<string name="reset_filters">Reset</string>
|
||||
<string name="filter_source">Filter</string>
|
||||
<string name="in_library">In library</string>
|
||||
|
||||
<!-- Reader Menu -->
|
||||
<string name="default_reader_mode">Default</string>
|
||||
|
||||
@@ -39,6 +39,9 @@ class SourceScreen(val source: Source) : Screen {
|
||||
onMangaClick = { navigator push MangaScreen(it) },
|
||||
onCloseSourceTabClick = sourcesNavigator::remove,
|
||||
onSourceSettingsClick = { navigator push SourceSettingsScreen(it) },
|
||||
displayMode = sourceVM.displayMode.collectAsState().value,
|
||||
gridColumns = sourceVM.gridColumns.collectAsState().value,
|
||||
gridSize = sourceVM.gridSize.collectAsState().value,
|
||||
mangas = sourceVM.mangas.collectAsState().value,
|
||||
hasNextPage = sourceVM.hasNextPage.collectAsState().value,
|
||||
loading = sourceVM.loading.collectAsState().value,
|
||||
@@ -51,6 +54,7 @@ class SourceScreen(val source: Source) : Screen {
|
||||
setMode = sourceVM::setMode,
|
||||
loadNextPage = sourceVM::loadNextPage,
|
||||
setUsingFilters = sourceVM::setUsingFilters,
|
||||
onSelectDisplayMode = sourceVM::selectDisplayMode,
|
||||
// FilterVM
|
||||
filters = filterVM.filters.collectAsState().value,
|
||||
showingFilters = filterVM.showingFilters.collectAsState().value,
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
package ca.gosyer.ui.sources.browse
|
||||
|
||||
import ca.gosyer.core.logging.CKLogger
|
||||
import ca.gosyer.data.catalog.CatalogPreferences
|
||||
import ca.gosyer.data.library.LibraryPreferences
|
||||
import ca.gosyer.data.library.model.DisplayMode
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.data.models.MangaPage
|
||||
import ca.gosyer.data.models.Source
|
||||
@@ -24,19 +27,29 @@ import me.tatarka.inject.annotations.Inject
|
||||
class SourceScreenViewModel(
|
||||
private val source: Source,
|
||||
private val sourceHandler: SourceInteractionHandler,
|
||||
private val catalogPreferences: CatalogPreferences,
|
||||
private val libraryPreferences: LibraryPreferences,
|
||||
contextWrapper: ContextWrapper
|
||||
) : ViewModel(contextWrapper) {
|
||||
|
||||
@Inject constructor(
|
||||
sourceHandler: SourceInteractionHandler,
|
||||
catalogPreferences: CatalogPreferences,
|
||||
libraryPreferences: LibraryPreferences,
|
||||
contextWrapper: ContextWrapper,
|
||||
params: Params
|
||||
) : this(
|
||||
params.source,
|
||||
sourceHandler,
|
||||
catalogPreferences,
|
||||
libraryPreferences,
|
||||
contextWrapper
|
||||
)
|
||||
|
||||
val displayMode = catalogPreferences.displayMode().stateIn(scope)
|
||||
val gridColumns = libraryPreferences.gridColumns().stateIn(scope)
|
||||
val gridSize = libraryPreferences.gridSize().stateIn(scope)
|
||||
|
||||
private val _mangas = MutableStateFlow(emptyList<Manga>())
|
||||
val mangas = _mangas.asStateFlow()
|
||||
|
||||
@@ -139,6 +152,10 @@ class SourceScreenViewModel(
|
||||
startSearch(sourceSearchQuery.value)
|
||||
}
|
||||
|
||||
fun selectDisplayMode(displayMode: DisplayMode) {
|
||||
catalogPreferences.displayMode().set(displayMode)
|
||||
}
|
||||
|
||||
data class Params(val source: Source)
|
||||
|
||||
private companion object : CKLogger({})
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.browse.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.i18n.MR
|
||||
import ca.gosyer.uicore.resources.stringResource
|
||||
|
||||
@Composable
|
||||
fun SourceMangaBadges(
|
||||
inLibrary: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (!inLibrary) return
|
||||
|
||||
Row(modifier = modifier.clip(MaterialTheme.shapes.medium)) {
|
||||
Text(
|
||||
text = stringResource(MR.strings.in_library),
|
||||
modifier = Modifier.background(MaterialTheme.colors.primary).then(BadgesInnerPadding),
|
||||
style = MaterialTheme.typography.caption,
|
||||
color = MaterialTheme.colors.onPrimary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val BadgesInnerPadding = Modifier.padding(horizontal = 3.dp, vertical = 1.dp)
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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.browse.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.GridCells
|
||||
import androidx.compose.foundation.lazy.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.ui.base.components.VerticalScrollbar
|
||||
import ca.gosyer.ui.base.components.rememberScrollbarAdapter
|
||||
import ca.gosyer.uicore.image.KamelImage
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun SourceMangaComfortableGrid(
|
||||
mangas: List<Manga>,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
onClickManga: (Long) -> Unit,
|
||||
hasNextPage: Boolean = false,
|
||||
onLoadNextPage: () -> Unit,
|
||||
) {
|
||||
Box {
|
||||
val state = rememberLazyListState()
|
||||
val cells = if (gridColumns < 1) {
|
||||
GridCells.Adaptive(gridSize.dp)
|
||||
} else {
|
||||
GridCells.Fixed(gridColumns)
|
||||
}
|
||||
LazyVerticalGrid(
|
||||
cells = cells,
|
||||
state = state,
|
||||
modifier = Modifier.fillMaxSize().padding(4.dp)
|
||||
) {
|
||||
itemsIndexed(mangas) { index, manga ->
|
||||
if (hasNextPage && index == mangas.lastIndex) {
|
||||
LaunchedEffect(Unit) { onLoadNextPage() }
|
||||
}
|
||||
SourceMangaComfortableGridItem(
|
||||
modifier = Modifier.clickable(
|
||||
onClick = { onClickManga(manga.id) }
|
||||
),
|
||||
manga = manga,
|
||||
inLibrary = manga.inLibrary
|
||||
)
|
||||
}
|
||||
}
|
||||
VerticalScrollbar(
|
||||
rememberScrollbarAdapter(state),
|
||||
Modifier.align(Alignment.CenterEnd)
|
||||
.fillMaxHeight()
|
||||
.padding(horizontal = 4.dp, vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SourceMangaComfortableGridItem(
|
||||
modifier: Modifier,
|
||||
manga: Manga,
|
||||
inLibrary: Boolean
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(MaterialTheme.shapes.medium) then modifier
|
||||
) {
|
||||
Column {
|
||||
KamelImage(
|
||||
cover,
|
||||
contentDescription = manga.title,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(3f / 4f)
|
||||
.clip(MaterialTheme.shapes.medium),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Text(
|
||||
text = manga.title,
|
||||
style = fontStyle,
|
||||
maxLines = 3,
|
||||
modifier = Modifier.padding(vertical = 4.dp, horizontal = 4.dp)
|
||||
)
|
||||
}
|
||||
SourceMangaBadges(
|
||||
inLibrary = inLibrary,
|
||||
modifier = Modifier.padding(4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.browse.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.GridCells
|
||||
import androidx.compose.foundation.lazy.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.ui.base.components.VerticalScrollbar
|
||||
import ca.gosyer.ui.base.components.rememberScrollbarAdapter
|
||||
import ca.gosyer.uicore.image.KamelImage
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun SourceMangaCompactGrid(
|
||||
mangas: List<Manga>,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
onClickManga: (Long) -> Unit,
|
||||
hasNextPage: Boolean = false,
|
||||
onLoadNextPage: () -> Unit,
|
||||
) {
|
||||
Box {
|
||||
val state = rememberLazyListState()
|
||||
val cells = if (gridColumns < 1) {
|
||||
GridCells.Adaptive(gridSize.dp)
|
||||
} else {
|
||||
GridCells.Fixed(gridColumns)
|
||||
}
|
||||
LazyVerticalGrid(
|
||||
cells = cells,
|
||||
state = state,
|
||||
modifier = Modifier.fillMaxSize().padding(4.dp)
|
||||
) {
|
||||
itemsIndexed(mangas) { index, manga ->
|
||||
if (hasNextPage && index == mangas.lastIndex) {
|
||||
LaunchedEffect(Unit) { onLoadNextPage() }
|
||||
}
|
||||
SourceMangaCompactGridItem(
|
||||
modifier = Modifier.clickable(
|
||||
onClick = { onClickManga(manga.id) }
|
||||
),
|
||||
manga = manga,
|
||||
inLibrary = manga.inLibrary
|
||||
)
|
||||
}
|
||||
}
|
||||
VerticalScrollbar(
|
||||
rememberScrollbarAdapter(state),
|
||||
Modifier.align(Alignment.CenterEnd)
|
||||
.fillMaxHeight()
|
||||
.padding(horizontal = 4.dp, vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SourceMangaCompactGridItem(
|
||||
modifier: Modifier,
|
||||
manga: Manga,
|
||||
inLibrary: Boolean
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier.padding(4.dp)
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(3f / 4f)
|
||||
.clip(MaterialTheme.shapes.medium) then modifier
|
||||
) {
|
||||
KamelImage(
|
||||
cover,
|
||||
manga.title,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Box(modifier = Modifier.fillMaxSize().then(shadowGradient))
|
||||
Text(
|
||||
text = manga.title,
|
||||
color = Color.White,
|
||||
style = fontStyle,
|
||||
maxLines = 2,
|
||||
modifier = Modifier.align(Alignment.BottomStart).padding(8.dp)
|
||||
)
|
||||
SourceMangaBadges(
|
||||
inLibrary = inLibrary,
|
||||
modifier = Modifier.padding(4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val shadowGradient = Modifier.drawWithCache {
|
||||
val gradient = Brush.linearGradient(
|
||||
0.75f to Color.Transparent,
|
||||
1.0f to Color(0xAA000000),
|
||||
start = Offset(0f, 0f),
|
||||
end = Offset(0f, size.height)
|
||||
)
|
||||
onDrawBehind {
|
||||
drawRect(gradient)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.browse.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredHeight
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.ui.base.components.VerticalScrollbar
|
||||
import ca.gosyer.ui.base.components.rememberScrollbarAdapter
|
||||
import ca.gosyer.uicore.components.MangaListItem
|
||||
import ca.gosyer.uicore.components.MangaListItemImage
|
||||
import ca.gosyer.uicore.components.MangaListItemTitle
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun SourceMangaList(
|
||||
mangas: List<Manga>,
|
||||
onClickManga: (Long) -> Unit,
|
||||
hasNextPage: Boolean = false,
|
||||
onLoadNextPage: () -> Unit,
|
||||
) {
|
||||
Box {
|
||||
val state = rememberLazyListState()
|
||||
LazyColumn(
|
||||
state = state,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
itemsIndexed(mangas) { index, manga ->
|
||||
if (hasNextPage && index == mangas.lastIndex) {
|
||||
LaunchedEffect(Unit) { onLoadNextPage() }
|
||||
}
|
||||
MangaListItem(
|
||||
modifier = Modifier.clickable(
|
||||
onClick = { onClickManga(manga.id) }
|
||||
),
|
||||
manga = manga,
|
||||
inLibrary = manga.inLibrary
|
||||
)
|
||||
}
|
||||
}
|
||||
VerticalScrollbar(
|
||||
rememberScrollbarAdapter(state),
|
||||
Modifier.align(Alignment.CenterEnd)
|
||||
.fillMaxHeight()
|
||||
.padding(horizontal = 4.dp, vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MangaListItem(
|
||||
modifier: Modifier,
|
||||
manga: Manga,
|
||||
inLibrary: Boolean
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
MangaListItem(
|
||||
modifier = modifier then Modifier
|
||||
.requiredHeight(56.dp)
|
||||
.padding(horizontal = 16.dp),
|
||||
) {
|
||||
MangaListItemImage(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(MaterialTheme.shapes.medium),
|
||||
cover = cover,
|
||||
contentDescription = manga.title
|
||||
)
|
||||
MangaListItemTitle(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = 16.dp),
|
||||
text = manga.title,
|
||||
)
|
||||
SourceMangaBadges(inLibrary)
|
||||
}
|
||||
}
|
||||
@@ -15,17 +15,14 @@ import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.gestures.forEachGesture
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.GridCells
|
||||
import androidx.compose.foundation.lazy.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.BottomSheetScaffold
|
||||
import androidx.compose.material.BottomSheetState
|
||||
import androidx.compose.material.BottomSheetValue
|
||||
import androidx.compose.material.DropdownMenu
|
||||
import androidx.compose.material.DropdownMenuItem
|
||||
import androidx.compose.material.ExtendedFloatingActionButton
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Scaffold
|
||||
@@ -35,29 +32,31 @@ import androidx.compose.material.icons.rounded.Explore
|
||||
import androidx.compose.material.icons.rounded.FilterList
|
||||
import androidx.compose.material.icons.rounded.NewReleases
|
||||
import androidx.compose.material.icons.rounded.Settings
|
||||
import androidx.compose.material.icons.rounded.ViewModule
|
||||
import androidx.compose.material.rememberBottomSheetScaffoldState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import ca.gosyer.data.library.model.DisplayMode
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.data.models.Source
|
||||
import ca.gosyer.i18n.MR
|
||||
import ca.gosyer.ui.base.components.VerticalScrollbar
|
||||
import ca.gosyer.ui.base.components.rememberScrollbarAdapter
|
||||
import ca.gosyer.ui.base.navigation.ActionItem
|
||||
import ca.gosyer.ui.base.navigation.BackHandler
|
||||
import ca.gosyer.ui.base.navigation.Toolbar
|
||||
import ca.gosyer.ui.sources.browse.filter.SourceFiltersMenu
|
||||
import ca.gosyer.ui.sources.browse.filter.model.SourceFiltersView
|
||||
import ca.gosyer.uicore.components.LoadingScreen
|
||||
import ca.gosyer.uicore.components.MangaGridItem
|
||||
import ca.gosyer.uicore.resources.stringResource
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun SourceScreenContent(
|
||||
@@ -65,6 +64,9 @@ fun SourceScreenContent(
|
||||
onMangaClick: (Long) -> Unit,
|
||||
onCloseSourceTabClick: (Source) -> Unit,
|
||||
onSourceSettingsClick: (Long) -> Unit,
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
mangas: List<Manga>,
|
||||
hasNextPage: Boolean,
|
||||
loading: Boolean,
|
||||
@@ -77,6 +79,7 @@ fun SourceScreenContent(
|
||||
setMode: (Boolean) -> Unit,
|
||||
loadNextPage: () -> Unit,
|
||||
setUsingFilters: (Boolean) -> Unit,
|
||||
onSelectDisplayMode: (DisplayMode) -> Unit,
|
||||
// filter
|
||||
filters: List<SourceFiltersView<*, *>>,
|
||||
showingFilters: Boolean,
|
||||
@@ -103,6 +106,9 @@ fun SourceScreenContent(
|
||||
onMangaClick = onMangaClick,
|
||||
onCloseSourceTabClick = onCloseSourceTabClick,
|
||||
onSourceSettingsClick = onSourceSettingsClick,
|
||||
displayMode = displayMode,
|
||||
gridColumns = gridColumns,
|
||||
gridSize = gridSize,
|
||||
mangas = mangas,
|
||||
hasNextPage = hasNextPage,
|
||||
loading = loading,
|
||||
@@ -118,6 +124,7 @@ fun SourceScreenContent(
|
||||
showingFilters = showingFilters,
|
||||
showFilterButton = showFilterButton,
|
||||
setShowingFilters = setShowingFilters,
|
||||
onSelectDisplayMode = onSelectDisplayMode,
|
||||
resetFiltersClicked = resetFiltersClicked
|
||||
)
|
||||
} else {
|
||||
@@ -126,6 +133,9 @@ fun SourceScreenContent(
|
||||
onMangaClick = onMangaClick,
|
||||
onCloseSourceTabClick = onCloseSourceTabClick,
|
||||
onSourceSettingsClick = onSourceSettingsClick,
|
||||
displayMode = displayMode,
|
||||
gridColumns = gridColumns,
|
||||
gridSize = gridSize,
|
||||
mangas = mangas,
|
||||
hasNextPage = hasNextPage,
|
||||
loading = loading,
|
||||
@@ -141,6 +151,7 @@ fun SourceScreenContent(
|
||||
showingFilters = showingFilters,
|
||||
showFilterButton = showFilterButton,
|
||||
setShowingFilters = setShowingFilters,
|
||||
onSelectDisplayMode = onSelectDisplayMode,
|
||||
resetFiltersClicked = resetFiltersClicked
|
||||
)
|
||||
}
|
||||
@@ -153,6 +164,9 @@ private fun SourceWideScreenContent(
|
||||
onMangaClick: (Long) -> Unit,
|
||||
onCloseSourceTabClick: (Source) -> Unit,
|
||||
onSourceSettingsClick: (Long) -> Unit,
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
mangas: List<Manga>,
|
||||
hasNextPage: Boolean,
|
||||
loading: Boolean,
|
||||
@@ -169,6 +183,7 @@ private fun SourceWideScreenContent(
|
||||
showingFilters: Boolean,
|
||||
showFilterButton: Boolean,
|
||||
setShowingFilters: (Boolean) -> Unit,
|
||||
onSelectDisplayMode: (DisplayMode) -> Unit,
|
||||
resetFiltersClicked: () -> Unit
|
||||
) {
|
||||
Scaffold(
|
||||
@@ -186,11 +201,15 @@ private fun SourceWideScreenContent(
|
||||
showingFilters = showingFilters,
|
||||
onClickMode = setMode,
|
||||
onToggleFiltersClick = setShowingFilters,
|
||||
onSelectDisplayMode = onSelectDisplayMode
|
||||
)
|
||||
},
|
||||
) { padding ->
|
||||
Box(Modifier.padding(padding)) {
|
||||
MangaTable(
|
||||
displayMode = displayMode,
|
||||
gridColumns = gridColumns,
|
||||
gridSize = gridSize,
|
||||
mangas = mangas,
|
||||
isLoading = loading,
|
||||
hasNextPage = hasNextPage,
|
||||
@@ -235,6 +254,9 @@ private fun SourceThinScreenContent(
|
||||
onMangaClick: (Long) -> Unit,
|
||||
onCloseSourceTabClick: (Source) -> Unit,
|
||||
onSourceSettingsClick: (Long) -> Unit,
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
mangas: List<Manga>,
|
||||
hasNextPage: Boolean,
|
||||
loading: Boolean,
|
||||
@@ -251,6 +273,7 @@ private fun SourceThinScreenContent(
|
||||
showingFilters: Boolean,
|
||||
showFilterButton: Boolean,
|
||||
setShowingFilters: (Boolean) -> Unit,
|
||||
onSelectDisplayMode: (DisplayMode) -> Unit,
|
||||
resetFiltersClicked: () -> Unit
|
||||
) {
|
||||
val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
|
||||
@@ -288,6 +311,7 @@ private fun SourceThinScreenContent(
|
||||
showingFilters = showingFilters,
|
||||
onClickMode = setMode,
|
||||
onToggleFiltersClick = setShowingFilters,
|
||||
onSelectDisplayMode = onSelectDisplayMode
|
||||
)
|
||||
},
|
||||
sheetContent = {
|
||||
@@ -306,6 +330,9 @@ private fun SourceThinScreenContent(
|
||||
) { padding ->
|
||||
Box(Modifier.padding(padding)) {
|
||||
MangaTable(
|
||||
displayMode = displayMode,
|
||||
gridColumns = gridColumns,
|
||||
gridSize = gridSize,
|
||||
mangas = mangas,
|
||||
isLoading = loading,
|
||||
hasNextPage = hasNextPage,
|
||||
@@ -358,7 +385,8 @@ fun SourceToolbar(
|
||||
isLatest: Boolean,
|
||||
showingFilters: Boolean,
|
||||
onClickMode: (Boolean) -> Unit,
|
||||
onToggleFiltersClick: (Boolean) -> Unit
|
||||
onToggleFiltersClick: (Boolean) -> Unit,
|
||||
onSelectDisplayMode: (DisplayMode) -> Unit,
|
||||
) {
|
||||
Toolbar(
|
||||
source.name,
|
||||
@@ -370,6 +398,12 @@ fun SourceToolbar(
|
||||
search = onSearch,
|
||||
searchSubmit = onSubmitSearch,
|
||||
actions = {
|
||||
var displayModeSelectOpen by remember { mutableStateOf(false) }
|
||||
DisplayModeSelect(
|
||||
isVisible = displayModeSelectOpen,
|
||||
onSelectDisplayMode = onSelectDisplayMode,
|
||||
onDismissRequest = { displayModeSelectOpen = false }
|
||||
)
|
||||
getActionItems(
|
||||
isConfigurable = source.isConfigurable,
|
||||
onSourceSettingsClick = {
|
||||
@@ -383,14 +417,44 @@ fun SourceToolbar(
|
||||
},
|
||||
onClickMode = {
|
||||
onClickMode(!isLatest)
|
||||
}
|
||||
},
|
||||
openDisplayModeSelect = { displayModeSelectOpen = true }
|
||||
)
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DisplayModeSelect(
|
||||
isVisible: Boolean,
|
||||
onSelectDisplayMode: (DisplayMode) -> Unit,
|
||||
onDismissRequest: () -> Unit
|
||||
) {
|
||||
DropdownMenu(
|
||||
isVisible,
|
||||
onDismissRequest
|
||||
) {
|
||||
val list = DisplayMode.values().toList() - DisplayMode.CoverOnlyGrid
|
||||
list.fastForEach {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
onSelectDisplayMode(it)
|
||||
onDismissRequest()
|
||||
}
|
||||
) {
|
||||
Text(stringResource(it.res))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MangaTable(
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
mangas: List<Manga>,
|
||||
isLoading: Boolean = false,
|
||||
hasNextPage: Boolean = false,
|
||||
@@ -400,28 +464,30 @@ private fun MangaTable(
|
||||
if (isLoading || mangas.isEmpty()) {
|
||||
LoadingScreen(isLoading)
|
||||
} else {
|
||||
val lazyListState = rememberLazyListState()
|
||||
Box {
|
||||
LazyVerticalGrid(GridCells.Adaptive(160.dp), state = lazyListState) {
|
||||
itemsIndexed(mangas) { index, manga ->
|
||||
if (hasNextPage && index == mangas.lastIndex) {
|
||||
LaunchedEffect(Unit) { onLoadNextPage() }
|
||||
}
|
||||
MangaGridItem(
|
||||
title = manga.title,
|
||||
cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium),
|
||||
onClick = {
|
||||
onMangaClick(manga.id)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
VerticalScrollbar(
|
||||
rememberScrollbarAdapter(lazyListState),
|
||||
Modifier.align(Alignment.CenterEnd)
|
||||
.fillMaxHeight()
|
||||
.padding(horizontal = 4.dp, vertical = 8.dp)
|
||||
when (displayMode) {
|
||||
DisplayMode.CompactGrid -> SourceMangaCompactGrid(
|
||||
mangas = mangas,
|
||||
gridColumns = gridColumns,
|
||||
gridSize = gridSize,
|
||||
onClickManga = onMangaClick,
|
||||
hasNextPage = hasNextPage,
|
||||
onLoadNextPage = onLoadNextPage
|
||||
)
|
||||
DisplayMode.ComfortableGrid -> SourceMangaComfortableGrid(
|
||||
mangas = mangas,
|
||||
gridColumns = gridColumns,
|
||||
gridSize = gridSize,
|
||||
onClickManga = onMangaClick,
|
||||
hasNextPage = hasNextPage,
|
||||
onLoadNextPage = onLoadNextPage
|
||||
)
|
||||
DisplayMode.List -> SourceMangaList(
|
||||
mangas = mangas,
|
||||
onClickManga = onMangaClick,
|
||||
hasNextPage = hasNextPage,
|
||||
onLoadNextPage = onLoadNextPage
|
||||
)
|
||||
else -> Box {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,16 +501,10 @@ private fun getActionItems(
|
||||
showLatestButton: Boolean,
|
||||
showFilterButton: Boolean,
|
||||
onToggleFiltersClick: () -> Unit,
|
||||
onClickMode: () -> Unit
|
||||
onClickMode: () -> Unit,
|
||||
openDisplayModeSelect: () -> Unit
|
||||
): List<ActionItem> {
|
||||
return listOfNotNull(
|
||||
if (isConfigurable) {
|
||||
ActionItem(
|
||||
name = stringResource(MR.strings.location_settings),
|
||||
icon = Icons.Rounded.Settings,
|
||||
doAction = onSourceSettingsClick
|
||||
)
|
||||
} else null,
|
||||
if (showFilterButton) {
|
||||
ActionItem(
|
||||
name = stringResource(MR.strings.filter_source),
|
||||
@@ -469,6 +529,18 @@ private fun getActionItems(
|
||||
},
|
||||
doAction = onClickMode
|
||||
)
|
||||
} else null
|
||||
} else null,
|
||||
ActionItem(
|
||||
name = stringResource(MR.strings.display_mode),
|
||||
icon = Icons.Rounded.ViewModule,
|
||||
doAction = openDisplayModeSelect
|
||||
),
|
||||
if (isConfigurable) {
|
||||
ActionItem(
|
||||
name = stringResource(MR.strings.location_settings),
|
||||
icon = Icons.Rounded.Settings,
|
||||
doAction = onSourceSettingsClick
|
||||
)
|
||||
} else null,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user