Add save as feature
This commit is contained in:
parent
e1fdb10793
commit
50c34c8520
10 changed files with 300 additions and 12 deletions
|
|
@ -1,12 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path android:pathData="M0 0h24v24H0z" />
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M17 3H5c-1.11 0-2 0.9-2 2v14c0 1.1 0.89 2 2 2h14c1.1 0 2-0.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z" />
|
||||
</vector>
|
||||
android:fillColor="#5f6368"
|
||||
android:pathData="M840,280v480q0,33 -23.5,56.5T760,840L200,840q-33,0 -56.5,-23.5T120,760v-560q0,-33 23.5,-56.5T200,120h480l160,160ZM760,314L646,200L200,200v560h560v-446ZM480,720q50,0 85,-35t35,-85q0,-50 -35,-85t-85,-35q-50,0 -85,35t-35,85q0,50 35,85t85,35ZM240,400h360v-160L240,240v160ZM200,314v446,-560 114Z" />
|
||||
</vector>
|
||||
|
|
|
|||
|
|
@ -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:pathData="M200,840q-33,0 -56.5,-23.5T120,760v-560q0,-33 23.5,-56.5T200,120h480l160,160v212q-19,-8 -39.5,-10.5t-40.5,0.5v-169L647,200L200,200v560h240v80L200,840ZM200,200v560,-560ZM520,920v-123l221,-220q9,-9 20,-13t22,-4q12,0 23,4.5t20,13.5l37,37q8,9 12.5,20t4.5,22q0,11 -4,22.5T863,700L643,920L520,920ZM820,657 L783,620 820,657ZM580,860h38l121,-122 -18,-19 -19,-18 -122,121v38ZM721,719 L702,701 739,738 721,719ZM240,400h360v-160L240,240v160ZM480,720h4l116,-115v-5q0,-50 -35,-85t-85,-35q-50,0 -85,35t-35,85q0,50 35,85t85,35Z"
|
||||
android:fillColor="#5f6368"/>
|
||||
</vector>
|
||||
|
|
@ -123,6 +123,9 @@
|
|||
<string name="character_sheet_edit__add_roll_action">Ajouter une action de lancer</string>
|
||||
<string name="character_sheet_edit__delete__label">Supprimer</string>
|
||||
<string name="character_sheet_edit__occupation__label">Compétence d'occupation</string>
|
||||
<string name="character_sheet_edit__copy__title">Enregistrer une copie</string>
|
||||
<string name="character_sheet_edit__copy__label">Nouvel identifiant</string>
|
||||
<string name="character_sheet_edit__copy__error">Identifiant déjà utilisé.</string>
|
||||
|
||||
<string name="character_sheet__level">niv : %1$d</string>
|
||||
<string name="character_sheet__diminished__label">État diminué</string>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.pixelized.desktop.lwa.repository.characterSheet
|
||||
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetPreview
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
|
||||
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheetPreview
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
|
|
@ -69,4 +69,10 @@ class CharacterSheetRepository(
|
|||
) {
|
||||
store.deleteCharacterSheet(characterId = characterId)
|
||||
}
|
||||
|
||||
fun checkCharacterSheetIdValidity(
|
||||
characterSheetId: String,
|
||||
): Boolean {
|
||||
return store.previewFlow.value.none { it.characterSheetId == characterSheetId }
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,8 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
|
||||
@Stable
|
||||
data class LwaTextFieldUio(
|
||||
val enable: Boolean,
|
||||
val enable: Boolean = true,
|
||||
val isError: StateFlow<Boolean>,
|
||||
val labelFlow: StateFlow<String?>,
|
||||
val valueFlow: StateFlow<String>,
|
||||
val placeHolderFlow: StateFlow<String?>,
|
||||
|
|
@ -45,6 +46,7 @@ fun LwaTextField(
|
|||
val label = field.labelFlow.collectAsState()
|
||||
val value = field.valueFlow.collectAsState()
|
||||
val placeHolder = field.placeHolderFlow.collectAsState()
|
||||
val isError = field.isError.collectAsState()
|
||||
|
||||
TextField(
|
||||
modifier = localModifier.then(other = modifier),
|
||||
|
|
@ -63,6 +65,7 @@ fun LwaTextField(
|
|||
)
|
||||
}
|
||||
},
|
||||
isError = isError.value,
|
||||
label = label.value?.let {
|
||||
{
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,187 @@
|
|||
package com.pixelized.desktop.lwa.ui.screen.characterSheet.copy
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
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.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
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.collectAsState
|
||||
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.input.key.Key
|
||||
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.ui.composable.textfield.LwaTextField
|
||||
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
|
||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__copy__error
|
||||
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 CharacterSheetCopyDialogUio(
|
||||
val label: String,
|
||||
val value: LwaTextFieldUio,
|
||||
val validate: () -> Boolean
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun CharacterSheetCopyDialog(
|
||||
dialog: State<CharacterSheetCopyDialogUio?>,
|
||||
onConfirm: (CharacterSheetCopyDialogUio) -> 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 -> {
|
||||
val focusRequester = remember {
|
||||
FocusRequester()
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
Dialog(
|
||||
dialog = it,
|
||||
focusRequester = focusRequester,
|
||||
onConfirm = onConfirm,
|
||||
onDismissRequest = onDismissRequest,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Dialog(
|
||||
dialog: CharacterSheetCopyDialogUio,
|
||||
focusRequester: FocusRequester = remember { FocusRequester() },
|
||||
onConfirm: (CharacterSheetCopyDialogUio) -> Unit,
|
||||
onDismissRequest: () -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
onClick = onDismissRequest,
|
||||
)
|
||||
.onPreviewKeyEvent {
|
||||
when {
|
||||
it.key == Key.Escape -> {
|
||||
onDismissRequest()
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
.fillMaxSize()
|
||||
.padding(all = 32.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
DecoratedBox {
|
||||
Surface {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(space = 8.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 16.dp, start = 24.dp, end = 24.dp),
|
||||
style = MaterialTheme.typography.caption,
|
||||
text = dialog.label,
|
||||
)
|
||||
|
||||
LwaTextField(
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequester = focusRequester)
|
||||
.width(512.dp),
|
||||
field = dialog.value,
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = dialog.value.isError.collectAsState().value,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
Text(
|
||||
style = MaterialTheme.lwa.typography.base.caption,
|
||||
color = MaterialTheme.lwa.colorScheme.base.error,
|
||||
text = stringResource(Res.string.character_sheet_edit__copy__error)
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 4.dp)
|
||||
.padding(horizontal = 16.dp)
|
||||
.align(alignment = Alignment.End),
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
space = 4.dp,
|
||||
alignment = Alignment.End
|
||||
)
|
||||
) {
|
||||
TextButton(
|
||||
onClick = onDismissRequest,
|
||||
) {
|
||||
Text(
|
||||
color = MaterialTheme.colors.primary.copy(alpha = .7f),
|
||||
text = stringResource(Res.string.dialog__cancel_action)
|
||||
)
|
||||
}
|
||||
TextButton(
|
||||
onClick = { if(dialog.validate()) {onConfirm(dialog)} },
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(Res.string.dialog__confirm_action)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ 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.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
|
|
@ -31,6 +32,7 @@ import com.pixelized.desktop.lwa.LocalWindowController
|
|||
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.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
|
||||
|
|
@ -55,6 +57,7 @@ import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__sa
|
|||
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_save_24dp
|
||||
import lwacharactersheet.composeapp.generated.resources.ic_save_as_24dp
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
|
|
@ -121,6 +124,11 @@ fun CharacterSheetEditPage(
|
|||
viewModel.onNewAction()
|
||||
}
|
||||
},
|
||||
onCopy = {
|
||||
scope.launch {
|
||||
viewModel.showCopyCharacterSheetDialog()
|
||||
}
|
||||
},
|
||||
onSave = {
|
||||
scope.launch {
|
||||
viewModel.save()
|
||||
|
|
@ -131,6 +139,22 @@ fun CharacterSheetEditPage(
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
CharacterSheetCopyDialog(
|
||||
dialog = viewModel.copyCharacterSheetDialog,
|
||||
onConfirm = { dialog ->
|
||||
scope.launch {
|
||||
val characterSheetId = dialog.value.valueFlow.value
|
||||
viewModel.saveAs(characterSheetId = characterSheetId)
|
||||
if (screen.popBackStack().not()) {
|
||||
windowController.hideWindow(window = window)
|
||||
}
|
||||
}
|
||||
},
|
||||
onDismissRequest = {
|
||||
viewModel.hideCopyCharacterSheetDialog()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -141,6 +165,7 @@ fun CharacterSheetEdit(
|
|||
onNewSpecialSkill: () -> Unit,
|
||||
onNewMagicSkill: () -> Unit,
|
||||
onNewAction: () -> Unit,
|
||||
onCopy: () -> Unit,
|
||||
onSave: () -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
|
|
@ -155,6 +180,15 @@ fun CharacterSheetEdit(
|
|||
)
|
||||
},
|
||||
actions = {
|
||||
IconButton(
|
||||
onClick = onCopy,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(Res.drawable.ic_save_as_24dp),
|
||||
tint = MaterialTheme.colors.primary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = onSave,
|
||||
) {
|
||||
|
|
@ -200,7 +234,7 @@ fun CharacterSheetEdit(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
LevelUpField(
|
||||
modifier = Modifier.weight(1f),
|
||||
modifier = Modifier.weight(1f).offset(y = 4.dp),
|
||||
field = form.levelUp,
|
||||
)
|
||||
SimpleField(
|
||||
|
|
|
|||
|
|
@ -5,13 +5,18 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||
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.edit.common.SkillFieldFactory
|
||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.edit.composable.ActionFieldUio
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import lwacharactersheet.composeapp.generated.resources.Res
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__action_label
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__actions__name_label
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__copy__label
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__copy__title
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__magic_title
|
||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__skills__special_title
|
||||
import org.jetbrains.compose.resources.getString
|
||||
|
|
@ -26,6 +31,9 @@ class CharacterSheetEditViewModel(
|
|||
|
||||
private val argument = CharacterSheetEditDestination.Argument(savedStateHandle)
|
||||
|
||||
private val copyDialog = mutableStateOf<CharacterSheetCopyDialogUio?>(null)
|
||||
val copyCharacterSheetDialog: State<CharacterSheetCopyDialogUio?> = copyDialog
|
||||
|
||||
private val _characterSheet = mutableStateOf(
|
||||
runBlocking {
|
||||
sheetFactory.convertToUio(
|
||||
|
|
@ -36,6 +44,34 @@ class CharacterSheetEditViewModel(
|
|||
)
|
||||
val characterSheet: State<CharacterSheetEditPageUio> get() = _characterSheet
|
||||
|
||||
// TODO
|
||||
suspend fun showCopyCharacterSheetDialog() {
|
||||
val characterSheetId = MutableStateFlow(argument.id ?: "")
|
||||
val error = MutableStateFlow(false)
|
||||
copyDialog.value = CharacterSheetCopyDialogUio(
|
||||
label = getString(Res.string.character_sheet_edit__copy__title),
|
||||
value = LwaTextFieldUio(
|
||||
labelFlow = MutableStateFlow(getString(Res.string.character_sheet_edit__copy__label)),
|
||||
isError = error,
|
||||
valueFlow = characterSheetId,
|
||||
placeHolderFlow = MutableStateFlow(null),
|
||||
onValueChange = { characterSheetId.value = it },
|
||||
),
|
||||
validate = {
|
||||
characterSheetRepository.checkCharacterSheetIdValidity(
|
||||
characterSheetId = characterSheetId.value
|
||||
).also {
|
||||
error.value = it.not()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// TODO
|
||||
fun hideCopyCharacterSheetDialog() {
|
||||
copyDialog.value = null
|
||||
}
|
||||
|
||||
suspend fun onNewSpecialSkill() {
|
||||
val id = UUID.randomUUID().toString()
|
||||
val skill = skillFactory.createSkill(
|
||||
|
|
@ -115,4 +151,16 @@ class CharacterSheetEditViewModel(
|
|||
characterSheet = updatedSheet,
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun saveAs(
|
||||
characterSheetId: String,
|
||||
) {
|
||||
val updatedSheet = sheetFactory.updateCharacterSheet(
|
||||
currentSheet = characterSheetRepository.characterDetail(characterSheetId = _characterSheet.value.id.value),
|
||||
editedSheet = _characterSheet.value,
|
||||
)
|
||||
characterSheetRepository.updateCharacter(
|
||||
characterSheet = updatedSheet.copy(id = characterSheetId),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ fun LevelUpField(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
style = MaterialTheme.lwa.typography.base.caption,
|
||||
style = MaterialTheme.typography.body1,
|
||||
text = field.label,
|
||||
)
|
||||
Checkbox(
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class GameMasterViewModel(
|
|||
enable = true,
|
||||
labelFlow = MutableStateFlow(runBlocking { getString(Res.string.game_master__character__filter) }),
|
||||
valueFlow = _filter,
|
||||
isError = MutableStateFlow(false),
|
||||
placeHolderFlow = MutableStateFlow(null),
|
||||
onValueChange = { _filter.value = it },
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue