diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts
index 1a7af98..c7c8f0d 100644
--- a/composeApp/build.gradle.kts
+++ b/composeApp/build.gradle.kts
@@ -42,6 +42,8 @@ kotlin {
implementation(libs.ktor.client.negotiation)
// shell
implementation(libs.turtle)
+ // log
+ implementation(libs.logback)
}
commonTest.dependencies {
diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_settings_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_settings_24dp.xml
new file mode 100644
index 0000000..d18bac0
--- /dev/null
+++ b/composeApp/src/commonMain/composeResources/drawable/ic_settings_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_timeline_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_timeline_24dp.xml
new file mode 100644
index 0000000..a38d9fd
--- /dev/null
+++ b/composeApp/src/commonMain/composeResources/drawable/ic_timeline_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index aff4200..7c6d410 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -7,10 +7,9 @@
Créer une feuille de personnage
Ouvrir le dossier de sauvegarde
- Consulter l'historique des lancers
+ Historique des lancers
Configuration de la table
-
Jet de :
Réussite si lancer inférieur ou égal à : %1$s
Réussite critique
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 7213333..7da8014 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/App.kt
@@ -26,9 +26,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
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.window.ApplicationScope
import androidx.compose.ui.window.Window
+import androidx.compose.ui.window.rememberWindowState
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.desktop.lwa.repository.network.NetworkRepository.Status
import com.pixelized.desktop.lwa.ui.composable.key.KeyEventHandler
@@ -40,12 +42,15 @@ import com.pixelized.desktop.lwa.ui.navigation.window.WindowController
import com.pixelized.desktop.lwa.ui.navigation.window.WindowsNavHost
import com.pixelized.desktop.lwa.ui.navigation.window.destination.CharacterSheetEditWindow
import com.pixelized.desktop.lwa.ui.navigation.window.destination.CharacterSheetWindow
+import com.pixelized.desktop.lwa.ui.navigation.window.destination.NetworkWindows
import com.pixelized.desktop.lwa.ui.navigation.window.destination.RollHistoryWindow
import com.pixelized.desktop.lwa.ui.navigation.window.rememberMaxWindowHeight
import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
import com.pixelized.desktop.lwa.ui.screen.characterSheet.CharacterSheetMainNavHost
+import com.pixelized.desktop.lwa.ui.screen.network.NetworkPage
import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryPage
+import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryViewModel
import com.pixelized.desktop.lwa.ui.theme.LwaTheme
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@@ -88,74 +93,82 @@ fun ApplicationScope.App() {
) {
Window(
onCloseRequest = ::exitApplication,
+ state = rememberWindowState(size = DpSize(width = 800.dp, height = maxWindowHeight)),
title = runBlocking { getString(Res.string.app_name) },
onKeyEvent = { event ->
keyEventHandlers.reversed().any { it(event) }
},
) {
- LwaTheme {
- Surface(
- modifier = Modifier.fillMaxSize()
- ) {
- Scaffold(
- snackbarHost = {
- Column(
- modifier = Modifier.padding(all = 8.dp),
- verticalArrangement = Arrangement.spacedBy(space = 4.dp)
- ) {
- SnackbarHost(
- hostState = snackHostState,
- snackbar = {
- Snackbar(
- snackbarData = it,
- )
- }
- )
- SnackbarHost(
- hostState = errorSnackHostState,
- snackbar = {
- Snackbar(
- snackbarData = it,
- backgroundColor = MaterialTheme.colors.error,
- contentColor = MaterialTheme.colors.onError,
- actionColor = MaterialTheme.colors.onError,
- )
- }
- )
- }
- },
- content = {
- MainScreen()
- }
- )
- NetworkSnackHandler(
- snack = snackHostState,
- )
- WindowsHandler(
- windowController = windowController,
- )
- }
- }
+ MainWindowScreen()
}
}
}
@Composable
-private fun MainScreen(
+private fun MainWindowScreen(
campaignViewModel: CampaignViewModel = koinViewModel(),
networkViewModel: NetworkViewModel = koinViewModel(),
+ rollViewModel: RollHistoryViewModel = koinViewModel(),
) {
LaunchedEffect(Unit) {
networkViewModel.connect()
campaignViewModel.init()
}
- MainNavHost()
+ val snackHostState = LocalSnackHost.current
+ val errorSnackHostState = LocalErrorSnackHost.current
+ val windowController = LocalWindowController.current
+
+ LwaTheme {
+ Surface(
+ modifier = Modifier.fillMaxSize()
+ ) {
+ Scaffold(
+ snackbarHost = {
+ Column(
+ modifier = Modifier.padding(all = 8.dp),
+ verticalArrangement = Arrangement.spacedBy(space = 4.dp)
+ ) {
+ SnackbarHost(
+ hostState = snackHostState,
+ snackbar = {
+ Snackbar(
+ snackbarData = it,
+ )
+ }
+ )
+ SnackbarHost(
+ hostState = errorSnackHostState,
+ snackbar = {
+ Snackbar(
+ snackbarData = it,
+ backgroundColor = MaterialTheme.colors.error,
+ contentColor = MaterialTheme.colors.onError,
+ actionColor = MaterialTheme.colors.onError,
+ )
+ }
+ )
+ }
+ },
+ content = {
+ MainNavHost()
+ }
+ )
+ NetworkSnackHandler(
+ snack = snackHostState,
+ )
+ WindowsHandler(
+ windowController = windowController,
+ rollViewModel = rollViewModel,
+ )
+ }
+ }
}
@Composable
private fun WindowsHandler(
windowController: WindowController,
+ rollViewModel: RollHistoryViewModel = koinViewModel(),
) {
WindowsNavHost(
controller = windowController,
@@ -173,7 +186,11 @@ private fun WindowsHandler(
),
)
- is RollHistoryWindow -> RollHistoryPage()
+ is RollHistoryWindow -> RollHistoryPage(
+ viewModel = rollViewModel,
+ )
+
+ is NetworkWindows -> NetworkPage()
}
}
)
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 cacb771..24df629 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/Module.kt
@@ -34,7 +34,6 @@ import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryViewModel
import com.pixelized.desktop.lwa.usecase.SettingsUseCase
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
import io.ktor.client.HttpClient
-import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.websocket.WebSockets
@@ -56,12 +55,9 @@ val appModuleDependencies
val toolsDependencies
get() = module {
- single {
- OkHttp.create()
- }
single {
HttpClient(
- engine = get()
+ engine = OkHttp.create(),
) {
install(WebSockets) {
pingIntervalMillis = 20_000
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/network/NetworkRepository.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/network/NetworkRepository.kt
index b91b069..eb56eff 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/network/NetworkRepository.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/network/NetworkRepository.kt
@@ -7,6 +7,10 @@ import com.pixelized.desktop.lwa.utils.extention.encodeToFrame
import com.pixelized.shared.lwa.protocol.websocket.Message
import com.pixelized.shared.lwa.protocol.websocket.payload.MessagePayload
import io.ktor.client.HttpClient
+import io.ktor.client.engine.okhttp.OkHttp
+import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
+import io.ktor.client.plugins.websocket.WebSockets
+import io.ktor.serialization.kotlinx.json.json
import io.ktor.websocket.Frame
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -22,10 +26,11 @@ import kotlinx.serialization.json.Json
class NetworkRepository(
private val settingsRepository: SettingsRepository,
- private val client: HttpClient,
+ private val json: Json,
) {
- private val scope = CoroutineScope(Dispatchers.IO)
+ private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
private var networkJob: Job? = null
+ private var client: HttpClient? = null
private val outgoingMessageBuffer = MutableSharedFlow()
private val incomingMessageBuffer = MutableSharedFlow()
@@ -41,10 +46,11 @@ class NetworkRepository(
onFailure: (Exception) -> Unit = { },
onClose: () -> Unit = { },
) {
+ client = buildNewClient()
networkJob?.cancel()
- networkJob = scope.launch {
+ networkJob = scope.launch(Dispatchers.IO) {
try {
- client.connectWebSocket(host = host, port = port) {
+ client?.connectWebSocket(host = host, port = port) {
_status.value = Status.CONNECTED
onConnect()
@@ -78,7 +84,8 @@ class NetworkRepository(
fun disconnect() {
networkJob?.cancel()
scope.launch {
- client.close()
+ client?.close()
+ client = null
}
}
@@ -96,6 +103,17 @@ class NetworkRepository(
}
}
+ private fun buildNewClient() = HttpClient(
+ engine = OkHttp.create(),
+ ) {
+ install(WebSockets) {
+ pingIntervalMillis = 20_000
+ }
+ install(ContentNegotiation) {
+ json(json)
+ }
+ }
+
enum class Status {
CONNECTED,
DISCONNECTED
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/network/helper/Client.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/network/helper/Client.kt
index 80f4259..eed0fa1 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/network/helper/Client.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/repository/network/helper/Client.kt
@@ -13,7 +13,7 @@ suspend fun HttpClient.connectWebSocket(
) {
webSocket(
method = HttpMethod.Get,
- host = host,
+ host = host.removePrefix("http://"),
port = port,
path = "/ws",
block = block,
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 188faa6..e85735a 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
@@ -1,15 +1,14 @@
package com.pixelized.desktop.lwa.repository.settings
-import com.pixelized.desktop.lwa.usecase.SettingsUseCase
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.SettingsJsonV1
+import com.pixelized.desktop.lwa.usecase.SettingsUseCase
class SettingsFactory(
private val useCase: SettingsUseCase,
) {
-
fun convertToJson(
settings: Settings,
): SettingsJson {
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 8ac2ce4..8d2b853 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
@@ -5,5 +5,5 @@ data class Settings(
val port: Int,
val playerName: String,
) {
- val root: String get() = "http://${host}:${port}"
+ 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/ui/navigation/screen/destination/NetworkDestination.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/NetworkDestination.kt
index 82a8af7..84cc322 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/NetworkDestination.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/screen/destination/NetworkDestination.kt
@@ -3,7 +3,7 @@ package com.pixelized.desktop.lwa.ui.navigation.screen.destination
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
-import com.pixelized.desktop.lwa.ui.screen.network.NetworkPage
+import com.pixelized.desktop.lwa.ui.screen.network.NetworkScreen
object NetworkDestination {
private const val ROUTE = "network"
@@ -16,7 +16,7 @@ fun NavGraphBuilder.composableNetworkPage() {
composable(
route = NetworkDestination.baseRoute(),
) {
- NetworkPage()
+ NetworkScreen()
}
}
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/destination/NetworkWindows.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/destination/NetworkWindows.kt
new file mode 100644
index 0000000..d70afd4
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/navigation/window/destination/NetworkWindows.kt
@@ -0,0 +1,28 @@
+package com.pixelized.desktop.lwa.ui.navigation.window.destination
+
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import com.pixelized.desktop.lwa.ui.navigation.window.WindowController
+
+@Stable
+class NetworkWindows(
+ title: String,
+ size: DpSize,
+) : Window(
+ title = title,
+ size = size,
+)
+
+fun WindowController.navigateToNetwork(
+ title: String = "",
+) {
+ showWindow(
+ window = NetworkWindows(
+ title = title, size = DpSize(
+ width = 464.dp,
+ height = 300.dp,
+ )
+ )
+ )
+}
\ 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 c24064f..e04a78d 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
@@ -13,12 +13,9 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
-import androidx.compose.material.Icon
-import androidx.compose.material.IconButton
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -34,33 +31,24 @@ import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
-import com.pixelized.desktop.lwa.LocalWindowController
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContent
import com.pixelized.desktop.lwa.ui.composable.blur.rememberBlurContentController
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterDetailCharacteristicDialogViewModel
import com.pixelized.desktop.lwa.ui.composable.character.characteristic.CharacterSheetCharacteristicDialog
import com.pixelized.desktop.lwa.ui.composable.key.KeyHandler
-import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
-import com.pixelized.desktop.lwa.ui.navigation.screen.destination.navigateToOldMainPage
-import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToRollHistory
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailPanel
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetailViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDiminishedViewModel
import com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon.PlayerRibbon
-import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.Toolbar
+import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.CampaignToolbar
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.DiminishedStatDialog
import com.pixelized.desktop.lwa.ui.screen.roll.RollPage
import com.pixelized.desktop.lwa.ui.screen.roll.RollViewModel
import kotlinx.coroutines.launch
-import lwacharactersheet.composeapp.generated.resources.Res
-import lwacharactersheet.composeapp.generated.resources.ic_d20_24dp
-import lwacharactersheet.composeapp.generated.resources.ic_table_24dp
-import org.jetbrains.compose.resources.painterResource
import org.koin.compose.viewmodel.koinViewModel
@Composable
fun MainPage(
- campaignViewModel: CampaignViewModel = koinViewModel(),
characterDetailViewModel: CharacterDetailViewModel = koinViewModel(),
characteristicDialogViewModel: CharacterDetailCharacteristicDialogViewModel = koinViewModel(),
dismissedViewModel: CharacterDiminishedViewModel = koinViewModel(),
@@ -76,9 +64,7 @@ fun MainPage(
else -> false
}
}
-
- val windows = LocalWindowController.current
- val screen = LocalScreenController.current
+
val scope = rememberCoroutineScope()
val blurController = rememberBlurContentController()
@@ -92,31 +78,7 @@ fun MainPage(
CampaignScreenLayout(
modifier = Modifier.fillMaxSize(),
top = {
- Toolbar(
- title = campaignViewModel.title.collectAsState(initial = "").value,
- actions = {
- IconButton(
- onClick = {
- windows.navigateToRollHistory()
- },
- ) {
- Icon(
- painter = painterResource(Res.drawable.ic_d20_24dp),
- contentDescription = null,
- )
- }
- IconButton(
- onClick = {
- screen.navigateToOldMainPage()
- },
- ) {
- Icon(
- painter = painterResource(Res.drawable.ic_table_24dp),
- contentDescription = null,
- )
- }
- },
- )
+ CampaignToolbar()
},
bottom = {
Surface(
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 13cca63..c901665 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
@@ -5,8 +5,10 @@ import androidx.lifecycle.viewModelScope
import com.pixelized.desktop.lwa.repository.alteration.AlterationRepository
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 kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@@ -14,6 +16,7 @@ class CampaignViewModel(
private val characterRepository: CharacterSheetRepository,
private val alterationRepository: AlterationRepository,
private val campaignRepository: CampaignRepository,
+ private val network: NetworkRepository,
) : ViewModel() {
val title: Flow = campaignRepository.campaignFlow
@@ -21,15 +24,22 @@ class CampaignViewModel(
fun init() {
viewModelScope.launch {
- campaignRepository.campaignFlow.collectLatest {
- it.characters.keys.forEach { id ->
- characterRepository.characterDetail(
- characterSheetId = id.characterSheetId,
- forceUpdate = true,
- )
- alterationRepository.updateActiveAlterations(
- characterInstanceId = id,
- )
+ 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/toolbar/CampaignToolbar.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/CampaignToolbar.kt
new file mode 100644
index 0000000..d301dce
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/CampaignToolbar.kt
@@ -0,0 +1,145 @@
+package com.pixelized.desktop.lwa.ui.screen.campaign.toolbar
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.DropdownMenu
+import androidx.compose.material.DropdownMenuItem
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.material.TopAppBar
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.MoreVert
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.pixelized.desktop.lwa.LocalWindowController
+import com.pixelized.desktop.lwa.ui.navigation.screen.LocalScreenController
+import com.pixelized.desktop.lwa.ui.navigation.screen.destination.navigateToOldMainPage
+import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToNetwork
+import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToRollHistory
+import com.pixelized.desktop.lwa.ui.screen.campaign.CampaignViewModel
+import com.pixelized.desktop.lwa.ui.theme.lwa
+import lwacharactersheet.composeapp.generated.resources.Res
+import lwacharactersheet.composeapp.generated.resources.ic_settings_24dp
+import lwacharactersheet.composeapp.generated.resources.ic_table_24dp
+import lwacharactersheet.composeapp.generated.resources.ic_timeline_24dp
+import lwacharactersheet.composeapp.generated.resources.main_page__network_action
+import lwacharactersheet.composeapp.generated.resources.main_page__roll_history_action
+import org.jetbrains.compose.resources.painterResource
+import org.jetbrains.compose.resources.stringResource
+import org.koin.compose.viewmodel.koinViewModel
+
+@Composable
+fun CampaignToolbar(
+ campaignViewModel: CampaignViewModel = koinViewModel(),
+) {
+ val windows = LocalWindowController.current
+ val screen = LocalScreenController.current
+
+ val isOverflowMenuOpen = remember { mutableStateOf(false) }
+
+ CampaignToolbarContent(
+ title = campaignViewModel.title.collectAsState(initial = ""),
+ isOverflowMenuOpen = isOverflowMenuOpen,
+ onOverflow = {
+ isOverflowMenuOpen.value = isOverflowMenuOpen.value.not()
+ },
+ onDismissOverflowMenu = {
+ isOverflowMenuOpen.value = false
+ },
+ onRollHistory = {
+ windows.navigateToRollHistory()
+ },
+ onNetwork = {
+ windows.navigateToNetwork()
+ },
+ onOlUi = {
+ screen.navigateToOldMainPage()
+ },
+ )
+}
+
+@Composable
+private fun CampaignToolbarContent(
+ modifier: Modifier = Modifier,
+ title: State,
+ isOverflowMenuOpen: State,
+ onOverflow: () -> Unit,
+ onDismissOverflowMenu: () -> Unit,
+ onRollHistory: () -> Unit,
+ onNetwork: () -> Unit,
+ onOlUi: () -> Unit,
+) {
+ TopAppBar(
+ modifier = modifier,
+ title = {
+ Text(
+ text = title.value,
+ )
+ },
+ actions = {
+ IconButton(
+ onClick = onOverflow,
+ ) {
+ Icon(
+ imageVector = Icons.Default.MoreVert,
+ tint = MaterialTheme.colors.primary,
+ contentDescription = null,
+ )
+ }
+
+ DropdownMenu(
+ expanded = isOverflowMenuOpen.value,
+ onDismissRequest = onDismissOverflowMenu,
+ ) {
+ DropdownMenuItem(
+ onClick = { onDismissOverflowMenu(); onRollHistory() },
+ ) {
+ Icon(
+ painter = painterResource(Res.drawable.ic_timeline_24dp),
+ tint = MaterialTheme.lwa.colorScheme.base.primary,
+ contentDescription = null,
+ )
+ Text(
+ modifier = Modifier.padding(start = 8.dp),
+ color = MaterialTheme.colors.primary,
+ text = stringResource(Res.string.main_page__roll_history_action),
+ )
+ }
+ DropdownMenuItem(
+ onClick = { onDismissOverflowMenu(); onNetwork() },
+ ) {
+ Icon(
+ painter = painterResource(Res.drawable.ic_settings_24dp),
+ tint = MaterialTheme.lwa.colorScheme.base.primary,
+ contentDescription = null,
+ )
+ Text(
+ modifier = Modifier.padding(start = 8.dp),
+ color = MaterialTheme.colors.primary,
+ text = stringResource(Res.string.main_page__network_action),
+ )
+ }
+ DropdownMenuItem(
+ onClick = { onDismissOverflowMenu(); onOlUi() },
+ ) {
+ Icon(
+ painter = painterResource(Res.drawable.ic_table_24dp),
+ tint = MaterialTheme.lwa.colorScheme.base.primary,
+ contentDescription = null,
+ )
+ Text(
+ modifier = Modifier.padding(start = 8.dp),
+ color = MaterialTheme.colors.primary,
+ text = "Ancienne interface utilisateur",
+ )
+ }
+ }
+ },
+ )
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/Toolbar.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/Toolbar.kt
deleted file mode 100644
index 650d645..0000000
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/toolbar/Toolbar.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.pixelized.desktop.lwa.ui.screen.campaign.toolbar
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.RowScope
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material.LocalContentColor
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Surface
-import androidx.compose.material.Text
-import androidx.compose.material.minimumInteractiveComponentSize
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import com.pixelized.desktop.lwa.ui.theme.lwa
-
-@Composable
-fun Toolbar(
- modifier: Modifier = Modifier,
- title: String,
- actions: @Composable RowScope.() -> Unit = { },
-) {
- Surface(
- modifier = modifier,
- elevation = 1.dp,
- ) {
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .minimumInteractiveComponentSize(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Text(
- modifier = Modifier.padding(start = 16.dp),
- style = MaterialTheme.typography.h6,
- text = title,
- )
- CompositionLocalProvider(
- LocalContentColor provides MaterialTheme.lwa.colorScheme.base.primary,
- ) {
- Row(
- modifier = Modifier.padding(end = 8.dp),
- content = actions,
- )
- }
- }
- }
-}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/characterSheet/detail/CharacterSheetPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/characterSheet/detail/CharacterSheetPage.kt
index 23dc7e0..4e94b05 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/characterSheet/detail/CharacterSheetPage.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/characterSheet/detail/CharacterSheetPage.kt
@@ -124,7 +124,7 @@ data class CharacterSheetPageUio(
data class Roll(
val label: String,
val value: String,
- val roll: RollActionUio?,
+ val roll: RollActionUio,
)
}
@@ -187,7 +187,7 @@ fun CharacterSheetPage(
onUseSkill = viewModel::onUseSkill,
onRoll = { roll ->
blurController.show()
- rollViewModel.prepareRoll(sheet = sheet, roll = roll)
+ rollViewModel.prepareRoll(roll.roll)
viewModel.showRollOverlay()
},
)
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/main/MainPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/main/MainPage.kt
index d759c0f..b8a7c37 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/main/MainPage.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/main/MainPage.kt
@@ -13,9 +13,11 @@ 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.Scaffold
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextButton
+import androidx.compose.material.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
@@ -32,7 +34,6 @@ import com.pixelized.desktop.lwa.ui.navigation.screen.destination.navigateToNetw
import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToCharacterSheet
import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToCharacterSheetEdit
import com.pixelized.desktop.lwa.ui.navigation.window.destination.navigateToRollHistory
-import com.pixelized.desktop.lwa.ui.screen.campaign.toolbar.Toolbar
import com.pixelized.shared.lwa.model.campaign.Campaign
import kotlinx.coroutines.runBlocking
import lwacharactersheet.composeapp.generated.resources.Res
@@ -117,168 +118,177 @@ fun MainPageContent(
onNetwork: () -> Unit,
onMainPage: () -> Unit,
) {
- Column(
+ Scaffold(
modifier = modifier,
- ) {
- Toolbar(
- title = runBlocking { getString(Res.string.app_name) },
- actions = {
- TextButton(
- onClick = onMainPage,
- ) {
- Row(
- horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
- verticalAlignment = Alignment.CenterVertically,
+ topBar = {
+ TopAppBar(
+ title = {
+ Text(
+ text = runBlocking { getString(Res.string.app_name) },
+ )
+ },
+ actions = {
+ TextButton(
+ onClick = onMainPage,
) {
- Icon(
- painter = painterResource(Res.drawable.ic_swords_24dp),
- contentDescription = null,
- )
- Text(
- overflow = TextOverflow.Ellipsis,
- textAlign = TextAlign.Start,
- maxLines = 1,
- text = "Nouvelle interface utilisateur",
- )
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ painter = painterResource(Res.drawable.ic_swords_24dp),
+ contentDescription = null,
+ )
+ Text(
+ overflow = TextOverflow.Ellipsis,
+ textAlign = TextAlign.Start,
+ maxLines = 1,
+ text = "Nouvelle interface utilisateur",
+ )
+ }
}
}
- }
- )
- Box(
- modifier = Modifier
- .verticalScroll(state = scrollState)
- .fillMaxSize()
- .padding(horizontal = 16.dp),
- contentAlignment = Alignment.Center,
- ) {
+ )
+ },
+ content = {
Column {
- if (characters.value.isNotEmpty()) {
+ Box(
+ modifier = Modifier
+ .verticalScroll(state = scrollState)
+ .fillMaxSize()
+ .padding(horizontal = 16.dp),
+ contentAlignment = Alignment.Center,
+ ) {
Column {
- characters.value.forEach { sheet ->
- TextButton(
- onClick = { onCharacter(sheet) },
+ if (characters.value.isNotEmpty()) {
+ Column {
+ characters.value.forEach { sheet ->
+ TextButton(
+ onClick = { onCharacter(sheet) },
+ ) {
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ overflow = TextOverflow.Ellipsis,
+ textAlign = TextAlign.Start,
+ maxLines = 1,
+ text = sheet.name,
+ )
+ }
+ }
+ }
+ }
+
+ if (characters.value.isNotEmpty() && npcs.value.isNotEmpty()) {
+ Spacer(modifier = Modifier.height(height = 24.dp))
+ }
+
+ if (npcs.value.isNotEmpty()) {
+ Column {
+ npcs.value.forEach { sheet ->
+ TextButton(
+ onClick = { onCharacter(sheet) },
+ ) {
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ overflow = TextOverflow.Ellipsis,
+ textAlign = TextAlign.Start,
+ maxLines = 1,
+ text = sheet.name,
+ )
+ }
+ }
+ }
+ }
+
+ Spacer(modifier = Modifier.height(height = 24.dp))
+
+ TextButton(
+ onClick = onCreateCharacter,
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
) {
+ Icon(
+ painter = painterResource(Res.drawable.ic_file_24dp),
+ contentDescription = null,
+ )
Text(
- modifier = Modifier.fillMaxWidth(),
+ maxLines = 1,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Start,
- maxLines = 1,
- text = sheet.name,
+ text = stringResource(Res.string.main_page__create_action),
)
}
}
- }
- }
- if (characters.value.isNotEmpty() && npcs.value.isNotEmpty()) {
- Spacer(modifier = Modifier.height(height = 24.dp))
- }
-
- if (npcs.value.isNotEmpty()) {
- Column {
- npcs.value.forEach { sheet ->
- TextButton(
- onClick = { onCharacter(sheet) },
+ TextButton(
+ onClick = onOpenSaveDirectory,
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
) {
+ Icon(
+ painter = painterResource(Res.drawable.ic_folder_24dp),
+ contentDescription = null,
+ )
Text(
- modifier = Modifier.fillMaxWidth(),
+ maxLines = 1,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Start,
- maxLines = 1,
- text = sheet.name,
+ text = stringResource(Res.string.main_page__open_save_directory),
)
}
}
- }
- }
- Spacer(modifier = Modifier.height(height = 24.dp))
+ TextButton(
+ enabled = enableRollHistory.value,
+ onClick = onRollHistory,
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ painter = painterResource(Res.drawable.ic_d20_24dp),
+ contentDescription = null,
+ )
+ Text(
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ textAlign = TextAlign.Start,
+ text = stringResource(Res.string.main_page__roll_history_action),
+ )
+ }
+ }
- TextButton(
- onClick = onCreateCharacter,
- ) {
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Icon(
- painter = painterResource(Res.drawable.ic_file_24dp),
- contentDescription = null,
- )
- Text(
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- textAlign = TextAlign.Start,
- text = stringResource(Res.string.main_page__create_action),
- )
- }
- }
-
- TextButton(
- onClick = onOpenSaveDirectory,
- ) {
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Icon(
- painter = painterResource(Res.drawable.ic_folder_24dp),
- contentDescription = null,
- )
- Text(
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- textAlign = TextAlign.Start,
- text = stringResource(Res.string.main_page__open_save_directory),
- )
- }
- }
-
- TextButton(
- enabled = enableRollHistory.value,
- onClick = onRollHistory,
- ) {
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Icon(
- painter = painterResource(Res.drawable.ic_d20_24dp),
- contentDescription = null,
- )
- Text(
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- textAlign = TextAlign.Start,
- text = stringResource(Res.string.main_page__roll_history_action),
- )
- }
- }
-
- TextButton(
- onClick = onNetwork,
- ) {
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Icon(
- painter = painterResource(Res.drawable.ic_table_24dp),
- contentDescription = null,
- )
- Text(
- overflow = TextOverflow.Ellipsis,
- textAlign = TextAlign.Start,
- maxLines = 1,
- text = stringResource(Res.string.main_page__network_action),
- )
+ TextButton(
+ onClick = onNetwork,
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ painter = painterResource(Res.drawable.ic_table_24dp),
+ contentDescription = null,
+ )
+ Text(
+ overflow = TextOverflow.Ellipsis,
+ textAlign = TextAlign.Start,
+ maxLines = 1,
+ text = stringResource(Res.string.main_page__network_action),
+ )
+ }
+ }
}
}
}
- }
- }
+ },
+ )
}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/network/NetworkPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/network/NetworkScreen.kt
similarity index 51%
rename from composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/network/NetworkPage.kt
rename to composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/network/NetworkScreen.kt
index 08cfbf5..69ac12e 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/network/NetworkPage.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/network/NetworkScreen.kt
@@ -8,6 +8,7 @@ import androidx.compose.foundation.ScrollState
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.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
@@ -82,7 +83,7 @@ data class NetworkPageUio(
}
@Composable
-fun NetworkPage(
+fun NetworkScreen(
viewModel: NetworkViewModel = koinViewModel(),
) {
val screen = LocalScreenController.current
@@ -97,17 +98,43 @@ fun NetworkPage(
) {
BlurContent(
modifier = Modifier.fillMaxSize(),
- controller = viewModel.controller,
+ controller = viewModel.blurController,
) {
- NetworkContent(
+ Scaffold(
modifier = Modifier.fillMaxSize(),
- player = viewModel.network.collectAsState(),
- onBack = { screen.popBackStack() },
- onPlayerChange = viewModel::onPlayerNameChange,
- onHostChange = viewModel::onHostChange,
- onPortChange = viewModel::onPortChange,
- onConnect = viewModel::connect,
- onDisconnect = viewModel::disconnect,
+ topBar = {
+ TopAppBar(
+ title = {
+ Text(
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1,
+ text = stringResource(Res.string.network__title),
+ )
+ },
+ navigationIcon = {
+ IconButton(
+ onClick = { screen.popBackStack() },
+ ) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = null,
+ )
+ }
+ }
+ )
+ },
+ content = { paddingValues ->
+ NetworkContent(
+ modifier = Modifier.fillMaxSize(),
+ paddingValues = paddingValues,
+ player = viewModel.network.collectAsState(),
+ onPlayerChange = viewModel::onPlayerNameChange,
+ onHostChange = viewModel::onHostChange,
+ onPortChange = viewModel::onPortChange,
+ onConnect = viewModel::connect,
+ onDisconnect = viewModel::disconnect,
+ )
+ }
)
}
@@ -138,96 +165,119 @@ fun NetworkPage(
}
}
+@Composable
+fun NetworkPage(
+ viewModel: NetworkViewModel = koinViewModel(),
+) {
+ val snack = LocalSnackHost.current
+
+ Surface {
+ BlurContent(
+ modifier = Modifier.fillMaxSize(),
+ controller = viewModel.blurController,
+ ) {
+ NetworkContent(
+ modifier = Modifier.fillMaxSize(),
+ player = viewModel.network.collectAsState(),
+ onPlayerChange = viewModel::onPlayerNameChange,
+ onHostChange = viewModel::onHostChange,
+ onPortChange = viewModel::onPortChange,
+ onConnect = viewModel::connect,
+ onDisconnect = viewModel::disconnect,
+ )
+ }
+
+ AnimatedContent(
+ modifier = Modifier.size(size = 64.dp),
+ targetState = viewModel.isLoading.value,
+ transitionSpec = { fadeIn() togetherWith fadeOut() },
+ ) {
+ when (it) {
+ true -> CircularProgressIndicator()
+ else -> Box(modifier = Modifier)
+ }
+ }
+
+ LaunchedEffect(Unit) {
+ viewModel.message.collect {
+ snack.showSnackbar(
+ message = it,
+ duration = SnackbarDuration.Short,
+ )
+ }
+ }
+
+ ErrorSnack(
+ error = viewModel.networkError,
+ )
+ }
+}
+
@Composable
private fun NetworkContent(
modifier: Modifier = Modifier,
scrollState: ScrollState = rememberScrollState(),
+ paddingValues: PaddingValues = PaddingValues(),
player: State,
- onBack: () -> Unit,
onPlayerChange: (String) -> Unit,
onHostChange: (String) -> Unit,
onPortChange: (String) -> Unit,
onConnect: () -> Unit,
onDisconnect: () -> Unit,
) {
- Scaffold(
- modifier = modifier,
- topBar = {
- TopAppBar(
- title = {
- Text(
- overflow = TextOverflow.Ellipsis,
- maxLines = 1,
- text = stringResource(Res.string.network__title),
- )
- },
- navigationIcon = {
- IconButton(
- onClick = onBack,
- ) {
- Icon(
- imageVector = Icons.AutoMirrored.Filled.ArrowBack,
- contentDescription = null,
- )
- }
- }
+ Column(
+ modifier = Modifier
+ .verticalScroll(state = scrollState)
+ .padding(paddingValues)
+ .padding(all = 16.dp)
+ .then(other = modifier),
+ ) {
+ TextField(
+ modifier = Modifier.fillMaxWidth(),
+ singleLine = true,
+ enabled = player.value.enableFields,
+ label = { Text(text = stringResource(Res.string.network__player_name__label)) },
+ onValueChange = { onPlayerChange(it) },
+ value = player.value.player,
+ )
+
+ Spacer(
+ modifier = Modifier.height(16.dp),
+ )
+
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
+ ) {
+ TextField(
+ modifier = Modifier.weight(1f),
+ singleLine = true,
+ enabled = player.value.enableFields,
+ label = { Text(text = stringResource(Res.string.network__host__label)) },
+ onValueChange = { onHostChange(it) },
+ value = player.value.host,
+ )
+ TextField(
+ modifier = Modifier.width(100.dp),
+ singleLine = true,
+ enabled = player.value.enableFields,
+ label = { Text(text = stringResource(Res.string.network__port__label)) },
+ onValueChange = { onPortChange(it) },
+ value = player.value.port,
)
- },
- content = { paddingValues ->
- Column(
- modifier = Modifier
- .verticalScroll(state = scrollState)
- .padding(paddingValues)
- .padding(all = 16.dp),
- ) {
- TextField(
- modifier = Modifier.fillMaxWidth(),
- singleLine = true,
- enabled = player.value.enableFields,
- label = { Text(text = stringResource(Res.string.network__player_name__label)) },
- onValueChange = { onPlayerChange(it) },
- value = player.value.player,
- )
-
- Spacer(
- modifier = Modifier.height(16.dp),
- )
-
- Row(
- horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
- ) {
- TextField(
- modifier = Modifier.weight(1f),
- singleLine = true,
- enabled = player.value.enableFields,
- label = { Text(text = stringResource(Res.string.network__host__label)) },
- onValueChange = { onHostChange(it) },
- value = player.value.host,
- )
- TextField(
- modifier = Modifier.width(100.dp),
- singleLine = true,
- enabled = player.value.enableFields,
- label = { Text(text = stringResource(Res.string.network__port__label)) },
- onValueChange = { onPortChange(it) },
- value = player.value.port,
- )
- }
-
- TextButton(
- enabled = player.value.enableActions,
- onClick = onConnect,
- ) {
- Text(text = stringResource(Res.string.network__socket__connect_action))
- }
-
- TextButton(
- enabled = player.value.enableCancel,
- onClick = onDisconnect,
- ) {
- Text(text = stringResource(Res.string.network__socket__disconnect_action))
- }
- }
}
- )
+
+ TextButton(
+ enabled = player.value.enableActions,
+ onClick = onConnect,
+ ) {
+ Text(text = stringResource(Res.string.network__socket__connect_action))
+ }
+
+ TextButton(
+ enabled = player.value.enableCancel,
+ onClick = onDisconnect,
+ ) {
+ Text(text = stringResource(Res.string.network__socket__disconnect_action))
+ }
+ }
}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/network/NetworkViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/network/NetworkViewModel.kt
index 9f44169..78418fb 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/network/NetworkViewModel.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/network/NetworkViewModel.kt
@@ -9,6 +9,7 @@ import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
import com.pixelized.desktop.lwa.ui.composable.blur.BlurContentController
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackUio
import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -22,8 +23,8 @@ class NetworkViewModel(
private val factory: NetworkFactory,
) : ViewModel() {
private val settings = settingsRepository.settings()
- private val host = mutableStateOf(settings.host)
- private val port = mutableStateOf(settings.port)
+ private val hostFlow = MutableStateFlow(settings.host)
+ private val portFlow = MutableStateFlow(settings.port)
private val _networkError = MutableSharedFlow()
val networkError: SharedFlow get() = _networkError
@@ -34,17 +35,19 @@ class NetworkViewModel(
private val _isLoading = mutableStateOf(false)
val isLoading: State get() = _isLoading
- val controller: BlurContentController = BlurContentController()
+ val blurController: BlurContentController = BlurContentController()
val network: StateFlow = combine(
settingsRepository.settingsFlow(),
networkRepository.status,
- ) { settings, status ->
+ hostFlow,
+ portFlow,
+ ) { settings, status, host, port ->
factory.convertToUio(
player = settings.playerName,
status = status,
- host = host.value,
- port = port.value,
+ host = host,
+ port = port,
)
}.stateIn(
scope = viewModelScope,
@@ -61,36 +64,36 @@ class NetworkViewModel(
}
fun onPortChange(port: String) {
- this.port.value = port.toIntOrNull() ?: settings.port
+ this.portFlow.value = port.toIntOrNull() ?: settings.port
}
fun onHostChange(host: String) {
- this.host.value = host
+ this.hostFlow.value = host
}
fun connect() {
- controller.show()
+ blurController.show()
_isLoading.value = true
- if (settings.host != host.value || settings.port != port.value) {
+ if (settings.host != hostFlow.value || settings.port != portFlow.value) {
settingsRepository.update(
settings = settings.copy(
- host = host.value,
- port = port.value
+ host = hostFlow.value,
+ port = portFlow.value
)
)
}
networkRepository.connect(
- host = host.value,
- port = port.value,
+ host = hostFlow.value,
+ port = portFlow.value,
onConnect = {
_isLoading.value = false
- controller.hide()
+ blurController.hide()
},
onFailure = {
_isLoading.value = false
- controller.hide()
+ blurController.hide()
viewModelScope.launch {
_networkError.emit(ErrorSnackUio(it))
}
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/roll/RollViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/roll/RollViewModel.kt
index 093c6f8..2287c75 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/roll/RollViewModel.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/roll/RollViewModel.kt
@@ -8,6 +8,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
+import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.CharacterSheetPageUio
import com.pixelized.desktop.lwa.ui.screen.roll.DifficultyUio.Difficulty
import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
@@ -33,6 +34,7 @@ import org.jetbrains.compose.resources.getString
class RollViewModel(
private val characterSheetRepository: CharacterSheetRepository,
+ private val settingsRepository: SettingsRepository,
private val skillComputation: ExpressionUseCase,
private val skillStepUseCase: SkillStepUseCase,
private val networkRepository: NetworkRepository,
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/rollhistory/RollHistoryItem.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/rollhistory/RollHistoryItem.kt
index 8689a5e..88f2abb 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/rollhistory/RollHistoryItem.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/rollhistory/RollHistoryItem.kt
@@ -42,7 +42,7 @@ fun RollHistoryItem(
modifier = Modifier.weight(1f)
) {
Row(
- horizontalArrangement = Arrangement.spacedBy(4.dp),
+ horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
Text(
modifier = Modifier.alignByBaseline(),
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/rollhistory/RollHistoryPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/rollhistory/RollHistoryPage.kt
index a295e1d..420aea3 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/rollhistory/RollHistoryPage.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/rollhistory/RollHistoryPage.kt
@@ -13,6 +13,7 @@ import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
+import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@@ -26,12 +27,13 @@ import org.koin.compose.viewmodel.koinViewModel
fun RollHistoryPage(
viewModel: RollHistoryViewModel = koinViewModel(),
) {
+ val rolls = viewModel.rolls.collectAsState()
Surface(
modifier = Modifier.fillMaxSize(),
) {
RollHistoryContent(
modifier = Modifier.fillMaxSize(),
- rolls = viewModel.rolls,
+ rolls = rolls,
)
}
}
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/rollhistory/RollHistoryViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/rollhistory/RollHistoryViewModel.kt
index 3bcd53b..265ef37 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/rollhistory/RollHistoryViewModel.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/rollhistory/RollHistoryViewModel.kt
@@ -1,47 +1,44 @@
package com.pixelized.desktop.lwa.ui.screen.rollhistory
-import androidx.compose.runtime.State
-import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import com.pixelized.desktop.lwa.repository.campaign.model.CharacterSheetPreview
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
-import com.pixelized.shared.lwa.model.characterSheet.CharacterSheet
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.flow.stateIn
class RollHistoryViewModel(
- private val characterRepository: CharacterSheetRepository,
- private val rollRepository: RollHistoryRepository,
+ characterRepository: CharacterSheetRepository,
+ rollRepository: RollHistoryRepository,
) : ViewModel() {
- private val _rolls = mutableStateOf((emptyList()))
- val rolls: State> get() = _rolls
-
- init {
- viewModelScope.launch {
- combine(
- characterRepository.characterSheetPreviewFlow,
- rollRepository.rolls,
- ) { sheets: List, content ->
- _rolls.value.toMutableList().apply {
- val name = sheets.firstOrNull { it.id == content.characterId }?.name ?: ""
- add(
- index = 0,
- element = RollHistoryItemUio(
- character = name,
- skillLabel = content.skillLabel,
- rollDifficulty = content.rollDifficulty,
- resultLabel = content.resultLabel,
- rollValue = content.rollValue,
- rollSuccessLimit = content.rollSuccessLimit,
- )
- )
- }
- }.collect { content ->
- _rolls.value = content
- }
+ private var _rolls: List = emptyList()
+ val rolls = combine(
+ characterRepository.characterSheetPreviewFlow,
+ rollRepository.rolls,
+ ) { sheets, message ->
+ val name = sheets.firstOrNull { it.id == message.characterId }?.name ?: ""
+ val roll = RollHistoryItemUio(
+ character = name,
+ skillLabel = message.skillLabel,
+ rollDifficulty = message.rollDifficulty,
+ resultLabel = message.resultLabel,
+ rollValue = message.rollValue,
+ rollSuccessLimit = message.rollSuccessLimit,
+ )
+ _rolls = _rolls.toMutableList().also {
+ if (it.size == MAX_ROLL_HISTORY_SIZE) it.removeAt(index = it.lastIndex)
+ it.add(index = 0, element = roll)
}
+ _rolls
+ }.stateIn(
+ scope = viewModelScope,
+ started = SharingStarted.Eagerly,
+ initialValue = _rolls,
+ )
+
+ companion object {
+ const val MAX_ROLL_HISTORY_SIZE = 100
}
}
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 221c87d..b0e746f 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -7,8 +7,8 @@ androidx-lifecycle = "2.8.3"
androidx-navigation = "2.8.0-alpha10"
ktor = "3.0.1"
koin = "4.0.0"
-turtle = "0.5.0"
-logback = "1.5.11"
+turtle = "0.10.0"
+logback = "1.5.17"
coil = "3.1.0"
[plugins]
@@ -47,6 +47,7 @@ ktor-server-core = { group = 'io.ktor', name = "ktor-server-core", version.ref =
ktor-server-netty = { group = 'io.ktor', name = "ktor-server-netty", version.ref = "ktor" }
ktor-server-websockets = { group = 'io.ktor', name = "ktor-server-websockets", version.ref = "ktor" }
ktor-server-negotiation = { group = 'io.ktor', name = 'ktor-server-content-negotiation', version.ref = "ktor" }
-
+# Utilitary run command line.
turtle = { group = "com.lordcodes.turtle", name = "turtle", version.ref = "turtle" }
+# Utilitary logging use by Ktor.
logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
\ No newline at end of file