diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d7487e5d..a1a9e1af 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ composeGradle = "1.1.1" voyager = "1.0.0-beta16" accompanist = "0.24.4" kamel = "0.3.0" -materialDialogs = "0.7.0" +materialDialogs = "0.7.1" # Android androidGradle = "7.0.4" diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/base/prefs/PreferencesUiBuilder.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/base/prefs/PreferencesUiBuilder.kt index 876dda68..62157c85 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/base/prefs/PreferencesUiBuilder.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/base/prefs/PreferencesUiBuilder.kt @@ -29,25 +29,18 @@ import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Checkbox import androidx.compose.material.ContentAlpha import androidx.compose.material.Icon import androidx.compose.material.LocalContentColor import androidx.compose.material.MaterialTheme -import androidx.compose.material.RadioButton import androidx.compose.material.Surface import androidx.compose.material.Switch import androidx.compose.material.Text @@ -60,7 +53,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -84,6 +76,8 @@ import com.vanpra.composematerialdialogs.MaterialDialogButtons import com.vanpra.composematerialdialogs.MaterialDialogState import com.vanpra.composematerialdialogs.TextFieldStyle import com.vanpra.composematerialdialogs.input +import com.vanpra.composematerialdialogs.listItemsMultiChoice +import com.vanpra.composematerialdialogs.listItemsSingleChoice import com.vanpra.composematerialdialogs.rememberMaterialDialogState import com.vanpra.composematerialdialogs.title @@ -274,32 +268,16 @@ fun ChoiceDialog( title(title) Box { val listState = rememberLazyListState() - LazyColumn(Modifier.defaultMinSize(minHeight = 64.dp).fillMaxWidth(), listState) { - items(items) { (value, text) -> - Row( - modifier = Modifier - .requiredHeight(48.dp) - .fillMaxWidth() - .clickable( - onClick = { - onSelected(value) - state.hide() - } - ) - .padding(horizontal = 8.dp), - verticalAlignment = Alignment.CenterVertically - ) { - RadioButton( - selected = value == selected, - onClick = { - onSelected(value) - state.hide() - }, - ) - Text(text = text, modifier = Modifier.padding(start = 24.dp)) - } - } - } + listItemsSingleChoice( + items.map { it.second }, + state = listState, + initialSelection = items.indexOfFirst { it.first == selected }.takeUnless { it == -1 }, + waitForPositiveButton = false, + onChoiceChange = { + onSelected(items[it].first) + submit() + } + ) VerticalScrollbar( rememberScrollbarAdapter(listState), Modifier.align(Alignment.CenterEnd) @@ -319,13 +297,10 @@ fun MultiSelectDialog( onFinished: (List) -> Unit, title: String, ) { - val checked = remember(selected) { selected.orEmpty().toMutableStateList() } MaterialDialog( state, buttons = { - positiveButton(stringResource(MR.strings.action_ok)) { - onFinished(checked) - } + positiveButton(stringResource(MR.strings.action_ok)) negativeButton(stringResource(MR.strings.action_cancel)) }, properties = getMaterialDialogProperties(), @@ -335,35 +310,20 @@ fun MultiSelectDialog( } ) { title(title) - val listState = rememberLazyListState() Box { - LazyColumn(Modifier.defaultMinSize(minHeight = 64.dp).fillMaxWidth(), listState) { - items(items) { (value, text) -> - Row( - modifier = Modifier - .requiredHeight(48.dp) - .fillMaxWidth() - .clickable( - onClick = { - if (value in checked) { - checked -= value - } else { - checked += value - } - } - ) - .padding(horizontal = 8.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Checkbox( - checked = value in checked, - onCheckedChange = null, - ) - Text(text = text, modifier = Modifier.padding(start = 24.dp)) - } - } - item { Spacer(Modifier.height(80.dp)) } - } + val listState = rememberLazyListState() + listItemsMultiChoice( + items.map { it.second }, + state = listState, + initialSelection = selected?.mapNotNull { item -> + items.indexOfFirst { it.first == item }.takeUnless { it == -1 } + } + ?.toSet() + .orEmpty(), + onCheckedChange = { indexes -> + onFinished(indexes.map { items[it].first }) + } + ) VerticalScrollbar( rememberScrollbarAdapter(listState), Modifier.align(Alignment.CenterEnd) diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/ExtensionsScreenViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/ExtensionsScreenViewModel.kt index e9c0c647..793bc720 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/ExtensionsScreenViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/ExtensionsScreenViewModel.kt @@ -49,8 +49,8 @@ class ExtensionsScreenViewModel @Inject constructor( }.stateIn(scope, SharingStarted.Eagerly, emptyMap()) val availableLangs = extensionList.filterNotNull().map { langs -> - langs.map { it.lang }.toSet() - }.stateIn(scope, SharingStarted.Eagerly, emptySet()) + langs.map { it.lang }.distinct() + }.stateIn(scope, SharingStarted.Eagerly, emptyList()) private val _isLoading = MutableStateFlow(true) val isLoading = _isLoading.asStateFlow() diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/components/ExtensionsScreenContent.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/components/ExtensionsScreenContent.kt index d5b458b1..6c7b64dd 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/components/ExtensionsScreenContent.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/extensions/components/ExtensionsScreenContent.kt @@ -7,8 +7,6 @@ package ca.gosyer.jui.ui.extensions.components import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -27,16 +25,12 @@ import androidx.compose.material.Button import androidx.compose.material.ContentAlpha import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold -import androidx.compose.material.Switch import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Translate import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue import androidx.compose.runtime.remember -import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -64,6 +58,7 @@ import ca.gosyer.jui.uicore.image.KamelImage import ca.gosyer.jui.uicore.resources.stringResource import com.vanpra.composematerialdialogs.MaterialDialog import com.vanpra.composematerialdialogs.MaterialDialogState +import com.vanpra.composematerialdialogs.listItemsMultiChoice import com.vanpra.composematerialdialogs.rememberMaterialDialogState import com.vanpra.composematerialdialogs.title import io.kamel.image.lazyPainterResource @@ -75,7 +70,7 @@ fun ExtensionsScreenContent( query: String?, setQuery: (String) -> Unit, enabledLangs: Set, - availableLangs: Set, + availableLangs: List, setEnabledLanguages: (Set) -> Unit, installExtension: (Extension) -> Unit, updateExtension: (Extension) -> Unit, @@ -205,51 +200,34 @@ fun ExtensionItem( fun LanguageDialog( state: MaterialDialogState, enabledLangs: Set, - availableLangs: Set, + availableLangs: List, setLangs: (Set) -> Unit ) { - val modifiedLangs = remember(enabledLangs) { enabledLangs.toMutableStateList() } MaterialDialog( state, buttons = { - positiveButton(stringResource(MR.strings.action_ok)) { - setLangs(modifiedLangs.toSet()) - } + positiveButton(stringResource(MR.strings.action_ok)) negativeButton(stringResource(MR.strings.action_cancel)) }, properties = getMaterialDialogProperties(), ) { title(BuildKonfig.NAME) + Box { val locale = remember { Locale.current } val listState = rememberLazyListState() - LazyColumn(Modifier.fillMaxWidth(), listState) { - items(availableLangs.toList()) { lang -> - Row( - modifier = Modifier.fillMaxWidth() - .height(48.dp) - .clickable { - if (lang in modifiedLangs) { - modifiedLangs -= lang - } else { - modifiedLangs += lang - } - } - .padding(horizontal = 16.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - val langName by derivedStateOf { - Locale(lang).getDisplayName(locale).ifBlank { lang.capitalize(Locale.current) } - } - Text(langName) - Switch( - checked = lang in modifiedLangs, - onCheckedChange = null - ) - } - } - } + listItemsMultiChoice( + list = availableLangs.map { lang -> + Locale(lang).getDisplayName(locale).ifBlank { lang.capitalize(Locale.current) } + }, + state = listState, + initialSelection = enabledLangs.mapNotNull { lang -> + availableLangs.indexOfFirst { it == lang }.takeUnless { it == -1 } + }.toSet(), + onCheckedChange = { indexes -> + setLangs(indexes.map { availableLangs[it] }.toSet()) + } + ) VerticalScrollbar( rememberScrollbarAdapter(listState), Modifier.align(Alignment.CenterEnd) diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/components/MangaMenu.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/components/MangaMenu.kt index 815b8021..fc8fa5bd 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/components/MangaMenu.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/components/MangaMenu.kt @@ -6,8 +6,6 @@ package ca.gosyer.jui.ui.manga.components -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column @@ -17,23 +15,17 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.Card -import androidx.compose.material.Checkbox import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.FilterQuality @@ -54,6 +46,7 @@ import ca.gosyer.jui.uicore.resources.stringResource import com.google.accompanist.flowlayout.FlowRow import com.vanpra.composematerialdialogs.MaterialDialog import com.vanpra.composematerialdialogs.MaterialDialogState +import com.vanpra.composematerialdialogs.listItemsMultiChoice import com.vanpra.composematerialdialogs.title import io.kamel.image.lazyPainterResource @@ -171,44 +164,28 @@ fun CategorySelectDialog( oldCategories: List, onPositiveClick: (List, List) -> Unit ) { - val enabledCategories = remember(oldCategories) { oldCategories.toMutableStateList() } MaterialDialog( state, buttons = { - positiveButton(stringResource(MR.strings.action_ok)) { - onPositiveClick(enabledCategories.toList(), oldCategories) - } + positiveButton(stringResource(MR.strings.action_ok)) negativeButton(stringResource(MR.strings.action_cancel)) }, properties = getMaterialDialogProperties(), ) { title(stringResource(MR.strings.select_categories)) - val listState = rememberLazyListState() + Box { - LazyColumn(state = listState) { - items(categories) { category -> - Row( - Modifier.fillMaxWidth() - .height(48.dp) - .clickable { - if (category in enabledCategories) { - enabledCategories -= category - } else { - enabledCategories += category - } - } - .padding(horizontal = 16.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Text(category.name, style = MaterialTheme.typography.subtitle1) - Checkbox( - category in enabledCategories, - onCheckedChange = null - ) - } - } - } + val listState = rememberLazyListState() + listItemsMultiChoice( + list = categories.map { it.name }, + state = listState, + initialSelection = oldCategories.mapNotNull { category -> + categories.indexOfFirst { it.id == category.id }.takeUnless { it == -1 } + }.toSet(), + onCheckedChange = { indexes -> + onPositiveClick(indexes.map { categories[it] }, oldCategories) + } + ) VerticalScrollbar( modifier = Modifier.align(Alignment.CenterEnd) .fillMaxHeight() diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/settings/SettingsBackupScreen.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/settings/SettingsBackupScreen.kt index 05036d53..a38d6aa2 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/settings/SettingsBackupScreen.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/settings/SettingsBackupScreen.kt @@ -12,13 +12,10 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Icon -import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold -import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Check import androidx.compose.material.icons.rounded.Warning @@ -58,6 +55,7 @@ import cafe.adriel.voyager.core.screen.ScreenKey import cafe.adriel.voyager.core.screen.uniqueScreenKey import com.vanpra.composematerialdialogs.MaterialDialog import com.vanpra.composematerialdialogs.MaterialDialogState +import com.vanpra.composematerialdialogs.listItems import com.vanpra.composematerialdialogs.rememberMaterialDialogState import com.vanpra.composematerialdialogs.title import io.ktor.client.plugins.onDownload @@ -365,14 +363,7 @@ private fun MissingSourcesDialog( title(stringResource(MR.strings.missing_sources)) Box { val listState = rememberLazyListState() - LazyColumn(Modifier.fillMaxSize(), state = listState) { - item { - Text(stringResource(MR.strings.missing_sources), style = MaterialTheme.typography.subtitle2) - } - items(missingSources) { - Text(it) - } - } + listItems(missingSources, state = listState) VerticalScrollbar( rememberScrollbarAdapter(listState), Modifier.align(Alignment.CenterEnd) diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/home/SourceHomeScreenViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/home/SourceHomeScreenViewModel.kt index 41d016b6..0acb0aa0 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/home/SourceHomeScreenViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/home/SourceHomeScreenViewModel.kt @@ -43,8 +43,8 @@ class SourceHomeScreenViewModel @Inject constructor( }.stateIn(scope, SharingStarted.Eagerly, emptyList()) val sourceLanguages = installedSources.map { sources -> - sources.map { it.lang }.toSet() - Source.LOCAL_SOURCE_LANG - }.stateIn(scope, SharingStarted.Eagerly, emptySet()) + sources.map { it.lang }.distinct() - Source.LOCAL_SOURCE_LANG + }.stateIn(scope, SharingStarted.Eagerly, emptyList()) private val _query = MutableStateFlow("") val query = _query.asStateFlow() diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/home/components/SourceHomeScreenContent.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/home/components/SourceHomeScreenContent.kt index cbb2704c..8468afe4 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/home/components/SourceHomeScreenContent.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/home/components/SourceHomeScreenContent.kt @@ -66,7 +66,7 @@ fun SourceHomeScreenContent( isLoading: Boolean, sources: List, languages: Set, - sourceLanguages: Set, + sourceLanguages: List, setEnabledLanguages: (Set) -> Unit, query: String, setQuery: (String) -> Unit,