Sources menu improvements

This commit is contained in:
Syer10
2022-08-25 00:58:46 -04:00
parent 11920e20f9
commit cfdeb70e96
2 changed files with 147 additions and 44 deletions

View File

@@ -6,9 +6,12 @@
package ca.gosyer.jui.ui.sources.home 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.data.source.SourceRepositoryImpl
import ca.gosyer.jui.domain.source.model.Source import ca.gosyer.jui.domain.source.model.Source
import ca.gosyer.jui.domain.source.service.CatalogPreferences 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.ContextWrapper
import ca.gosyer.jui.uicore.vm.ViewModel import ca.gosyer.jui.uicore.vm.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -37,9 +40,39 @@ class SourceHomeScreenViewModel @Inject constructor(
val languages = _languages.asStateFlow() val languages = _languages.asStateFlow()
val sources = combine(installedSources, languages) { installedSources, languages -> val sources = combine(installedSources, languages) { installedSources, languages ->
installedSources.filter { val all = MR.strings.all.toPlatformString()
it.lang in languages || it.id == Source.LOCAL_SOURCE_ID 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()) }.stateIn(scope, SharingStarted.Eagerly, emptyList())
val sourceLanguages = installedSources.map { sources -> val sourceLanguages = installedSources.map { sources ->
@@ -56,10 +89,7 @@ class SourceHomeScreenViewModel @Inject constructor(
private fun getSources() { private fun getSources() {
sourceHandler.getSourceList() sourceHandler.getSourceList()
.onEach { .onEach {
installedSources.value = it.sortedWith( installedSources.value = it
compareBy(String.CASE_INSENSITIVE_ORDER, Source::displayLang)
.thenBy(String.CASE_INSENSITIVE_ORDER, Source::name)
)
_isLoading.value = false _isLoading.value = false
} }
.catch { .catch {
@@ -82,3 +112,8 @@ class SourceHomeScreenViewModel @Inject constructor(
private val log = logging() private val log = logging()
} }
} }
sealed class SourceUI {
data class Header(val header: String) : SourceUI()
data class SourceItem(val source: Source): SourceUI()
}

View File

@@ -7,6 +7,7 @@
package ca.gosyer.jui.ui.sources.home.components package ca.gosyer.jui.ui.sources.home.components
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row 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.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells 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.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.grid.rememberLazyGridState 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.Modifier
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.FilterQuality 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.style.TextOverflow
import androidx.compose.ui.text.toUpperCase
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import ca.gosyer.jui.domain.source.model.Source 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.ActionItem
import ca.gosyer.jui.ui.base.navigation.Toolbar import ca.gosyer.jui.ui.base.navigation.Toolbar
import ca.gosyer.jui.ui.extensions.components.LanguageDialog 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.LoadingScreen
import ca.gosyer.jui.uicore.components.VerticalScrollbar import ca.gosyer.jui.uicore.components.VerticalScrollbar
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
@@ -64,7 +65,7 @@ import com.vanpra.composematerialdialogs.rememberMaterialDialogState
fun SourceHomeScreenContent( fun SourceHomeScreenContent(
onAddSource: (Source) -> Unit, onAddSource: (Source) -> Unit,
isLoading: Boolean, isLoading: Boolean,
sources: List<Source>, sources: List<SourceUI>,
languages: Set<String>, languages: Set<String>,
sourceLanguages: List<String>, sourceLanguages: List<String>,
setEnabledLanguages: (Set<String>) -> Unit, setEnabledLanguages: (Set<String>) -> Unit,
@@ -82,44 +83,15 @@ fun SourceHomeScreenContent(
submitSearch = submitSearch submitSearch = submitSearch
) )
} }
) { ) { padding ->
if (sources.isEmpty()) { if (sources.isEmpty()) {
LoadingScreen(isLoading) LoadingScreen(isLoading)
} else { } else {
BoxWithConstraints(Modifier.fillMaxSize().padding(it), Alignment.TopCenter) { BoxWithConstraints(Modifier.fillMaxSize().padding(padding), Alignment.TopCenter) {
if (maxWidth > 720.dp) { if (maxWidth > 720.dp) {
val state = rememberLazyGridState() WideSourcesMenu(sources, onAddSource)
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)
)
} else { } else {
val state = rememberLazyListState() ThinSourcesMenu(sources, onAddSource)
LazyColumn(state = state) {
items(sources) { source ->
ThinSourceItem(
source,
onSourceClicked = onAddSource
)
}
}
VerticalScrollbar(
modifier = Modifier.align(Alignment.CenterEnd)
.fillMaxHeight()
.scrollbarPadding(),
adapter = rememberScrollbarAdapter(state)
)
} }
} }
} }
@@ -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 @Composable
fun WideSourceItem( fun WideSourceItem(
source: Source, source: Source,
@@ -182,7 +206,7 @@ fun WideSourceItem(
) )
Spacer(Modifier.height(4.dp)) Spacer(Modifier.height(4.dp))
Text( Text(
"${source.name} (${source.displayLang.toUpperCase(Locale.current)})", source.name,
color = MaterialTheme.colors.onBackground, color = MaterialTheme.colors.onBackground,
maxLines = 2, maxLines = 2,
overflow = TextOverflow.Ellipsis 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 @Composable
fun ThinSourceItem( fun ThinSourceItem(