Change the access to some old screen from the new UI.
This commit is contained in:
parent
1bf0cb7c0a
commit
3941c5dd76
25 changed files with 657 additions and 450 deletions
|
|
@ -42,6 +42,8 @@ kotlin {
|
|||
implementation(libs.ktor.client.negotiation)
|
||||
// shell
|
||||
implementation(libs.turtle)
|
||||
// log
|
||||
implementation(libs.logback)
|
||||
}
|
||||
|
||||
commonTest.dependencies {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:pathData="m370,880 l-16,-128q-13,-5 -24.5,-12T307,725l-119,50L78,585l103,-78q-1,-7 -1,-13.5v-27q0,-6.5 1,-13.5L78,375l110,-190 119,50q11,-8 23,-15t24,-12l16,-128h220l16,128q13,5 24.5,12t22.5,15l119,-50 110,190 -103,78q1,7 1,13.5v27q0,6.5 -2,13.5l103,78 -110,190 -118,-50q-11,8 -23,15t-24,12L590,880L370,880ZM440,800h79l14,-106q31,-8 57.5,-23.5T639,633l99,41 39,-68 -86,-65q5,-14 7,-29.5t2,-31.5q0,-16 -2,-31.5t-7,-29.5l86,-65 -39,-68 -99,42q-22,-23 -48.5,-38.5T533,266l-13,-106h-79l-14,106q-31,8 -57.5,23.5T321,327l-99,-41 -39,68 86,64q-5,15 -7,30t-2,32q0,16 2,31t7,30l-86,65 39,68 99,-42q22,23 48.5,38.5T427,694l13,106ZM482,620q58,0 99,-41t41,-99q0,-58 -41,-99t-99,-41q-59,0 -99.5,41T342,480q0,58 40.5,99t99.5,41ZM480,480Z"
|
||||
android:fillColor="#5f6368"/>
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:pathData="M120,720q-33,0 -56.5,-23.5T40,640q0,-33 23.5,-56.5T120,560h10.5q4.5,0 9.5,2l182,-182q-2,-5 -2,-9.5L320,360q0,-33 23.5,-56.5T400,280q33,0 56.5,23.5T480,360q0,2 -2,20l102,102q5,-2 9.5,-2h21q4.5,0 9.5,2l142,-142q-2,-5 -2,-9.5L760,320q0,-33 23.5,-56.5T840,240q33,0 56.5,23.5T920,320q0,33 -23.5,56.5T840,400h-10.5q-4.5,0 -9.5,-2L678,540q2,5 2,9.5v10.5q0,33 -23.5,56.5T600,640q-33,0 -56.5,-23.5T520,560v-10.5q0,-4.5 2,-9.5L420,438q-5,2 -9.5,2L400,440q-2,0 -20,-2L198,620q2,5 2,9.5v10.5q0,33 -23.5,56.5T120,720Z"
|
||||
android:fillColor="#5f6368"/>
|
||||
</vector>
|
||||
|
|
@ -7,10 +7,9 @@
|
|||
|
||||
<string name="main_page__create_action">Créer une feuille de personnage</string>
|
||||
<string name="main_page__open_save_directory">Ouvrir le dossier de sauvegarde</string>
|
||||
<string name="main_page__roll_history_action">Consulter l'historique des lancers</string>
|
||||
<string name="main_page__roll_history_action">Historique des lancers</string>
|
||||
<string name="main_page__network_action">Configuration de la table</string>
|
||||
|
||||
|
||||
<string name="roll_page__roll__label">Jet de :</string>
|
||||
<string name="roll_page__roll__success_label">Réussite si lancer inférieur ou égal à : %1$s</string>
|
||||
<string name="roll_page__critical_success">Réussite critique</string>
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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<HttpClientEngine> {
|
||||
OkHttp.create()
|
||||
}
|
||||
single {
|
||||
HttpClient(
|
||||
engine = get()
|
||||
engine = OkHttp.create(),
|
||||
) {
|
||||
install(WebSockets) {
|
||||
pingIntervalMillis = 20_000
|
||||
|
|
|
|||
|
|
@ -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<Message>()
|
||||
private val incomingMessageBuffer = MutableSharedFlow<Message>()
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ suspend fun HttpClient.connectWebSocket(
|
|||
) {
|
||||
webSocket(
|
||||
method = HttpMethod.Get,
|
||||
host = host,
|
||||
host = host.removePrefix("http://"),
|
||||
port = port,
|
||||
path = "/ws",
|
||||
block = block,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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://")}"
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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<String> = 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String>,
|
||||
isOverflowMenuOpen: State<Boolean>,
|
||||
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",
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -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<NetworkPageUio>,
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ErrorSnackUio>()
|
||||
val networkError: SharedFlow<ErrorSnackUio> get() = _networkError
|
||||
|
|
@ -34,17 +35,19 @@ class NetworkViewModel(
|
|||
private val _isLoading = mutableStateOf(false)
|
||||
val isLoading: State<Boolean> get() = _isLoading
|
||||
|
||||
val controller: BlurContentController = BlurContentController()
|
||||
val blurController: BlurContentController = BlurContentController()
|
||||
|
||||
val network: StateFlow<NetworkPageUio> = 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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<RollHistoryItemUio>()))
|
||||
val rolls: State<List<RollHistoryItemUio>> get() = _rolls
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
combine(
|
||||
characterRepository.characterSheetPreviewFlow,
|
||||
rollRepository.rolls,
|
||||
) { sheets: List<CharacterSheetPreview>, 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<RollHistoryItemUio> = 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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" }
|
||||
Loading…
Add table
Add a link
Reference in a new issue