From ca5d01c0ca2ba60adb67ae5e41c3758041d114de Mon Sep 17 00:00:00 2001 From: "Andres Gomez, Thomas (ITDV RL)" Date: Sat, 12 Apr 2025 10:19:48 +0200 Subject: [PATCH] Add a download link to a portrait. --- .../drawable/ic_link_24dp.xml | 9 +++ .../ui/overlay/portrait/PortraitOverlay.kt | 72 ++++++++++++++----- .../portrait/PortraitOverlayViewModel.kt | 26 ++++++- 3 files changed, 85 insertions(+), 22 deletions(-) create mode 100644 composeApp/src/commonMain/composeResources/drawable/ic_link_24dp.xml diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_link_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_link_24dp.xml new file mode 100644 index 0000000..b9af710 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/ic_link_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/overlay/portrait/PortraitOverlay.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/overlay/portrait/PortraitOverlay.kt index 2095b64..39586d3 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/overlay/portrait/PortraitOverlay.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/overlay/portrait/PortraitOverlay.kt @@ -2,15 +2,18 @@ package com.pixelized.desktop.lwa.ui.overlay.portrait import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.animateContentSize 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.Row import androidx.compose.foundation.layout.size import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.rememberCoroutineScope @@ -23,9 +26,16 @@ import com.pixelized.desktop.lwa.ui.theme.lwa import kotlinx.coroutines.launch import lwacharactersheet.composeapp.generated.resources.Res import lwacharactersheet.composeapp.generated.resources.ic_cancel_24dp +import lwacharactersheet.composeapp.generated.resources.ic_link_24dp import org.jetbrains.compose.resources.painterResource import org.koin.compose.viewmodel.koinViewModel +@Stable +data class PortraitOptionUio( + val isBrowserAvailable: Boolean, + val isGameMaster: Boolean, +) + @Composable fun PortraitOverlay( modifier: Modifier = Modifier, @@ -33,13 +43,16 @@ fun PortraitOverlay( ) { val scope = rememberCoroutineScope() val portrait = viewModel.portrait.collectAsState() - val isGameMaster = viewModel.isGameMaster.collectAsState() + val options = viewModel.options.collectAsState() PortraitContent( modifier = modifier, portrait = portrait, - isGameMaster = isGameMaster, - onGameMaster = { + options = options, + onDownload = { url -> + viewModel.browse(url) + }, + onClose = { scope.launch { viewModel.dismissPortrait() } @@ -51,8 +64,9 @@ fun PortraitOverlay( private fun PortraitContent( modifier: Modifier = Modifier, portrait: State, - isGameMaster: State, - onGameMaster: () -> Unit, + options: State, + onDownload: (url: String) -> Unit, + onClose: () -> Unit, ) { AnimatedContent( modifier = Modifier @@ -65,11 +79,11 @@ private fun PortraitContent( ) { when (it) { null -> Box( - modifier = Modifier.size(MaterialTheme.lwa.size.portrait.maximized) + modifier = Modifier.size(size = MaterialTheme.lwa.size.portrait.maximized) ) else -> Box( - modifier = Modifier.size(MaterialTheme.lwa.size.portrait.maximized) + modifier = Modifier.size(size = MaterialTheme.lwa.size.portrait.maximized) ) { AsyncImage( modifier = Modifier.matchParentSize(), @@ -78,20 +92,40 @@ private fun PortraitContent( contentDescription = null ) - AnimatedVisibility( - modifier = Modifier.align(alignment = Alignment.TopEnd), - visible = isGameMaster.value, - enter = fadeIn(), - exit = fadeOut(), + Row( + modifier = Modifier + .align(alignment = Alignment.TopEnd) + .animateContentSize(), ) { - IconButton( - onClick = onGameMaster, + AnimatedVisibility( + visible = options.value.isBrowserAvailable, + enter = fadeIn(), + exit = fadeOut(), ) { - Icon( - painter = painterResource(Res.drawable.ic_cancel_24dp), - tint = MaterialTheme.lwa.colorScheme.base.primary, - contentDescription = null - ) + IconButton( + onClick = { onDownload(it) }, + ) { + Icon( + painter = painterResource(Res.drawable.ic_link_24dp), + tint = MaterialTheme.lwa.colorScheme.base.primary, + contentDescription = null + ) + } + } + AnimatedVisibility( + visible = options.value.isGameMaster, + enter = fadeIn(), + exit = fadeOut(), + ) { + IconButton( + onClick = onClose, + ) { + Icon( + painter = painterResource(Res.drawable.ic_cancel_24dp), + tint = MaterialTheme.lwa.colorScheme.base.primary, + contentDescription = null + ) + } } } } diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/overlay/portrait/PortraitOverlayViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/overlay/portrait/PortraitOverlayViewModel.kt index 76a4281..dd3fd34 100644 --- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/overlay/portrait/PortraitOverlayViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/overlay/portrait/PortraitOverlayViewModel.kt @@ -13,6 +13,8 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.stateIn +import java.awt.Desktop +import java.net.URI class PortraitOverlayViewModel( private val networkRepository: NetworkRepository, @@ -32,12 +34,21 @@ class PortraitOverlayViewModel( initialValue = null ) - val isGameMaster = settingsRepository.settingsFlow() - .map { settings -> settings.isGameMaster ?: false } + val options = settingsRepository.settingsFlow() + .map { settings -> + PortraitOptionUio( + isGameMaster = settings.isGameMaster ?: false, + isBrowserAvailable = Desktop.isDesktopSupported() + && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE) + ) + } .stateIn( scope = viewModelScope, started = SharingStarted.Eagerly, - initialValue = false + initialValue = PortraitOptionUio( + isGameMaster = false, + isBrowserAvailable = false, + ) ) suspend fun dismissPortrait() { @@ -48,4 +59,13 @@ class PortraitOverlayViewModel( ) ) } + + fun browse(url: String) { + if (Desktop.isDesktopSupported()) { + val desktop = Desktop.getDesktop() + if (desktop.isSupported(Desktop.Action.BROWSE)) { + desktop.browse(URI.create(url)) + } + } + } } \ No newline at end of file