enhancement: reorderable instance selector (#1045)
This commit is contained in:
parent
136cefaf2a
commit
38eadde947
@ -76,28 +76,30 @@ class SelectInstanceBottomSheet : Screen {
|
|||||||
var instanceToDelete by remember { mutableStateOf<String?>(null) }
|
var instanceToDelete by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
LaunchedEffect(model) {
|
LaunchedEffect(model) {
|
||||||
model.effects.onEach { evt ->
|
model.effects
|
||||||
when (evt) {
|
.onEach { evt ->
|
||||||
SelectInstanceMviModel.Effect.CloseDialog -> {
|
when (evt) {
|
||||||
changeInstanceDialogOpen = false
|
SelectInstanceMviModel.Effect.CloseDialog -> {
|
||||||
}
|
changeInstanceDialogOpen = false
|
||||||
|
}
|
||||||
|
|
||||||
is SelectInstanceMviModel.Effect.Confirm -> {
|
is SelectInstanceMviModel.Effect.Confirm -> {
|
||||||
notificationCenter.send(NotificationCenterEvent.InstanceSelected(evt.instance))
|
notificationCenter.send(NotificationCenterEvent.InstanceSelected(evt.instance))
|
||||||
navigationCoordinator.hideBottomSheet()
|
navigationCoordinator.hideBottomSheet()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}.launchIn(this)
|
||||||
}.launchIn(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.padding(
|
Modifier
|
||||||
top = Spacing.s,
|
.padding(
|
||||||
start = Spacing.s,
|
top = Spacing.s,
|
||||||
end = Spacing.s,
|
start = Spacing.s,
|
||||||
bottom = Spacing.m,
|
end = Spacing.s,
|
||||||
).fillMaxHeight(0.6f),
|
bottom = Spacing.m,
|
||||||
|
).fillMaxHeight(0.6f),
|
||||||
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
verticalArrangement = Arrangement.spacedBy(Spacing.s),
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
@ -158,15 +160,19 @@ class SelectInstanceBottomSheet : Screen {
|
|||||||
shadowElevation = elevation,
|
shadowElevation = elevation,
|
||||||
) {
|
) {
|
||||||
SelectInstanceItem(
|
SelectInstanceItem(
|
||||||
modifier = Modifier.draggableHandle(),
|
|
||||||
instance = instance,
|
instance = instance,
|
||||||
isActive = isActive,
|
isActive = isActive,
|
||||||
|
onDragStarted =
|
||||||
|
rememberCallback(model) {
|
||||||
|
model.reduce(SelectInstanceMviModel.Intent.HapticIndication)
|
||||||
|
},
|
||||||
onClick =
|
onClick =
|
||||||
rememberCallback(model) {
|
rememberCallback(model) {
|
||||||
model.reduce(
|
model.reduce(
|
||||||
SelectInstanceMviModel.Intent.SelectInstance(instance),
|
SelectInstanceMviModel.Intent.SelectInstance(instance),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
reorderableScope = this,
|
||||||
options =
|
options =
|
||||||
buildList {
|
buildList {
|
||||||
if (!isActive) {
|
if (!isActive) {
|
||||||
|
@ -8,13 +8,24 @@ interface SelectInstanceMviModel :
|
|||||||
MviModel<SelectInstanceMviModel.Intent, SelectInstanceMviModel.State, SelectInstanceMviModel.Effect>,
|
MviModel<SelectInstanceMviModel.Intent, SelectInstanceMviModel.State, SelectInstanceMviModel.Effect>,
|
||||||
ScreenModel {
|
ScreenModel {
|
||||||
sealed interface Intent {
|
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
|
data object SubmitChangeInstanceDialog : Intent
|
||||||
}
|
}
|
||||||
@ -30,6 +41,8 @@ interface SelectInstanceMviModel :
|
|||||||
sealed interface Effect {
|
sealed interface Effect {
|
||||||
data object CloseDialog : Effect
|
data object CloseDialog : Effect
|
||||||
|
|
||||||
data class Confirm(val instance: String) : Effect
|
data class Confirm(
|
||||||
|
val instance: String,
|
||||||
|
) : Effect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.architecture.DefaultMviModel
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.InstanceSelectionRepository
|
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.InstanceSelectionRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.ValidationError
|
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.identity.repository.ApiConfigurationRepository
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommunityRepository
|
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommunityRepository
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
@ -18,22 +19,27 @@ class SelectInstanceViewModel(
|
|||||||
private val instanceRepository: InstanceSelectionRepository,
|
private val instanceRepository: InstanceSelectionRepository,
|
||||||
private val communityRepository: CommunityRepository,
|
private val communityRepository: CommunityRepository,
|
||||||
private val apiConfigurationRepository: ApiConfigurationRepository,
|
private val apiConfigurationRepository: ApiConfigurationRepository,
|
||||||
) : SelectInstanceMviModel,
|
private val hapticFeedback: HapticFeedback,
|
||||||
DefaultMviModel<SelectInstanceMviModel.Intent, SelectInstanceMviModel.State, SelectInstanceMviModel.Effect>(
|
) : DefaultMviModel<SelectInstanceMviModel.Intent, SelectInstanceMviModel.State, SelectInstanceMviModel.Effect>(
|
||||||
initialState = SelectInstanceMviModel.State(),
|
initialState = SelectInstanceMviModel.State(),
|
||||||
) {
|
),
|
||||||
|
SelectInstanceMviModel {
|
||||||
private val saveOperationChannel = Channel<List<String>>()
|
private val saveOperationChannel = Channel<List<String>>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
screenModelScope.launch {
|
screenModelScope.launch {
|
||||||
apiConfigurationRepository.instance.onEach { instance ->
|
apiConfigurationRepository.instance
|
||||||
updateState { it.copy(currentInstance = instance) }
|
.onEach { instance ->
|
||||||
}.launchIn(this)
|
updateState { it.copy(currentInstance = instance) }
|
||||||
|
}.launchIn(this)
|
||||||
|
|
||||||
@OptIn(FlowPreview::class)
|
@OptIn(FlowPreview::class)
|
||||||
saveOperationChannel.receiveAsFlow().debounce(500).onEach { newInstances ->
|
saveOperationChannel
|
||||||
instanceRepository.updateAll(newInstances)
|
.receiveAsFlow()
|
||||||
}.launchIn(this)
|
.debounce(500)
|
||||||
|
.onEach { newInstances ->
|
||||||
|
instanceRepository.updateAll(newInstances)
|
||||||
|
}.launchIn(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uiState.value.instances.isEmpty()) {
|
if (uiState.value.instances.isEmpty()) {
|
||||||
@ -59,6 +65,7 @@ class SelectInstanceViewModel(
|
|||||||
is SelectInstanceMviModel.Intent.SubmitChangeInstanceDialog -> submitChangeInstance()
|
is SelectInstanceMviModel.Intent.SubmitChangeInstanceDialog -> submitChangeInstance()
|
||||||
is SelectInstanceMviModel.Intent.DeleteInstance -> deleteInstance(intent.value)
|
is SelectInstanceMviModel.Intent.DeleteInstance -> deleteInstance(intent.value)
|
||||||
is SelectInstanceMviModel.Intent.SwapIntances -> swapInstances(intent.from, intent.to)
|
is SelectInstanceMviModel.Intent.SwapIntances -> swapInstances(intent.from, intent.to)
|
||||||
|
SelectInstanceMviModel.Intent.HapticIndication -> hapticFeedback.vibrate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.commonui.lemmyui.OptionId
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
|
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
|
||||||
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp
|
import com.github.diegoberaldin.raccoonforlemmy.core.utils.toLocalDp
|
||||||
|
import sh.calvin.reorderable.ReorderableCollectionItemScope
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun SelectInstanceItem(
|
internal fun SelectInstanceItem(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
reorderableScope: ReorderableCollectionItemScope,
|
||||||
instance: String,
|
instance: String,
|
||||||
isActive: Boolean = false,
|
isActive: Boolean = false,
|
||||||
onClick: (() -> Unit)? = null,
|
onClick: (() -> Unit)? = null,
|
||||||
|
onDragStarted: (() -> Unit)? = null,
|
||||||
options: List<Option> = emptyList(),
|
options: List<Option> = emptyList(),
|
||||||
onOptionSelected: ((OptionId) -> Unit)? = null,
|
onOptionSelected: ((OptionId) -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
@ -84,10 +87,17 @@ internal fun SelectInstanceItem(
|
|||||||
Modifier
|
Modifier
|
||||||
.size(IconSize.m)
|
.size(IconSize.m)
|
||||||
.padding(Spacing.xs)
|
.padding(Spacing.xs)
|
||||||
.onGloballyPositioned {
|
.then(
|
||||||
|
with(reorderableScope) {
|
||||||
|
Modifier.draggableHandle(
|
||||||
|
onDragStarted = {
|
||||||
|
onDragStarted?.invoke()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
).onGloballyPositioned {
|
||||||
optionsOffset = it.positionInParent()
|
optionsOffset = it.positionInParent()
|
||||||
}
|
}.onClick(
|
||||||
.onClick(
|
|
||||||
onClick = {
|
onClick = {
|
||||||
optionsMenuOpen = true
|
optionsMenuOpen = true
|
||||||
},
|
},
|
||||||
|
@ -11,6 +11,7 @@ val selectInstanceModule =
|
|||||||
instanceRepository = get(),
|
instanceRepository = get(),
|
||||||
communityRepository = get(),
|
communityRepository = get(),
|
||||||
apiConfigurationRepository = get(),
|
apiConfigurationRepository = get(),
|
||||||
|
hapticFeedback = get(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user