Add confirmation dialog for GM actions.
This commit is contained in:
parent
9be8f2b209
commit
651d05a7c4
13 changed files with 315 additions and 88 deletions
|
|
@ -329,5 +329,17 @@
|
||||||
<string name="game_master__item__edit_consumable">Consommable</string>
|
<string name="game_master__item__edit_consumable">Consommable</string>
|
||||||
<string name="game_master__item__edit_add_alteration">Ajouter une alteration</string>
|
<string name="game_master__item__edit_add_alteration">Ajouter une alteration</string>
|
||||||
<string name="game_master__character_edit__title">Édition de personnage</string>
|
<string name="game_master__character_edit__title">Édition de personnage</string>
|
||||||
|
<string name="game_master__actions__on_server_sync__title">Synchronisation du serveur</string>
|
||||||
|
<string name="game_master__actions__on_server_sync__description">Demander au serveur d'invalider son cache</string>
|
||||||
|
<string name="game_master__actions__party_heal__title">Soigner les personnages joueurs</string>
|
||||||
|
<string name="game_master__actions__party_heal__description">Cette action réinitialisera les points de vie, de pouvoir et d'état diminué de chaque personnage joueur présent dans le groupe.</string>
|
||||||
|
<string name="game_master__actions__hide_player__title">Cacher le groupe de personnages joueur</string>
|
||||||
|
<string name="game_master__actions__hide_player__description">Cacher le panneau latéral gauche pour tous les joueurs.</string>
|
||||||
|
<string name="game_master__actions__show_player__title">Montrer les personnages joueurs</string>
|
||||||
|
<string name="game_master__actions__show_player__description">Montrer le panneau latéral gauche pour tous les joueurs.</string>
|
||||||
|
<string name="game_master__actions__hide_npc__title">Cacher le groupe de npcs</string>
|
||||||
|
<string name="game_master__actions__hide_npc__description">Cacher le panneau latéral droit pour tous les joueurs.</string>
|
||||||
|
<string name="game_master__actions__show_npc__title">Montrer le groupe de npcs</string>
|
||||||
|
<string name="game_master__actions__show_npc__description">Montrer le panneau latéral droit pour tous les joueurs.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -47,6 +47,7 @@ import com.pixelized.desktop.lwa.ui.screen.campaign.text.TextMessageFactory
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.CampaignToolbarViewModel
|
import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.CampaignToolbarViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.links.ResourcesViewModel
|
import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.links.ResourcesViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.gamemaster.GameMasterViewModel
|
import com.pixelized.desktop.lwa.ui.screen.gamemaster.GameMasterViewModel
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.gamemaster.action.GMActionUseCase
|
||||||
import com.pixelized.desktop.lwa.ui.screen.gamemaster.action.GMActionViewModel
|
import com.pixelized.desktop.lwa.ui.screen.gamemaster.action.GMActionViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.screen.gamemaster.alteration.edit.GMAlterationEditFactory
|
import com.pixelized.desktop.lwa.ui.screen.gamemaster.alteration.edit.GMAlterationEditFactory
|
||||||
import com.pixelized.desktop.lwa.ui.screen.gamemaster.alteration.edit.GMAlterationEditViewModel
|
import com.pixelized.desktop.lwa.ui.screen.gamemaster.alteration.edit.GMAlterationEditViewModel
|
||||||
|
|
@ -198,4 +199,5 @@ val viewModelDependencies
|
||||||
val useCaseDependencies
|
val useCaseDependencies
|
||||||
get() = module {
|
get() = module {
|
||||||
factoryOf(::SettingsUseCase)
|
factoryOf(::SettingsUseCase)
|
||||||
|
factoryOf(::GMActionUseCase)
|
||||||
}
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Surface
|
import androidx.compose.material.Surface
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
|
@ -18,6 +19,8 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import com.pixelized.desktop.lwa.LocalBlurController
|
||||||
|
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
|
||||||
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
|
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
|
||||||
import com.pixelized.desktop.lwa.utils.extention.onPreviewEscape
|
import com.pixelized.desktop.lwa.utils.extention.onPreviewEscape
|
||||||
|
|
||||||
|
|
@ -29,6 +32,7 @@ object LwaDialogDefault {
|
||||||
@Composable
|
@Composable
|
||||||
fun <T> LwaDialog(
|
fun <T> LwaDialog(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
blur: BlurContentController? = LocalBlurController.current,
|
||||||
paddings: PaddingValues = LwaDialogDefault.paddings,
|
paddings: PaddingValues = LwaDialogDefault.paddings,
|
||||||
color: Color = MaterialTheme.colors.surface,
|
color: Color = MaterialTheme.colors.surface,
|
||||||
state: State<T?>,
|
state: State<T?>,
|
||||||
|
|
@ -37,6 +41,16 @@ fun <T> LwaDialog(
|
||||||
content: @Composable BoxScope.(T) -> Unit,
|
content: @Composable BoxScope.(T) -> Unit,
|
||||||
) {
|
) {
|
||||||
state.value?.let { dialog ->
|
state.value?.let { dialog ->
|
||||||
|
|
||||||
|
blur?.let {
|
||||||
|
DisposableEffect("LwaDialog") {
|
||||||
|
blur.show()
|
||||||
|
onDispose {
|
||||||
|
blur.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Dialog(
|
Dialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
content = {
|
content = {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import androidx.compose.material.Surface
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.minimumInteractiveComponentSize
|
import androidx.compose.material.minimumInteractiveComponentSize
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
|
|
@ -28,13 +29,12 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackHandler
|
import com.pixelized.desktop.lwa.LocalBlurController
|
||||||
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackUio
|
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
|
||||||
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
|
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
|
||||||
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.GMFilterHeader
|
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.GMFilterHeader
|
||||||
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagUio
|
import com.pixelized.desktop.lwa.ui.screen.gamemaster.common.tag.GMTagUio
|
||||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import lwacharactersheet.composeapp.generated.resources.Res
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_close_24dp
|
import lwacharactersheet.composeapp.generated.resources.ic_close_24dp
|
||||||
|
|
@ -52,12 +52,23 @@ data class CharacterSheetAlterationDialogUio(
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CharacterSheetAlterationDialog(
|
fun CharacterSheetAlterationDialog(
|
||||||
|
blur: BlurContentController? = LocalBlurController.current,
|
||||||
dialog: State<CharacterSheetAlterationDialogUio?>,
|
dialog: State<CharacterSheetAlterationDialogUio?>,
|
||||||
onTag: (String) -> Unit,
|
onTag: (String) -> Unit,
|
||||||
onAlteration: (characterSheetId: String, alterationId: String, active: Boolean) -> Unit,
|
onAlteration: (characterSheetId: String, alterationId: String, active: Boolean) -> Unit,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
) {
|
) {
|
||||||
dialog.value?.let {
|
dialog.value?.let {
|
||||||
|
|
||||||
|
blur?.let {
|
||||||
|
DisposableEffect("LwaDialog") {
|
||||||
|
blur.show()
|
||||||
|
onDispose {
|
||||||
|
blur.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Dialog(
|
Dialog(
|
||||||
properties = DialogProperties(
|
properties = DialogProperties(
|
||||||
usePlatformDefaultWidth = false,
|
usePlatformDefaultWidth = false,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.composable.confirmation
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.material.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.pixelized.desktop.lwa.LocalBlurController
|
||||||
|
import com.pixelized.desktop.lwa.ui.composable.character.LwaDialog
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
|
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 ConfirmationDialogUio(
|
||||||
|
val title: String,
|
||||||
|
val description: String,
|
||||||
|
val onConfirmRequest: () -> Unit,
|
||||||
|
val onDismissRequest: () -> Unit,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
object ConfirmationDialogDefault {
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
val paddings = PaddingValues(start = 16.dp, top = 16.dp, end = 16.dp)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
val spacings: Dp = 8.dp
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
|
@Composable
|
||||||
|
fun ConfirmationDialog(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
paddingValues: PaddingValues = ConfirmationDialogDefault.paddings,
|
||||||
|
spacing: Dp = ConfirmationDialogDefault.spacings,
|
||||||
|
dialog: State<ConfirmationDialogUio?>,
|
||||||
|
) {
|
||||||
|
LwaDialog(
|
||||||
|
modifier = modifier,
|
||||||
|
blur = LocalBlurController.current,
|
||||||
|
state = dialog,
|
||||||
|
onDismissRequest = { dialog.value?.onDismissRequest?.invoke() },
|
||||||
|
onConfirm = { dialog.value?.onConfirmRequest?.invoke() },
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(paddingValues = paddingValues),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(space = spacing),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.typography.caption,
|
||||||
|
text = it.title,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.typography.body1,
|
||||||
|
text = it.description,
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.align(alignment = Alignment.End),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(
|
||||||
|
space = spacing / 2,
|
||||||
|
alignment = Alignment.End,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
TextButton(
|
||||||
|
onClick = it.onDismissRequest,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
color = MaterialTheme.colors.primaryVariant,
|
||||||
|
text = stringResource(Res.string.dialog__cancel_action)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TextButton(
|
||||||
|
onClick = it.onConfirmRequest,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
color = MaterialTheme.colors.primary,
|
||||||
|
text = stringResource(Res.string.dialog__confirm_action)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -169,7 +169,6 @@ fun CampaignScreen(
|
||||||
.width(width = 128.dp * 4)
|
.width(width = 128.dp * 4)
|
||||||
.fillMaxHeight(),
|
.fillMaxHeight(),
|
||||||
transitionSpec = rememberTransitionAnimation(direction = LayoutDirection.Ltr),
|
transitionSpec = rememberTransitionAnimation(direction = LayoutDirection.Ltr),
|
||||||
blurController = blurController,
|
|
||||||
detailPanelViewModel = npcDetailViewModel,
|
detailPanelViewModel = npcDetailViewModel,
|
||||||
characterDiminishedViewModel = dismissedViewModel,
|
characterDiminishedViewModel = dismissedViewModel,
|
||||||
characteristicDialogViewModel = characteristicDialogViewModel,
|
characteristicDialogViewModel = characteristicDialogViewModel,
|
||||||
|
|
@ -186,7 +185,6 @@ fun CampaignScreen(
|
||||||
.width(width = 128.dp * 4)
|
.width(width = 128.dp * 4)
|
||||||
.fillMaxHeight(),
|
.fillMaxHeight(),
|
||||||
transitionSpec = rememberTransitionAnimation(direction = LayoutDirection.Rtl),
|
transitionSpec = rememberTransitionAnimation(direction = LayoutDirection.Rtl),
|
||||||
blurController = blurController,
|
|
||||||
detailPanelViewModel = playerDetailViewModel,
|
detailPanelViewModel = playerDetailViewModel,
|
||||||
characterDiminishedViewModel = dismissedViewModel,
|
characterDiminishedViewModel = dismissedViewModel,
|
||||||
characteristicDialogViewModel = characteristicDialogViewModel,
|
characteristicDialogViewModel = characteristicDialogViewModel,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.pager.HorizontalPager
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
import androidx.compose.foundation.pager.PagerState
|
import androidx.compose.foundation.pager.PagerState
|
||||||
import androidx.compose.foundation.pager.rememberPagerState
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
|
|
@ -31,7 +30,6 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Shape
|
import androidx.compose.ui.graphics.Shape
|
||||||
import androidx.compose.ui.unit.LayoutDirection
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import com.pixelized.desktop.lwa.LocalRollHostState
|
import com.pixelized.desktop.lwa.LocalRollHostState
|
||||||
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
|
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
|
||||||
import com.pixelized.desktop.lwa.ui.composable.character.alterteration.CharacterSheetAlterationDialogViewModel
|
import com.pixelized.desktop.lwa.ui.composable.character.alterteration.CharacterSheetAlterationDialogViewModel
|
||||||
|
|
@ -74,7 +72,6 @@ enum class DetailPanelUio {
|
||||||
@Composable
|
@Composable
|
||||||
fun CharacterDetailPanel(
|
fun CharacterDetailPanel(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
blurController: BlurContentController,
|
|
||||||
transitionSpec: AnimatedContentTransitionScope<CharacterDetailPanelUio>.() -> ContentTransform = rememberTransitionAnimation(),
|
transitionSpec: AnimatedContentTransitionScope<CharacterDetailPanelUio>.() -> ContentTransform = rememberTransitionAnimation(),
|
||||||
detailPanelViewModel: CharacterDetailPanelViewModel,
|
detailPanelViewModel: CharacterDetailPanelViewModel,
|
||||||
characteristicDialogViewModel: CharacterSheetCharacteristicDialogViewModel,
|
characteristicDialogViewModel: CharacterSheetCharacteristicDialogViewModel,
|
||||||
|
|
@ -121,12 +118,10 @@ fun CharacterDetailPanel(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onAlteration = {
|
onAlteration = {
|
||||||
blurController.show()
|
|
||||||
alterationViewModel.show(characterSheetId = it)
|
alterationViewModel.show(characterSheetId = it)
|
||||||
},
|
},
|
||||||
onDiminished = {
|
onDiminished = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
blurController.show()
|
|
||||||
characterDiminishedViewModel.showDiminishedDialog(
|
characterDiminishedViewModel.showDiminishedDialog(
|
||||||
characterSheetId = it
|
characterSheetId = it
|
||||||
)
|
)
|
||||||
|
|
@ -134,7 +129,6 @@ fun CharacterDetailPanel(
|
||||||
},
|
},
|
||||||
onHp = {
|
onHp = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
blurController.show()
|
|
||||||
characteristicDialogViewModel.showSubCharacteristicDialog(
|
characteristicDialogViewModel.showSubCharacteristicDialog(
|
||||||
characterSheetId = it,
|
characterSheetId = it,
|
||||||
characteristic = CharacterSheetCharacteristicDialogUio.Characteristic.Damage,
|
characteristic = CharacterSheetCharacteristicDialogUio.Characteristic.Damage,
|
||||||
|
|
@ -143,7 +137,6 @@ fun CharacterDetailPanel(
|
||||||
},
|
},
|
||||||
onPp = {
|
onPp = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
blurController.show()
|
|
||||||
characteristicDialogViewModel.showSubCharacteristicDialog(
|
characteristicDialogViewModel.showSubCharacteristicDialog(
|
||||||
characterSheetId = it,
|
characterSheetId = it,
|
||||||
characteristic = CharacterSheetCharacteristicDialogUio.Characteristic.Fatigue,
|
characteristic = CharacterSheetCharacteristicDialogUio.Characteristic.Fatigue,
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,6 @@ fun CharacterDetailInventory(
|
||||||
itemDetailDialogViewModel: ItemDetailDialogViewModel = koinViewModel(),
|
itemDetailDialogViewModel: ItemDetailDialogViewModel = koinViewModel(),
|
||||||
inventory: State<CharacterDetailInventoryUio?>,
|
inventory: State<CharacterDetailInventoryUio?>,
|
||||||
) {
|
) {
|
||||||
val blur = LocalBlurController.current
|
|
||||||
val focus = LocalFocusManager.current
|
val focus = LocalFocusManager.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
|
@ -120,14 +119,12 @@ fun CharacterDetailInventory(
|
||||||
spacing = spacing,
|
spacing = spacing,
|
||||||
inventory = unWrap,
|
inventory = unWrap,
|
||||||
onPurse = {
|
onPurse = {
|
||||||
blur.show()
|
|
||||||
purseViewModel.showPurseDialog(
|
purseViewModel.showPurseDialog(
|
||||||
characterSheetId = it,
|
characterSheetId = it,
|
||||||
)
|
)
|
||||||
focus.clearFocus(force = true)
|
focus.clearFocus(force = true)
|
||||||
},
|
},
|
||||||
onItem = { item ->
|
onItem = { item ->
|
||||||
blur.show()
|
|
||||||
itemDetailDialogViewModel.showItemDialog(
|
itemDetailDialogViewModel.showItemDialog(
|
||||||
characterSheetId = item.characterSheetId,
|
characterSheetId = item.characterSheetId,
|
||||||
inventoryId = item.inventoryId,
|
inventoryId = item.inventoryId,
|
||||||
|
|
@ -136,7 +133,6 @@ fun CharacterDetailInventory(
|
||||||
focus.clearFocus(force = true)
|
focus.clearFocus(force = true)
|
||||||
},
|
},
|
||||||
onAddItem = {
|
onAddItem = {
|
||||||
blur.show()
|
|
||||||
inventoryDialogViewModel.showInventoryDialog(
|
inventoryDialogViewModel.showInventoryDialog(
|
||||||
characterSheetId = it,
|
characterSheetId = it,
|
||||||
)
|
)
|
||||||
|
|
@ -166,7 +162,6 @@ fun CharacterDetailInventory(
|
||||||
PurseDialog(
|
PurseDialog(
|
||||||
dialog = purseViewModel.purseDialog.collectAsState(),
|
dialog = purseViewModel.purseDialog.collectAsState(),
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
blur.hide()
|
|
||||||
purseViewModel.hidePurseDialog()
|
purseViewModel.hidePurseDialog()
|
||||||
},
|
},
|
||||||
onSwapSign = {
|
onSwapSign = {
|
||||||
|
|
@ -175,7 +170,6 @@ fun CharacterDetailInventory(
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
if (purseViewModel.confirmPurse(dialog = it)) {
|
if (purseViewModel.confirmPurse(dialog = it)) {
|
||||||
blur.hide()
|
|
||||||
purseViewModel.hidePurseDialog()
|
purseViewModel.hidePurseDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -185,11 +179,9 @@ fun CharacterDetailInventory(
|
||||||
InventoryDialog(
|
InventoryDialog(
|
||||||
dialog = inventoryDialogViewModel.inventoryDialog.collectAsState(),
|
dialog = inventoryDialogViewModel.inventoryDialog.collectAsState(),
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
blur.hide()
|
|
||||||
inventoryDialogViewModel.hideInventoryDialog()
|
inventoryDialogViewModel.hideInventoryDialog()
|
||||||
},
|
},
|
||||||
onItem = { dialog, itemId ->
|
onItem = { dialog, itemId ->
|
||||||
blur.show()
|
|
||||||
itemDetailDialogViewModel.showItemDialog(
|
itemDetailDialogViewModel.showItemDialog(
|
||||||
characterSheetId = dialog.characterSheetId,
|
characterSheetId = dialog.characterSheetId,
|
||||||
inventoryId = null,
|
inventoryId = null,
|
||||||
|
|
@ -201,7 +193,6 @@ fun CharacterDetailInventory(
|
||||||
ItemDetailDialog(
|
ItemDetailDialog(
|
||||||
dialog = itemDetailDialogViewModel.itemDialog.collectAsState(),
|
dialog = itemDetailDialogViewModel.itemDialog.collectAsState(),
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
blur.hide()
|
|
||||||
itemDetailDialogViewModel.hideItemDialog()
|
itemDetailDialogViewModel.hideItemDialog()
|
||||||
},
|
},
|
||||||
onAddItem = { dialog ->
|
onAddItem = { dialog ->
|
||||||
|
|
@ -210,7 +201,6 @@ fun CharacterDetailInventory(
|
||||||
dialog = dialog,
|
dialog = dialog,
|
||||||
)
|
)
|
||||||
if (result) {
|
if (result) {
|
||||||
blur.hide()
|
|
||||||
itemDetailDialogViewModel.hideItemDialog()
|
itemDetailDialogViewModel.hideItemDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -221,7 +211,6 @@ fun CharacterDetailInventory(
|
||||||
dialog = dialog,
|
dialog = dialog,
|
||||||
)
|
)
|
||||||
if (result) {
|
if (result) {
|
||||||
blur.hide()
|
|
||||||
itemDetailDialogViewModel.hideItemDialog()
|
itemDetailDialogViewModel.hideItemDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +222,6 @@ fun CharacterDetailInventory(
|
||||||
inventoryId = dialog.inventoryId,
|
inventoryId = dialog.inventoryId,
|
||||||
)
|
)
|
||||||
if (result) {
|
if (result) {
|
||||||
blur.hide()
|
|
||||||
itemDetailDialogViewModel.hideItemDialog()
|
itemDetailDialogViewModel.hideItemDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -245,7 +233,6 @@ fun CharacterDetailInventory(
|
||||||
inventoryId = dialog.inventoryId,
|
inventoryId = dialog.inventoryId,
|
||||||
)
|
)
|
||||||
if (result) {
|
if (result) {
|
||||||
blur.hide()
|
|
||||||
itemDetailDialogViewModel.hideItemDialog()
|
itemDetailDialogViewModel.hideItemDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -257,7 +244,6 @@ fun CharacterDetailInventory(
|
||||||
inventoryId = dialog.inventoryId,
|
inventoryId = dialog.inventoryId,
|
||||||
)
|
)
|
||||||
if (result) {
|
if (result) {
|
||||||
blur.hide()
|
|
||||||
itemDetailDialogViewModel.hideItemDialog()
|
itemDetailDialogViewModel.hideItemDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,13 @@ import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackHandler
|
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackHandler
|
||||||
|
import com.pixelized.desktop.lwa.ui.composable.confirmation.ConfirmationDialog
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import lwacharactersheet.composeapp.generated.resources.Res
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_camping_24dp
|
import lwacharactersheet.composeapp.generated.resources.ic_camping_24dp
|
||||||
|
|
@ -36,7 +37,8 @@ fun GMActionPage(
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val scroll = rememberScrollState()
|
val scroll = rememberScrollState()
|
||||||
val actions = viewModel.actions.collectAsState()
|
val actions = viewModel.actions.collectAsStateWithLifecycle()
|
||||||
|
val validationDialog = viewModel.validationDialog.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
GMActionContent(
|
GMActionContent(
|
||||||
actions = actions,
|
actions = actions,
|
||||||
|
|
@ -66,6 +68,10 @@ fun GMActionPage(
|
||||||
ErrorSnackHandler(
|
ErrorSnackHandler(
|
||||||
error = viewModel.error,
|
error = viewModel.error,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ConfirmationDialog(
|
||||||
|
dialog = validationDialog,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.screen.gamemaster.action
|
||||||
|
|
||||||
|
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
||||||
|
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||||
|
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||||
|
import com.pixelized.shared.lwa.protocol.websocket.GameAdminEvent
|
||||||
|
import com.pixelized.shared.lwa.protocol.websocket.GameMasterEvent
|
||||||
|
|
||||||
|
class GMActionUseCase(
|
||||||
|
private val characterRepository: CharacterSheetRepository,
|
||||||
|
private val networkRepository: NetworkRepository,
|
||||||
|
private val campaignRepository: CampaignRepository,
|
||||||
|
) {
|
||||||
|
suspend fun invalidateServerCache() {
|
||||||
|
networkRepository.share(
|
||||||
|
GameAdminEvent.ServerSynchronization(
|
||||||
|
timestamp = System.currentTimeMillis(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun healPlayerParty() {
|
||||||
|
campaignRepository.campaignFlow().value.characters.forEach { characterSheetId ->
|
||||||
|
val sheet = characterRepository.characterDetail(
|
||||||
|
characterSheetId = characterSheetId,
|
||||||
|
) ?: return@forEach
|
||||||
|
|
||||||
|
val updated = sheet.copy(
|
||||||
|
damage = 0,
|
||||||
|
fatigue = 0,
|
||||||
|
diminished = 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (sheet != updated) {
|
||||||
|
characterRepository.updateCharacter(
|
||||||
|
sheet = updated,
|
||||||
|
create = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun toggleNpcVisibility() {
|
||||||
|
networkRepository.share(
|
||||||
|
GameMasterEvent.ToggleNpc(
|
||||||
|
timestamp = System.currentTimeMillis(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun togglePlayerVisibility() {
|
||||||
|
networkRepository.share(
|
||||||
|
GameMasterEvent.TogglePlayer(
|
||||||
|
timestamp = System.currentTimeMillis(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,23 +3,36 @@ package com.pixelized.desktop.lwa.ui.screen.gamemaster.action
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
import com.pixelized.desktop.lwa.ui.composable.confirmation.ConfirmationDialogUio
|
||||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
|
||||||
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackUio
|
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackUio
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.GameAdminEvent
|
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.GameMasterEvent
|
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.game_master__actions__hide_npc__description
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.game_master__actions__hide_npc__title
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.game_master__actions__hide_player__description
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.game_master__actions__hide_player__title
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.game_master__actions__on_server_sync__description
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.game_master__actions__on_server_sync__title
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.game_master__actions__party_heal__description
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.game_master__actions__party_heal__title
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.game_master__actions__show_npc__description
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.game_master__actions__show_npc__title
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.game_master__actions__show_player__description
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.game_master__actions__show_player__title
|
||||||
|
import org.jetbrains.compose.resources.StringResource
|
||||||
|
import org.jetbrains.compose.resources.getString
|
||||||
|
|
||||||
class GMActionViewModel(
|
class GMActionViewModel(
|
||||||
private val characterRepository: CharacterSheetRepository,
|
private val actionUseCase: GMActionUseCase,
|
||||||
private val networkRepository: NetworkRepository,
|
campaignRepository: CampaignRepository,
|
||||||
private val campaignRepository: CampaignRepository,
|
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _error = MutableSharedFlow<ErrorSnackUio>()
|
private val _error = MutableSharedFlow<ErrorSnackUio>()
|
||||||
|
|
@ -39,68 +52,102 @@ class GMActionViewModel(
|
||||||
initialValue = null,
|
initialValue = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val _validationDialog = MutableStateFlow<ConfirmationDialogUio?>(null)
|
||||||
|
val validationDialog: StateFlow<ConfirmationDialogUio?> = _validationDialog
|
||||||
|
|
||||||
suspend fun onServerSync() {
|
suspend fun onServerSync() {
|
||||||
try {
|
showConfirmationDialog(
|
||||||
networkRepository.share(
|
title = Res.string.game_master__actions__on_server_sync__title,
|
||||||
GameAdminEvent.ServerSynchronization(
|
description = Res.string.game_master__actions__on_server_sync__description,
|
||||||
timestamp = System.currentTimeMillis(),
|
onConfirmationRequest = {
|
||||||
)
|
|
||||||
)
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
val message = ErrorSnackUio.from(exception = exception)
|
|
||||||
_error.emit(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun onPartyHeal() {
|
|
||||||
campaignRepository.campaignFlow().value.characters.forEach { characterSheetId ->
|
|
||||||
val sheet = characterRepository.characterDetail(
|
|
||||||
characterSheetId = characterSheetId,
|
|
||||||
) ?: return@forEach
|
|
||||||
|
|
||||||
val updated = sheet.copy(
|
|
||||||
damage = 0,
|
|
||||||
fatigue = 0,
|
|
||||||
diminished = 0,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (sheet != updated) {
|
|
||||||
try {
|
try {
|
||||||
characterRepository.updateCharacter(
|
actionUseCase.invalidateServerCache()
|
||||||
sheet = updated,
|
|
||||||
create = false,
|
|
||||||
)
|
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
val message = ErrorSnackUio.from(exception = exception)
|
val message = ErrorSnackUio.from(exception = exception)
|
||||||
_error.emit(message)
|
_error.emit(message)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
onDismissRequest = {
|
||||||
|
_validationDialog.value = null
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun onNpcVisibility() {
|
suspend fun onPartyHeal() {
|
||||||
try {
|
showConfirmationDialog(
|
||||||
networkRepository.share(
|
title = Res.string.game_master__actions__party_heal__title,
|
||||||
GameMasterEvent.ToggleNpc(
|
description = Res.string.game_master__actions__party_heal__description,
|
||||||
timestamp = System.currentTimeMillis(),
|
onConfirmationRequest = {
|
||||||
)
|
try {
|
||||||
)
|
actionUseCase.healPlayerParty()
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
val message = ErrorSnackUio.from(exception = exception)
|
val message = ErrorSnackUio.from(exception = exception)
|
||||||
_error.emit(message)
|
_error.emit(message)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun onPlayerVisibility() {
|
suspend fun onPlayerVisibility() {
|
||||||
try {
|
showConfirmationDialog(
|
||||||
networkRepository.share(
|
title = when (actions.value?.party) {
|
||||||
GameMasterEvent.TogglePlayer(
|
true -> Res.string.game_master__actions__hide_player__title
|
||||||
timestamp = System.currentTimeMillis(),
|
else -> Res.string.game_master__actions__show_player__title
|
||||||
)
|
},
|
||||||
)
|
description = when (actions.value?.party) {
|
||||||
} catch (exception: Exception) {
|
true -> Res.string.game_master__actions__hide_player__description
|
||||||
val message = ErrorSnackUio.from(exception = exception)
|
else -> Res.string.game_master__actions__show_player__description
|
||||||
_error.emit(message)
|
},
|
||||||
}
|
onConfirmationRequest = {
|
||||||
|
try {
|
||||||
|
actionUseCase.togglePlayerVisibility()
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
val message = ErrorSnackUio.from(exception = exception)
|
||||||
|
_error.emit(message)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun onNpcVisibility() {
|
||||||
|
showConfirmationDialog(
|
||||||
|
title = when (actions.value?.npc) {
|
||||||
|
true -> Res.string.game_master__actions__hide_npc__title
|
||||||
|
else -> Res.string.game_master__actions__show_npc__title
|
||||||
|
},
|
||||||
|
description = when (actions.value?.npc) {
|
||||||
|
true -> Res.string.game_master__actions__hide_npc__description
|
||||||
|
else -> Res.string.game_master__actions__show_npc__description
|
||||||
|
},
|
||||||
|
onConfirmationRequest = {
|
||||||
|
try {
|
||||||
|
actionUseCase.toggleNpcVisibility()
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
val message = ErrorSnackUio.from(exception = exception)
|
||||||
|
_error.emit(message)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend inline fun showConfirmationDialog(
|
||||||
|
title: StringResource,
|
||||||
|
description: StringResource,
|
||||||
|
crossinline onConfirmationRequest: suspend () -> Unit,
|
||||||
|
crossinline onDismissRequest: () -> Unit = { _validationDialog.value = null },
|
||||||
|
) {
|
||||||
|
_validationDialog.value = ConfirmationDialogUio(
|
||||||
|
title = getString(title),
|
||||||
|
description = getString(description),
|
||||||
|
onConfirmRequest = {
|
||||||
|
viewModelScope.launch {
|
||||||
|
onConfirmationRequest()
|
||||||
|
onDismissRequest()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDismissRequest = {
|
||||||
|
onDismissRequest()
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -109,7 +109,6 @@ fun GMCharacterPage(
|
||||||
.width(width = 128.dp * 4)
|
.width(width = 128.dp * 4)
|
||||||
.fillMaxHeight(),
|
.fillMaxHeight(),
|
||||||
transitionSpec = rememberTransitionAnimation(direction = LayoutDirection.Rtl),
|
transitionSpec = rememberTransitionAnimation(direction = LayoutDirection.Rtl),
|
||||||
blurController = blurController,
|
|
||||||
detailPanelViewModel = characterDetailViewModel,
|
detailPanelViewModel = characterDetailViewModel,
|
||||||
characterDiminishedViewModel = dismissedViewModel,
|
characterDiminishedViewModel = dismissedViewModel,
|
||||||
characteristicDialogViewModel = characteristicDialogViewModel,
|
characteristicDialogViewModel = characteristicDialogViewModel,
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,10 @@ data class LwaColors(
|
||||||
@Composable
|
@Composable
|
||||||
@Stable
|
@Stable
|
||||||
fun darkLwaColorTheme(
|
fun darkLwaColorTheme(
|
||||||
base: Colors = darkColors(),
|
base: Colors = darkColors(
|
||||||
|
primary = Color(0xFFBB86FC),
|
||||||
|
primaryVariant = Color(0xB2BB86FC),
|
||||||
|
),
|
||||||
elevated: LwaColors.Elevated = LwaColors.Elevated(
|
elevated: LwaColors.Elevated = LwaColors.Elevated(
|
||||||
base1dp = base.calculateElevatedColor(
|
base1dp = base.calculateElevatedColor(
|
||||||
color = base.surface,
|
color = base.surface,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue