Separate User Preferences

This commit is contained in:
Sad Ellie 2023-05-18 23:43:08 +03:00
parent 1b61b4bd85
commit 86dce329ac
20 changed files with 373 additions and 309 deletions

View File

@ -120,8 +120,7 @@ dependencies {
implementation(project(mapOf("path" to ":feature:calculator"))) implementation(project(mapOf("path" to ":feature:calculator")))
implementation(project(mapOf("path" to ":feature:settings"))) implementation(project(mapOf("path" to ":feature:settings")))
implementation(project(mapOf("path" to ":feature:unitslist"))) implementation(project(mapOf("path" to ":feature:unitslist")))
implementation(project(mapOf("path" to ":feature:datedifference"))) implementation(project(mapOf("path" to ":feature:datedifference")))
implementation(project(mapOf("path" to ":data:units")))
implementation(project(mapOf("path" to ":data:model"))) implementation(project(mapOf("path" to ":data:model")))
implementation(project(mapOf("path" to ":data:userprefs"))) implementation(project(mapOf("path" to ":data:userprefs")))
implementation(project(mapOf("path" to ":core:ui"))) implementation(project(mapOf("path" to ":core:ui")))

View File

@ -40,13 +40,11 @@ internal class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val userPrefsFlow = userPrefsRepository.userPreferencesFlow
setContent { setContent {
val userPrefs = userPrefsFlow val uiPrefs = userPrefsRepository.uiPreferencesFlow
.collectAsStateWithLifecycle(null).value .collectAsStateWithLifecycle(null).value
if (userPrefs != null) UnittoApp(userPrefs) if (uiPrefs != null) UnittoApp(uiPrefs)
} }
} }

View File

