Add a download link to a portrait.
This commit is contained in:
parent
7dd5ade1f5
commit
ca5d01c0ca
3 changed files with 85 additions and 22 deletions
|
|
@ -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>
|
||||||
|
|
@ -2,15 +2,18 @@ package com.pixelized.desktop.lwa.ui.overlay.portrait
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.togetherWith
|
import androidx.compose.animation.togetherWith
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material.Icon
|
import androidx.compose.material.Icon
|
||||||
import androidx.compose.material.IconButton
|
import androidx.compose.material.IconButton
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
|
@ -23,9 +26,16 @@ import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import lwacharactersheet.composeapp.generated.resources.Res
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_cancel_24dp
|
import lwacharactersheet.composeapp.generated.resources.ic_cancel_24dp
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.ic_link_24dp
|
||||||
import org.jetbrains.compose.resources.painterResource
|
import org.jetbrains.compose.resources.painterResource
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class PortraitOptionUio(
|
||||||
|
val isBrowserAvailable: Boolean,
|
||||||
|
val isGameMaster: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PortraitOverlay(
|
fun PortraitOverlay(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
@ -33,13 +43,16 @@ fun PortraitOverlay(
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val portrait = viewModel.portrait.collectAsState()
|
val portrait = viewModel.portrait.collectAsState()
|
||||||
val isGameMaster = viewModel.isGameMaster.collectAsState()
|
val options = viewModel.options.collectAsState()
|
||||||
|
|
||||||
PortraitContent(
|
PortraitContent(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
portrait = portrait,
|
portrait = portrait,
|
||||||
isGameMaster = isGameMaster,
|
options = options,
|
||||||
onGameMaster = {
|
onDownload = { url ->
|
||||||
|
viewModel.browse(url)
|
||||||
|
},
|
||||||
|
onClose = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
viewModel.dismissPortrait()
|
viewModel.dismissPortrait()
|
||||||
}
|
}
|
||||||
|
|
@ -51,8 +64,9 @@ fun PortraitOverlay(
|
||||||
private fun PortraitContent(
|
private fun PortraitContent(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
portrait: State<String?>,
|
portrait: State<String?>,
|
||||||
isGameMaster: State<Boolean>,
|
options: State<PortraitOptionUio>,
|
||||||
onGameMaster: () -> Unit,
|
onDownload: (url: String) -> Unit,
|
||||||
|
onClose: () -> Unit,
|
||||||
) {
|
) {
|
||||||
AnimatedContent(
|
AnimatedContent(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -65,11 +79,11 @@ private fun PortraitContent(
|
||||||
) {
|
) {
|
||||||
when (it) {
|
when (it) {
|
||||||
null -> Box(
|
null -> Box(
|
||||||
modifier = Modifier.size(MaterialTheme.lwa.size.portrait.maximized)
|
modifier = Modifier.size(size = MaterialTheme.lwa.size.portrait.maximized)
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> Box(
|
else -> Box(
|
||||||
modifier = Modifier.size(MaterialTheme.lwa.size.portrait.maximized)
|
modifier = Modifier.size(size = MaterialTheme.lwa.size.portrait.maximized)
|
||||||
) {
|
) {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
modifier = Modifier.matchParentSize(),
|
modifier = Modifier.matchParentSize(),
|
||||||
|
|
@ -78,20 +92,40 @@ private fun PortraitContent(
|
||||||
contentDescription = null
|
contentDescription = null
|
||||||
)
|
)
|
||||||
|
|
||||||
AnimatedVisibility(
|
Row(
|
||||||
modifier = Modifier.align(alignment = Alignment.TopEnd),
|
modifier = Modifier
|
||||||
visible = isGameMaster.value,
|
.align(alignment = Alignment.TopEnd)
|
||||||
enter = fadeIn(),
|
.animateContentSize(),
|
||||||
exit = fadeOut(),
|
|
||||||
) {
|
) {
|
||||||
IconButton(
|
AnimatedVisibility(
|
||||||
onClick = onGameMaster,
|
visible = options.value.isBrowserAvailable,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
) {
|
) {
|
||||||
Icon(
|
IconButton(
|
||||||
painter = painterResource(Res.drawable.ic_cancel_24dp),
|
onClick = { onDownload(it) },
|
||||||
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
) {
|
||||||
contentDescription = null
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.mapNotNull
|
import kotlinx.coroutines.flow.mapNotNull
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import java.awt.Desktop
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
class PortraitOverlayViewModel(
|
class PortraitOverlayViewModel(
|
||||||
private val networkRepository: NetworkRepository,
|
private val networkRepository: NetworkRepository,
|
||||||
|
|
@ -32,12 +34,21 @@ class PortraitOverlayViewModel(
|
||||||
initialValue = null
|
initialValue = null
|
||||||
)
|
)
|
||||||
|
|
||||||
val isGameMaster = settingsRepository.settingsFlow()
|
val options = settingsRepository.settingsFlow()
|
||||||
.map { settings -> settings.isGameMaster ?: false }
|
.map { settings ->
|
||||||
|
PortraitOptionUio(
|
||||||
|
isGameMaster = settings.isGameMaster ?: false,
|
||||||
|
isBrowserAvailable = Desktop.isDesktopSupported()
|
||||||
|
&& Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)
|
||||||
|
)
|
||||||
|
}
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope = viewModelScope,
|
scope = viewModelScope,
|
||||||
started = SharingStarted.Eagerly,
|
started = SharingStarted.Eagerly,
|
||||||
initialValue = false
|
initialValue = PortraitOptionUio(
|
||||||
|
isGameMaster = false,
|
||||||
|
isBrowserAvailable = false,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun dismissPortrait() {
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue