Bump version to 0.7.1
This commit is contained in:
parent
2f113a14d6
commit
e0593c9cbf
16 changed files with 237 additions and 49 deletions
|
|
@ -15,6 +15,7 @@ import com.pixelized.rplexicon.data.repository.character.InventoryRepository
|
|||
import com.pixelized.rplexicon.data.repository.character.ObjectActionRepository
|
||||
import com.pixelized.rplexicon.data.repository.character.SkillRepository
|
||||
import com.pixelized.rplexicon.data.repository.character.SpellRepository
|
||||
import com.pixelized.rplexicon.data.repository.lexicon.CategoryOrderRepository
|
||||
import com.pixelized.rplexicon.data.repository.lexicon.LexiconRepository
|
||||
import com.pixelized.rplexicon.data.repository.lexicon.LocationRepository
|
||||
import com.pixelized.rplexicon.data.repository.lexicon.QuestRepository
|
||||
|
|
@ -30,6 +31,7 @@ import javax.inject.Inject
|
|||
|
||||
@HiltViewModel
|
||||
class LauncherViewModel @Inject constructor(
|
||||
categoryRepository: CategoryOrderRepository,
|
||||
lexiconRepository: LexiconRepository,
|
||||
locationRepository: LocationRepository,
|
||||
questRepository: QuestRepository,
|
||||
|
|
@ -52,6 +54,14 @@ class LauncherViewModel @Inject constructor(
|
|||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val order = async {
|
||||
try {
|
||||
categoryRepository.fetchCategoryOrder()
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, exception.message, exception)
|
||||
_error.emit(FetchErrorUio.Structure(type = Type.CATEGORY_ORDER))
|
||||
}
|
||||
}
|
||||
val lexicon = async {
|
||||
try {
|
||||
lexiconRepository.fetchLexicon()
|
||||
|
|
@ -151,7 +161,7 @@ class LauncherViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
awaitAll(lexicon, location, quest)
|
||||
awaitAll(order, lexicon, location, quest)
|
||||
awaitAll(description, inventory, equipment, alteration, action, objects, spell, skill)
|
||||
|
||||
isLoading = false
|
||||
|
|
|
|||
|
|
@ -6,13 +6,17 @@ import androidx.compose.runtime.Stable
|
|||
@Stable
|
||||
data class Quest(
|
||||
val id: String,
|
||||
val group: String?,
|
||||
val title: String,
|
||||
val entries: List<QuestEntry>,
|
||||
)
|
||||
) {
|
||||
val complete = entries.all { it.complete }
|
||||
}
|
||||
|
||||
@Stable
|
||||
data class QuestEntry(
|
||||
val sheetIndex: Int,
|
||||
val group: String?,
|
||||
val title: String,
|
||||
val subtitle: String?,
|
||||
val complete: Boolean,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package com.pixelized.rplexicon.data.parser
|
||||
|
||||
import com.google.api.services.sheets.v4.model.ValueRange
|
||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||
import javax.inject.Inject
|
||||
|
||||
class CategoryOrderParser @Inject constructor() {
|
||||
|
||||
@Throws(IncompatibleSheetStructure::class)
|
||||
fun parse(sheet: ValueRange): Map<String, Map<String, Int>> = parserScope {
|
||||
val categories = hashMapOf<String, HashMap<String, Int>>()
|
||||
var currentCategory: String? = null
|
||||
|
||||
sheet.forEachLine { line ->
|
||||
val category = line.getOrNull(0)?.toItem()
|
||||
val amount = line.getOrNull(1)?.toItem()?.toIntOrNull()
|
||||
|
||||
if (category != null) {
|
||||
if (amount == null) {
|
||||
currentCategory = category
|
||||
categories[category] = hashMapOf()
|
||||
} else if (currentCategory != null) {
|
||||
categories[currentCategory]?.put(category, amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return@parserScope categories
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ class QuestParser @Inject constructor(
|
|||
val entry = QuestEntry(
|
||||
sheetIndex = index,
|
||||
title = quest,
|
||||
group = item.parse(column = GROUP),
|
||||
subtitle = item.parse(column = SUB_TITLE),
|
||||
complete = item.parseBool(column = COMPLETED) ?: false,
|
||||
questGiver = item.parse(column = QUEST_GIVER),
|
||||
|
|
@ -39,10 +40,12 @@ class QuestParser @Inject constructor(
|
|||
}
|
||||
|
||||
val quests = entries.keys.map { quest ->
|
||||
val relatedEntries = entries[quest] ?: emptyList()
|
||||
Quest(
|
||||
id = "$quest-1", // TODO refactor that when quest have ids in the google sheet.
|
||||
title = quest,
|
||||
entries = entries[quest] ?: emptyList()
|
||||
group = relatedEntries.firstNotNullOfOrNull { it.group },
|
||||
entries = relatedEntries,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -51,6 +54,7 @@ class QuestParser @Inject constructor(
|
|||
|
||||
companion object {
|
||||
private val TITLE = column("Titre")
|
||||
private val GROUP = column("Groupe")
|
||||
private val SUB_TITLE = column("Sous Titre")
|
||||
private val COMPLETED = column("Compléter")
|
||||
private val QUEST_GIVER = column("Commanditaire")
|
||||
|
|
@ -64,6 +68,7 @@ class QuestParser @Inject constructor(
|
|||
private val COLUMNS
|
||||
get() = listOf(
|
||||
TITLE,
|
||||
GROUP,
|
||||
SUB_TITLE,
|
||||
COMPLETED,
|
||||
QUEST_GIVER,
|
||||
|
|
|
|||
|
|
@ -34,6 +34,12 @@ class SheetParserScope<T> {
|
|||
sheet?.mapNotNull { it as? List<*> }?.forEachIndexed(block)
|
||||
}
|
||||
|
||||
inline fun ValueRange.forEachLine(
|
||||
crossinline block: (line: List<*>) -> Unit
|
||||
) {
|
||||
this.values.sheet()?.mapNotNull { it as? List<*> }?.forEach(block)
|
||||
}
|
||||
|
||||
inline fun ValueRange.forEachDataLine(
|
||||
columns: List<Column>,
|
||||
crossinline block: (row: List<*>) -> Unit
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ object LexiconBinder {
|
|||
const val QUEST_JOURNAL = "Journal de quêtes"
|
||||
const val MAP = "Cartes"
|
||||
const val WORLD = "Monde"
|
||||
const val ORDER = "Ordre des catégories"
|
||||
}
|
||||
|
||||
object CharacterBinder {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
package com.pixelized.rplexicon.data.repository.lexicon
|
||||
|
||||
import com.pixelized.rplexicon.data.parser.CategoryOrderParser
|
||||
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
|
||||
import com.pixelized.rplexicon.data.repository.LexiconBinder
|
||||
import com.pixelized.rplexicon.utilitary.Update
|
||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class CategoryOrderRepository @Inject constructor(
|
||||
private val googleRepository: GoogleSheetServiceRepository,
|
||||
private val parser: CategoryOrderParser,
|
||||
) {
|
||||
private val _data = MutableStateFlow<Map<String, Map<String, Int>>>(emptyMap())
|
||||
val data: StateFlow<Map<String, Map<String, Int>>> get() = _data
|
||||
|
||||
var lastSuccessFullUpdate: Update = Update.INITIAL
|
||||
private set
|
||||
|
||||
fun finLexiconOrder(quest: String?): Int {
|
||||
return _data.value[LEXICON]?.get(quest) ?: Int.MAX_VALUE
|
||||
}
|
||||
|
||||
fun findMapOrder(quest: String?): Int {
|
||||
return _data.value[MAP]?.get(quest) ?: Int.MAX_VALUE
|
||||
}
|
||||
|
||||
fun findQuestOrder(quest: String?): Int {
|
||||
return _data.value[QUEST]?.get(quest) ?: Int.MAX_VALUE
|
||||
}
|
||||
|
||||
@Throws(IncompatibleSheetStructure::class, Exception::class)
|
||||
suspend fun fetchCategoryOrder() {
|
||||
googleRepository.fetch { sheet ->
|
||||
val request = sheet.get(LexiconBinder.ID, LexiconBinder.ORDER)
|
||||
val data = parser.parse(sheet = request.execute())
|
||||
_data.tryEmit(data)
|
||||
|
||||
lastSuccessFullUpdate = Update.currentTime()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val QUEST = "Quêtes"
|
||||
private const val LEXICON = "Lexique"
|
||||
private const val MAP = "Cartes"
|
||||
}
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@ sealed class FetchErrorUio {
|
|||
OBJECT,
|
||||
SKILL,
|
||||
SPELL,
|
||||
CATEGORY_ORDER,
|
||||
LEXICON,
|
||||
LOCATION,
|
||||
QUEST,
|
||||
|
|
@ -57,6 +58,7 @@ fun HandleFetchError(
|
|||
FetchErrorUio.Structure.Type.OBJECT -> R.string.error_structure_objects
|
||||
FetchErrorUio.Structure.Type.SKILL -> R.string.error_structure_skill
|
||||
FetchErrorUio.Structure.Type.SPELL -> R.string.error_structure_spell
|
||||
FetchErrorUio.Structure.Type.CATEGORY_ORDER -> R.string.error_structure_category_order
|
||||
FetchErrorUio.Structure.Type.LEXICON -> R.string.error_structure_lexicon
|
||||
FetchErrorUio.Structure.Type.LOCATION -> R.string.error_structure_location
|
||||
FetchErrorUio.Structure.Type.QUEST -> R.string.error_structure_quest
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package com.pixelized.rplexicon.ui.screens.quest.list
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
|
||||
@Stable
|
||||
data class QuestCategoryUio(
|
||||
val title: String,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun QuestCategory(
|
||||
modifier: Modifier = Modifier,
|
||||
item: QuestCategoryUio,
|
||||
) {
|
||||
Text(
|
||||
modifier = modifier,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
fontWeight = FontWeight.Light,
|
||||
text = item.title,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.pixelized.rplexicon.ui.screens.quest.list
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
|
||||
@Stable
|
||||
data class QuestGroupUio(
|
||||
val category: QuestCategoryUio,
|
||||
val quests: List<QuestItemUio>,
|
||||
) {
|
||||
val complete = quests.all { it.complete }
|
||||
}
|
||||
|
|
@ -56,7 +56,6 @@ fun QuestItem(
|
|||
item: QuestItemUio,
|
||||
) {
|
||||
val typography = MaterialTheme.lexicon.typography
|
||||
|
||||
Box(
|
||||
modifier = modifier,
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
|
|
@ -64,19 +63,16 @@ fun QuestItem(
|
|||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
val alignModifier = Modifier
|
||||
.alignByBaseline()
|
||||
.placeholder { item.placeholder }
|
||||
Text(
|
||||
modifier = when (item.placeholder) {
|
||||
true -> Modifier.placeholder { true }
|
||||
else -> Modifier.alignByBaseline()
|
||||
},
|
||||
modifier = alignModifier,
|
||||
style = typography.base.titleMedium,
|
||||
text = if (item.complete) LOS_FULL else LOS_HOLLOW,
|
||||
)
|
||||
Text(
|
||||
modifier = when (item.placeholder) {
|
||||
true -> Modifier.placeholder { true }
|
||||
else -> Modifier.alignByBaseline()
|
||||
},
|
||||
modifier = alignModifier,
|
||||
style = typography.base.titleMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import androidx.compose.animation.AnimatedContent
|
|||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
|
|
@ -23,6 +24,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.rplexicon.ui.composable.Loader
|
||||
import com.pixelized.rplexicon.ui.composable.error.HandleFetchError
|
||||
|
|
@ -76,7 +78,7 @@ private fun QuestListContent(
|
|||
lazyColumnState: LazyListState,
|
||||
refreshState: PullRefreshState,
|
||||
refreshing: State<Boolean>,
|
||||
items: State<List<QuestItemUio>>,
|
||||
items: State<List<QuestGroupUio>>,
|
||||
onItem: (QuestItemUio) -> Unit,
|
||||
) {
|
||||
Box(
|
||||
|
|
@ -111,17 +113,29 @@ private fun QuestListContent(
|
|||
state = lazyColumnState,
|
||||
contentPadding = MaterialTheme.lexicon.dimens.itemListPadding,
|
||||
) {
|
||||
items(
|
||||
items = items.value,
|
||||
key = { it.id },
|
||||
contentType = { "Quest" },
|
||||
) {
|
||||
QuestItem(
|
||||
modifier = Modifier
|
||||
.clickable { onItem(it) }
|
||||
.cell(),
|
||||
item = it,
|
||||
)
|
||||
items.value.forEachIndexed { index, entry ->
|
||||
item(
|
||||
contentType = { "Header" },
|
||||
) {
|
||||
QuestCategory(
|
||||
modifier = Modifier
|
||||
.padding(top = if (index == 0) 0.dp else 16.dp)
|
||||
.padding(horizontal = 16.dp),
|
||||
item = entry.category,
|
||||
)
|
||||
}
|
||||
items(
|
||||
items = entry.quests,
|
||||
key = { it.id },
|
||||
contentType = { "Quest" },
|
||||
) {
|
||||
QuestItem(
|
||||
modifier = Modifier
|
||||
.clickable { onItem(it) }
|
||||
.cell(),
|
||||
item = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -152,13 +166,20 @@ private fun QuestListPreview() {
|
|||
items = remember {
|
||||
mutableStateOf(
|
||||
listOf(
|
||||
QuestItemUio.preview(
|
||||
id = "La chasse aux loups",
|
||||
title = "La chasse aux loups",
|
||||
),
|
||||
QuestItemUio.preview(
|
||||
id = "Les enfants de la caravanes",
|
||||
title = "Les enfants de la caravanes",
|
||||
QuestGroupUio(
|
||||
category = QuestCategoryUio(
|
||||
title = "Faerûn",
|
||||
),
|
||||
quests = listOf(
|
||||
QuestItemUio.preview(
|
||||
id = "La chasse aux loups",
|
||||
title = "La chasse aux loups",
|
||||
),
|
||||
QuestItemUio.preview(
|
||||
id = "Les enfants de la caravanes",
|
||||
title = "Les enfants de la caravanes",
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,22 @@
|
|||
package com.pixelized.rplexicon.ui.screens.quest.list
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.data.repository.lexicon.CategoryOrderRepository
|
||||
import com.pixelized.rplexicon.data.repository.lexicon.QuestRepository
|
||||
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
|
||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||
import com.pixelized.rplexicon.utilitary.extentions.context
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
|
@ -19,13 +24,15 @@ import javax.inject.Inject
|
|||
@HiltViewModel
|
||||
class QuestListViewModel @Inject constructor(
|
||||
private val repository: QuestRepository,
|
||||
) : ViewModel() {
|
||||
private val order: CategoryOrderRepository,
|
||||
application: Application,
|
||||
) : AndroidViewModel(application = application) {
|
||||
|
||||
private val _isLoading = mutableStateOf(false)
|
||||
val isLoading: State<Boolean> get() = _isLoading
|
||||
|
||||
private val _items = mutableStateOf<List<QuestItemUio>>(emptyList())
|
||||
val items: State<List<QuestItemUio>> get() = _items
|
||||
private val _items = mutableStateOf<List<QuestGroupUio>>(emptyList())
|
||||
val items: State<List<QuestGroupUio>> get() = _items
|
||||
|
||||
private val _error = MutableSharedFlow<FetchErrorUio>()
|
||||
val error: SharedFlow<FetchErrorUio> get() = _error
|
||||
|
|
@ -33,22 +40,35 @@ class QuestListViewModel @Inject constructor(
|
|||
init {
|
||||
viewModelScope.launch {
|
||||
launch(Dispatchers.IO) {
|
||||
repository.data.collect { items ->
|
||||
val quest = items
|
||||
.map { item ->
|
||||
QuestItemUio(
|
||||
id = item.id,
|
||||
title = item.title,
|
||||
complete = item.entries.all { it.complete },
|
||||
order.data.combine(repository.data) { _, quests -> quests }
|
||||
.collect { items ->
|
||||
val quests = items
|
||||
.asSequence()
|
||||
.sortedBy { it.title }
|
||||
.sortedBy { it.complete }
|
||||
.groupBy(
|
||||
keySelector = {
|
||||
QuestCategoryUio(
|
||||
title = it.group ?: context.getString(R.string.default_category_other),
|
||||
)
|
||||
},
|
||||
valueTransform = { item ->
|
||||
QuestItemUio(
|
||||
id = item.id,
|
||||
title = item.title,
|
||||
complete = item.complete,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
.sortedBy { it.title }
|
||||
.sortedBy { it.complete }
|
||||
.map { QuestGroupUio(category = it.key, quests = it.value) }
|
||||
.sortedBy { order.findQuestOrder(quest = it.category.title) }
|
||||
.sortedBy { it.complete }
|
||||
.toList()
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
_items.value = quest
|
||||
withContext(Dispatchers.Main) {
|
||||
_items.value = quests
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
launch(Dispatchers.IO) {
|
||||
update(force = false)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue