Refactor quest id reference & locatino isNew feature.
This commit is contained in:
parent
9f44ce4543
commit
3904ab22ff
21 changed files with 469 additions and 150 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"identityHash": "c69ae3b018a2383edc96a92797dfdfd3",
|
"identityHash": "5657267ebe96b4b324d58b58aefce6f8",
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
"tableName": "lexicon",
|
"tableName": "lexicon",
|
||||||
|
|
@ -104,7 +104,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tableName": "quest",
|
"tableName": "quest",
|
||||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `category` TEXT, `title` TEXT NOT NULL, `subTitle` TEXT, `completed` INTEGER NOT NULL, `questGiver` TEXT, `area` TEXT, `groupReward` TEXT, `individualReward` TEXT, `description` TEXT NOT NULL, `illustrations` TEXT, `background` TEXT, `lastUpdated` INTEGER, `lastRead` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `category` TEXT, `title` TEXT NOT NULL, `subTitle` TEXT, `completed` INTEGER NOT NULL, `questGiverId` TEXT, `questGiverName` TEXT, `locationId` TEXT, `locationName` TEXT, `groupReward` TEXT, `individualReward` TEXT, `description` TEXT NOT NULL, `illustrations` TEXT, `background` TEXT, `lastUpdated` INTEGER, `lastRead` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "id",
|
"fieldPath": "id",
|
||||||
|
|
@ -137,14 +137,26 @@
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldPath": "questGiver",
|
"fieldPath": "questGiverId",
|
||||||
"columnName": "questGiver",
|
"columnName": "questGiverId",
|
||||||
"affinity": "TEXT",
|
"affinity": "TEXT",
|
||||||
"notNull": false
|
"notNull": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldPath": "area",
|
"fieldPath": "questGiverName",
|
||||||
"columnName": "area",
|
"columnName": "questGiverName",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "locationId",
|
||||||
|
"columnName": "locationId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "locationName",
|
||||||
|
"columnName": "locationName",
|
||||||
"affinity": "TEXT",
|
"affinity": "TEXT",
|
||||||
"notNull": false
|
"notNull": false
|
||||||
},
|
},
|
||||||
|
|
@ -199,12 +211,119 @@
|
||||||
},
|
},
|
||||||
"indices": [],
|
"indices": [],
|
||||||
"foreignKeys": []
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "location",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `category` TEXT, `description` TEXT, `map` TEXT, `illustrations` TEXT, `lastUpdated` INTEGER, `lastRead` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "category",
|
||||||
|
"columnName": "category",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "description",
|
||||||
|
"columnName": "description",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "map",
|
||||||
|
"columnName": "map",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "illustrations",
|
||||||
|
"columnName": "illustrations",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastUpdated",
|
||||||
|
"columnName": "lastUpdated",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastRead",
|
||||||
|
"columnName": "lastRead",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "world",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`parentId` TEXT NOT NULL, `childId` TEXT NOT NULL, `child` TEXT NOT NULL, `x` REAL, `y` REAL, PRIMARY KEY(`parentId`, `childId`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "parentId",
|
||||||
|
"columnName": "parentId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "childId",
|
||||||
|
"columnName": "childId",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "child",
|
||||||
|
"columnName": "child",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "x",
|
||||||
|
"columnName": "x",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "y",
|
||||||
|
"columnName": "y",
|
||||||
|
"affinity": "REAL",
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"parentId",
|
||||||
|
"childId"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"indices": [],
|
||||||
|
"foreignKeys": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"views": [],
|
"views": [],
|
||||||
"setupQueries": [
|
"setupQueries": [
|
||||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c69ae3b018a2383edc96a92797dfdfd3')"
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5657267ebe96b4b324d58b58aefce6f8')"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -155,8 +155,8 @@ class SearchUseCase @Inject constructor(
|
||||||
val entry = item.entries.any {
|
val entry = item.entries.any {
|
||||||
val title = it.subtitle?.contains(criteria, true) == true
|
val title = it.subtitle?.contains(criteria, true) == true
|
||||||
val subTitle = it.subtitle?.contains(criteria, true) == true
|
val subTitle = it.subtitle?.contains(criteria, true) == true
|
||||||
val questGiver = it.questGiver?.contains(criteria, true) == true
|
val questGiver = it.questGiverName?.contains(criteria, true) == true
|
||||||
val location = it.area?.contains(criteria, true) == true
|
val location = it.locationName?.contains(criteria, true) == true
|
||||||
val individualReward = it.individualReward?.contains(criteria, true) == true
|
val individualReward = it.individualReward?.contains(criteria, true) == true
|
||||||
val groupReward = it.groupReward?.contains(criteria, true) == true
|
val groupReward = it.groupReward?.contains(criteria, true) == true
|
||||||
val description = it.description.contains(criteria, true)
|
val description = it.description.contains(criteria, true)
|
||||||
|
|
@ -169,8 +169,8 @@ class SearchUseCase @Inject constructor(
|
||||||
criterion.map { criteria ->
|
criterion.map { criteria ->
|
||||||
val title = it.subtitle?.contains(criteria, true) == true
|
val title = it.subtitle?.contains(criteria, true) == true
|
||||||
val subTitle = it.subtitle?.contains(criteria, true) == true
|
val subTitle = it.subtitle?.contains(criteria, true) == true
|
||||||
val questGiver = it.questGiver?.contains(criteria, true) == true
|
val questGiver = it.questGiverName?.contains(criteria, true) == true
|
||||||
val location = it.area?.contains(criteria, true) == true
|
val location = it.locationName?.contains(criteria, true) == true
|
||||||
val individualReward = it.individualReward?.contains(criteria, true) == true
|
val individualReward = it.individualReward?.contains(criteria, true) == true
|
||||||
val groupReward = it.groupReward?.contains(criteria, true) == true
|
val groupReward = it.groupReward?.contains(criteria, true) == true
|
||||||
val description = it.description.contains(criteria, true)
|
val description = it.description.contains(criteria, true)
|
||||||
|
|
@ -188,7 +188,7 @@ class SearchUseCase @Inject constructor(
|
||||||
highlightRegex styleWith typography.search.titleHighlight,
|
highlightRegex styleWith typography.search.titleHighlight,
|
||||||
dropCapRegex styleWith typography.titleMediumDropCap,
|
dropCapRegex styleWith typography.titleMediumDropCap,
|
||||||
),
|
),
|
||||||
owner = entry?.questGiver?.let {
|
owner = entry?.questGiverName?.let {
|
||||||
AnnotatedString(
|
AnnotatedString(
|
||||||
text = "$ownerPrefix ",
|
text = "$ownerPrefix ",
|
||||||
spanStyle = typography.search.extractBold,
|
spanStyle = typography.search.extractBold,
|
||||||
|
|
@ -197,7 +197,7 @@ class SearchUseCase @Inject constructor(
|
||||||
highlightRegex styleWith typography.search.extractHighlight,
|
highlightRegex styleWith typography.search.extractHighlight,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
location = entry?.area?.let {
|
location = entry?.locationName?.let {
|
||||||
AnnotatedString(
|
AnnotatedString(
|
||||||
text = "$locationPrefix ",
|
text = "$locationPrefix ",
|
||||||
spanStyle = typography.search.extractBold,
|
spanStyle = typography.search.extractBold,
|
||||||
|
|
@ -256,7 +256,7 @@ class SearchUseCase @Inject constructor(
|
||||||
val category = item.category?.contains(criteria, true) == true
|
val category = item.category?.contains(criteria, true) == true
|
||||||
val name = item.name.contains(criteria, true)
|
val name = item.name.contains(criteria, true)
|
||||||
val description = item.description?.contains(criteria, true) == true
|
val description = item.description?.contains(criteria, true) == true
|
||||||
val child = item.child.any { it.second.name.contains(criteria, true) }
|
val child = item.child.any { it.name.contains(criteria, true) }
|
||||||
category || name || description || child
|
category || name || description || child
|
||||||
}.all { it }
|
}.all { it }
|
||||||
}.map { item ->
|
}.map { item ->
|
||||||
|
|
@ -281,7 +281,7 @@ class SearchUseCase @Inject constructor(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
destination = item.child.mapNotNull { child ->
|
destination = item.child.mapNotNull { child ->
|
||||||
highlightRegex.find(child.second.name)?.let { child.second.name }
|
highlightRegex.find(child.name)?.let { child.name }
|
||||||
}.takeIf { it.any() }?.let {
|
}.takeIf { it.any() }?.let {
|
||||||
AnnotatedString(
|
AnnotatedString(
|
||||||
text = "$destinationPrefix ",
|
text = "$destinationPrefix ",
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
package com.pixelized.rplexicon.data.database
|
package com.pixelized.rplexicon.data.database
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.room.AutoMigration
|
|
||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import com.pixelized.rplexicon.data.database.lexicon.LexiconDao
|
import com.pixelized.rplexicon.data.database.lexicon.LexiconDao
|
||||||
import com.pixelized.rplexicon.data.database.lexicon.LexiconDbo
|
import com.pixelized.rplexicon.data.database.lexicon.LexiconDbo
|
||||||
|
import com.pixelized.rplexicon.data.database.location.LocationDao
|
||||||
|
import com.pixelized.rplexicon.data.database.location.LocationDbo
|
||||||
|
import com.pixelized.rplexicon.data.database.location.WorldDao
|
||||||
|
import com.pixelized.rplexicon.data.database.location.WorldDbo
|
||||||
import com.pixelized.rplexicon.data.database.quest.QuestDao
|
import com.pixelized.rplexicon.data.database.quest.QuestDao
|
||||||
import com.pixelized.rplexicon.data.database.quest.QuestDbo
|
import com.pixelized.rplexicon.data.database.quest.QuestDbo
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
|
@ -16,19 +19,20 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [LexiconDbo::class, QuestDbo::class],
|
entities = [LexiconDbo::class, QuestDbo::class, LocationDbo::class, WorldDbo::class],
|
||||||
version = 1,
|
version = 1,
|
||||||
exportSchema = true,
|
exportSchema = true,
|
||||||
)
|
)
|
||||||
abstract class CompanionDatabase : RoomDatabase() {
|
abstract class CompanionDatabase : RoomDatabase() {
|
||||||
abstract fun lexiconDao(): LexiconDao
|
abstract fun lexiconDao(): LexiconDao
|
||||||
abstract fun questsDao(): QuestDao
|
abstract fun questsDao(): QuestDao
|
||||||
|
abstract fun locationDao(): LocationDao
|
||||||
|
abstract fun worldDao(): WorldDao
|
||||||
}
|
}
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
class DatabaseModule {
|
class DatabaseModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
fun provideCompanionDatabase(
|
fun provideCompanionDatabase(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,6 @@ interface LexiconDao {
|
||||||
@Query("SELECT * from lexicon WHERE id = :id")
|
@Query("SELECT * from lexicon WHERE id = :id")
|
||||||
fun getByIdFlow(id: String): Flow<LexiconDbo>
|
fun getByIdFlow(id: String): Flow<LexiconDbo>
|
||||||
|
|
||||||
@Query("SELECT id from lexicon WHERE name = :name LIMIT 1")
|
|
||||||
suspend fun getIdByName(name: String): String?
|
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
fun insert(item: LexiconDbo)
|
fun insert(item: LexiconDbo)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.pixelized.rplexicon.data.database.location
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.Update
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface LocationDao {
|
||||||
|
@Query("SELECT * from location")
|
||||||
|
fun getAllFlow(): Flow<List<LocationDbo>>
|
||||||
|
|
||||||
|
@Query("SELECT * from location WHERE id = :id")
|
||||||
|
fun getByIdFlow(id: String): Flow<LocationDbo>
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
fun insert(item: LocationDbo)
|
||||||
|
|
||||||
|
@Update(entity = LocationDbo::class)
|
||||||
|
fun update(item: LocationDataDbo): Int
|
||||||
|
|
||||||
|
@Update(entity = LocationDbo::class)
|
||||||
|
suspend fun update(item: LocationReadTimestampDbo): Int
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.pixelized.rplexicon.data.database.location
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "location")
|
||||||
|
data class LocationDbo(
|
||||||
|
@PrimaryKey
|
||||||
|
val id: String,
|
||||||
|
val name: String,
|
||||||
|
val category: String?,
|
||||||
|
val description: String?,
|
||||||
|
val map: String?,
|
||||||
|
val illustrations: String?,
|
||||||
|
val lastUpdated: Long?,
|
||||||
|
val lastRead: Long,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Entity(tableName = "location")
|
||||||
|
data class LocationDataDbo(
|
||||||
|
@PrimaryKey
|
||||||
|
val id: String,
|
||||||
|
val name: String,
|
||||||
|
val category: String?,
|
||||||
|
val description: String?,
|
||||||
|
val map: String?,
|
||||||
|
val illustrations: String?,
|
||||||
|
val lastUpdated: Long?,
|
||||||
|
) {
|
||||||
|
infix fun with(lastRead: Long) = LocationDbo(
|
||||||
|
id = id,
|
||||||
|
name = name,
|
||||||
|
category = category,
|
||||||
|
description = description,
|
||||||
|
map = map,
|
||||||
|
illustrations = illustrations,
|
||||||
|
lastUpdated = lastUpdated,
|
||||||
|
lastRead = lastRead,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(tableName = "location")
|
||||||
|
data class LocationReadTimestampDbo(
|
||||||
|
@PrimaryKey
|
||||||
|
val id: String,
|
||||||
|
val lastRead: Long,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.pixelized.rplexicon.data.database.location
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface WorldDao {
|
||||||
|
|
||||||
|
@Query("SELECT * from world")
|
||||||
|
fun getAllFlow(): Flow<List<WorldDbo>>
|
||||||
|
|
||||||
|
@Query("SELECT * from world WHERE parentId = :parentId")
|
||||||
|
fun getByParentIdFlow(parentId: String): Flow<List<WorldDbo>>
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insert(items: List<WorldDbo>)
|
||||||
|
|
||||||
|
@Query("DELETE FROM world")
|
||||||
|
fun deleteAll()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.pixelized.rplexicon.data.database.location
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
|
||||||
|
@Entity(tableName = "world", primaryKeys = ["parentId", "childId"])
|
||||||
|
data class WorldDbo(
|
||||||
|
val parentId: String,
|
||||||
|
val childId: String,
|
||||||
|
val child: String,
|
||||||
|
val x: Float?,
|
||||||
|
val y: Float?,
|
||||||
|
)
|
||||||
|
|
@ -11,8 +11,10 @@ data class QuestDbo(
|
||||||
val title: String,
|
val title: String,
|
||||||
val subTitle: String?,
|
val subTitle: String?,
|
||||||
val completed: Boolean,
|
val completed: Boolean,
|
||||||
val questGiver: String?,
|
val questGiverId: String?,
|
||||||
val area: String?,
|
val questGiverName: String?,
|
||||||
|
val locationId: String?,
|
||||||
|
val locationName: String?,
|
||||||
val groupReward: String?,
|
val groupReward: String?,
|
||||||
val individualReward: String?,
|
val individualReward: String?,
|
||||||
val description: String,
|
val description: String,
|
||||||
|
|
@ -30,8 +32,10 @@ data class QuestDataDbo(
|
||||||
val title: String,
|
val title: String,
|
||||||
val subTitle: String?,
|
val subTitle: String?,
|
||||||
val completed: Boolean,
|
val completed: Boolean,
|
||||||
val questGiver: String?,
|
val questGiverId: String?,
|
||||||
val area: String?,
|
val questGiverName: String?,
|
||||||
|
val locationId: String?,
|
||||||
|
val locationName: String?,
|
||||||
val groupReward: String?,
|
val groupReward: String?,
|
||||||
val individualReward: String?,
|
val individualReward: String?,
|
||||||
val description: String,
|
val description: String,
|
||||||
|
|
@ -45,8 +49,10 @@ data class QuestDataDbo(
|
||||||
title = title,
|
title = title,
|
||||||
subTitle = subTitle,
|
subTitle = subTitle,
|
||||||
completed = completed,
|
completed = completed,
|
||||||
questGiver = questGiver,
|
questGiverId = questGiverId,
|
||||||
area = area,
|
questGiverName = questGiverName,
|
||||||
|
locationId = locationId,
|
||||||
|
locationName = locationName,
|
||||||
groupReward = groupReward,
|
groupReward = groupReward,
|
||||||
individualReward = individualReward,
|
individualReward = individualReward,
|
||||||
description = description,
|
description = description,
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,18 @@ data class Location(
|
||||||
val id: String,
|
val id: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
val category: String?,
|
val category: String?,
|
||||||
val uri: Uri?,
|
|
||||||
val description: String?,
|
val description: String?,
|
||||||
|
val map: Uri?,
|
||||||
val illustrations: List<Uri>,
|
val illustrations: List<Uri>,
|
||||||
val child: List<Pair<Offset, Location>> = emptyList(),
|
val lastUpdated: Long?,
|
||||||
)
|
val lastRead: Long,
|
||||||
|
val child: List<Child> = emptyList(),
|
||||||
|
) {
|
||||||
|
val isNew: Boolean get() = lastRead - (lastUpdated ?: 0) < 0
|
||||||
|
|
||||||
|
data class Child(
|
||||||
|
val id: String,
|
||||||
|
val name: String,
|
||||||
|
val position: Offset,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -22,8 +22,10 @@ data class QuestEntry(
|
||||||
val title: String,
|
val title: String,
|
||||||
val subtitle: String?,
|
val subtitle: String?,
|
||||||
val complete: Boolean,
|
val complete: Boolean,
|
||||||
val questGiver: String?,
|
val questGiverId: String?,
|
||||||
val area: String?,
|
val questGiverName: String?,
|
||||||
|
val locationId: String?,
|
||||||
|
val locationName: String?,
|
||||||
val groupReward: String?,
|
val groupReward: String?,
|
||||||
val individualReward: String?,
|
val individualReward: String?,
|
||||||
val description: String,
|
val description: String,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package com.pixelized.rplexicon.data.parser
|
||||||
|
|
||||||
|
|
||||||
import com.google.api.services.sheets.v4.model.ValueRange
|
import com.google.api.services.sheets.v4.model.ValueRange
|
||||||
import com.pixelized.rplexicon.BuildConfig
|
|
||||||
import com.pixelized.rplexicon.data.database.quest.QuestDataDbo
|
import com.pixelized.rplexicon.data.database.quest.QuestDataDbo
|
||||||
import com.pixelized.rplexicon.data.database.quest.QuestDbo
|
import com.pixelized.rplexicon.data.database.quest.QuestDbo
|
||||||
import com.pixelized.rplexicon.data.model.Quest
|
import com.pixelized.rplexicon.data.model.Quest
|
||||||
|
|
@ -32,8 +31,10 @@ class QuestParser @Inject constructor(
|
||||||
title = quest,
|
title = quest,
|
||||||
subTitle = item.parse(column = SUB_TITLE),
|
subTitle = item.parse(column = SUB_TITLE),
|
||||||
completed = item.parseBool(column = COMPLETED) ?: false,
|
completed = item.parseBool(column = COMPLETED) ?: false,
|
||||||
questGiver = item.parse(column = QUEST_GIVER),
|
questGiverId = item.parse(column = QUEST_GIVER_ID),
|
||||||
area = item.parse(column = AREA),
|
questGiverName = item.parse(column = QUEST_GIVER_NAME),
|
||||||
|
locationId = item.parse(column = LOCATION_ID),
|
||||||
|
locationName = item.parse(column = LOCATION_NAME),
|
||||||
groupReward = item.parse(column = GROUP_REWARD),
|
groupReward = item.parse(column = GROUP_REWARD),
|
||||||
individualReward = item.parse(column = INDIVIDUAL_REWARD),
|
individualReward = item.parse(column = INDIVIDUAL_REWARD),
|
||||||
description = description,
|
description = description,
|
||||||
|
|
@ -71,8 +72,10 @@ class QuestParser @Inject constructor(
|
||||||
title = data.title,
|
title = data.title,
|
||||||
subtitle = data.subTitle,
|
subtitle = data.subTitle,
|
||||||
complete = data.completed,
|
complete = data.completed,
|
||||||
questGiver = data.questGiver,
|
questGiverId = data.questGiverId,
|
||||||
area = data.area,
|
questGiverName = data.questGiverName,
|
||||||
|
locationId = data.locationId,
|
||||||
|
locationName = data.locationName,
|
||||||
groupReward = data.groupReward,
|
groupReward = data.groupReward,
|
||||||
individualReward = data.individualReward,
|
individualReward = data.individualReward,
|
||||||
description = data.description,
|
description = data.description,
|
||||||
|
|
@ -88,8 +91,10 @@ class QuestParser @Inject constructor(
|
||||||
private val CATEGORY = column("Catégorie")
|
private val CATEGORY = column("Catégorie")
|
||||||
private val SUB_TITLE = column("Sous Titre")
|
private val SUB_TITLE = column("Sous Titre")
|
||||||
private val COMPLETED = column("Compléter")
|
private val COMPLETED = column("Compléter")
|
||||||
private val QUEST_GIVER = column("Commanditaire")
|
private val QUEST_GIVER_ID = column("Id Commanditaire")
|
||||||
private val AREA = column("Lieu")
|
private val QUEST_GIVER_NAME = column("Commanditaire")
|
||||||
|
private val LOCATION_ID = column("Id Lieu")
|
||||||
|
private val LOCATION_NAME = column("Lieu")
|
||||||
private val GROUP_REWARD = column("Récompense de groupe")
|
private val GROUP_REWARD = column("Récompense de groupe")
|
||||||
private val INDIVIDUAL_REWARD = column("Récompense individuelle")
|
private val INDIVIDUAL_REWARD = column("Récompense individuelle")
|
||||||
private val DESCRIPTION = column("Description")
|
private val DESCRIPTION = column("Description")
|
||||||
|
|
@ -104,8 +109,10 @@ class QuestParser @Inject constructor(
|
||||||
CATEGORY,
|
CATEGORY,
|
||||||
SUB_TITLE,
|
SUB_TITLE,
|
||||||
COMPLETED,
|
COMPLETED,
|
||||||
QUEST_GIVER,
|
QUEST_GIVER_ID,
|
||||||
AREA,
|
QUEST_GIVER_NAME,
|
||||||
|
LOCATION_ID,
|
||||||
|
LOCATION_NAME,
|
||||||
GROUP_REWARD,
|
GROUP_REWARD,
|
||||||
INDIVIDUAL_REWARD,
|
INDIVIDUAL_REWARD,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,42 @@
|
||||||
package com.pixelized.rplexicon.data.parser.map
|
package com.pixelized.rplexicon.data.parser.map
|
||||||
|
|
||||||
import com.google.api.services.sheets.v4.model.ValueRange
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import com.pixelized.rplexicon.data.database.location.LocationDbo
|
||||||
|
import com.pixelized.rplexicon.data.database.location.WorldDbo
|
||||||
import com.pixelized.rplexicon.data.model.Location
|
import com.pixelized.rplexicon.data.model.Location
|
||||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
import com.pixelized.rplexicon.data.parser.IllustrationParser
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.toUriOrNull
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class LocationParser @Inject constructor(
|
class LocationParser @Inject constructor(
|
||||||
private val mapParser: MapParser,
|
private val illustrationParser: IllustrationParser,
|
||||||
private val worldParser: WorldParser,
|
|
||||||
) {
|
) {
|
||||||
@Throws(IncompatibleSheetStructure::class)
|
fun convert(location: LocationDbo, world: Map<String, List<WorldDbo>>): Location {
|
||||||
fun parse(mapSheet: ValueRange, worldSheet: ValueRange): List<Location> {
|
val map = Location(
|
||||||
val localMaps = mapParser.parse(sheet = mapSheet)
|
id = location.id,
|
||||||
val localWorld = worldParser.parse(sheet = worldSheet)
|
name = location.name,
|
||||||
|
category = location.category,
|
||||||
val mapHash = localMaps
|
description = location.description,
|
||||||
.map { localMap ->
|
map = location.map?.toUriOrNull(),
|
||||||
Location(
|
illustrations = illustrationParser.parse(value = location.illustrations),
|
||||||
id = localMap.name,
|
lastUpdated = location.lastUpdated,
|
||||||
name = localMap.name,
|
lastRead = location.lastRead,
|
||||||
category = localMap.category,
|
child = world[location.id]?.map {
|
||||||
uri = localMap.uri,
|
Location.Child(
|
||||||
description = localMap.description,
|
id = it.childId,
|
||||||
illustrations = localMap.illustrations,
|
name = it.child,
|
||||||
child = emptyList(),
|
position = when {
|
||||||
|
it.x != null && it.y != null -> Offset(it.x, it.y)
|
||||||
|
else -> Offset.Unspecified
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}.associateBy { it.name }
|
} ?: emptyList()
|
||||||
|
)
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
val maps = mapHash.map { entry ->
|
fun convert(locations: List<LocationDbo>, world: List<WorldDbo>): List<Location> {
|
||||||
entry.value.copy(
|
val worldHash = world.groupBy { it.parentId }
|
||||||
child = localWorld
|
return locations.map { convert(location = it, world = worldHash) }
|
||||||
.filter { it.parent == entry.key }
|
|
||||||
.mapNotNull { world -> mapHash[world.child]?.let { map -> world.position to map } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return maps
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,30 +1,32 @@
|
||||||
package com.pixelized.rplexicon.data.parser.map
|
package com.pixelized.rplexicon.data.parser.map
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import com.google.api.services.sheets.v4.model.ValueRange
|
import com.google.api.services.sheets.v4.model.ValueRange
|
||||||
import com.pixelized.rplexicon.data.parser.IllustrationParser
|
import com.pixelized.rplexicon.data.database.location.LocationDataDbo
|
||||||
|
import com.pixelized.rplexicon.data.parser.TimeUpdateParser
|
||||||
import com.pixelized.rplexicon.data.parser.column
|
import com.pixelized.rplexicon.data.parser.column
|
||||||
import com.pixelized.rplexicon.data.parser.parserScope
|
import com.pixelized.rplexicon.data.parser.parserScope
|
||||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class MapParser @Inject constructor(
|
class MapParser @Inject constructor(
|
||||||
private val illustrationParser: IllustrationParser,
|
private val timeParser: TimeUpdateParser,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@Throws(IncompatibleSheetStructure::class)
|
@Throws(IncompatibleSheetStructure::class)
|
||||||
fun parse(sheet: ValueRange): List<MapDto> = parserScope {
|
fun parse(sheet: ValueRange): List<LocationDataDbo> = parserScope(timeParser) {
|
||||||
val maps = mutableListOf<MapDto>()
|
val maps = mutableListOf<LocationDataDbo>()
|
||||||
|
|
||||||
sheet.forEachDataLine(columns = COLUMNS) {
|
sheet.forEachDataLine(columns = COLUMNS) {
|
||||||
|
val id = it.parse(column = ID)
|
||||||
val name = it.parse(column = NAME)
|
val name = it.parse(column = NAME)
|
||||||
if (name != null) {
|
if (id != null && name != null) {
|
||||||
val map = MapDto(
|
val map = LocationDataDbo(
|
||||||
|
id = id,
|
||||||
name = name,
|
name = name,
|
||||||
category = it.parse(column = CATEGORY),
|
category = it.parse(column = CATEGORY),
|
||||||
uri = it.parseUri(column = URI),
|
|
||||||
description = it.parse(column = DESCRIPTION),
|
description = it.parse(column = DESCRIPTION),
|
||||||
illustrations = illustrationParser.parse(it.parse(column = ILLUSTRATIONS)),
|
map = it.parse(column = MAP),
|
||||||
|
illustrations = it.parse(column = ILLUSTRATIONS),
|
||||||
|
lastUpdated = it.parseTime(column = UPDATE),
|
||||||
)
|
)
|
||||||
maps.add(map)
|
maps.add(map)
|
||||||
}
|
}
|
||||||
|
|
@ -33,20 +35,16 @@ class MapParser @Inject constructor(
|
||||||
return@parserScope maps
|
return@parserScope maps
|
||||||
}
|
}
|
||||||
|
|
||||||
data class MapDto(
|
|
||||||
val name: String,
|
|
||||||
val category: String?,
|
|
||||||
val uri: Uri?,
|
|
||||||
val description: String?,
|
|
||||||
val illustrations: List<Uri>,
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private val ID = column("Id")
|
||||||
private val NAME = column("Nom")
|
private val NAME = column("Nom")
|
||||||
private val CATEGORY = column("Catégorie")
|
private val CATEGORY = column("Catégorie")
|
||||||
private val URI = column("Carte")
|
private val MAP = column("Carte")
|
||||||
private val DESCRIPTION = column("Description")
|
private val DESCRIPTION = column("Description")
|
||||||
private val ILLUSTRATIONS = column("Illustrations")
|
private val ILLUSTRATIONS = column("Illustrations")
|
||||||
private val COLUMNS get() = listOf(NAME, CATEGORY, URI, DESCRIPTION, ILLUSTRATIONS)
|
private val UPDATE = column("Mise à jour")
|
||||||
|
|
||||||
|
private val COLUMNS
|
||||||
|
get() = listOf(ID, NAME, CATEGORY, MAP, DESCRIPTION, ILLUSTRATIONS, UPDATE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package com.pixelized.rplexicon.data.parser.map
|
package com.pixelized.rplexicon.data.parser.map
|
||||||
|
|
||||||
import androidx.compose.ui.geometry.Offset
|
|
||||||
import com.google.api.services.sheets.v4.model.ValueRange
|
import com.google.api.services.sheets.v4.model.ValueRange
|
||||||
|
import com.pixelized.rplexicon.data.database.location.WorldDbo
|
||||||
import com.pixelized.rplexicon.data.parser.column
|
import com.pixelized.rplexicon.data.parser.column
|
||||||
import com.pixelized.rplexicon.data.parser.parserScope
|
import com.pixelized.rplexicon.data.parser.parserScope
|
||||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||||
|
|
@ -10,22 +10,28 @@ import javax.inject.Inject
|
||||||
class WorldParser @Inject constructor() {
|
class WorldParser @Inject constructor() {
|
||||||
|
|
||||||
@Throws(IncompatibleSheetStructure::class)
|
@Throws(IncompatibleSheetStructure::class)
|
||||||
fun parse(sheet: ValueRange): List<WorldDto> = parserScope {
|
fun parse(sheet: ValueRange): List<WorldDbo> = parserScope {
|
||||||
val worlds = mutableListOf<WorldDto>()
|
val worlds = mutableListOf<WorldDbo>()
|
||||||
|
|
||||||
sheet.forEachDataLine(columns = COLUMNS) { line ->
|
sheet.forEachDataLine(columns = COLUMNS) { line ->
|
||||||
|
val parentId = line.parse(column = PARENT_ID)
|
||||||
val parent = line.parse(column = PARENT)
|
val parent = line.parse(column = PARENT)
|
||||||
|
val childId = line.parse(column = CHILD_ID)
|
||||||
val child = line.parse(column = CHILD)
|
val child = line.parse(column = CHILD)
|
||||||
val x = line.parseFloat(column = X)
|
val x = line.parseFloat(column = X)
|
||||||
val y = line.parseFloat(column = Y)
|
val y = line.parseFloat(column = Y)
|
||||||
if (child != null) {
|
|
||||||
val world = WorldDto(
|
// We check but don't use the parent value, because of the pre validation of the datasheet
|
||||||
parent = parent,
|
if (
|
||||||
|
parentId != null && parent != null &&
|
||||||
|
childId != null && child != null
|
||||||
|
) {
|
||||||
|
val world = WorldDbo(
|
||||||
|
parentId = parentId,
|
||||||
|
childId = childId,
|
||||||
child = child,
|
child = child,
|
||||||
position = when {
|
x = x,
|
||||||
x != null && y != null -> Offset(x = x, y = y)
|
y = y,
|
||||||
else -> Offset.Unspecified
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
worlds.add(world)
|
worlds.add(world)
|
||||||
}
|
}
|
||||||
|
|
@ -34,17 +40,13 @@ class WorldParser @Inject constructor() {
|
||||||
return@parserScope worlds
|
return@parserScope worlds
|
||||||
}
|
}
|
||||||
|
|
||||||
data class WorldDto(
|
|
||||||
val parent: String?,
|
|
||||||
val child: String,
|
|
||||||
val position: Offset,
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private val PARENT_ID = column("Id Parent")
|
||||||
private val PARENT = column("Parent")
|
private val PARENT = column("Parent")
|
||||||
|
private val CHILD_ID = column("Id Enfant")
|
||||||
private val CHILD = column("Enfant")
|
private val CHILD = column("Enfant")
|
||||||
private val X = column("X")
|
private val X = column("X")
|
||||||
private val Y = column("Y")
|
private val Y = column("Y")
|
||||||
private val COLUMNS get() = listOf(PARENT, CHILD, X, Y)
|
private val COLUMNS get() = listOf(PARENT_ID, PARENT, CHILD_ID, CHILD, X, Y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -54,15 +54,6 @@ class LexiconRepository @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the first or null [Lexicon] instance in the list by filtering by name.
|
|
||||||
* @param name the name of the [Lexicon] instance.
|
|
||||||
* @return a nullable [Lexicon] instance.
|
|
||||||
*/
|
|
||||||
suspend fun getByNameFlow(name: String?): String? {
|
|
||||||
return name?.let { database.lexiconDao().getIdByName(name = name) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query the [Lexicon] from the backend.
|
* Query the [Lexicon] from the backend.
|
||||||
* @throws IncompatibleSheetStructure if the data structure change and mandatory data are missing.
|
* @throws IncompatibleSheetStructure if the data structure change and mandatory data are missing.
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,67 @@
|
||||||
package com.pixelized.rplexicon.data.repository.lexicon
|
package com.pixelized.rplexicon.data.repository.lexicon
|
||||||
|
|
||||||
|
import com.pixelized.rplexicon.data.database.CompanionDatabase
|
||||||
|
import com.pixelized.rplexicon.data.database.location.LocationReadTimestampDbo
|
||||||
import com.pixelized.rplexicon.data.model.Location
|
import com.pixelized.rplexicon.data.model.Location
|
||||||
import com.pixelized.rplexicon.data.parser.map.LocationParser
|
import com.pixelized.rplexicon.data.parser.map.LocationParser
|
||||||
|
import com.pixelized.rplexicon.data.parser.map.MapParser
|
||||||
|
import com.pixelized.rplexicon.data.parser.map.WorldParser
|
||||||
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
|
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
|
||||||
import com.pixelized.rplexicon.data.repository.LexiconBinder
|
import com.pixelized.rplexicon.data.repository.LexiconBinder
|
||||||
import com.pixelized.rplexicon.utilitary.Update
|
import com.pixelized.rplexicon.utilitary.Update
|
||||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class LocationRepository @Inject constructor(
|
class LocationRepository @Inject constructor(
|
||||||
private val googleRepository: GoogleSheetServiceRepository,
|
private val googleRepository: GoogleSheetServiceRepository,
|
||||||
private val parser: LocationParser,
|
private val database: CompanionDatabase,
|
||||||
|
private val locationParser: LocationParser,
|
||||||
|
private val mapParser: MapParser,
|
||||||
|
private val worldParser: WorldParser,
|
||||||
) {
|
) {
|
||||||
|
private val scope = CoroutineScope(Dispatchers.Default + Job())
|
||||||
|
|
||||||
private val _data = MutableStateFlow<List<Location>>(emptyList())
|
private val _data = MutableStateFlow<List<Location>>(emptyList())
|
||||||
val data: StateFlow<List<Location>> get() = _data
|
val data: StateFlow<List<Location>> get() = _data
|
||||||
|
|
||||||
var lastSuccessFullUpdate: Update = Update.INITIAL
|
var lastSuccessFullUpdate: Update = Update.INITIAL
|
||||||
private set
|
private set
|
||||||
|
|
||||||
fun find(id: String?): Location? {
|
init {
|
||||||
return id?.let { _data.value.firstOrNull { it.id == id } }
|
scope.launch(Dispatchers.IO) {
|
||||||
|
database.locationDao().getAllFlow()
|
||||||
|
.combine(database.worldDao().getAllFlow()) { location, world -> location to world }
|
||||||
|
.collect { data ->
|
||||||
|
val (locations, world) = data
|
||||||
|
_data.value = locationParser.convert(locations = locations, world = world)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getByIdFlow(id: String?): Flow<Location> = when (id) {
|
||||||
|
null -> emptyFlow()
|
||||||
|
else -> database.locationDao().getByIdFlow(id = id)
|
||||||
|
.combine(database.worldDao().getByParentIdFlow(parentId = id)) { map, link ->
|
||||||
|
map to link.groupBy { it.parentId }
|
||||||
|
}
|
||||||
|
.map { data ->
|
||||||
|
val (locations, world) = data
|
||||||
|
locationParser.convert(location = locations, world = world)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IncompatibleSheetStructure::class, Exception::class)
|
@Throws(IncompatibleSheetStructure::class, Exception::class)
|
||||||
|
|
@ -35,12 +71,36 @@ class LocationRepository @Inject constructor(
|
||||||
async { sheets.get(LexiconBinder.ID, LexiconBinder.MAP).execute() },
|
async { sheets.get(LexiconBinder.ID, LexiconBinder.MAP).execute() },
|
||||||
async { sheets.get(LexiconBinder.ID, LexiconBinder.WORLD).execute() },
|
async { sheets.get(LexiconBinder.ID, LexiconBinder.WORLD).execute() },
|
||||||
)
|
)
|
||||||
val data = parser.parse(mapSheet = map, worldSheet = world)
|
database.runInTransaction {
|
||||||
_data.emit(data)
|
val mapDao = database.locationDao()
|
||||||
|
mapParser.parse(map).forEach { item ->
|
||||||
|
val row = mapDao.update(item)
|
||||||
|
if (row == 0) mapDao.insert(item with System.currentTimeMillis())
|
||||||
|
}
|
||||||
|
|
||||||
|
val worldDao = database.worldDao()
|
||||||
|
worldDao.deleteAll()
|
||||||
|
worldDao.insert(items = worldParser.parse(world))
|
||||||
|
}
|
||||||
|
|
||||||
lastSuccessFullUpdate = Update.currentTime()
|
lastSuccessFullUpdate = Update.currentTime()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the [Location#lastTime] field of a [Location] instance.
|
||||||
|
* @param id the id of the [Location] instance.
|
||||||
|
* @param timestamp the timestamp that will update the lastRead filed.
|
||||||
|
*/
|
||||||
|
suspend fun updateReadTime(id: String, timestamp: Long = System.currentTimeMillis()) {
|
||||||
|
database.locationDao().update(
|
||||||
|
item = LocationReadTimestampDbo(
|
||||||
|
id = id,
|
||||||
|
lastRead = timestamp,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val WORLD_SHEET_URL =
|
const val WORLD_SHEET_URL =
|
||||||
"https://docs.google.com/spreadsheets/d/${LexiconBinder.ID}/edit#gid=1943877267"
|
"https://docs.google.com/spreadsheets/d/${LexiconBinder.ID}/edit#gid=1943877267"
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.pixelized.rplexicon.data.repository.lexicon.LocationRepository
|
import com.pixelized.rplexicon.data.repository.lexicon.LocationRepository
|
||||||
import com.pixelized.rplexicon.ui.navigation.screens.lexiconDetailArgument
|
import com.pixelized.rplexicon.ui.navigation.screens.lexiconDetailArgument
|
||||||
import com.pixelized.rplexicon.ui.navigation.screens.locationDetailArgument
|
import com.pixelized.rplexicon.ui.navigation.screens.locationDetailArgument
|
||||||
|
|
@ -16,6 +17,9 @@ import com.pixelized.rplexicon.utilitary.cells
|
||||||
import com.pixelized.rplexicon.utilitary.line
|
import com.pixelized.rplexicon.utilitary.line
|
||||||
import com.pixelized.rplexicon.utilitary.table
|
import com.pixelized.rplexicon.utilitary.table
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
|
|
@ -28,7 +32,9 @@ class LocationDetailViewModel @Inject constructor(
|
||||||
private val clipboard = application.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
private val clipboard = application.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
|
|
||||||
val sheetUri = LocationRepository.WORLD_SHEET_URL
|
val sheetUri = LocationRepository.WORLD_SHEET_URL
|
||||||
val location: State<LocationDetailUio?>
|
|
||||||
|
private val _location = mutableStateOf<LocationDetailUio?>(null)
|
||||||
|
val location: State<LocationDetailUio?> get() = _location
|
||||||
|
|
||||||
private val _selectedMarquee = mutableStateOf<Int?>(null)
|
private val _selectedMarquee = mutableStateOf<Int?>(null)
|
||||||
val selectedMarquee: State<Int?> get() = _selectedMarquee
|
val selectedMarquee: State<Int?> get() = _selectedMarquee
|
||||||
|
|
@ -44,25 +50,35 @@ class LocationDetailViewModel @Inject constructor(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val argument = savedStateHandle.locationDetailArgument
|
val argument = savedStateHandle.locationDetailArgument
|
||||||
val source = repository.find(id = argument.id)
|
|
||||||
|
|
||||||
location = mutableStateOf(
|
viewModelScope.launch {
|
||||||
source?.let {
|
launch(Dispatchers.IO) {
|
||||||
LocationDetailUio(
|
// update the last read time for that lexicon
|
||||||
name = it.name,
|
repository.updateReadTime(id = argument.id)
|
||||||
map = it.uri,
|
|
||||||
description = it.description,
|
|
||||||
illustrations = it.illustrations,
|
|
||||||
marquees = it.child.map { child ->
|
|
||||||
MarqueeUio(
|
|
||||||
id = child.second.id,
|
|
||||||
name = child.second.name,
|
|
||||||
position = child.first,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
launch(Dispatchers.IO) {
|
||||||
|
// fetch and display the detail data.
|
||||||
|
repository.getByIdFlow(id = argument.id).collect { source ->
|
||||||
|
val location = LocationDetailUio(
|
||||||
|
name = source.name,
|
||||||
|
map = source.map,
|
||||||
|
description = source.description,
|
||||||
|
illustrations = source.illustrations,
|
||||||
|
marquees = source.child.map { child ->
|
||||||
|
MarqueeUio(
|
||||||
|
id = child.id,
|
||||||
|
name = child.name,
|
||||||
|
position = child.position,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// Update the UI state
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
_location.value = location
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSelectMarquee(marqueeUio: MarqueeUio) {
|
fun onSelectMarquee(marqueeUio: MarqueeUio) {
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ data class LocationItemUio(
|
||||||
val id: String,
|
val id: String,
|
||||||
val title: String,
|
val title: String,
|
||||||
val placeholder: Boolean = false,
|
val placeholder: Boolean = false,
|
||||||
|
val isNew: Boolean = false,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun preview(
|
fun preview(
|
||||||
|
|
@ -73,6 +74,7 @@ fun LocationItem(
|
||||||
true -> Modifier.placeholder { true }
|
true -> Modifier.placeholder { true }
|
||||||
else -> Modifier.alignByBaseline()
|
else -> Modifier.alignByBaseline()
|
||||||
},
|
},
|
||||||
|
color = if (item.isNew) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
|
||||||
style = typography.base.titleMedium,
|
style = typography.base.titleMedium,
|
||||||
text = LOS_FULL,
|
text = LOS_FULL,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,9 @@ class LocationViewModel @Inject constructor(
|
||||||
},
|
},
|
||||||
valueTransform = { entry ->
|
valueTransform = { entry ->
|
||||||
LocationItemUio(
|
LocationItemUio(
|
||||||
id = entry.name,
|
id = entry.id,
|
||||||
title = entry.name,
|
title = entry.name,
|
||||||
|
isNew = entry.isNew,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
package com.pixelized.rplexicon.ui.screens.quest.detail
|
package com.pixelized.rplexicon.ui.screens.quest.detail
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
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.data.repository.lexicon.QuestRepository
|
||||||
import com.pixelized.rplexicon.ui.navigation.screens.lexiconDetailArgument
|
import com.pixelized.rplexicon.ui.navigation.screens.lexiconDetailArgument
|
||||||
import com.pixelized.rplexicon.ui.navigation.screens.questDetailArgument
|
import com.pixelized.rplexicon.ui.navigation.screens.questDetailArgument
|
||||||
|
|
@ -21,8 +18,6 @@ import javax.inject.Inject
|
||||||
class QuestDetailViewModel @Inject constructor(
|
class QuestDetailViewModel @Inject constructor(
|
||||||
savedStateHandle: SavedStateHandle,
|
savedStateHandle: SavedStateHandle,
|
||||||
questRepository: QuestRepository,
|
questRepository: QuestRepository,
|
||||||
lexiconRepository: LexiconRepository,
|
|
||||||
locationRepository: LocationRepository,
|
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _quest = mutableStateOf<QuestDetailUio?>(null)
|
private val _quest = mutableStateOf<QuestDetailUio?>(null)
|
||||||
val quest: State<QuestDetailUio?> get() = _quest
|
val quest: State<QuestDetailUio?> get() = _quest
|
||||||
|
|
@ -46,13 +41,12 @@ class QuestDetailViewModel @Inject constructor(
|
||||||
background = source.entries.mapNotNull { it.background }.randomOrNull(),
|
background = source.entries.mapNotNull { it.background }.randomOrNull(),
|
||||||
title = source.title,
|
title = source.title,
|
||||||
steps = source.entries.map { entry ->
|
steps = source.entries.map { entry ->
|
||||||
val location = locationRepository.find(id = entry.area)
|
|
||||||
QuestDetailUio.QuestStep(
|
QuestDetailUio.QuestStep(
|
||||||
subtitle = entry.subtitle,
|
subtitle = entry.subtitle,
|
||||||
giverId = lexiconRepository.getByNameFlow(name = entry.questGiver),
|
giverId = entry.questGiverId,
|
||||||
giver = entry.questGiver,
|
giver = entry.questGiverName,
|
||||||
placeId = location?.id,
|
placeId = entry.locationId,
|
||||||
place = location?.name ?: entry.area,
|
place = entry.locationName,
|
||||||
globalReward = entry.groupReward,
|
globalReward = entry.groupReward,
|
||||||
individualReward = entry.individualReward,
|
individualReward = entry.individualReward,
|
||||||
description = entry.description,
|
description = entry.description,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue