Add images support for quest details & quest detail UI clean up.
This commit is contained in:
parent
99d5076168
commit
82ce19ec4d
14 changed files with 209 additions and 259 deletions
|
|
@ -21,5 +21,6 @@ data class QuestEntry(
|
||||||
val groupReward: String?,
|
val groupReward: String?,
|
||||||
val individualReward: String?,
|
val individualReward: String?,
|
||||||
val description: String,
|
val description: String,
|
||||||
|
val images: List<Uri>,
|
||||||
val background: Uri?,
|
val background: Uri?,
|
||||||
)
|
)
|
||||||
|
|
@ -37,7 +37,6 @@ class QuestRepository @Inject constructor(
|
||||||
private suspend fun updateData(data: ValueRange) {
|
private suspend fun updateData(data: ValueRange) {
|
||||||
val questEntries = questParser.parse(value = data)
|
val questEntries = questParser.parse(value = data)
|
||||||
val questMap = questEntries.groupBy { it.title }
|
val questMap = questEntries.groupBy { it.title }
|
||||||
|
|
||||||
val quests = questMap.keys.mapIndexed { index, item ->
|
val quests = questMap.keys.mapIndexed { index, item ->
|
||||||
Quest(
|
Quest(
|
||||||
id = index,
|
id = index,
|
||||||
|
|
|
||||||
|
|
@ -3,79 +3,66 @@ package com.pixelized.rplexicon.repository.parser
|
||||||
|
|
||||||
import com.google.api.services.sheets.v4.model.ValueRange
|
import com.google.api.services.sheets.v4.model.ValueRange
|
||||||
import com.pixelized.rplexicon.model.QuestEntry
|
import com.pixelized.rplexicon.model.QuestEntry
|
||||||
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
|
|
||||||
import com.pixelized.rplexicon.utilitary.extentions.sheet
|
|
||||||
import com.pixelized.rplexicon.utilitary.extentions.toUriOrNull
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class QuestParser @Inject constructor() {
|
class QuestParser @Inject constructor(
|
||||||
|
private val imageParser: PortraitParser
|
||||||
fun parse(value: ValueRange): List<QuestEntry> {
|
) {
|
||||||
val sheet = value.values.sheet()
|
fun parse(value: ValueRange): List<QuestEntry> = parserScope {
|
||||||
lateinit var structure: Map<String, Int>
|
val quest = mutableListOf<QuestEntry>()
|
||||||
|
|
||||||
return sheet?.mapIndexedNotNull { index, item ->
|
|
||||||
when {
|
|
||||||
index == 0 -> {
|
|
||||||
structure = item.checkSheetStructure(model = COLUMNS)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
item is List<*> -> {
|
|
||||||
val title = item.getOrNull(structure.title) as? String
|
|
||||||
val subtitle = item.getOrNull(structure.subtitle) as? String?
|
|
||||||
val complete = item.getOrNull(structure.complete) as? String?
|
|
||||||
val questGiver = item.getOrNull(structure.questGiver) as? String?
|
|
||||||
val area = item.getOrNull(structure.area) as? String?
|
|
||||||
val groupReward = item.getOrNull(structure.groupReward) as? String?
|
|
||||||
val individualReward = item.getOrNull(structure.individualReward) as? String?
|
|
||||||
val description = item.getOrNull(structure.description) as? String
|
|
||||||
val background = item.getOrNull(structure.background) as? String?
|
|
||||||
|
|
||||||
|
value.forEachRow { index, item ->
|
||||||
|
when (index) {
|
||||||
|
0 -> updateStructure(row = item, columns = COLUMNS)
|
||||||
|
else -> {
|
||||||
|
val title = item.parse(TITLE)
|
||||||
|
val description = item.parse(DESCRIPTION)
|
||||||
if (title?.isNotEmpty() == true && description?.isNotEmpty() == true) {
|
if (title?.isNotEmpty() == true && description?.isNotEmpty() == true) {
|
||||||
QuestEntry(
|
val entry = QuestEntry(
|
||||||
sheetIndex = index,
|
sheetIndex = index,
|
||||||
title = title,
|
title = title,
|
||||||
subtitle = subtitle?.takeIf { it.isNotBlank() },
|
subtitle = item.parse(SUB_TITLE),
|
||||||
complete = complete.equals("TRUE", ignoreCase = true),
|
complete = item.parseBool(COMPLETED) ?: false,
|
||||||
questGiver = questGiver?.takeIf { it.isNotBlank() },
|
questGiver = item.parse(QUEST_GIVER),
|
||||||
area = area?.takeIf { it.isNotBlank() },
|
area = item.parse(AREA),
|
||||||
groupReward = groupReward?.takeIf { it.isNotBlank() },
|
groupReward = item.parse(GROUP_REWARD),
|
||||||
individualReward = individualReward?.takeIf { it.isNotBlank() },
|
individualReward = item.parse(INDIVIDUAL_REWARD),
|
||||||
description = description,
|
description = description,
|
||||||
background = background?.toUriOrNull(),
|
images = imageParser.parse(item.parse(IMAGE)),
|
||||||
|
background = item.parseUri(BACKGROUND),
|
||||||
)
|
)
|
||||||
} else {
|
quest.add(entry)
|
||||||
null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> null
|
|
||||||
}
|
}
|
||||||
} ?: emptyList()
|
}
|
||||||
|
|
||||||
|
quest
|
||||||
}
|
}
|
||||||
|
|
||||||
private val Map<String, Int>.title: Int get() = getValue(COLUMNS[0])
|
|
||||||
private val Map<String, Int>.subtitle: Int get() = getValue(COLUMNS[1])
|
|
||||||
private val Map<String, Int>.complete: Int get() = getValue(COLUMNS[2])
|
|
||||||
private val Map<String, Int>.questGiver: Int get() = getValue(COLUMNS[3])
|
|
||||||
private val Map<String, Int>.area: Int get() = getValue(COLUMNS[4])
|
|
||||||
private val Map<String, Int>.groupReward: Int get() = getValue(COLUMNS[5])
|
|
||||||
private val Map<String, Int>.individualReward: Int get() = getValue(COLUMNS[6])
|
|
||||||
private val Map<String, Int>.description: Int get() = getValue(COLUMNS[7])
|
|
||||||
private val Map<String, Int>.background: Int get() = getValue(COLUMNS[8])
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val TITLE = "Titre"
|
||||||
|
private const val SUB_TITLE = "Sous Titre"
|
||||||
|
private const val COMPLETED = "Compléter"
|
||||||
|
private const val QUEST_GIVER = "Commanditaire"
|
||||||
|
private const val AREA = "Lieu"
|
||||||
|
private const val GROUP_REWARD = "Récompense de groupe"
|
||||||
|
private const val INDIVIDUAL_REWARD = "Récompense individuelle"
|
||||||
|
private const val DESCRIPTION = "Description"
|
||||||
|
private const val IMAGE = "Image"
|
||||||
|
private const val BACKGROUND = "fond" // TODO
|
||||||
|
|
||||||
private val COLUMNS = listOf(
|
private val COLUMNS = listOf(
|
||||||
"Titre",
|
TITLE,
|
||||||
"Sous Titre",
|
SUB_TITLE,
|
||||||
"Compléter",
|
COMPLETED,
|
||||||
"Commanditaire",
|
QUEST_GIVER,
|
||||||
"Lieu",
|
AREA,
|
||||||
"Récompense de groupe",
|
GROUP_REWARD,
|
||||||
"Récompense individuelle",
|
INDIVIDUAL_REWARD,
|
||||||
"Description",
|
DESCRIPTION,
|
||||||
"fond"
|
IMAGE,
|
||||||
|
BACKGROUND,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package com.pixelized.rplexicon.repository.parser
|
package com.pixelized.rplexicon.repository.parser
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.core.net.toUri
|
||||||
import com.google.api.services.sheets.v4.model.ValueRange
|
import com.google.api.services.sheets.v4.model.ValueRange
|
||||||
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
|
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
|
||||||
import com.pixelized.rplexicon.utilitary.extentions.sheet
|
import com.pixelized.rplexicon.utilitary.extentions.sheet
|
||||||
|
|
@ -34,4 +36,10 @@ class SheetParserScope<T> {
|
||||||
|
|
||||||
fun List<*>.parseInt(column: String): Int? =
|
fun List<*>.parseInt(column: String): Int? =
|
||||||
parse(column)?.toIntOrNull()
|
parse(column)?.toIntOrNull()
|
||||||
|
|
||||||
|
fun List<*>.parseBool(column: String): Boolean? =
|
||||||
|
parse(column)?.equals("TRUE", ignoreCase = true)
|
||||||
|
|
||||||
|
fun List<*>.parseUri(column: String): Uri? =
|
||||||
|
parse(column)?.takeIf { it.isNotBlank() }?.toUri()
|
||||||
}
|
}
|
||||||
|
|
@ -368,16 +368,6 @@ private fun LocationContent(
|
||||||
style = MaterialTheme.typography.headlineSmall,
|
style = MaterialTheme.typography.headlineSmall,
|
||||||
text = item.value.name,
|
text = item.value.name,
|
||||||
)
|
)
|
||||||
Image(
|
|
||||||
modifier = Modifier
|
|
||||||
.height(24.dp)
|
|
||||||
.graphicsLayer { rotationZ = 180f },
|
|
||||||
painter = painterResource(id = R.drawable.art_divider_1),
|
|
||||||
contentScale = ContentScale.FillWidth,
|
|
||||||
alignment = Alignment.Center,
|
|
||||||
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,19 @@ package com.pixelized.rplexicon.ui.screens.quest.detail
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.ScrollState
|
import androidx.compose.foundation.ScrollState
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
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
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.aspectRatio
|
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.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
|
@ -28,18 +29,14 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
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.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.draw.shadow
|
import androidx.compose.ui.draw.shadow
|
||||||
import androidx.compose.ui.graphics.Color
|
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.layout.ContentScale
|
||||||
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.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
|
@ -49,16 +46,19 @@ 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 androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.pixelized.rplexicon.R
|
import com.pixelized.rplexicon.R
|
||||||
|
import com.pixelized.rplexicon.ui.composable.AsyncImage
|
||||||
import com.pixelized.rplexicon.ui.composable.BackgroundImage
|
import com.pixelized.rplexicon.ui.composable.BackgroundImage
|
||||||
|
import com.pixelized.rplexicon.ui.composable.FullScreenImageHandler
|
||||||
|
import com.pixelized.rplexicon.ui.composable.FullScreenImageViewModel
|
||||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||||
import com.pixelized.rplexicon.ui.navigation.screens.navigateToLexiconDetail
|
import com.pixelized.rplexicon.ui.navigation.screens.navigateToLexiconDetail
|
||||||
import com.pixelized.rplexicon.ui.navigation.screens.navigateToLocationDetail
|
import com.pixelized.rplexicon.ui.navigation.screens.navigateToLocationDetail
|
||||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||||
import com.pixelized.rplexicon.utilitary.LOS_FULL
|
import com.pixelized.rplexicon.utilitary.LOS_FULL
|
||||||
import com.pixelized.rplexicon.utilitary.LOS_HOLLOW
|
import com.pixelized.rplexicon.utilitary.LOS_HOLLOW
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.annotateWithDropCap
|
||||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
|
import com.pixelized.rplexicon.utilitary.extentions.lexicon
|
||||||
import com.pixelized.rplexicon.utilitary.extentions.scrollOffset
|
import com.pixelized.rplexicon.utilitary.extentions.scrollOffset
|
||||||
import java.lang.Integer.min
|
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class QuestDetailUio(
|
data class QuestDetailUio(
|
||||||
|
|
@ -70,82 +70,22 @@ data class QuestDetailUio(
|
||||||
) {
|
) {
|
||||||
@Stable
|
@Stable
|
||||||
data class QuestStep(
|
data class QuestStep(
|
||||||
val subtitle: String?,
|
val subtitle: String? = null,
|
||||||
val giverId: Int? = null,
|
val giverId: Int? = null,
|
||||||
val giver: String?,
|
val giver: String? = null,
|
||||||
val placeId: Int? = null,
|
val placeId: Int? = null,
|
||||||
val place: String?,
|
val place: String? = null,
|
||||||
val globalReward: String?,
|
val globalReward: String? = null,
|
||||||
val individualReward: String?,
|
val individualReward: String? = null,
|
||||||
|
val images: List<Uri> = emptyList(),
|
||||||
val description: String,
|
val description: String,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Stable
|
|
||||||
data class AnnotatedQuestDetailUio(
|
|
||||||
val title: String,
|
|
||||||
val completed: Boolean,
|
|
||||||
val background: Uri?,
|
|
||||||
val steps: List<AnnotatedQuestStep>,
|
|
||||||
) {
|
|
||||||
@Stable
|
|
||||||
data class AnnotatedQuestStep(
|
|
||||||
val subtitle: String?,
|
|
||||||
val giverId: Int?,
|
|
||||||
val giver: String?,
|
|
||||||
val placeId: Int?,
|
|
||||||
val place: String?,
|
|
||||||
val individualReward: String?,
|
|
||||||
val globalReward: String?,
|
|
||||||
val description: AnnotatedString,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
@Stable
|
|
||||||
private fun QuestDetailUio.annotate(): AnnotatedQuestDetailUio {
|
|
||||||
val annotatedSteps = steps.map { it.annotate() }
|
|
||||||
return remember {
|
|
||||||
AnnotatedQuestDetailUio(
|
|
||||||
completed = completed,
|
|
||||||
title = title,
|
|
||||||
background = background,
|
|
||||||
steps = annotatedSteps,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
@Stable
|
|
||||||
private fun QuestDetailUio.QuestStep.annotate(): AnnotatedQuestDetailUio.AnnotatedQuestStep {
|
|
||||||
val typography = MaterialTheme.lexicon.typography
|
|
||||||
|
|
||||||
return remember {
|
|
||||||
AnnotatedQuestDetailUio.AnnotatedQuestStep(
|
|
||||||
subtitle = subtitle,
|
|
||||||
giverId = giverId,
|
|
||||||
giver = giver,
|
|
||||||
placeId = placeId,
|
|
||||||
place = place,
|
|
||||||
globalReward = globalReward,
|
|
||||||
individualReward = individualReward,
|
|
||||||
description = AnnotatedString(
|
|
||||||
text = description,
|
|
||||||
spanStyles = listOf(
|
|
||||||
AnnotatedString.Range(
|
|
||||||
item = typography.bodyDropCapSpan,
|
|
||||||
start = 0,
|
|
||||||
end = min(1, description.length),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun QuestDetailScreen(
|
fun QuestDetailScreen(
|
||||||
viewModel: QuestDetailViewModel = hiltViewModel(),
|
viewModel: QuestDetailViewModel = hiltViewModel(),
|
||||||
|
imageViewModel: FullScreenImageViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val screen = LocalScreenNavHost.current
|
val screen = LocalScreenNavHost.current
|
||||||
|
|
||||||
|
|
@ -156,6 +96,11 @@ fun QuestDetailScreen(
|
||||||
onBack = { screen.popBackStack() },
|
onBack = { screen.popBackStack() },
|
||||||
onGiver = { screen.navigateToLexiconDetail(id = it) },
|
onGiver = { screen.navigateToLexiconDetail(id = it) },
|
||||||
onLocation = { screen.navigateToLocationDetail(id = it) },
|
onLocation = { screen.navigateToLocationDetail(id = it) },
|
||||||
|
onImage = { imageViewModel.showDetail(it) }
|
||||||
|
)
|
||||||
|
|
||||||
|
FullScreenImageHandler(
|
||||||
|
viewModel = imageViewModel,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -169,8 +114,9 @@ private fun QuestDetailContent(
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
onGiver: (Int) -> Unit,
|
onGiver: (Int) -> Unit,
|
||||||
onLocation: (Int) -> Unit,
|
onLocation: (Int) -> Unit,
|
||||||
|
onImage: (Uri) -> Unit,
|
||||||
) {
|
) {
|
||||||
val annotatedQuest = item.value.annotate()
|
val quest = item.value
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -203,9 +149,9 @@ private fun QuestDetailContent(
|
||||||
) {
|
) {
|
||||||
BackgroundImage(
|
BackgroundImage(
|
||||||
modifier = Modifier.matchParentSize(),
|
modifier = Modifier.matchParentSize(),
|
||||||
model = annotatedQuest.background,
|
model = quest.background,
|
||||||
)
|
)
|
||||||
if (annotatedQuest.completed) {
|
if (quest.completed) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.TopEnd)
|
.align(Alignment.TopEnd)
|
||||||
|
|
@ -213,7 +159,7 @@ private fun QuestDetailContent(
|
||||||
.rotate(degrees = 12f),
|
.rotate(degrees = 12f),
|
||||||
style = MaterialTheme.lexicon.typography.stamp,
|
style = MaterialTheme.lexicon.typography.stamp,
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.35f),
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.35f),
|
||||||
text = "Completed",
|
text = stringResource(id = R.string.quest_detail_completed),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -221,80 +167,55 @@ private fun QuestDetailContent(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.verticalScroll(state)
|
.verticalScroll(state)
|
||||||
.padding(
|
.padding(
|
||||||
top = when (annotatedQuest.background) {
|
top = when {
|
||||||
null -> 16.dp
|
quest.background == null && quest.completed -> 96.dp
|
||||||
|
quest.background == null -> 16.dp
|
||||||
else -> MaterialTheme.lexicon.dimens.detailPadding
|
else -> MaterialTheme.lexicon.dimens.detailPadding
|
||||||
},
|
},
|
||||||
end = 16.dp,
|
|
||||||
bottom = 16.dp,
|
bottom = 16.dp,
|
||||||
start = 16.dp,
|
|
||||||
),
|
),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(space = 16.dp),
|
||||||
) {
|
) {
|
||||||
Column {
|
Text(
|
||||||
Text(
|
modifier = Modifier
|
||||||
modifier = Modifier.fillMaxWidth(),
|
.fillMaxWidth()
|
||||||
textAlign = TextAlign.Center,
|
.padding(horizontal = 16.dp)
|
||||||
style = MaterialTheme.typography.displaySmall,
|
.padding(bottom = 16.dp),
|
||||||
text = annotatedQuest.title,
|
textAlign = TextAlign.Center,
|
||||||
)
|
style = MaterialTheme.typography.headlineLarge,
|
||||||
Image(
|
text = quest.title.annotateWithDropCap(
|
||||||
modifier = Modifier
|
style = MaterialTheme.lexicon.typography.headlineLargeDropCap,
|
||||||
.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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
annotatedQuest.steps.forEach { quest ->
|
quest.steps.forEach { quest ->
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
) {
|
) {
|
||||||
quest.subtitle?.let { subtitle ->
|
quest.subtitle?.let { subtitle ->
|
||||||
Row(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.padding(horizontal = 16.dp)
|
||||||
.padding(top = 64.dp),
|
.padding(top = 16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
horizontalArrangement = Arrangement.Center,
|
textAlign = TextAlign.Center,
|
||||||
) {
|
overflow = TextOverflow.Ellipsis,
|
||||||
Image(
|
maxLines = 3,
|
||||||
modifier = Modifier.graphicsLayer { rotationY = 180f },
|
text = subtitle.annotateWithDropCap(
|
||||||
painter = painterResource(id = R.drawable.art_clip_1),
|
style = MaterialTheme.lexicon.typography.titleLargeDropCap,
|
||||||
contentScale = ContentScale.FillWidth,
|
),
|
||||||
alignment = Alignment.Center,
|
)
|
||||||
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface),
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
modifier = Modifier
|
|
||||||
.weight(weight = 1f, fill = false)
|
|
||||||
.padding(horizontal = 8.dp),
|
|
||||||
style = MaterialTheme.typography.titleLarge,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
maxLines = 3,
|
|
||||||
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(
|
Column(
|
||||||
modifier = Modifier.clickable(
|
modifier = Modifier
|
||||||
enabled = quest.giverId != null,
|
.fillMaxWidth()
|
||||||
onClick = { quest.giverId?.let { onGiver(it) } }
|
.clickable(
|
||||||
)
|
enabled = quest.giverId != null,
|
||||||
|
onClick = { quest.giverId?.let { onGiver(it) } }
|
||||||
|
)
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
|
@ -310,12 +231,16 @@ private fun QuestDetailContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
quest.place?.let {
|
quest.place?.let {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.clickable(
|
modifier = Modifier
|
||||||
enabled = quest.placeId != null,
|
.fillMaxWidth()
|
||||||
onClick = { quest.placeId?.let { onLocation(it) } }
|
.clickable(
|
||||||
)
|
enabled = quest.placeId != null,
|
||||||
|
onClick = { quest.placeId?.let { onLocation(it) } }
|
||||||
|
)
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
|
@ -331,8 +256,11 @@ private fun QuestDetailContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
quest.globalReward?.let {
|
quest.globalReward?.let {
|
||||||
Column {
|
Column(
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
|
|
@ -340,12 +268,15 @@ private fun QuestDetailContent(
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
text = it,
|
text = "$LOS_HOLLOW $it",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
quest.individualReward?.let {
|
quest.individualReward?.let {
|
||||||
Column {
|
Column(
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
|
|
@ -353,15 +284,35 @@ private fun QuestDetailContent(
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
text = it,
|
text = "$LOS_HOLLOW $it",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
text = quest.description,
|
text = quest.description.annotateWithDropCap(
|
||||||
|
style = MaterialTheme.lexicon.typography.bodyMediumDropCap,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (quest.images.isNotEmpty()) {
|
||||||
|
LazyRow(
|
||||||
|
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
) {
|
||||||
|
items(items = quest.images) {
|
||||||
|
AsyncImage(
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable { onImage(it) }
|
||||||
|
.height(height = 160.dp),
|
||||||
|
contentScale = ContentScale.FillHeight,
|
||||||
|
model = it,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -383,6 +334,7 @@ private fun QuestDetailPreview(
|
||||||
onBack = { },
|
onBack = { },
|
||||||
onGiver = { },
|
onGiver = { },
|
||||||
onLocation = { },
|
onLocation = { },
|
||||||
|
onImage = { },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -402,15 +354,11 @@ private class QuestDetailPreviewProvider : PreviewParameterProvider<State<QuestD
|
||||||
giver = "Sergent d'arme",
|
giver = "Sergent d'arme",
|
||||||
place = "DaggerFord",
|
place = "DaggerFord",
|
||||||
individualReward = "5po",
|
individualReward = "5po",
|
||||||
globalReward = null,
|
|
||||||
description = "Des nobles participant aux festivités de DaggerFord aurait entendu des loups dans la forêt proche. Sur ordre du baron, cette forêt doit être fouillée bien que depuis 300 ans, aucun loup n'y ait été vu.",
|
description = "Des nobles participant aux festivités de DaggerFord aurait entendu des loups dans la forêt proche. Sur ordre du baron, cette forêt doit être fouillée bien que depuis 300 ans, aucun loup n'y ait été vu.",
|
||||||
),
|
),
|
||||||
QuestDetailUio.QuestStep(
|
QuestDetailUio.QuestStep(
|
||||||
subtitle = "Partie 2",
|
subtitle = "Partie 2",
|
||||||
giver = "Sergent d'arme",
|
|
||||||
place = "DaggerFord",
|
|
||||||
individualReward = "5po. Bonus de 1po par loup.",
|
individualReward = "5po. Bonus de 1po par loup.",
|
||||||
globalReward = null,
|
|
||||||
description = "Nous devons rapporter la dépouille d'un loup pour prouver au sergent que nous avons tué ces bêtes.",
|
description = "Nous devons rapporter la dépouille d'un loup pour prouver au sergent que nous avons tué ces bêtes.",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -424,11 +372,7 @@ private class QuestDetailPreviewProvider : PreviewParameterProvider<State<QuestD
|
||||||
title = "Les enfants de la caravanes",
|
title = "Les enfants de la caravanes",
|
||||||
steps = listOf(
|
steps = listOf(
|
||||||
QuestDetailUio.QuestStep(
|
QuestDetailUio.QuestStep(
|
||||||
subtitle = null,
|
globalReward = "Pouvoir se regarder dans une glace",
|
||||||
giver = null,
|
|
||||||
place = null,
|
|
||||||
individualReward = "Pouvoir se regarder dans une glace",
|
|
||||||
globalReward = null,
|
|
||||||
description = "Une meute de lycan a massacré une caravane marchande quittant DaggerFord. Leur dessin n'est pas encore clair, mais ils ont enlevé les enfants. Nous les pourchassons afin de les sauver.",
|
description = "Une meute de lycan a massacré une caravane marchande quittant DaggerFord. Leur dessin n'est pas encore clair, mais ils ont enlevé les enfants. Nous les pourchassons afin de les sauver.",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ class QuestDetailViewModel @Inject constructor(
|
||||||
globalReward = entry.groupReward,
|
globalReward = entry.groupReward,
|
||||||
individualReward = entry.individualReward,
|
individualReward = entry.individualReward,
|
||||||
description = entry.description,
|
description = entry.description,
|
||||||
|
images = entry.images,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,11 @@ import com.pixelized.rplexicon.repository.data.lexicon.QuestRepository
|
||||||
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
|
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
|
||||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
|
|
@ -30,25 +32,34 @@ class QuestListViewModel @Inject constructor(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
launch {
|
launch(Dispatchers.IO) {
|
||||||
repository.data.collect { items ->
|
repository.data.collect { items ->
|
||||||
_items.value = items.map { item ->
|
val quest = items
|
||||||
QuestItemUio(
|
.map { item ->
|
||||||
id = item.id,
|
QuestItemUio(
|
||||||
title = item.title,
|
id = item.id,
|
||||||
complete = item.entries.all { it.complete },
|
title = item.title,
|
||||||
)
|
complete = item.entries.all { it.complete },
|
||||||
}.sortedBy { it.title }
|
)
|
||||||
|
}
|
||||||
|
.sortedBy { it.title }
|
||||||
|
.sortedBy { it.complete }
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
_items.value = quest
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
launch {
|
launch(Dispatchers.IO) {
|
||||||
update(force = false)
|
update(force = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun update(force: Boolean) {
|
suspend fun update(force: Boolean) {
|
||||||
_isLoading.value = true
|
withContext(context = Dispatchers.Main) {
|
||||||
|
_isLoading.value = true
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (force || repository.lastSuccessFullUpdate.shouldUpdate()) {
|
if (force || repository.lastSuccessFullUpdate.shouldUpdate()) {
|
||||||
repository.fetchQuests()
|
repository.fetchQuests()
|
||||||
|
|
@ -64,9 +75,11 @@ class QuestListViewModel @Inject constructor(
|
||||||
Log.e(TAG, exception.message, exception)
|
Log.e(TAG, exception.message, exception)
|
||||||
_error.emit(FetchErrorUio.Default)
|
_error.emit(FetchErrorUio.Default)
|
||||||
}
|
}
|
||||||
// clean the laoding state
|
// clean the loading state
|
||||||
finally {
|
finally {
|
||||||
_isLoading.value = false
|
withContext(context = Dispatchers.Main) {
|
||||||
|
_isLoading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ data class LexiconDimens(
|
||||||
fun lexiconDimen(
|
fun lexiconDimen(
|
||||||
density: Density,
|
density: Density,
|
||||||
itemHeight: Dp = 52.dp,
|
itemHeight: Dp = 52.dp,
|
||||||
detailPadding: Dp = 248.dp,
|
detailPadding: Dp = 320.dp,
|
||||||
itemListPadding: PaddingValues = PaddingValues(
|
itemListPadding: PaddingValues = PaddingValues(
|
||||||
top = 8.dp,
|
top = 8.dp,
|
||||||
bottom = 8.dp + 16.dp + 56.dp + 16.dp,
|
bottom = 8.dp + 16.dp + 56.dp + 16.dp,
|
||||||
|
|
|
||||||
|
|
@ -28,18 +28,43 @@ class LexiconTypography(
|
||||||
val stamp: TextStyle = base.headlineLarge.copy(
|
val stamp: TextStyle = base.headlineLarge.copy(
|
||||||
fontFamily = stampFontFamily,
|
fontFamily = stampFontFamily,
|
||||||
),
|
),
|
||||||
|
@Deprecated("")
|
||||||
val bodyDropCap: TextStyle = base.headlineLarge.copy(
|
val bodyDropCap: TextStyle = base.headlineLarge.copy(
|
||||||
fontFamily = zallFontFamily,
|
fontFamily = zallFontFamily,
|
||||||
baselineShift = BaselineShift(-0.1f),
|
baselineShift = BaselineShift(-0.1f),
|
||||||
letterSpacing = (-3).sp
|
letterSpacing = (-3).sp
|
||||||
),
|
),
|
||||||
|
@Deprecated("")
|
||||||
val titleDropCap: TextStyle = base.displayMedium.copy(
|
val titleDropCap: TextStyle = base.displayMedium.copy(
|
||||||
fontFamily = zallFontFamily,
|
fontFamily = zallFontFamily,
|
||||||
baselineShift = BaselineShift(-0.1f),
|
baselineShift = BaselineShift(-0.1f),
|
||||||
letterSpacing = (-4).sp
|
letterSpacing = (-4).sp
|
||||||
),
|
),
|
||||||
|
@Deprecated("")
|
||||||
val bodyDropCapSpan: SpanStyle = bodyDropCap.toSpanStyle(),
|
val bodyDropCapSpan: SpanStyle = bodyDropCap.toSpanStyle(),
|
||||||
|
@Deprecated("")
|
||||||
val titleDropCapSpan: SpanStyle = titleDropCap.toSpanStyle(),
|
val titleDropCapSpan: SpanStyle = titleDropCap.toSpanStyle(),
|
||||||
|
|
||||||
|
val bodyMediumDropCap: SpanStyle = base.bodyMedium.toDropCapSpan(
|
||||||
|
sizeRatio = 1.8f,
|
||||||
|
),
|
||||||
|
|
||||||
|
val titleLargeDropCap: SpanStyle = base.titleLarge.toDropCapSpan(
|
||||||
|
sizeRatio = 1.4f,
|
||||||
|
),
|
||||||
|
|
||||||
|
val headlineLargeDropCap: SpanStyle = base.headlineLarge.toDropCapSpan(
|
||||||
|
sizeRatio = 1.2f,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun TextStyle.toDropCapSpan(
|
||||||
|
sizeRatio : Float,
|
||||||
|
): SpanStyle {
|
||||||
|
return copy(
|
||||||
|
fontFamily = zallFontFamily,
|
||||||
|
fontSize = fontSize * sizeRatio,
|
||||||
|
).toSpanStyle()
|
||||||
|
}
|
||||||
|
|
||||||
fun lexiconTypography() = LexiconTypography()
|
fun lexiconTypography() = LexiconTypography()
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="48dp"
|
|
||||||
android:height="16dp"
|
|
||||||
android:viewportWidth="1280"
|
|
||||||
android:viewportHeight="425">
|
|
||||||
<path
|
|
||||||
android:fillColor="#000000"
|
|
||||||
android:pathData="M961.5,1.9c-9.1,3.3 -16.9,7.2 -25,12.6 -23.2,15.4 -34.6,32.5 -44.4,67 -1.7,6.1 -3.8,12.7 -4.6,14.8 -0.8,2 -1.5,6.6 -1.5,10.1 0,12.6 8.9,24.2 24.7,32.3 9.9,5.1 18.6,7.3 29.4,7.3 11.1,-0 16.6,-1.9 21.7,-7.4 3.4,-3.7 4.1,-5.4 5.2,-11.3 0.9,-5.5 0.9,-8 -0.1,-11.2 -2.7,-9.2 -8.7,-10.1 -28.1,-4.3 -17.4,5.3 -22.7,6 -27.7,3.8 -5,-2.3 -7.1,-6.2 -7.8,-14.4 -1.1,-13.4 3.8,-32.3 12.5,-48.2 5.5,-10 20.2,-25.5 29.4,-30.9 17.4,-10.2 35.8,-13.2 58.2,-9.6 47.7,7.7 88.8,46.1 102.8,96 4.1,14.3 4.7,17.4 5.9,30 2.4,25 -1.3,47.7 -10.9,67 -4.8,9.7 -11,18.7 -29.4,42.9 -40.1,52.4 -60.3,73.6 -96,100.4 -29.1,21.8 -49,32.2 -61.8,32.2 -11.3,-0 -24,-3.4 -73.2,-19.6 -60.6,-19.9 -100.3,-34.7 -154.8,-57.7 -29.3,-12.3 -54.7,-24.4 -108,-51.3 -23.9,-12.1 -52,-25.7 -62.5,-30.3 -58.4,-25.8 -108.4,-38.8 -165.8,-43.2 -20.2,-1.5 -70.9,-0.7 -94.7,1.6 -17.2,1.6 -21.2,2.4 -47.5,8.4 -26.1,5.9 -48.3,7.8 -54.5,4.6 -3.6,-1.8 -3.9,-4.2 -0.7,-5.5 51.5,-21.1 65.9,-26.1 94.1,-32.6 50.5,-11.6 88.6,-16.3 137.3,-17.1 78.9,-1.3 144.1,8 230.3,32.9 33.6,9.7 61.3,20.6 115.7,45.6 23.5,10.8 72.9,31.3 103.1,42.8 12,4.5 39.1,12.5 50.7,14.9 13.3,2.7 30.1,3.8 42.5,2.5 6.3,-0.6 17.6,-1.5 25,-2 22.4,-1.7 35.9,-6.1 49.3,-16.3 11.3,-8.6 18.1,-19.1 17.5,-27 -0.8,-11.1 -14.9,-7.2 -36.8,10.1 -11.8,9.3 -20,13.8 -34.5,18.5l-13,4.2 -20,-0.1c-18.1,-0 -21.1,-0.3 -31.5,-2.7 -27.6,-6.5 -54.9,-16.3 -107.1,-38.5 -16.7,-7.2 -38.5,-16.4 -48.4,-20.5 -9.9,-4.2 -22.9,-9.9 -28.9,-12.7 -40.8,-19.1 -70.8,-29.6 -119.1,-41.5 -23,-5.7 -43.3,-9.6 -70.2,-13.5 -39,-5.7 -56,-7.1 -97.4,-7.7 -76.2,-1.2 -122.4,4.5 -194.4,23.7 -30.9,8.3 -33.1,9.1 -63.5,23.5 -27.6,13.1 -32.2,14.5 -50.2,15.4 -13.6,0.7 -15.1,1 -21,3.9 -11.4,5.6 -16.6,12.1 -25.2,31.4 -4.1,9.2 -10.4,15.9 -17.2,18.2 -5.2,1.7 -13.7,8.4 -27.6,21.3 -12.5,11.6 -12.2,11.1 -9.5,16.4 3,5.7 6.3,6.3 12.1,2.1 12.3,-8.8 40.8,-24.2 44.8,-24.2 3.2,-0 5.9,2 7.4,5.5 1.5,3.6 7.6,8.4 14.7,11.5 7.2,3.2 21.3,5.2 36.2,5.4 13.1,0.1 14.3,-0.1 18.9,-2.4 6.1,-3.1 8.4,-6.1 9.2,-12.1 0.5,-4 0.3,-5 -1.5,-6.8 -2.8,-2.8 -4.6,-2.4 -19.7,3.5 -20,7.9 -28,8.8 -34.1,4.1 -1.4,-1.1 -5.2,-3.3 -8.4,-4.8 -14.2,-6.7 -10.4,-15.5 9.1,-21 4,-1.2 15.7,-6 25.9,-10.9 35.7,-16.8 48.2,-21 56.8,-18.9 11.2,2.7 27,11.6 50.3,28.4 4.7,3.4 14.4,9.5 21.5,13.7 23.9,13.8 101.5,52.4 143.5,71.3 22.8,10.3 48.1,21.8 56.1,25.6 28,13.2 71.3,28.2 129.4,44.8 77.6,22.1 133.6,30.3 195.5,28.7 23.7,-0.6 50.9,-3.1 69.4,-6.2 16.7,-2.8 28.1,-6.1 46.1,-13.2 25.4,-9.9 28,-10.6 39.5,-9.9 5.2,0.3 15.6,2 23,3.6 16.8,3.8 23.5,4.7 42.9,6.1 19.9,1.4 62.4,1.4 77.1,0.1 48.2,-4.5 93.9,-17.2 125.4,-34.8 24.4,-13.6 39.2,-25.4 69.6,-55.3 10.6,-10.4 16.4,-27.4 15.8,-45.8 -0.2,-9.1 -0.7,-11.5 -2.8,-15.6 -3.2,-6.3 -8.2,-9.1 -16.1,-9.1 -13.1,-0 -15.3,5.6 -10.3,26.8 3.6,15.2 3.9,28.4 0.6,34.5 -3.1,5.9 -20.7,23.3 -32.2,31.8 -12.4,9.3 -37,24 -54.5,32.5 -21.1,10.4 -39.2,15.8 -67,20.1 -13.8,2.1 -18.3,2.3 -54.5,2.2 -32.9,-0 -43.4,-0.4 -63,-2.2 -32.4,-3.1 -41.8,-4.3 -40,-5.4 53.4,-31.2 78.9,-50.2 105.1,-77.9 23.9,-25.2 38.3,-43.5 52.9,-67 20.7,-33.3 29,-61.3 27.7,-93.4 -2,-48.8 -25.2,-92.7 -65.7,-124.4 -6.3,-5 -22.7,-13.1 -31.5,-15.6 -21.3,-6.1 -55.1,-8.5 -66,-4.6zM351.5,190.8c32,2 64.4,9.2 115,25.4 37.4,12 60,22.2 110,49.8 43.3,23.9 171.4,80.5 193.8,85.5 4,0.9 20.7,6.2 37.2,11.7 29.9,10 52.5,17 74.8,23 6.4,1.7 11.7,3.5 11.7,3.9 0,0.5 -1.7,1.1 -3.7,1.4 -2.1,0.4 -10.2,2.6 -18,5 -34.9,10.8 -64.3,14.6 -113.4,14.5 -30.8,-0 -43.5,-0.6 -76.9,-4.1 -26.7,-2.7 -42,-5.3 -59,-9.9 -80,-21.6 -116,-32.8 -136,-42.3 -6.3,-3 -15.5,-6.7 -20.5,-8.2 -24.7,-7.5 -94.4,-40.7 -186,-88.4 -24.4,-12.8 -34.9,-18.8 -40.2,-23.2 -4,-3.2 -7.9,-5.9 -8.6,-5.9 -2.5,-0 -40.7,-21.7 -40.7,-23.1 0,-0.4 3,-1.2 6.8,-1.9 3.7,-0.6 14.4,-3.1 23.7,-5.5 20.4,-5.3 33.8,-7.4 54.5,-8.5 18.8,-0.9 53.7,-0.6 75.5,0.8zM143.8,202.7c3.5,0.9 5.9,1.9 5.5,2.3 -0.4,0.4 -6,2.4 -12.3,4.5 -6.2,2 -14.5,5.2 -18.2,7.1 -3.7,1.9 -10,4.7 -14,6.3 -4,1.6 -13.6,5.9 -21.3,9.5 -20,9.4 -21.8,8.9 -5.8,-1.6 11.1,-7.3 14.8,-9.5 32.3,-19.8 6.9,-4.1 13.9,-8.2 15.5,-9.2 3.4,-2 7.6,-1.8 18.3,0.9zM96.5,203c-0.4,0.6 -2.7,2.1 -5.3,3.4 -2.6,1.3 -5.6,3.4 -6.7,4.6 -2.4,2.6 -7.2,5.3 -8.7,4.8 -1.2,-0.4 9.9,-9.1 15.4,-12.1 3.6,-1.9 6.3,-2.3 5.3,-0.7z"
|
|
||||||
android:strokeColor="#00000000" />
|
|
||||||
</vector>
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -78,6 +78,7 @@
|
||||||
<string name="search_item_tags">Mots clés :</string>
|
<string name="search_item_tags">Mots clés :</string>
|
||||||
|
|
||||||
<string name="quest_detail_title">Détails de quête</string>
|
<string name="quest_detail_title">Détails de quête</string>
|
||||||
|
<string name="quest_detail_completed">Complétée</string>
|
||||||
<string name="quest_detail_giver">Commanditaire :</string>
|
<string name="quest_detail_giver">Commanditaire :</string>
|
||||||
<string name="quest_detail_area">Lieu :</string>
|
<string name="quest_detail_area">Lieu :</string>
|
||||||
<string name="quest_detail_individual_reward">Récompense individuelle :</string>
|
<string name="quest_detail_individual_reward">Récompense individuelle :</string>
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@
|
||||||
<string name="search_item_tags">Tags:</string>
|
<string name="search_item_tags">Tags:</string>
|
||||||
|
|
||||||
<string name="quest_detail_title">Quest details</string>
|
<string name="quest_detail_title">Quest details</string>
|
||||||
|
<string name="quest_detail_completed">Completed</string>
|
||||||
<string name="quest_detail_giver">Quest giver:</string>
|
<string name="quest_detail_giver">Quest giver:</string>
|
||||||
<string name="quest_detail_area">Area:</string>
|
<string name="quest_detail_area">Area:</string>
|
||||||
<string name="quest_detail_individual_reward">Individual reward:</string>
|
<string name="quest_detail_individual_reward">Individual reward:</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue