mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-17 18:22:04 +01:00
Sources menu improvements
This commit is contained in:
@@ -6,9 +6,12 @@
|
||||
|
||||
package ca.gosyer.jui.ui.sources.home
|
||||
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import ca.gosyer.jui.core.lang.displayName
|
||||
import ca.gosyer.jui.data.source.SourceRepositoryImpl
|
||||
import ca.gosyer.jui.domain.source.model.Source
|
||||
import ca.gosyer.jui.domain.source.service.CatalogPreferences
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
import ca.gosyer.jui.uicore.vm.ViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -37,9 +40,39 @@ class SourceHomeScreenViewModel @Inject constructor(
|
||||
val languages = _languages.asStateFlow()
|
||||
|
||||
val sources = combine(installedSources, languages) { installedSources, languages ->
|
||||
installedSources.filter {
|
||||
it.lang in languages || it.id == Source.LOCAL_SOURCE_ID
|
||||
}
|
||||
val all = MR.strings.all.toPlatformString()
|
||||
val other = MR.strings.other.toPlatformString()
|
||||
installedSources
|
||||
.distinctBy { it.id }
|
||||
.filter {
|
||||
it.lang in languages || it.id == Source.LOCAL_SOURCE_ID
|
||||
}
|
||||
.groupBy(Source::displayLang)
|
||||
|
||||
.mapValues {
|
||||
it.value.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, Source::name))
|
||||
.map(SourceUI::SourceItem)
|
||||
}
|
||||
.mapKeys { (key) ->
|
||||
when (key) {
|
||||
"all" -> all
|
||||
"other" -> other
|
||||
else -> Locale(key).displayName
|
||||
}
|
||||
}
|
||||
.toList()
|
||||
.sortedWith(
|
||||
compareBy<Pair<String, *>> { (key) ->
|
||||
when (key) {
|
||||
all -> 1
|
||||
other -> 3
|
||||
else -> 2
|
||||
}
|
||||
}.thenBy(String.CASE_INSENSITIVE_ORDER, Pair<String, *>::first)
|
||||
)
|
||||
.flatMap { (key, value) ->
|
||||
listOf(SourceUI.Header(key)) + value
|
||||
}
|
||||
}.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
val sourceLanguages = installedSources.map { sources ->
|
||||
@@ -56,10 +89,7 @@ class SourceHomeScreenViewModel @Inject constructor(
|
||||
private fun getSources() {
|
||||
sourceHandler.getSourceList()
|
||||
.onEach {
|
||||
installedSources.value = it.sortedWith(
|
||||
compareBy(String.CASE_INSENSITIVE_ORDER, Source::displayLang)
|
||||
.thenBy(String.CASE_INSENSITIVE_ORDER, Source::name)
|
||||
)
|
||||
installedSources.value = it
|
||||
_isLoading.value = false
|
||||
}
|
||||
.catch {
|
||||
@@ -82,3 +112,8 @@ class SourceHomeScreenViewModel @Inject constructor(
|
||||
private val log = logging()
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SourceUI {
|
||||
data class Header(val header: String) : SourceUI()
|
||||
data class SourceItem(val source: Source): SourceUI()
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
package ca.gosyer.jui.ui.sources.home.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -21,6 +22,7 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||
@@ -39,9 +41,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.text.toUpperCase
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.jui.domain.source.model.Source
|
||||
@@ -51,6 +51,7 @@ import ca.gosyer.jui.ui.base.components.localeToString
|
||||
import ca.gosyer.jui.ui.base.navigation.ActionItem
|
||||
import ca.gosyer.jui.ui.base.navigation.Toolbar
|
||||
import ca.gosyer.jui.ui.extensions.components.LanguageDialog
|
||||
import ca.gosyer.jui.ui.sources.home.SourceUI
|
||||
import ca.gosyer.jui.uicore.components.LoadingScreen
|
||||
import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
|
||||
@@ -64,7 +65,7 @@ import com.vanpra.composematerialdialogs.rememberMaterialDialogState
|
||||
fun SourceHomeScreenContent(
|
||||
onAddSource: (Source) -> Unit,
|
||||
isLoading: Boolean,
|
||||
sources: List<Source>,
|
||||
sources: List<SourceUI>,
|
||||
languages: Set<String>,
|
||||
sourceLanguages: List<String>,
|
||||
setEnabledLanguages: (Set<String>) -> Unit,
|
||||
@@ -82,44 +83,15 @@ fun SourceHomeScreenContent(
|
||||
submitSearch = submitSearch
|
||||
)
|
||||
}
|
||||
) {
|
||||
) { padding ->
|
||||
if (sources.isEmpty()) {
|
||||
LoadingScreen(isLoading)
|
||||
} else {
|
||||
BoxWithConstraints(Modifier.fillMaxSize().padding(it), Alignment.TopCenter) {
|
||||
BoxWithConstraints(Modifier.fillMaxSize().padding(padding), Alignment.TopCenter) {
|
||||
if (maxWidth > 720.dp) {
|
||||
val state = rememberLazyGridState()
|
||||
val cells = GridCells.Adaptive(120.dp)
|
||||
LazyVerticalGrid(cells, state = state) {
|
||||
items(sources) { source ->
|
||||
WideSourceItem(
|
||||
source,
|
||||
onSourceClicked = onAddSource
|
||||
)
|
||||
}
|
||||
}
|
||||
VerticalScrollbar(
|
||||
modifier = Modifier.align(Alignment.CenterEnd)
|
||||
.fillMaxHeight()
|
||||
.scrollbarPadding(),
|
||||
adapter = rememberVerticalScrollbarAdapter(state, cells)
|
||||
)
|
||||
WideSourcesMenu(sources, onAddSource)
|
||||
} else {
|
||||
val state = rememberLazyListState()
|
||||
LazyColumn(state = state) {
|
||||
items(sources) { source ->
|
||||
ThinSourceItem(
|
||||
source,
|
||||
onSourceClicked = onAddSource
|
||||
)
|
||||
}
|
||||
}
|
||||
VerticalScrollbar(
|
||||
modifier = Modifier.align(Alignment.CenterEnd)
|
||||
.fillMaxHeight()
|
||||
.scrollbarPadding(),
|
||||
adapter = rememberScrollbarAdapter(state)
|
||||
)
|
||||
ThinSourcesMenu(sources, onAddSource)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,6 +123,58 @@ fun SourceHomeScreenToolbar(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WideSourcesMenu(
|
||||
sources: List<SourceUI>,
|
||||
onAddSource: (Source) -> Unit
|
||||
) {
|
||||
Box {
|
||||
val state = rememberLazyGridState()
|
||||
val cells = GridCells.Adaptive(120.dp)
|
||||
LazyVerticalGrid(cells, state = state, modifier = Modifier.fillMaxSize()) {
|
||||
items(
|
||||
sources,
|
||||
contentType = {
|
||||
when (it) {
|
||||
is SourceUI.Header -> "header"
|
||||
is SourceUI.SourceItem -> "source"
|
||||
}
|
||||
},
|
||||
key = {
|
||||
when (it) {
|
||||
is SourceUI.Header -> it.header
|
||||
is SourceUI.SourceItem -> it.source.id
|
||||
}
|
||||
},
|
||||
span = {
|
||||
when (it) {
|
||||
is SourceUI.Header -> GridItemSpan(maxLineSpan)
|
||||
is SourceUI.SourceItem -> GridItemSpan(1)
|
||||
}
|
||||
}
|
||||
) { sourceUI ->
|
||||
when (sourceUI) {
|
||||
is SourceUI.Header -> Text(
|
||||
sourceUI.header,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp)
|
||||
)
|
||||
is SourceUI.SourceItem -> WideSourceItem(
|
||||
sourceUI.source,
|
||||
onSourceClicked = onAddSource
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
VerticalScrollbar(
|
||||
modifier = Modifier.align(Alignment.CenterEnd)
|
||||
.fillMaxHeight()
|
||||
.scrollbarPadding(),
|
||||
adapter = rememberVerticalScrollbarAdapter(state, cells)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WideSourceItem(
|
||||
source: Source,
|
||||
@@ -182,7 +206,7 @@ fun WideSourceItem(
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
"${source.name} (${source.displayLang.toUpperCase(Locale.current)})",
|
||||
source.name,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
@@ -190,6 +214,50 @@ fun WideSourceItem(
|
||||
}
|
||||
}
|
||||
}
|
||||
@Composable
|
||||
fun ThinSourcesMenu(
|
||||
sources: List<SourceUI>,
|
||||
onAddSource: (Source) -> Unit
|
||||
) {
|
||||
Box {
|
||||
val state = rememberLazyListState()
|
||||
LazyColumn(state = state, modifier = Modifier.fillMaxSize()) {
|
||||
items(
|
||||
sources,
|
||||
contentType = {
|
||||
when (it) {
|
||||
is SourceUI.Header -> "header"
|
||||
is SourceUI.SourceItem -> "source"
|
||||
}
|
||||
},
|
||||
key = {
|
||||
when (it) {
|
||||
is SourceUI.Header -> it.header
|
||||
is SourceUI.SourceItem -> it.source.id
|
||||
}
|
||||
}
|
||||
) { sourceUI ->
|
||||
when (sourceUI) {
|
||||
is SourceUI.Header -> Text(
|
||||
sourceUI.header,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp)
|
||||
)
|
||||
is SourceUI.SourceItem -> ThinSourceItem(
|
||||
sourceUI.source,
|
||||
onSourceClicked = onAddSource
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
VerticalScrollbar(
|
||||
modifier = Modifier.align(Alignment.CenterEnd)
|
||||
.fillMaxHeight()
|
||||
.scrollbarPadding(),
|
||||
adapter = rememberScrollbarAdapter(state)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ThinSourceItem(
|
||||
|
||||
Reference in New Issue
Block a user