@ -44,23 +44,23 @@ import com.sadellie.unitto.core.ui.model.DrawerItems
import com.sadellie.unitto.core.ui.theme.AppTypography import com.sadellie.unitto.core.ui.theme.AppTypography
import com.sadellie.unitto.core.ui.theme.DarkThemeColors import com.sadellie.unitto.core.ui.theme.DarkThemeColors
import com.sadellie.unitto.core.ui.theme.LightThemeColors import com.sadellie.unitto.core.ui.theme.LightThemeColors
import com.sadellie.unitto.data.userprefs.UserPreferences import com.sadellie.unitto.data.userprefs.UIPreferences
import io.github.sadellie.themmo.Themmo import io.github.sadellie.themmo.Themmo
import io.github.sadellie.themmo.rememberThemmoController import io.github.sadellie.themmo.rememberThemmoController
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
internal fun UnittoApp(userPrefs: UserPreferences) { internal fun UnittoApp(uiPrefs: UIPreferences) {
val themmoController = rememberThemmoController( val themmoController = rememberThemmoController(
lightColorScheme = LightThemeColors, lightColorScheme = LightThemeColors,
darkColorScheme = DarkThemeColors, darkColorScheme = DarkThemeColors,
// Anything below will not be called if theming mode is still loading from DataStore // Anything below will not be called if theming mode is still loading from DataStore
themingMode = userPrefs.themingMode, themingMode = uiPrefs.themingMode,
dynamicThemeEnabled = userPrefs.enableDynamicTheme, dynamicThemeEnabled = uiPrefs.enableDynamicTheme,
amoledThemeEnabled = userPrefs.enableAmoledTheme, amoledThemeEnabled = uiPrefs.enableAmoledTheme,
customColor = userPrefs.customColor, customColor = uiPrefs.customColor,
monetMode = userPrefs.monetMode monetMode = uiPrefs.monetMode
) )
val navController = rememberNavController() val navController = rememberNavController()
val sysUiController = rememberSystemUiController() val sysUiController = rememberSystemUiController()
@ -131,7 +131,7 @@ internal fun UnittoApp(userPrefs: UserPreferences) {
UnittoNavigation( UnittoNavigation(
navController = navController, navController = navController,
themmoController = it, themmoController = it,
startDestination = userPrefs.startingScreen, startDestination = uiPrefs.startingScreen,
openDrawer = { drawerScope.launch { drawerState.open() } } openDrawer = { drawerScope.launch { drawerState.open() } }
) )
} }

View File

@ -30,7 +30,6 @@ import com.sadellie.unitto.feature.calculator.navigation.calculatorScreen
import com.sadellie.unitto.feature.converter.ConverterViewModel import com.sadellie.unitto.feature.converter.ConverterViewModel
import com.sadellie.unitto.feature.converter.navigation.converterScreen import com.sadellie.unitto.feature.converter.navigation.converterScreen
import com.sadellie.unitto.feature.datedifference.navigation.dateDifferenceScreen import com.sadellie.unitto.feature.datedifference.navigation.dateDifferenceScreen
import com.sadellie.unitto.feature.settings.SettingsViewModel
import com.sadellie.unitto.feature.settings.navigation.navigateToSettings import com.sadellie.unitto.feature.settings.navigation.navigateToSettings
import com.sadellie.unitto.feature.settings.navigation.navigateToUnitGroups import com.sadellie.unitto.feature.settings.navigation.navigateToUnitGroups
import com.sadellie.unitto.feature.settings.navigation.settingGraph import com.sadellie.unitto.feature.settings.navigation.settingGraph
@ -50,7 +49,6 @@ internal fun UnittoNavigation(
) { ) {
val converterViewModel: ConverterViewModel = hiltViewModel() val converterViewModel: ConverterViewModel = hiltViewModel()
val unitsListViewModel: UnitsListViewModel = hiltViewModel() val unitsListViewModel: UnitsListViewModel = hiltViewModel()
val settingsViewModel: SettingsViewModel = hiltViewModel()
NavHost( NavHost(
navController = navController, navController = navController,
@ -90,7 +88,6 @@ internal fun UnittoNavigation(
) )
settingGraph( settingGraph(
settingsViewModel = settingsViewModel,
themmoController = themmoController, themmoController = themmoController,
navController = navController, navController = navController,
menuButtonClick = openDrawer menuButtonClick = openDrawer

View File

@ -1,115 +0,0 @@
/*
* Unitto is a unit converter for Android
* Copyright (c) 2022-2023 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.data.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)
)
}
}
}
}

View File

@ -39,6 +39,7 @@ import io.github.sadellie.themmo.MonetMode
import io.github.sadellie.themmo.ThemingMode import io.github.sadellie.themmo.ThemingMode
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import java.io.IOException import java.io.IOException
import javax.inject.Inject import javax.inject.Inject
@ -84,6 +85,29 @@ data class UserPreferences(
val unitConverterSorting: UnitsListSorting = UnitsListSorting.USAGE, val unitConverterSorting: UnitsListSorting = UnitsListSorting.USAGE,
) )
data class UIPreferences(
val themingMode: ThemingMode = ThemingMode.AUTO,
val enableDynamicTheme: Boolean = false,
val enableAmoledTheme: Boolean = false,
val customColor: Color = Color.Unspecified,
val monetMode: MonetMode = MonetMode.TONAL_SPOT,
val startingScreen: String = TopLevelDestinations.Converter.route,
)
data class MainPreferences(
val digitsPrecision: Int = 3,
val separator: Int = Separator.SPACES,
val outputFormat: Int = OutputFormat.PLAIN,
val latestLeftSideUnit: String = MyUnitIDS.kilometer,
val latestRightSideUnit: String = MyUnitIDS.mile,
val shownUnitGroups: List<UnitGroup> = ALL_UNIT_GROUPS,
val enableVibrations: Boolean = true,
val radianMode: Boolean = true,
val unitConverterFavoritesOnly: Boolean = false,
val unitConverterFormatTime: Boolean = false,
val unitConverterSorting: UnitsListSorting = UnitsListSorting.USAGE,
)
/** /**
* Repository that works with DataStore * Repository that works with DataStore
*/ */
@ -112,7 +136,7 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
val UNIT_CONVERTER_SORTING = stringPreferencesKey("UNIT_CONVERTER_SORTING_PREF_KEY") val UNIT_CONVERTER_SORTING = stringPreferencesKey("UNIT_CONVERTER_SORTING_PREF_KEY")
} }
val userPreferencesFlow: Flow<UserPreferences> = dataStore.data val uiPreferencesFlow: Flow<UIPreferences> = dataStore.data
.catch { exception -> .catch { exception ->
if (exception is IOException) { if (exception is IOException) {
emit(emptyPreferences()) emit(emptyPreferences())
@ -128,6 +152,27 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
val customColor: Color = preferences[PrefsKeys.CUSTOM_COLOR]?.let { Color(it.toULong()) } ?: Color.Unspecified val customColor: Color = preferences[PrefsKeys.CUSTOM_COLOR]?.let { Color(it.toULong()) } ?: Color.Unspecified
val monetMode: MonetMode = preferences[PrefsKeys.MONET_MODE]?.let { MonetMode.valueOf(it) } val monetMode: MonetMode = preferences[PrefsKeys.MONET_MODE]?.let { MonetMode.valueOf(it) }
?: MonetMode.TONAL_SPOT ?: MonetMode.TONAL_SPOT
val startingScreen: String = preferences[PrefsKeys.STARTING_SCREEN] ?: TopLevelDestinations.Converter.route
UIPreferences(
themingMode = themingMode,
enableDynamicTheme = enableDynamicTheme,
enableAmoledTheme = enableAmoledTheme,
customColor = customColor,
monetMode = monetMode,
startingScreen = startingScreen
)
}
val mainPreferencesFlow: Flow<MainPreferences> = dataStore.data
.catch { exception ->
if (exception is IOException) {
emit(emptyPreferences())
} else {
throw exception
}
}
.map { preferences ->
val digitsPrecision: Int = preferences[PrefsKeys.DIGITS_PRECISION] ?: 3 val digitsPrecision: Int = preferences[PrefsKeys.DIGITS_PRECISION] ?: 3
val separator: Int = preferences[PrefsKeys.SEPARATOR] ?: Separator.SPACES val separator: Int = preferences[PrefsKeys.SEPARATOR] ?: Separator.SPACES
val outputFormat: Int = preferences[PrefsKeys.OUTPUT_FORMAT] ?: OutputFormat.PLAIN val outputFormat: Int = preferences[PrefsKeys.OUTPUT_FORMAT] ?: OutputFormat.PLAIN
@ -147,19 +192,12 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
} ?: ALL_UNIT_GROUPS } ?: ALL_UNIT_GROUPS
val enableVibrations: Boolean = preferences[PrefsKeys.ENABLE_VIBRATIONS] ?: true val enableVibrations: Boolean = preferences[PrefsKeys.ENABLE_VIBRATIONS] ?: true
val enableToolsExperiment: Boolean = preferences[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] ?: false
val startingScreen: String = preferences[PrefsKeys.STARTING_SCREEN] ?: TopLevelDestinations.Converter.route
val radianMode: Boolean = preferences[PrefsKeys.RADIAN_MODE] ?: true val radianMode: Boolean = preferences[PrefsKeys.RADIAN_MODE] ?: true
val unitConverterFavoritesOnly: Boolean = preferences[PrefsKeys.UNIT_CONVERTER_FAVORITES_ONLY] ?: false val unitConverterFavoritesOnly: Boolean = preferences[PrefsKeys.UNIT_CONVERTER_FAVORITES_ONLY] ?: false
val unitConverterFormatTime: Boolean = preferences[PrefsKeys.UNIT_CONVERTER_FORMAT_TIME] ?: false val unitConverterFormatTime: Boolean = preferences[PrefsKeys.UNIT_CONVERTER_FORMAT_TIME] ?: false
val unitConverterSorting: UnitsListSorting = preferences[PrefsKeys.UNIT_CONVERTER_SORTING]?.let { UnitsListSorting.valueOf(it) } ?: UnitsListSorting.USAGE val unitConverterSorting: UnitsListSorting = preferences[PrefsKeys.UNIT_CONVERTER_SORTING]?.let { UnitsListSorting.valueOf(it) } ?: UnitsListSorting.USAGE
UserPreferences( MainPreferences(
themingMode = themingMode,
enableDynamicTheme = enableDynamicTheme,
enableAmoledTheme = enableAmoledTheme,
customColor = customColor,
monetMode = monetMode,
digitsPrecision = digitsPrecision, digitsPrecision = digitsPrecision,
separator = separator, separator = separator,
outputFormat = outputFormat, outputFormat = outputFormat,
@ -167,8 +205,6 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
latestRightSideUnit = latestRightSideUnit, latestRightSideUnit = latestRightSideUnit,
shownUnitGroups = shownUnitGroups, shownUnitGroups = shownUnitGroups,
enableVibrations = enableVibrations, enableVibrations = enableVibrations,
enableToolsExperiment = enableToolsExperiment,
startingScreen = startingScreen,
radianMode = radianMode, radianMode = radianMode,
unitConverterFavoritesOnly = unitConverterFavoritesOnly, unitConverterFavoritesOnly = unitConverterFavoritesOnly,
unitConverterFormatTime = unitConverterFormatTime, unitConverterFormatTime = unitConverterFormatTime,
@ -176,6 +212,31 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
) )
} }
val allPreferencesFlow = combine(
mainPreferencesFlow, uiPreferencesFlow
) { main, ui ->
return@combine UserPreferences(
themingMode = ui.themingMode,
enableDynamicTheme = ui.enableDynamicTheme,
enableAmoledTheme = ui.enableAmoledTheme,
customColor = ui.customColor,
monetMode = ui.monetMode,
digitsPrecision = main.digitsPrecision,
separator = main.separator,
outputFormat = main.outputFormat,
latestLeftSideUnit = main.latestLeftSideUnit,
latestRightSideUnit = main.latestRightSideUnit,
shownUnitGroups = main.shownUnitGroups,
enableVibrations = main.enableVibrations,
enableToolsExperiment = false,
startingScreen = ui.startingScreen,
radianMode = main.radianMode,
unitConverterFavoritesOnly = main.unitConverterFavoritesOnly,
unitConverterFormatTime = main.unitConverterFormatTime,
unitConverterSorting = main.unitConverterSorting,
)
}
/** /**
* Update precision preference in DataStore * Update precision preference in DataStore
* *

View File

@ -29,7 +29,6 @@ android {
dependencies { dependencies {
testImplementation(libs.junit) testImplementation(libs.junit)
implementation(libs.com.github.sadellie.themmo)
implementation(project(mapOf("path" to ":data:common"))) implementation(project(mapOf("path" to ":data:common")))
implementation(project(mapOf("path" to ":data:userprefs"))) implementation(project(mapOf("path" to ":data:userprefs")))

View File

@ -31,7 +31,7 @@ import com.sadellie.unitto.data.common.isExpression
import com.sadellie.unitto.data.common.setMinimumRequiredScale import com.sadellie.unitto.data.common.setMinimumRequiredScale
import com.sadellie.unitto.data.common.toStringWith import com.sadellie.unitto.data.common.toStringWith
import com.sadellie.unitto.data.common.trimZeros import com.sadellie.unitto.data.common.trimZeros
import com.sadellie.unitto.data.userprefs.UserPreferences import com.sadellie.unitto.data.userprefs.MainPreferences
import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import com.sadellie.unitto.data.userprefs.UserPreferencesRepository
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import io.github.sadellie.evaluatto.Expression import io.github.sadellie.evaluatto.Expression
@ -54,11 +54,11 @@ internal class CalculatorViewModel @Inject constructor(
private val userPrefsRepository: UserPreferencesRepository, private val userPrefsRepository: UserPreferencesRepository,
private val calculatorHistoryRepository: CalculatorHistoryRepository, private val calculatorHistoryRepository: CalculatorHistoryRepository,
) : ViewModel() { ) : ViewModel() {
private val _userPrefs: StateFlow<UserPreferences> = private val _userPrefs: StateFlow<MainPreferences> =
userPrefsRepository.userPreferencesFlow.stateIn( userPrefsRepository.mainPreferencesFlow.stateIn(
viewModelScope, viewModelScope,
SharingStarted.WhileSubscribed(5000L), SharingStarted.WhileSubscribed(5000L),
UserPreferences() MainPreferences()
) )
private val _input: MutableStateFlow<TextFieldValue> = MutableStateFlow(TextFieldValue()) private val _input: MutableStateFlow<TextFieldValue> = MutableStateFlow(TextFieldValue())

View File

@ -36,7 +36,6 @@ dependencies {
kapt(libs.androidx.room.compiler) kapt(libs.androidx.room.compiler)
testImplementation(libs.androidx.datastore) testImplementation(libs.androidx.datastore)
implementation(libs.com.github.sadellie.themmo)
implementation(libs.com.squareup.moshi) implementation(libs.com.squareup.moshi)
implementation(libs.com.squareup.retrofit2) implementation(libs.com.squareup.retrofit2)

View File

@ -39,7 +39,7 @@ import com.sadellie.unitto.data.units.MyUnitIDS
import com.sadellie.unitto.data.units.combine import com.sadellie.unitto.data.units.combine
import com.sadellie.unitto.data.units.remote.CurrencyApi import com.sadellie.unitto.data.units.remote.CurrencyApi
import com.sadellie.unitto.data.units.remote.CurrencyUnitResponse import com.sadellie.unitto.data.units.remote.CurrencyUnitResponse
import com.sadellie.unitto.data.userprefs.UserPreferences import com.sadellie.unitto.data.userprefs.MainPreferences
import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import com.sadellie.unitto.data.userprefs.UserPreferencesRepository
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import io.github.sadellie.evaluatto.Expression import io.github.sadellie.evaluatto.Expression
@ -65,10 +65,10 @@ class ConverterViewModel @Inject constructor(
private val allUnitsRepository: AllUnitsRepository private val allUnitsRepository: AllUnitsRepository
) : ViewModel() { ) : ViewModel() {
private val _userPrefs = userPrefsRepository.userPreferencesFlow.stateIn( private val _userPrefs = userPrefsRepository.mainPreferencesFlow.stateIn(
viewModelScope, viewModelScope,
SharingStarted.WhileSubscribed(5000), SharingStarted.WhileSubscribed(5000),
UserPreferences() MainPreferences()
) )
/** /**
@ -334,7 +334,7 @@ class ConverterViewModel @Inject constructor(
private fun loadInitialUnitPair() { private fun loadInitialUnitPair() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
val initialUserPrefs = userPrefsRepository.userPreferencesFlow.first() val initialUserPrefs = userPrefsRepository.mainPreferencesFlow.first()
// First we load latest pair of units // First we load latest pair of units
_unitFrom.update { _unitFrom.update {

View File

@ -43,6 +43,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.core.base.BuildConfig import com.sadellie.unitto.core.base.BuildConfig
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
@ -54,7 +55,7 @@ import com.sadellie.unitto.core.ui.openLink
internal fun AboutScreen( internal fun AboutScreen(
navigateUpAction: () -> Unit, navigateUpAction: () -> Unit,
navigateToThirdParty: () -> Unit, navigateToThirdParty: () -> Unit,
viewModel: SettingsViewModel viewModel: SettingsViewModel = hiltViewModel()
) { ) {
val mContext = LocalContext.current val mContext = LocalContext.current
val userPrefs = viewModel.userPrefs.collectAsStateWithLifecycle() val userPrefs = viewModel.userPrefs.collectAsStateWithLifecycle()

View File

@ -43,6 +43,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.core.base.BuildConfig import com.sadellie.unitto.core.base.BuildConfig
import com.sadellie.unitto.core.base.OUTPUT_FORMAT import com.sadellie.unitto.core.base.OUTPUT_FORMAT
@ -63,7 +64,7 @@ import com.sadellie.unitto.feature.settings.navigation.unitsGroupRoute
@Composable @Composable
internal fun SettingsScreen( internal fun SettingsScreen(
viewModel: SettingsViewModel, viewModel: SettingsViewModel = hiltViewModel(),
menuButtonClick: () -> Unit, menuButtonClick: () -> Unit,
navControllerAction: (String) -> Unit navControllerAction: (String) -> Unit
) { ) {

View File

@ -18,80 +18,27 @@
package com.sadellie.unitto.feature.settings package com.sadellie.unitto.feature.settings
import androidx.compose.ui.graphics.Color
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.data.model.UnitGroup
import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.UnitsListSorting
import com.sadellie.unitto.data.unitgroups.UnitGroupsRepository
import com.sadellie.unitto.data.userprefs.UserPreferences import com.sadellie.unitto.data.userprefs.UserPreferences
import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import com.sadellie.unitto.data.userprefs.UserPreferencesRepository
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import io.github.sadellie.themmo.MonetMode
import io.github.sadellie.themmo.ThemingMode
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.burnoutcrew.reorderable.ItemPosition
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class SettingsViewModel @Inject constructor( class SettingsViewModel @Inject constructor(
private val userPrefsRepository: UserPreferencesRepository, private val userPrefsRepository: UserPreferencesRepository,
private val unitGroupsRepository: UnitGroupsRepository,
) : ViewModel() { ) : ViewModel() {
val userPrefs = userPrefsRepository.userPreferencesFlow val userPrefs = userPrefsRepository.allPreferencesFlow
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), .stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5000),
UserPreferences() UserPreferences()
) )
val shownUnitGroups = unitGroupsRepository.shownUnitGroups
val hiddenUnitGroups = unitGroupsRepository.hiddenUnitGroups
/**
* @see UserPreferencesRepository.updateThemingMode
*/
fun updateThemingMode(themingMode: ThemingMode) {
viewModelScope.launch {
userPrefsRepository.updateThemingMode(themingMode)
}
}
/**
* @see UserPreferencesRepository.updateDynamicTheme
*/
fun updateDynamicTheme(enabled: Boolean) {
viewModelScope.launch {
userPrefsRepository.updateDynamicTheme(enabled)
}
}
/**
* @see UserPreferencesRepository.updateAmoledTheme
*/
fun updateAmoledTheme(enabled: Boolean) {
viewModelScope.launch {
userPrefsRepository.updateAmoledTheme(enabled)
}
}
/**
* @see UserPreferencesRepository.updateCustomColor
*/
fun updateCustomColor(color: Color) {
viewModelScope.launch {
userPrefsRepository.updateCustomColor(color)
}
}
/**
* @see UserPreferencesRepository.updateMonetMode
*/
fun updateMonetMode(monetMode: MonetMode) {
viewModelScope.launch {
userPrefsRepository.updateMonetMode(monetMode)
}
}
/** /**
* @see UserPreferencesRepository.updateDigitsPrecision * @see UserPreferencesRepository.updateDigitsPrecision
@ -138,46 +85,6 @@ class SettingsViewModel @Inject constructor(
} }
} }
/**
* @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)
}
}
/** /**
* @see UserPreferencesRepository.updateToolsExperiment * @see UserPreferencesRepository.updateToolsExperiment
*/ */
@ -204,20 +111,4 @@ class SettingsViewModel @Inject constructor(
userPrefsRepository.updateUnitConverterSorting(sorting) userPrefsRepository.updateUnitConverterSorting(sorting)
} }
} }
/**
* 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.userPreferencesFlow.first().shownUnitGroups
)
}
}
} }

View File

@ -27,10 +27,9 @@ import androidx.navigation.compose.navigation
import com.sadellie.unitto.core.base.TopLevelDestinations import com.sadellie.unitto.core.base.TopLevelDestinations
import com.sadellie.unitto.feature.settings.AboutScreen import com.sadellie.unitto.feature.settings.AboutScreen
import com.sadellie.unitto.feature.settings.SettingsScreen import com.sadellie.unitto.feature.settings.SettingsScreen
import com.sadellie.unitto.feature.settings.SettingsViewModel import com.sadellie.unitto.feature.settings.themes.ThemesRoute
import com.sadellie.unitto.feature.settings.ThemesRoute
import com.sadellie.unitto.feature.settings.ThirdPartyLicensesScreen import com.sadellie.unitto.feature.settings.ThirdPartyLicensesScreen
import com.sadellie.unitto.feature.settings.UnitGroupsScreen import com.sadellie.unitto.feature.settings.unitgroups.UnitGroupsScreen
import io.github.sadellie.themmo.ThemmoController import io.github.sadellie.themmo.ThemmoController
private val settingsGraph: String by lazy { TopLevelDestinations.Settings.route } private val settingsGraph: String by lazy { TopLevelDestinations.Settings.route }
@ -49,7 +48,6 @@ fun NavController.navigateToUnitGroups() {
} }
fun NavGraphBuilder.settingGraph( fun NavGraphBuilder.settingGraph(
settingsViewModel: SettingsViewModel,
themmoController: ThemmoController, themmoController: ThemmoController,
navController: NavHostController, navController: NavHostController,
menuButtonClick: () -> Unit menuButtonClick: () -> Unit
@ -57,37 +55,34 @@ fun NavGraphBuilder.settingGraph(
navigation(settingsRoute, settingsGraph) { navigation(settingsRoute, settingsGraph) {
composable(settingsRoute) { composable(settingsRoute) {
SettingsScreen( SettingsScreen(
viewModel = settingsViewModel, menuButtonClick = menuButtonClick,
menuButtonClick = menuButtonClick navControllerAction = navController::navigate
) { route -> navController.navigate(route) } )
} }
composable(themesRoute) { composable(themesRoute) {
ThemesRoute( ThemesRoute(
navigateUpAction = { navController.navigateUp() }, navigateUpAction = navController::navigateUp,
themmoController = themmoController, themmoController = themmoController,
viewModel = settingsViewModel
) )
} }
composable(thirdPartyRoute) { composable(thirdPartyRoute) {
ThirdPartyLicensesScreen( ThirdPartyLicensesScreen(
navigateUpAction = { navController.navigateUp() } navigateUpAction = navController::navigateUp,
) )
} }
composable(aboutRoute) { composable(aboutRoute) {
AboutScreen( AboutScreen(
navigateUpAction = { navController.navigateUp() }, navigateUpAction = navController::navigateUp,
navigateToThirdParty = { navController.navigate(thirdPartyRoute) }, navigateToThirdParty = { navController.navigate(thirdPartyRoute) },
viewModel = settingsViewModel
) )
} }
composable(unitsGroupRoute) { composable(unitsGroupRoute) {
UnitGroupsScreen( UnitGroupsScreen(
viewModel = settingsViewModel, navigateUpAction = navController::navigateUp,
navigateUpAction = { navController.navigateUp() }
) )
} }
} }

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.sadellie.unitto.feature.settings package com.sadellie.unitto.feature.settings.themes
import android.os.Build import android.os.Build
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
@ -49,6 +49,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.base.R
import com.sadellie.unitto.core.ui.common.Header import com.sadellie.unitto.core.ui.common.Header
import com.sadellie.unitto.core.ui.common.NavigateUpButton import com.sadellie.unitto.core.ui.common.NavigateUpButton
@ -82,7 +83,7 @@ private val colorSchemes: List<Color> by lazy {
internal fun ThemesRoute( internal fun ThemesRoute(
navigateUpAction: () -> Unit = {}, navigateUpAction: () -> Unit = {},
themmoController: ThemmoController, themmoController: ThemmoController,
viewModel: SettingsViewModel viewModel: ThemesViewModel = hiltViewModel()
) { ) {
ThemesScreen( ThemesScreen(
navigateUpAction = navigateUpAction, navigateUpAction = navigateUpAction,

View File

@ -0,0 +1,80 @@
/*
* Unitto is a unit converter for Android
* Copyright (c) 2023 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.themes
import androidx.compose.ui.graphics.Color
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.data.userprefs.UserPreferencesRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import io.github.sadellie.themmo.MonetMode
import io.github.sadellie.themmo.ThemingMode
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class ThemesViewModel @Inject constructor(
private val userPrefsRepository: UserPreferencesRepository
) : ViewModel() {
/**
* @see UserPreferencesRepository.updateThemingMode
*/
fun updateThemingMode(themingMode: ThemingMode) {
viewModelScope.launch {
userPrefsRepository.updateThemingMode(themingMode)
}
}
/**
* @see UserPreferencesRepository.updateDynamicTheme
*/
fun updateDynamicTheme(enabled: Boolean) {
viewModelScope.launch {
userPrefsRepository.updateDynamicTheme(enabled)
}
}
/**
* @see UserPreferencesRepository.updateAmoledTheme
*/
fun updateAmoledTheme(enabled: Boolean) {
viewModelScope.launch {
userPrefsRepository.updateAmoledTheme(enabled)
}
}
/**
* @see UserPreferencesRepository.updateCustomColor
*/
fun updateCustomColor(color: Color) {
viewModelScope.launch {
userPrefsRepository.updateCustomColor(color)
}
}
/**
* @see UserPreferencesRepository.updateMonetMode
*/
fun updateMonetMode(monetMode: MonetMode) {
viewModelScope.launch {
userPrefsRepository.updateMonetMode(monetMode)
}
}
}

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.sadellie.unitto.feature.settings package com.sadellie.unitto.feature.settings.unitgroups
import androidx.compose.animation.animateColor import androidx.compose.animation.animateColor
import androidx.compose.animation.core.animateDp import androidx.compose.animation.core.animateDp
@ -40,13 +40,14 @@ import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp 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.base.R
import com.sadellie.unitto.core.ui.common.Header import com.sadellie.unitto.core.ui.common.Header
import com.sadellie.unitto.core.ui.common.NavigateUpButton import com.sadellie.unitto.core.ui.common.NavigateUpButton
@ -59,21 +60,20 @@ import org.burnoutcrew.reorderable.reorderable
@Composable @Composable
internal fun UnitGroupsScreen( internal fun UnitGroupsScreen(
viewModel: SettingsViewModel, viewModel: UnitGroupsViewModel = hiltViewModel(),
navigateUpAction: () -> Unit navigateUpAction: () -> Unit
) { ) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
UnittoScreenWithLargeTopBar( UnittoScreenWithLargeTopBar(
title = stringResource(R.string.unit_groups_setting), title = stringResource(R.string.unit_groups_setting),
navigationIcon = { NavigateUpButton(navigateUpAction) } navigationIcon = { NavigateUpButton(navigateUpAction) }
) { paddingValues -> ) { paddingValues ->
val shownUnits = viewModel.shownUnitGroups.collectAsState()
val hiddenUnits = viewModel.hiddenUnitGroups.collectAsState()
val state = rememberReorderableLazyListState( val state = rememberReorderableLazyListState(
onMove = viewModel::onMove, onMove = viewModel::moveShownUnitGroups,
canDragOver = { from, _ -> viewModel.canDragOver(from) }, canDragOver = { from, _ -> viewModel.canDragOver(from) },
onDragEnd = { _, _ -> viewModel.onDragEnd() } onDragEnd = { _, _ -> viewModel.saveShownUnitGroups() }
) )
LazyColumn( LazyColumn(
@ -89,7 +89,7 @@ internal fun UnitGroupsScreen(
) )
} }
items(shownUnits.value, { it }) { item -> items(uiState.value.shownGroups, { it }) { item ->
ReorderableItem(state, key = item) { isDragging -> ReorderableItem(state, key = item) { isDragging ->
val transition = updateTransition(isDragging, label = "draggedTransition") val transition = updateTransition(isDragging, label = "draggedTransition")
val background by transition.animateColor(label = "background") { val background by transition.animateColor(label = "background") {
@ -104,7 +104,7 @@ internal fun UnitGroupsScreen(
modifier = Modifier modifier = Modifier
.padding(horizontal = itemPadding) .padding(horizontal = itemPadding)
.clip(CircleShape) .clip(CircleShape)
.clickable { viewModel.hideUnitGroup(item) } .clickable { viewModel.markUnitGroupAsHidden(item) }
.detectReorderAfterLongPress(state), .detectReorderAfterLongPress(state),
colors = ListItemDefaults.colors( colors = ListItemDefaults.colors(
containerColor = background containerColor = background
@ -117,7 +117,7 @@ internal fun UnitGroupsScreen(
modifier = Modifier.clickable( modifier = Modifier.clickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(false), indication = rememberRipple(false),
onClick = { viewModel.hideUnitGroup(item) } onClick = { viewModel.markUnitGroupAsHidden(item) }
) )
) )
}, },
@ -147,11 +147,11 @@ internal fun UnitGroupsScreen(
) )
} }
items(hiddenUnits.value, { it }) { items(uiState.value.hiddenGroups, { it }) {
ListItem( ListItem(
modifier = Modifier modifier = Modifier
.background(MaterialTheme.colorScheme.surface) .background(MaterialTheme.colorScheme.surface)
.clickable { viewModel.returnUnitGroup(it) } .clickable { viewModel.markUnitGroupAsShown(it) }
.animateItemPlacement(), .animateItemPlacement(),
headlineContent = { Text(stringResource(it.res)) }, headlineContent = { Text(stringResource(it.res)) },
trailingContent = { trailingContent = {
@ -162,7 +162,7 @@ internal fun UnitGroupsScreen(
modifier = Modifier.clickable( modifier = Modifier.clickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(false), indication = rememberRipple(false),
onClick = { viewModel.returnUnitGroup(it) } onClick = { viewModel.markUnitGroupAsShown(it) }
) )
) )
} }

View File

@ -0,0 +1,26 @@
/*
* Unitto is a unit converter for Android
* Copyright (c) 2023 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
data class UnitGroupsUIState(
val shownGroups: List<UnitGroup>,
val hiddenGroups: List<UnitGroup>
)

View File

@ -0,0 +1,135 @@
/*
* Unitto is a unit converter for Android
* Copyright (c) 2023 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 androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.data.model.ALL_UNIT_GROUPS
import com.sadellie.unitto.data.model.UnitGroup
import com.sadellie.unitto.data.userprefs.UserPreferencesRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.burnoutcrew.reorderable.ItemPosition
import javax.inject.Inject
@HiltViewModel
class UnitGroupsViewModel @Inject constructor(
private val userPreferencesRepository: UserPreferencesRepository,
) : ViewModel() {
private var mutex: Mutex = Mutex()
/**
* Currently shown [UnitGroup]s.
*/
private val _shownUnitGroups = MutableStateFlow(listOf<UnitGroup>())
/**
* Currently hidden [UnitGroup]s.
*/
private val _hiddenUnitGroups = MutableStateFlow(listOf<UnitGroup>())
init {
viewModelScope.launch {
val shown = userPreferencesRepository.mainPreferencesFlow.first().shownUnitGroups
mutex.withLock {
_shownUnitGroups.update { shown }
_hiddenUnitGroups.update { ALL_UNIT_GROUPS - shown.toSet() }
}
}
}
val uiState = combine(_shownUnitGroups, _hiddenUnitGroups) { shown, hidden ->
return@combine UnitGroupsUIState(
shownGroups = shown,
hiddenGroups = hidden
)
}.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5000L),
UnitGroupsUIState(emptyList(), emptyList())
)
/**
* Moves [UnitGroup] from [_shownUnitGroups] to [_hiddenUnitGroups]
*
* @param unitGroup [UnitGroup] to hide.
*/
fun markUnitGroupAsHidden(unitGroup: UnitGroup) = viewModelScope.launch {
mutex.withLock {
_shownUnitGroups.update { it - unitGroup }
// Newly hidden unit will appear at the top of the list
_hiddenUnitGroups.update { listOf(unitGroup) + it }
}
}
/**
* Moves [UnitGroup] from [_hiddenUnitGroups] to [_shownUnitGroups]
*
* @param unitGroup [UnitGroup] to show.
*/
fun markUnitGroupAsShown(unitGroup: UnitGroup) = viewModelScope.launch {
mutex.withLock {
_hiddenUnitGroups.update { it - unitGroup }
_shownUnitGroups.update { it + 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]
*/
fun moveShownUnitGroups(from: ItemPosition, to: ItemPosition) = viewModelScope.launch {
mutex.withLock {
_shownUnitGroups.update { shown ->
shown.toMutableList().apply {
val initialIndex = shown.indexOfFirst { it == from.key }
/**
* No such item. Happens when dragging item and clicking "remove" while item is
* still being dragged.
*/
if (initialIndex == -1) return@launch
add(
shown.indexOfFirst { it == to.key },
removeAt(initialIndex)
)
}
}
}
}
fun canDragOver(pos: ItemPosition) = uiState.value.shownGroups.any { it == pos.key }
fun saveShownUnitGroups() = viewModelScope.launch {
userPreferencesRepository.updateShownUnitGroups(
uiState.value.shownGroups
)
}
}

View File

@ -26,9 +26,8 @@ import com.sadellie.unitto.data.database.UnitsEntity
import com.sadellie.unitto.data.database.UnitsRepository import com.sadellie.unitto.data.database.UnitsRepository
import com.sadellie.unitto.data.model.AbstractUnit import com.sadellie.unitto.data.model.AbstractUnit
import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.UnitGroup
import com.sadellie.unitto.data.unitgroups.UnitGroupsRepository
import com.sadellie.unitto.data.units.AllUnitsRepository import com.sadellie.unitto.data.units.AllUnitsRepository
import com.sadellie.unitto.data.userprefs.UserPreferences import com.sadellie.unitto.data.userprefs.MainPreferences
import com.sadellie.unitto.data.userprefs.UserPreferencesRepository import com.sadellie.unitto.data.userprefs.UserPreferencesRepository
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -48,33 +47,30 @@ class UnitsListViewModel @Inject constructor(
private val allUnitsRepository: AllUnitsRepository, private val allUnitsRepository: AllUnitsRepository,
private val mContext: Application, private val mContext: Application,
private val userPrefsRepository: UserPreferencesRepository, private val userPrefsRepository: UserPreferencesRepository,
unitGroupsRepository: UnitGroupsRepository,
) : ViewModel() { ) : ViewModel() {
private val _userPrefs: StateFlow<UserPreferences> = private val _userPrefs: StateFlow<MainPreferences> =
userPrefsRepository.userPreferencesFlow.stateIn( userPrefsRepository.mainPreferencesFlow.stateIn(
viewModelScope, viewModelScope,
SharingStarted.WhileSubscribed(5000L), SharingStarted.WhileSubscribed(5000L),
UserPreferences() MainPreferences()
) )
private val _unitsToShow = MutableStateFlow(emptyMap<UnitGroup, List<AbstractUnit>>()) private val _unitsToShow = MutableStateFlow(emptyMap<UnitGroup, List<AbstractUnit>>())
private val _searchQuery = MutableStateFlow("") private val _searchQuery = MutableStateFlow("")
private val _chosenUnitGroup: MutableStateFlow<UnitGroup?> = MutableStateFlow(null) private val _chosenUnitGroup: MutableStateFlow<UnitGroup?> = MutableStateFlow(null)
private val _shownUnitGroups = unitGroupsRepository.shownUnitGroups
val mainFlow = combine( val mainFlow = combine(
_userPrefs, _userPrefs,
_unitsToShow, _unitsToShow,
_searchQuery, _searchQuery,
_chosenUnitGroup, _chosenUnitGroup,
_shownUnitGroups, ) { userPrefs, unitsToShow, searchQuery, chosenUnitGroup ->
) { userPrefs, unitsToShow, searchQuery, chosenUnitGroup, shownUnitGroups ->
return@combine SecondScreenUIState( return@combine SecondScreenUIState(
favoritesOnly = userPrefs.unitConverterFavoritesOnly, favoritesOnly = userPrefs.unitConverterFavoritesOnly,
unitsToShow = unitsToShow, unitsToShow = unitsToShow,
searchQuery = searchQuery, searchQuery = searchQuery,
chosenUnitGroup = chosenUnitGroup, chosenUnitGroup = chosenUnitGroup,
shownUnitGroups = shownUnitGroups, shownUnitGroups = userPrefs.shownUnitGroups,
formatterSymbols = AllFormatterSymbols.getById(userPrefs.separator) formatterSymbols = AllFormatterSymbols.getById(userPrefs.separator)
) )
} }
@ -136,7 +132,7 @@ class UnitsListViewModel @Inject constructor(
chosenUnitGroup = _chosenUnitGroup.value, chosenUnitGroup = _chosenUnitGroup.value,
favoritesOnly = _userPrefs.value.unitConverterFavoritesOnly, favoritesOnly = _userPrefs.value.unitConverterFavoritesOnly,
searchQuery = _searchQuery.value, searchQuery = _searchQuery.value,
allUnitsGroups = _shownUnitGroups.value, allUnitsGroups = _userPrefs.value.shownUnitGroups,
sorting = _userPrefs.value.unitConverterSorting sorting = _userPrefs.value.unitConverterSorting
) )