Refactor the chat to add a collapsable state.

This commit is contained in:
Thomas Andres Gomez 2025-10-12 21:19:03 +02:00
parent 651d05a7c4
commit fc06e3ef95
11 changed files with 246 additions and 195 deletions

View file

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#5f6368">
<path d="M200-200v-440h80v360h360v80H200Zm200-200v-440h80v360h360v80H400Z" />
</svg>

After

Width:  |  Height:  |  Size: 212 B

View file

@ -269,12 +269,6 @@
<string name="settings__player_portrait__dyn_dice_delay_tile">Délai pour les Dés dynamiques</string> <string name="settings__player_portrait__dyn_dice_delay_tile">Délai pour les Dés dynamiques</string>
<string name="settings__player_portrait__dyn_dice_delay_description">Délai après lequel les dés dynamiques disparaissent.</string> <string name="settings__player_portrait__dyn_dice_delay_description">Délai après lequel les dés dynamiques disparaissent.</string>
<string name="settings__chat_log__section">Chatlog options</string> <string name="settings__chat_log__section">Chatlog options</string>
<string name="settings__chat_log__auto_show_title">Afficher automatiquement le chat</string>
<string name="settings__chat_log__auto_show_description">Affiche automatiquement le chat lors de la réception d'un message</string>
<string name="settings__chat_log__auto_hide_title">Cacher automatiquement le chat</string>
<string name="settings__chat_log__auto_hide_description">Cache automatiquement le chat au bout d'un certain temps</string>
<string name="settings__chat_log__auto_hide_delay_title">Délai pour cacher le chat</string>
<string name="settings__chat_log__auto_hide_delay_description">Délai après lequel le chat disparaît</string>
<string name="settings__chat_log__auto_scroll_title">Défilement automatique</string> <string name="settings__chat_log__auto_scroll_title">Défilement automatique</string>
<string name="settings__chat_log__auto_scroll_description">Défilement automatique du chat vers le dernier message reçu lors de la réception de ce dernier.</string> <string name="settings__chat_log__auto_scroll_description">Défilement automatique du chat vers le dernier message reçu lors de la réception de ce dernier.</string>
<string name="settings__chat_log__line_count_title">Nombre de lignes de textes visibles</string> <string name="settings__chat_log__line_count_title">Nombre de lignes de textes visibles</string>

View file

