Add a download link to a portrait.

This commit is contained in:
Andres Gomez, Thomas (ITDV RL) 2025-04-12 10:19:48 +02:00
parent 7dd5ade1f5
commit ca5d01c0ca
3 changed files with 85 additions and 22 deletions

View file

@ -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:fillColor="#000000"
android:pathData="M440,680L280,680Q197,680 138.5,621.5Q80,563 80,480Q80,397 138.5,338.5Q197,280 280,280L440,280L440,360L280,360Q230,360 195,395Q160,430 160,480Q160,530 195,565Q230,600 280,600L440,600L440,680ZM320,520L320,440L640,440L640,520L320,520ZM520,680L520,600L680,600Q730,600 765,565Q800,530 800,480Q800,430 765,395Q730,360 680,360L520,360L520,280L680,280Q763,280 821.5,338.5Q880,397 880,480Q880,563 821.5,621.5Q763,680 680,680L520,680Z" />
</vector>

View file

@ -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<String?>,
isGameMaster: State<Boolean>,
onGameMaster: () -> Unit,
options: State<PortraitOptionUio>,
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
)
}
}
}
}

View file

@ -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))
}
}
}
}