Add roll values to the new UI
This commit is contained in:
		
							parent
							
								
									6385d4c8bd
								
							
						
					
					
						commit
						3c8eecdab5
					
				
					 16 changed files with 262 additions and 53 deletions
				
			
		| 
						 | 
					@ -8,3 +8,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Kotlinx coroutines rules seems to be outdated with the latest version of Kotlin and Proguard
 | 
					# Kotlinx coroutines rules seems to be outdated with the latest version of Kotlin and Proguard
 | 
				
			||||||
-keep class kotlinx.coroutines.** { *; }
 | 
					-keep class kotlinx.coroutines.** { *; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# OkHttp comming from COIL.
 | 
				
			||||||
 | 
					-dontwarn okhttp3.internal.platform.**
 | 
				
			||||||
| 
						 | 
					@ -41,6 +41,7 @@ import com.pixelized.desktop.lwa.ui.navigation.window.destination.RollHistoryWin
 | 
				
			||||||
import com.pixelized.desktop.lwa.ui.navigation.window.rememberMaxWindowHeight
 | 
					import com.pixelized.desktop.lwa.ui.navigation.window.rememberMaxWindowHeight
 | 
				
			||||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
 | 
					import com.pixelized.desktop.lwa.repository.network.NetworkRepository
 | 
				
			||||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository.Status
 | 
					import com.pixelized.desktop.lwa.repository.network.NetworkRepository.Status
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.navigation.screen.MainNavHost
 | 
				
			||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.CampaignScreen
 | 
					import com.pixelized.desktop.lwa.ui.screen.campaign.player.CampaignScreen
 | 
				
			||||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.CharacterSheetMainNavHost
 | 
					import com.pixelized.desktop.lwa.ui.screen.characterSheet.CharacterSheetMainNavHost
 | 
				
			||||||
import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryPage
 | 
					import com.pixelized.desktop.lwa.ui.screen.rollhistory.RollHistoryPage
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ import com.pixelized.desktop.lwa.utils.extention.decodeFromFrame
 | 
				
			||||||
import com.pixelized.desktop.lwa.utils.extention.encodeToFrame
 | 
					import com.pixelized.desktop.lwa.utils.extention.encodeToFrame
 | 
				
			||||||
import com.pixelized.server.lwa.SERVER_PORT
 | 
					import com.pixelized.server.lwa.SERVER_PORT
 | 
				
			||||||
import com.pixelized.server.lwa.protocol.Message
 | 
					import com.pixelized.server.lwa.protocol.Message
 | 
				
			||||||
import com.pixelized.server.lwa.protocol.MessageContent
 | 
					import com.pixelized.server.lwa.protocol.MessageType
 | 
				
			||||||
import io.ktor.client.HttpClient
 | 
					import io.ktor.client.HttpClient
 | 
				
			||||||
import io.ktor.websocket.Frame
 | 
					import io.ktor.websocket.Frame
 | 
				
			||||||
import kotlinx.coroutines.CoroutineScope
 | 
					import kotlinx.coroutines.CoroutineScope
 | 
				
			||||||
| 
						 | 
					@ -99,11 +99,13 @@ class NetworkRepository(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    suspend fun share(
 | 
					    suspend fun share(
 | 
				
			||||||
        playerName: String = settingsRepository.settings().playerName,
 | 
					        playerName: String = settingsRepository.settings().playerName,
 | 
				
			||||||
        content: MessageContent,
 | 
					        type: MessageType,
 | 
				
			||||||
 | 
					        content: String,
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        if (status.value == Status.CONNECTED) {
 | 
					        if (status.value == Status.CONNECTED) {
 | 
				
			||||||
            val message = Message(
 | 
					            val message = Message(
 | 
				
			||||||
                from = playerName,
 | 
					                from = playerName,
 | 
				
			||||||
 | 
					                type = type,
 | 
				
			||||||
                value = content,
 | 
					                value = content,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            // emit the message into the outgoing buffer
 | 
					            // emit the message into the outgoing buffer
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,23 +1,28 @@
 | 
				
			||||||
package com.pixelized.desktop.lwa.repository.roll_history
 | 
					package com.pixelized.desktop.lwa.repository.roll_history
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
 | 
					import com.pixelized.desktop.lwa.repository.network.NetworkRepository
 | 
				
			||||||
import com.pixelized.server.lwa.protocol.Message
 | 
					import com.pixelized.server.lwa.protocol.MessageType
 | 
				
			||||||
import com.pixelized.server.lwa.protocol.RollMessage
 | 
					import com.pixelized.server.lwa.protocol.roll.RollMessage
 | 
				
			||||||
import kotlinx.coroutines.CoroutineScope
 | 
					import kotlinx.coroutines.CoroutineScope
 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
import kotlinx.coroutines.flow.SharedFlow
 | 
					import kotlinx.coroutines.flow.SharedFlow
 | 
				
			||||||
import kotlinx.coroutines.flow.SharingStarted
 | 
					import kotlinx.coroutines.flow.SharingStarted
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.map
 | 
				
			||||||
import kotlinx.coroutines.flow.mapNotNull
 | 
					import kotlinx.coroutines.flow.mapNotNull
 | 
				
			||||||
import kotlinx.coroutines.flow.shareIn
 | 
					import kotlinx.coroutines.flow.shareIn
 | 
				
			||||||
import kotlinx.coroutines.launch
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
 | 
					import kotlinx.serialization.encodeToString
 | 
				
			||||||
 | 
					import kotlinx.serialization.json.Json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RollHistoryRepository(
 | 
					class RollHistoryRepository(
 | 
				
			||||||
    private val network: NetworkRepository,
 | 
					    private val network: NetworkRepository,
 | 
				
			||||||
 | 
					    private val jsonFormatter: Json,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    private val scope = CoroutineScope(Dispatchers.IO)
 | 
					    private val scope = CoroutineScope(Dispatchers.IO)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val rolls: SharedFlow<Message> = network.data
 | 
					    val rolls: SharedFlow<RollMessage> = network.data
 | 
				
			||||||
        .mapNotNull { it.takeIf { it.value is RollMessage } }
 | 
					        .mapNotNull { it.takeIf { it.type == MessageType.Roll } }
 | 
				
			||||||
 | 
					        .map { jsonFormatter.decodeFromString<RollMessage>(it.value) }
 | 
				
			||||||
        .shareIn(
 | 
					        .shareIn(
 | 
				
			||||||
            scope = scope,
 | 
					            scope = scope,
 | 
				
			||||||
            started = SharingStarted.Eagerly,
 | 
					            started = SharingStarted.Eagerly,
 | 
				
			||||||
| 
						 | 
					@ -32,20 +37,24 @@ class RollHistoryRepository(
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    suspend fun share(
 | 
					    suspend fun share(
 | 
				
			||||||
 | 
					        characterId: String,
 | 
				
			||||||
        skillLabel: String,
 | 
					        skillLabel: String,
 | 
				
			||||||
        rollDifficulty: String?,
 | 
					        rollDifficulty: String?,
 | 
				
			||||||
        rollValue: Int,
 | 
					        rollValue: Int,
 | 
				
			||||||
        resultLabel: String?,
 | 
					        resultLabel: String?,
 | 
				
			||||||
        rollSuccessLimit: Int?,
 | 
					        rollSuccessLimit: Int?,
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        network.share(
 | 
					        val content = RollMessage(
 | 
				
			||||||
            content = RollMessage(
 | 
					            characterId = characterId,
 | 
				
			||||||
            skillLabel = skillLabel,
 | 
					            skillLabel = skillLabel,
 | 
				
			||||||
            rollDifficulty = rollDifficulty,
 | 
					            rollDifficulty = rollDifficulty,
 | 
				
			||||||
            rollValue = rollValue,
 | 
					            rollValue = rollValue,
 | 
				
			||||||
            resultLabel = resultLabel,
 | 
					            resultLabel = resultLabel,
 | 
				
			||||||
            rollSuccessLimit = rollSuccessLimit,
 | 
					            rollSuccessLimit = rollSuccessLimit,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        network.share(
 | 
				
			||||||
 | 
					            type = MessageType.Roll,
 | 
				
			||||||
 | 
					            content = jsonFormatter.encodeToString(content),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.height
 | 
				
			||||||
import androidx.compose.foundation.layout.padding
 | 
					import androidx.compose.foundation.layout.padding
 | 
				
			||||||
import androidx.compose.material.Surface
 | 
					import androidx.compose.material.Surface
 | 
				
			||||||
import androidx.compose.runtime.Composable
 | 
					import androidx.compose.runtime.Composable
 | 
				
			||||||
 | 
					import androidx.compose.runtime.LaunchedEffect
 | 
				
			||||||
import androidx.compose.runtime.State
 | 
					import androidx.compose.runtime.State
 | 
				
			||||||
import androidx.compose.runtime.mutableStateOf
 | 
					import androidx.compose.runtime.mutableStateOf
 | 
				
			||||||
import androidx.compose.runtime.remember
 | 
					import androidx.compose.runtime.remember
 | 
				
			||||||
| 
						 | 
					@ -30,13 +31,19 @@ import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDetai
 | 
				
			||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.CharacterDiminishedViewModel
 | 
					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.player.ribbon.PlayerRibbon
 | 
				
			||||||
import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.DiminishedStatDialog
 | 
					import com.pixelized.desktop.lwa.ui.screen.characterSheet.detail.dialog.DiminishedStatDialog
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.screen.network.NetworkViewModel
 | 
				
			||||||
import org.koin.compose.viewmodel.koinViewModel
 | 
					import org.koin.compose.viewmodel.koinViewModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Composable
 | 
					@Composable
 | 
				
			||||||
fun CampaignScreen(
 | 
					fun CampaignScreen(
 | 
				
			||||||
    characterDetailViewModel: CharacterDetailViewModel = koinViewModel(),
 | 
					    characterDetailViewModel: CharacterDetailViewModel = koinViewModel(),
 | 
				
			||||||
    dismissedViewModel: CharacterDiminishedViewModel = koinViewModel(),
 | 
					    dismissedViewModel: CharacterDiminishedViewModel = koinViewModel(),
 | 
				
			||||||
 | 
					    networkViewModel: NetworkViewModel = koinViewModel(),
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
 | 
					    LaunchedEffect(Unit) {
 | 
				
			||||||
 | 
					        networkViewModel.connect()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    KeyHandler {
 | 
					    KeyHandler {
 | 
				
			||||||
        when {
 | 
					        when {
 | 
				
			||||||
            it.type == KeyEventType.KeyUp && it.key == Key.Escape -> {
 | 
					            it.type == KeyEventType.KeyUp && it.key == Key.Escape -> {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,129 @@
 | 
				
			||||||
 | 
					package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.compose.animation.AnimatedContent
 | 
				
			||||||
 | 
					import androidx.compose.animation.SizeTransform
 | 
				
			||||||
 | 
					import androidx.compose.animation.core.Animatable
 | 
				
			||||||
 | 
					import androidx.compose.animation.core.AnimationVector1D
 | 
				
			||||||
 | 
					import androidx.compose.animation.core.Spring
 | 
				
			||||||
 | 
					import androidx.compose.animation.core.spring
 | 
				
			||||||
 | 
					import androidx.compose.animation.fadeIn
 | 
				
			||||||
 | 
					import androidx.compose.animation.fadeOut
 | 
				
			||||||
 | 
					import androidx.compose.animation.togetherWith
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.Box
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.size
 | 
				
			||||||
 | 
					import androidx.compose.material.Icon
 | 
				
			||||||
 | 
					import androidx.compose.material.MaterialTheme
 | 
				
			||||||
 | 
					import androidx.compose.material.Text
 | 
				
			||||||
 | 
					import androidx.compose.runtime.Composable
 | 
				
			||||||
 | 
					import androidx.compose.runtime.LaunchedEffect
 | 
				
			||||||
 | 
					import androidx.compose.runtime.Stable
 | 
				
			||||||
 | 
					import androidx.compose.runtime.State
 | 
				
			||||||
 | 
					import androidx.compose.runtime.remember
 | 
				
			||||||
 | 
					import androidx.compose.ui.Alignment
 | 
				
			||||||
 | 
					import androidx.compose.ui.Modifier
 | 
				
			||||||
 | 
					import androidx.compose.ui.geometry.Offset
 | 
				
			||||||
 | 
					import androidx.compose.ui.graphics.Shadow
 | 
				
			||||||
 | 
					import androidx.compose.ui.graphics.graphicsLayer
 | 
				
			||||||
 | 
					import androidx.compose.ui.text.style.TextAlign
 | 
				
			||||||
 | 
					import androidx.compose.ui.unit.dp
 | 
				
			||||||
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
 | 
					import lwacharactersheet.composeapp.generated.resources.Res
 | 
				
			||||||
 | 
					import lwacharactersheet.composeapp.generated.resources.ic_d20_24dp
 | 
				
			||||||
 | 
					import org.jetbrains.compose.resources.painterResource
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Stable
 | 
				
			||||||
 | 
					data class PlayerPortraitRollAnimation(
 | 
				
			||||||
 | 
					    val alpha: Animatable<Float, AnimationVector1D> = Animatable(0f),
 | 
				
			||||||
 | 
					    val rotation: Animatable<Float, AnimationVector1D> = Animatable(0f),
 | 
				
			||||||
 | 
					    val scale: Animatable<Float, AnimationVector1D> = Animatable(1f),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Composable
 | 
				
			||||||
 | 
					fun PlayerPortraitRoll(
 | 
				
			||||||
 | 
					    modifier: Modifier = Modifier,
 | 
				
			||||||
 | 
					    value: Int?,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    AnimatedContent(
 | 
				
			||||||
 | 
					        modifier = modifier.graphicsLayer { clip = false },
 | 
				
			||||||
 | 
					        targetState = value,
 | 
				
			||||||
 | 
					        transitionSpec = {
 | 
				
			||||||
 | 
					            val enter = fadeIn()
 | 
				
			||||||
 | 
					            val exit = fadeOut()
 | 
				
			||||||
 | 
					            enter togetherWith exit using SizeTransform(clip = false)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        val animation = diceIconAnimation(key = it ?: Unit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Box(
 | 
				
			||||||
 | 
					            modifier = Modifier.graphicsLayer {
 | 
				
			||||||
 | 
					                this.scaleX = animation.scale.value
 | 
				
			||||||
 | 
					                this.scaleY = animation.scale.value
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            contentAlignment = Alignment.Center,
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            when (it) {
 | 
				
			||||||
 | 
					                null -> Unit
 | 
				
			||||||
 | 
					                else -> {
 | 
				
			||||||
 | 
					                    Icon(
 | 
				
			||||||
 | 
					                        modifier = Modifier
 | 
				
			||||||
 | 
					                            .graphicsLayer {
 | 
				
			||||||
 | 
					                                this.alpha = 0.8f
 | 
				
			||||||
 | 
					                                this.rotationZ = animation.rotation.value
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            .size(48.dp),
 | 
				
			||||||
 | 
					                        painter = painterResource(Res.drawable.ic_d20_24dp),
 | 
				
			||||||
 | 
					                        tint = MaterialTheme.colors.primary,
 | 
				
			||||||
 | 
					                        contentDescription = null,
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                    Text(
 | 
				
			||||||
 | 
					                        style = MaterialTheme.typography.h5.copy(
 | 
				
			||||||
 | 
					                            shadow = Shadow(
 | 
				
			||||||
 | 
					                                color = MaterialTheme.colors.surface,
 | 
				
			||||||
 | 
					                                offset = Offset.Zero,
 | 
				
			||||||
 | 
					                                blurRadius = 8f,
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                        textAlign = TextAlign.Center,
 | 
				
			||||||
 | 
					                        color = MaterialTheme.colors.onSurface,
 | 
				
			||||||
 | 
					                        text = it.toString()
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Composable
 | 
				
			||||||
 | 
					private fun diceIconAnimation(key: Any = Unit): PlayerPortraitRollAnimation {
 | 
				
			||||||
 | 
					    val animation = remember(key) {
 | 
				
			||||||
 | 
					        PlayerPortraitRollAnimation()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    LaunchedEffect(key) {
 | 
				
			||||||
 | 
					        launch {
 | 
				
			||||||
 | 
					            animation.scale.animateTo(
 | 
				
			||||||
 | 
					                targetValue = 1.20f,
 | 
				
			||||||
 | 
					                animationSpec = spring(
 | 
				
			||||||
 | 
					                    dampingRatio = Spring.DampingRatioNoBouncy,
 | 
				
			||||||
 | 
					                    stiffness = 800f,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            animation.scale.animateTo(
 | 
				
			||||||
 | 
					                targetValue = 1f,
 | 
				
			||||||
 | 
					                animationSpec = spring(
 | 
				
			||||||
 | 
					                    dampingRatio = 0.28f,
 | 
				
			||||||
 | 
					                    stiffness = 800f,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        launch {
 | 
				
			||||||
 | 
					            animation.rotation.animateTo(
 | 
				
			||||||
 | 
					                targetValue = 360f * 3,
 | 
				
			||||||
 | 
					                animationSpec = spring(
 | 
				
			||||||
 | 
					                    dampingRatio = Spring.DampingRatioNoBouncy,
 | 
				
			||||||
 | 
					                    stiffness = Spring.StiffnessLow,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return animation
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,8 @@
 | 
				
			||||||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
 | 
					package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import androidx.compose.foundation.layout.Arrangement
 | 
					import androidx.compose.foundation.layout.Arrangement
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.Row
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.size
 | 
				
			||||||
import androidx.compose.foundation.lazy.LazyColumn
 | 
					import androidx.compose.foundation.lazy.LazyColumn
 | 
				
			||||||
import androidx.compose.foundation.lazy.items
 | 
					import androidx.compose.foundation.lazy.items
 | 
				
			||||||
import androidx.compose.runtime.Composable
 | 
					import androidx.compose.runtime.Composable
 | 
				
			||||||
| 
						 | 
					@ -25,10 +27,19 @@ fun PlayerRibbon(
 | 
				
			||||||
            items = characters.value,
 | 
					            items = characters.value,
 | 
				
			||||||
            key = { it.id },
 | 
					            key = { it.id },
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
 | 
					            Row {
 | 
				
			||||||
                PlayerPortrait(
 | 
					                PlayerPortrait(
 | 
				
			||||||
                    character = it,
 | 
					                    character = it,
 | 
				
			||||||
                    onCharacter = onCharacter,
 | 
					                    onCharacter = onCharacter,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
 | 
					                PlayerPortraitRoll(
 | 
				
			||||||
 | 
					                    modifier = Modifier.size(
 | 
				
			||||||
 | 
					                        width = 64.dp,
 | 
				
			||||||
 | 
					                        height = PlayerPortrait.Default.size.height
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    value = playerRibbonViewModel.roll(characterId = it.id).value,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,26 @@
 | 
				
			||||||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
 | 
					package com.pixelized.desktop.lwa.ui.screen.campaign.player.ribbon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.compose.runtime.Composable
 | 
				
			||||||
 | 
					import androidx.compose.runtime.LaunchedEffect
 | 
				
			||||||
 | 
					import androidx.compose.runtime.Stable
 | 
				
			||||||
 | 
					import androidx.compose.runtime.State
 | 
				
			||||||
 | 
					import androidx.compose.runtime.mutableStateOf
 | 
				
			||||||
 | 
					import androidx.compose.runtime.remember
 | 
				
			||||||
 | 
					import androidx.compose.runtime.saveable.rememberSaveable
 | 
				
			||||||
import androidx.lifecycle.ViewModel
 | 
					import androidx.lifecycle.ViewModel
 | 
				
			||||||
import androidx.lifecycle.viewModelScope
 | 
					import androidx.lifecycle.viewModelScope
 | 
				
			||||||
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
 | 
					import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
 | 
				
			||||||
import kotlinx.coroutines.flow.SharingStarted
 | 
					import kotlinx.coroutines.flow.SharingStarted
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.StateFlow
 | 
				
			||||||
import kotlinx.coroutines.flow.map
 | 
					import kotlinx.coroutines.flow.map
 | 
				
			||||||
import kotlinx.coroutines.flow.stateIn
 | 
					import kotlinx.coroutines.flow.stateIn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PlayerRibbonViewModel(
 | 
					class PlayerRibbonViewModel(
 | 
				
			||||||
    repository: CharacterSheetRepository,
 | 
					    private val rollHistoryRepository: RollHistoryRepository,
 | 
				
			||||||
 | 
					    characterRepository: CharacterSheetRepository,
 | 
				
			||||||
) : ViewModel() {
 | 
					) : ViewModel() {
 | 
				
			||||||
 | 
					    val characters: StateFlow<List<PlayerPortraitUio>> = characterRepository.characterSheetFlow()
 | 
				
			||||||
    val characters = repository.characterSheetFlow()
 | 
					 | 
				
			||||||
        .map { sheets ->
 | 
					        .map { sheets ->
 | 
				
			||||||
            sheets.map { sheet ->
 | 
					            sheets.map { sheet ->
 | 
				
			||||||
                PlayerPortraitUio(
 | 
					                PlayerPortraitUio(
 | 
				
			||||||
| 
						 | 
					@ -28,4 +37,31 @@ class PlayerRibbonViewModel(
 | 
				
			||||||
            started = SharingStarted.Eagerly,
 | 
					            started = SharingStarted.Eagerly,
 | 
				
			||||||
            initialValue = emptyList()
 | 
					            initialValue = emptyList()
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val _rolls: HashMap<String, Int?> = hashMapOf()
 | 
				
			||||||
 | 
					    val rolls: StateFlow<Map<String, Int?>> = rollHistoryRepository.rolls
 | 
				
			||||||
 | 
					        .map {
 | 
				
			||||||
 | 
					            _rolls[it.characterId] = it.rollValue
 | 
				
			||||||
 | 
					            _rolls
 | 
				
			||||||
 | 
					        }.stateIn(
 | 
				
			||||||
 | 
					            scope = viewModelScope,
 | 
				
			||||||
 | 
					            started = SharingStarted.Eagerly,
 | 
				
			||||||
 | 
					            initialValue = emptyMap()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Composable
 | 
				
			||||||
 | 
					    @Stable
 | 
				
			||||||
 | 
					    fun roll(characterId: String): State<Int?> {
 | 
				
			||||||
 | 
					        val state = rememberSaveable(characterId) {
 | 
				
			||||||
 | 
					            mutableStateOf<Int?>(null)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        LaunchedEffect(characterId) {
 | 
				
			||||||
 | 
					            rollHistoryRepository.rolls.collect {
 | 
				
			||||||
 | 
					                if (it.characterId == characterId) {
 | 
				
			||||||
 | 
					                    state.value = it.rollValue
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return state
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -95,13 +95,16 @@ fun RollPage(
 | 
				
			||||||
    val scope = rememberCoroutineScope()
 | 
					    val scope = rememberCoroutineScope()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    KeyHandler {
 | 
					    KeyHandler {
 | 
				
			||||||
        if (it.type == KeyEventType.KeyUp && it.key == Key.Escape) {
 | 
					        when {
 | 
				
			||||||
 | 
					            it.type == KeyEventType.KeyUp && it.key == Key.Escape -> {
 | 
				
			||||||
                onDismissRequest()
 | 
					                onDismissRequest()
 | 
				
			||||||
                true
 | 
					                true
 | 
				
			||||||
        } else {
 | 
					            }
 | 
				
			||||||
 | 
					            else -> {
 | 
				
			||||||
                false
 | 
					                false
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Column(
 | 
					    Column(
 | 
				
			||||||
        modifier = Modifier.fillMaxSize()
 | 
					        modifier = Modifier.fillMaxSize()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -192,6 +192,7 @@ class RollViewModel(
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                    launch {
 | 
					                    launch {
 | 
				
			||||||
                        rollHistoryRepository.share(
 | 
					                        rollHistoryRepository.share(
 | 
				
			||||||
 | 
					                            characterId = sheet.id,
 | 
				
			||||||
                            skillLabel = _rollTitle.value.label,
 | 
					                            skillLabel = _rollTitle.value.label,
 | 
				
			||||||
                            rollDifficulty = when (_rollDifficulty.value?.difficulty) {
 | 
					                            rollDifficulty = when (_rollDifficulty.value?.difficulty) {
 | 
				
			||||||
                                Difficulty.EASY -> getString(Res.string.roll_page__dc_easy__label)
 | 
					                                Difficulty.EASY -> getString(Res.string.roll_page__dc_easy__label)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,7 +20,7 @@ import org.jetbrains.compose.resources.stringResource
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Stable
 | 
					@Stable
 | 
				
			||||||
data class RollHistoryItemUio(
 | 
					data class RollHistoryItemUio(
 | 
				
			||||||
    val from: String,
 | 
					    val character: String,
 | 
				
			||||||
    val skillLabel: String,
 | 
					    val skillLabel: String,
 | 
				
			||||||
    val rollDifficulty: String?,
 | 
					    val rollDifficulty: String?,
 | 
				
			||||||
    val rollValue: Int,
 | 
					    val rollValue: Int,
 | 
				
			||||||
| 
						 | 
					@ -50,7 +50,7 @@ fun RollHistoryItem(
 | 
				
			||||||
                    fontWeight = FontWeight.Bold,
 | 
					                    fontWeight = FontWeight.Bold,
 | 
				
			||||||
                    overflow = TextOverflow.Ellipsis,
 | 
					                    overflow = TextOverflow.Ellipsis,
 | 
				
			||||||
                    maxLines = 1,
 | 
					                    maxLines = 1,
 | 
				
			||||||
                    text = roll.from,
 | 
					                    text = roll.character,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                Text(
 | 
					                Text(
 | 
				
			||||||
                    modifier = Modifier.alignByBaseline(),
 | 
					                    modifier = Modifier.alignByBaseline(),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,12 +4,15 @@ import androidx.compose.runtime.State
 | 
				
			||||||
import androidx.compose.runtime.mutableStateOf
 | 
					import androidx.compose.runtime.mutableStateOf
 | 
				
			||||||
import androidx.lifecycle.ViewModel
 | 
					import androidx.lifecycle.ViewModel
 | 
				
			||||||
import androidx.lifecycle.viewModelScope
 | 
					import androidx.lifecycle.viewModelScope
 | 
				
			||||||
import com.pixelized.server.lwa.protocol.RollMessage
 | 
					import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.repository.characterSheet.model.CharacterSheet
 | 
				
			||||||
import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
 | 
					import com.pixelized.desktop.lwa.repository.roll_history.RollHistoryRepository
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.combine
 | 
				
			||||||
import kotlinx.coroutines.launch
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RollHistoryViewModel(
 | 
					class RollHistoryViewModel(
 | 
				
			||||||
    private val repository: RollHistoryRepository
 | 
					    private val characterRepository: CharacterSheetRepository,
 | 
				
			||||||
 | 
					    private val rollRepository: RollHistoryRepository,
 | 
				
			||||||
) : ViewModel() {
 | 
					) : ViewModel() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val _rolls = mutableStateOf((emptyList<RollHistoryItemUio>()))
 | 
					    private val _rolls = mutableStateOf((emptyList<RollHistoryItemUio>()))
 | 
				
			||||||
| 
						 | 
					@ -17,13 +20,15 @@ class RollHistoryViewModel(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    init {
 | 
					    init {
 | 
				
			||||||
        viewModelScope.launch {
 | 
					        viewModelScope.launch {
 | 
				
			||||||
            repository.rolls.collect {
 | 
					            combine(
 | 
				
			||||||
                (it.value as? RollMessage)?.let { content ->
 | 
					                characterRepository.characterSheetFlow(),
 | 
				
			||||||
                    _rolls.value = _rolls.value.toMutableList().apply {
 | 
					                rollRepository.rolls,
 | 
				
			||||||
 | 
					            ) { sheets: List<CharacterSheet>, content ->
 | 
				
			||||||
 | 
					                _rolls.value.toMutableList().apply {
 | 
				
			||||||
                    add(
 | 
					                    add(
 | 
				
			||||||
                        index = 0,
 | 
					                        index = 0,
 | 
				
			||||||
                        element = RollHistoryItemUio(
 | 
					                        element = RollHistoryItemUio(
 | 
				
			||||||
                                from = it.from,
 | 
					                            character = sheets.firstOrNull { it.id == content.characterId }?.name ?: "",
 | 
				
			||||||
                            skillLabel = content.skillLabel,
 | 
					                            skillLabel = content.skillLabel,
 | 
				
			||||||
                            rollDifficulty = content.rollDifficulty,
 | 
					                            rollDifficulty = content.rollDifficulty,
 | 
				
			||||||
                            resultLabel = content.resultLabel,
 | 
					                            resultLabel = content.resultLabel,
 | 
				
			||||||
| 
						 | 
					@ -32,7 +37,8 @@ class RollHistoryViewModel(
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                }
 | 
					            }.collect { content ->
 | 
				
			||||||
 | 
					                _rolls.value = content
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,5 +5,6 @@ import kotlinx.serialization.Serializable
 | 
				
			||||||
@Serializable
 | 
					@Serializable
 | 
				
			||||||
data class Message(
 | 
					data class Message(
 | 
				
			||||||
    val from: String,
 | 
					    val from: String,
 | 
				
			||||||
    val value: MessageContent,
 | 
					    val type: MessageType,
 | 
				
			||||||
 | 
					    val value: String,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -1,6 +0,0 @@
 | 
				
			||||||
package com.pixelized.server.lwa.protocol
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import kotlinx.serialization.Serializable
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Serializable
 | 
					 | 
				
			||||||
sealed interface MessageContent
 | 
					 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					package com.pixelized.server.lwa.protocol
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum class MessageType {
 | 
				
			||||||
 | 
					    Roll
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,13 @@
 | 
				
			||||||
package com.pixelized.server.lwa.protocol
 | 
					package com.pixelized.server.lwa.protocol.roll
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import kotlinx.serialization.Serializable
 | 
					import kotlinx.serialization.Serializable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Serializable
 | 
					@Serializable
 | 
				
			||||||
data class RollMessage(
 | 
					data class RollMessage(
 | 
				
			||||||
 | 
					    val characterId: String,
 | 
				
			||||||
    val skillLabel: String,
 | 
					    val skillLabel: String,
 | 
				
			||||||
    val resultLabel: String?,
 | 
					    val resultLabel: String?,
 | 
				
			||||||
    val rollDifficulty: String?,
 | 
					    val rollDifficulty: String?,
 | 
				
			||||||
    val rollValue: Int,
 | 
					    val rollValue: Int,
 | 
				
			||||||
    val rollSuccessLimit: Int?,
 | 
					    val rollSuccessLimit: Int?,
 | 
				
			||||||
) : MessageContent
 | 
					)
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue