Update the map feature.

This commit is contained in:
Thomas Andres Gomez 2023-11-10 15:29:55 +01:00
parent 86b2054f0a
commit 53969a4174
122 changed files with 775 additions and 785 deletions

View file

@ -35,4 +35,4 @@
# This rule will properly ProGuard all the model classes in
# the package com.yourcompany.models.
# Modify this rule to fit the structure of your app.
-keepclassmembers class com.pixelized.rplexicon.model.CharacterSheetFire.** { *; }
-keepclassmembers class com.pixelized.rplexicon.data.model.CharacterSheetFire.** { *; }

View file

@ -6,18 +6,18 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.repository.data.character.ActionRepository
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.repository.data.character.EquipmentRepository
import com.pixelized.rplexicon.repository.data.character.InventoryRepository
import com.pixelized.rplexicon.repository.data.character.ObjectActionRepository
import com.pixelized.rplexicon.repository.data.character.SkillRepository
import com.pixelized.rplexicon.repository.data.character.SpellRepository
import com.pixelized.rplexicon.repository.data.lexicon.LexiconRepository
import com.pixelized.rplexicon.repository.data.lexicon.LocationRepository
import com.pixelized.rplexicon.repository.data.lexicon.QuestRepository
import com.pixelized.rplexicon.data.repository.character.ActionRepository
import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.data.repository.character.EquipmentRepository
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.LexiconRepository
import com.pixelized.rplexicon.data.repository.lexicon.LocationRepository
import com.pixelized.rplexicon.data.repository.lexicon.QuestRepository
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio.Structure.Type
import dagger.hilt.android.lifecycle.HiltViewModel
@ -62,7 +62,7 @@ class LauncherViewModel @Inject constructor(
}
val location = async {
try {
locationRepository.fetchLocation()
locationRepository.fetchMap()
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
_error.emit(FetchErrorUio.Structure(type = Type.LOCATION))

View file

@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Snackbar
@ -20,9 +21,10 @@ import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember
@ -42,6 +44,8 @@ import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.ddBorder
import dagger.hilt.android.AndroidEntryPoint
val NO_WINDOW_INSETS = WindowInsets(0, 0, 0, 0)
val LocalActivity = staticCompositionLocalOf<Activity> {
error("Activity not available")
}
@ -51,7 +55,6 @@ val LocalSnack = staticCompositionLocalOf<SnackbarHostState> {
val LocalRollOverlay = compositionLocalOf<BlurredRollOverlayHostState> {
error("LocalRollOverlay not yet ready")
}
val NO_WINDOW_INSETS = WindowInsets(0, 0, 0, 0)
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@ -113,6 +116,15 @@ class MainActivity : ComponentActivity() {
modifier = Modifier.navigationBarsPadding(),
hostState = LocalSnack.current,
) {
val actionComposable: (@Composable () -> Unit)? =
it.visuals.actionLabel?.let { actionLabel ->
@Composable {
TextButton(
onClick = { it.performAction() },
content = { Text(actionLabel) }
)
}
}
Snackbar(
modifier = Modifier
.padding(horizontal = 16.dp)
@ -123,6 +135,7 @@ class MainActivity : ComponentActivity() {
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
elevation = elevation.value
),
action = actionComposable,
content = {
Text(
modifier = Modifier.padding(vertical = 8.dp),

View file

@ -3,19 +3,19 @@ package com.pixelized.rplexicon.business
import android.app.Application
import android.content.Context
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Alteration
import com.pixelized.rplexicon.model.AssignedSpell
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.model.Skill
import com.pixelized.rplexicon.model.Throw
import com.pixelized.rplexicon.repository.data.character.ActionRepository
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.character.ObjectActionRepository
import com.pixelized.rplexicon.repository.data.character.SkillRepository
import com.pixelized.rplexicon.repository.data.character.SpellRepository
import com.pixelized.rplexicon.data.model.Alteration
import com.pixelized.rplexicon.data.model.AssignedSpell
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.DiceThrow
import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.model.Skill
import com.pixelized.rplexicon.data.model.Throw
import com.pixelized.rplexicon.data.repository.character.ActionRepository
import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
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.ui.screens.rolls.composable.RollDiceUio
import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCardUio
import com.pixelized.rplexicon.utilitary.extentions.icon

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
data class Alteration(
val name: String,

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
data class AssignedSpell(
val hit: Throw?,

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
data class Attack(
val title: String,

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
data class CharacterSheet(
val name: String,

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
import androidx.annotation.Keep
import com.google.firebase.database.IgnoreExtraProperties

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
data class Description(
val name: String,

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
sealed class DiceThrow(val character: String) {
class Initiative(character: String) : DiceThrow(character)

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
data class Equipment(
var silhouette: String? = null,

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
data class Inventory(
val items: List<Item>,

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
import android.net.Uri
import androidx.compose.runtime.Stable

View file

@ -0,0 +1,12 @@
package com.pixelized.rplexicon.data.model
import android.net.Uri
import androidx.compose.ui.geometry.Offset
data class Location(
val id: String,
val name: String,
val uri: Uri?,
val description: String?,
val child: List<Pair<Offset, Location>> = emptyList(),
)

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
data class ObjectAction(
val name: String,

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
enum class Property(val key: String) {
PROFICIENCY("Maîtrise"),

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
import android.net.Uri
import androidx.compose.runtime.Stable

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
data class Roll(
val character: String,

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
data class Skill(
val name: String,

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
data class Spell(
val name: String,

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.model
package com.pixelized.rplexicon.data.model
class Throw(
val amount: Int,

View file

@ -1,9 +1,10 @@
package com.pixelized.rplexicon.repository.parser
package com.pixelized.rplexicon.data.parser
import android.util.Log
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Attack
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.data.model.Attack
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.parser.roll.ThrowParser
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
@ -18,7 +19,7 @@ class AttackParser @Inject constructor(
// declare helper method to parse String
val actions = hashMapOf<String, MutableList<Attack>>()
value.forEachRow { index, row ->
value.forEachRowIndexed { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS)
else -> {

View file

@ -1,7 +1,7 @@
package com.pixelized.rplexicon.repository.parser
package com.pixelized.rplexicon.data.parser
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.repository.parser
package com.pixelized.rplexicon.data.parser
data class Column(
val names: List<String>,

View file

@ -1,7 +1,7 @@
package com.pixelized.rplexicon.repository.parser
package com.pixelized.rplexicon.data.parser
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Description
import com.pixelized.rplexicon.data.model.Description
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
@ -11,7 +11,7 @@ class DescriptionParser @Inject constructor() {
fun parse(sheet: ValueRange): Map<String, Description> = parserScope {
val descriptions = hashMapOf<String, Description>()
sheet.forEachRow { index, row ->
sheet.forEachRowIndexed { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS)
else -> {

View file

@ -1,6 +1,6 @@
package com.pixelized.rplexicon.repository.parser
package com.pixelized.rplexicon.data.parser
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.data.model.Lexicon
import javax.inject.Inject
class GenderParser @Inject constructor() {

View file

@ -1,7 +1,7 @@
package com.pixelized.rplexicon.repository.parser
package com.pixelized.rplexicon.data.parser
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.data.model.Lexicon
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
@ -15,7 +15,7 @@ class LexiconParser @Inject constructor(
var id = 0
val lexicons = mutableListOf<Lexicon>()
sheet.forEachRow { index, row ->
sheet.forEachRowIndexed { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS)

View file

@ -1,7 +1,8 @@
package com.pixelized.rplexicon.repository.parser
package com.pixelized.rplexicon.data.parser
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.ObjectAction
import com.pixelized.rplexicon.data.model.ObjectAction
import com.pixelized.rplexicon.data.parser.roll.ThrowParser
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
@ -12,7 +13,7 @@ class ObjectActionParser @Inject constructor(
fun parse(data: ValueRange): Map<String, List<ObjectAction>> = parserScope {
val objects = hashMapOf<String, MutableList<ObjectAction>>()
data.forEachRow { index, row ->
data.forEachRowIndexed { index, row ->
when {
index == 0 -> updateStructure(row = row, columns = COLUMNS)

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.repository.parser
package com.pixelized.rplexicon.data.parser
import android.net.Uri
import com.pixelized.rplexicon.utilitary.extentions.toUriOrNull

View file

@ -1,8 +1,8 @@
package com.pixelized.rplexicon.repository.parser
package com.pixelized.rplexicon.data.parser
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.QuestEntry
import com.pixelized.rplexicon.data.model.QuestEntry
import javax.inject.Inject
class QuestParser @Inject constructor(
@ -11,7 +11,7 @@ class QuestParser @Inject constructor(
fun parse(sheet: ValueRange): List<QuestEntry> = parserScope {
val quest = mutableListOf<QuestEntry>()
sheet.forEachRow { index, item ->
sheet.forEachRowIndexed { index, item ->
when (index) {
0 -> updateStructure(row = item, columns = COLUMNS)
else -> {

View file

@ -1,6 +1,6 @@
package com.pixelized.rplexicon.repository.parser
package com.pixelized.rplexicon.data.parser
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.data.model.Lexicon
import javax.inject.Inject
class RaceParser @Inject constructor() {

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.repository.parser
package com.pixelized.rplexicon.data.parser
import android.net.Uri
import androidx.core.net.toUri
@ -23,11 +23,38 @@ class SheetParserScope<T> {
return this.block()
}
fun ValueRange.forEachRow(block: (index: Int, row: List<*>) -> Unit) {
inline fun ValueRange.forEachRow(
crossinline block: (row: List<*>) -> Unit,
) = forEachRowIndexed(
block = { _, row -> block(row) }
)
fun ValueRange.forEachRowIndexed(block: (index: Int, row: List<*>) -> Unit) {
val sheet = this.values.sheet()
sheet?.mapNotNull { it as? List<*> }?.forEachIndexed(block)
}
inline fun ValueRange.forEachDataLine(
columns: List<Column>,
crossinline block: (row: List<*>) -> Unit
) = forEachDataLineIndexed(
columns = columns,
block = { _, line -> block(line) },
)
fun ValueRange.forEachDataLineIndexed(
columns: List<Column>,
block: (index: Int, row: List<*>) -> Unit
) {
val sheet = this.values.sheet()
sheet?.mapNotNull { it as? List<*> }?.forEachIndexed { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = columns)
else -> block(index - 1, row)
}
}
}
fun Any?.toItem(): String? =
this.toString().takeIf { it.isNotBlank() }
@ -37,6 +64,11 @@ class SheetParserScope<T> {
fun List<*>.parseInt(column: Column): Int? =
parse(column)?.toIntOrNull()
fun List<*>.parseFloat(column: Column): Float? =
parse(column = column)
?.replace(oldValue = ",", newValue = ".")
?.toFloatOrNull()
fun List<*>.parseBool(column: Column): Boolean? =
parse(column)?.equals("TRUE", ignoreCase = true)

View file

@ -1,7 +1,8 @@
package com.pixelized.rplexicon.repository.parser
package com.pixelized.rplexicon.data.parser
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Skill
import com.pixelized.rplexicon.data.model.Skill
import com.pixelized.rplexicon.data.parser.roll.ThrowParser
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
@ -12,7 +13,7 @@ class SkillParser @Inject constructor(
fun parse(sheet: ValueRange): Map<String, List<Skill>> = parserScope {
val skills = hashMapOf<String, MutableList<Skill>>()
sheet.forEachRow { index, row ->
sheet.forEachRowIndexed { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS)
else -> {

View file

@ -1,13 +1,13 @@
package com.pixelized.rplexicon.repository.parser.alteration
package com.pixelized.rplexicon.data.parser.alteration
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Alteration
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.repository.parser.roll.DiceParser
import com.pixelized.rplexicon.repository.parser.roll.FlatValueParser
import com.pixelized.rplexicon.data.model.Alteration
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.parser.column
import com.pixelized.rplexicon.data.parser.parserScope
import com.pixelized.rplexicon.data.parser.roll.DiceParser
import com.pixelized.rplexicon.data.parser.roll.FlatValueParser
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
@ -23,7 +23,7 @@ class AlterationParser @Inject constructor(
val properties = Property.values()
val alterations = hashMapOf<String, MutableList<Alteration>>()
sheet.forEachRow { index, row ->
sheet.forEachRowIndexed { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS + characterSheets.map { column(it.name) })

View file

@ -1,8 +1,8 @@
package com.pixelized.rplexicon.repository.parser.alteration
package com.pixelized.rplexicon.data.parser.alteration
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.data.parser.column
import com.pixelized.rplexicon.data.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
@ -15,7 +15,7 @@ class AssignedAlterationParser @Inject constructor() {
): Map<String, List<String>> = parserScope {
val status = hashMapOf<String, MutableList<String>>()
sheet.forEachRow { index, row ->
sheet.forEachRowIndexed { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = characterNames.map { column(it) })
else -> {

View file

@ -1,7 +1,7 @@
package com.pixelized.rplexicon.repository.parser.alteration
package com.pixelized.rplexicon.data.parser.alteration
import android.util.Log
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.data.model.Property
import javax.inject.Inject
class PropertyParser @Inject constructor() {

View file

@ -1,8 +1,8 @@
package com.pixelized.rplexicon.repository.parser.inventory
package com.pixelized.rplexicon.data.parser.inventory
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Equipment
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.data.model.Equipment
import com.pixelized.rplexicon.data.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
@ -13,7 +13,7 @@ class EquipmentParser @Inject constructor() {
lateinit var characters: List<String>
val equipments = hashMapOf<String, Equipment.Builder>()
data.forEachRow { index, row ->
data.forEachRowIndexed { index, row ->
when (index) {
// ignore the first column
0 -> characters = row.drop(1).map { it.toString() }

View file

@ -1,9 +1,9 @@
package com.pixelized.rplexicon.repository.parser.inventory
package com.pixelized.rplexicon.data.parser.inventory
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Inventory
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.data.model.Inventory
import com.pixelized.rplexicon.data.parser.column
import com.pixelized.rplexicon.data.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
@ -13,7 +13,7 @@ class InventoryParser @Inject constructor() {
fun parse(sheet: ValueRange): Map<String, Inventory> = parserScope {
val inventories = hashMapOf<String, Inventory.Builder>()
sheet.forEachRow { index, row ->
sheet.forEachRowIndexed { index, row ->
when {
index == 0 -> updateStructure(row = row, columns = COLUMNS)

View file

@ -0,0 +1,38 @@
package com.pixelized.rplexicon.data.parser.map
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.data.model.Location
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
class LocationParser @Inject constructor(
private val mapParser: MapParser,
private val worldParser: WorldParser,
) {
@Throws(IncompatibleSheetStructure::class)
fun parse(mapSheet: ValueRange, worldSheet: ValueRange): List<Location> {
val localMaps = mapParser.parse(sheet = mapSheet)
val localWorld = worldParser.parse(sheet = worldSheet)
val mapHash = localMaps
.map { localMap ->
Location(
id = localMap.name,
name = localMap.name,
uri = localMap.uri,
description = localMap.description,
child = emptyList(),
)
}.associateBy { it.name }
val maps = mapHash.map { entry ->
entry.value.copy(
child = localWorld
.filter { it.parent == entry.key }
.mapNotNull { world -> mapHash[world.child]?.let { map -> world.position to map } }
)
}
return maps
}
}

View file

@ -0,0 +1,45 @@
package com.pixelized.rplexicon.data.parser.map
import android.net.Uri
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.data.parser.column
import com.pixelized.rplexicon.data.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
class MapParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class)
fun parse(sheet: ValueRange): List<MapDto> = parserScope {
val maps = mutableListOf<MapDto>()
sheet.forEachDataLine(columns = COLUMNS) {
val name = it.parse(column = NAME)
val uri = it.parseUri(column = URI)
val description = it.parse(column = DESCRIPTION)
if (name != null) {
val map = MapDto(
name = name,
uri = uri,
description = description,
)
maps.add(map)
}
}
return@parserScope maps
}
data class MapDto(
val name: String,
val uri: Uri?,
val description: String?,
)
companion object {
private val NAME = column("Nom")
private val URI = column("Carte")
private val DESCRIPTION = column("Description")
private val COLUMNS get() = listOf(NAME, URI, DESCRIPTION)
}
}

View file

@ -0,0 +1,47 @@
package com.pixelized.rplexicon.data.parser.map
import androidx.compose.ui.geometry.Offset
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.data.parser.column
import com.pixelized.rplexicon.data.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
class WorldParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class)
fun parse(sheet: ValueRange): List<WorldDto> = parserScope {
val worlds = mutableListOf<WorldDto>()
sheet.forEachDataLine(columns = COLUMNS) { line ->
val parent = line.parse(column = PARENT)
val child = line.parse(column = CHILD)
val x = line.parseFloat(column = X)
val y = line.parseFloat(column = Y)
if (child != null && x != null && y != null) {
val world = WorldDto(
parent = parent,
child = child,
position = Offset(x = x, y = y),
)
worlds.add(world)
}
}
return@parserScope worlds
}
data class WorldDto(
val parent: String?,
val child: String,
val position: Offset,
)
companion object {
private val PARENT = column("Parent")
private val CHILD = column("Enfant")
private val X = column("X")
private val Y = column("Y")
private val COLUMNS get() = listOf(PARENT, CHILD, X, Y)
}
}

View file

@ -1,6 +1,6 @@
package com.pixelized.rplexicon.repository.parser.roll
package com.pixelized.rplexicon.data.parser.roll
import com.pixelized.rplexicon.model.Roll
import com.pixelized.rplexicon.data.model.Roll
import javax.inject.Inject
class DiceParser @Inject constructor() {

View file

@ -1,6 +1,6 @@
package com.pixelized.rplexicon.repository.parser.roll
package com.pixelized.rplexicon.data.parser.roll
import com.pixelized.rplexicon.model.Roll
import com.pixelized.rplexicon.data.model.Roll
import javax.inject.Inject
class FlatValueParser @Inject constructor() {

View file

@ -1,7 +1,7 @@
package com.pixelized.rplexicon.repository.parser.roll
package com.pixelized.rplexicon.data.parser.roll
import com.pixelized.rplexicon.repository.parser.alteration.PropertyParser
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.data.parser.alteration.PropertyParser
import com.pixelized.rplexicon.data.model.Property
import javax.inject.Inject
class ModifierParser @Inject constructor(

View file

@ -1,9 +1,6 @@
package com.pixelized.rplexicon.repository.parser
package com.pixelized.rplexicon.data.parser.roll
import com.pixelized.rplexicon.model.Throw
import com.pixelized.rplexicon.repository.parser.roll.DiceParser
import com.pixelized.rplexicon.repository.parser.roll.FlatValueParser
import com.pixelized.rplexicon.repository.parser.roll.ModifierParser
import com.pixelized.rplexicon.data.model.Throw
import javax.inject.Inject
class ThrowParser @Inject constructor(

View file

@ -1,11 +1,11 @@
package com.pixelized.rplexicon.repository.parser.spell
package com.pixelized.rplexicon.data.parser.spell
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.AssignedSpell
import com.pixelized.rplexicon.model.Spell
import com.pixelized.rplexicon.repository.parser.ThrowParser
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.data.model.AssignedSpell
import com.pixelized.rplexicon.data.model.Spell
import com.pixelized.rplexicon.data.parser.roll.ThrowParser
import com.pixelized.rplexicon.data.parser.column
import com.pixelized.rplexicon.data.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
@ -19,7 +19,7 @@ class AssignedSpellParser @Inject constructor(
): Map<String, List<AssignedSpell>> = parserScope {
val spellsBook = spells.associateBy { it.name }
val assignedSpells = hashMapOf<String, MutableList<AssignedSpell>>()
sheet.forEachRow { index, row ->
sheet.forEachRowIndexed { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS)
else -> {

View file

@ -1,10 +1,10 @@
package com.pixelized.rplexicon.repository.parser.spell
package com.pixelized.rplexicon.data.parser.spell
import android.util.Log
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Spell
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.data.model.Spell
import com.pixelized.rplexicon.data.parser.column
import com.pixelized.rplexicon.data.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
@ -13,7 +13,7 @@ class SpellBookParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class)
fun parse(sheet: ValueRange): List<Spell> = parserScope {
val spells = mutableListOf<Spell>()
sheet.forEachRow { index, row ->
sheet.forEachRowIndexed { index, row ->
when {
index == 0 -> updateStructure(row = row, columns = COLUMNS)
else -> {

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.repository
package com.pixelized.rplexicon.data.repository
import android.app.Application
import com.google.api.client.extensions.android.http.AndroidHttp

View file

@ -1,12 +1,12 @@
package com.pixelized.rplexicon.repository.data
package com.pixelized.rplexicon.data.repository
object LexiconBinder {
const val ID = "1oL9Nu5y37BPEbKxHre4TN9o8nrgy2JQoON4RRkdAHMs"
const val LEXICON = "Lexique"
const val QUEST_JOURNAL = "Journal de quêtes"
const val MAP = "Lieux"
const val MARQUEE = "Points d'intérêt"
const val MAP = "Cartes"
const val WORLD = "Monde"
}
object CharacterBinder {

View file

@ -1,15 +1,7 @@
package com.pixelized.rplexicon.repository.authentication
package com.pixelized.rplexicon.data.repository.authentication
import android.accounts.Account
import android.content.Context
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import com.google.android.gms.auth.api.identity.SignInCredential
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential
import com.google.api.client.util.ExponentialBackOff
import com.google.api.services.sheets.v4.SheetsScopes
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import javax.inject.Singleton

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.repository.authentication
package com.pixelized.rplexicon.data.repository.authentication
import android.app.Application
import android.util.Log
@ -8,7 +8,7 @@ import com.google.firebase.database.ValueEventListener
import com.google.firebase.database.ktx.database
import com.google.firebase.ktx.Firebase
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.CharacterSheetFire
import com.pixelized.rplexicon.data.model.CharacterSheetFire
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow

View file

@ -1,10 +1,10 @@
package com.pixelized.rplexicon.repository.data.character
package com.pixelized.rplexicon.data.repository.character
import com.pixelized.rplexicon.model.Attack
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.AttackParser
import com.pixelized.rplexicon.data.model.Attack
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.parser.AttackParser
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow

View file

@ -1,11 +1,11 @@
package com.pixelized.rplexicon.repository.data.character
package com.pixelized.rplexicon.data.repository.character
import com.pixelized.rplexicon.model.Alteration
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.alteration.AlterationParser
import com.pixelized.rplexicon.data.model.Alteration
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.parser.alteration.AlterationParser
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.Flow

View file

@ -1,9 +1,9 @@
package com.pixelized.rplexicon.repository.data.character
package com.pixelized.rplexicon.data.repository.character
import com.pixelized.rplexicon.repository.parser.CharacterSheetParser
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.data.parser.CharacterSheetParser
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow

View file

@ -1,9 +1,9 @@
package com.pixelized.rplexicon.repository.data.character
package com.pixelized.rplexicon.data.repository.character
import com.pixelized.rplexicon.model.Description
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.DescriptionParser
import com.pixelized.rplexicon.data.model.Description
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.parser.DescriptionParser
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow

View file

@ -1,9 +1,9 @@
package com.pixelized.rplexicon.repository.data.character
package com.pixelized.rplexicon.data.repository.character
import com.pixelized.rplexicon.model.Equipment
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.inventory.EquipmentParser
import com.pixelized.rplexicon.data.model.Equipment
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.parser.inventory.EquipmentParser
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow

View file

@ -1,9 +1,9 @@
package com.pixelized.rplexicon.repository.data.character
package com.pixelized.rplexicon.data.repository.character
import com.pixelized.rplexicon.model.Inventory
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.inventory.InventoryParser
import com.pixelized.rplexicon.data.model.Inventory
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.parser.inventory.InventoryParser
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow

View file

@ -1,9 +1,9 @@
package com.pixelized.rplexicon.repository.data.character
package com.pixelized.rplexicon.data.repository.character
import com.pixelized.rplexicon.model.ObjectAction
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.ObjectActionParser
import com.pixelized.rplexicon.data.model.ObjectAction
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.parser.ObjectActionParser
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow

View file

@ -1,9 +1,9 @@
package com.pixelized.rplexicon.repository.data.character
package com.pixelized.rplexicon.data.repository.character
import com.pixelized.rplexicon.model.Skill
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.SkillParser
import com.pixelized.rplexicon.data.model.Skill
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.parser.SkillParser
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow

View file

@ -1,11 +1,11 @@
package com.pixelized.rplexicon.repository.data.character
package com.pixelized.rplexicon.data.repository.character
import com.pixelized.rplexicon.model.AssignedSpell
import com.pixelized.rplexicon.model.Spell
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.CharacterBinder
import com.pixelized.rplexicon.repository.parser.spell.AssignedSpellParser
import com.pixelized.rplexicon.repository.parser.spell.SpellBookParser
import com.pixelized.rplexicon.data.model.AssignedSpell
import com.pixelized.rplexicon.data.model.Spell
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.parser.spell.AssignedSpellParser
import com.pixelized.rplexicon.data.parser.spell.SpellBookParser
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.async

View file

@ -1,9 +1,9 @@
package com.pixelized.rplexicon.repository.data.lexicon
package com.pixelized.rplexicon.data.repository.lexicon
import com.pixelized.rplexicon.repository.parser.LexiconParser
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.LexiconBinder
import com.pixelized.rplexicon.data.parser.LexiconParser
import com.pixelized.rplexicon.data.model.Lexicon
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

View file

@ -0,0 +1,48 @@
package com.pixelized.rplexicon.data.repository.lexicon
import com.pixelized.rplexicon.data.model.Location
import com.pixelized.rplexicon.data.parser.map.LocationParser
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.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class LocationRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository,
private val parser: LocationParser,
) {
private val _data = MutableStateFlow<List<Location>>(emptyList())
val data: StateFlow<List<Location>> get() = _data
var lastSuccessFullUpdate: Update = Update.INITIAL
private set
fun find(id: String): Location? {
return _data.value.firstOrNull { it.id == id }
}
@Throws(IncompatibleSheetStructure::class, Exception::class)
suspend fun fetchMap() {
googleRepository.fetch { sheets ->
val (map, world) = awaitAll(
async { sheets.get(LexiconBinder.ID, LexiconBinder.MAP).execute() },
async { sheets.get(LexiconBinder.ID, LexiconBinder.WORLD).execute() },
)
val data = parser.parse(mapSheet = map, worldSheet = world)
_data.emit(data)
lastSuccessFullUpdate = Update.currentTime()
}
}
companion object {
const val WORLD_SHEET_URL =
"https://docs.google.com/spreadsheets/d/${LexiconBinder.ID}/edit#gid=1943877267"
}
}

View file

@ -1,10 +1,10 @@
package com.pixelized.rplexicon.repository.data.lexicon
package com.pixelized.rplexicon.data.repository.lexicon
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.repository.parser.QuestParser
import com.pixelized.rplexicon.model.Quest
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.LexiconBinder
import com.pixelized.rplexicon.data.parser.QuestParser
import com.pixelized.rplexicon.data.model.Quest
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

View file

@ -1,22 +0,0 @@
package com.pixelized.rplexicon.model
import android.net.Uri
import androidx.compose.runtime.Stable
import androidx.compose.ui.geometry.Offset
@Stable
data class Location(
val id: Int,
val sheetIndex: Int,
val name: String,
val uri: Uri,
val marquees: List<Marquee>,
) {
@Stable
data class Marquee(
val map: String,
val name: String?,
val position: Offset,
val description: String?,
)
}

View file

@ -1,70 +0,0 @@
package com.pixelized.rplexicon.repository.data.lexicon
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.repository.parser.LocationParser
import com.pixelized.rplexicon.repository.parser.MarqueeParser
import com.pixelized.rplexicon.model.Location
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.repository.data.LexiconBinder
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class LocationRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository,
private val locationParser: LocationParser,
private val marqueeParser: MarqueeParser,
) {
private val _data = MutableStateFlow<List<Location>>(emptyList())
val data: StateFlow<List<Location>> get() = _data
var lastSuccessFullUpdate: Update = Update.INITIAL
private set
fun findId(name: String?): Int? {
return name?.let { _data.value.firstOrNull { item -> item.name == it }?.id }
}
@Throws(IncompatibleSheetStructure::class, Exception::class)
suspend fun fetchLocation() {
googleRepository.fetch { sheet ->
val (map, marquee) = awaitAll(
async { sheet.get(LexiconBinder.ID, LexiconBinder.MAP).execute() },
async { sheet.get(LexiconBinder.ID, LexiconBinder.MARQUEE).execute() },
)
updateData(map = map, marquee = marquee)
lastSuccessFullUpdate = Update.currentTime()
}
}
@Throws(IncompatibleSheetStructure::class)
private suspend fun updateData(map: ValueRange, marquee: ValueRange) {
val marquees = marqueeParser
.parse(sheet = marquee)
.groupBy { it.map }
val maps = locationParser
.parse(sheet = map)
.map {
val associatedMarquees = marquees[it.name]
if (associatedMarquees != null) {
it.copy(marquees = associatedMarquees)
} else {
it
}
}
_data.emit(maps)
}
companion object {
const val SHEET_URL =
"https://docs.google.com/spreadsheets/d/${LexiconBinder.ID}/edit#gid=1985553511"
}
}

View file

@ -1,43 +0,0 @@
package com.pixelized.rplexicon.repository.parser
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Location
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
class LocationParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class)
fun parse(sheet: ValueRange): List<Location> = parserScope {
val locations = mutableListOf<Location>()
var id = 0
sheet.forEachRow { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS)
else -> {
val name = row.parse(column = NAME)
val uri = row.parseUri(column = MAP)
if (name != null && uri != null) {
val location = Location(
id = id++,
sheetIndex = index,
name = name,
uri = uri,
marquees = emptyList(),
)
locations.add(location)
}
}
}
}
return@parserScope locations
}
companion object {
private val NAME = column("Nom")
private val MAP = column("Carte")
private val COLUMNS get() = listOf(NAME, MAP)
}
}

View file

@ -1,54 +0,0 @@
package com.pixelized.rplexicon.repository.parser
import androidx.compose.ui.geometry.Offset
import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Location
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject
class MarqueeParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class)
fun parse(sheet: ValueRange): List<Location.Marquee> = parserScope {
val marquees = mutableListOf<Location.Marquee>()
sheet.forEachRow { index, item ->
when (index) {
0 -> updateStructure(row = item, columns = COLUMNS)
else -> {
val map = item.parse(column = MAP)
val x = item.parse(column = X)
?.replace(oldValue = ",", newValue = ".")
?.toFloatOrNull()
val y = item.parse(column = Y)
?.replace(oldValue = ",", newValue = ".")
?.toFloatOrNull()
if (map != null) {
val marquee = Location.Marquee(
map = map,
name = item.parse(column = NAME),
position = when {
x != null && y != null -> Offset(x, y)
else -> Offset.Unspecified
},
description = item.parse(column = DESCRIPTION),
)
marquees.add(marquee)
}
}
}
}
return@parserScope marquees
}
companion object {
private val MAP = column("Carte")
private val NAME = column("Nom")
private val X = column("X")
private val Y = column("Y")
private val DESCRIPTION = column("Description")
private val COLUMNS get() = listOf(MAP, NAME, X, Y, DESCRIPTION)
}
}

View file

@ -8,7 +8,6 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.animation.with
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@ -40,7 +39,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.data.model.Lexicon
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.composable.stringResource
import com.pixelized.rplexicon.utilitary.extentions.lexicon

View file

@ -8,7 +8,7 @@ import androidx.navigation.NavHostController
import androidx.navigation.NavOptionsBuilder
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.data.model.Lexicon
import com.pixelized.rplexicon.ui.navigation.NavigationAnimation
import com.pixelized.rplexicon.ui.navigation.animatedComposable
import com.pixelized.rplexicon.ui.screens.lexicon.detail.LexiconDetailScreen

View file

@ -22,7 +22,7 @@ val LOCATION_DETAIL_ROUTE = ROUTE +
@Stable
@Immutable
data class LocationDetailArgument(
val id: Int,
val id: String,
)
val SavedStateHandle.locationDetailArgument: LocationDetailArgument
@ -35,7 +35,7 @@ fun NavGraphBuilder.composableLocationDetail() {
route = LOCATION_DETAIL_ROUTE,
arguments = listOf(
navArgument(name = ARG_ID) {
type = NavType.IntType
type = NavType.StringType
},
),
animation = NavigationAnimation.Push,
@ -45,7 +45,7 @@ fun NavGraphBuilder.composableLocationDetail() {
}
fun NavHostController.navigateToLocationDetail(
id: Int,
id: String,
option: NavOptionsBuilder.() -> Unit = {},
) {
val route = ROUTE +

View file

@ -20,7 +20,7 @@ import com.google.firebase.auth.GoogleAuthProvider
import com.google.firebase.auth.ktx.auth
import com.google.firebase.ktx.Firebase
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.repository.authentication.AuthenticationRepository
import com.pixelized.rplexicon.data.repository.authentication.AuthenticationRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

View file

@ -6,16 +6,16 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.repository.data.character.ActionRepository
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.repository.data.character.EquipmentRepository
import com.pixelized.rplexicon.repository.data.character.InventoryRepository
import com.pixelized.rplexicon.repository.data.character.ObjectActionRepository
import com.pixelized.rplexicon.repository.data.character.SkillRepository
import com.pixelized.rplexicon.repository.data.character.SpellRepository
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.character.ActionRepository
import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.data.repository.character.EquipmentRepository
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.ui.composable.error.FetchErrorUio
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio.Structure.Type
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument

View file

@ -28,7 +28,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Attack
import com.pixelized.rplexicon.data.model.Attack
import com.pixelized.rplexicon.ui.screens.character.composable.common.DiceButton
import com.pixelized.rplexicon.ui.theme.LexiconTheme

View file

@ -32,7 +32,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Spell
import com.pixelized.rplexicon.data.model.Spell
import com.pixelized.rplexicon.ui.screens.character.composable.common.DiceButton
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.local.icon

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Spell
import com.pixelized.rplexicon.data.model.Spell
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellHeaderUio
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellUio
import com.pixelized.rplexicon.utilitary.extentions.local.icon

View file

@ -1,10 +1,10 @@
package com.pixelized.rplexicon.ui.screens.character.factory
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Alteration
import com.pixelized.rplexicon.model.Attack
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.data.model.Alteration
import com.pixelized.rplexicon.data.model.Attack
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AttackUio
import com.pixelized.rplexicon.utilitary.extentions.icon
import com.pixelized.rplexicon.utilitary.extentions.modifier

View file

@ -1,9 +1,9 @@
package com.pixelized.rplexicon.ui.screens.character.factory
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Alteration
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.data.model.Alteration
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.ui.screens.character.composable.character.LabelPointUio
import com.pixelized.rplexicon.ui.screens.character.composable.character.ProficiencyUio
import com.pixelized.rplexicon.ui.screens.character.composable.character.StatUio

View file

@ -1,6 +1,6 @@
package com.pixelized.rplexicon.ui.screens.character.factory
import com.pixelized.rplexicon.model.Inventory
import com.pixelized.rplexicon.data.model.Inventory
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio
import javax.inject.Inject

View file

@ -1,9 +1,9 @@
package com.pixelized.rplexicon.ui.screens.character.factory
import com.pixelized.rplexicon.model.AssignedSpell
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.data.model.AssignedSpell
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellUio
import com.pixelized.rplexicon.utilitary.extentions.icon
import com.pixelized.rplexicon.utilitary.extentions.local.icon

View file

@ -6,12 +6,12 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.model.Attack
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.repository.data.character.ActionRepository
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.model.Attack
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.DiceThrow
import com.pixelized.rplexicon.data.repository.character.ActionRepository
import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AttackUio
import com.pixelized.rplexicon.ui.screens.character.factory.AttackUioFactory

View file

@ -7,11 +7,11 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.model.DiceThrow
import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.ui.composable.edit.HpPointDialogUio
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio

View file

@ -5,9 +5,9 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.repository.data.character.ObjectActionRepository
import com.pixelized.rplexicon.data.model.DiceThrow
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.data.repository.character.ObjectActionRepository
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.ObjectItemUio
import dagger.hilt.android.lifecycle.HiltViewModel

View file

@ -6,15 +6,15 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.model.CharacterSheetFire
import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.model.Skill
import com.pixelized.rplexicon.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.repository.data.character.SkillRepository
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.CharacterSheetFire
import com.pixelized.rplexicon.data.model.DiceThrow
import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.model.Skill
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.data.repository.character.SkillRepository
import com.pixelized.rplexicon.ui.composable.edit.SkillEditDialogUio
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SkillItemUio

View file

@ -7,15 +7,15 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.AssignedSpell
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.model.CharacterSheetFire
import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.model.Throw
import com.pixelized.rplexicon.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.character.SpellRepository
import com.pixelized.rplexicon.data.model.AssignedSpell
import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.CharacterSheetFire
import com.pixelized.rplexicon.data.model.DiceThrow
import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.model.Throw
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.repository.character.SpellRepository
import com.pixelized.rplexicon.ui.composable.edit.SpellEditDialogUio
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellHeaderUio

View file

@ -7,8 +7,8 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AlterationItemUio
import com.pixelized.rplexicon.ui.screens.rolls.factory.AlterationFactory

View file

@ -7,9 +7,9 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.repository.data.character.DescriptionRepository
import com.pixelized.rplexicon.repository.data.character.EquipmentRepository
import com.pixelized.rplexicon.repository.data.character.InventoryRepository
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.data.repository.character.EquipmentRepository
import com.pixelized.rplexicon.data.repository.character.InventoryRepository
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItemUio
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio

View file

@ -6,9 +6,9 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.model.DiceThrow
import com.pixelized.rplexicon.repository.data.character.AlterationRepository
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.model.DiceThrow
import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.character.ProficiencyUio
import com.pixelized.rplexicon.ui.screens.character.composable.character.StatUio

View file

@ -55,7 +55,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.data.model.Lexicon
import com.pixelized.rplexicon.ui.composable.AsyncImage
import com.pixelized.rplexicon.ui.composable.BackgroundImage
import com.pixelized.rplexicon.ui.composable.FullScreenImageHandler

View file

@ -4,8 +4,8 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.lexicon.LexiconRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.repository.lexicon.LexiconRepository
import com.pixelized.rplexicon.ui.navigation.screens.lexiconDetailArgument
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

View file

@ -6,9 +6,9 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.repository.data.character.CharacterSheetRepository
import com.pixelized.rplexicon.repository.data.lexicon.LexiconRepository
import com.pixelized.rplexicon.data.model.Lexicon
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.repository.lexicon.LexiconRepository
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import dagger.hilt.android.lifecycle.HiltViewModel

View file

@ -30,7 +30,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.data.model.Lexicon
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.composable.stringResource
import com.pixelized.rplexicon.utilitary.extentions.annotatedSpan

View file

@ -41,7 +41,7 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.NO_WINDOW_INSETS
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.data.model.Lexicon
import com.pixelized.rplexicon.ui.composable.CollapsingHeader
import com.pixelized.rplexicon.ui.composable.form.DropDownField
import com.pixelized.rplexicon.ui.composable.form.DropDownFieldUio

View file

@ -4,10 +4,10 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.model.Lexicon.Gender
import com.pixelized.rplexicon.model.Lexicon.Race
import com.pixelized.rplexicon.repository.data.lexicon.LexiconRepository
import com.pixelized.rplexicon.data.model.Lexicon
import com.pixelized.rplexicon.data.model.Lexicon.Gender
import com.pixelized.rplexicon.data.model.Lexicon.Race
import com.pixelized.rplexicon.data.repository.lexicon.LexiconRepository
import com.pixelized.rplexicon.ui.composable.form.DropDownFieldUio
import com.pixelized.rplexicon.ui.composable.form.TextFieldUio
import com.pixelized.rplexicon.utilitary.composable.stringResource

View file

@ -42,7 +42,7 @@ fun FantasyMap(
contentScale: ContentScale = ContentScale.Fit,
items: State<List<MarqueeUio>>,
highlight: State<Offset>,
selectedItem: State<Int>,
selectedItem: State<Int?>,
onMarquee: (MarqueeUio) -> Unit,
onTap: (Offset) -> Unit,
) {
@ -67,9 +67,11 @@ fun FantasyMap(
}
LaunchedEffect(key1 = "CenterOnMarquee:${selectedItem.value}") {
items.value.getOrNull(selectedItem.value)?.position
selectedItem.value?.let {
items.value.getOrNull(it)?.position
?.let { state.pan(state.computeMarqueeOffset(it)) }
}
}
Box(
modifier = modifier

View file

@ -2,22 +2,24 @@ package com.pixelized.rplexicon.ui.screens.location.detail
import android.content.res.Configuration
import android.net.Uri
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
@ -33,8 +35,8 @@ import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
@ -49,10 +51,8 @@ import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalUriHandler
@ -66,24 +66,24 @@ import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.LocalSnack
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.Handle
import com.pixelized.rplexicon.ui.composable.rememberBackgroundGradient
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
import com.pixelized.rplexicon.ui.navigation.screens.navigateToLocationDetail
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.annotateWithDropCap
import com.pixelized.rplexicon.utilitary.extentions.lexicon
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlin.math.max
@Stable
data class LocationDetailUio(
val name: String,
val map: Uri,
val map: Uri?,
val description: String?,
val marquees: List<MarqueeUio>,
)
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LocationDetail(
viewModel: LocationDetailViewModel = hiltViewModel()
@ -95,40 +95,30 @@ fun LocationDetail(
val scope = rememberCoroutineScope()
val scroll = rememberScrollState()
val pager = rememberPagerState(
pageCount = { viewModel.location.value.marquees.size },
)
val fantasy = rememberFantasyMapState()
val snapBehavior = rememberSnapConnection(scrollState = scroll)
val scrollBehavior = rememberScrollConnection(scrollState = scroll)
val ok = stringResource(id = android.R.string.ok)
val snackJob = remember { mutableStateOf<Job?>(null) }
val mapHighlight = remember { mutableStateOf(Offset.Unspecified) }
val selectedIndex = remember { mutableIntStateOf(0) }
Surface {
LocationContent(
modifier = Modifier
.fillMaxSize()
.nestedScroll(connection = snapBehavior),
connection = scrollBehavior,
scrollState = scroll,
pagerState = pager,
fantasyMapState = fantasy,
item = viewModel.location,
selectedIndex = selectedIndex,
selectedIndex = viewModel.selectedMarquee,
mapHighlight = mapHighlight,
onBack = {
screen.popBackStack()
},
onMarquee = {
scope.launch {
val index = max(viewModel.location.value.marquees.indexOf(it), 0)
selectedIndex.value = index
pager.animateScrollToPage(page = index)
}
onMarquee = viewModel::onSelectMarquee,
onDestination = {
screen.navigateToLocationDetail(id = it.id)
},
onMapTap = {
snackJob.value?.cancel()
@ -192,33 +182,21 @@ fun LocationDetail(
}
},
)
HandlePagerScroll(
pagerState = pager,
index = selectedIndex,
onIndexChange = { page ->
pager.animateScrollToPage(page = page)
},
onPageChange = { page ->
selectedIndex.value = page
}
)
}
}
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun LocationContent(
modifier: Modifier,
connection: NestedScrollConnection,
scrollState: ScrollState,
pagerState: PagerState,
fantasyMapState: FantasyMapState,
item: State<LocationDetailUio>,
selectedIndex: State<Int>,
item: State<LocationDetailUio?>,
selectedIndex: State<Int?>,
mapHighlight: State<Offset>,
onBack: () -> Unit,
onMarquee: (MarqueeUio) -> Unit,
onDestination: (MarqueeUio) -> Unit,
onMapTap: (Offset) -> Unit,
onTouch: (Boolean) -> Unit,
onCenter: () -> Unit,
@ -226,7 +204,6 @@ private fun LocationContent(
onZoomOut: () -> Unit,
) {
val density = LocalDensity.current
val itemNameSize = remember { mutableStateOf(0.dp) }
val filledIconButtonColors = IconButtonDefaults.filledIconButtonColors(
containerColor = MaterialTheme.colorScheme.surface,
@ -265,6 +242,7 @@ private fun LocationContent(
Column(
modifier = Modifier.verticalScroll(state = scrollState),
) {
if (item.value?.map != null) {
Surface(
tonalElevation = 2.dp,
) {
@ -280,19 +258,58 @@ private fun LocationContent(
)
.offset(scrollState = scrollState),
state = fantasyMapState,
model = item.value.map,
model = item.value?.map,
contentScale = ContentScale.Fit,
items = remember { derivedStateOf { item.value.marquees } },
items = remember {
derivedStateOf {
item.value?.marquees ?: emptyList()
}
},
selectedItem = selectedIndex,
highlight = mapHighlight,
onMarquee = onMarquee,
onTap = onMapTap,
)
FilledIconToggleButton(
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.End,
) {
val marquee = remember {
derivedStateOf {
when (val index = selectedIndex.value) {
null -> null
else -> item.value?.marquees?.get(index)
}
}
}
AnimatedVisibility(
visible = marquee.value != null && fantasyMapState.freeHand.not(),
enter = fadeIn() + expandVertically(),
exit = shrinkVertically() + fadeOut(),
) {
Text(
modifier = Modifier
.align(alignment = Alignment.TopEnd)
.padding(all = 16.dp),
.clickable { marquee.value?.let(onDestination) }
.background(
brush = rememberBackgroundGradient(
from = 0.8f,
to = 0.4f,
)
)
.minimumInteractiveComponentSize()
.fillMaxWidth(),
style = MaterialTheme.typography.titleMedium,
textAlign = TextAlign.Center,
text = annotateWithDropCap(
text = marquee.value?.name ?: "",
style = MaterialTheme.lexicon.typography.titleMediumDropCap,
)
)
}
FilledIconToggleButton(
modifier = Modifier.padding(all = 8.dp),
checked = fantasyMapState.freeHand,
onCheckedChange = onTouch,
colors = filledIconToggleButtonColors,
@ -302,11 +319,12 @@ private fun LocationContent(
contentDescription = null
)
}
}
Column(
modifier = Modifier
.align(alignment = Alignment.BottomEnd)
.padding(all = 16.dp),
.padding(all = 8.dp),
) {
FilledIconButton(
onClick = onZoomOut,
@ -338,45 +356,31 @@ private fun LocationContent(
}
}
}
Handle(
modifier = Modifier
.align(alignment = Alignment.CenterHorizontally)
.padding(vertical = 16.dp)
)
}
Column(
modifier = Modifier
.onSizeChanged { itemNameSize.value = with(density) { it.height.toDp() } }
.padding(horizontal = 16.dp)
.fillMaxWidth(),
.fillMaxWidth()
.heightIn(min = this@constraint.maxHeight)
.padding(all = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(space = 24.dp),
) {
Text(
textAlign = TextAlign.Center,
style = MaterialTheme.typography.headlineSmall,
text = annotateWithDropCap(
text = item.value.name,
text = item.value?.name ?: "",
style = MaterialTheme.lexicon.typography.headlineSmallDropCap,
),
)
}
HorizontalPager(
modifier = Modifier
.fillMaxWidth()
.nestedScroll(connection),
state = pagerState,
verticalAlignment = Alignment.Top,
contentPadding = PaddingValues(all = 16.dp),
pageSpacing = 16.dp,
) {
item.value.marquees.getOrNull(it)?.let { marquee ->
MarqueeItem(
modifier = Modifier
.fillMaxWidth()
.height(this@constraint.maxHeight - 32.dp - itemNameSize.value),
marquee = marquee,
item.value?.description?.let {
Text(
style = MaterialTheme.typography.bodyMedium,
text = annotateWithDropCap(
text = it,
style = MaterialTheme.lexicon.typography.bodyMediumDropCap
),
)
}
}
@ -385,24 +389,6 @@ private fun LocationContent(
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun HandlePagerScroll(
pagerState: PagerState,
index: State<Int>,
onIndexChange: suspend (page: Int) -> Unit,
onPageChange: suspend (page: Int) -> Unit,
) {
if (!pagerState.isScrollInProgress) {
LaunchedEffect(Unit) {
onPageChange.invoke(pagerState.currentPage)
}
}
LaunchedEffect(key1 = index.value) {
onIndexChange.invoke(index.value)
}
}
@Composable
@Stable
private fun rememberSnapConnection(
@ -410,56 +396,23 @@ private fun rememberSnapConnection(
scrollState: ScrollState,
): NestedScrollConnection {
val dimens = MaterialTheme.lexicon.dimens
return remember(scope, scrollState) {
object : NestedScrollConnection {
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
if (scrollState.value < dimens.map.mapSnapPx) {
scope.launch { scrollState.animateScrollTo(0) }
}
if ((scrollState.maxValue - scrollState.value) < dimens.map.mapSnapPx) {
scope.launch { scrollState.animateScrollTo(scrollState.maxValue) }
}
return super.onPostFling(consumed, available)
}
}
}
}
@Composable
@Stable
private fun rememberScrollConnection(
scope: CoroutineScope = rememberCoroutineScope(),
scrollState: ScrollState,
): NestedScrollConnection {
return remember(scrollState) {
object : NestedScrollConnection {
override fun onPreScroll(
available: Offset,
source: NestedScrollSource,
): Offset {
val delta = when {
scrollState.value == 0 && available.y > 0f -> 0f
scrollState.value == scrollState.maxValue -> 0f
else -> {
scope.launch { scrollState.scrollBy(-available.y) }
available.y
}
}
return Offset(x = 0f, y = delta)
}
}
}
}
@Stable
private fun Modifier.offset(scrollState: ScrollState) = this.offset {
IntOffset(x = 0, y = scrollState.value / 2)
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@ -468,34 +421,49 @@ private fun LocationPreview() {
Surface {
LocationContent(
modifier = Modifier.fillMaxSize(),
connection = remember { object : NestedScrollConnection {} },
scrollState = rememberScrollState(),
pagerState = rememberPagerState(pageCount = { 2 }),
fantasyMapState = rememberFantasyMapState(),
item = remember {
mutableStateOf(
LocationDetailUio(
name = "Daggerfall",
map = Uri.parse("https://i.pinimg.com/originals/6d/56/cd/6d56cd9358cc94a7077157ea3c1b5842.jpg"),
name = "Barovie",
map = Uri.parse("https://cdn.discordapp.com/attachments/1123326578508714106/1161014438736969759/Map_of_Barovia_-_4k_-_Names_-_No_Special_Locations_-_Without_Hex.jpg?ex=6536c1f4&is=65244cf4&hm=671ddc88d073e5559bd37af14dc731e497b78457dc37a3ea5620a5342004b733&"),
description = "Contrée sombre et maudite soumise au joug de Stradh von Zarovith",
marquees = listOf(
MarqueeUio(
name = "start",
position = Offset.Zero,
description = "Marquee en haut à gauche."
id = "Barovie (village)",
name = "Barovie (village)",
position = Offset(0.79310584f, 0.61436355f)
),
MarqueeUio(
name = "end",
position = Offset(1f, 1f),
description = "Marquee en bas à droite."
id = "Vallaki",
name = "Vallaki",
position = Offset(0.42546257f, 0.31059146f)
),
MarqueeUio(
id = "Krezk",
name = "Krezk",
position = Offset(0.1110163f, 0.29166123f)
),
MarqueeUio(
id = "Campement Vistani",
name = "Campement Vistani",
position = Offset(0.6623672f, 0.6120087f)
),
MarqueeUio(
id = "Vieux moulin",
name = "Vieux moulin",
position = Offset(0.49444568f, 0.40867198f)
),
),
)
)
},
selectedIndex = remember { mutableStateOf(0) },
selectedIndex = remember { mutableIntStateOf(0) },
mapHighlight = remember { mutableStateOf(Offset(0.5f, 0.5f)) },
onBack = { },
onMarquee = { },
onDestination = { },
onMapTap = { },
onTouch = { },
onCenter = { },

View file

@ -4,18 +4,21 @@ import android.app.Application
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context.CLIPBOARD_SERVICE
import androidx.compose.runtime.IntState
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.geometry.Offset
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle
import com.pixelized.rplexicon.repository.data.lexicon.LocationRepository
import com.pixelized.rplexicon.data.repository.lexicon.LocationRepository
import com.pixelized.rplexicon.ui.navigation.screens.locationDetailArgument
import com.pixelized.rplexicon.utilitary.cells
import com.pixelized.rplexicon.utilitary.line
import com.pixelized.rplexicon.utilitary.table
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlin.math.max
@HiltViewModel
class LocationDetailViewModel @Inject constructor(
@ -25,28 +28,39 @@ class LocationDetailViewModel @Inject constructor(
) : AndroidViewModel(application) {
private val clipboard = application.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val sheetUri = LocationRepository.SHEET_URL
val location: State<LocationDetailUio>
val sheetUri = LocationRepository.WORLD_SHEET_URL
val location: State<LocationDetailUio?>
private val _selectedMarquee = mutableStateOf<Int?>(null)
val selectedMarquee: State<Int?> get() = _selectedMarquee
init {
val argument = savedStateHandle.locationDetailArgument
val source = repository.data.value[argument.id]
val source = repository.find(id = argument.id)
location = mutableStateOf(
source?.let {
LocationDetailUio(
name = source.name,
map = source.uri,
marquees = source.marquees.map { marquee ->
name = it.name,
map = it.uri,
description = it.description,
marquees = it.child.map { child ->
MarqueeUio(
name = marquee.name,
position = marquee.position,
description = marquee.description,
id = child.second.id,
name = child.second.name,
position = child.first,
)
}
)
}
)
}
fun onSelectMarquee(marqueeUio: MarqueeUio) {
val index = max(location.value?.marquees?.indexOf(marqueeUio) ?: 0, 0)
_selectedMarquee.value = index
}
fun clip(
label: String,
coordinate: Offset,

View file

@ -1,12 +1,7 @@
package com.pixelized.rplexicon.ui.screens.location.detail
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
@ -20,15 +15,14 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.LOS_HOLLOW
import com.pixelized.rplexicon.utilitary.annotateWithDropCap
import com.pixelized.rplexicon.utilitary.extentions.lexicon
@Stable
data class MarqueeUio(
val name: String?,
val id: String,
val name: String,
val position: Offset,
val description: String?,
)
@Composable
@ -36,44 +30,19 @@ fun MarqueeItem(
modifier: Modifier = Modifier,
marquee: MarqueeUio,
) {
Column(
Text(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(space = 8.dp)
) {
marquee.name?.let {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
Text(
modifier = Modifier.alignByBaseline(),
text = LOS_HOLLOW,
)
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
overflow = TextOverflow.Ellipsis,
maxLines = 3,
text = annotateWithDropCap(
text = it,
text = marquee.name,
style = MaterialTheme.lexicon.typography.titleMediumDropCap,
),
)
}
}
marquee.description?.let {
Text(
modifier = Modifier.verticalScroll(rememberScrollState()),
style = MaterialTheme.typography.bodyMedium,
text = annotateWithDropCap(
text = it,
style = MaterialTheme.lexicon.typography.bodyMediumDropCap,
),
)
}
}
}
@Composable
@Preview
@ -85,9 +54,9 @@ private fun MarqueeItemPreview() {
.fillMaxWidth()
.padding(all = 16.dp),
marquee = MarqueeUio(
id = "Barovie",
name = "Barovie",
position = Offset.Zero,
description = "Village lugubre à la population clairsemé théatre récent d'une d'une rebelion sanglante.",
),
)
}

View file

@ -27,13 +27,13 @@ import com.pixelized.rplexicon.utilitary.extentions.placeholder
@Stable
data class LocationItemUio(
val id: Int,
val id: String,
val title: String,
val placeholder: Boolean = false,
) {
companion object {
fun preview(
id: Int = 0,
id: String = "0",
title: String = "Daggerfall",
placeHolder: Boolean = false,
): LocationItemUio {

View file

@ -152,8 +152,8 @@ private fun QuestListPreview() {
items = remember {
mutableStateOf(
listOf(
LocationItemUio.preview(id = 0, title = "Daggerfall"),
LocationItemUio.preview(id = 1, title = "Athkatla"),
LocationItemUio.preview(id = "0", title = "Daggerfall"),
LocationItemUio.preview(id = "1", title = "Athkatla"),
)
)
},

Some files were not shown because too many files have changed in this diff Show more