Add background image to quest.
This commit is contained in:
parent
331fbb87e5
commit
6a326ad1a8
12 changed files with 321 additions and 158 deletions
|
|
@ -1,11 +1,13 @@
|
|||
package com.pixelized.rplexicon.model
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.compose.runtime.Stable
|
||||
|
||||
@Stable
|
||||
data class Quest(
|
||||
val id: Int,
|
||||
val title: String,
|
||||
val background: Uri?,
|
||||
val entries: List<QuestEntry>,
|
||||
)
|
||||
|
||||
|
|
@ -20,4 +22,5 @@ data class QuestEntry(
|
|||
val groupReward: String?,
|
||||
val individualReward: String?,
|
||||
val description: String,
|
||||
val background: Uri?,
|
||||
)
|
||||
|
|
@ -13,6 +13,7 @@ import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
|||
import com.pixelized.rplexicon.utilitary.exceptions.ServiceNotReady
|
||||
import com.pixelized.rplexicon.utilitary.extentions.checkSheetStructure
|
||||
import com.pixelized.rplexicon.utilitary.extentions.sheet
|
||||
import com.pixelized.rplexicon.utilitary.extentions.toUriOrNull
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -135,12 +136,6 @@ class LexiconRepository @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun String?.toUriOrNull(): Uri? = try {
|
||||
this?.takeIf { it.isNotBlank() }?.toUri()
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
private val Map<String, Int>?.name: Int get() = this?.getValue(Sheet.NAME) ?: 0
|
||||
private val Map<String, Int>?.diminutive: Int get() = this?.getValue(Sheet.DIMINUTIVE) ?: 1
|
||||
private val Map<String, Int>?.gender: Int get() = this?.getValue(Sheet.GENDER) ?: 2
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
|||
import com.pixelized.rplexicon.utilitary.exceptions.ServiceNotReady
|
||||
import com.pixelized.rplexicon.utilitary.extentions.checkSheetStructure
|
||||
import com.pixelized.rplexicon.utilitary.extentions.sheet
|
||||
import com.pixelized.rplexicon.utilitary.extentions.toUriOrNull
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -82,6 +83,7 @@ class QuestRepository @Inject constructor(
|
|||
Quest(
|
||||
id = index,
|
||||
title = item,
|
||||
background = questMap[item]?.mapNotNull { it.background }?.randomOrNull(),
|
||||
entries = questMap[item] ?: emptyList(),
|
||||
)
|
||||
}
|
||||
|
|
@ -96,24 +98,26 @@ class QuestRepository @Inject constructor(
|
|||
): QuestEntry? {
|
||||
val title = row?.getOrNull(sheetStructure.title) as? String
|
||||
val subtitle = row?.getOrNull(sheetStructure.subtitle) as? String?
|
||||
val complete = row?.getOrNull(sheetStructure.complete) as? Boolean? ?: false
|
||||
val complete = row?.getOrNull(sheetStructure.complete) as? String?
|
||||
val questGiver = row?.getOrNull(sheetStructure.questGiver) as? String?
|
||||
val area = row?.getOrNull(sheetStructure.area) as? String?
|
||||
val groupReward = row?.getOrNull(sheetStructure.groupReward) as? String?
|
||||
val individualReward = row?.getOrNull(sheetStructure.individualReward) as? String?
|
||||
val description = row?.getOrNull(sheetStructure.description) as? String
|
||||
val background = row?.getOrNull(sheetStructure.background) as? String?
|
||||
|
||||
return if (title?.isNotEmpty() == true && description?.isNotEmpty() == true) {
|
||||
QuestEntry(
|
||||
sheetIndex = sheetIndex,
|
||||
title = title,
|
||||
subtitle = subtitle?.takeIf { it.isNotBlank() },
|
||||
complete = complete,
|
||||
complete = complete.equals("TRUE", ignoreCase = true),
|
||||
questGiver = questGiver?.takeIf { it.isNotBlank() },
|
||||
area = area?.takeIf { it.isNotBlank() },
|
||||
groupReward = groupReward?.takeIf { it.isNotBlank() },
|
||||
individualReward = individualReward?.takeIf { it.isNotBlank() },
|
||||
description = description,
|
||||
background = background?.toUriOrNull(),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
@ -128,6 +132,7 @@ class QuestRepository @Inject constructor(
|
|||
private val Map<String, Int>?.groupReward: Int get() = this?.getValue(Sheet.G_REWARD) ?: 5
|
||||
private val Map<String, Int>?.individualReward: Int get() = this?.getValue(Sheet.I_REWARD) ?: 6
|
||||
private val Map<String, Int>?.description: Int get() = this?.getValue(Sheet.DESCRIPTION) ?: 7
|
||||
private val Map<String, Int>?.background: Int get() = this?.getValue(Sheet.BACKGROUND) ?: 8
|
||||
|
||||
private object Sheet {
|
||||
const val ID = "1sDAay8DjbRYKM39MvEXWs-RuvyxjOFpOfRZLAEWjIUY"
|
||||
|
|
@ -143,6 +148,7 @@ class QuestRepository @Inject constructor(
|
|||
"Récompense de groupe",
|
||||
"Récompense individuelle",
|
||||
"Description",
|
||||
"fond"
|
||||
)
|
||||
val TITLE = COLUMNS[0]
|
||||
val SUBTITLE = COLUMNS[1]
|
||||
|
|
@ -152,5 +158,6 @@ class QuestRepository @Inject constructor(
|
|||
val G_REWARD = COLUMNS[5]
|
||||
val I_REWARD = COLUMNS[6]
|
||||
val DESCRIPTION = COLUMNS[7]
|
||||
val BACKGROUND = COLUMNS[8]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.pixelized.rplexicon.ui.composable
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ColorMatrix
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.skydoves.landscapist.ImageOptions
|
||||
|
||||
@Composable
|
||||
fun BackgroundImage(
|
||||
modifier: Modifier = Modifier,
|
||||
alignment: Alignment = Alignment.TopCenter,
|
||||
contentScale: ContentScale = ContentScale.Crop,
|
||||
model: () -> Any?,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
) {
|
||||
AsyncImage(
|
||||
modifier = Modifier.matchParentSize(),
|
||||
imageOptions = ImageOptions(
|
||||
alignment = alignment,
|
||||
contentScale = contentScale,
|
||||
colorFilter = remember {
|
||||
ColorFilter.colorMatrix(
|
||||
ColorMatrix().also { it.setToSaturation(0f) }
|
||||
)
|
||||
},
|
||||
),
|
||||
imageModel = model,
|
||||
previewPlaceholder = R.drawable.im_brulkhai,
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.background(brush = rememberBackgroundGradient())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberBackgroundGradient(): Brush {
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
return remember {
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
colorScheme.surface.copy(alpha = 0.5f),
|
||||
colorScheme.surface.copy(alpha = 1.0f),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.pixelized.rplexicon.ui.composable.remember
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
|
||||
/**
|
||||
* https://stackoverflow.com/questions/73333935/current-scroll-position-value-in-pixels-in-lazycolumn-jetpack-compose
|
||||
*/
|
||||
@Composable
|
||||
fun rememberCurrentOffset(state: LazyListState): State<Int> {
|
||||
val position = remember { derivedStateOf { state.firstVisibleItemIndex } }
|
||||
val itemOffset = remember { derivedStateOf { state.firstVisibleItemScrollOffset } }
|
||||
val lastPosition = rememberPrevious(position.value)
|
||||
val lastItemOffset = rememberPrevious(itemOffset.value)
|
||||
val currentOffset = remember { mutableStateOf(0) }
|
||||
|
||||
LaunchedEffect(position.value, itemOffset.value) {
|
||||
if (lastPosition == null || position.value == 0) {
|
||||
currentOffset.value = itemOffset.value
|
||||
} else if (lastPosition == position.value) {
|
||||
currentOffset.value += (itemOffset.value - (lastItemOffset ?: 0))
|
||||
} else if (lastPosition > position.value) {
|
||||
currentOffset.value -= (lastItemOffset ?: 0)
|
||||
} else { // lastPosition.value < position.value
|
||||
currentOffset.value += itemOffset.value
|
||||
}
|
||||
}
|
||||
|
||||
return currentOffset
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.pixelized.rplexicon.ui.composable.remember
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
|
||||
@Composable
|
||||
fun <T> rememberPrevious(
|
||||
current: T,
|
||||
shouldUpdate: (prev: T?, curr: T) -> Boolean = { a: T?, b: T -> a != b },
|
||||
): T? {
|
||||
val ref = rememberRef<T>()
|
||||
|
||||
// launched after render, so the current render will have the old value anyway
|
||||
SideEffect {
|
||||
if (shouldUpdate(ref.value, current)) {
|
||||
ref.value = current
|
||||
}
|
||||
}
|
||||
|
||||
return ref.value
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.pixelized.rplexicon.ui.composable.remember
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.remember
|
||||
|
||||
/**
|
||||
* Returns a dummy MutableState that does not cause render when setting it
|
||||
*/
|
||||
@Composable
|
||||
fun <T> rememberRef(): MutableState<T?> {
|
||||
// for some reason it always recreated the value with vararg keys,
|
||||
// leaving out the keys as a parameter for remember for now
|
||||
return remember {
|
||||
object : MutableState<T?> {
|
||||
override var value: T? = null
|
||||
|
||||
override fun component1(): T? = value
|
||||
|
||||
override fun component2(): (T?) -> Unit = { value = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
|||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -35,14 +34,10 @@ import androidx.compose.runtime.Stable
|
|||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ColorMatrix
|
||||
import androidx.compose.ui.graphics.Shadow
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
|
|
@ -60,6 +55,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
|||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.model.Lexicon
|
||||
import com.pixelized.rplexicon.ui.composable.AsyncImage
|
||||
import com.pixelized.rplexicon.ui.composable.BackgroundImage
|
||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
import com.pixelized.rplexicon.utilitary.composable.stringResource
|
||||
|
|
@ -197,32 +193,13 @@ private fun LexiconDetailContent(
|
|||
modifier = Modifier.padding(paddingValues = paddingValues),
|
||||
) {
|
||||
annotatedItem.portrait.firstOrNull()?.let { uri ->
|
||||
Box(
|
||||
BackgroundImage(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(ratio = 1f)
|
||||
.scrollOffset(scrollState = state) { -it / 2 },
|
||||
) {
|
||||
AsyncImage(
|
||||
modifier = Modifier.matchParentSize(),
|
||||
imageOptions = ImageOptions(
|
||||
alignment = Alignment.TopCenter,
|
||||
contentScale = ContentScale.Crop,
|
||||
colorFilter = remember {
|
||||
ColorFilter.colorMatrix(
|
||||
ColorMatrix().also { it.setToSaturation(0f) }
|
||||
)
|
||||
},
|
||||
),
|
||||
imageModel = { uri.toString() },
|
||||
previewPlaceholder = R.drawable.im_brulkhai,
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.background(brush = rememberBackgroundGradient())
|
||||
)
|
||||
}
|
||||
model = { uri.toString() },
|
||||
)
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -342,19 +319,6 @@ private fun LexiconDetailContent(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberBackgroundGradient(): Brush {
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
return remember {
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
colorScheme.surface.copy(alpha = 0.5f),
|
||||
colorScheme.surface.copy(alpha = 1.0f),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberPortraitWidth(): Dp {
|
||||
val configuration = LocalConfiguration.current
|
||||
|
|
|
|||
|
|
@ -1,17 +1,22 @@
|
|||
package com.pixelized.rplexicon.ui.screens.quest.detail
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -27,10 +32,12 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
|
|
@ -39,9 +46,12 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.ui.composable.BackgroundImage
|
||||
import com.pixelized.rplexicon.ui.composable.remember.rememberCurrentOffset
|
||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
|
||||
|
|
@ -50,6 +60,7 @@ import java.lang.Integer.min
|
|||
@Stable
|
||||
data class QuestDetailUio(
|
||||
val id: Int,
|
||||
val background: Uri?,
|
||||
val title: String,
|
||||
val steps: List<QuestStep>,
|
||||
) {
|
||||
|
|
@ -68,6 +79,7 @@ data class QuestDetailUio(
|
|||
@Stable
|
||||
data class AnnotatedQuestDetailUio(
|
||||
val title: String,
|
||||
val background: Uri?,
|
||||
val steps: List<AnnotatedQuestStep>,
|
||||
) {
|
||||
@Stable
|
||||
|
|
@ -89,6 +101,7 @@ private fun QuestDetailUio.annotate(): AnnotatedQuestDetailUio {
|
|||
return remember {
|
||||
AnnotatedQuestDetailUio(
|
||||
title = title,
|
||||
background = background,
|
||||
steps = annotatedSteps,
|
||||
)
|
||||
}
|
||||
|
|
@ -143,6 +156,7 @@ private fun QuestDetailContent(
|
|||
item: State<QuestDetailUio>,
|
||||
onBack: () -> Unit,
|
||||
) {
|
||||
val state = rememberLazyListState()
|
||||
val annotatedQuest = item.value.annotate()
|
||||
|
||||
Scaffold(
|
||||
|
|
@ -164,128 +178,158 @@ private fun QuestDetailContent(
|
|||
)
|
||||
},
|
||||
content = { padding ->
|
||||
LazyColumn(
|
||||
Surface(
|
||||
modifier = Modifier.padding(padding),
|
||||
contentPadding = PaddingValues(
|
||||
top = 40.dp,
|
||||
bottom = 16.dp,
|
||||
start = 16.dp,
|
||||
end = 16.dp
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(40.dp),
|
||||
) {
|
||||
item {
|
||||
Column {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.displaySmall,
|
||||
text = annotatedQuest.title,
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.height(24.dp)
|
||||
.graphicsLayer { rotationZ = 180f }
|
||||
.align(Alignment.CenterHorizontally),
|
||||
painter = painterResource(id = R.drawable.art_divider_1),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
alignment = Alignment.Center,
|
||||
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
items(annotatedQuest.steps) { quest ->
|
||||
Column {
|
||||
quest.subtitle?.let { subtitle ->
|
||||
Row(
|
||||
BackgroundImage(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(ratio = 1f)
|
||||
.scrollOffset(scrollState = state) { -it / 2 },
|
||||
model = { annotatedQuest.background },
|
||||
)
|
||||
LazyColumn(
|
||||
state = state,
|
||||
contentPadding = PaddingValues(
|
||||
top = 248.dp,
|
||||
bottom = 16.dp,
|
||||
start = 16.dp,
|
||||
end = 16.dp
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
||||
) {
|
||||
item {
|
||||
Column {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.graphicsLayer { rotationY = 180f },
|
||||
painter = painterResource(id = R.drawable.art_clip_1),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
alignment = Alignment.Center,
|
||||
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(all = 8.dp),
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
text = subtitle,
|
||||
)
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.art_clip_1),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
alignment = Alignment.Center,
|
||||
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
|
||||
contentDescription = null,
|
||||
)
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.displaySmall,
|
||||
text = annotatedQuest.title,
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.height(24.dp)
|
||||
.graphicsLayer { rotationZ = 180f }
|
||||
.align(Alignment.CenterHorizontally),
|
||||
painter = painterResource(id = R.drawable.art_divider_1),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
alignment = Alignment.Center,
|
||||
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
items(annotatedQuest.steps) { quest ->
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
quest.subtitle?.let { subtitle ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 64.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.graphicsLayer { rotationY = 180f },
|
||||
painter = painterResource(id = R.drawable.art_clip_1),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
alignment = Alignment.Center,
|
||||
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
text = subtitle,
|
||||
)
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.art_clip_1),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
alignment = Alignment.Center,
|
||||
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
quest.giver?.let {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
text = "Commanditaire",
|
||||
)
|
||||
quest.giver?.let {
|
||||
Column {
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
text = "Commanditaire",
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
quest.place?.let {
|
||||
Column {
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
text = "Lieu",
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
quest.globalReward?.let {
|
||||
Column {
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
text = "Récompense de groupe",
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
quest.individualReward?.let {
|
||||
Column {
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
text = "Récompense individuelle",
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = it,
|
||||
text = quest.description,
|
||||
)
|
||||
}
|
||||
quest.place?.let {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
text = "Lieu",
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = it,
|
||||
)
|
||||
}
|
||||
quest.globalReward?.let {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
text = "Récompense de groupe",
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = it,
|
||||
)
|
||||
}
|
||||
quest.individualReward?.let {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
text = "Récompense individuelle",
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = it,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 24.dp),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = quest.description,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Stable
|
||||
private fun Modifier.scrollOffset(
|
||||
scrollState: LazyListState,
|
||||
block: (Dp) -> Dp
|
||||
): Modifier = composed {
|
||||
val scroll = rememberCurrentOffset(scrollState)
|
||||
val density = LocalDensity.current
|
||||
this.offset(y = with(density) { block(scroll.value.toDp()) })
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
|
|
@ -307,6 +351,7 @@ private class QuestDetailPreviewProvider : PreviewParameterProvider<State<QuestD
|
|||
mutableStateOf(
|
||||
QuestDetailUio(
|
||||
id = 0,
|
||||
background = Uri.parse("https://as1.ftcdn.net/v2/jpg/05/50/22/58/1000_F_550225869_jAkLTRVb7ym7EHJYvDApVXQnpANvRd8O.jpg"),
|
||||
title = "La chasse aux loups",
|
||||
steps = listOf(
|
||||
QuestDetailUio.QuestStep(
|
||||
|
|
@ -333,6 +378,7 @@ private class QuestDetailPreviewProvider : PreviewParameterProvider<State<QuestD
|
|||
mutableStateOf(
|
||||
QuestDetailUio(
|
||||
id = 1,
|
||||
background = Uri.parse("https://cdnb.artstation.com/p/assets/images/images/008/823/761/large/jon-pintar-adventurers-caravan-jon-pintar.jpg?1515529013"),
|
||||
title = "Les enfants de la caravanes",
|
||||
steps = listOf(
|
||||
QuestDetailUio.QuestStep(
|
||||
|
|
@ -349,4 +395,3 @@ private class QuestDetailPreviewProvider : PreviewParameterProvider<State<QuestD
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ class QuestDetailViewModel @Inject constructor(
|
|||
quest = mutableStateOf(
|
||||
QuestDetailUio(
|
||||
id = source.id,
|
||||
background = source.background,
|
||||
title = source.title,
|
||||
steps = source.entries.map { entry ->
|
||||
QuestDetailUio.QuestStep(
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ private fun QuestListContent(
|
|||
modifier: Modifier = Modifier,
|
||||
lazyColumnState: LazyListState,
|
||||
paddingValues: PaddingValues = PaddingValues(
|
||||
top = 6.dp,
|
||||
top = 8.dp,
|
||||
bottom = 8.dp + 16.dp + 56.dp + 16.dp,
|
||||
),
|
||||
refreshState: PullRefreshState,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package com.pixelized.rplexicon.utilitary.extentions
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
|
||||
val String.ARG: String get() = "$this={$this}"
|
||||
|
||||
val String?.highlightRegex: Regex?
|
||||
|
|
@ -15,3 +18,9 @@ val String?.finderRegex: Regex?
|
|||
)
|
||||
}
|
||||
|
||||
fun String?.toUriOrNull(): Uri? = try {
|
||||
this?.takeIf { it.isNotBlank() }?.toUri()
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue