Temporarily return old unit groups setting logic

i tried to make it better and it broke
This commit is contained in:
Sad Ellie 2023-05-21 23:05:18 +03:00
parent 7ec8cf934a
commit 11f2d82b50
4 changed files with 176 additions and 127 deletions

View File

@ -0,0 +1,115 @@
/*
* 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.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

@ -40,6 +40,7 @@ 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
@ -47,7 +48,6 @@ 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.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
@ -63,17 +63,18 @@ internal fun UnitGroupsScreen(
viewModel: UnitGroupsViewModel = hiltViewModel(), 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::moveShownUnitGroups, onMove = viewModel::onMove,
canDragOver = { from, _ -> viewModel.canDragOver(from) }, canDragOver = { from, _ -> viewModel.canDragOver(from) },
onDragEnd = { _, _ -> viewModel.saveShownUnitGroups() } onDragEnd = { _, _ -> viewModel.onDragEnd() }
) )
LazyColumn( LazyColumn(
@ -89,7 +90,7 @@ internal fun UnitGroupsScreen(
) )
} }
items(uiState.value.shownGroups, { it }) { item -> items(shownUnits.value, { 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 +105,7 @@ internal fun UnitGroupsScreen(
modifier = Modifier modifier = Modifier
.padding(horizontal = itemPadding) .padding(horizontal = itemPadding)
.clip(CircleShape) .clip(CircleShape)
.clickable { viewModel.markUnitGroupAsHidden(item) } .clickable { viewModel.hideUnitGroup(item) }
.detectReorderAfterLongPress(state), .detectReorderAfterLongPress(state),
colors = ListItemDefaults.colors( colors = ListItemDefaults.colors(
containerColor = background containerColor = background
@ -117,7 +118,7 @@ internal fun UnitGroupsScreen(
modifier = Modifier.clickable( modifier = Modifier.clickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(false), indication = rememberRipple(false),
onClick = { viewModel.markUnitGroupAsHidden(item) } onClick = { viewModel.hideUnitGroup(item) }
) )
) )
}, },
@ -147,11 +148,11 @@ internal fun UnitGroupsScreen(
) )
} }
items(uiState.value.hiddenGroups, { it }) { items(hiddenUnits.value, { it }) {
ListItem( ListItem(
modifier = Modifier modifier = Modifier
.background(MaterialTheme.colorScheme.surface) .background(MaterialTheme.colorScheme.surface)
.clickable { viewModel.markUnitGroupAsShown(it) } .clickable { viewModel.returnUnitGroup(it) }
.animateItemPlacement(), .animateItemPlacement(),
headlineContent = { Text(stringResource(it.res)) }, headlineContent = { Text(stringResource(it.res)) },
trailingContent = { trailingContent = {
@ -162,7 +163,7 @@ internal fun UnitGroupsScreen(
modifier = Modifier.clickable( modifier = Modifier.clickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(false), indication = rememberRipple(false),
onClick = { viewModel.markUnitGroupAsShown(it) } onClick = { viewModel.returnUnitGroup(it) }
) )
) )
} }

View File

@ -1,26 +0,0 @@
/*
* 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

@ -20,116 +20,75 @@ package com.sadellie.unitto.feature.settings.unitgroups
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope 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.model.UnitGroup
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.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.burnoutcrew.reorderable.ItemPosition import org.burnoutcrew.reorderable.ItemPosition
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class UnitGroupsViewModel @Inject constructor( class UnitGroupsViewModel @Inject constructor(
private val userPreferencesRepository: UserPreferencesRepository, private val userPrefsRepository: UserPreferencesRepository,
private val unitGroupsRepository: UnitGroupsRepository,
) : ViewModel() { ) : ViewModel() {
val shownUnitGroups = unitGroupsRepository.shownUnitGroups
private var mutex: Mutex = Mutex() val hiddenUnitGroups = unitGroupsRepository.hiddenUnitGroups
/** /**
* Currently shown [UnitGroup]s. * @see UnitGroupsRepository.markUnitGroupAsHidden
* @see UserPreferencesRepository.updateShownUnitGroups
*/ */
private val _shownUnitGroups = MutableStateFlow(listOf<UnitGroup>()) fun hideUnitGroup(unitGroup: UnitGroup) {
viewModelScope.launch {
unitGroupsRepository.markUnitGroupAsHidden(unitGroup)
userPrefsRepository.updateShownUnitGroups(unitGroupsRepository.shownUnitGroups.value)
}
}
/** /**
* Currently hidden [UnitGroup]s. * @see UnitGroupsRepository.markUnitGroupAsShown
* @see UserPreferencesRepository.updateShownUnitGroups
*/ */
private val _hiddenUnitGroups = MutableStateFlow(listOf<UnitGroup>()) 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 { init {
viewModelScope.launch { viewModelScope.launch {
val shown = userPreferencesRepository.mainPreferencesFlow.first().shownUnitGroups unitGroupsRepository.updateShownGroups(
mutex.withLock { userPrefsRepository.mainPreferencesFlow.first().shownUnitGroups
_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
)
}
}