enhancement: reorderable instance selector (#1045)

This commit is contained in:
Diego Beraldin 2024-06-27 18:00:17 +02:00 committed by GitHub
parent 136cefaf2a
commit 38eadde947
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 71 additions and 34 deletions

View File

@ -76,28 +76,30 @@ class SelectInstanceBottomSheet : Screen {
var instanceToDelete by remember { mutableStateOf<String?>(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) {

View File

@ -8,13 +8,24 @@ interface SelectInstanceMviModel :
MviModel<SelectInstanceMviModel.Intent, SelectInstanceMviModel.State, SelectInstanceMviModel.Effect>,
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
}
}

View File

@ -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<SelectInstanceMviModel.Intent, SelectInstanceMviModel.State, SelectInstanceMviModel.Effect>(
private val hapticFeedback: HapticFeedback,
) : DefaultMviModel<SelectInstanceMviModel.Intent, SelectInstanceMviModel.State, SelectInstanceMviModel.Effect>(
initialState = SelectInstanceMviModel.State(),
) {
),
SelectInstanceMviModel {
private val saveOperationChannel = Channel<List<String>>()
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()
}
}

View File

@ -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<Option> = emptyList(),
onOptionSelected: ((OptionId) -> Unit)? = null,
) {
@ -84,10 +87,17 @@ internal fun SelectInstanceItem(
Modifier
.size(IconSize.m)
.padding(Spacing.xs)
.onGloballyPositioned {
.then(
with(reorderableScope) {
Modifier.draggableHandle(
onDragStarted = {
onDragStarted?.invoke()
},
)
},
).onGloballyPositioned {
optionsOffset = it.positionInParent()
}
.onClick(
}.onClick(
onClick = {
optionsMenuOpen = true
},

View File

@ -11,6 +11,7 @@ val selectInstanceModule =
instanceRepository = get(),
communityRepository = get(),
apiConfigurationRepository = get(),
hapticFeedback = get(),
)
}
}