Add roll sharing feature over the WebSocket.
This commit is contained in:
parent
0e5fee6771
commit
f92922c228
17 changed files with 476 additions and 183 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="main_page__create_action">Créer une feuille de personnage</string>
|
<string name="main_page__create_action">Créer une feuille de personnage</string>
|
||||||
<string name="main_page__network_action">Configuration réseau</string>
|
<string name="main_page__network_action">Configuration réseau</string>
|
||||||
|
<string name="main_page__roll_history_action">Consulter l'historique des lancés</string>
|
||||||
|
|
||||||
<string name="roll_page__critical_success">Réussite critique</string>
|
<string name="roll_page__critical_success">Réussite critique</string>
|
||||||
<string name="roll_page__special_success">Réussite spéciale</string>
|
<string name="roll_page__special_success">Réussite spéciale</string>
|
||||||
|
|
@ -81,4 +82,6 @@
|
||||||
<string name="network__socket__type_server">Serveur</string>
|
<string name="network__socket__type_server">Serveur</string>
|
||||||
<string name="network__socket__type_client">Client</string>
|
<string name="network__socket__type_client">Client</string>
|
||||||
<string name="network__socket__type_none">Aucun</string>
|
<string name="network__socket__type_none">Aucun</string>
|
||||||
|
|
||||||
|
<string name="roll_history__title">Historique des lancés</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -1,11 +1,16 @@
|
||||||
package com.pixelized.desktop.lwa
|
package com.pixelized.desktop.lwa
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.material.Scaffold
|
||||||
|
import androidx.compose.material.SnackbarHost
|
||||||
|
import androidx.compose.material.SnackbarHostState
|
||||||
import androidx.compose.material.Surface
|
import androidx.compose.material.Surface
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
@ -13,9 +18,19 @@ import androidx.compose.ui.window.ApplicationScope
|
||||||
import androidx.compose.ui.window.Window
|
import androidx.compose.ui.window.Window
|
||||||
import androidx.compose.ui.window.rememberWindowState
|
import androidx.compose.ui.window.rememberWindowState
|
||||||
import com.pixelized.desktop.lwa.navigation.MainNavHost
|
import com.pixelized.desktop.lwa.navigation.MainNavHost
|
||||||
|
import com.pixelized.desktop.lwa.navigation.destination.CharacterSheetDestination
|
||||||
|
import com.pixelized.desktop.lwa.navigation.destination.CharacterSheetEditDestination
|
||||||
|
import com.pixelized.desktop.lwa.screen.characterSheet.CharacterSheetMainNavHost
|
||||||
|
import com.pixelized.desktop.lwa.screen.main.CharacterUio
|
||||||
import com.pixelized.desktop.lwa.theme.LwaTheme
|
import com.pixelized.desktop.lwa.theme.LwaTheme
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__title
|
||||||
|
import org.jetbrains.compose.resources.stringResource
|
||||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||||
|
|
||||||
|
val LocalSnackHost = compositionLocalOf<SnackbarHostState> {
|
||||||
|
error("Local Snack Controller is not yet ready")
|
||||||
|
}
|
||||||
val LocalWindowController = compositionLocalOf<WindowController> {
|
val LocalWindowController = compositionLocalOf<WindowController> {
|
||||||
error("Local Window Controller is not yet ready")
|
error("Local Window Controller is not yet ready")
|
||||||
}
|
}
|
||||||
|
|
@ -24,23 +39,47 @@ val LocalWindowController = compositionLocalOf<WindowController> {
|
||||||
data class WindowController(
|
data class WindowController(
|
||||||
private val onCloseRequest: () -> Unit
|
private val onCloseRequest: () -> Unit
|
||||||
) {
|
) {
|
||||||
fun close() = onCloseRequest()
|
val sheet: State<Set<CharacterUio>> get() = _sheet
|
||||||
|
|
||||||
|
val create: State<Set<Int>> get() = _create
|
||||||
|
|
||||||
|
fun showCreateCharacterSheet() {
|
||||||
|
_create.value = _create.value.toMutableSet().apply { add(size) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hideCreateCharacterSheet(id: Int) {
|
||||||
|
_create.value = _create.value.toMutableSet().apply { remove(id) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showCharacterSheet(sheet: CharacterUio) {
|
||||||
|
_sheet.value = _sheet.value.toMutableSet().apply { add(sheet) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hideCharacterSheet(sheet: CharacterUio) {
|
||||||
|
_sheet.value = _sheet.value.toMutableSet().apply { remove(sheet) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun closeWindows() = onCloseRequest()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val _sheet = mutableStateOf<Set<CharacterUio>>(emptySet())
|
||||||
|
private val _create = mutableStateOf<Set<Int>>(emptySet())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview
|
@Preview
|
||||||
fun ApplicationScope.App() {
|
fun ApplicationScope.App() {
|
||||||
val controller = remember {
|
val controller = remember { WindowController(onCloseRequest = ::exitApplication) }
|
||||||
WindowController(
|
val snackHostState = remember { SnackbarHostState() }
|
||||||
onCloseRequest = ::exitApplication
|
|
||||||
)
|
|
||||||
}
|
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalWindowController provides controller,
|
LocalWindowController provides controller,
|
||||||
|
LocalSnackHost provides snackHostState,
|
||||||
) {
|
) {
|
||||||
Window(
|
Window(
|
||||||
onCloseRequest = {
|
onCloseRequest = {
|
||||||
controller.close()
|
controller.closeWindows()
|
||||||
},
|
},
|
||||||
state = rememberWindowState(
|
state = rememberWindowState(
|
||||||
width = 320.dp + 64.dp,
|
width = 320.dp + 64.dp,
|
||||||
|
|
@ -52,9 +91,86 @@ fun ApplicationScope.App() {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
MainNavHost()
|
Scaffold(
|
||||||
|
snackbarHost = {
|
||||||
|
SnackbarHost(
|
||||||
|
hostState = snackHostState,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content = {
|
||||||
|
MainNavHost()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
HandleCharacterSheet(
|
||||||
|
sheets = controller.sheet,
|
||||||
|
onCloseRequest = { controller.hideCharacterSheet(sheet = it) }
|
||||||
|
)
|
||||||
|
HandleCharacterSheetCreation(
|
||||||
|
sheets = controller.create,
|
||||||
|
onCloseRequest = { controller.hideCreateCharacterSheet(id = it) },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HandleCharacterSheet(
|
||||||
|
sheets: State<Set<CharacterUio>>,
|
||||||
|
onCloseRequest: (id: CharacterUio) -> Unit,
|
||||||
|
) {
|
||||||
|
sheets.value.forEach { sheet ->
|
||||||
|
val controller = remember {
|
||||||
|
WindowController(
|
||||||
|
onCloseRequest = { onCloseRequest(sheet) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalWindowController provides controller,
|
||||||
|
) {
|
||||||
|
Window(
|
||||||
|
onCloseRequest = { onCloseRequest(sheet) },
|
||||||
|
state = rememberWindowState(
|
||||||
|
width = 400.dp + 64.dp,
|
||||||
|
height = 900.dp,
|
||||||
|
),
|
||||||
|
title = sheet.name,
|
||||||
|
) {
|
||||||
|
CharacterSheetMainNavHost(
|
||||||
|
startDestination = CharacterSheetDestination.navigationRoute(id = sheet.id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HandleCharacterSheetCreation(
|
||||||
|
sheets: State<Set<Int>>,
|
||||||
|
onCloseRequest: (id: Int) -> Unit,
|
||||||
|
) {
|
||||||
|
sheets.value.forEach { sheet ->
|
||||||
|
val controller = remember {
|
||||||
|
WindowController(
|
||||||
|
onCloseRequest = { onCloseRequest(sheet) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalWindowController provides controller,
|
||||||
|
) {
|
||||||
|
Window(
|
||||||
|
onCloseRequest = { controller.closeWindows() },
|
||||||
|
state = rememberWindowState(
|
||||||
|
width = 400.dp + 64.dp,
|
||||||
|
height = 900.dp,
|
||||||
|
),
|
||||||
|
title = stringResource(Res.string.character_sheet_edit__title),
|
||||||
|
) {
|
||||||
|
CharacterSheetMainNavHost(
|
||||||
|
startDestination = CharacterSheetEditDestination.navigationRoute(id = null)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,6 +9,8 @@ import androidx.navigation.compose.rememberNavController
|
||||||
import com.pixelized.desktop.lwa.navigation.destination.MainDestination
|
import com.pixelized.desktop.lwa.navigation.destination.MainDestination
|
||||||
import com.pixelized.desktop.lwa.navigation.destination.composableMainPage
|
import com.pixelized.desktop.lwa.navigation.destination.composableMainPage
|
||||||
import com.pixelized.desktop.lwa.navigation.destination.composableNetworkPage
|
import com.pixelized.desktop.lwa.navigation.destination.composableNetworkPage
|
||||||
|
import com.pixelized.desktop.lwa.navigation.destination.composableRollHistory
|
||||||
|
import com.pixelized.desktop.lwa.screen.main.MainPageViewModel
|
||||||
|
|
||||||
val LocalScreenController = compositionLocalOf<NavHostController> {
|
val LocalScreenController = compositionLocalOf<NavHostController> {
|
||||||
error("MainNavHost controller is not yet ready")
|
error("MainNavHost controller is not yet ready")
|
||||||
|
|
@ -28,6 +30,7 @@ fun MainNavHost(
|
||||||
) {
|
) {
|
||||||
composableMainPage()
|
composableMainPage()
|
||||||
composableNetworkPage()
|
composableNetworkPage()
|
||||||
|
composableRollHistory()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import com.pixelized.desktop.lwa.screen.main.MainPage
|
import com.pixelized.desktop.lwa.screen.main.MainPage
|
||||||
|
import com.pixelized.desktop.lwa.screen.main.MainPageViewModel
|
||||||
|
|
||||||
object MainDestination {
|
object MainDestination {
|
||||||
private const val ROUTE = "main"
|
private const val ROUTE = "main"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.pixelized.desktop.lwa.navigation.destination
|
||||||
|
|
||||||
|
import androidx.navigation.NavGraphBuilder
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import com.pixelized.desktop.lwa.screen.rollhistory.RollHistoryPage
|
||||||
|
|
||||||
|
object RollHistoryDestination {
|
||||||
|
private const val ROUTE = "roll_history"
|
||||||
|
|
||||||
|
fun baseRoute() = ROUTE
|
||||||
|
fun navigationRoute() = ROUTE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun NavGraphBuilder.composableRollHistory() {
|
||||||
|
composable(
|
||||||
|
route = RollHistoryDestination.baseRoute()
|
||||||
|
) {
|
||||||
|
RollHistoryPage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun NavHostController.navigateToRollHistory() {
|
||||||
|
val route = RollHistoryDestination.navigationRoute()
|
||||||
|
navigate(route = route)
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import com.pixelized.desktop.lwa.repository.network.helper.client
|
||||||
import com.pixelized.desktop.lwa.repository.network.helper.connectWebSocket
|
import com.pixelized.desktop.lwa.repository.network.helper.connectWebSocket
|
||||||
import com.pixelized.desktop.lwa.repository.network.helper.server
|
import com.pixelized.desktop.lwa.repository.network.helper.server
|
||||||
import com.pixelized.desktop.lwa.repository.network.protocol.Message
|
import com.pixelized.desktop.lwa.repository.network.protocol.Message
|
||||||
|
import com.pixelized.desktop.lwa.repository.network.protocol.MessageContent
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.server.engine.EmbeddedServer
|
import io.ktor.server.engine.EmbeddedServer
|
||||||
import io.ktor.server.netty.NettyApplicationEngine
|
import io.ktor.server.netty.NettyApplicationEngine
|
||||||
|
|
@ -15,8 +16,8 @@ import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.channels.consumeEach
|
import kotlinx.coroutines.channels.consumeEach
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
|
@ -31,8 +32,9 @@ object NetworkRepository {
|
||||||
private var server: Server? = null
|
private var server: Server? = null
|
||||||
private var client: Client? = null
|
private var client: Client? = null
|
||||||
|
|
||||||
private val messageResponseFlow = MutableSharedFlow<String>()
|
private val outgoingMessageBuffer = MutableSharedFlow<Message>()
|
||||||
private val sharedFlow = messageResponseFlow.asSharedFlow()
|
private val incomingMessageBuffer = MutableSharedFlow<Message>()
|
||||||
|
val data: SharedFlow<Message> get() = incomingMessageBuffer
|
||||||
|
|
||||||
private val _player = MutableStateFlow("")
|
private val _player = MutableStateFlow("")
|
||||||
val player: StateFlow<String> get() = _player
|
val player: StateFlow<String> get() = _player
|
||||||
|
|
@ -57,17 +59,19 @@ object NetworkRepository {
|
||||||
println("Server launched")
|
println("Server launched")
|
||||||
|
|
||||||
val job = launch {
|
val job = launch {
|
||||||
sharedFlow.collect { message ->
|
// send local message to the clients
|
||||||
println("Broadcast: $message")
|
outgoingMessageBuffer.collect { message ->
|
||||||
send(Frame.Text(message))
|
send(Json.encodeToFrame(message = message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
|
// watching for clients incoming message
|
||||||
incoming.consumeEach { frame ->
|
incoming.consumeEach { frame ->
|
||||||
if (frame is Frame.Text) {
|
if (frame is Frame.Text) {
|
||||||
val receivedText = frame.readText()
|
val message = Json.decodeFromFrame(frame = frame)
|
||||||
messageResponseFlow.emit(receivedText)
|
incomingMessageBuffer.emit(message)
|
||||||
|
// broadcast to clients the message
|
||||||
|
outgoingMessageBuffer.emit(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onFailure { exception ->
|
}.onFailure { exception ->
|
||||||
|
|
@ -108,17 +112,17 @@ object NetworkRepository {
|
||||||
println("Client launched")
|
println("Client launched")
|
||||||
|
|
||||||
val job = launch {
|
val job = launch {
|
||||||
sharedFlow.collect { message ->
|
// send message to the server
|
||||||
println("Send: $message")
|
outgoingMessageBuffer.collect { message ->
|
||||||
send(Frame.Text(message))
|
send(Json.encodeToFrame(message = message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
|
// watching for server incoming message
|
||||||
incoming.consumeEach { frame ->
|
incoming.consumeEach { frame ->
|
||||||
if (frame is Frame.Text) {
|
if (frame is Frame.Text) {
|
||||||
val receivedText = frame.readText()
|
val message = Json.decodeFromFrame(frame = frame)
|
||||||
println("client received: $receivedText")
|
incomingMessageBuffer.emit(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.also {
|
}.also {
|
||||||
|
|
@ -144,15 +148,19 @@ object NetworkRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun share(
|
suspend fun share(
|
||||||
type: String,
|
content: MessageContent,
|
||||||
value: String,
|
|
||||||
) {
|
) {
|
||||||
if (status.value == Status.CONNECTED) {
|
if (status.value == Status.CONNECTED) {
|
||||||
scope.launch {
|
val message = Message(
|
||||||
val message = Message(from = player.value, type = type, value = value)
|
from = player.value,
|
||||||
val json = Json.encodeToJsonElement(message)
|
value = content,
|
||||||
messageResponseFlow.emit(json.toString())
|
)
|
||||||
|
// emit the message into the outgoing buffer
|
||||||
|
outgoingMessageBuffer.emit(message)
|
||||||
|
// emit the message into the incoming buffer IF we are the server
|
||||||
|
if (type.value == Type.SERVER) {
|
||||||
|
incomingMessageBuffer.emit(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -167,4 +175,14 @@ object NetworkRepository {
|
||||||
SERVER,
|
SERVER,
|
||||||
NONE,
|
NONE,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Json.decodeFromFrame(frame: Frame.Text): Message {
|
||||||
|
val json = frame.readText()
|
||||||
|
return decodeFromString<Message>(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Json.encodeToFrame(message: Message): Frame {
|
||||||
|
val json = encodeToJsonElement(message)
|
||||||
|
return Frame.Text(text = json.toString())
|
||||||
}
|
}
|
||||||
|
|
@ -3,8 +3,7 @@ package com.pixelized.desktop.lwa.repository.network.protocol
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Message(
|
data class Message(
|
||||||
val from: String,
|
val from: String,
|
||||||
val type: String,
|
val value: MessageContent,
|
||||||
val value: String,
|
|
||||||
)
|
)
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.pixelized.desktop.lwa.repository.network.protocol
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
sealed interface MessageContent
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.pixelized.desktop.lwa.repository.network.protocol
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class RollMessage(
|
||||||
|
val label: String,
|
||||||
|
val roll: Int,
|
||||||
|
): MessageContent
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.pixelized.desktop.lwa.repository.roll
|
||||||
|
|
||||||
|
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
||||||
|
import com.pixelized.desktop.lwa.repository.network.protocol.Message
|
||||||
|
import com.pixelized.desktop.lwa.repository.network.protocol.RollMessage
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.mapNotNull
|
||||||
|
import kotlinx.coroutines.flow.shareIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
object RollHistoryRepository {
|
||||||
|
private val scope = CoroutineScope(Dispatchers.IO)
|
||||||
|
private val network = NetworkRepository
|
||||||
|
|
||||||
|
val rolls: SharedFlow<Message> = network.data
|
||||||
|
.mapNotNull { it.takeIf { it.value is RollMessage } }
|
||||||
|
.shareIn(
|
||||||
|
scope = scope,
|
||||||
|
started = SharingStarted.Eagerly,
|
||||||
|
)
|
||||||
|
|
||||||
|
init {
|
||||||
|
scope.launch {
|
||||||
|
network.data.collect {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun share(
|
||||||
|
label: String,
|
||||||
|
roll: Int,
|
||||||
|
) {
|
||||||
|
network.share(
|
||||||
|
content = RollMessage(label = label, roll = roll)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,7 +24,6 @@ import androidx.compose.material.Surface
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.material.TopAppBar
|
import androidx.compose.material.TopAppBar
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
import androidx.compose.material.icons.filled.Edit
|
import androidx.compose.material.icons.filled.Edit
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -124,7 +123,7 @@ fun CharacterSheetPage(
|
||||||
scope.launch {
|
scope.launch {
|
||||||
viewModel.deleteCharacter(id = sheet.id)
|
viewModel.deleteCharacter(id = sheet.id)
|
||||||
if (screen.popBackStack().not()) {
|
if (screen.popBackStack().not()) {
|
||||||
window.close()
|
window.closeWindows()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -191,16 +190,6 @@ fun CharacterSheetPageContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
navigationIcon = {
|
|
||||||
IconButton(
|
|
||||||
onClick = onBack,
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
content = { paddingValues ->
|
content = { paddingValues ->
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ fun CharacterSheetEditPage(
|
||||||
scope.launch {
|
scope.launch {
|
||||||
viewModel.save()
|
viewModel.save()
|
||||||
if (screen.popBackStack().not()) {
|
if (screen.popBackStack().not()) {
|
||||||
window.close()
|
window.closeWindows()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package com.pixelized.desktop.lwa.screen.main
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
|
@ -27,16 +26,17 @@ import androidx.compose.ui.window.rememberWindowState
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.pixelized.desktop.lwa.LocalWindowController
|
import com.pixelized.desktop.lwa.LocalWindowController
|
||||||
import com.pixelized.desktop.lwa.WindowController
|
import com.pixelized.desktop.lwa.WindowController
|
||||||
import com.pixelized.desktop.lwa.composable.decoratedBox.DecoratedBox
|
|
||||||
import com.pixelized.desktop.lwa.navigation.LocalScreenController
|
import com.pixelized.desktop.lwa.navigation.LocalScreenController
|
||||||
import com.pixelized.desktop.lwa.navigation.destination.CharacterSheetDestination
|
import com.pixelized.desktop.lwa.navigation.destination.CharacterSheetDestination
|
||||||
import com.pixelized.desktop.lwa.navigation.destination.CharacterSheetEditDestination
|
import com.pixelized.desktop.lwa.navigation.destination.CharacterSheetEditDestination
|
||||||
import com.pixelized.desktop.lwa.navigation.destination.navigateToNetwork
|
import com.pixelized.desktop.lwa.navigation.destination.navigateToNetwork
|
||||||
|
import com.pixelized.desktop.lwa.navigation.destination.navigateToRollHistory
|
||||||
import com.pixelized.desktop.lwa.screen.characterSheet.CharacterSheetMainNavHost
|
import com.pixelized.desktop.lwa.screen.characterSheet.CharacterSheetMainNavHost
|
||||||
import lwacharactersheet.composeapp.generated.resources.Res
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__title
|
import lwacharactersheet.composeapp.generated.resources.character_sheet_edit__title
|
||||||
import lwacharactersheet.composeapp.generated.resources.main_page__create_action
|
import lwacharactersheet.composeapp.generated.resources.main_page__create_action
|
||||||
import lwacharactersheet.composeapp.generated.resources.main_page__network_action
|
import lwacharactersheet.composeapp.generated.resources.main_page__network_action
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.main_page__roll_history_action
|
||||||
import org.jetbrains.compose.resources.stringResource
|
import org.jetbrains.compose.resources.stringResource
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
|
|
@ -49,6 +49,7 @@ data class CharacterUio(
|
||||||
fun MainPage(
|
fun MainPage(
|
||||||
viewModel: MainPageViewModel = viewModel { MainPageViewModel() },
|
viewModel: MainPageViewModel = viewModel { MainPageViewModel() },
|
||||||
) {
|
) {
|
||||||
|
val window = LocalWindowController.current
|
||||||
val screen = LocalScreenController.current
|
val screen = LocalScreenController.current
|
||||||
|
|
||||||
Surface(
|
Surface(
|
||||||
|
|
@ -64,10 +65,13 @@ fun MainPage(
|
||||||
MainPageContent(
|
MainPageContent(
|
||||||
characters = viewModel.characters,
|
characters = viewModel.characters,
|
||||||
onCharacter = {
|
onCharacter = {
|
||||||
viewModel.showCharacterSheet(sheet = it)
|
window.showCharacterSheet(sheet = it)
|
||||||
},
|
},
|
||||||
onCreateCharacter = {
|
onCreateCharacter = {
|
||||||
viewModel.showCreateCharacterSheet()
|
window.showCreateCharacterSheet()
|
||||||
|
},
|
||||||
|
onRollHistory = {
|
||||||
|
screen.navigateToRollHistory()
|
||||||
},
|
},
|
||||||
onNetwork = {
|
onNetwork = {
|
||||||
screen.navigateToNetwork()
|
screen.navigateToNetwork()
|
||||||
|
|
@ -75,140 +79,72 @@ fun MainPage(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HandleCharacterSheet(
|
|
||||||
sheets = viewModel.sheet,
|
|
||||||
onCloseRequest = { viewModel.hideCharacterSheet(sheet = it) }
|
|
||||||
)
|
|
||||||
|
|
||||||
HandleCharacterSheetCreation(
|
|
||||||
sheets = viewModel.create,
|
|
||||||
onCloseRequest = { viewModel.hideCreateCharacterSheet(id = it) },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun HandleCharacterSheet(
|
|
||||||
sheets: State<Set<CharacterUio>>,
|
|
||||||
onCloseRequest: (id: CharacterUio) -> Unit,
|
|
||||||
) {
|
|
||||||
sheets.value.forEach { sheet ->
|
|
||||||
val controller = remember {
|
|
||||||
WindowController(
|
|
||||||
onCloseRequest = { onCloseRequest(sheet) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
CompositionLocalProvider(
|
|
||||||
LocalWindowController provides controller,
|
|
||||||
) {
|
|
||||||
Window(
|
|
||||||
onCloseRequest = { onCloseRequest(sheet) },
|
|
||||||
state = rememberWindowState(
|
|
||||||
width = 400.dp + 64.dp,
|
|
||||||
height = 900.dp,
|
|
||||||
),
|
|
||||||
title = sheet.name,
|
|
||||||
) {
|
|
||||||
CharacterSheetMainNavHost(
|
|
||||||
startDestination = CharacterSheetDestination.navigationRoute(id = sheet.id)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun HandleCharacterSheetCreation(
|
|
||||||
sheets: State<Set<Int>>,
|
|
||||||
onCloseRequest: (id: Int) -> Unit,
|
|
||||||
) {
|
|
||||||
sheets.value.forEach { sheet ->
|
|
||||||
val controller = remember {
|
|
||||||
WindowController(
|
|
||||||
onCloseRequest = { onCloseRequest(sheet) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
CompositionLocalProvider(
|
|
||||||
LocalWindowController provides controller,
|
|
||||||
) {
|
|
||||||
Window(
|
|
||||||
onCloseRequest = { controller.close() },
|
|
||||||
state = rememberWindowState(
|
|
||||||
width = 400.dp + 64.dp,
|
|
||||||
height = 900.dp,
|
|
||||||
),
|
|
||||||
title = stringResource(Res.string.character_sheet_edit__title),
|
|
||||||
) {
|
|
||||||
CharacterSheetMainNavHost(
|
|
||||||
startDestination = CharacterSheetEditDestination.navigationRoute(id = null)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainPageContent(
|
fun MainPageContent(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
characters: State<List<CharacterUio>>,
|
characters: State<List<CharacterUio>>,
|
||||||
onCharacter: (CharacterUio) -> Unit,
|
onCharacter: (CharacterUio) -> Unit,
|
||||||
onCreateCharacter: () -> Unit,
|
onCreateCharacter: () -> Unit,
|
||||||
|
onRollHistory: () -> Unit,
|
||||||
onNetwork: () -> Unit,
|
onNetwork: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
) {
|
) {
|
||||||
Spacer(
|
Column {
|
||||||
modifier = Modifier.weight(weight = 1f)
|
characters.value.forEach { sheet ->
|
||||||
)
|
|
||||||
DecoratedBox {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.padding(horizontal = 8.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(space = 32.dp),
|
|
||||||
) {
|
|
||||||
Column {
|
|
||||||
characters.value.forEach { sheet ->
|
|
||||||
TextButton(
|
|
||||||
onClick = { onCharacter(sheet) },
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
textAlign = TextAlign.Start,
|
|
||||||
maxLines = 1,
|
|
||||||
text = sheet.name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = { onCreateCharacter() },
|
onClick = { onCharacter(sheet) },
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
textAlign = TextAlign.Start,
|
textAlign = TextAlign.Start,
|
||||||
text = stringResource(Res.string.main_page__create_action),
|
maxLines = 1,
|
||||||
|
text = sheet.name,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer(
|
|
||||||
modifier = Modifier.weight(weight = 1f)
|
|
||||||
)
|
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = { onNetwork() },
|
onClick = onCreateCharacter,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
textAlign = TextAlign.Start,
|
textAlign = TextAlign.Start,
|
||||||
text = stringResource(Res.string.main_page__network_action),
|
text = stringResource(Res.string.main_page__create_action),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
TextButton(
|
||||||
|
onClick = onRollHistory,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
textAlign = TextAlign.Start,
|
||||||
|
text = stringResource(Res.string.main_page__roll_history_action),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TextButton(
|
||||||
|
onClick = onNetwork,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
textAlign = TextAlign.Start,
|
||||||
|
maxLines = 1,
|
||||||
|
text = stringResource(Res.string.main_page__network_action),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,6 @@ package com.pixelized.desktop.lwa.screen.main
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||||
import com.pixelized.desktop.lwa.utils.extention.collectAsState
|
import com.pixelized.desktop.lwa.utils.extention.collectAsState
|
||||||
|
|
@ -12,12 +11,6 @@ class MainPageViewModel : ViewModel() {
|
||||||
// using a variable to help with later injection.
|
// using a variable to help with later injection.
|
||||||
private val repository = CharacterSheetRepository
|
private val repository = CharacterSheetRepository
|
||||||
|
|
||||||
private val _sheet = mutableStateOf<Set<CharacterUio>>(emptySet())
|
|
||||||
val sheet: State<Set<CharacterUio>> get() = _sheet
|
|
||||||
|
|
||||||
private val _create = mutableStateOf<Set<Int>>(emptySet())
|
|
||||||
val create: State<Set<Int>> get() = _create
|
|
||||||
|
|
||||||
val characters: State<List<CharacterUio>>
|
val characters: State<List<CharacterUio>>
|
||||||
@Composable
|
@Composable
|
||||||
@Stable
|
@Stable
|
||||||
|
|
@ -31,20 +24,4 @@ class MainPageViewModel : ViewModel() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showCreateCharacterSheet() {
|
|
||||||
_create.value = _create.value.toMutableSet().apply { add(size) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun hideCreateCharacterSheet(id: Int) {
|
|
||||||
_create.value = _create.value.toMutableSet().apply { remove(id) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun showCharacterSheet(sheet: CharacterUio) {
|
|
||||||
_sheet.value = _sheet.value.toMutableSet().apply { add(sheet) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun hideCharacterSheet(sheet: CharacterUio) {
|
|
||||||
_sheet.value = _sheet.value.toMutableSet().apply { remove(sheet) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -10,7 +10,7 @@ import com.pixelized.desktop.lwa.business.RollUseCase
|
||||||
import com.pixelized.desktop.lwa.business.SkillStepUseCase
|
import com.pixelized.desktop.lwa.business.SkillStepUseCase
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheet
|
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheet
|
||||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
|
||||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
|
import com.pixelized.desktop.lwa.repository.roll.RollHistoryRepository
|
||||||
import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio
|
import com.pixelized.desktop.lwa.screen.characterSheet.detail.CharacterSheetPageUio
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
|
@ -26,7 +26,7 @@ import lwacharactersheet.composeapp.generated.resources.roll_page__success
|
||||||
import org.jetbrains.compose.resources.getString
|
import org.jetbrains.compose.resources.getString
|
||||||
|
|
||||||
class RollViewModel : ViewModel() {
|
class RollViewModel : ViewModel() {
|
||||||
private val network = NetworkRepository
|
private val repository = RollHistoryRepository
|
||||||
|
|
||||||
private val _roll = mutableStateOf(RollUio(label = "", value = 0))
|
private val _roll = mutableStateOf(RollUio(label = "", value = 0))
|
||||||
val roll: State<RollUio> get() = _roll
|
val roll: State<RollUio> get() = _roll
|
||||||
|
|
@ -138,13 +138,11 @@ class RollViewModel : ViewModel() {
|
||||||
value = roll,
|
value = roll,
|
||||||
)
|
)
|
||||||
|
|
||||||
share(roll = roll)
|
launch {
|
||||||
|
repository.share(label = _roll.value.label, roll = roll)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun share(roll: Int) {
|
|
||||||
network.share(type = "roll", value = "$roll")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
package com.pixelized.desktop.lwa.screen.rollhistory
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.IconButton
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Scaffold
|
||||||
|
import androidx.compose.material.Surface
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.material.TopAppBar
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.pixelized.desktop.lwa.navigation.LocalScreenController
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.roll_history__title
|
||||||
|
import org.jetbrains.compose.resources.stringResource
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class RollItemUio(
|
||||||
|
val from: String,
|
||||||
|
val label: String,
|
||||||
|
val roll: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RollHistoryPage(
|
||||||
|
viewModel: RollHistoryViewModel = viewModel { RollHistoryViewModel() }
|
||||||
|
) {
|
||||||
|
val screen = LocalScreenController.current
|
||||||
|
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
RollHistoryContent(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
rolls = viewModel.rolls,
|
||||||
|
onBack = {
|
||||||
|
screen.popBackStack()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RollHistoryContent(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
rolls: State<List<RollItemUio>>,
|
||||||
|
onBack: () -> Unit,
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
modifier = modifier,
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
text = stringResource(Res.string.roll_history__title),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(
|
||||||
|
onClick = onBack,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content = {
|
||||||
|
val state = rememberLazyListState()
|
||||||
|
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
state = state,
|
||||||
|
reverseLayout = true,
|
||||||
|
contentPadding = PaddingValues(all = 24.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
|
||||||
|
) {
|
||||||
|
items(items = rolls.value) {
|
||||||
|
RollItem(
|
||||||
|
roll = it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun RollItem(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
roll: RollItemUio,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.typography.body1,
|
||||||
|
fontWeight = FontWeight.Thin,
|
||||||
|
text = roll.from,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.typography.body1,
|
||||||
|
fontWeight = FontWeight.Light,
|
||||||
|
text = roll.label,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.alignByBaseline(),
|
||||||
|
style = MaterialTheme.typography.body1,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
text = "${roll.roll}",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.pixelized.desktop.lwa.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.network.protocol.RollMessage
|
||||||
|
import com.pixelized.desktop.lwa.repository.roll.RollHistoryRepository
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class RollHistoryViewModel : ViewModel() {
|
||||||
|
|
||||||
|
private val repository = RollHistoryRepository
|
||||||
|
|
||||||
|
private val _rolls = mutableStateOf((emptyList<RollItemUio>()))
|
||||||
|
val rolls: State<List<RollItemUio>> get() = _rolls
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
repository.rolls.collect {
|
||||||
|
(it.value as? RollMessage)?.let { content ->
|
||||||
|
_rolls.value = _rolls.value.toMutableList().apply {
|
||||||
|
add(
|
||||||
|
index = 0,
|
||||||
|
element = RollItemUio(
|
||||||
|
from = it.from,
|
||||||
|
label = content.label,
|
||||||
|
roll = content.roll
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue