diff --git a/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/SelectInstanceBottomSheet.kt b/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/SelectInstanceBottomSheet.kt index 4214b7f5b..9415e1d5d 100644 --- a/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/SelectInstanceBottomSheet.kt +++ b/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/SelectInstanceBottomSheet.kt @@ -76,28 +76,30 @@ class SelectInstanceBottomSheet : Screen { var instanceToDelete by remember { mutableStateOf(null) } LaunchedEffect(model) { - model.effects.onEach { evt -> - when (evt) { - SelectInstanceMviModel.Effect.CloseDialog -> { - changeInstanceDialogOpen = false - } + model.effects + .onEach { evt -> + when (evt) { + SelectInstanceMviModel.Effect.CloseDialog -> { + changeInstanceDialogOpen = false + } - is SelectInstanceMviModel.Effect.Confirm -> { - notificationCenter.send(NotificationCenterEvent.InstanceSelected(evt.instance)) - navigationCoordinator.hideBottomSheet() + is SelectInstanceMviModel.Effect.Confirm -> { + notificationCenter.send(NotificationCenterEvent.InstanceSelected(evt.instance)) + navigationCoordinator.hideBottomSheet() + } } - } - }.launchIn(this) + }.launchIn(this) } Column( modifier = - Modifier.padding( - top = Spacing.s, - start = Spacing.s, - end = Spacing.s, - bottom = Spacing.m, - ).fillMaxHeight(0.6f), + Modifier + .padding( + top = Spacing.s, + start = Spacing.s, + end = Spacing.s, + bottom = Spacing.m, + ).fillMaxHeight(0.6f), verticalArrangement = Arrangement.spacedBy(Spacing.s), ) { Box( @@ -158,15 +160,19 @@ class SelectInstanceBottomSheet : Screen { shadowElevation = elevation, ) { SelectInstanceItem( - modifier = Modifier.draggableHandle(), instance = instance, isActive = isActive, + onDragStarted = + rememberCallback(model) { + model.reduce(SelectInstanceMviModel.Intent.HapticIndication) + }, onClick = rememberCallback(model) { model.reduce( SelectInstanceMviModel.Intent.SelectInstance(instance), ) }, + reorderableScope = this, options = buildList { if (!isActive) { diff --git a/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/SelectInstanceMviModel.kt b/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/SelectInstanceMviModel.kt index d26b8e06b..403b4d0d6 100644 --- a/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/SelectInstanceMviModel.kt +++ b/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/SelectInstanceMviModel.kt @@ -8,13 +8,24 @@ interface SelectInstanceMviModel : MviModel, ScreenModel { sealed interface Intent { - data class SelectInstance(val value: String) : Intent + data class SelectInstance( + val value: String, + ) : Intent - data class DeleteInstance(val value: String) : Intent + data class DeleteInstance( + val value: String, + ) : Intent - data class ChangeInstanceName(val value: String) : Intent + data class ChangeInstanceName( + val value: String, + ) : Intent - data class SwapIntances(val from: Int, val to: Int) : Intent + data object HapticIndication : Intent + + data class SwapIntances( + val from: Int, + val to: Int, + ) : Intent data object SubmitChangeInstanceDialog : Intent } @@ -30,6 +41,8 @@ interface SelectInstanceMviModel : sealed interface Effect { data object CloseDialog : Effect - data class Confirm(val instance: String) : Effect + data class Confirm( + val instance: String, + ) : Effect } } diff --git a/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/SelectInstanceViewModel.kt b/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/SelectInstanceViewModel.kt index 327642e16..7b0d38e9e 100644 --- a/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/SelectInstanceViewModel.kt +++ b/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/SelectInstanceViewModel.kt @@ -4,6 +4,7 @@ import cafe.adriel.voyager.core.model.screenModelScope import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.InstanceSelectionRepository import com.github.diegoberaldin.raccoonforlemmy.core.utils.ValidationError +import com.github.diegoberaldin.raccoonforlemmy.core.utils.vibrate.HapticFeedback import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.ApiConfigurationRepository import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommunityRepository import kotlinx.coroutines.FlowPreview @@ -18,22 +19,27 @@ class SelectInstanceViewModel( private val instanceRepository: InstanceSelectionRepository, private val communityRepository: CommunityRepository, private val apiConfigurationRepository: ApiConfigurationRepository, -) : SelectInstanceMviModel, - DefaultMviModel( + private val hapticFeedback: HapticFeedback, +) : DefaultMviModel( initialState = SelectInstanceMviModel.State(), - ) { + ), + SelectInstanceMviModel { private val saveOperationChannel = Channel>() init { screenModelScope.launch { - apiConfigurationRepository.instance.onEach { instance -> - updateState { it.copy(currentInstance = instance) } - }.launchIn(this) + apiConfigurationRepository.instance + .onEach { instance -> + updateState { it.copy(currentInstance = instance) } + }.launchIn(this) @OptIn(FlowPreview::class) - saveOperationChannel.receiveAsFlow().debounce(500).onEach { newInstances -> - instanceRepository.updateAll(newInstances) - }.launchIn(this) + saveOperationChannel + .receiveAsFlow() + .debounce(500) + .onEach { newInstances -> + instanceRepository.updateAll(newInstances) + }.launchIn(this) } if (uiState.value.instances.isEmpty()) { @@ -59,6 +65,7 @@ class SelectInstanceViewModel( is SelectInstanceMviModel.Intent.SubmitChangeInstanceDialog -> submitChangeInstance() is SelectInstanceMviModel.Intent.DeleteInstance -> deleteInstance(intent.value) is SelectInstanceMviModel.Intent.SwapIntances -> swapInstances(intent.from, intent.to) + SelectInstanceMviModel.Intent.HapticIndication -> hapticFeedback.vibrate() } } diff --git a/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/components/SelectInstanceItem.kt b/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/components/SelectInstanceItem.kt index fa74c2295..9bf45e080 100644 --- a/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/components/SelectInstanceItem.kt +++ b/unit/selectinstance/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/selectinstance/components/SelectInstanceItem.kt @@ -33,13 +33,16 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.Option import com.github.diegoberaldin.raccoonforlemmy.core.commonui.lemmyui.OptionId import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp +import sh.calvin.reorderable.ReorderableCollectionItemScope @Composable internal fun SelectInstanceItem( modifier: Modifier = Modifier, + reorderableScope: ReorderableCollectionItemScope, instance: String, isActive: Boolean = false, onClick: (() -> Unit)? = null, + onDragStarted: (() -> Unit)? = null, options: List