mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-18 16:25:27 +02:00
Refactor UnitGroupsScreen
This commit is contained in:
parent
5c81a1c675
commit
32eb7422d5
@ -21,17 +21,10 @@ package com.sadellie.unitto.data.model
|
||||
import androidx.annotation.StringRes
|
||||
import com.sadellie.unitto.core.base.R
|
||||
|
||||
val ALL_UNIT_GROUPS: List<UnitGroup> by lazy {
|
||||
UnitGroup.entries
|
||||
}
|
||||
|
||||
/**
|
||||
* As not all measurements can be converted between each other, we separate them into groups.
|
||||
* Within one group all measurements can be converted
|
||||
*/
|
||||
enum class UnitGroup(
|
||||
@StringRes val res: Int
|
||||
) {
|
||||
// NOTE: This order is used as default for new users
|
||||
LENGTH(res = R.string.unit_group_length),
|
||||
CURRENCY(res = R.string.unit_group_currency),
|
||||
MASS(res = R.string.unit_group_mass),
|
||||
|
@ -71,6 +71,10 @@ interface UserPreferencesRepository {
|
||||
|
||||
suspend fun updateShownUnitGroups(shownUnitGroups: List<UnitGroup>)
|
||||
|
||||
suspend fun addShownUnitGroup(unitGroup: UnitGroup)
|
||||
|
||||
suspend fun removeShownUnitGroup(unitGroup: UnitGroup)
|
||||
|
||||
suspend fun updateLastReadChangelog(value: String)
|
||||
|
||||
suspend fun updateVibrations(enabled: Boolean)
|
||||
|
@ -23,90 +23,105 @@ import com.sadellie.unitto.core.base.OutputFormat
|
||||
import com.sadellie.unitto.core.base.Separator
|
||||
import com.sadellie.unitto.core.base.TopLevelDestinations
|
||||
import com.sadellie.unitto.data.converter.UnitID
|
||||
import com.sadellie.unitto.data.model.ALL_UNIT_GROUPS
|
||||
import com.sadellie.unitto.data.model.UnitGroup
|
||||
import com.sadellie.unitto.data.model.UnitsListSorting
|
||||
import io.github.sadellie.themmo.core.MonetMode
|
||||
import io.github.sadellie.themmo.core.ThemingMode
|
||||
|
||||
fun Preferences.getEnableDynamicTheme(): Boolean {
|
||||
internal fun Preferences.getEnableDynamicTheme(): Boolean {
|
||||
return this[PrefsKeys.ENABLE_DYNAMIC_THEME] ?: true
|
||||
}
|
||||
|
||||
fun Preferences.getThemingMode(): ThemingMode {
|
||||
internal fun Preferences.getThemingMode(): ThemingMode {
|
||||
return this[PrefsKeys.THEMING_MODE]
|
||||
?.letTryOrNull { ThemingMode.valueOf(it) }
|
||||
?: ThemingMode.AUTO
|
||||
}
|
||||
|
||||
fun Preferences.getEnableAmoledTheme(): Boolean {
|
||||
internal fun Preferences.getEnableAmoledTheme(): Boolean {
|
||||
return this[PrefsKeys.ENABLE_AMOLED_THEME] ?: false
|
||||
}
|
||||
|
||||
fun Preferences.getCustomColor(): Long {
|
||||
internal fun Preferences.getCustomColor(): Long {
|
||||
return this[PrefsKeys.CUSTOM_COLOR] ?: 16L // From Color.Unspecified
|
||||
}
|
||||
|
||||
fun Preferences.getMonetMode(): MonetMode {
|
||||
internal fun Preferences.getMonetMode(): MonetMode {
|
||||
return this[PrefsKeys.MONET_MODE]
|
||||
?.letTryOrNull { MonetMode.valueOf(it) }
|
||||
?: MonetMode.TonalSpot
|
||||
}
|
||||
|
||||
fun Preferences.getStartingScreen(): String {
|
||||
internal fun Preferences.getStartingScreen(): String {
|
||||
return this[PrefsKeys.STARTING_SCREEN] ?: TopLevelDestinations.CALCULATOR_GRAPH
|
||||
}
|
||||
|
||||
fun Preferences.getEnableToolsExperiment(): Boolean {
|
||||
internal fun Preferences.getEnableToolsExperiment(): Boolean {
|
||||
return this[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] ?: false
|
||||
}
|
||||
|
||||
fun Preferences.getSystemFont(): Boolean {
|
||||
internal fun Preferences.getSystemFont(): Boolean {
|
||||
return this[PrefsKeys.SYSTEM_FONT] ?: false
|
||||
}
|
||||
|
||||
fun Preferences.getLastReadChangelog(): String {
|
||||
internal fun Preferences.getLastReadChangelog(): String {
|
||||
return this[PrefsKeys.LAST_READ_CHANGELOG] ?: ""
|
||||
}
|
||||
|
||||
fun Preferences.getEnableVibrations(): Boolean {
|
||||
internal fun Preferences.getEnableVibrations(): Boolean {
|
||||
return this[PrefsKeys.ENABLE_VIBRATIONS] ?: true
|
||||
}
|
||||
|
||||
fun Preferences.getRadianMode(): Boolean {
|
||||
internal fun Preferences.getRadianMode(): Boolean {
|
||||
return this[PrefsKeys.RADIAN_MODE] ?: true
|
||||
}
|
||||
|
||||
fun Preferences.getSeparator(): Int {
|
||||
internal fun Preferences.getSeparator(): Int {
|
||||
return this[PrefsKeys.SEPARATOR] ?: Separator.SPACE
|
||||
}
|
||||
|
||||
fun Preferences.getMiddleZero(): Boolean {
|
||||
internal fun Preferences.getMiddleZero(): Boolean {
|
||||
return this[PrefsKeys.MIDDLE_ZERO] ?: true
|
||||
}
|
||||
|
||||
fun Preferences.getPartialHistoryView(): Boolean {
|
||||
internal fun Preferences.getPartialHistoryView(): Boolean {
|
||||
return this[PrefsKeys.PARTIAL_HISTORY_VIEW] ?: true
|
||||
}
|
||||
|
||||
fun Preferences.getDigitsPrecision(): Int {
|
||||
internal fun Preferences.getDigitsPrecision(): Int {
|
||||
return this[PrefsKeys.DIGITS_PRECISION] ?: 3
|
||||
}
|
||||
|
||||
fun Preferences.getOutputFormat(): Int {
|
||||
internal fun Preferences.getOutputFormat(): Int {
|
||||
return this[PrefsKeys.OUTPUT_FORMAT] ?: OutputFormat.PLAIN
|
||||
}
|
||||
|
||||
fun Preferences.getUnitConverterFormatTime(): Boolean {
|
||||
internal fun Preferences.getUnitConverterFormatTime(): Boolean {
|
||||
return this[PrefsKeys.UNIT_CONVERTER_FORMAT_TIME] ?: false
|
||||
}
|
||||
|
||||
fun Preferences.getUnitConverterSorting(): UnitsListSorting {
|
||||
internal fun Preferences.getUnitConverterSorting(): UnitsListSorting {
|
||||
return this[PrefsKeys.UNIT_CONVERTER_SORTING]
|
||||
?.let { UnitsListSorting.valueOf(it) } ?: UnitsListSorting.USAGE
|
||||
}
|
||||
|
||||
fun Preferences.getShownUnitGroups(): List<UnitGroup> {
|
||||
// TODO Remove when 80% users are on sets
|
||||
internal fun Preferences.getShownUnitGroups(): List<UnitGroup> {
|
||||
// Uncomment once legacy is gone
|
||||
// return this[PrefsKeys.ENABLED_UNIT_GROUPS]
|
||||
// ?.letTryOrNull { stringSet: Set<String> ->
|
||||
// stringSet.map { UnitGroup.valueOf(it) }
|
||||
// } ?: UnitGroup.entries
|
||||
|
||||
// Try new method
|
||||
val unitGroups: List<UnitGroup>? = this[PrefsKeys.ENABLED_UNIT_GROUPS]
|
||||
?.letTryOrNull { stringSet: Set<String> ->
|
||||
stringSet.map { UnitGroup.valueOf(it) }
|
||||
}
|
||||
|
||||
if (!unitGroups.isNullOrEmpty()) return unitGroups
|
||||
|
||||
// Failed to get from sets, try old method
|
||||
return this[PrefsKeys.SHOWN_UNIT_GROUPS]
|
||||
?.letTryOrNull { list ->
|
||||
list
|
||||
@ -114,23 +129,23 @@ fun Preferences.getShownUnitGroups(): List<UnitGroup> {
|
||||
.split(",")
|
||||
.map { UnitGroup.valueOf(it) }
|
||||
}
|
||||
?: ALL_UNIT_GROUPS
|
||||
?: UnitGroup.entries
|
||||
}
|
||||
|
||||
fun Preferences.getUnitConverterFavoritesOnly(): Boolean {
|
||||
internal fun Preferences.getUnitConverterFavoritesOnly(): Boolean {
|
||||
return this[PrefsKeys.UNIT_CONVERTER_FAVORITES_ONLY]
|
||||
?: false
|
||||
}
|
||||
|
||||
fun Preferences.getLatestLeftSide(): String {
|
||||
internal fun Preferences.getLatestLeftSide(): String {
|
||||
return this[PrefsKeys.LATEST_LEFT_SIDE] ?: UnitID.kilometer
|
||||
}
|
||||
|
||||
fun Preferences.getLatestRightSide(): String {
|
||||
internal fun Preferences.getLatestRightSide(): String {
|
||||
return this[PrefsKeys.LATEST_RIGHT_SIDE] ?: UnitID.mile
|
||||
}
|
||||
|
||||
fun Preferences.getAcButton(): Boolean {
|
||||
internal fun Preferences.getAcButton(): Boolean {
|
||||
return this[PrefsKeys.AC_BUTTON] ?: true
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
package com.sadellie.unitto.data.userprefs
|
||||
|
||||
import com.sadellie.unitto.data.model.ALL_UNIT_GROUPS
|
||||
import com.sadellie.unitto.data.model.UnitGroup
|
||||
import com.sadellie.unitto.data.model.UnitsListSorting
|
||||
import com.sadellie.unitto.data.model.userprefs.AboutPreferences
|
||||
@ -90,7 +89,7 @@ data class FormattingPreferencesImpl(
|
||||
) : FormattingPreferences
|
||||
|
||||
data class UnitGroupsPreferencesImpl(
|
||||
override val shownUnitGroups: List<UnitGroup> = ALL_UNIT_GROUPS,
|
||||
override val shownUnitGroups: List<UnitGroup> = UnitGroup.entries,
|
||||
) : UnitGroupsPreferences
|
||||
|
||||
data class AddSubtractPreferencesImpl(
|
||||
|
@ -22,6 +22,7 @@ import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.core.longPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringSetPreferencesKey
|
||||
|
||||
object PrefsKeys {
|
||||
// COMMON
|
||||
@ -52,6 +53,7 @@ object PrefsKeys {
|
||||
val LATEST_LEFT_SIDE = stringPreferencesKey("LATEST_LEFT_SIDE_PREF_KEY")
|
||||
val LATEST_RIGHT_SIDE = stringPreferencesKey("LATEST_RIGHT_SIDE_PREF_KEY")
|
||||
val SHOWN_UNIT_GROUPS = stringPreferencesKey("SHOWN_UNIT_GROUPS_PREF_KEY")
|
||||
val ENABLED_UNIT_GROUPS = stringSetPreferencesKey("ENABLED_UNIT_GROUPS_PREF_KEY")
|
||||
val UNIT_CONVERTER_FAVORITES_ONLY = booleanPreferencesKey("UNIT_CONVERTER_FAVORITES_ONLY_PREF_KEY")
|
||||
val UNIT_CONVERTER_FORMAT_TIME = booleanPreferencesKey("UNIT_CONVERTER_FORMAT_TIME_PREF_KEY")
|
||||
val UNIT_CONVERTER_SORTING = stringPreferencesKey("UNIT_CONVERTER_SORTING_PREF_KEY")
|
||||
|
@ -221,7 +221,25 @@ class UserPreferencesRepositoryImpl @Inject constructor(
|
||||
|
||||
override suspend fun updateShownUnitGroups(shownUnitGroups: List<UnitGroup>) {
|
||||
dataStore.edit { preferences ->
|
||||
preferences[PrefsKeys.SHOWN_UNIT_GROUPS] = shownUnitGroups.joinToString(",")
|
||||
preferences[PrefsKeys.ENABLED_UNIT_GROUPS] = shownUnitGroups
|
||||
.map { it.name }
|
||||
.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun addShownUnitGroup(unitGroup: UnitGroup) {
|
||||
dataStore.edit { preferences ->
|
||||
preferences[PrefsKeys.ENABLED_UNIT_GROUPS] = preferences[PrefsKeys.ENABLED_UNIT_GROUPS]
|
||||
?.plus(unitGroup.name)
|
||||
?: emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun removeShownUnitGroup(unitGroup: UnitGroup) {
|
||||
dataStore.edit { preferences ->
|
||||
preferences[PrefsKeys.ENABLED_UNIT_GROUPS] = preferences[PrefsKeys.ENABLED_UNIT_GROUPS]
|
||||
?.minus(unitGroup.name)
|
||||
?: emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,6 @@ import androidx.compose.ui.unit.dp
|
||||
import com.sadellie.unitto.core.base.R
|
||||
import com.sadellie.unitto.core.ui.common.AssistChip
|
||||
import com.sadellie.unitto.core.ui.common.FilterChip
|
||||
import com.sadellie.unitto.data.model.ALL_UNIT_GROUPS
|
||||
import com.sadellie.unitto.data.model.UnitGroup
|
||||
|
||||
/**
|
||||
@ -55,7 +54,7 @@ import com.sadellie.unitto.data.model.UnitGroup
|
||||
*/
|
||||
@Composable
|
||||
internal fun ChipsRow(
|
||||
items: List<UnitGroup> = ALL_UNIT_GROUPS,
|
||||
items: List<UnitGroup> = UnitGroup.entries,
|
||||
chosenUnitGroup: UnitGroup?,
|
||||
selectAction: (UnitGroup?) -> Unit,
|
||||
navigateToSettingsAction: () -> Unit,
|
||||
@ -105,7 +104,7 @@ fun PreviewUnittoChips() {
|
||||
}
|
||||
|
||||
ChipsRow(
|
||||
items = ALL_UNIT_GROUPS,
|
||||
items = UnitGroup.entries,
|
||||
chosenUnitGroup = selected,
|
||||
selectAction = { selectAction(it) },
|
||||
navigateToSettingsAction = {},
|
||||
|
@ -41,7 +41,7 @@ import com.sadellie.unitto.core.ui.common.EmptyScreen
|
||||
import com.sadellie.unitto.core.ui.common.ListItem
|
||||
import com.sadellie.unitto.core.ui.common.NavigateUpButton
|
||||
import com.sadellie.unitto.core.ui.common.ScaffoldWithLargeTopBar
|
||||
import com.sadellie.unitto.data.model.ALL_UNIT_GROUPS
|
||||
import com.sadellie.unitto.data.model.UnitGroup
|
||||
import com.sadellie.unitto.data.model.UnitsListSorting
|
||||
import com.sadellie.unitto.data.model.userprefs.ConverterPreferences
|
||||
import com.sadellie.unitto.data.userprefs.ConverterPreferencesImpl
|
||||
@ -139,7 +139,7 @@ private fun PreviewConverterSettingsScreen() {
|
||||
outputFormat = OutputFormat.PLAIN,
|
||||
unitConverterFormatTime = false,
|
||||
unitConverterSorting = UnitsListSorting.USAGE,
|
||||
shownUnitGroups = ALL_UNIT_GROUPS,
|
||||
shownUnitGroups = UnitGroup.entries,
|
||||
unitConverterFavoritesOnly = false,
|
||||
enableToolsExperiment = false,
|
||||
latestLeftSideUnit = "kilometer",
|
||||
|
@ -34,7 +34,7 @@ import com.sadellie.unitto.feature.settings.formatting.FormattingRoute
|
||||
import com.sadellie.unitto.feature.settings.language.LanguageRoute
|
||||
import com.sadellie.unitto.feature.settings.startingscreen.StartingScreenRoute
|
||||
import com.sadellie.unitto.feature.settings.thirdparty.ThirdPartyLicensesScreen
|
||||
import com.sadellie.unitto.feature.settings.unitgroups.UnitGroupsScreen
|
||||
import com.sadellie.unitto.feature.settings.unitgroups.UnitGroupsRoute
|
||||
import io.github.sadellie.themmo.ThemmoController
|
||||
|
||||
private val graph = DrawerItem.Settings.graph
|
||||
@ -115,7 +115,7 @@ fun NavGraphBuilder.settingGraph(
|
||||
}
|
||||
|
||||
unittoStackedComposable(unitsGroupRoute) {
|
||||
UnitGroupsScreen(
|
||||
UnitGroupsRoute(
|
||||
navigateUpAction = navController::navigateUp,
|
||||
)
|
||||
}
|
||||
|
@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Unitto is a calculator for Android
|
||||
* Copyright (c) 2023-2024 Elshan Agaev
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sadellie.unitto.feature.settings.unitgroups
|
||||
|
||||
import com.sadellie.unitto.data.model.ALL_UNIT_GROUPS
|
||||
import com.sadellie.unitto.data.model.UnitGroup
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.burnoutcrew.reorderable.ItemPosition
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Repository that holds information about shown and hidden [UnitGroup]s and provides methods to
|
||||
* show/hide [UnitGroup]s.
|
||||
*/
|
||||
@Singleton
|
||||
class UnitGroupsRepository @Inject constructor() {
|
||||
|
||||
/**
|
||||
* Mutex is need needed because we work with flow (sync stuff).
|
||||
*/
|
||||
private val mutex = Mutex()
|
||||
|
||||
/**
|
||||
* Currently shown [UnitGroup]s.
|
||||
*/
|
||||
var shownUnitGroups = MutableStateFlow(listOf<UnitGroup>())
|
||||
private set
|
||||
|
||||
/**
|
||||
* Currently hidden [UnitGroup]s.
|
||||
*/
|
||||
var hiddenUnitGroups = MutableStateFlow(listOf<UnitGroup>())
|
||||
private set
|
||||
|
||||
/**
|
||||
* Sets [shownUnitGroups] and updates [hiddenUnitGroups] as a side effect. [hiddenUnitGroups] is
|
||||
* everything from [ALL_UNIT_GROUPS] that was not in [shownUnitGroups].
|
||||
*
|
||||
* @param list List of [UnitGroup]s that need to be shown.
|
||||
*/
|
||||
suspend fun updateShownGroups(list: List<UnitGroup>) {
|
||||
mutex.withLock {
|
||||
shownUnitGroups.value = list
|
||||
hiddenUnitGroups.value = ALL_UNIT_GROUPS - list.toSet()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves [UnitGroup] from [shownUnitGroups] to [hiddenUnitGroups]
|
||||
*
|
||||
* @param unitGroup [UnitGroup] to hide.
|
||||
*/
|
||||
suspend fun markUnitGroupAsHidden(unitGroup: UnitGroup) {
|
||||
mutex.withLock {
|
||||
shownUnitGroups.value = shownUnitGroups.value - unitGroup
|
||||
// Newly hidden unit will appear at the top of the list
|
||||
hiddenUnitGroups.value = listOf(unitGroup) + hiddenUnitGroups.value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves [UnitGroup] from [hiddenUnitGroups] to [shownUnitGroups]
|
||||
*
|
||||
* @param unitGroup [UnitGroup] to show.
|
||||
*/
|
||||
suspend fun markUnitGroupAsShown(unitGroup: UnitGroup) {
|
||||
mutex.withLock {
|
||||
hiddenUnitGroups.value = hiddenUnitGroups.value - unitGroup
|
||||
shownUnitGroups.value = shownUnitGroups.value + unitGroup
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves [UnitGroup] in [shownUnitGroups] from one index to another (reorder).
|
||||
*
|
||||
* @param from Position from which we need to move from
|
||||
* @param to Position where to put [UnitGroup]
|
||||
*/
|
||||
suspend fun moveShownUnitGroups(from: ItemPosition, to: ItemPosition) {
|
||||
mutex.withLock {
|
||||
shownUnitGroups.value = shownUnitGroups.value.toMutableList().apply {
|
||||
val initialIndex = shownUnitGroups.value.indexOfFirst { it == from.key }
|
||||
/**
|
||||
* No such item. Happens when dragging item and clicking "remove" while item is
|
||||
* still being dragged.
|
||||
*/
|
||||
if (initialIndex == -1) return
|
||||
|
||||
add(
|
||||
shownUnitGroups.value.indexOfFirst { it == to.key },
|
||||
removeAt(initialIndex)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -40,18 +40,23 @@ import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.sadellie.unitto.core.base.R
|
||||
import com.sadellie.unitto.core.ui.common.EmptyScreen
|
||||
import com.sadellie.unitto.core.ui.common.Header
|
||||
import com.sadellie.unitto.core.ui.common.NavigateUpButton
|
||||
import com.sadellie.unitto.core.ui.common.ScaffoldWithLargeTopBar
|
||||
import com.sadellie.unitto.data.model.UnitGroup
|
||||
import org.burnoutcrew.reorderable.ReorderableItem
|
||||
import org.burnoutcrew.reorderable.detectReorder
|
||||
import org.burnoutcrew.reorderable.detectReorderAfterLongPress
|
||||
@ -59,22 +64,52 @@ import org.burnoutcrew.reorderable.rememberReorderableLazyListState
|
||||
import org.burnoutcrew.reorderable.reorderable
|
||||
|
||||
@Composable
|
||||
internal fun UnitGroupsScreen(
|
||||
internal fun UnitGroupsRoute(
|
||||
viewModel: UnitGroupsViewModel = hiltViewModel(),
|
||||
navigateUpAction: () -> Unit,
|
||||
) {
|
||||
when (val uiState = viewModel.uiState.collectAsStateWithLifecycle().value) {
|
||||
UnitGroupsUIState.Loading -> EmptyScreen()
|
||||
is UnitGroupsUIState.Ready -> UnitGroupsScreen(
|
||||
uiState = uiState,
|
||||
navigateUpAction = navigateUpAction,
|
||||
updateShownUnitGroups = viewModel::updateShownUnitGroups,
|
||||
addShownUnitGroup = viewModel::addShownUnitGroup,
|
||||
removeShownUnitGroup = viewModel::removeShownUnitGroup,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UnitGroupsScreen(
|
||||
uiState: UnitGroupsUIState.Ready,
|
||||
navigateUpAction: () -> Unit,
|
||||
updateShownUnitGroups: (List<UnitGroup>) -> Unit,
|
||||
addShownUnitGroup: (UnitGroup) -> Unit,
|
||||
removeShownUnitGroup: (UnitGroup) -> Unit,
|
||||
) {
|
||||
ScaffoldWithLargeTopBar(
|
||||
title = stringResource(R.string.settings_unit_groups_title),
|
||||
navigationIcon = { NavigateUpButton(navigateUpAction) }
|
||||
) { paddingValues ->
|
||||
|
||||
val shownUnits = viewModel.shownUnitGroups.collectAsState()
|
||||
val hiddenUnits = viewModel.hiddenUnitGroups.collectAsState()
|
||||
|
||||
val copiedShownList = rememberUpdatedState(uiState.shownUnitGroups) as MutableState
|
||||
val state = rememberReorderableLazyListState(
|
||||
onMove = viewModel::onMove,
|
||||
canDragOver = { from, _ -> viewModel.canDragOver(from) },
|
||||
onDragEnd = { _, _ -> viewModel.onDragEnd() }
|
||||
onMove = { from, to ->
|
||||
copiedShownList.value = copiedShownList.value
|
||||
.toMutableList()
|
||||
.apply {
|
||||
// -1 for list header
|
||||
add(to.index - 1, removeAt(from.index - 1))
|
||||
}
|
||||
},
|
||||
canDragOver = { draggedOver, _ ->
|
||||
// offset by 1 for list header
|
||||
draggedOver.index in 1..(copiedShownList.value.lastIndex + 1)
|
||||
},
|
||||
onDragEnd = onDragEnd@{ from, to ->
|
||||
if (from == to) return@onDragEnd
|
||||
updateShownUnitGroups(copiedShownList.value)
|
||||
}
|
||||
)
|
||||
|
||||
LazyColumn(
|
||||
@ -90,12 +125,18 @@ internal fun UnitGroupsScreen(
|
||||
)
|
||||
}
|
||||
|
||||
items(shownUnits.value, { it }) { item ->
|
||||
items(copiedShownList.value, { it }) { item ->
|
||||
ReorderableItem(state, key = item) { isDragging ->
|
||||
val transition = updateTransition(isDragging, label = "draggedTransition")
|
||||
val background by transition.animateColor(label = "background") {
|
||||
if (it) MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.surface
|
||||
}
|
||||
val textColor by transition.animateColor(label = "background") {
|
||||
if (it) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSurface
|
||||
}
|
||||
val iconColor by transition.animateColor(label = "background") {
|
||||
if (it) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.outline
|
||||
}
|
||||
val itemPadding by transition.animateDp(label = "itemPadding") {
|
||||
if (it) 16.dp else 0.dp
|
||||
}
|
||||
@ -105,28 +146,29 @@ internal fun UnitGroupsScreen(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = itemPadding)
|
||||
.clip(CircleShape)
|
||||
.clickable { viewModel.hideUnitGroup(item) }
|
||||
.clickable { removeShownUnitGroup(item) }
|
||||
.detectReorderAfterLongPress(state),
|
||||
colors = ListItemDefaults.colors(
|
||||
containerColor = background
|
||||
containerColor = background,
|
||||
headlineColor = textColor,
|
||||
leadingIconColor = iconColor,
|
||||
trailingIconColor = iconColor,
|
||||
),
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Default.RemoveCircle,
|
||||
stringResource(R.string.settings_disable_unit_group_description),
|
||||
tint = MaterialTheme.colorScheme.outline,
|
||||
imageVector = Icons.Default.RemoveCircle,
|
||||
contentDescription = stringResource(R.string.settings_disable_unit_group_description),
|
||||
modifier = Modifier.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(false),
|
||||
onClick = { viewModel.hideUnitGroup(item) }
|
||||
onClick = { removeShownUnitGroup(item) }
|
||||
)
|
||||
)
|
||||
},
|
||||
trailingContent = {
|
||||
Icon(
|
||||
Icons.Default.DragHandle,
|
||||
stringResource(R.string.settings_reorder_unit_group_description),
|
||||
tint = MaterialTheme.colorScheme.outline,
|
||||
imageVector = Icons.Default.DragHandle,
|
||||
contentDescription = stringResource(R.string.settings_reorder_unit_group_description),
|
||||
modifier = Modifier
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
@ -148,11 +190,11 @@ internal fun UnitGroupsScreen(
|
||||
)
|
||||
}
|
||||
|
||||
items(hiddenUnits.value, { it }) {
|
||||
items(uiState.hiddenUnitGroups, { it }) {
|
||||
ListItem(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.clickable { viewModel.returnUnitGroup(it) }
|
||||
.clickable { addShownUnitGroup(it) }
|
||||
.animateItemPlacement(),
|
||||
headlineContent = { Text(stringResource(it.res)) },
|
||||
trailingContent = {
|
||||
@ -163,7 +205,7 @@ internal fun UnitGroupsScreen(
|
||||
modifier = Modifier.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(false),
|
||||
onClick = { viewModel.returnUnitGroup(it) }
|
||||
onClick = { addShownUnitGroup(it) }
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -172,3 +214,20 @@ internal fun UnitGroupsScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewUnitGroupsScreen() {
|
||||
val shownUnitGroups = UnitGroup.entries.take(4)
|
||||
|
||||
UnitGroupsScreen(
|
||||
uiState = UnitGroupsUIState.Ready(
|
||||
shownUnitGroups = shownUnitGroups,
|
||||
hiddenUnitGroups = UnitGroup.entries - shownUnitGroups.toSet()
|
||||
),
|
||||
navigateUpAction = {},
|
||||
updateShownUnitGroups = {},
|
||||
addShownUnitGroup = {},
|
||||
removeShownUnitGroup = {},
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Unitto is a calculator for Android
|
||||
* Copyright (c) 2024 Elshan Agaev
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sadellie.unitto.feature.settings.unitgroups
|
||||
|
||||
import com.sadellie.unitto.data.model.UnitGroup
|
||||
|
||||
internal sealed class UnitGroupsUIState {
|
||||
data object Loading: UnitGroupsUIState()
|
||||
|
||||
data class Ready(
|
||||
val shownUnitGroups: List<UnitGroup>,
|
||||
val hiddenUnitGroups: List<UnitGroup>
|
||||
): UnitGroupsUIState()
|
||||
}
|
@ -20,75 +20,52 @@ package com.sadellie.unitto.feature.settings.unitgroups
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.sadellie.unitto.data.common.stateIn
|
||||
import com.sadellie.unitto.data.model.UnitGroup
|
||||
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import org.burnoutcrew.reorderable.ItemPosition
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class UnitGroupsViewModel @Inject constructor(
|
||||
internal class UnitGroupsViewModel @Inject constructor(
|
||||
private val userPrefsRepository: UserPreferencesRepository,
|
||||
private val unitGroupsRepository: UnitGroupsRepository,
|
||||
) : ViewModel() {
|
||||
val shownUnitGroups = unitGroupsRepository.shownUnitGroups
|
||||
val hiddenUnitGroups = unitGroupsRepository.hiddenUnitGroups
|
||||
|
||||
/**
|
||||
* @see UnitGroupsRepository.markUnitGroupAsHidden
|
||||
* @see UserPreferencesRepository.updateShownUnitGroups
|
||||
*/
|
||||
fun hideUnitGroup(unitGroup: UnitGroup) {
|
||||
viewModelScope.launch {
|
||||
unitGroupsRepository.markUnitGroupAsHidden(unitGroup)
|
||||
userPrefsRepository.updateShownUnitGroups(unitGroupsRepository.shownUnitGroups.value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UnitGroupsRepository.markUnitGroupAsShown
|
||||
* @see UserPreferencesRepository.updateShownUnitGroups
|
||||
*/
|
||||
fun returnUnitGroup(unitGroup: UnitGroup) {
|
||||
viewModelScope.launch {
|
||||
unitGroupsRepository.markUnitGroupAsShown(unitGroup)
|
||||
userPrefsRepository.updateShownUnitGroups(unitGroupsRepository.shownUnitGroups.value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UnitGroupsRepository.moveShownUnitGroups
|
||||
*/
|
||||
fun onMove(from: ItemPosition, to: ItemPosition) {
|
||||
viewModelScope.launch {
|
||||
unitGroupsRepository.moveShownUnitGroups(from, to)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UserPreferencesRepository.updateShownUnitGroups
|
||||
*/
|
||||
fun onDragEnd() {
|
||||
viewModelScope.launch {
|
||||
userPrefsRepository.updateShownUnitGroups(unitGroupsRepository.shownUnitGroups.value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent from dragging over non-draggable items (headers and hidden)
|
||||
*
|
||||
* @param pos Position we are dragging over.
|
||||
* @return True if can drag over given item.
|
||||
*/
|
||||
fun canDragOver(pos: ItemPosition) = shownUnitGroups.value.any { it == pos.key }
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
unitGroupsRepository.updateShownGroups(
|
||||
userPrefsRepository.unitGroupsPrefs.first().shownUnitGroups
|
||||
val uiState = userPrefsRepository.unitGroupsPrefs
|
||||
.map {
|
||||
UnitGroupsUIState.Ready(
|
||||
shownUnitGroups = it.shownUnitGroups,
|
||||
hiddenUnitGroups = UnitGroup.entries - it.shownUnitGroups.toSet()
|
||||
)
|
||||
}
|
||||
.stateIn(viewModelScope, UnitGroupsUIState.Loading)
|
||||
|
||||
/**
|
||||
* @see UserPreferencesRepository.removeShownUnitGroup
|
||||
*/
|
||||
fun removeShownUnitGroup(unitGroup: UnitGroup) {
|
||||
viewModelScope.launch {
|
||||
userPrefsRepository.removeShownUnitGroup(unitGroup)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UserPreferencesRepository.addShownUnitGroup
|
||||
*/
|
||||
fun addShownUnitGroup(unitGroup: UnitGroup) {
|
||||
viewModelScope.launch {
|
||||
userPrefsRepository.addShownUnitGroup(unitGroup)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UserPreferencesRepository.updateShownUnitGroups
|
||||
*/
|
||||
fun updateShownUnitGroups(unitGroups: List<UnitGroup>) {
|
||||
viewModelScope.launch {
|
||||
userPrefsRepository.updateShownUnitGroups(unitGroups)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user