diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index 3501227..0df87e7 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -242,6 +242,7 @@ Ajouter aux Npcs Retirer des Npcs Créer un personnage + Édition d'Altération Filtrer par nom : Créer une altération Supprimer l'altération diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/network/LwaNetworkException.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/network/LwaNetworkException.kt index 3f048e8..753751a 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/network/LwaNetworkException.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/network/LwaNetworkException.kt @@ -4,10 +4,12 @@ import com.pixelized.shared.lwa.protocol.rest.APIResponse class LwaNetworkException( val status: Int, + val code: APIResponse.ErrorCode?, message: String, ) : Exception(message) { constructor(error: APIResponse<*>) : this( status = error.status, + code = error.code, message = error.message ?: "An unknown error occurred" ) } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/error/ErrorSnackUio.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/error/ErrorSnackUio.kt index 3b39cda..d3f60bf 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/error/ErrorSnackUio.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/error/ErrorSnackUio.kt @@ -7,6 +7,10 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.Stable import com.pixelized.desktop.lwa.LocalErrorSnackHost import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.collectLatest +import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.error__default__action +import org.jetbrains.compose.resources.getString @Stable class ErrorSnackUio( @@ -15,10 +19,10 @@ class ErrorSnackUio( val duration: SnackbarDuration, ) { companion object { - fun from(exception: Exception) = ErrorSnackUio( + suspend fun from(exception: Exception) = ErrorSnackUio( message = exception.localizedMessage, - action = "Ok", - duration = SnackbarDuration.Indefinite + action = getString(Res.string.error__default__action), + duration = SnackbarDuration.Long, ) } } @@ -29,7 +33,7 @@ fun ErrorSnackHandler( error: SharedFlow, ) { LaunchedEffect(Unit) { - error.collect { + error.collectLatest { snack.showSnackbar( message = it.message, actionLabel = it.action, diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/textfield/LwaTextField.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/textfield/LwaTextField.kt index ada79c6..22fdf75 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/textfield/LwaTextField.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/composable/textfield/LwaTextField.kt @@ -1,6 +1,7 @@ package com.pixelized.desktop.lwa.ui.composable.textfield import androidx.compose.foundation.layout.height +import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.TextField import androidx.compose.material.TextFieldColors @@ -9,20 +10,21 @@ import androidx.compose.runtime.Stable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.graphics.Shape import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.pixelized.desktop.lwa.ui.theme.color.component.LwaTextFieldColors import com.pixelized.desktop.lwa.utils.rememberKeyboardActions -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.MutableStateFlow @Stable data class LwaTextFieldUio( val enable: Boolean = true, - val isError: StateFlow, - val labelFlow: StateFlow?, - val valueFlow: StateFlow, - val placeHolderFlow: StateFlow?, + val isError: MutableStateFlow, + val labelFlow: MutableStateFlow?, + val valueFlow: MutableStateFlow, + val placeHolderFlow: MutableStateFlow?, val onValueChange: (String) -> Unit, ) @@ -30,6 +32,7 @@ data class LwaTextFieldUio( fun LwaTextField( modifier: Modifier = Modifier, colors: TextFieldColors = LwaTextFieldColors(), + shape: Shape = MaterialTheme.shapes.small, leadingIcon: @Composable (() -> Unit)? = null, trailingIcon: @Composable (() -> Unit)? = null, singleLine: Boolean = true, @@ -51,6 +54,7 @@ fun LwaTextField( TextField( modifier = localModifier.then(other = modifier), colors = colors, + shape = shape, keyboardActions = rememberKeyboardActions { focus.moveFocus(FocusDirection.Next) }, diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMAlterationEditDestination.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMAlterationEditDestination.kt index 8bdd8f9..b24461a 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMAlterationEditDestination.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/gamemaster/GMAlterationEditDestination.kt @@ -7,7 +7,7 @@ import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.composable import androidx.navigation.navArgument -import com.pixelized.desktop.lwa.ui.screen.gamemaster.alteration.edit.GMAlterationEditPage +import com.pixelized.desktop.lwa.ui.screen.gamemaster.alteration.edit.GMAlterationEditScreen import com.pixelized.desktop.lwa.utils.extention.ARG @Stable @@ -44,7 +44,7 @@ fun NavGraphBuilder.composableGameMasterAlterationEditPage() { route = GMAlterationEditDestination.baseRoute(), arguments = GMAlterationEditDestination.arguments(), ) { - GMAlterationEditPage() + GMAlterationEditScreen() } } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/alteration/edit/GMAlterationEditFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/alteration/edit/GMAlterationEditFactory.kt index 14e5d51..c06cae7 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/alteration/edit/GMAlterationEditFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/alteration/edit/GMAlterationEditFactory.kt @@ -1,34 +1,39 @@ package com.pixelized.desktop.lwa.ui.screen.gamemaster.alteration.edit import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio +import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagFactory import com.pixelized.shared.lwa.model.alteration.Alteration +import com.pixelized.shared.lwa.model.tag.Tag import com.pixelized.shared.lwa.parser.expression.ExpressionParser import kotlinx.coroutines.flow.MutableStateFlow import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.game_master__alteration__edit_description import lwacharactersheet.composeapp.generated.resources.game_master__alteration__edit_field_expression import lwacharactersheet.composeapp.generated.resources.game_master__alteration__edit_field_id import lwacharactersheet.composeapp.generated.resources.game_master__alteration__edit_id import lwacharactersheet.composeapp.generated.resources.game_master__alteration__edit_label -import lwacharactersheet.composeapp.generated.resources.game_master__alteration__edit_description -import lwacharactersheet.composeapp.generated.resources.game_master__alteration__edit_tags import org.jetbrains.compose.resources.getString import java.util.UUID class GMAlterationEditFactory( private val expressionParser: ExpressionParser, + private val tagFactory: GMTagFactory, ) { suspend fun createForm( + originId: String?, alteration: Alteration?, + tags: Collection, ): GMAlterationEditPageUio { val id = MutableStateFlow(alteration?.id ?: "") val label = MutableStateFlow(alteration?.metadata?.name ?: "") val description = MutableStateFlow(alteration?.metadata?.description ?: "") - val tags = MutableStateFlow(alteration?.tags?.joinToString(", ") { it } ?: "") - val fields = MutableStateFlow(alteration?.fields?.map { createField(it) } ?: listOf(createField(null))) + val fields = MutableStateFlow(alteration?.fields?.map { createField(it) } ?: listOf( + createField(null) + )) return GMAlterationEditPageUio( id = LwaTextFieldUio( - enable = true, + enable = originId == null, isError = MutableStateFlow(false), labelFlow = MutableStateFlow(getString(Res.string.game_master__alteration__edit_id)), valueFlow = id, @@ -51,13 +56,9 @@ class GMAlterationEditFactory( placeHolderFlow = null, onValueChange = { description.value = it }, ), - tags = LwaTextFieldUio( - enable = true, - isError = MutableStateFlow(false), - labelFlow = MutableStateFlow(getString(Res.string.game_master__alteration__edit_tags)), - valueFlow = tags, - placeHolderFlow = null, - onValueChange = { tags.value = it }, + tags = tagFactory.convertToGMTagItemUio( + tags = tags, + selectedTagIds = alteration?.tags ?: emptyList(), ), fields = fields, ) @@ -101,7 +102,7 @@ class GMAlterationEditFactory( name = form.label.valueFlow.value, description = form.description.valueFlow.value, ), - tags = form.tags.valueFlow.value.split(","), + tags = form.tags.filter { it.highlight }.map { it.id }, fields = form.fields.value.mapNotNull { field -> expressionParser.parse(input = field.expression.valueFlow.value)?.let { Alteration.Field( diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/alteration/edit/GMAlterationEditPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/alteration/edit/GMAlterationEditPage.kt index 8d6c051..e07e731 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/alteration/edit/GMAlterationEditPage.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/alteration/edit/GMAlterationEditPage.kt @@ -1,9 +1,16 @@ package com.pixelized.desktop.lwa.ui.screen.gamemaster.alteration.edit import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.togetherWith +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.draggable +import androidx.compose.foundation.gestures.rememberDraggableState +import androidx.compose.foundation.gestures.scrollBy import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -11,21 +18,31 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.Button import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold import androidx.compose.material.Text +import androidx.compose.material.TextButton +import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Close import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable +import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment @@ -34,21 +51,27 @@ import androidx.compose.ui.input.key.Key import androidx.compose.ui.input.key.KeyEventType import androidx.compose.ui.input.key.key import androidx.compose.ui.input.key.type +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavHostController import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackHandler import com.pixelized.desktop.lwa.ui.composable.key.KeyHandler import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextField import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController -import com.pixelized.desktop.lwa.ui.screen.gamemaster.LocalGMScreenController +import com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster.GMAlterationEditDestination +import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagButton +import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagUio import com.pixelized.desktop.lwa.ui.theme.color.component.LwaButtonColors import com.pixelized.desktop.lwa.ui.theme.lwa +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.game_master__alteration__edit_add_field -import lwacharactersheet.composeapp.generated.resources.game_master__alteration__edit_field_cancel import lwacharactersheet.composeapp.generated.resources.game_master__alteration__edit_field_save +import lwacharactersheet.composeapp.generated.resources.game_master__alteration__title import lwacharactersheet.composeapp.generated.resources.ic_save_24dp import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource @@ -59,7 +82,7 @@ data class GMAlterationEditPageUio( val id: LwaTextFieldUio, val label: LwaTextFieldUio, val description: LwaTextFieldUio, - val tags: LwaTextFieldUio, + val tags: List, val fields: MutableStateFlow>, ) { @Stable @@ -76,40 +99,37 @@ object GMAlterationEditPageDefault { } @Composable -fun GMAlterationEditPage( +fun GMAlterationEditScreen( viewModel: GMAlterationEditViewModel = koinViewModel(), ) { val screen = LocalScreenController.current val scope = rememberCoroutineScope() val form = viewModel.form.collectAsState() - AnimatedContent( - targetState = form.value, - transitionSpec = { fadeIn() togetherWith fadeOut() } - ) { - when (it) { - null -> Box(modifier = Modifier.fillMaxSize()) - else -> GMAlterationEditContent( - modifier = Modifier.fillMaxSize(), - form = it, - paddings = GMAlterationEditPageDefault.paddings, - addField = { - scope.launch { - viewModel.addField() - } - }, - removeField = viewModel::removeField, - onSave = { - scope.launch { - viewModel.save() - } - }, - onCancel = { - screen.popBackStack() - }, - ) - } - } + GMAlterationEditContent( + modifier = Modifier.fillMaxSize(), + form = form, + paddings = GMAlterationEditPageDefault.paddings, + onBack = { + screen.navigateBack() + }, + addField = { + scope.launch { + viewModel.addField() + } + }, + removeField = viewModel::removeField, + onSave = { + scope.launch { + if (viewModel.save()) { + screen.navigateBack() + } + } + }, + onTag = { tag -> + viewModel.addTag(tag = tag) + }, + ) ErrorSnackHandler( error = viewModel.error, @@ -117,7 +137,7 @@ fun GMAlterationEditPage( AlterationEditKeyHandler( onDismissRequest = { - screen.popBackStack() + screen.navigateBack() } ) } @@ -125,148 +145,218 @@ fun GMAlterationEditPage( @Composable private fun GMAlterationEditContent( modifier: Modifier = Modifier, - form: GMAlterationEditPageUio, + scope: CoroutineScope = rememberCoroutineScope(), + tagsState: LazyListState = rememberLazyListState(), + form: State, paddings: PaddingValues, + onBack: () -> Unit, addField: () -> Unit, removeField: (index: Int) -> Unit, onSave: () -> Unit, - onCancel: () -> Unit, + onTag: (GMTagUio) -> Unit, ) { - val fields = form.fields.collectAsState() - - LazyColumn( + Scaffold( modifier = modifier, - contentPadding = paddings, - verticalArrangement = Arrangement.spacedBy(space = 8.dp), - ) { - item( - key = "Id", - ) { - LwaTextField( - modifier = Modifier - .animateItem() - .fillMaxWidth(), - field = form.id, - singleLine = true, - ) - } - item( - key = "Name", - ) { - LwaTextField( - modifier = Modifier - .animateItem() - .fillMaxWidth(), - field = form.label, - singleLine = true, - ) - } - item( - key = "Description", - ) { - LwaTextField( - modifier = Modifier - .animateItem() - .fillMaxWidth(), - field = form.description, - singleLine = false, - ) - } - item( - key = "Tags", - ) { - LwaTextField( - modifier = Modifier - .animateItem() - .fillMaxWidth(), - field = form.tags, - singleLine = true, - ) - } - itemsIndexed( - items = fields.value, - key = { _, item -> item.key }, - ) { index, item -> - Row( - modifier = Modifier.animateItem(), - horizontalArrangement = Arrangement.spacedBy(space = 8.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - LwaTextField( - modifier = Modifier.weight(1f), - field = item.id, - singleLine = true, - ) - LwaTextField( - modifier = Modifier.weight(1f), - field = item.expression, - singleLine = true, - ) - IconButton( - onClick = { removeField(index) }, - ) { - Icon( - imageVector = Icons.Default.Close, - contentDescription = null, - tint = MaterialTheme.lwa.colorScheme.base.primary, + topBar = { + TopAppBar( + title = { + Text( + text = stringResource(Res.string.game_master__alteration__title), ) + }, + navigationIcon = { + IconButton( + onClick = onBack, + ) { + Icon( + imageVector = Icons.AutoMirrored.Default.ArrowBack, + contentDescription = null, + ) + } + }, + actions = { + TextButton( + onClick = onSave, + ) { + Text( + modifier = Modifier.padding(end = 4.dp), + color = MaterialTheme.lwa.colorScheme.base.primary, + fontWeight = FontWeight.SemiBold, + text = stringResource(Res.string.game_master__alteration__edit_field_save), + ) + Icon( + painter = painterResource(Res.drawable.ic_save_24dp), + tint = MaterialTheme.lwa.colorScheme.base.primary, + contentDescription = null, + ) + } + } + ) + }, + content = { + AnimatedContent( + targetState = form.value, + transitionSpec = { + if (initialState?.id == targetState?.id) { + EnterTransition.None togetherWith ExitTransition.None + } else { + fadeIn() togetherWith fadeOut() + } + } + ) { + when (it) { + null -> Box( + modifier = Modifier.fillMaxSize(), + ) + + else -> { + val fields = it.fields.collectAsState() + + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = paddings, + verticalArrangement = Arrangement.spacedBy(space = 8.dp), + ) { + item( + key = "Id", + ) { + LwaTextField( + modifier = Modifier + .animateItem() + .fillMaxWidth(), + field = it.id, + singleLine = true, + ) + } + item( + key = "Name", + ) { + LwaTextField( + modifier = Modifier + .animateItem() + .fillMaxWidth(), + field = it.label, + singleLine = true, + ) + } + item( + key = "Description", + ) { + LwaTextField( + modifier = Modifier + .animateItem() + .fillMaxWidth(), + field = it.description, + singleLine = false, + ) + } + item( + key = "Tags", + ) { + LazyRow( + modifier = Modifier.draggable( + orientation = Orientation.Horizontal, + state = rememberDraggableState { delta -> + scope.launch { + tagsState.scrollBy(-delta) + } + }, + ), + state = tagsState, + horizontalArrangement = Arrangement.spacedBy(space = 8.dp), + ) { + items( + items = it.tags, + ) { tag -> + GMTagButton( + modifier = Modifier.height(48.dp), + tag = tag, + onTag = { onTag(tag) } + ) + } + } + } + itemsIndexed( + items = fields.value, + key = { _, item -> item.key }, + ) { index, item -> + Row( + modifier = Modifier.animateItem(), + horizontalArrangement = Arrangement.spacedBy(space = 8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + LwaTextField( + modifier = Modifier.weight(1f), + field = item.id, + singleLine = true, + ) + LwaTextField( + modifier = Modifier.weight(1f), + field = item.expression, + singleLine = true, + ) + IconButton( + modifier = Modifier + .size(size = 56.dp) + .background( + color = MaterialTheme.lwa.colorScheme.elevated.base1dp, + shape = MaterialTheme.shapes.small, + ), + onClick = { removeField(index) }, + ) { + Icon( + imageVector = Icons.Default.Close, + tint = MaterialTheme.lwa.colorScheme.base.primary, + contentDescription = null, + ) + } + } + } + item( + key = "Actions", + ) { + Column( + modifier = Modifier + .animateItem() + .fillMaxWidth(), + horizontalAlignment = Alignment.End + ) { + Button( + colors = LwaButtonColors(), + shape = CircleShape, + onClick = addField, + ) { + Text( + modifier = Modifier.padding(end = 4.dp), + text = stringResource(Res.string.game_master__alteration__edit_add_field), + ) + Icon( + imageVector = Icons.Default.Add, + contentDescription = null, + ) + } + Button( + colors = LwaButtonColors(), + shape = CircleShape, + onClick = onSave, + ) { + Text( + modifier = Modifier.padding(end = 4.dp), + text = stringResource(Res.string.game_master__alteration__edit_field_save), + ) + Icon( + painter = painterResource(Res.drawable.ic_save_24dp), + contentDescription = null, + ) + } + } + } + } + } } } } - item( - key = "Actions", - ) { - Column( - modifier = Modifier - .animateItem() - .fillMaxWidth(), - horizontalAlignment = Alignment.End - ) { - Button( - colors = LwaButtonColors(), - shape = CircleShape, - onClick = addField, - ) { - Text( - modifier = Modifier.padding(end = 4.dp), - text = stringResource(Res.string.game_master__alteration__edit_add_field), - ) - Icon( - imageVector = Icons.Default.Add, - contentDescription = null, - ) - } - Button( - colors = LwaButtonColors(), - shape = CircleShape, - onClick = onSave, - ) { - Text( - modifier = Modifier.padding(end = 4.dp), - text = stringResource(Res.string.game_master__alteration__edit_field_save), - ) - Icon( - painter = painterResource(Res.drawable.ic_save_24dp), - contentDescription = null, - ) - } - Button( - colors = LwaButtonColors(), - shape = CircleShape, - onClick = onCancel, - ) { - Text( - modifier = Modifier.padding(end = 4.dp), - text = stringResource(Res.string.game_master__alteration__edit_field_cancel), - ) - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = null, - ) - } - } - } - } + ) } @Composable @@ -283,4 +373,9 @@ private fun AlterationEditKeyHandler( else -> false } } -} \ No newline at end of file +} + +private fun NavHostController.navigateBack() = popBackStack( + route = GMAlterationEditDestination.baseRoute(), + inclusive = true, +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/alteration/edit/GMAlterationEditViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/alteration/edit/GMAlterationEditViewModel.kt index c3a1d1f..aa27701 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/alteration/edit/GMAlterationEditViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/alteration/edit/GMAlterationEditViewModel.kt @@ -3,9 +3,13 @@ package com.pixelized.desktop.lwa.ui.screen.gamemaster.alteration.edit import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.pixelized.desktop.lwa.network.LwaNetworkException import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository +import com.pixelized.desktop.lwa.repository.tag.TagRepository import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackUio import com.pixelized.desktop.lwa.ui.navigation.screen.destination.gamemaster.GMAlterationEditDestination +import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagUio +import com.pixelized.shared.lwa.protocol.rest.APIResponse.ErrorCode import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow @@ -15,6 +19,7 @@ import kotlinx.coroutines.launch class GMAlterationEditViewModel( private val alterationRepository: AlterationRepository, + private val tagRepository: TagRepository, private val factory: GMAlterationEditFactory, savedStateHandle: SavedStateHandle, ) : ViewModel() { @@ -29,28 +34,33 @@ class GMAlterationEditViewModel( init { viewModelScope.launch { _form.value = factory.createForm( - alteration = alterationRepository.alteration(alterationId = argument.id) + originId = argument.id, + alteration = alterationRepository.alteration(alterationId = argument.id), + tags = tagRepository.alterationsTagFlow().value.values, ) } } - suspend fun save() { - val edited = factory.createAlteration(form = form.value) - val actual = alterationRepository.alterationFlow.value[edited?.id] - - if (edited == null) return + suspend fun save(): Boolean { + val edited = factory.createAlteration(form = form.value) ?: return false try { - if (argument.id == null && actual?.id != null) { - error("Id already taken by an another alteration") - } alterationRepository.updateAlteration( alteration = edited, create = argument.id == null, ) + return true + } catch (exception: LwaNetworkException) { + _form.value?.id?.isError?.value = exception.code == ErrorCode.AlterationId + _form.value?.label?.isError?.value = exception.code == ErrorCode.AlterationName + + val message = ErrorSnackUio.from(exception = exception) + _error.emit(message) + return false } catch (exception: Exception) { val message = ErrorSnackUio.from(exception = exception) _error.emit(message) + return false } } @@ -69,4 +79,16 @@ class GMAlterationEditViewModel( } } } + + fun addTag(tag: GMTagUio) { + _form.update { + it?.copy( + tags = it.tags.toMutableList().also { tags -> + val index = tags.indexOf(tag) + if (index > -1) + tags[index] = tag.copy(highlight = tag.highlight.not()) + } + ) + } + } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/common/tag/GMTag.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/common/tag/GMTag.kt index 1437e22..f1d7b05 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/common/tag/GMTag.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/common/tag/GMTag.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Button import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.material.Text @@ -15,6 +16,7 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.ui.theme.color.component.LwaButtonColors import com.pixelized.desktop.lwa.ui.theme.lwa @@ -61,4 +63,36 @@ fun GMTag( text = tag.label, ) } +} + + +@Composable +fun GMTagButton( + modifier: Modifier = Modifier, + padding: PaddingValues = GmTagDefault.padding, + shape: Shape = MaterialTheme.shapes.small, + tag: GMTagUio, + onTag: (() -> Unit)? = null, +) { + val animatedColor = animateColorAsState( + when (tag.highlight) { + true -> MaterialTheme.lwa.colorScheme.base.secondary + else -> MaterialTheme.lwa.colorScheme.base.onSurface + } + ) + Button( + modifier = modifier, + colors = LwaButtonColors(contentColor = animatedColor.value), + shape = shape, + enabled = onTag != null, + onClick = { onTag?.invoke() }, + ) { + Text( + modifier = Modifier.padding(paddingValues = padding), + color = animatedColor.value, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = tag.label, + ) + } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/common/tag/GMTagFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/common/tag/GMTagFactory.kt index 10f59cd..76e7d4c 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/common/tag/GMTagFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/common/tag/GMTagFactory.kt @@ -5,6 +5,23 @@ import java.text.Collator class GMTagFactory { + fun convertToGMTagItemUio( + tags: Collection, + selectedTagIds: List, + ): List { + return tags + .map { tag -> + GMTagUio( + id = tag.id, + label = tag.label, + highlight = selectedTagIds.contains(tag.id), + ) + } + .sortedWith( + comparator = compareBy(Collator.getInstance()) { it.label } + ) + } + fun convertToGMTagItemUio( tags: Collection, selectedTagId: String?, diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/LwaTheme.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/LwaTheme.kt index 9438901..3e06b69 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/LwaTheme.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/LwaTheme.kt @@ -56,7 +56,7 @@ fun LwaTheme( MaterialTheme( colors = lwaColors.base, typography = MaterialTheme.typography, - shapes = MaterialTheme.shapes, + shapes = lwaShapes.base, content = content, ) } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/color/component/LwaButtonColors.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/color/component/LwaButtonColors.kt index 8a8b2b6..de546e3 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/color/component/LwaButtonColors.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/color/component/LwaButtonColors.kt @@ -6,12 +6,14 @@ import androidx.compose.material.ContentAlpha import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable +import androidx.compose.ui.graphics.Color @Composable @Stable -fun LwaButtonColors(): ButtonColors = ButtonDefaults.buttonColors( +fun LwaButtonColors( + contentColor: Color = MaterialTheme.colors.primary, +): ButtonColors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.surface, - contentColor = MaterialTheme.colors.primary, + contentColor = contentColor, disabledContentColor = MaterialTheme.colors.surface.copy(alpha = ContentAlpha.disabled), -) - +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/shapes/LwaShapes.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/shapes/LwaShapes.kt index 17dfada..97b11a7 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/shapes/LwaShapes.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/theme/shapes/LwaShapes.kt @@ -1,6 +1,7 @@ package com.pixelized.desktop.lwa.ui.theme.shapes import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Shapes import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.runtime.remember @@ -9,6 +10,7 @@ import androidx.compose.ui.unit.dp @Stable data class LwaShapes( + val base: Shapes, val portrait: Shape, val panel: Shape, val settings: Shape, @@ -18,12 +20,16 @@ data class LwaShapes( @Stable @Composable fun lwaShapes( + base: Shapes = Shapes( + small = RoundedCornerShape(4.dp), + ), portrait: Shape = RoundedCornerShape(8.dp), panel: Shape = RoundedCornerShape(8.dp), settings: Shape = RoundedCornerShape(8.dp), gameMaster: Shape = RoundedCornerShape(8.dp), ): LwaShapes = remember { LwaShapes( + base = base, portrait = portrait, panel = panel, settings = settings, diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationStore.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationStore.kt index 0a155cd..ce74f7e 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationStore.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationStore.kt @@ -1,8 +1,14 @@ package com.pixelized.server.lwa.model.alteration +import com.pixelized.server.lwa.server.exception.BusinessException +import com.pixelized.server.lwa.server.exception.FileReadException +import com.pixelized.server.lwa.server.exception.FileWriteException +import com.pixelized.server.lwa.server.exception.JsonCodingException +import com.pixelized.server.lwa.server.exception.JsonConversionException import com.pixelized.shared.lwa.model.alteration.Alteration import com.pixelized.shared.lwa.model.alteration.AlterationJson import com.pixelized.shared.lwa.model.alteration.AlterationJsonFactory +import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.utils.PathProvider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -93,8 +99,9 @@ class AlterationStore( val file = alterationFile(id = json.id) // Guard case on update alteration if (create && file.exists()) { - val root = Exception("Alteration already exist, creation is impossible.") - throw BusinessException(root = root) + throw BusinessException( + message = "Alteration already exist, creation is impossible.", + ) } // Transform the json into the model. val alteration = try { @@ -102,6 +109,18 @@ class AlterationStore( } catch (exception: Exception) { throw JsonConversionException(root = exception) } + if (alteration.id.isEmpty()) { + throw BusinessException( + message = "Alteration 'id' is a mandatory field.", + code = APIResponse.ErrorCode.AlterationId, + ) + } + if (alteration.metadata.name.isEmpty()) { + throw BusinessException( + message = "Alteration 'name' is a mandatory field.", + code = APIResponse.ErrorCode.AlterationName, + ) + } // Encode the json into a string. val data = try { this.json.encodeToString(json) @@ -139,13 +158,15 @@ class AlterationStore( val file = alterationFile(id = id) // Guard case on the file existence. if (file.exists().not()) { - val root = Exception("Alteration doesn't not exist, deletion is impossible.") - throw BusinessException(root = root) + throw BusinessException( + message = "Alteration doesn't not exist, deletion is impossible.", + ) } // Guard case on the file deletion if (file.delete().not()) { - val root = Exception("Alteration file have not been deleted for unknown reason.") - throw BusinessException(root = root) + throw BusinessException( + message = "Alteration file have not been deleted for unknown reason.", + ) } // Update the data model with the deleted alteration. alterationFlow.update { alterations -> @@ -162,11 +183,4 @@ class AlterationStore( private fun alterationFile(id: String): File { return File("${pathProvider.alterationsPath()}${id}.json") } - - sealed class AlterationStoreException(root: Exception) : Exception(root) - class JsonConversionException(root: Exception) : AlterationStoreException(root) - class JsonCodingException(root: Exception) : AlterationStoreException(root) - class BusinessException(root: Exception) : AlterationStoreException(root) - class FileWriteException(root: Exception) : AlterationStoreException(root) - class FileReadException(root: Exception) : AlterationStoreException(root) } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignService.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignService.kt index 105df98..e71b8fa 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignService.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignService.kt @@ -1,5 +1,6 @@ package com.pixelized.server.lwa.model.campaign +import com.pixelized.server.lwa.server.exception.BusinessException import com.pixelized.shared.lwa.model.campaign.Campaign import com.pixelized.shared.lwa.model.campaign.CampaignJson import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory @@ -43,8 +44,7 @@ class CampaignService( ) { // Check if the character is already in the campaign. if (campaign.instances.contains(characterSheetId)) { - val root = Exception("Character with id:$characterSheetId is already in the campaign.") - throw CampaignStore.BusinessException(root = root) + throw BusinessException(message = "Character with id:$characterSheetId is already in the campaign.") } // Update the corresponding instance val characters = campaign.characters.toMutableSet().also { @@ -62,8 +62,7 @@ class CampaignService( ) { // Check if the character is already in the campaign. if (campaign.instances.contains(characterSheetId)) { - val root = Exception("Character with id:$characterSheetId is already in the campaign.") - throw CampaignStore.BusinessException(root = root) + throw BusinessException(message = "Character with id:$characterSheetId is already in the campaign.") } // Update the corresponding instance val characters = campaign.npcs.toMutableSet().also { @@ -81,8 +80,7 @@ class CampaignService( ) { // Check if the character is in the campaign. if (campaign.characters.contains(characterSheetId).not()) { - val root = Exception("Character with id:$characterSheetId is not in the party.") - throw CampaignStore.BusinessException(root = root) + throw BusinessException(message = "Character with id:$characterSheetId is not in the party.") } // Update the corresponding instance val characters = campaign.characters.toMutableSet().also { @@ -100,8 +98,7 @@ class CampaignService( ) { // Check if the character is in the campaign. if (campaign.npcs.contains(characterSheetId).not()) { - val root = Exception("Character with id:$characterSheetId is not in the npcs.") - throw CampaignStore.BusinessException(root = root) + throw BusinessException(message = "Character with id:$characterSheetId is not in the npcs.") } // Update the corresponding instance val characters = campaign.npcs.toMutableSet().also { diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignStore.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignStore.kt index 1baa868..5a499b4 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignStore.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignStore.kt @@ -1,5 +1,9 @@ package com.pixelized.server.lwa.model.campaign +import com.pixelized.server.lwa.server.exception.FileReadException +import com.pixelized.server.lwa.server.exception.FileWriteException +import com.pixelized.server.lwa.server.exception.JsonCodingException +import com.pixelized.server.lwa.server.exception.JsonConversionException import com.pixelized.shared.lwa.model.campaign.Campaign import com.pixelized.shared.lwa.model.campaign.CampaignJson import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory @@ -108,11 +112,4 @@ class CampaignStore( private fun campaignFile(): File { return File("${pathProvider.campaignPath()}campaign.json") } - - sealed class CampaignStoreException(root: Exception) : Exception(root) - class JsonConversionException(root: Exception) : CampaignStoreException(root) - class JsonCodingException(root: Exception) : CampaignStoreException(root) - class BusinessException(root: Exception) : CampaignStoreException(root) - class FileWriteException(root: Exception) : CampaignStoreException(root) - class FileReadException(root: Exception) : CampaignStoreException(root) } \ No newline at end of file diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetStore.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetStore.kt index d804870..06ff442 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetStore.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetStore.kt @@ -1,8 +1,14 @@ package com.pixelized.server.lwa.model.character +import com.pixelized.server.lwa.server.exception.BusinessException +import com.pixelized.server.lwa.server.exception.FileReadException +import com.pixelized.server.lwa.server.exception.FileWriteException +import com.pixelized.server.lwa.server.exception.JsonCodingException +import com.pixelized.server.lwa.server.exception.JsonConversionException import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJson import com.pixelized.shared.lwa.model.characterSheet.factory.CharacterSheetJsonFactory +import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.utils.PathProvider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -93,8 +99,7 @@ class CharacterSheetStore( val file = characterSheetFile(id = sheet.id) // Guard case on update alteration if (create && file.exists()) { - val root = Exception("Character already exist, creation is impossible.") - throw BusinessException(root = root) + throw BusinessException(message = "Character already exist, creation is impossible.") } // Transform the json into the model. val json = try { @@ -139,13 +144,16 @@ class CharacterSheetStore( val file = characterSheetFile(id = characterSheetId) // Guard case on the file existence. if (file.exists().not()) { - val root = Exception("Character file with id:$characterSheetId doesn't not exist.") - throw BusinessException(root = root) + throw BusinessException( + message = "Character file with id:$characterSheetId doesn't not exist.", + code = APIResponse.ErrorCode.CharacterSheetId + ) } // Guard case on the file deletion if (file.delete().not()) { - val root = Exception("Character file have not been deleted for unknown reason.") - throw BusinessException(root = root) + throw BusinessException( + message = "Character file have not been deleted for unknown reason.", + ) } // Update the data model with the deleted character. characterSheetsFlow.update { characters -> @@ -158,11 +166,4 @@ class CharacterSheetStore( private fun characterSheetFile(id: String): File { return File("${pathProvider.characterStorePath()}${id}.json") } - - sealed class CharacterSheetStoreException(root: Exception) : Exception(root) - class JsonConversionException(root: Exception) : CharacterSheetStoreException(root) - class JsonCodingException(root: Exception) : CharacterSheetStoreException(root) - class BusinessException(root: Exception) : CharacterSheetStoreException(root) - class FileWriteException(root: Exception) : CharacterSheetStoreException(root) - class FileReadException(root: Exception) : CharacterSheetStoreException(root) } \ No newline at end of file diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/tag/TagStore.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/tag/TagStore.kt index 5bbff0b..1538552 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/model/tag/TagStore.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/tag/TagStore.kt @@ -1,5 +1,8 @@ package com.pixelized.server.lwa.model.tag +import com.pixelized.server.lwa.server.exception.FileReadException +import com.pixelized.server.lwa.server.exception.FileWriteException +import com.pixelized.server.lwa.server.exception.JsonConversionException import com.pixelized.shared.lwa.model.tag.TagJson import com.pixelized.shared.lwa.utils.PathProvider import kotlinx.coroutines.CoroutineScope @@ -100,9 +103,4 @@ class TagStore( private fun characterFile() = File("${pathProvider.tagsPath()}$CHARACTER.json") private fun alterationFile() = File("${pathProvider.tagsPath()}$ALTERATION.json") - - sealed class TagStoreException(root: Exception) : Exception(root) - class JsonConversionException(root: Exception) : TagStoreException(root) - class FileWriteException(root: Exception) : TagStoreException(root) - class FileReadException(root: Exception) : TagStoreException(root) } \ No newline at end of file diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/BusinessException.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/BusinessException.kt new file mode 100644 index 0000000..54d0a7b --- /dev/null +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/BusinessException.kt @@ -0,0 +1,10 @@ +package com.pixelized.server.lwa.server.exception + +import com.pixelized.shared.lwa.protocol.rest.APIResponse + +class BusinessException( + message: String, + val code: APIResponse.ErrorCode? = null, +) : ServerException( + root = Exception(message) +) \ No newline at end of file diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/FileReadException.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/FileReadException.kt new file mode 100644 index 0000000..f3fe591 --- /dev/null +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/FileReadException.kt @@ -0,0 +1,3 @@ +package com.pixelized.server.lwa.server.exception + +class FileReadException(root: Exception) : ServerException(root) \ No newline at end of file diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/FileWriteException.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/FileWriteException.kt new file mode 100644 index 0000000..5b66d40 --- /dev/null +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/FileWriteException.kt @@ -0,0 +1,3 @@ +package com.pixelized.server.lwa.server.exception + +class FileWriteException(root: Exception) : ServerException(root) \ No newline at end of file diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/JsonCodingException.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/JsonCodingException.kt new file mode 100644 index 0000000..35f271e --- /dev/null +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/JsonCodingException.kt @@ -0,0 +1,3 @@ +package com.pixelized.server.lwa.server.exception + +class JsonCodingException(root: Exception) : ServerException(root) \ No newline at end of file diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/JsonConversionException.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/JsonConversionException.kt new file mode 100644 index 0000000..0c98955 --- /dev/null +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/JsonConversionException.kt @@ -0,0 +1,3 @@ +package com.pixelized.server.lwa.server.exception + +class JsonConversionException(root: Exception) : ServerException(root) \ No newline at end of file diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/MissingParameterException.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/MissingParameterException.kt new file mode 100644 index 0000000..9ae5498 --- /dev/null +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/MissingParameterException.kt @@ -0,0 +1,4 @@ +package com.pixelized.server.lwa.server.exception + +class MissingParameterException(name: String) : + ServerException(root = Exception("Missing '$name' parameter.")) \ No newline at end of file diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/ServerException.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/ServerException.kt new file mode 100644 index 0000000..4b3fc3a --- /dev/null +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/exception/ServerException.kt @@ -0,0 +1,3 @@ +package com.pixelized.server.lwa.server.exception + +sealed class ServerException(root: Exception) : Exception(root) \ No newline at end of file diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/DELETE_Alteration.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/DELETE_Alteration.kt index 557ce43..837240e 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/DELETE_Alteration.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/DELETE_Alteration.kt @@ -1,8 +1,8 @@ package com.pixelized.server.lwa.server.rest.alteration import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.MissingParameterException import com.pixelized.server.lwa.utils.extentions.alterationId +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.protocol.websocket.ApiSynchronisation import io.ktor.server.response.respond @@ -27,19 +27,9 @@ fun Engine.deleteAlteration(): suspend RoutingContext.() -> Unit { alterationId = alterationId, ), ) - } catch (exception: MissingParameterException) { - call.respond( - message = APIResponse.error( - status = exception.errorCode, - message = exception.message ?: "?", - ) - ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception, ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Alteration.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Alteration.kt index 6a70c41..30f89cd 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Alteration.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Alteration.kt @@ -2,6 +2,7 @@ package com.pixelized.server.lwa.server.rest.alteration import com.pixelized.server.lwa.server.Engine import com.pixelized.server.lwa.utils.extentions.alterationId +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import io.ktor.server.response.respond import io.ktor.server.routing.RoutingContext @@ -22,11 +23,8 @@ fun Engine.getAlteration(): suspend RoutingContext.() -> Unit { ) ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception, ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_AlterationTags.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_AlterationTags.kt index 07edd33..fe86478 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_AlterationTags.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_AlterationTags.kt @@ -1,6 +1,7 @@ package com.pixelized.server.lwa.server.rest.alteration import com.pixelized.server.lwa.server.Engine +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import io.ktor.server.response.respond import io.ktor.server.routing.RoutingContext @@ -14,11 +15,8 @@ fun Engine.getAlterationTags(): suspend RoutingContext.() -> Unit { ), ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception, ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Alterations.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Alterations.kt index 6022814..728f750 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Alterations.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/GET_Alterations.kt @@ -1,6 +1,7 @@ package com.pixelized.server.lwa.server.rest.alteration import com.pixelized.server.lwa.server.Engine +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import io.ktor.server.response.respond import io.ktor.server.routing.RoutingContext @@ -14,11 +15,8 @@ fun Engine.getAlterations(): suspend RoutingContext.() -> Unit { ) ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception, ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/PUT_Alateration.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/PUT_Alteration.kt similarity index 68% rename from server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/PUT_Alateration.kt rename to server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/PUT_Alteration.kt index da2e233..ee1a824 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/PUT_Alateration.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/alteration/PUT_Alteration.kt @@ -1,8 +1,8 @@ package com.pixelized.server.lwa.server.rest.alteration import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.MissingParameterException import com.pixelized.server.lwa.utils.extentions.create +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.model.alteration.AlterationJson import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.protocol.websocket.ApiSynchronisation @@ -29,19 +29,9 @@ fun Engine.putAlteration(): suspend RoutingContext.() -> Unit { alterationId = form.id, ), ) - } catch (exception: MissingParameterException) { - call.respond( - message = APIResponse.error( - status = exception.errorCode, - message = exception.message ?: "?", - ) - ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception, ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/DELETE_Campaign_Character.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/DELETE_Campaign_Character.kt index a2e1a2d..fdec0e7 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/DELETE_Campaign_Character.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/DELETE_Campaign_Character.kt @@ -1,8 +1,8 @@ package com.pixelized.server.lwa.server.rest.campaign import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.MissingParameterException import com.pixelized.server.lwa.utils.extentions.characterSheetId +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent import io.ktor.server.response.respond @@ -27,19 +27,9 @@ fun Engine.removeCampaignCharacter(): suspend RoutingContext.() -> Unit { characterSheetId = characterSheetId, ) ) - } catch (exception: MissingParameterException) { - call.respond( - message = APIResponse.error( - status = exception.errorCode, - message = exception.message ?: "?", - ) - ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/DELETE_Campaign_Npc.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/DELETE_Campaign_Npc.kt index 336e562..7d4f911 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/DELETE_Campaign_Npc.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/DELETE_Campaign_Npc.kt @@ -1,8 +1,8 @@ package com.pixelized.server.lwa.server.rest.campaign import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.MissingParameterException import com.pixelized.server.lwa.utils.extentions.characterSheetId +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent import io.ktor.server.response.respond @@ -27,19 +27,9 @@ fun Engine.removeCampaignNpc(): suspend RoutingContext.() -> Unit { characterSheetId = characterSheetId, ) ) - } catch (exception: MissingParameterException) { - call.respond( - message = APIResponse.error( - status = exception.errorCode, - message = exception.message ?: "?", - ) - ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/GET_Campaign.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/GET_Campaign.kt index f345e13..72a4ad7 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/GET_Campaign.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/GET_Campaign.kt @@ -1,6 +1,7 @@ package com.pixelized.server.lwa.server.rest.campaign import com.pixelized.server.lwa.server.Engine +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import io.ktor.server.response.respond import io.ktor.server.routing.RoutingContext @@ -14,11 +15,8 @@ fun Engine.getCampaign(): suspend RoutingContext.() -> Unit { ), ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_Campaign_Character.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_Campaign_Character.kt index 80f012a..a301e2a 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_Campaign_Character.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_Campaign_Character.kt @@ -1,8 +1,8 @@ package com.pixelized.server.lwa.server.rest.campaign import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.MissingParameterException import com.pixelized.server.lwa.utils.extentions.characterSheetId +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent import io.ktor.server.response.respond @@ -27,19 +27,9 @@ fun Engine.putCampaignCharacter(): suspend RoutingContext.() -> Unit { characterSheetId = characterSheetId, ) ) - } catch (exception: MissingParameterException) { - call.respond( - message = APIResponse.error( - status = exception.errorCode, - message = exception.message ?: "?", - ) - ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_Campaign_Npc.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_Campaign_Npc.kt index 07dfd5d..c0dbcad 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_Campaign_Npc.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_Campaign_Npc.kt @@ -1,8 +1,8 @@ package com.pixelized.server.lwa.server.rest.campaign import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.MissingParameterException import com.pixelized.server.lwa.utils.extentions.characterSheetId +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent import io.ktor.server.response.respond @@ -27,19 +27,9 @@ fun Engine.putCampaignNpc(): suspend RoutingContext.() -> Unit { characterSheetId = characterSheetId, ) ) - } catch (exception: MissingParameterException) { - call.respond( - message = APIResponse.error( - status = exception.errorCode, - message = exception.message ?: "?", - ) - ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_Campaign_Scene_Name.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_Campaign_Scene_Name.kt index e2e37bb..2da6626 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_Campaign_Scene_Name.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/campaign/PUT_Campaign_Scene_Name.kt @@ -1,7 +1,7 @@ package com.pixelized.server.lwa.server.rest.campaign import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.MissingParameterException +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.model.campaign.CampaignJsonV2 import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent @@ -30,19 +30,9 @@ fun Engine.putCampaignScene(): suspend RoutingContext.() -> Unit { name = scene.name, ) ) - } catch (exception: MissingParameterException) { - call.respond( - message = APIResponse.error( - status = exception.errorCode, - message = exception.message ?: "?", - ) - ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/DELETE_Character.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/DELETE_Character.kt index 42629b4..89dc7c7 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/DELETE_Character.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/DELETE_Character.kt @@ -1,8 +1,8 @@ package com.pixelized.server.lwa.server.rest.character import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.MissingParameterException import com.pixelized.server.lwa.utils.extentions.characterSheetId +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.protocol.websocket.ApiSynchronisation import io.ktor.server.response.respond @@ -27,19 +27,9 @@ fun Engine.deleteCharacter(): suspend RoutingContext.() -> Unit { characterSheetId = characterSheetId, ), ) - } catch (exception: MissingParameterException) { - call.respond( - message = APIResponse.error( - status = exception.errorCode, - message = exception.message ?: "?", - ) - ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/GET_Character.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/GET_Character.kt index 200bba4..2e70f75 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/GET_Character.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/GET_Character.kt @@ -1,8 +1,8 @@ package com.pixelized.server.lwa.server.rest.character import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.MissingParameterException import com.pixelized.server.lwa.utils.extentions.characterSheetId +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import io.ktor.server.response.respond import io.ktor.server.routing.RoutingContext @@ -22,19 +22,9 @@ fun Engine.getCharacter(): suspend RoutingContext.() -> Unit { data = characterSheet, ), ) - } catch (exception: MissingParameterException) { - call.respond( - message = APIResponse.error( - status = exception.errorCode, - message = exception.message ?: "?", - ) - ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/GET_CharacterTags.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/GET_CharacterTags.kt index 52b5c3f..8f1b876 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/GET_CharacterTags.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/GET_CharacterTags.kt @@ -1,6 +1,7 @@ package com.pixelized.server.lwa.server.rest.character import com.pixelized.server.lwa.server.Engine +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import io.ktor.server.response.respond import io.ktor.server.routing.RoutingContext @@ -14,11 +15,8 @@ fun Engine.getCharacterTags(): suspend RoutingContext.() -> Unit { ), ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/GET_Characters.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/GET_Characters.kt index 354b4ee..4f62a70 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/GET_Characters.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/GET_Characters.kt @@ -1,6 +1,7 @@ package com.pixelized.server.lwa.server.rest.character import com.pixelized.server.lwa.server.Engine +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import io.ktor.server.response.respond import io.ktor.server.routing.RoutingContext @@ -14,11 +15,8 @@ fun Engine.getCharacters(): suspend RoutingContext.() -> Unit { ), ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character.kt index dbc0f21..58225a3 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character.kt @@ -1,8 +1,8 @@ package com.pixelized.server.lwa.server.rest.character import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.MissingParameterException import com.pixelized.server.lwa.utils.extentions.create +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetJson import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.protocol.websocket.ApiSynchronisation @@ -28,19 +28,9 @@ fun Engine.putCharacter(): suspend RoutingContext.() -> Unit { characterSheetId = form.id, ), ) - } catch (exception: MissingParameterException) { - call.respond( - message = APIResponse.error( - status = exception.errorCode, - message = exception.message ?: "?", - ) - ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Alteration.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Alteration.kt index a6fd05b..bb77670 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Alteration.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Alteration.kt @@ -1,10 +1,10 @@ package com.pixelized.server.lwa.server.rest.character import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.MissingParameterException import com.pixelized.server.lwa.utils.extentions.active import com.pixelized.server.lwa.utils.extentions.alterationId import com.pixelized.server.lwa.utils.extentions.characterSheetId +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.protocol.websocket.CharacterSheetEvent import io.ktor.server.response.respond @@ -35,19 +35,9 @@ fun Engine.putCharacterAlteration(): suspend RoutingContext.() -> Unit { active = active, ) ) - } catch (exception: MissingParameterException) { - call.respond( - message = APIResponse.error( - status = exception.errorCode, - message = exception.message ?: "?", - ) - ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Damage.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Damage.kt index 427c4be..3687b79 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Damage.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Damage.kt @@ -1,8 +1,9 @@ package com.pixelized.server.lwa.server.rest.character import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.MissingParameterException +import com.pixelized.server.lwa.server.exception.MissingParameterException import com.pixelized.server.lwa.utils.extentions.characterSheetId +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.protocol.websocket.CharacterSheetEvent import io.ktor.server.response.respond @@ -14,10 +15,7 @@ fun Engine.putCharacterDamage(): suspend RoutingContext.() -> Unit { // get the query parameter val characterSheetId = call.queryParameters.characterSheetId val damage = call.queryParameters["damage"]?.toIntOrNull() - ?: throw MissingParameterException( - name = "damage", - errorCode = APIResponse.MISSING_DAMAGE - ) + ?: throw MissingParameterException(name = "damage") // fetch the character sheet val characterSheet = characterService.character(characterSheetId) ?: error("CharacterSheet with id:$characterSheetId not found.") @@ -38,19 +36,9 @@ fun Engine.putCharacterDamage(): suspend RoutingContext.() -> Unit { damage = damage, ) ) - } catch (exception: MissingParameterException) { - call.respond( - message = APIResponse.error( - status = exception.errorCode, - message = exception.message ?: "?", - ) - ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Diminished.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Diminished.kt index b695e26..666eb99 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Diminished.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Diminished.kt @@ -1,8 +1,9 @@ package com.pixelized.server.lwa.server.rest.character import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.MissingParameterException +import com.pixelized.server.lwa.server.exception.MissingParameterException import com.pixelized.server.lwa.utils.extentions.characterSheetId +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.protocol.websocket.CharacterSheetEvent import io.ktor.server.response.respond @@ -14,10 +15,7 @@ fun Engine.putCharacterDiminished(): suspend RoutingContext.() -> Unit { // get the query parameter val characterSheetId = call.queryParameters.characterSheetId val diminished = call.queryParameters["diminished"]?.toIntOrNull() - ?: throw MissingParameterException( - name = "diminished", - errorCode = APIResponse.MISSING_DIMINISHED - ) + ?: throw MissingParameterException(name = "diminished") // Update the character damage characterService.updateDiminished( characterSheetId = characterSheetId, @@ -34,19 +32,9 @@ fun Engine.putCharacterDiminished(): suspend RoutingContext.() -> Unit { diminished = diminished, ) ) - } catch (exception: MissingParameterException) { - call.respond( - message = APIResponse.error( - status = exception.errorCode, - message = exception.message ?: "?", - ) - ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Fatigue.kt b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Fatigue.kt index 6f565cc..1709485 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Fatigue.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/server/rest/character/PUT_Character_Fatigue.kt @@ -1,8 +1,9 @@ package com.pixelized.server.lwa.server.rest.character import com.pixelized.server.lwa.server.Engine -import com.pixelized.server.lwa.utils.extentions.MissingParameterException +import com.pixelized.server.lwa.server.exception.MissingParameterException import com.pixelized.server.lwa.utils.extentions.characterSheetId +import com.pixelized.server.lwa.utils.extentions.exception import com.pixelized.shared.lwa.protocol.rest.APIResponse import com.pixelized.shared.lwa.protocol.websocket.CharacterSheetEvent import io.ktor.server.response.respond @@ -14,10 +15,7 @@ fun Engine.putCharacterFatigue(): suspend RoutingContext.() -> Unit { // get the query parameter val characterSheetId = call.queryParameters.characterSheetId val fatigue = call.queryParameters["fatigue"]?.toIntOrNull() - ?: throw MissingParameterException( - name = "fatigue", - errorCode = APIResponse.MISSING_FATIGUE - ) + ?: throw MissingParameterException(name = "fatigue") // fetch the character sheet val characterSheet = characterService.character(characterSheetId) ?: error("CharacterSheet with id:$characterSheetId not found.") @@ -38,19 +36,9 @@ fun Engine.putCharacterFatigue(): suspend RoutingContext.() -> Unit { fatigue = fatigue, ) ) - } catch (exception: MissingParameterException) { - call.respond( - message = APIResponse.error( - status = exception.errorCode, - message = exception.message ?: "?", - ) - ) } catch (exception: Exception) { - call.respond( - message = APIResponse.error( - status = APIResponse.GENERIC, - message = exception.message ?: "?", - ) + call.exception( + exception = exception ) } } diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/utils/extentions/ParametersExt.kt b/server/src/main/kotlin/com/pixelized/server/lwa/utils/extentions/ParametersExt.kt index b719213..1559b91 100644 --- a/server/src/main/kotlin/com/pixelized/server/lwa/utils/extentions/ParametersExt.kt +++ b/server/src/main/kotlin/com/pixelized/server/lwa/utils/extentions/ParametersExt.kt @@ -1,41 +1,24 @@ package com.pixelized.server.lwa.utils.extentions -import com.pixelized.shared.lwa.protocol.rest.APIResponse +import com.pixelized.server.lwa.server.exception.MissingParameterException import io.ktor.http.Parameters val Parameters.characterSheetId get() = "characterSheetId".let { param -> - this[param] ?: throw MissingParameterException( - name = param, - errorCode = APIResponse.MISSING_CHARACTER_SHEET_ID, - ) + this[param] ?: throw MissingParameterException(name = param) } val Parameters.alterationId get() = "alterationId".let { param -> - this[param] ?: throw MissingParameterException( - name = param, - errorCode = APIResponse.MISSING_ALTERATION_ID, - ) + this[param] ?: throw MissingParameterException(name = param) } val Parameters.create get() = "create".let { param -> - this[param]?.toBooleanStrictOrNull() ?: throw MissingParameterException( - name = param, - errorCode = APIResponse.MISSING_CREATE - ) + this[param]?.toBooleanStrictOrNull() ?: throw MissingParameterException(name = param) } val Parameters.active get() = "active".let { param -> - this[param]?.toBooleanStrictOrNull() ?: throw MissingParameterException( - name = param, - errorCode = APIResponse.MISSING_ACTIVE - ) + this[param]?.toBooleanStrictOrNull() ?: throw MissingParameterException(name = param) } - -class MissingParameterException( - name: String, - val errorCode: Int, -) : Exception("Missing $name parameter.") diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/utils/extentions/RoutingCallExt+exception.kt b/server/src/main/kotlin/com/pixelized/server/lwa/utils/extentions/RoutingCallExt+exception.kt new file mode 100644 index 0000000..45688b6 --- /dev/null +++ b/server/src/main/kotlin/com/pixelized/server/lwa/utils/extentions/RoutingCallExt+exception.kt @@ -0,0 +1,40 @@ +package com.pixelized.server.lwa.utils.extentions + +import com.pixelized.server.lwa.server.exception.BusinessException +import com.pixelized.server.lwa.server.exception.MissingParameterException +import com.pixelized.shared.lwa.protocol.rest.APIResponse +import io.ktor.server.response.respond +import io.ktor.server.routing.RoutingCall + + +suspend inline fun RoutingCall.exception(exception: T) { + when (exception) { + + is MissingParameterException -> { + respond( + message = APIResponse.error( + status = APIResponse.BAD_REQUEST, + message = exception.message ?: "?", + code = APIResponse.ErrorCode.AlterationName, + ) + ) + } + + is BusinessException -> { + respond( + message = APIResponse.error( + status = APIResponse.INTERNAL_ERROR, + message = exception.message ?: "?", + code = exception.code, + ) + ) + } + + else -> respond( + message = APIResponse.error( + status = APIResponse.INTERNAL_ERROR, + message = exception.message ?: "?", + ) + ) + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/rest/APIResponse.kt b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/rest/APIResponse.kt index f5893b4..a6d2d9e 100644 --- a/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/rest/APIResponse.kt +++ b/shared/src/commonMain/kotlin/com/pixelized/shared/lwa/protocol/rest/APIResponse.kt @@ -7,46 +7,52 @@ data class APIResponse( val success: Boolean, val status: Int, val message: String?, + val code: ErrorCode?, val data: T?, ) { + @Serializable + enum class ErrorCode { + AlterationId, + AlterationName, + CharacterSheetId, + } + companion object { - const val SUCCESS = 100 - - const val GENERIC = 600 - - const val MISSING_PARAMETER = 700 - const val MISSING_CHARACTER_SHEET_ID = MISSING_PARAMETER + 1 - const val MISSING_ALTERATION_ID = MISSING_PARAMETER + 2 - const val MISSING_CREATE = MISSING_PARAMETER + 3 - const val MISSING_ACTIVE = MISSING_PARAMETER + 4 - const val MISSING_DAMAGE = MISSING_PARAMETER + 5 - const val MISSING_FATIGUE = MISSING_PARAMETER + 6 - const val MISSING_DIMINISHED = MISSING_PARAMETER + 7 - fun error( status: Int, + code: ErrorCode? = null, message: String?, ) = APIResponse( success = false, status = status, + code = code, message = message, data = null, ) - fun success() = APIResponse( + fun success( + status: Int = OK, + ) = APIResponse( success = true, - status = SUCCESS, + status = status, + code = null, message = null, data = null, ) inline fun success( + status: Int = OK, data: T? = null, ) = APIResponse( success = true, - status = SUCCESS, + status = status, + code = null, message = null, data = data, ) + + const val OK = 200 + const val BAD_REQUEST = 400 + const val INTERNAL_ERROR = 500 } } \ No newline at end of file