From f60a58f71ec9507aba8a5ccc449e51cd7b9b94fd Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Mon, 3 Mar 2025 22:49:29 +0100 Subject: [PATCH] Add some chat log messages (diminished + hp/pp changes) --- .../composeResources/values/strings.xml | 5 ++ .../kotlin/com/pixelized/desktop/lwa/App.kt | 1 - .../com/pixelized/desktop/lwa/Module.kt | 2 + .../repository/campaign/CampaignRepository.kt | 4 + .../lwa/repository/campaign/CampaignStore.kt | 2 +- .../repository/settings/SettingsFactory.kt | 10 ++- .../lwa/repository/settings/model/Settings.kt | 4 + .../settings/model/SettingsJsonV1.kt | 4 + .../lwa/ui/screen/campaign/CampaignScreen.kt | 3 +- .../ui/screen/campaign/CampaignViewModel.kt | 39 +++++---- .../ui/screen/campaign/chat/CampaignChat.kt | 42 +++++++--- .../campaign/chat/CampaignChatViewModel.kt | 4 + .../campaign/chat/TextMessageFactory.kt | 66 ++++++++++++++- .../chat/text/CharacteristicTextMessage.kt | 64 +++++++++++++++ .../chat/text/DiminishedTextMessage.kt | 62 ++++++++++++++ .../text/{RollText.kt => RollTextMessage.kt} | 0 .../lwa/ui/screen/settings/SettingsScreen.kt | 44 +++++++++- .../ui/screen/settings/SettingsViewModel.kt | 81 +++++++++++++++++++ .../settings/composable/SettingItemUio.kt | 6 ++ .../settings/composable/SettingSection.kt | 30 +++++++ .../settings/composable/SettingToggleItem.kt | 64 +++++++++++++++ .../desktop/lwa/usecase/SettingsUseCase.kt | 4 + 22 files changed, 503 insertions(+), 38 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/text/CharacteristicTextMessage.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/text/DiminishedTextMessage.kt rename composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/text/{RollText.kt => RollTextMessage.kt} (100%) create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsViewModel.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingItemUio.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingSection.kt create mode 100644 composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingToggleItem.kt diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index f17faa1..1c3b949 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -157,4 +157,9 @@ Historique des lancers lance Difficulté + + %1$s passe à %2$d d\'état diminuée + %1$s passe à %2$d %3$s + Hp + Pp \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt index 10bc5ac..7b34129 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt @@ -28,7 +28,6 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.max import androidx.compose.ui.unit.min import androidx.compose.ui.window.ApplicationScope import androidx.compose.ui.window.Window diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt index c509289..23e5558 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt @@ -33,6 +33,7 @@ import com.pixelized.desktop.lwa.ui.screen.network.NetworkFactory import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel import com.pixelized.desktop.lwa.ui.screen.roll.RollViewModel import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryViewModel +import com.pixelized.desktop.lwa.ui.screen.settings.SettingsViewModel import com.pixelized.desktop.lwa.usecase.SettingsUseCase import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory import com.pixelized.shared.lwa.utils.PathProvider @@ -124,6 +125,7 @@ val viewModelDependencies viewModelOf(::CharacterDiminishedViewModel) viewModelOf(::CharacterDetailCharacteristicDialogViewModel) viewModelOf(::CampaignChatViewModel) + viewModelOf(::SettingsViewModel) } val useCaseDependencies diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/campaign/CampaignRepository.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/campaign/CampaignRepository.kt index 5d6303b..c98cc3a 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/campaign/CampaignRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/campaign/CampaignRepository.kt @@ -17,6 +17,10 @@ class CampaignRepository( val campaignFlow get() = store.campaignFlow + suspend fun update() { + store.update() + } + fun characterInstanceFlow( id: Campaign.CharacterInstance.Id, ): StateFlow { diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/campaign/CampaignStore.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/campaign/CampaignStore.kt index d924209..61dddfe 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/campaign/CampaignStore.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/campaign/CampaignStore.kt @@ -37,7 +37,7 @@ class CampaignStore( } } - private suspend fun update() { + suspend fun update() { _campaignFlow.value = load() } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/SettingsFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/SettingsFactory.kt index e85735a..b5a5916 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/SettingsFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/SettingsFactory.kt @@ -16,6 +16,10 @@ class SettingsFactory( host = settings.host, port = settings.port, playerName = settings.playerName, + dynamicDice = settings.dynamicDice, + autoHideChat = settings.autoHideChat, + autoShowChat = settings.autoShowChat, + autoScrollChat = settings.autoScrollChat, ) } @@ -34,7 +38,11 @@ class SettingsFactory( return Settings( host = json.host ?: default.host, port = json.port ?: default.port, - playerName = json.playerName ?: default.playerName + playerName = json.playerName ?: default.playerName, + dynamicDice = json.dynamicDice ?: default.dynamicDice, + autoHideChat = json.autoHideChat ?: default.autoHideChat, + autoShowChat = json.autoShowChat ?: default.autoShowChat, + autoScrollChat = json.autoScrollChat ?: default.autoScrollChat, ) } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/Settings.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/Settings.kt index 8d2b853..7af6816 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/Settings.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/Settings.kt @@ -4,6 +4,10 @@ data class Settings( val host: String, val port: Int, val playerName: String, + val dynamicDice: Boolean, + val autoHideChat: Boolean, + val autoShowChat: Boolean, + val autoScrollChat: Boolean, ) { val root: String get() = "http://${"${host}:${port}".removePrefix("http://")}" } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/SettingsJsonV1.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/SettingsJsonV1.kt index 4aee3e5..bbc4f26 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/SettingsJsonV1.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/settings/model/SettingsJsonV1.kt @@ -7,4 +7,8 @@ data class SettingsJsonV1( val host: String?, val port: Int?, val playerName: String?, + val dynamicDice: Boolean?, + val autoHideChat: Boolean?, + val autoShowChat: Boolean?, + val autoScrollChat: Boolean?, ) : SettingsJson \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt index f6afe13..1bd2842 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignScreen.kt @@ -92,6 +92,7 @@ fun MainPage( top = { CampaignToolbar( campaignViewModel = campaignViewModel, + networkViewModel = networkViewModel, ) }, bottom = { @@ -103,7 +104,7 @@ fun MainPage( chat = { CampaignChat( modifier = Modifier.padding(all = 8.dp), - campaignChatViewModel = campaignChatViewModel, + chatViewModel = campaignChatViewModel, ) }, leftOverlay = { diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignViewModel.kt index 02447fa..de9a871 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/CampaignViewModel.kt @@ -26,21 +26,30 @@ class CampaignViewModel( fun init() { viewModelScope.launch { - combine( - network.status, - campaignRepository.campaignFlow, - ) { status, campaign -> - status to campaign - }.collectLatest { (status, campaign) -> - if (status == NetworkRepository.Status.CONNECTED) { - campaign.characters.keys.forEach { id -> - characterRepository.characterDetail( - characterSheetId = id.characterSheetId, - forceUpdate = true, - ) - alterationRepository.updateActiveAlterations( - characterInstanceId = id, - ) + launch { + network.status.collect { status -> + if (status == NetworkRepository.Status.CONNECTED) { + campaignRepository.update() + } + } + } + launch { + combine( + network.status, + campaignRepository.campaignFlow, + ) { status, campaign -> + status to campaign + }.collectLatest { (status, campaign) -> + if (status == NetworkRepository.Status.CONNECTED) { + campaign.characters.keys.forEach { id -> + characterRepository.characterDetail( + characterSheetId = id.characterSheetId, + forceUpdate = true, + ) + alterationRepository.updateActiveAlterations( + characterInstanceId = id, + ) + } } } } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/CampaignChat.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/CampaignChat.kt index 13e4fa3..cfd0e49 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/CampaignChat.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/CampaignChat.kt @@ -32,9 +32,14 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.max import androidx.compose.ui.unit.min import androidx.compose.ui.window.WindowState +import com.pixelized.desktop.lwa.repository.settings.model.Settings import com.pixelized.desktop.lwa.ui.navigation.window.LocalWindowState import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignLayoutScope import com.pixelized.desktop.lwa.ui.screen.campaign.LocalCampaignLayoutScope +import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.CharacteristicTextMessage +import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.CharacteristicTextMessageUio +import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.DiminishedTextMessage +import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.DiminishedTextMessageUio import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.RollTextMessage import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.RollTextMessageUio import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.TextMessage @@ -47,19 +52,21 @@ import org.koin.compose.viewmodel.koinViewModel @Composable fun CampaignChat( modifier: Modifier = Modifier, - campaignChatViewModel: CampaignChatViewModel = koinViewModel(), + chatViewModel: CampaignChatViewModel = koinViewModel(), ) { val scope = rememberCoroutineScope() val lazyState = rememberLazyListState() val animatedChatWidth = rememberAnimatedChatWidth() val colorScheme = MaterialTheme.lwa.colorScheme - val messages = campaignChatViewModel.messages.collectAsState() + val messages = chatViewModel.messages.collectAsState() + val settings = chatViewModel.settings.collectAsState() ChatScrollDownEffect( lazyState = lazyState, messages = messages, - displayChat = campaignChatViewModel::displayChat, - hideChat = campaignChatViewModel::hideChat, + settings = settings, + displayChat = chatViewModel::displayChat, + hideChat = chatViewModel::hideChat, ) Box( @@ -69,17 +76,19 @@ fun CampaignChat( height = PlayerRibbon.Default.size.height * 2 + 8.dp, ) .graphicsLayer { - alpha = campaignChatViewModel.chatAnimatedVisibility.value + alpha = chatViewModel.chatAnimatedVisibility.value } .background( shape = remember { RoundedCornerShape(8.dp) }, color = remember { colorScheme.elevated.base1dp.copy(alpha = 0.5f) }, ) .onPointerEvent(eventType = PointerEventType.Enter) { - scope.launch { campaignChatViewModel.displayChat() } + scope.launch { chatViewModel.displayChat() } } .onPointerEvent(eventType = PointerEventType.Exit) { - scope.launch { campaignChatViewModel.hideChat() } + if (settings.value.autoHideChat) { + scope.launch { chatViewModel.hideChat() } + } }, ) { LazyColumn( @@ -98,6 +107,8 @@ fun CampaignChat( ) { when (it) { is RollTextMessageUio -> RollTextMessage(message = it) + is DiminishedTextMessageUio -> DiminishedTextMessage(message = it) + is CharacteristicTextMessageUio -> CharacteristicTextMessage(message = it) } } } @@ -108,6 +119,7 @@ fun CampaignChat( private fun ChatScrollDownEffect( lazyState: LazyListState, messages: State>, + settings: State, displayChat: suspend () -> Unit, hideChat: suspend () -> Unit, ) { @@ -115,11 +127,17 @@ private fun ChatScrollDownEffect( key1 = messages.value.lastOrNull()?.id, ) { if (messages.value.isNotEmpty()) { - displayChat() - lazyState.animateScrollToItem( - index = messages.value.lastIndex + 1, - ) - hideChat() + if (settings.value.autoShowChat) { + displayChat() + } + if (settings.value.autoScrollChat) { + lazyState.animateScrollToItem( + index = messages.value.lastIndex + 1, + ) + } + if (settings.value.autoHideChat) { + hideChat() + } } } } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/CampaignChatViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/CampaignChatViewModel.kt index 160843a..8c8e72e 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/CampaignChatViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/CampaignChatViewModel.kt @@ -5,6 +5,7 @@ import androidx.compose.animation.core.tween import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.pixelized.desktop.lwa.repository.network.NetworkRepository +import com.pixelized.desktop.lwa.repository.settings.SettingsRepository import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.TextMessage import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -15,8 +16,11 @@ import kotlinx.coroutines.flow.stateIn class CampaignChatViewModel( networkRepository: NetworkRepository, textMessageFactory: TextMessageFactory, + settingsRepository: SettingsRepository, ) : ViewModel() { + val settings = settingsRepository.settingsFlow() + val chatAnimatedVisibility = Animatable(0f) private var _messages = emptyList() diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/TextMessageFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/TextMessageFactory.kt index bc8d5df..b7674a5 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/TextMessageFactory.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/TextMessageFactory.kt @@ -1,26 +1,40 @@ package com.pixelized.desktop.lwa.ui.screen.campaign.chat +import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository +import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.CharacteristicTextMessageUio +import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.DiminishedTextMessageUio import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.RollTextMessageUio import com.pixelized.desktop.lwa.ui.screen.campaign.chat.text.TextMessage +import com.pixelized.shared.lwa.model.AlteredCharacterSheetFactory +import com.pixelized.shared.lwa.model.campaign.Campaign +import com.pixelized.shared.lwa.model.campaign.CampaignJsonV1.CharacterInstanceJsonV1.CharacteristicV1.Damage +import com.pixelized.shared.lwa.model.campaign.CampaignJsonV1.CharacterInstanceJsonV1.CharacteristicV1.Power import com.pixelized.shared.lwa.protocol.websocket.Message import com.pixelized.shared.lwa.protocol.websocket.payload.CampaignMessage import com.pixelized.shared.lwa.protocol.websocket.payload.RestSynchronisation import com.pixelized.shared.lwa.protocol.websocket.payload.RollMessage import com.pixelized.shared.lwa.protocol.websocket.payload.UpdateSkillUsageMessage +import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.chat__characteristic__hp +import lwacharactersheet.composeapp.generated.resources.chat__characteristic__pp +import org.jetbrains.compose.resources.getString import java.text.SimpleDateFormat class TextMessageFactory( private val characterSheetRepository: CharacterSheetRepository, + private val alterationRepository: AlterationRepository, + private val alteredCharacterSheetFactory: AlteredCharacterSheetFactory, ) { private val formatId = SimpleDateFormat("yyyy/MM/dd-HH:mm:ss:SSS") private val formatTime = SimpleDateFormat("HH:mm:ss") - fun convertToTextMessage( + suspend fun convertToTextMessage( message: Message, ): TextMessage? { val time = System.currentTimeMillis() val id = formatId.format(time) + return when (val payload = message.value) { is RollMessage -> { val sheetPreview = characterSheetRepository @@ -47,8 +61,54 @@ class TextMessageFactory( ) } - is CampaignMessage.UpdateCharacteristic -> null - is CampaignMessage.UpdateDiminished -> null + is CampaignMessage.UpdateDiminished -> { + val characterInstanceId = Campaign.CharacterInstance.Id( + characterSheetId = payload.characterSheetId, + instanceId = payload.instanceId, + ) + + val sheetPreview = characterSheetRepository + .characterPreview(characterId = payload.characterSheetId) + ?: return null + + DiminishedTextMessageUio( + id = id, + timestamp = formatTime.format(time), + character = sheetPreview.name, + diminished = payload.diminished, + ) + } + + is CampaignMessage.UpdateCharacteristic -> { + val sheet = characterSheetRepository.characterDetail( + characterSheetId = payload.characterSheetId, + ) ?: return null + val characterInstanceId = Campaign.CharacterInstance.Id( + characterSheetId = payload.characterSheetId, + instanceId = payload.instanceId, + ) + val alterations = alterationRepository.alterations( + characterInstanceId = characterInstanceId, + ) + val alteredSheet = alteredCharacterSheetFactory.sheet( + characterSheet = sheet, + alterations = alterations, + ) + CharacteristicTextMessageUio( + id = id, + timestamp = formatTime.format(time), + character = sheet.name, + value = when (payload.characteristic) { + Damage -> alteredSheet.maxHp - payload.value + Power -> alteredSheet.maxPp - payload.value + }, + characteristic = when (payload.characteristic) { + Damage -> getString(Res.string.chat__characteristic__hp) + Power -> getString(Res.string.chat__characteristic__pp) + }, + ) + } + RestSynchronisation.Campaign -> null is RestSynchronisation.CharacterDelete -> null is RestSynchronisation.CharacterUpdate -> null diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/text/CharacteristicTextMessage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/text/CharacteristicTextMessage.kt new file mode 100644 index 0000000..9328aee --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/text/CharacteristicTextMessage.kt @@ -0,0 +1,64 @@ +package com.pixelized.desktop.lwa.ui.screen.campaign.chat.text + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.ui.theme.lwa +import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.chat__characteristic_change +import org.jetbrains.compose.resources.stringResource + +@Stable +data class CharacteristicTextMessageUio( + override val id: String, + override val timestamp: String, + val character: String, + val value: Int, + val characteristic: String, +) : TextMessage + +@Composable +fun CharacteristicTextMessage( + modifier: Modifier = Modifier, + message: CharacteristicTextMessageUio, +) { + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(space = 3.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + modifier = Modifier.alignByBaseline(), + style = MaterialTheme.lwa.typography.chat.timestamp, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = message.timestamp, + ) + Text( + modifier = Modifier.alignByBaseline(), + style = MaterialTheme.lwa.typography.chat.timestamp, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = ">", + ) + Text( + modifier = Modifier.alignByBaseline(), + style = MaterialTheme.lwa.typography.chat.text, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = stringResource( + Res.string.chat__characteristic_change, + message.character, + message.value, + message.characteristic, + ), + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/text/DiminishedTextMessage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/text/DiminishedTextMessage.kt new file mode 100644 index 0000000..9da5f99 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/text/DiminishedTextMessage.kt @@ -0,0 +1,62 @@ +package com.pixelized.desktop.lwa.ui.screen.campaign.chat.text + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.ui.theme.lwa +import lwacharactersheet.composeapp.generated.resources.Res +import lwacharactersheet.composeapp.generated.resources.chat__diminished_change +import org.jetbrains.compose.resources.stringResource + +@Stable +data class DiminishedTextMessageUio( + override val id: String, + override val timestamp: String, + val character: String, + val diminished: Int, +) : TextMessage + +@Composable +fun DiminishedTextMessage( + modifier: Modifier = Modifier, + message: DiminishedTextMessageUio, +) { + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(space = 3.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + modifier = Modifier.alignByBaseline(), + style = MaterialTheme.lwa.typography.chat.timestamp, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = message.timestamp, + ) + Text( + modifier = Modifier.alignByBaseline(), + style = MaterialTheme.lwa.typography.chat.timestamp, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = ">", + ) + Text( + modifier = Modifier.alignByBaseline(), + style = MaterialTheme.lwa.typography.chat.text, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + text = stringResource( + Res.string.chat__diminished_change, + message.character, + message.diminished + ), + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/text/RollText.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/text/RollTextMessage.kt similarity index 100% rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/text/RollText.kt rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/chat/text/RollTextMessage.kt diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsScreen.kt index abfd139..0391b5b 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsScreen.kt @@ -1,26 +1,43 @@ package com.pixelized.desktop.lwa.ui.screen.settings +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.Scaffold import androidx.compose.material.Surface +import androidx.compose.material.Text import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController +import com.pixelized.desktop.lwa.ui.screen.settings.composable.SettingItemUio +import com.pixelized.desktop.lwa.ui.screen.settings.composable.SettingSection +import com.pixelized.desktop.lwa.ui.screen.settings.composable.SettingSectionUio +import com.pixelized.desktop.lwa.ui.screen.settings.composable.SettingToggleItem +import com.pixelized.desktop.lwa.ui.screen.settings.composable.SettingToggleItemUio +import org.koin.compose.viewmodel.koinViewModel + @Composable -fun SettingsScreen() { +fun SettingsScreen( + viewModel: SettingsViewModel = koinViewModel(), +) { val screen = LocalScreenController.current Surface { SettingsContent( modifier = Modifier.fillMaxSize(), + items = viewModel.items, onBack = { screen.popBackStack() }, @@ -31,13 +48,18 @@ fun SettingsScreen() { @Composable private fun SettingsContent( modifier: Modifier = Modifier, + state: ScrollState = rememberScrollState(), + spacing: Dp = 8.dp, + items: List, onBack: () -> Unit, ) { Scaffold( modifier = modifier, topBar = { TopAppBar( - title = { }, + title = { + Text(text = "Paramètres de l\'application") + }, navigationIcon = { IconButton( onClick = onBack, @@ -52,10 +74,24 @@ private fun SettingsContent( }, content = { paddingValues -> Column( - modifier = Modifier.padding(paddingValues = paddingValues), + modifier = Modifier + .padding(paddingValues = paddingValues) + .verticalScroll(state = state), + verticalArrangement = Arrangement.spacedBy(space = spacing), ) { + items.forEach { + when (it) { + is SettingSectionUio -> { + SettingSection(item = it) + } + is SettingToggleItemUio -> { + SettingToggleItem(item = it) + } + } + } } } ) -} \ No newline at end of file +} + diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsViewModel.kt new file mode 100644 index 0000000..2f728b3 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/SettingsViewModel.kt @@ -0,0 +1,81 @@ +package com.pixelized.desktop.lwa.ui.screen.settings + +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.pixelized.desktop.lwa.repository.settings.SettingsRepository +import com.pixelized.desktop.lwa.ui.screen.settings.composable.SettingSectionUio +import com.pixelized.desktop.lwa.ui.screen.settings.composable.SettingToggleItemUio +import kotlinx.coroutines.launch + +class SettingsViewModel( + private val settingsRepository: SettingsRepository, +) : ViewModel() { + private val settings = settingsRepository.settingsFlow() + private val states = hashMapOf>() + + val items = listOf( + SettingSectionUio( + title = "Portrait joueurs." + ), + SettingToggleItemUio( + title = "Dés dynamiques", + description = "Affiche un dés à côté du portrait d'un personnage lorsqu\'un jet est fait par ce dernier.", + checked = states.dynamicDice, + onToggle = { + settingsRepository.update(settings = settings.value.copy(dynamicDice = it)) + }, + ), + SettingSectionUio( + title = "Chatlog options." + ), + SettingToggleItemUio( + title = "Afficher automatiquement", + description = "Affiche automatiquement le chat lors de la réception d'un message.", + checked = states.autoShowChat, + onToggle = { + settingsRepository.update(settings = settings.value.copy(autoShowChat = it)) + }, + ), + SettingToggleItemUio( + title = "Cacher automatiquement", + description = "Cache automatiquement le chat au bout d'un certain temps.", + checked = states.autoHideChat, + onToggle = { + settingsRepository.update(settings = settings.value.copy(autoHideChat = it)) + }, + ), + SettingToggleItemUio( + title = "Défilement automatique", + description = "Défilement automatique de chat vers le dernier message reçu lors de la réception de ce dernier.", + checked = states.autoScrollChat, + onToggle = { + settingsRepository.update(settings = settings.value.copy(autoScrollChat = it)) + }, + ), + ) + + init { + viewModelScope.launch { + settingsRepository.settingsFlow().collect { settings -> + states.dynamicDice.value = settings.dynamicDice + states.autoShowChat.value = settings.autoShowChat + states.autoHideChat.value = settings.autoHideChat + states.autoScrollChat.value = settings.autoScrollChat + } + } + } + + private val HashMap>.dynamicDice + get() = getOrPut("DYNAMIC_DICE") { mutableStateOf(settings.value.dynamicDice) } + + private val HashMap>.autoShowChat + get() = getOrPut("AUTO_SHOW_CHAT") { mutableStateOf(settings.value.autoShowChat) } + + private val HashMap>.autoHideChat + get() = getOrPut("AUTO_HIDE_CHAT") { mutableStateOf(settings.value.autoHideChat) } + + private val HashMap>.autoScrollChat + get() = getOrPut("AUTO_SCROLL_CHAT") { mutableStateOf(settings.value.autoScrollChat) } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingItemUio.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingItemUio.kt new file mode 100644 index 0000000..4660a2e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingItemUio.kt @@ -0,0 +1,6 @@ +package com.pixelized.desktop.lwa.ui.screen.settings.composable + +import androidx.compose.runtime.Stable + +@Stable +sealed interface SettingItemUio \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingSection.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingSection.kt new file mode 100644 index 0000000..6086799 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingSection.kt @@ -0,0 +1,30 @@ +package com.pixelized.desktop.lwa.ui.screen.settings.composable + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.ui.theme.lwa + +@Stable +data class SettingSectionUio( + val title: String, +) : SettingItemUio + +@Composable +fun SettingSection( + modifier: Modifier = Modifier, + padding: PaddingValues = PaddingValues(start = 16.dp, top = 32.dp, end = 16.dp), + item: SettingSectionUio, +) { + Text( + modifier = modifier.padding(paddingValues = padding), + style = MaterialTheme.lwa.typography.base.h6, + maxLines = 1, + text = item.title, + ) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingToggleItem.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingToggleItem.kt new file mode 100644 index 0000000..514415e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/settings/composable/SettingToggleItem.kt @@ -0,0 +1,64 @@ +package com.pixelized.desktop.lwa.ui.screen.settings.composable + +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.Switch +import androidx.compose.material.SwitchDefaults +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.State +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.pixelized.desktop.lwa.ui.theme.lwa + +@Stable +data class SettingToggleItemUio( + val title: String, + val description: String, + val checked: State, + val onToggle: (Boolean) -> Unit, +) : SettingItemUio + +@Composable +fun SettingToggleItem( + modifier: Modifier = Modifier, + padding: PaddingValues = PaddingValues(start = 16.dp, top = 8.dp, end = 16.dp), + spacing: Dp = 8.dp, + item: SettingToggleItemUio, +) { + Row( + modifier = modifier.padding(paddingValues = padding), + horizontalArrangement = Arrangement.spacedBy(space = spacing), + ) { + Column( + modifier = Modifier.weight(weight = 1f), + verticalArrangement = Arrangement.spacedBy(space = spacing), + ) { + Text( + style = MaterialTheme.lwa.typography.base.body1, + maxLines = 1, + text = item.title, + ) + Text( + style = MaterialTheme.lwa.typography.base.caption, + fontStyle = FontStyle.Italic, + text = item.description, + ) + } + + Switch( + colors = SwitchDefaults.colors( + checkedThumbColor = MaterialTheme.lwa.colorScheme.base.primary, + ), + onCheckedChange = item.onToggle, + checked = item.checked.value + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/usecase/SettingsUseCase.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/usecase/SettingsUseCase.kt index 2a71f22..df53702 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/usecase/SettingsUseCase.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/usecase/SettingsUseCase.kt @@ -8,6 +8,10 @@ class SettingsUseCase { host = DEFAULT_HOST, port = DEFAULT_PORT, playerName = "", + dynamicDice = true, + autoHideChat = true, + autoShowChat = true, + autoScrollChat = true, ) companion object {