Add characterSheet deletion feature

This commit is contained in:
Thomas Andres Gomez 2025-03-21 09:41:53 +01:00
parent 67e154ed4a
commit ecb0a6705f
14 changed files with 257 additions and 52 deletions

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#5f6368"
android:pathData="m376,660 l104,-104 104,104 56,-56 -104,-104 104,-104 -56,-56 -104,104 -104,-104 -56,56 104,104 -104,104 56,56ZM280,840q-33,0 -56.5,-23.5T200,760v-520h-40v-80h200v-40h240v40h200v80h-40v520q0,33 -23.5,56.5T680,840L280,840ZM680,240L280,240v520h400v-520ZM280,240v520,-520Z" />
</vector>

View file

@ -59,15 +59,15 @@ class CharacterSheetRepository(
}
suspend fun updateCharacter(
characterSheet: CharacterSheet,
sheet: CharacterSheet,
) {
store.updateCharacterSheet(sheet = characterSheet)
store.updateCharacterSheet(sheet = sheet)
}
suspend fun deleteCharacter(
characterId: String,
characterSheetId: String,
) {
store.deleteCharacterSheet(characterId = characterId)
store.deleteCharacterSheet(characterSheetId = characterSheetId)
}
fun checkCharacterSheetIdValidity(

View file

@ -84,11 +84,11 @@ class CharacterSheetStore(
}
suspend fun deleteCharacterSheet(
characterId: String,
characterSheetId: String,
) {
try {
client.deleteCharacterSheet(id = characterId)
_detailFlow.delete(characterId = characterId)
client.deleteCharacterSheet(id = characterSheetId)
_detailFlow.delete(characterId = characterSheetId)
} catch (exception: Exception) {
println(exception) // TODO
}

View file

@ -42,6 +42,7 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
import com.pixelized.desktop.lwa.utils.extention.onPreviewEscape
import com.pixelized.shared.lwa.model.campaign.Campaign
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.dialog__cancel_action
@ -112,15 +113,7 @@ private fun Dialog(
indication = null,
onClick = onDismissRequest,
)
.onPreviewKeyEvent {
when {
it.key == Key.Escape -> {
onDismissRequest()
true
}
else -> false
}
}
.onPreviewEscape(event = onDismissRequest)
.fillMaxSize()
.padding(all = 32.dp),
contentAlignment = Alignment.Center,

View file

@ -39,6 +39,7 @@ import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextField
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
import com.pixelized.desktop.lwa.ui.theme.lwa
import com.pixelized.desktop.lwa.utils.extention.onPreviewEscape
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__copy__error
import lwacharactersheet.composeapp.generated.resources.dialog__cancel_action
@ -109,16 +110,7 @@ private fun Dialog(
indication = null,
onClick = onDismissRequest,
)
.onPreviewKeyEvent {
when {
it.key == Key.Escape -> {
onDismissRequest()
true
}
else -> false
}
}
.onPreviewEscape(event = onDismissRequest)
.fillMaxSize()
.padding(all = 32.dp),
contentAlignment = Alignment.Center,

View file

@ -0,0 +1,151 @@
package com.pixelized.desktop.lwa.ui.screen.characterSheet.delete
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
import com.pixelized.desktop.lwa.utils.extention.onPreviewEscape
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__description
import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__title
import lwacharactersheet.composeapp.generated.resources.dialog__cancel_action
import lwacharactersheet.composeapp.generated.resources.dialog__confirm_action
import org.jetbrains.compose.resources.stringResource
@Stable
data class CharacterSheetDeleteDialogUio(
val characterSheetId: String,
val name: String,
)
@Composable
fun CharacterSheetDeleteDialog(
dialog: State<CharacterSheetDeleteDialogUio?>,
onConfirm: (CharacterSheetDeleteDialogUio) -> Unit,
onDismissRequest: () -> Unit,
) {
AnimatedContent(
modifier = Modifier.fillMaxSize(),
targetState = dialog.value,
transitionSpec = {
val enter = fadeIn() + slideInVertically { 32 }
val exit = fadeOut() + slideOutVertically { 32 }
enter togetherWith exit using SizeTransform(clip = false)
},
) {
Box(
modifier = Modifier.fillMaxSize()
) {
when (it) {
null -> Box(
modifier = Modifier,
)
else -> {
Dialog(
dialog = it,
onConfirm = onConfirm,
onDismissRequest = onDismissRequest,
)
}
}
}
}
}
@Composable
private fun Dialog(
dialog: CharacterSheetDeleteDialogUio,
onConfirm: (CharacterSheetDeleteDialogUio) -> Unit,
onDismissRequest: () -> Unit,
) {
Box(
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = onDismissRequest,
)
.onPreviewEscape(event = onDismissRequest)
.fillMaxSize()
.padding(all = 32.dp),
contentAlignment = Alignment.Center,
) {
DecoratedBox {
Surface {
Column(
verticalArrangement = Arrangement.spacedBy(space = 24.dp),
) {
Text(
modifier = Modifier
.padding(horizontal = 24.dp)
.padding(top = 24.dp),
style = MaterialTheme.typography.h6,
text = stringResource(Res.string.character_sheet__delete_dialog__title),
)
Text(
modifier = Modifier
.padding(horizontal = 24.dp),
style = MaterialTheme.typography.body1,
text = stringResource(
Res.string.character_sheet__delete_dialog__description,
dialog.name
),
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = 4.dp),
horizontalArrangement = Arrangement.spacedBy(
space = 4.dp,
alignment = Alignment.End
)
) {
TextButton(
onClick = onDismissRequest,
) {
Text(
text = stringResource(Res.string.dialog__cancel_action)
)
}
TextButton(
onClick = { onConfirm(dialog) },
) {
Text(
text = stringResource(Res.string.dialog__confirm_action)
)
}
}
}
}
}
}
}

View file

@ -74,7 +74,7 @@ class CharacterSheetViewModel(
)
suspend fun deleteCharacter(id: String) {
characterRepository.deleteCharacter(characterId = id)
characterRepository.deleteCharacter(characterSheetId = id)
}
fun onUseSkill(skill: CharacterSheetPageUio.Node) {

View file

@ -31,6 +31,7 @@ import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
import com.pixelized.desktop.lwa.utils.extention.onPreviewEscape
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.character_sheet__delete_dialog__description
@ -91,15 +92,7 @@ private fun Dialog(
indication = null,
onClick = onDismissRequest,
)
.onPreviewKeyEvent {
when {
it.key == Key.Escape -> {
onDismissRequest()
true
}
else -> false
}
}
.onPreviewEscape(event = onDismissRequest)
.fillMaxSize()
.padding(all = 32.dp),
contentAlignment = Alignment.Center,

View file

@ -42,6 +42,7 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
import com.pixelized.desktop.lwa.utils.extention.onPreviewEscape
import com.pixelized.shared.lwa.model.campaign.Campaign
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.dialog__cancel_action
@ -110,16 +111,7 @@ private fun Dialog(
indication = null,
onClick = onDismissRequest,
)
.onPreviewKeyEvent {
when {
it.key == Key.Escape -> {
onDismissRequest()
true
}
else -> false
}
}
.onPreviewEscape(event = onDismissRequest)
.fillMaxSize()
.padding(all = 32.dp),
contentAlignment = Alignment.Center,

View file

@ -33,6 +33,8 @@ import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
import com.pixelized.desktop.lwa.ui.navigation.window.LocalWindow
import com.pixelized.desktop.lwa.ui.screen.characterSheet.copy.CharacterSheetCopyDialog
import com.pixelized.desktop.lwa.ui.screen.characterSheet.delete.CharacterSheetDeleteDialog
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.CharacterSheetDeleteConfirmationDialog
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.ActionField
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.ActionFieldUio
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.BaseSkillFieldUio
@ -56,6 +58,7 @@ import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__ch
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__save_action
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__magic_action
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__special_action
import lwacharactersheet.composeapp.generated.resources.ic_delete_forever_24dp
import lwacharactersheet.composeapp.generated.resources.ic_save_24dp
import lwacharactersheet.composeapp.generated.resources.ic_save_as_24dp
import org.jetbrains.compose.resources.painterResource
@ -124,6 +127,11 @@ fun CharacterSheetEditPage(
viewModel.onNewAction()
}
},
onDelete = {
scope.launch {
viewModel.showDeleteCharacterSheetDialog()
}
},
onCopy = {
scope.launch {
viewModel.showCopyCharacterSheetDialog()
@ -155,6 +163,21 @@ fun CharacterSheetEditPage(
viewModel.hideCopyCharacterSheetDialog()
}
)
CharacterSheetDeleteDialog(
dialog = viewModel.deleteCharacterSheetDialog,
onConfirm = { dialog ->
scope.launch {
viewModel.delete(characterSheetId = dialog.characterSheetId)
if (screen.popBackStack().not()) {
windowController.hideWindow(window = window)
}
}
},
onDismissRequest = {
viewModel.hideDeleteCharacterSheetDialog()
}
)
}
@Composable
@ -165,6 +188,7 @@ fun CharacterSheetEdit(
onNewSpecialSkill: () -> Unit,
onNewMagicSkill: () -> Unit,
onNewAction: () -> Unit,
onDelete: () -> Unit,
onCopy: () -> Unit,
onSave: () -> Unit,
) {
@ -180,6 +204,15 @@ fun CharacterSheetEdit(
)
},
actions = {
IconButton(
onClick = onDelete,
) {
Icon(
painter = painterResource(Res.drawable.ic_delete_forever_24dp),
tint = MaterialTheme.colors.primary,
contentDescription = null,
)
}
IconButton(
onClick = onCopy,
) {

View file

@ -8,6 +8,7 @@ import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetReposit
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
import com.pixelized.desktop.lwa.ui.navigation.screen.destination.CharacterSheetEditDestination
import com.pixelized.desktop.lwa.ui.screen.characterSheet.copy.CharacterSheetCopyDialogUio
import com.pixelized.desktop.lwa.ui.screen.characterSheet.delete.CharacterSheetDeleteDialogUio
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.common.SkillFieldFactory
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.ActionFieldUio
import kotlinx.coroutines.flow.MutableStateFlow
@ -34,6 +35,9 @@ class CharacterSheetEditViewModel(
private val copyDialog = mutableStateOf<CharacterSheetCopyDialogUio?>(null)
val copyCharacterSheetDialog: State<CharacterSheetCopyDialogUio?> = copyDialog
private val deleteDialog = mutableStateOf<CharacterSheetDeleteDialogUio?>(null)
val deleteCharacterSheetDialog: State<CharacterSheetDeleteDialogUio?> = deleteDialog
private val _characterSheet = mutableStateOf(
runBlocking {
sheetFactory.convertToUio(
@ -44,7 +48,6 @@ class CharacterSheetEditViewModel(
)
val characterSheet: State<CharacterSheetEditPageUio> get() = _characterSheet
// TODO
suspend fun showCopyCharacterSheetDialog() {
val characterSheetId = MutableStateFlow(argument.id ?: "")
val error = MutableStateFlow(false)
@ -67,11 +70,21 @@ class CharacterSheetEditViewModel(
)
}
// TODO
fun hideCopyCharacterSheetDialog() {
copyDialog.value = null
}
fun showDeleteCharacterSheetDialog() {
deleteDialog.value = CharacterSheetDeleteDialogUio(
characterSheetId = argument.id ?: "",
name = characterSheet.value.name.value.value,
)
}
fun hideDeleteCharacterSheetDialog() {
deleteDialog.value = null
}
suspend fun onNewSpecialSkill() {
val id = UUID.randomUUID().toString()
val skill = skillFactory.createSkill(
@ -148,7 +161,7 @@ class CharacterSheetEditViewModel(
editedSheet = _characterSheet.value,
)
characterSheetRepository.updateCharacter(
characterSheet = updatedSheet,
sheet = updatedSheet,
)
}
@ -160,7 +173,15 @@ class CharacterSheetEditViewModel(
editedSheet = _characterSheet.value,
)
characterSheetRepository.updateCharacter(
characterSheet = updatedSheet.copy(id = characterSheetId),
sheet = updatedSheet.copy(id = characterSheetId),
)
}
suspend fun delete(
characterSheetId: String,
) {
characterSheetRepository.deleteCharacter(
characterSheetId = characterSheetId
)
}
}

View file

@ -151,7 +151,7 @@ class LevelUpViewModel(
)
characterSheetRepository.updateCharacter(
characterSheet = levelUpCharacter,
sheet = levelUpCharacter,
)
}
}

View file

@ -0,0 +1,17 @@
package com.pixelized.desktop.lwa.utils.extention
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
fun Modifier.onPreviewEscape(event: () -> Unit): Modifier = this.onPreviewKeyEvent {
when {
it.key == Key.Escape -> {
event()
true
}
else -> false
}
}

View file

@ -9,7 +9,11 @@ import io.ktor.server.response.respondText
fun Engine.deleteCharacter(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
return {
val characterSheetId = call.parameters.characterSheetId
val deleted = characterService.deleteCharacterSheet(characterId = characterSheetId)
val deleted = characterService.deleteCharacterSheet(
characterId = characterSheetId
)
// TODO campaign & alteration cleanup.
if (deleted) {
call.respondText(