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
 | 
					package com.pixelized.rplexicon.model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.net.Uri
 | 
				
			||||||
import androidx.compose.runtime.Stable
 | 
					import androidx.compose.runtime.Stable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Stable
 | 
					@Stable
 | 
				
			||||||
data class Quest(
 | 
					data class Quest(
 | 
				
			||||||
    val id: Int,
 | 
					    val id: Int,
 | 
				
			||||||
    val title: String,
 | 
					    val title: String,
 | 
				
			||||||
 | 
					    val background: Uri?,
 | 
				
			||||||
    val entries: List<QuestEntry>,
 | 
					    val entries: List<QuestEntry>,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,4 +22,5 @@ data class QuestEntry(
 | 
				
			||||||
    val groupReward: String?,
 | 
					    val groupReward: String?,
 | 
				
			||||||
    val individualReward: String?,
 | 
					    val individualReward: String?,
 | 
				
			||||||
    val description: 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.exceptions.ServiceNotReady
 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.extentions.checkSheetStructure
 | 
					import com.pixelized.rplexicon.utilitary.extentions.checkSheetStructure
 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.extentions.sheet
 | 
					import com.pixelized.rplexicon.utilitary.extentions.sheet
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.utilitary.extentions.toUriOrNull
 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
import kotlinx.coroutines.flow.MutableStateFlow
 | 
					import kotlinx.coroutines.flow.MutableStateFlow
 | 
				
			||||||
import kotlinx.coroutines.flow.StateFlow
 | 
					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>?.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>?.diminutive: Int get() = this?.getValue(Sheet.DIMINUTIVE) ?: 1
 | 
				
			||||||
    private val Map<String, Int>?.gender: Int get() = this?.getValue(Sheet.GENDER) ?: 2
 | 
					    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.exceptions.ServiceNotReady
 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.extentions.checkSheetStructure
 | 
					import com.pixelized.rplexicon.utilitary.extentions.checkSheetStructure
 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.extentions.sheet
 | 
					import com.pixelized.rplexicon.utilitary.extentions.sheet
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.utilitary.extentions.toUriOrNull
 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
import kotlinx.coroutines.flow.MutableStateFlow
 | 
					import kotlinx.coroutines.flow.MutableStateFlow
 | 
				
			||||||
import kotlinx.coroutines.flow.StateFlow
 | 
					import kotlinx.coroutines.flow.StateFlow
 | 
				
			||||||
| 
						 | 
					@ -82,6 +83,7 @@ class QuestRepository @Inject constructor(
 | 
				
			||||||
            Quest(
 | 
					            Quest(
 | 
				
			||||||
                id = index,
 | 
					                id = index,
 | 
				
			||||||
                title = item,
 | 
					                title = item,
 | 
				
			||||||
 | 
					                background = questMap[item]?.mapNotNull { it.background }?.randomOrNull(),
 | 
				
			||||||
                entries = questMap[item] ?: emptyList(),
 | 
					                entries = questMap[item] ?: emptyList(),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -96,24 +98,26 @@ class QuestRepository @Inject constructor(
 | 
				
			||||||
    ): QuestEntry? {
 | 
					    ): QuestEntry? {
 | 
				
			||||||
        val title = row?.getOrNull(sheetStructure.title) as? String
 | 
					        val title = row?.getOrNull(sheetStructure.title) as? String
 | 
				
			||||||
        val subtitle = row?.getOrNull(sheetStructure.subtitle) 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 questGiver = row?.getOrNull(sheetStructure.questGiver) as? String?
 | 
				
			||||||
        val area = row?.getOrNull(sheetStructure.area) as? String?
 | 
					        val area = row?.getOrNull(sheetStructure.area) as? String?
 | 
				
			||||||
        val groupReward = row?.getOrNull(sheetStructure.groupReward) as? String?
 | 
					        val groupReward = row?.getOrNull(sheetStructure.groupReward) as? String?
 | 
				
			||||||
        val individualReward = row?.getOrNull(sheetStructure.individualReward) as? String?
 | 
					        val individualReward = row?.getOrNull(sheetStructure.individualReward) as? String?
 | 
				
			||||||
        val description = row?.getOrNull(sheetStructure.description) 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) {
 | 
					        return if (title?.isNotEmpty() == true && description?.isNotEmpty() == true) {
 | 
				
			||||||
            QuestEntry(
 | 
					            QuestEntry(
 | 
				
			||||||
                sheetIndex = sheetIndex,
 | 
					                sheetIndex = sheetIndex,
 | 
				
			||||||
                title = title,
 | 
					                title = title,
 | 
				
			||||||
                subtitle = subtitle?.takeIf { it.isNotBlank() },
 | 
					                subtitle = subtitle?.takeIf { it.isNotBlank() },
 | 
				
			||||||
                complete = complete,
 | 
					                complete = complete.equals("TRUE", ignoreCase = true),
 | 
				
			||||||
                questGiver = questGiver?.takeIf { it.isNotBlank() },
 | 
					                questGiver = questGiver?.takeIf { it.isNotBlank() },
 | 
				
			||||||
                area = area?.takeIf { it.isNotBlank() },
 | 
					                area = area?.takeIf { it.isNotBlank() },
 | 
				
			||||||
                groupReward = groupReward?.takeIf { it.isNotBlank() },
 | 
					                groupReward = groupReward?.takeIf { it.isNotBlank() },
 | 
				
			||||||
                individualReward = individualReward?.takeIf { it.isNotBlank() },
 | 
					                individualReward = individualReward?.takeIf { it.isNotBlank() },
 | 
				
			||||||
                description = description,
 | 
					                description = description,
 | 
				
			||||||
 | 
					                background = background?.toUriOrNull(),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            null
 | 
					            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>?.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>?.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>?.description: Int get() = this?.getValue(Sheet.DESCRIPTION) ?: 7
 | 
				
			||||||
 | 
					    private val Map<String, Int>?.background: Int get() = this?.getValue(Sheet.BACKGROUND) ?: 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private object Sheet {
 | 
					    private object Sheet {
 | 
				
			||||||
        const val ID = "1sDAay8DjbRYKM39MvEXWs-RuvyxjOFpOfRZLAEWjIUY"
 | 
					        const val ID = "1sDAay8DjbRYKM39MvEXWs-RuvyxjOFpOfRZLAEWjIUY"
 | 
				
			||||||
| 
						 | 
					@ -143,6 +148,7 @@ class QuestRepository @Inject constructor(
 | 
				
			||||||
            "Récompense de groupe",
 | 
					            "Récompense de groupe",
 | 
				
			||||||
            "Récompense individuelle",
 | 
					            "Récompense individuelle",
 | 
				
			||||||
            "Description",
 | 
					            "Description",
 | 
				
			||||||
 | 
					            "fond"
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        val TITLE = COLUMNS[0]
 | 
					        val TITLE = COLUMNS[0]
 | 
				
			||||||
        val SUBTITLE = COLUMNS[1]
 | 
					        val SUBTITLE = COLUMNS[1]
 | 
				
			||||||
| 
						 | 
					@ -152,5 +158,6 @@ class QuestRepository @Inject constructor(
 | 
				
			||||||
        val G_REWARD = COLUMNS[5]
 | 
					        val G_REWARD = COLUMNS[5]
 | 
				
			||||||
        val I_REWARD = COLUMNS[6]
 | 
					        val I_REWARD = COLUMNS[6]
 | 
				
			||||||
        val DESCRIPTION = COLUMNS[7]
 | 
					        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.content.res.Configuration.UI_MODE_NIGHT_YES
 | 
				
			||||||
import android.net.Uri
 | 
					import android.net.Uri
 | 
				
			||||||
import androidx.compose.foundation.ScrollState
 | 
					import androidx.compose.foundation.ScrollState
 | 
				
			||||||
import androidx.compose.foundation.background
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.Arrangement
 | 
					import androidx.compose.foundation.layout.Arrangement
 | 
				
			||||||
import androidx.compose.foundation.layout.Box
 | 
					import androidx.compose.foundation.layout.Box
 | 
				
			||||||
import androidx.compose.foundation.layout.Column
 | 
					import androidx.compose.foundation.layout.Column
 | 
				
			||||||
| 
						 | 
					@ -35,14 +34,10 @@ import androidx.compose.runtime.Stable
 | 
				
			||||||
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
 | 
				
			||||||
import androidx.compose.ui.Alignment
 | 
					 | 
				
			||||||
import androidx.compose.ui.Modifier
 | 
					import androidx.compose.ui.Modifier
 | 
				
			||||||
import androidx.compose.ui.composed
 | 
					import androidx.compose.ui.composed
 | 
				
			||||||
import androidx.compose.ui.geometry.Offset
 | 
					import androidx.compose.ui.geometry.Offset
 | 
				
			||||||
import androidx.compose.ui.graphics.Brush
 | 
					 | 
				
			||||||
import androidx.compose.ui.graphics.Color
 | 
					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.graphics.Shadow
 | 
				
			||||||
import androidx.compose.ui.layout.ContentScale
 | 
					import androidx.compose.ui.layout.ContentScale
 | 
				
			||||||
import androidx.compose.ui.platform.LocalConfiguration
 | 
					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.R
 | 
				
			||||||
import com.pixelized.rplexicon.model.Lexicon
 | 
					import com.pixelized.rplexicon.model.Lexicon
 | 
				
			||||||
import com.pixelized.rplexicon.ui.composable.AsyncImage
 | 
					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.navigation.LocalScreenNavHost
 | 
				
			||||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
					import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.composable.stringResource
 | 
					import com.pixelized.rplexicon.utilitary.composable.stringResource
 | 
				
			||||||
| 
						 | 
					@ -197,32 +193,13 @@ private fun LexiconDetailContent(
 | 
				
			||||||
            modifier = Modifier.padding(paddingValues = paddingValues),
 | 
					            modifier = Modifier.padding(paddingValues = paddingValues),
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            annotatedItem.portrait.firstOrNull()?.let { uri ->
 | 
					            annotatedItem.portrait.firstOrNull()?.let { uri ->
 | 
				
			||||||
                Box(
 | 
					                BackgroundImage(
 | 
				
			||||||
                    modifier = Modifier
 | 
					                    modifier = Modifier
 | 
				
			||||||
                        .fillMaxWidth()
 | 
					                        .fillMaxWidth()
 | 
				
			||||||
                        .aspectRatio(ratio = 1f)
 | 
					                        .aspectRatio(ratio = 1f)
 | 
				
			||||||
                        .scrollOffset(scrollState = state) { -it / 2 },
 | 
					                        .scrollOffset(scrollState = state) { -it / 2 },
 | 
				
			||||||
                ) {
 | 
					                    model = { uri.toString() },
 | 
				
			||||||
                    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())
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Column(
 | 
					            Column(
 | 
				
			||||||
                modifier = Modifier
 | 
					                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
 | 
					@Composable
 | 
				
			||||||
private fun rememberPortraitWidth(): Dp {
 | 
					private fun rememberPortraitWidth(): Dp {
 | 
				
			||||||
    val configuration = LocalConfiguration.current
 | 
					    val configuration = LocalConfiguration.current
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,22 @@
 | 
				
			||||||
package com.pixelized.rplexicon.ui.screens.quest.detail
 | 
					package com.pixelized.rplexicon.ui.screens.quest.detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.content.res.Configuration
 | 
					import android.content.res.Configuration
 | 
				
			||||||
 | 
					import android.net.Uri
 | 
				
			||||||
import androidx.compose.foundation.Image
 | 
					import androidx.compose.foundation.Image
 | 
				
			||||||
import androidx.compose.foundation.layout.Arrangement
 | 
					import androidx.compose.foundation.layout.Arrangement
 | 
				
			||||||
import androidx.compose.foundation.layout.Column
 | 
					import androidx.compose.foundation.layout.Column
 | 
				
			||||||
import androidx.compose.foundation.layout.PaddingValues
 | 
					import androidx.compose.foundation.layout.PaddingValues
 | 
				
			||||||
import androidx.compose.foundation.layout.Row
 | 
					import androidx.compose.foundation.layout.Row
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.aspectRatio
 | 
				
			||||||
import androidx.compose.foundation.layout.fillMaxSize
 | 
					import androidx.compose.foundation.layout.fillMaxSize
 | 
				
			||||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
					import androidx.compose.foundation.layout.fillMaxWidth
 | 
				
			||||||
import androidx.compose.foundation.layout.height
 | 
					import androidx.compose.foundation.layout.height
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.offset
 | 
				
			||||||
import androidx.compose.foundation.layout.padding
 | 
					import androidx.compose.foundation.layout.padding
 | 
				
			||||||
import androidx.compose.foundation.lazy.LazyColumn
 | 
					import androidx.compose.foundation.lazy.LazyColumn
 | 
				
			||||||
 | 
					import androidx.compose.foundation.lazy.LazyListState
 | 
				
			||||||
import androidx.compose.foundation.lazy.items
 | 
					import androidx.compose.foundation.lazy.items
 | 
				
			||||||
 | 
					import androidx.compose.foundation.lazy.rememberLazyListState
 | 
				
			||||||
import androidx.compose.material3.ExperimentalMaterial3Api
 | 
					import androidx.compose.material3.ExperimentalMaterial3Api
 | 
				
			||||||
import androidx.compose.material3.Icon
 | 
					import androidx.compose.material3.Icon
 | 
				
			||||||
import androidx.compose.material3.IconButton
 | 
					import androidx.compose.material3.IconButton
 | 
				
			||||||
| 
						 | 
					@ -27,10 +32,12 @@ import androidx.compose.runtime.mutableStateOf
 | 
				
			||||||
import androidx.compose.runtime.remember
 | 
					import androidx.compose.runtime.remember
 | 
				
			||||||
import androidx.compose.ui.Alignment
 | 
					import androidx.compose.ui.Alignment
 | 
				
			||||||
import androidx.compose.ui.Modifier
 | 
					import androidx.compose.ui.Modifier
 | 
				
			||||||
 | 
					import androidx.compose.ui.composed
 | 
				
			||||||
import androidx.compose.ui.graphics.Color
 | 
					import androidx.compose.ui.graphics.Color
 | 
				
			||||||
import androidx.compose.ui.graphics.ColorFilter
 | 
					import androidx.compose.ui.graphics.ColorFilter
 | 
				
			||||||
import androidx.compose.ui.graphics.graphicsLayer
 | 
					import androidx.compose.ui.graphics.graphicsLayer
 | 
				
			||||||
import androidx.compose.ui.layout.ContentScale
 | 
					import androidx.compose.ui.layout.ContentScale
 | 
				
			||||||
 | 
					import androidx.compose.ui.platform.LocalDensity
 | 
				
			||||||
import androidx.compose.ui.res.painterResource
 | 
					import androidx.compose.ui.res.painterResource
 | 
				
			||||||
import androidx.compose.ui.res.stringResource
 | 
					import androidx.compose.ui.res.stringResource
 | 
				
			||||||
import androidx.compose.ui.text.AnnotatedString
 | 
					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.Preview
 | 
				
			||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
 | 
					import androidx.compose.ui.tooling.preview.PreviewParameter
 | 
				
			||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
 | 
					import androidx.compose.ui.tooling.preview.PreviewParameterProvider
 | 
				
			||||||
 | 
					import androidx.compose.ui.unit.Dp
 | 
				
			||||||
import androidx.compose.ui.unit.dp
 | 
					import androidx.compose.ui.unit.dp
 | 
				
			||||||
import androidx.hilt.navigation.compose.hiltViewModel
 | 
					import androidx.hilt.navigation.compose.hiltViewModel
 | 
				
			||||||
import com.pixelized.rplexicon.R
 | 
					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.navigation.LocalScreenNavHost
 | 
				
			||||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
					import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
 | 
					import com.pixelized.rplexicon.utilitary.extentions.lexicon
 | 
				
			||||||
| 
						 | 
					@ -50,6 +60,7 @@ import java.lang.Integer.min
 | 
				
			||||||
@Stable
 | 
					@Stable
 | 
				
			||||||
data class QuestDetailUio(
 | 
					data class QuestDetailUio(
 | 
				
			||||||
    val id: Int,
 | 
					    val id: Int,
 | 
				
			||||||
 | 
					    val background: Uri?,
 | 
				
			||||||
    val title: String,
 | 
					    val title: String,
 | 
				
			||||||
    val steps: List<QuestStep>,
 | 
					    val steps: List<QuestStep>,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
| 
						 | 
					@ -68,6 +79,7 @@ data class QuestDetailUio(
 | 
				
			||||||
@Stable
 | 
					@Stable
 | 
				
			||||||
data class AnnotatedQuestDetailUio(
 | 
					data class AnnotatedQuestDetailUio(
 | 
				
			||||||
    val title: String,
 | 
					    val title: String,
 | 
				
			||||||
 | 
					    val background: Uri?,
 | 
				
			||||||
    val steps: List<AnnotatedQuestStep>,
 | 
					    val steps: List<AnnotatedQuestStep>,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    @Stable
 | 
					    @Stable
 | 
				
			||||||
| 
						 | 
					@ -89,6 +101,7 @@ private fun QuestDetailUio.annotate(): AnnotatedQuestDetailUio {
 | 
				
			||||||
    return remember {
 | 
					    return remember {
 | 
				
			||||||
        AnnotatedQuestDetailUio(
 | 
					        AnnotatedQuestDetailUio(
 | 
				
			||||||
            title = title,
 | 
					            title = title,
 | 
				
			||||||
 | 
					            background = background,
 | 
				
			||||||
            steps = annotatedSteps,
 | 
					            steps = annotatedSteps,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -143,6 +156,7 @@ private fun QuestDetailContent(
 | 
				
			||||||
    item: State<QuestDetailUio>,
 | 
					    item: State<QuestDetailUio>,
 | 
				
			||||||
    onBack: () -> Unit,
 | 
					    onBack: () -> Unit,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
 | 
					    val state = rememberLazyListState()
 | 
				
			||||||
    val annotatedQuest = item.value.annotate()
 | 
					    val annotatedQuest = item.value.annotate()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Scaffold(
 | 
					    Scaffold(
 | 
				
			||||||
| 
						 | 
					@ -164,128 +178,158 @@ private fun QuestDetailContent(
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        content = { padding ->
 | 
					        content = { padding ->
 | 
				
			||||||
            LazyColumn(
 | 
					            Surface(
 | 
				
			||||||
                modifier = Modifier.padding(padding),
 | 
					                modifier = Modifier.padding(padding),
 | 
				
			||||||
                contentPadding = PaddingValues(
 | 
					 | 
				
			||||||
                    top = 40.dp,
 | 
					 | 
				
			||||||
                    bottom = 16.dp,
 | 
					 | 
				
			||||||
                    start = 16.dp,
 | 
					 | 
				
			||||||
                    end = 16.dp
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                verticalArrangement = Arrangement.spacedBy(40.dp),
 | 
					 | 
				
			||||||
            ) {
 | 
					            ) {
 | 
				
			||||||
                item {
 | 
					                BackgroundImage(
 | 
				
			||||||
                    Column {
 | 
					                    modifier = Modifier
 | 
				
			||||||
                        Text(
 | 
					                        .fillMaxWidth()
 | 
				
			||||||
                            modifier = Modifier.fillMaxWidth(),
 | 
					                        .aspectRatio(ratio = 1f)
 | 
				
			||||||
                            textAlign = TextAlign.Center,
 | 
					                        .scrollOffset(scrollState = state) { -it / 2 },
 | 
				
			||||||
                            style = MaterialTheme.typography.displaySmall,
 | 
					                    model = { annotatedQuest.background },
 | 
				
			||||||
                            text = annotatedQuest.title,
 | 
					                )
 | 
				
			||||||
                        )
 | 
					                LazyColumn(
 | 
				
			||||||
                        Image(
 | 
					                    state = state,
 | 
				
			||||||
                            modifier = Modifier
 | 
					                    contentPadding = PaddingValues(
 | 
				
			||||||
                                .height(24.dp)
 | 
					                        top = 248.dp,
 | 
				
			||||||
                                .graphicsLayer { rotationZ = 180f }
 | 
					                        bottom = 16.dp,
 | 
				
			||||||
                                .align(Alignment.CenterHorizontally),
 | 
					                        start = 16.dp,
 | 
				
			||||||
                            painter = painterResource(id = R.drawable.art_divider_1),
 | 
					                        end = 16.dp
 | 
				
			||||||
                            contentScale = ContentScale.FillWidth,
 | 
					                    ),
 | 
				
			||||||
                            alignment = Alignment.Center,
 | 
					                    verticalArrangement = Arrangement.spacedBy(0.dp),
 | 
				
			||||||
                            colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
 | 
					                ) {
 | 
				
			||||||
                            contentDescription = null,
 | 
					                    item {
 | 
				
			||||||
                        )
 | 
					                        Column {
 | 
				
			||||||
                    }
 | 
					                            Text(
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                items(annotatedQuest.steps) { quest ->
 | 
					 | 
				
			||||||
                    Column {
 | 
					 | 
				
			||||||
                        quest.subtitle?.let { subtitle ->
 | 
					 | 
				
			||||||
                            Row(
 | 
					 | 
				
			||||||
                                modifier = Modifier.fillMaxWidth(),
 | 
					                                modifier = Modifier.fillMaxWidth(),
 | 
				
			||||||
                                verticalAlignment = Alignment.CenterVertically,
 | 
					                                textAlign = TextAlign.Center,
 | 
				
			||||||
                                horizontalArrangement = Arrangement.Center,
 | 
					                                style = MaterialTheme.typography.displaySmall,
 | 
				
			||||||
                            ) {
 | 
					                                text = annotatedQuest.title,
 | 
				
			||||||
                                Image(
 | 
					                            )
 | 
				
			||||||
                                    modifier = Modifier.graphicsLayer { rotationY = 180f },
 | 
					                            Image(
 | 
				
			||||||
                                    painter = painterResource(id = R.drawable.art_clip_1),
 | 
					                                modifier = Modifier
 | 
				
			||||||
                                    contentScale = ContentScale.FillWidth,
 | 
					                                    .height(24.dp)
 | 
				
			||||||
                                    alignment = Alignment.Center,
 | 
					                                    .graphicsLayer { rotationZ = 180f }
 | 
				
			||||||
                                    colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
 | 
					                                    .align(Alignment.CenterHorizontally),
 | 
				
			||||||
                                    contentDescription = null,
 | 
					                                painter = painterResource(id = R.drawable.art_divider_1),
 | 
				
			||||||
                                )
 | 
					                                contentScale = ContentScale.FillWidth,
 | 
				
			||||||
                                Text(
 | 
					                                alignment = Alignment.Center,
 | 
				
			||||||
                                    modifier = Modifier.padding(all = 8.dp),
 | 
					                                colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
 | 
				
			||||||
                                    textAlign = TextAlign.Center,
 | 
					                                contentDescription = null,
 | 
				
			||||||
                                    style = MaterialTheme.typography.titleLarge,
 | 
					                            )
 | 
				
			||||||
                                    text = subtitle,
 | 
					                        }
 | 
				
			||||||
                                )
 | 
					                    }
 | 
				
			||||||
                                Image(
 | 
					                    items(annotatedQuest.steps) { quest ->
 | 
				
			||||||
                                    painter = painterResource(id = R.drawable.art_clip_1),
 | 
					                        Column(
 | 
				
			||||||
                                    contentScale = ContentScale.FillWidth,
 | 
					                            verticalArrangement = Arrangement.spacedBy(16.dp),
 | 
				
			||||||
                                    alignment = Alignment.Center,
 | 
					                        ) {
 | 
				
			||||||
                                    colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
 | 
					                            quest.subtitle?.let { subtitle ->
 | 
				
			||||||
                                    contentDescription = null,
 | 
					                                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 {
 | 
				
			||||||
                        quest.giver?.let {
 | 
					                                Column {
 | 
				
			||||||
                            Text(
 | 
					                                    Text(
 | 
				
			||||||
                                modifier = Modifier.padding(top = 8.dp),
 | 
					                                        style = MaterialTheme.typography.bodyMedium,
 | 
				
			||||||
                                style = MaterialTheme.typography.bodyMedium,
 | 
					                                        fontWeight = FontWeight.Bold,
 | 
				
			||||||
                                fontWeight = FontWeight.Bold,
 | 
					                                        text = "Commanditaire",
 | 
				
			||||||
                                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(
 | 
					                            Text(
 | 
				
			||||||
                                style = MaterialTheme.typography.bodyMedium,
 | 
					                                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
 | 
					@Composable
 | 
				
			||||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
 | 
					@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
 | 
				
			||||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
 | 
					@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
 | 
				
			||||||
| 
						 | 
					@ -307,6 +351,7 @@ private class QuestDetailPreviewProvider : PreviewParameterProvider<State<QuestD
 | 
				
			||||||
        mutableStateOf(
 | 
					        mutableStateOf(
 | 
				
			||||||
            QuestDetailUio(
 | 
					            QuestDetailUio(
 | 
				
			||||||
                id = 0,
 | 
					                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",
 | 
					                title = "La chasse aux loups",
 | 
				
			||||||
                steps = listOf(
 | 
					                steps = listOf(
 | 
				
			||||||
                    QuestDetailUio.QuestStep(
 | 
					                    QuestDetailUio.QuestStep(
 | 
				
			||||||
| 
						 | 
					@ -333,6 +378,7 @@ private class QuestDetailPreviewProvider : PreviewParameterProvider<State<QuestD
 | 
				
			||||||
        mutableStateOf(
 | 
					        mutableStateOf(
 | 
				
			||||||
            QuestDetailUio(
 | 
					            QuestDetailUio(
 | 
				
			||||||
                id = 1,
 | 
					                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",
 | 
					                title = "Les enfants de la caravanes",
 | 
				
			||||||
                steps = listOf(
 | 
					                steps = listOf(
 | 
				
			||||||
                    QuestDetailUio.QuestStep(
 | 
					                    QuestDetailUio.QuestStep(
 | 
				
			||||||
| 
						 | 
					@ -348,5 +394,4 @@ private class QuestDetailPreviewProvider : PreviewParameterProvider<State<QuestD
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,7 @@ class QuestDetailViewModel @Inject constructor(
 | 
				
			||||||
        quest = mutableStateOf(
 | 
					        quest = mutableStateOf(
 | 
				
			||||||
            QuestDetailUio(
 | 
					            QuestDetailUio(
 | 
				
			||||||
                id = source.id,
 | 
					                id = source.id,
 | 
				
			||||||
 | 
					                background = source.background,
 | 
				
			||||||
                title = source.title,
 | 
					                title = source.title,
 | 
				
			||||||
                steps = source.entries.map { entry ->
 | 
					                steps = source.entries.map { entry ->
 | 
				
			||||||
                    QuestDetailUio.QuestStep(
 | 
					                    QuestDetailUio.QuestStep(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,7 +79,7 @@ private fun QuestListContent(
 | 
				
			||||||
    modifier: Modifier = Modifier,
 | 
					    modifier: Modifier = Modifier,
 | 
				
			||||||
    lazyColumnState: LazyListState,
 | 
					    lazyColumnState: LazyListState,
 | 
				
			||||||
    paddingValues: PaddingValues = PaddingValues(
 | 
					    paddingValues: PaddingValues = PaddingValues(
 | 
				
			||||||
        top = 6.dp,
 | 
					        top = 8.dp,
 | 
				
			||||||
        bottom = 8.dp + 16.dp + 56.dp + 16.dp,
 | 
					        bottom = 8.dp + 16.dp + 56.dp + 16.dp,
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    refreshState: PullRefreshState,
 | 
					    refreshState: PullRefreshState,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,8 @@
 | 
				
			||||||
package com.pixelized.rplexicon.utilitary.extentions
 | 
					package com.pixelized.rplexicon.utilitary.extentions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.net.Uri
 | 
				
			||||||
 | 
					import androidx.core.net.toUri
 | 
				
			||||||
 | 
					
 | 
				
			||||||
val String.ARG: String get() = "$this={$this}"
 | 
					val String.ARG: String get() = "$this={$this}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
val String?.highlightRegex: Regex?
 | 
					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