Skip to content

Commit

Permalink
Create delete confirmation (#229)
Browse files Browse the repository at this point in the history
* Create delete confirmation

* Add missing localizations
  • Loading branch information
alexandregpereira authored Jan 13, 2024
1 parent ab4239d commit ea08bb1
Show file tree
Hide file tree
Showing 16 changed files with 156 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ class MonsterCompendiumStateHolder(
}

fun loadMonsters() = scope.launch {
fetchMonsterCompendium()
}

private suspend fun fetchMonsterCompendium() {
getMonsterCompendiumUseCase()
.zip(
getLastCompendiumScrollItemPositionUseCase()
Expand Down Expand Up @@ -192,10 +196,7 @@ class MonsterCompendiumStateHolder(
private fun navigateToCompendiumIndexFromMonsterIndex(monsterIndex: String) {
flowOf(state.value.items)
.map { items ->
items.indexOfFirst {
it is MonsterCompendiumItem.Item
&& it.monster.index == monsterIndex
}
items.compendiumIndexOf(monsterIndex)
}.onEach { compendiumIndex ->
if (compendiumIndex < 0) throw NavigateToCompendiumIndexError(monsterIndex)
}
Expand All @@ -205,14 +206,23 @@ class MonsterCompendiumStateHolder(
}
.catch { error ->
if (error is NavigateToCompendiumIndexError) {
loadMonsters()
fetchMonsterCompendium()
state.value.items.compendiumIndexOf(monsterIndex).takeIf { it >= 0 }?.let {
sendAction(GoToCompendiumIndex(it))
}
} else {
analytics.logException(error)
}
}
.launchIn(scope)
}

private fun List<MonsterCompendiumItem>.compendiumIndexOf(monsterIndex: String): Int {
return indexOfFirst {
it is MonsterCompendiumItem.Item && it.monster.index == monsterIndex
}
}

private fun observeEvents() {
scope.launch {
folderPreviewResultListener.result.collect { event ->
Expand All @@ -236,8 +246,11 @@ class MonsterCompendiumStateHolder(
loadMonsters()
}.launchIn(scope)

monsterRegistrationEventListener.collectOnSaved {
loadMonsters()
monsterRegistrationEventListener.collectOnSaved { monsterIndex ->
scope.launch {
fetchMonsterCompendium()
navigateToCompendiumIndexFromMonsterIndex(monsterIndex)
}
}.launchIn(scope)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import br.alexandregpereira.hunter.detail.ui.MonsterDeleteConfirmation
import br.alexandregpereira.hunter.detail.ui.MonsterDetailOptionPicker
import br.alexandregpereira.hunter.detail.ui.MonsterDetailScreen
import br.alexandregpereira.hunter.ui.compose.FormField
import br.alexandregpereira.hunter.ui.compose.FormBottomSheet
import br.alexandregpereira.hunter.ui.compose.FormField
import br.alexandregpereira.hunter.ui.compose.LoadingScreen
import br.alexandregpereira.hunter.ui.compose.SwipeVerticalToDismiss
import br.alexandregpereira.hunter.ui.theme.HunterTheme
Expand Down Expand Up @@ -96,6 +97,13 @@ fun MonsterDetailFeature(
onClosed = viewModel::onCloneFormClosed,
onSaved = viewModel::onCloneFormSaved,
)

MonsterDeleteConfirmation(
show = viewState.showDeleteConfirmation,
contentPadding = contentPadding,
onConfirmed = viewModel::onDeleteConfirmed,
onClosed = viewModel::onDeleteClosed
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ internal class MonsterDetailViewModel(

fun onCloneFormSaved() = stateHolder.onCloneFormSaved()

fun onDeleteConfirmed() = stateHolder.onDeleteConfirmed()

fun onDeleteClosed() = stateHolder.onDeleteClosed()

private fun MonsterDetailViewState.asMonsterDetailState(): MonsterDetailState {
return MonsterDetailState(
showDetail = showDetail,
Expand All @@ -142,6 +146,7 @@ internal class MonsterDetailViewModel(
isLoading = isLoading,
showCloneForm = showCloneForm,
monsterCloneName = monsterCloneName,
showDeleteConfirmation = showDeleteConfirmation,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ data class MonsterDetailViewState(
val isLoading: Boolean = true,
val showCloneForm: Boolean = false,
val monsterCloneName: String = "",
val showDeleteConfirmation: Boolean = false,
)

fun SavedStateHandle.getState(): MonsterDetailViewState {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2022 Alexandre Gomes Pereira
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package br.alexandregpereira.hunter.detail.ui

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import br.alexandregpereira.hunter.detail.R
import br.alexandregpereira.hunter.ui.compose.AppButton
import br.alexandregpereira.hunter.ui.compose.BottomSheet
import br.alexandregpereira.hunter.ui.compose.ScreenHeader

@Composable
fun MonsterDeleteConfirmation(
show: Boolean,
contentPadding: PaddingValues = PaddingValues(),
onConfirmed: () -> Unit = {},
onClosed: () -> Unit = {}
) = BottomSheet(
opened = show,
contentPadding = PaddingValues(
top = 16.dp + contentPadding.calculateTopPadding(),
bottom = 16.dp + contentPadding.calculateBottomPadding(),
start = 16.dp,
end = 16.dp,
),
onClose = onClosed,
) {
Spacer(modifier = Modifier.height(16.dp))

ScreenHeader(
title = stringResource(R.string.monster_detail_delete_question),
)

Spacer(modifier = Modifier.height(32.dp))

AppButton(
text = stringResource(R.string.monster_detail_delete_confirmation),
onClick = onConfirmed
)
}

@Preview
@Composable
private fun MonsterDeleteConfirmationPreview() {
MonsterDeleteConfirmation(
show = true,
onConfirmed = {},
onClosed = {}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
Expand Down Expand Up @@ -214,13 +214,13 @@ private fun OnMonsterChanged(
pagerState: PagerState,
onMonsterChanged: (monster: MonsterState) -> Unit
) {
var initialMonsterIndexState by remember { mutableStateOf(initialMonsterIndex) }
var initialMonsterIndexState by remember { mutableIntStateOf(initialMonsterIndex) }

LaunchedEffect(key1 = initialMonsterIndex) {
pagerState.scrollToPage(initialMonsterIndex)
}

LaunchedEffect(pagerState) {
LaunchedEffect(pagerState, monsters) {
snapshotFlow { pagerState.currentPage }.collect { page ->
if (initialMonsterIndexState == initialMonsterIndex) {
onMonsterChanged(monsters[page])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,6 @@
<string name="monster_detail_clone_monster_name">Nome</string>
<string name="monster_detail_edit">Editar</string>
<string name="monster_detail_delete">Remover</string>
<string name="monster_detail_delete_question">Tem certeza que deseja excluir esse monstro?</string>
<string name="monster_detail_delete_confirmation">Tenho certeza</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,6 @@
<string name="monster_detail_clone_monster_name">Name</string>
<string name="monster_detail_edit">Edit</string>
<string name="monster_detail_delete">Delete</string>
<string name="monster_detail_delete_question">Are you sure you want to delete this monster?</string>
<string name="monster_detail_delete_confirmation">I\'m sure</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,22 @@ class MonsterDetailAnalytics(
)
)
}

fun trackMonsterDetailDeleteConfirmed(monsterIndex: String) {
analytics.track(
eventName = "MonsterDetail - delete confirmed",
params = mapOf(
"monsterIndex" to monsterIndex,
)
)
}

fun trackMonsterDetailDeleteCanceled(monsterIndex: String) {
analytics.track(
eventName = "MonsterDetail - delete canceled",
params = mapOf(
"monsterIndex" to monsterIndex,
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ data class MonsterDetailState(
val showDetail: Boolean = false,
val showCloneForm: Boolean = false,
val monsterCloneName: String = "",
val showDeleteConfirmation: Boolean = false,
)

val MonsterDetailState.ShowOptions: MonsterDetailState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ class MonsterDetailStateHolder(
}

DELETE -> {
deleteMonster()
analytics.trackMonsterDetailDeleteClicked(monsterIndex)
setState { copy(showDeleteConfirmation = true) }
}

CHANGE_TO_FEET -> {
Expand Down Expand Up @@ -217,6 +218,17 @@ class MonsterDetailStateHolder(
cloneMonster()
}

fun onDeleteConfirmed() {
analytics.trackMonsterDetailDeleteConfirmed(monsterIndex)
setState { copy(showDeleteConfirmation = false) }
deleteMonster()
}

fun onDeleteClosed() {
analytics.trackMonsterDetailDeleteCanceled(monsterIndex)
setState { copy(showDeleteConfirmation = false) }
}

private fun getMonsterDetail(
monsterIndex: String = this.monsterIndex,
invalidateCache: Boolean = false
Expand Down Expand Up @@ -306,8 +318,8 @@ class MonsterDetailStateHolder(
if (monsterIndexes.isEmpty()) {
getMonsterDetail(monsterIndex, invalidateCache = true)
.toMonsterDetailState()
.map {
it to monsterIndex
.map { state ->
state to monsterIndex
}
} else flowOf(currentState to currentMonsterIndex)
}.flowOn(dispatcher)
Expand All @@ -323,7 +335,6 @@ class MonsterDetailStateHolder(
}

private fun deleteMonster() {
analytics.trackMonsterDetailDeleteClicked(monsterIndex)
deleteMonster(monsterIndex)
.flowOn(dispatcher)
.onEach {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@ sealed class MonsterRegistrationEvent {
}

sealed class MonsterRegistrationResult {
data object OnSaved : MonsterRegistrationResult()
data class OnSaved(val monsterIndex: String) : MonsterRegistrationResult()
}

interface MonsterRegistrationEventListener : EventListener<MonsterRegistrationResult>

interface MonsterRegistrationEventDispatcher : EventDispatcher<MonsterRegistrationEvent>

fun EventListener<MonsterRegistrationResult>.collectOnSaved(
onAction: () -> Unit
): Flow<Unit> = events.map { it is MonsterRegistrationResult.OnSaved }.map { onAction() }
onAction: (String) -> Unit
): Flow<Unit> = events.map { it as? MonsterRegistrationResult.OnSaved }
.map { event -> event?.let { onAction(it.monsterIndex) } }

fun emptyMonsterRegistrationEventDispatcher(): MonsterRegistrationEventDispatcher {
return object : MonsterRegistrationEventDispatcher {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ class MonsterRegistrationStateHolder internal constructor(
.flowOn(dispatcher)
.onEach {
onClose()
eventResultManager.dispatchEvent(MonsterRegistrationResult.OnSaved)
eventResultManager.dispatchEvent(MonsterRegistrationResult.OnSaved(
monsterIndex = state.value.monster.index
))
}
.launchIn(scope)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ internal fun SettingsScreen(
Column(Modifier.padding(contentPadding)) {

SettingsItem(
text = "Advanced Settings",
text = stringResource(R.string.settings_manage_advanced_settings),
onClick = viewIntent::onAdvancedSettingsClick
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@
<string name="settings_alternative_sources_json">URL do JSON de Fontes Alternativas</string>
<string name="settings_manage_monster_content">Gerenciar Conteúdos de Monstros</string>
<string name="settings_sync">Sincronizar</string>
<string name="settings_manage_advanced_settings">Configurações Avançadas</string>
</resources>
1 change: 1 addition & 0 deletions feature/settings/android/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@
<string name="settings_alternative_sources_json">Alternative Sources JSON URL</string>
<string name="settings_manage_monster_content">Manage Monsters Content</string>
<string name="settings_sync">Sync</string>
<string name="settings_manage_advanced_settings">Advanced Settings</string>
</resources>

0 comments on commit ea08bb1

Please sign in to comment.