@ -3,6 +3,7 @@ package com.pixelized.desktop.lwa.repository.settings
import com.pixelized.desktop.lwa.repository.settings.model.Settings import com.pixelized.desktop.lwa.repository.settings.model.Settings
import com.pixelized.desktop.lwa.repository.settings.model.SettingsJson import com.pixelized.desktop.lwa.repository.settings.model.SettingsJson
import com.pixelized.desktop.lwa.repository.settings.model.SettingsJsonV1 import com.pixelized.desktop.lwa.repository.settings.model.SettingsJsonV1
import com.pixelized.desktop.lwa.repository.settings.model.SettingsJsonV2
import com.pixelized.desktop.lwa.usecase.SettingsUseCase import com.pixelized.desktop.lwa.usecase.SettingsUseCase
@ -12,15 +13,13 @@ class SettingsFactory(
fun convertToJson( fun convertToJson(
settings: Settings, settings: Settings,
): SettingsJson { ): SettingsJson {
return SettingsJsonV1( return SettingsJsonV2(
host = settings.network.host, host = settings.network.host,
port = settings.network.port, port = settings.network.port,
playerName = settings.playerName, playerName = settings.playerName,
dynamicDice = settings.portrait.dynamicDice, dynamicDice = settings.portrait.dynamicDice,
dynamicDiceDelay = settings.portrait.dynamicDiceDelay, dynamicDiceDelay = settings.portrait.dynamicDiceDelay,
autoHideChat = settings.chat.autoHideChat, showChat = settings.chat.showChat,
autoHideDelay = settings.chat.autoHideDelay,
autoShowChat = settings.chat.autoShowChat,
autoScrollChat = settings.chat.autoScrollChat, autoScrollChat = settings.chat.autoScrollChat,
maxLineCount = settings.chat.maxLineCount, maxLineCount = settings.chat.maxLineCount,
isAdmin = settings.isAdmin, isAdmin = settings.isAdmin,
@ -33,6 +32,7 @@ class SettingsFactory(
): Settings { ): Settings {
return when (json) { return when (json) {
is SettingsJsonV1 -> convertFromJsonV1(json) is SettingsJsonV1 -> convertFromJsonV1(json)
is SettingsJsonV2 -> convertFromJsonV2(json)
} }
} }
@ -51,9 +51,31 @@ class SettingsFactory(
dynamicDiceDelay = json.dynamicDiceDelay ?: default.portrait.dynamicDiceDelay, dynamicDiceDelay = json.dynamicDiceDelay ?: default.portrait.dynamicDiceDelay,
), ),
chat = Settings.Chat( chat = Settings.Chat(
autoHideChat = json.autoHideChat ?: default.chat.autoHideChat, showChat = default.chat.showChat,
autoHideDelay = json.autoHideDelay ?: default.chat.autoHideDelay, autoScrollChat = json.autoScrollChat ?: default.chat.autoScrollChat,
autoShowChat = json.autoShowChat ?: default.chat.autoShowChat, maxLineCount = json.maxLineCount ?: default.chat.maxLineCount,
),
isAdmin = json.isAdmin ?: default.isAdmin,
isGameMaster = json.isGameMaster ?: default.isGameMaster,
)
}
private fun convertFromJsonV2(
json: SettingsJsonV2,
): Settings {
val default = useCase.defaultSettings()
return Settings(
playerName = json.playerName ?: default.playerName,
network = Settings.Network(
host = json.host ?: default.network.host,
port = json.port ?: default.network.port,
),
portrait = Settings.Portrait(
dynamicDice = json.dynamicDice ?: default.portrait.dynamicDice,
dynamicDiceDelay = json.dynamicDiceDelay ?: default.portrait.dynamicDiceDelay,
),
chat = Settings.Chat(
showChat = json.showChat ?: default.chat.showChat,
autoScrollChat = json.autoScrollChat ?: default.chat.autoScrollChat, autoScrollChat = json.autoScrollChat ?: default.chat.autoScrollChat,
maxLineCount = json.maxLineCount ?: default.chat.maxLineCount, maxLineCount = json.maxLineCount ?: default.chat.maxLineCount,
), ),

View file

@ -14,9 +14,7 @@ data class Settings(
) )
data class Chat( data class Chat(
val autoHideChat: Boolean, val showChat: Boolean,
val autoHideDelay: Int,
val autoShowChat: Boolean,
val autoScrollChat: Boolean, val autoScrollChat: Boolean,
val maxLineCount: Int, val maxLineCount: Int,
) )

View file

@ -0,0 +1,17 @@
package com.pixelized.desktop.lwa.repository.settings.model
import kotlinx.serialization.Serializable
@Serializable
data class SettingsJsonV2(
val host: String?,
val port: Int?,
val playerName: String?,
val dynamicDice: Boolean?,
val dynamicDiceDelay: Int?,
val showChat: Boolean?,
val autoScrollChat: Boolean?,
val maxLineCount: Int?,
val isGameMaster: Boolean?,
val isAdmin: Boolean?,
) : SettingsJson

View file

@ -2,6 +2,7 @@ package com.pixelized.desktop.lwa.ui.screen.campaign
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -338,26 +339,25 @@ private fun CampaignLayout(
) { ) {
main() main()
} }
Box( Row {
modifier = Modifier Box(
.align(alignment = Alignment.BottomEnd) modifier = Modifier.onSizeChanged { leftPanelState.value = it.toDp(density) },
.onSizeChanged { chatOverlayState.value = it.toDp(density) }, ) {
) { leftPanel()
chat() }
} Box(
Box( modifier = Modifier
modifier = Modifier .align(alignment = Alignment.Bottom)
.align(alignment = Alignment.CenterStart) .weight(weight = 1f)
.onSizeChanged { leftPanelState.value = it.toDp(density) }, .onSizeChanged { chatOverlayState.value = it.toDp(density) },
) { ) {
leftPanel() chat()
} }
Box( Box(
modifier = Modifier modifier = Modifier.onSizeChanged { rightPanelState.value = it.toDp(density) },
.align(alignment = Alignment.CenterEnd) ) {
.onSizeChanged { rightPanelState.value = it.toDp(density) }, rightPanel()
) { }
rightPanel()
} }
Box( Box(
modifier = Modifier modifier = Modifier

View file

@ -0,0 +1,72 @@
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonPortrait
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonRoll
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.common.CharacterRibbonAlteration
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.player.PlayerRibbonViewModel
import org.koin.compose.viewmodel.koinViewModel
@Composable
fun CharacterRibbon(
modifier: Modifier = Modifier,
layoutDirection: LayoutDirection,
viewModel: PlayerRibbonViewModel = koinViewModel(),
padding: PaddingValues = PaddingValues(all = 8.dp),
onCharacterLeftClick: (characterSheetId: String) -> Unit,
onCharacterRightClick: (characterSheetId: String) -> Unit,
onLevelUp: (characterSheetId: String) -> Unit,
) {
val characters = viewModel.characters.collectAsState()
CompositionLocalProvider(
LocalLayoutDirection provides layoutDirection
) {
LazyColumn(
modifier = modifier,
contentPadding = padding,
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
) {
items(
items = characters.value,
key = { it.characterSheetId },
) {
Row(
modifier = Modifier
.animateItem()
.graphicsLayer { if (it.hideOverruled) this.alpha = 0.3f },
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
Box {
CharacterRibbonPortrait(
character = it.portrait,
onCharacterLeftClick = { onCharacterLeftClick(it.characterSheetId) },
onCharacterRightClick = { onCharacterRightClick(it.characterSheetId) },
onLevelUp = { onLevelUp(it.characterSheetId) },
)
CharacterRibbonRoll(
value = viewModel.roll(characterSheetId = it.characterSheetId).value,
)
}
CharacterRibbonAlteration(
status = it.status,
direction = LayoutDirection.Ltr,
)
}
}
}
}
}

View file

@ -1,18 +1,28 @@
package com.pixelized.desktop.lwa.ui.screen.campaign.text package com.pixelized.desktop.lwa.ui.screen.campaign.text
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.expandIn
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkOut
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -21,13 +31,10 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.onPointerEvent
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.compose.ui.unit.max import androidx.compose.ui.unit.max
@ -47,21 +54,21 @@ import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.RollTextMessag
import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.TextMessage import com.pixelized.desktop.lwa.ui.screen.campaign.text.messages.TextMessage
import com.pixelized.desktop.lwa.ui.theme.lwa import com.pixelized.desktop.lwa.ui.theme.lwa
import com.pixelized.desktop.lwa.usecase.SettingsUseCase import com.pixelized.desktop.lwa.usecase.SettingsUseCase
import kotlinx.coroutines.launch import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.ic_more_down_24dp
import org.jetbrains.compose.resources.painterResource
import org.koin.compose.viewmodel.koinViewModel import org.koin.compose.viewmodel.koinViewModel
@Stable @Stable
data class ChatSettingsUio( data class ChatSettingsUio(
val autoShowChat: Boolean, val show: Boolean,
val autoScrollChat: Boolean, val autoScroll: Boolean,
val autoHideChat: Boolean,
) { ) {
companion object { companion object {
fun default() = with(SettingsUseCase().defaultSettings()) { fun default() = with(SettingsUseCase().defaultSettings()) {
ChatSettingsUio( ChatSettingsUio(
autoShowChat = chat.autoShowChat, show = chat.showChat,
autoScrollChat = chat.autoScrollChat, autoScroll = chat.autoScrollChat,
autoHideChat = chat.autoHideChat,
) )
} }
} }
@ -73,9 +80,9 @@ fun CampaignChat(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
chatViewModel: CampaignChatViewModel = koinViewModel(), chatViewModel: CampaignChatViewModel = koinViewModel(),
) { ) {
val scope = rememberCoroutineScope()
val lazyState = rememberLazyListState() val lazyState = rememberLazyListState()
val animatedChatWidth = rememberAnimatedChatWidth() val campaignLayoutScope = LocalCampaignLayoutScope.current
val colorScheme = MaterialTheme.lwa.colorScheme val colorScheme = MaterialTheme.lwa.colorScheme
val messages = chatViewModel.messages.collectAsState() val messages = chatViewModel.messages.collectAsState()
val settings = chatViewModel.settings.collectAsState() val settings = chatViewModel.settings.collectAsState()
@ -84,54 +91,63 @@ fun CampaignChat(
lazyState = lazyState, lazyState = lazyState,
messages = messages, messages = messages,
settings = settings, settings = settings,
displayChat = chatViewModel::displayChat,
hideChat = chatViewModel::hideChat,
) )
Box( Row(
modifier = modifier modifier = modifier.background(
.size( shape = remember { RoundedCornerShape(8.dp) },
width = animatedChatWidth.value, color = remember { colorScheme.elevated.base1dp.copy(alpha = 0.5f) },
height = MaterialTheme.lwa.size.portrait.minimized.height * 2 + 8.dp, ),
)
.graphicsLayer {
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 { chatViewModel.displayChat() }
}
.onPointerEvent(eventType = PointerEventType.Exit) {
if (settings.value.autoHideChat) {
scope.launch { chatViewModel.hideChat() }
}
},
) { ) {
LazyColumn( AnimatedVisibility(
modifier = Modifier.fillMaxSize(), visible = settings.value.show,
state = lazyState, enter = fadeIn() + expandIn(),
verticalArrangement = Arrangement.spacedBy( exit = fadeOut() + shrinkOut(),
space = 4.dp,
alignment = Alignment.Bottom,
),
contentPadding = remember { PaddingValues(all = 8.dp) },
) { ) {
items( LazyColumn(
items = messages.value, modifier = Modifier
key = { it.id }, .width(width = campaignLayoutScope.chatOverlay.value.width - (32.dp + 8.dp))
contentType = { it.javaClass.simpleName } .heightIn(min = MaterialTheme.lwa.size.portrait.minimized.height * 2 + 8.dp),
state = lazyState,
verticalArrangement = Arrangement.spacedBy(
space = 4.dp,
alignment = Alignment.Bottom,
),
contentPadding = remember { PaddingValues(all = 8.dp) },
) { ) {
when (it) { items(
is RollTextMessageUio -> RollTextMessage(message = it) items = messages.value,
is PurseTextMessageUio -> PurseTextMessage(message = it) key = { it.id },
is DiminishedTextMessageUio -> DiminishedTextMessage(message = it) contentType = { it.javaClass.simpleName }
is CharacteristicTextMessageUio -> CharacteristicTextMessage(message = it) ) {
when (it) {
is RollTextMessageUio -> RollTextMessage(message = it)
is PurseTextMessageUio -> PurseTextMessage(message = it)
is DiminishedTextMessageUio -> DiminishedTextMessage(message = it)
is CharacteristicTextMessageUio -> CharacteristicTextMessage(message = it)
}
} }
} }
} }
Column {
IconButton(
modifier = Modifier.size(size = 32.dp),
onClick = chatViewModel::toggleChat
) {
val rotation = animateFloatAsState(
targetValue = if (settings.value.show) 0f else 180f,
)
Icon(
modifier = Modifier
.size(size = 16.dp)
.graphicsLayer {
this.rotationZ = rotation.value
},
painter = painterResource(Res.drawable.ic_more_down_24dp),
contentDescription = null,
)
}
}
} }
} }
@ -140,24 +156,16 @@ private fun ChatScrollDownEffect(
lazyState: LazyListState, lazyState: LazyListState,
messages: State<List<TextMessage>>, messages: State<List<TextMessage>>,
settings: State<ChatSettingsUio>, settings: State<ChatSettingsUio>,
displayChat: suspend () -> Unit,
hideChat: suspend () -> Unit,
) { ) {
LaunchedEffect( LaunchedEffect(
key1 = messages.value.lastOrNull()?.id, key1 = messages.value.lastOrNull()?.id,
) { ) {
if (messages.value.isNotEmpty()) { if (messages.value.isNotEmpty()) {
if (settings.value.autoShowChat) { if (settings.value.autoScroll) {
displayChat()
}
if (settings.value.autoScrollChat) {
lazyState.animateScrollToItem( lazyState.animateScrollToItem(
index = messages.value.lastIndex + 1, index = messages.value.lastIndex + 1,
) )
} }
if (settings.value.autoHideChat) {
hideChat()
}
} }
} }
} }
@ -167,19 +175,26 @@ private fun ChatScrollDownEffect(
private fun rememberAnimatedChatWidth( private fun rememberAnimatedChatWidth(
campaignScreenScope: CampaignLayoutScope = LocalCampaignLayoutScope.current, campaignScreenScope: CampaignLayoutScope = LocalCampaignLayoutScope.current,
windowsState: WindowState = LocalWindowState.current, windowsState: WindowState = LocalWindowState.current,
settings: State<ChatSettingsUio>,
): State<Dp> { ): State<Dp> {
val chatWidth = remember(windowsState, campaignScreenScope) { val chatWidth = remember(windowsState, campaignScreenScope) {
derivedStateOf { derivedStateOf {
val minChatWidth = 64.dp * 8 if (settings.value.show) {
val maxChatWidth = 64.dp * 12 val minChatWidth = 64.dp * 8
val windowWidth = windowsState.size.width val maxChatWidth = 64.dp * 12
if (windowWidth != Dp.Unspecified) { val windowWidth = windowsState.size.width
val width = windowWidth - campaignScreenScope.leftPanel.value.width - 16.dp if (windowWidth != Dp.Unspecified) {
min(max(width, minChatWidth), maxChatWidth) val width = windowWidth - campaignScreenScope.leftPanel.value.width - 16.dp
min(max(width, minChatWidth), maxChatWidth)
} else {
minChatWidth
}
} else { } else {
minChatWidth 0.dp
} }
} }
} }
return animateDpAsState(targetValue = chatWidth.value) return animateDpAsState(
targetValue = chatWidth.value,
)
} }

View file

@ -1,7 +1,5 @@
package com.pixelized.desktop.lwa.ui.screen.campaign.text package com.pixelized.desktop.lwa.ui.screen.campaign.text
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.tween
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
@ -13,22 +11,20 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.runningFold import kotlinx.coroutines.flow.runningFold
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
class CampaignChatViewModel( class CampaignChatViewModel(
private val settingsRepository: SettingsRepository, private val settingsRepository: SettingsRepository,
private val campaignRepository: CampaignRepository, campaignRepository: CampaignRepository,
networkRepository: NetworkRepository, networkRepository: NetworkRepository,
textMessageFactory: TextMessageFactory, textMessageFactory: TextMessageFactory,
) : ViewModel() { ) : ViewModel() {
val chatAnimatedVisibility = Animatable(0f)
val settings = settingsRepository.settingsFlow().map { val settings = settingsRepository.settingsFlow().map {
ChatSettingsUio( ChatSettingsUio(
autoShowChat = it.chat.autoShowChat, show = it.chat.showChat,
autoScrollChat = it.chat.autoScrollChat, autoScroll = it.chat.autoScrollChat,
autoHideChat = it.chat.autoHideChat,
) )
}.stateIn( }.stateIn(
scope = viewModelScope, scope = viewModelScope,
@ -59,20 +55,14 @@ class CampaignChatViewModel(
initialValue = emptyList(), initialValue = emptyList(),
) )
suspend fun displayChat() { fun toggleChat() {
chatAnimatedVisibility.animateTo( viewModelScope.launch {
targetValue = 1f, val settings = settingsRepository.settingsFlow().value
) settingsRepository.update(
} settings = settings.copy(
chat = settings.chat.copy(showChat = settings.chat.showChat.not())
suspend fun hideChat() { )
val settings = settingsRepository.settingsFlow().value
chatAnimatedVisibility.animateTo(
targetValue = 0f,
animationSpec = tween(
durationMillis = 2000,
delayMillis = settings.chat.autoHideDelay * 1000,
) )
) }
} }
} }

View file

@ -15,16 +15,8 @@ import lwacharactersheet.composeapp.generated.resources.ic_fan_focus_24dp
import lwacharactersheet.composeapp.generated.resources.ic_format_list_numbered_24dp import lwacharactersheet.composeapp.generated.resources.ic_format_list_numbered_24dp
import lwacharactersheet.composeapp.generated.resources.ic_ifl_24dp import lwacharactersheet.composeapp.generated.resources.ic_ifl_24dp
import lwacharactersheet.composeapp.generated.resources.ic_timer_24dp import lwacharactersheet.composeapp.generated.resources.ic_timer_24dp
import lwacharactersheet.composeapp.generated.resources.ic_visibility_24dp
import lwacharactersheet.composeapp.generated.resources.ic_visibility_off_24dp
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_hide_delay_description
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_hide_delay_title
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_hide_description
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_hide_title
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_scroll_description import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_scroll_description
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_scroll_title import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_scroll_title
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_show_description
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__auto_show_title
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__line_count_description import lwacharactersheet.composeapp.generated.resources.settings__chat_log__line_count_description
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__line_count_title import lwacharactersheet.composeapp.generated.resources.settings__chat_log__line_count_title
import lwacharactersheet.composeapp.generated.resources.settings__chat_log__section import lwacharactersheet.composeapp.generated.resources.settings__chat_log__section
@ -92,48 +84,6 @@ class SettingsViewModel(
SettingSectionUio( SettingSectionUio(
title = Res.string.settings__chat_log__section, title = Res.string.settings__chat_log__section,
), ),
SettingToggleItemUio(
icon = Res.drawable.ic_visibility_24dp,
title = Res.string.settings__chat_log__auto_show_title,
description = Res.string.settings__chat_log__auto_show_description,
checked = booleanStates.autoShowChat,
onToggle = {
settingsRepository.update(
settings = settings.value.copy(
chat = settings.value.chat.copy(autoShowChat = it)
)
)
},
),
SettingToggleItemUio(
icon = Res.drawable.ic_visibility_off_24dp,
title = Res.string.settings__chat_log__auto_hide_title,
description = Res.string.settings__chat_log__auto_hide_description,
checked = booleanStates.autoHideChat,
onToggle = {
settingsRepository.update(
settings = settings.value.copy(
chat = settings.value.chat.copy(autoHideChat = it)
)
)
},
),
SettingNumberItemUio(
icon = Res.drawable.ic_timer_24dp,
title = Res.string.settings__chat_log__auto_hide_delay_title,
description = Res.string.settings__chat_log__auto_hide_delay_description,
enable = booleanStates.autoHideChat,
value = intStates.autoHideDelay,
onValueChange = {
if (it in 0..999) {
settingsRepository.update(
settings = settings.value.copy(
chat = settings.value.chat.copy(autoHideDelay = it)
)
)
}
}
),
SettingToggleItemUio( SettingToggleItemUio(
icon = Res.drawable.ic_fan_focus_24dp, icon = Res.drawable.ic_fan_focus_24dp,
title = Res.string.settings__chat_log__auto_scroll_title, title = Res.string.settings__chat_log__auto_scroll_title,
@ -170,9 +120,6 @@ class SettingsViewModel(
settingsRepository.settingsFlow().collect { settings -> settingsRepository.settingsFlow().collect { settings ->
booleanStates.dynamicDice.value = settings.portrait.dynamicDice booleanStates.dynamicDice.value = settings.portrait.dynamicDice
intStates.dynamicDiceDelay.value = settings.portrait.dynamicDiceDelay intStates.dynamicDiceDelay.value = settings.portrait.dynamicDiceDelay
booleanStates.autoShowChat.value = settings.chat.autoShowChat
booleanStates.autoHideChat.value = settings.chat.autoHideChat
intStates.autoHideDelay.value = settings.chat.autoHideDelay
booleanStates.autoScrollChat.value = settings.chat.autoScrollChat booleanStates.autoScrollChat.value = settings.chat.autoScrollChat
intStates.maxLineCount.value = settings.chat.maxLineCount intStates.maxLineCount.value = settings.chat.maxLineCount
} }
@ -197,15 +144,6 @@ class SettingsViewModel(
private val HashMap<String, MutableState<Int>>.dynamicDiceDelay private val HashMap<String, MutableState<Int>>.dynamicDiceDelay
get() = getOrPut("DYNAMIC_DICE_DELAY") { mutableStateOf(settings.value.portrait.dynamicDiceDelay) } get() = getOrPut("DYNAMIC_DICE_DELAY") { mutableStateOf(settings.value.portrait.dynamicDiceDelay) }
private val HashMap<String, MutableState<Boolean>>.autoShowChat
get() = getOrPut("AUTO_SHOW_CHAT") { mutableStateOf(settings.value.chat.autoShowChat) }
private val HashMap<String, MutableState<Boolean>>.autoHideChat
get() = getOrPut("AUTO_HIDE_CHAT") { mutableStateOf(settings.value.chat.autoHideChat) }
private val HashMap<String, MutableState<Int>>.autoHideDelay
get() = getOrPut("AUTO_HIDE_DELAY") { mutableStateOf(settings.value.chat.autoHideDelay) }
private val HashMap<String, MutableState<Boolean>>.autoScrollChat private val HashMap<String, MutableState<Boolean>>.autoScrollChat
get() = getOrPut("AUTO_SCROLL_CHAT") { mutableStateOf(settings.value.chat.autoScrollChat) } get() = getOrPut("AUTO_SCROLL_CHAT") { mutableStateOf(settings.value.chat.autoScrollChat) }

View file

@ -8,12 +8,10 @@ class SettingsUseCase {
playerName = "", playerName = "",
portrait = Settings.Portrait( portrait = Settings.Portrait(
dynamicDice = true, dynamicDice = true,
dynamicDiceDelay = 5000, dynamicDiceDelay = 8000,
), ),
chat = Settings.Chat( chat = Settings.Chat(
autoHideChat = true, showChat = true,
autoHideDelay = 8,
autoShowChat = true,
autoScrollChat = true, autoScrollChat = true,
maxLineCount = 200, maxLineCount = 200,
), ),