From b6489e5a851eb7585fc859cea3d8a500a8451123 Mon Sep 17 00:00:00 2001 From: "Andres Gomez, Thomas (ITDV RL)" Date: Wed, 19 Jun 2024 22:40:39 +0200 Subject: [PATCH] Refactor the adventure database to better fit the model. --- .../2.json | 132 +++++++---- .../data/database/CompanionDatabase.kt | 6 +- .../data/database/adventure/AdventureDao.kt | 95 ++++---- .../data/database/adventure/AdventureDbo.kt | 136 +++++++++-- .../data/model/adventure/Adventure.kt | 6 +- .../adventure/AdventureDboFactory.kt | 105 +++++---- .../adventure/AdventureRepository.kt | 214 ++++++++++-------- ...Chapter.kt => ComposableAdventureStory.kt} | 4 +- .../adventure/book/AdventureBooksViewModel.kt | 8 +- .../adventure/chapter/AdventureChapter.kt | 16 +- ...ersScreen.kt => AdventureStoriesScreen.kt} | 30 +-- ...wModel.kt => AdventureStoriesViewModel.kt} | 15 +- .../adventure/detail/AdventureDetailScreen.kt | 39 ++-- .../detail/AdventureDetailViewModel.kt | 6 +- .../screens/adventure/detail/AdventureLine.kt | 22 +- 15 files changed, 497 insertions(+), 337 deletions(-) rename app/src/main/java/com/pixelized/rplexicon/ui/navigation/screens/{ComposableAdventureChapter.kt => ComposableAdventureStory.kt} (97%) rename app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/{AdventureChaptersScreen.kt => AdventureStoriesScreen.kt} (90%) rename app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/{AdventureChaptersViewModel.kt => AdventureStoriesViewModel.kt} (85%) diff --git a/app/schemas/com.pixelized.rplexicon.data.database.CompanionDatabase/2.json b/app/schemas/com.pixelized.rplexicon.data.database.CompanionDatabase/2.json index 79e0a5d..f6fa897 100644 --- a/app/schemas/com.pixelized.rplexicon.data.database.CompanionDatabase/2.json +++ b/app/schemas/com.pixelized.rplexicon.data.database.CompanionDatabase/2.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 2, - "identityHash": "eecd0da0c8ae6578a5d36c3b926c2fe8", + "identityHash": "95cb578b3b61a022ab4dda676d2e4645", "entities": [ { "tableName": "lexicon", @@ -320,51 +320,71 @@ "foreignKeys": [] }, { - "tableName": "adventures", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bookTitle` TEXT NOT NULL, `adventureTitle` TEXT NOT NULL, `adventureCategory` TEXT, `bookIcon` TEXT, `adventureBackground` TEXT, `index` INTEGER NOT NULL, `revision` INTEGER NOT NULL, `documentId` TEXT NOT NULL, PRIMARY KEY(`bookTitle`, `adventureTitle`))", + "tableName": "AdventureBooks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`documentId` TEXT NOT NULL, `title` TEXT NOT NULL, `icon` TEXT, PRIMARY KEY(`documentId`))", "fields": [ { - "fieldPath": "bookTitle", - "columnName": "bookTitle", + "fieldPath": "documentId", + "columnName": "documentId", "affinity": "TEXT", "notNull": true }, { - "fieldPath": "adventureTitle", - "columnName": "adventureTitle", + "fieldPath": "title", + "columnName": "title", "affinity": "TEXT", "notNull": true }, { - "fieldPath": "adventureCategory", - "columnName": "adventureCategory", + "fieldPath": "icon", + "columnName": "icon", "affinity": "TEXT", "notNull": false - }, + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "documentId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AdventureStory", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`title` TEXT NOT NULL, `category` TEXT, `background` TEXT, `revision` INTEGER NOT NULL, `index` INTEGER NOT NULL, `documentId` TEXT NOT NULL, PRIMARY KEY(`title`, `documentId`), FOREIGN KEY(`documentId`) REFERENCES `AdventureBooks`(`documentId`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ { - "fieldPath": "bookIcon", - "columnName": "bookIcon", + "fieldPath": "title", + "columnName": "title", "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "adventureBackground", - "columnName": "adventureBackground", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "index", - "columnName": "index", - "affinity": "INTEGER", "notNull": true }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "background", + "columnName": "background", + "affinity": "TEXT", + "notNull": false + }, { "fieldPath": "revision", "columnName": "revision", "affinity": "INTEGER", "notNull": true }, + { + "fieldPath": "index", + "columnName": "index", + "affinity": "INTEGER", + "notNull": true + }, { "fieldPath": "documentId", "columnName": "documentId", @@ -375,29 +395,29 @@ "primaryKey": { "autoGenerate": false, "columnNames": [ - "bookTitle", - "adventureTitle" + "title", + "documentId" ] }, "indices": [], - "foreignKeys": [] + "foreignKeys": [ + { + "table": "AdventureBooks", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "documentId" + ], + "referencedColumns": [ + "documentId" + ] + } + ] }, { - "tableName": "adventuresStories", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bookTitle` TEXT NOT NULL, `adventureTitle` TEXT NOT NULL, `index` INTEGER NOT NULL, `text` TEXT NOT NULL, `format` TEXT NOT NULL, PRIMARY KEY(`bookTitle`, `adventureTitle`, `index`), FOREIGN KEY(`bookTitle`, `adventureTitle`) REFERENCES `adventures`(`bookTitle`, `adventureTitle`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "tableName": "AdventureLine", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`index` INTEGER NOT NULL, `text` TEXT NOT NULL, `format` TEXT NOT NULL, `documentId` TEXT NOT NULL, `title` TEXT NOT NULL, PRIMARY KEY(`index`, `title`, `documentId`), FOREIGN KEY(`title`, `documentId`) REFERENCES `AdventureStory`(`title`, `documentId`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ - { - "fieldPath": "bookTitle", - "columnName": "bookTitle", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "adventureTitle", - "columnName": "adventureTitle", - "affinity": "TEXT", - "notNull": true - }, { "fieldPath": "index", "columnName": "index", @@ -415,29 +435,41 @@ "columnName": "format", "affinity": "TEXT", "notNull": true + }, + { + "fieldPath": "documentId", + "columnName": "documentId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "story", + "columnName": "title", + "affinity": "TEXT", + "notNull": true } ], "primaryKey": { "autoGenerate": false, "columnNames": [ - "bookTitle", - "adventureTitle", - "index" + "index", + "title", + "documentId" ] }, "indices": [], "foreignKeys": [ { - "table": "adventures", + "table": "AdventureStory", "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ - "bookTitle", - "adventureTitle" + "title", + "documentId" ], "referencedColumns": [ - "bookTitle", - "adventureTitle" + "title", + "documentId" ] } ] @@ -446,7 +478,7 @@ "views": [], "setupQueries": [ "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, 'eecd0da0c8ae6578a5d36c3b926c2fe8')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '95cb578b3b61a022ab4dda676d2e4645')" ] } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/data/database/CompanionDatabase.kt b/app/src/main/java/com/pixelized/rplexicon/data/database/CompanionDatabase.kt index 9827326..e18f06a 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/database/CompanionDatabase.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/database/CompanionDatabase.kt @@ -5,8 +5,9 @@ import androidx.room.AutoMigration import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase +import com.pixelized.rplexicon.data.database.adventure.AdventureBookDbo import com.pixelized.rplexicon.data.database.adventure.AdventureDao -import com.pixelized.rplexicon.data.database.adventure.AdventureDbo +import com.pixelized.rplexicon.data.database.adventure.AdventureLineDbo import com.pixelized.rplexicon.data.database.adventure.AdventureStoryDbo import com.pixelized.rplexicon.data.database.lexicon.LexiconDao import com.pixelized.rplexicon.data.database.lexicon.LexiconDbo @@ -28,8 +29,9 @@ import dagger.hilt.components.SingletonComponent QuestDbo::class, LocationDbo::class, WorldDbo::class, - AdventureDbo::class, + AdventureBookDbo::class, AdventureStoryDbo::class, + AdventureLineDbo::class, ], version = 2, exportSchema = true, diff --git a/app/src/main/java/com/pixelized/rplexicon/data/database/adventure/AdventureDao.kt b/app/src/main/java/com/pixelized/rplexicon/data/database/adventure/AdventureDao.kt index 93a426b..c5c3ec6 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/database/adventure/AdventureDao.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/database/adventure/AdventureDao.kt @@ -11,71 +11,74 @@ import kotlinx.coroutines.flow.Flow @Dao interface AdventureDao { - @Query("SELECT * from adventures") - fun getAllAdventureFlow(): Flow> + @Query("SELECT * from ${AdventureBookDbo.TABLE}") + fun getBooksFlow(): Flow> - @Query("SELECT * from adventuresStories") - fun getAllAdventureStoryFlow(): Flow> + @Query("SELECT * from ${AdventureStoryDbo.TABLE}") + fun getStoriesFlow(): Flow> - @Query("SELECT * from adventures") - fun findAdventures(): List + @Query("SELECT * from ${AdventureLineDbo.TABLE}") + fun getStoryLinesFlow(): Flow> - @Query("SELECT * from adventures where bookTitle = :bookTitle") - fun findAdventures(bookTitle: String): List + @Query("SELECT * from ${AdventureBookDbo.TABLE}") + fun findBooks(): List - @Query("SELECT * from adventures where bookTitle = :bookTitle and adventureTitle = :adventureTitle") - fun findAdventure(bookTitle: String, adventureTitle: String): AdventureDbo? + @Query("SELECT * from ${AdventureStoryDbo.TABLE}") + fun findStories(): List + + @Query("SELECT * from ${AdventureStoryDbo.TABLE} where documentId = :documentId") + fun findStories(documentId: String): List + + @Query("SELECT * from ${AdventureStoryDbo.TABLE} where documentId = :documentId AND title = :title") + fun findStory(documentId: String, title: String): AdventureStoryDbo? + + @Insert(onConflict = OnConflictStrategy.IGNORE) + fun insertBook(book: AdventureBookDbo) @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insertBook(item: AdventureDbo) + fun insertStory(story: AdventureStoryDbo) @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insertStory(item: AdventureStoryDbo) + fun insertLines(line: AdventureLineDbo) - @Delete(entity = AdventureDbo::class) - fun deleteBook(id: BookPartialId) - - @Delete(entity = AdventureDbo::class) - fun deleteBook(id: StoryPartialId) + @Delete(entity = AdventureBookDbo::class) + fun deleteBook(id: AdventureBookDbo.BookId) @Delete(entity = AdventureStoryDbo::class) - fun deleteStory(id: BookPartialId) + fun deleteStory(id: AdventureBookDbo.BookId) @Delete(entity = AdventureStoryDbo::class) - fun deleteStory(id: StoryPartialId) + fun deleteStory(id: AdventureStoryDbo.StoryId) - data class BookPartialId( - val bookTitle: String, - ) - - data class StoryPartialId( - val bookTitle: String, - val adventureTitle: String, - ) + @Delete(entity = AdventureLineDbo::class) + fun deleteLines(id: AdventureLineDbo.LineId) @Transaction fun update( - booksToRemove: List, - storiesToRemove: List, - adventure: List>> + books: List, + booksToRemove: List, + stories: List, + storiesToRemove: List, + lines: List, + linesToRemove: List, ) { // First clean the database from old unused data. - // story lines are remove with cascading foreign key. - booksToRemove.forEach { deleteBook(id = it) } - storiesToRemove.forEach { deleteBook(id = it) } - // then update the data. - adventure.forEach { (adventure, lines) -> - // then remove the story lines before inserting them again (easier than to keep track of them all) - deleteStory( - id = StoryPartialId( - bookTitle = adventure.bookTitle, - adventureTitle = adventure.adventureTitle, - ) - ) - // insert the adventure - insertBook(item = adventure) - // insert its lines. - lines.forEach { insertStory(item = it) } + booksToRemove.forEach { + deleteStory(id = it) + deleteBook(id = it) + } + // StoryLineDbo are remove with cascading foreign key from StoryBdo. + storiesToRemove.forEach { deleteStory(id = it) } + linesToRemove.forEach { deleteLines(id = it) } + + books.forEach { book -> + insertBook(book = book) + } + stories.forEach { story -> + insertStory(story = story) + } + lines.forEach { line -> + insertLines(line = line) } } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/data/database/adventure/AdventureDbo.kt b/app/src/main/java/com/pixelized/rplexicon/data/database/adventure/AdventureDbo.kt index 11176a8..157db52 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/database/adventure/AdventureDbo.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/database/adventure/AdventureDbo.kt @@ -1,39 +1,133 @@ package com.pixelized.rplexicon.data.database.adventure +import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey +import androidx.room.Ignore @Entity( - tableName = "adventures", - primaryKeys = ["bookTitle", "adventureTitle"], + tableName = AdventureBookDbo.TABLE, + primaryKeys = [AdventureBookDbo.DOCUMENT_ID], ) -data class AdventureDbo( - val bookTitle: String, - val adventureTitle: String, - val adventureCategory: String?, - val bookIcon: String?, - val adventureBackground: String?, - val index: Int, - val revision: Long, +data class AdventureBookDbo( + @ColumnInfo(name = DOCUMENT_ID) val documentId: String, -) + @ColumnInfo(name = TITLE) + val title: String, + @ColumnInfo(name = ICON) + val icon: String?, +) { + @Ignore + val id = BookId(documentId = documentId) + + data class BookId( + @ColumnInfo(name = DOCUMENT_ID) + val documentId: String, + ) + + companion object { + const val TABLE = "AdventureBooks" + const val DOCUMENT_ID = "documentId" + const val TITLE = "title" + const val ICON = "icon" + } +} @Entity( - tableName = "adventuresStories", - primaryKeys = ["bookTitle", "adventureTitle", "index"], + tableName = AdventureStoryDbo.TABLE, + primaryKeys = [ + AdventureStoryDbo.TITLE, + AdventureStoryDbo.FK_DOCUMENT_ID, + ], foreignKeys = [ ForeignKey( - entity = AdventureDbo::class, - parentColumns = ["bookTitle", "adventureTitle"], - childColumns = ["bookTitle", "adventureTitle"], - onDelete = ForeignKey.CASCADE, - ), + entity = AdventureBookDbo::class, + parentColumns = [AdventureBookDbo.DOCUMENT_ID], + childColumns = [AdventureStoryDbo.FK_DOCUMENT_ID], + onDelete = ForeignKey.NO_ACTION, + ) ], ) data class AdventureStoryDbo( - val bookTitle: String, - val adventureTitle: String, + @ColumnInfo(name = TITLE) + val title: String, + @ColumnInfo(name = CATEGORY) + val category: String?, + @ColumnInfo(name = BACKGROUND) + val background: String?, + @ColumnInfo(name = REVISION) + val revision: Long, + @ColumnInfo(name = INDEX) val index: Int, + @ColumnInfo(name = FK_DOCUMENT_ID) + val documentId: String, +) { + @Ignore + val id = StoryId(documentId = documentId, title = title) + + data class StoryId( + @ColumnInfo(name = FK_DOCUMENT_ID) + val documentId: String, + @ColumnInfo(name = TITLE) + val title: String, + ) + + companion object { + const val TABLE = "AdventureStory" + const val TITLE = "title" + const val CATEGORY = "category" + const val BACKGROUND = "background" + const val REVISION = "revision" + const val INDEX = "index" + const val FK_DOCUMENT_ID = AdventureBookDbo.DOCUMENT_ID + } +} + + +@Entity( + tableName = AdventureLineDbo.TABLE, + primaryKeys = [ + AdventureLineDbo.INDEX, + AdventureLineDbo.FK_STORY_TITLE, + AdventureLineDbo.FK_DOCUMENT_ID, + ], + foreignKeys = [ + ForeignKey( + entity = AdventureStoryDbo::class, + parentColumns = [AdventureStoryDbo.TITLE, AdventureStoryDbo.FK_DOCUMENT_ID], + childColumns = [AdventureLineDbo.FK_STORY_TITLE, AdventureLineDbo.FK_DOCUMENT_ID], + onDelete = ForeignKey.CASCADE, + ) + ], +) +data class AdventureLineDbo( + @ColumnInfo(name = INDEX) + val index: Int, + @ColumnInfo(name = TEXT) val text: String, + @ColumnInfo(name = FORMAT) val format: String, -) \ No newline at end of file + @ColumnInfo(name = FK_DOCUMENT_ID) + val documentId: String, + @ColumnInfo(name = FK_STORY_TITLE) + val story: String, +) { + @Ignore + val id = LineId(documentId = documentId, story = story) + + data class LineId( + @ColumnInfo(name = FK_DOCUMENT_ID) + val documentId: String, + @ColumnInfo(name = FK_STORY_TITLE) + val story: String, + ) + + companion object { + const val TABLE = "AdventureLine" + const val INDEX = "index" + const val TEXT = "text" + const val FORMAT = "format" + const val FK_DOCUMENT_ID = AdventureBookDbo.DOCUMENT_ID + const val FK_STORY_TITLE = AdventureStoryDbo.TITLE + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/data/model/adventure/Adventure.kt b/app/src/main/java/com/pixelized/rplexicon/data/model/adventure/Adventure.kt index 0f15c2a..80c1c59 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/model/adventure/Adventure.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/model/adventure/Adventure.kt @@ -5,9 +5,9 @@ import android.net.Uri data class Adventure( val bookTitle: String, val bookIcon: Uri?, - val adventureCategory: String?, - val adventureTitle: String, - val adventureBackground: Uri?, + val storyCategory: String?, + val storyTitle: String, + val storyBackground: Uri?, val documentId: String, val story: List, ) \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/data/repository/adventure/AdventureDboFactory.kt b/app/src/main/java/com/pixelized/rplexicon/data/repository/adventure/AdventureDboFactory.kt index 30bff7c..00eb069 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/repository/adventure/AdventureDboFactory.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/repository/adventure/AdventureDboFactory.kt @@ -1,6 +1,7 @@ package com.pixelized.rplexicon.data.repository.adventure -import com.pixelized.rplexicon.data.database.adventure.AdventureDbo +import com.pixelized.rplexicon.data.database.adventure.AdventureBookDbo +import com.pixelized.rplexicon.data.database.adventure.AdventureLineDbo import com.pixelized.rplexicon.data.database.adventure.AdventureStoryDbo import com.pixelized.rplexicon.data.model.adventure.Adventure import com.pixelized.rplexicon.data.model.adventure.AdventureBook @@ -12,65 +13,71 @@ import javax.inject.Inject class AdventureDboFactory @Inject constructor() { fun convertFromDbo( - adventures: List, + books: List, stories: List, + lines: List, ): List { - return adventures - .sortedBy { it.index } - .map { adventure -> - Adventure( - bookTitle = adventure.bookTitle, - bookIcon = adventure.bookIcon.toUriOrNull(), - adventureTitle = adventure.adventureTitle, - adventureCategory = adventure.adventureCategory, - adventureBackground = adventure.adventureBackground.toUriOrNull(), - documentId = adventure.documentId, - story = stories - .filter { - it.bookTitle == adventure.bookTitle && it.adventureTitle == adventure.adventureTitle - } - .sortedBy { - it.index - } - .map { story -> - AdventureLine( - text = story.text, - format = try { - AdventureLine.Format.valueOf(story.format) - } catch (_: Exception) { - AdventureLine.Format.PARAGRAPH - }, - ) - }, - ) - } + return books.flatMap { book: AdventureBookDbo -> + stories + .filter { story -> story.documentId == book.documentId } + .sortedBy { story -> story.index } + .map { story: AdventureStoryDbo -> + Adventure( + bookTitle = book.title, + bookIcon = book.icon.toUriOrNull(), + documentId = book.documentId, + storyTitle = story.title, + storyCategory = story.category, + storyBackground = story.background.toUriOrNull(), + story = lines + .filter { it.documentId == story.documentId && it.story == story.title } + .sortedBy { it.index } + .map { line -> + AdventureLine( + text = line.text, + format = try { + AdventureLine.Format.valueOf(line.format) + } catch (_: Exception) { + AdventureLine.Format.PARAGRAPH + }, + ) + }, + ) + } + } } - fun convertToAdventureDbo( + fun convertToDbo( book: AdventureBook, - story: AdventureStory, - index: Int, - ) = AdventureDbo( - bookTitle = book.title, - adventureTitle = story.title, - adventureCategory = story.category, - bookIcon = book.icon.toString(), - adventureBackground = story.background.toString(), - index = index, - revision = story.revision, + ) = AdventureBookDbo( documentId = book.documentId, + title = book.title, + icon = book.icon.toString(), ) - fun convertToStoryDbo( - book: AdventureBook, + fun convertToDbo( story: AdventureStory, index: Int, - line: AdventureLine + documentId: String, ) = AdventureStoryDbo( - bookTitle = book.title, - adventureTitle = story.title, + title = story.title, + category = story.category, + background = story.background.toString(), + revision = story.revision, index = index, - text = line.text, - format = line.format.name, + documentId = documentId, + ) + + fun convertToDbo( + adventureLine: AdventureLine, + index: Int, + documentId: String, + storyTitle: String, + ) = AdventureLineDbo( + index = index, + text = adventureLine.text, + format = adventureLine.format.name, + documentId = documentId, + story = storyTitle, ) } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/data/repository/adventure/AdventureRepository.kt b/app/src/main/java/com/pixelized/rplexicon/data/repository/adventure/AdventureRepository.kt index 84230f9..67f4126 100644 --- a/app/src/main/java/com/pixelized/rplexicon/data/repository/adventure/AdventureRepository.kt +++ b/app/src/main/java/com/pixelized/rplexicon/data/repository/adventure/AdventureRepository.kt @@ -1,8 +1,8 @@ package com.pixelized.rplexicon.data.repository.adventure import com.pixelized.rplexicon.data.database.CompanionDatabase -import com.pixelized.rplexicon.data.database.adventure.AdventureDao -import com.pixelized.rplexicon.data.database.adventure.AdventureDbo +import com.pixelized.rplexicon.data.database.adventure.AdventureBookDbo +import com.pixelized.rplexicon.data.database.adventure.AdventureLineDbo import com.pixelized.rplexicon.data.database.adventure.AdventureStoryDbo import com.pixelized.rplexicon.data.model.adventure.Adventure import com.pixelized.rplexicon.data.model.adventure.AdventureBook @@ -32,123 +32,151 @@ class AdventureRepository @Inject constructor( private val adventureStoryLineParser: AdventureStoryLineParser, private val adventureDboFactory: AdventureDboFactory, ) { - val adventure = database.adventureDao().getAllAdventureFlow() - .combine( - flow = database.adventureDao().getAllAdventureStoryFlow(), - transform = adventureDboFactory::convertFromDbo, - ).stateIn( + val adventure = database.adventureDao().let { database -> + combine( + database.getBooksFlow(), + database.getStoriesFlow(), + database.getStoryLinesFlow(), + ) { books, stories, lines -> + adventureDboFactory.convertFromDbo( + books = books, + stories = stories, + lines = lines, + ) + }.stateIn( scope = CoroutineScope(Dispatchers.Default + Job()), started = SharingStarted.Lazily, initialValue = emptyList(), ) + } @Throws(IncompatibleSheetStructure::class, Exception::class) - suspend fun fetchAdventures() { + suspend fun fetchBooks() { val database = database.adventureDao() - val bookStore = database.findAdventures() - val bookToRemove: MutableMap = bookStore.associate { - it.bookTitle to true - }.toMutableMap() + val booksToRemove = database.findBooks() + .map { it.id } + .toMutableList() - val storyToRemove: MutableMap, Boolean> = bookStore.associate { - (it.bookTitle to it.adventureTitle) to true - }.toMutableMap() + val books: List = fetchAdventureBooks().map { book -> + // convert to BookDbo. + adventureDboFactory.convertToDbo(book = book).also { + // Flag this book to not delete it + booksToRemove.remove(element = it.id) + } + } - val adventures = fetchAdventureBooks().flatMap { book -> - // tag this book to keep it in the database - bookToRemove[book.title] = false + val storiesToRemove = books + .flatMap { book -> database.findStories(documentId = book.documentId) } + .map { it.id } + .toMutableList() - fetchAdventuresContent( - database = database, - storyToRemove = storyToRemove, - book = book, - ) + val stories: List = books.flatMap { book -> + fetchAdventureStory(documentId = book.documentId) + .mapIndexed { index, story -> + // convert to StoryBdo + val update = adventureDboFactory.convertToDbo( + story = story, + index = index, + documentId = book.documentId, + ).also { + // Flag this story to not delete it + storiesToRemove.remove(element = it.id) + } + val cache = database.findStory( + documentId = book.documentId, + title = story.title, + ) + when { + cache == null -> update + cache.revision < update.revision -> update + else -> null + } + } + .mapNotNull { it } + } + + val lines: List = stories.flatMap { story -> + fetchAdventureLine( + documentId = story.documentId, + storyTitle = story.title, + ).mapIndexed { index, line -> + adventureDboFactory.convertToDbo( + adventureLine = line, + index = index, + documentId = story.documentId, + storyTitle = story.title, + ) + } } database.update( - booksToRemove = bookToRemove - .filter { it.value } - .map { AdventureDao.BookPartialId(it.key) }, - storiesToRemove = storyToRemove - .filter { it.value } - .map { AdventureDao.StoryPartialId(it.key.first, it.key.second) }, - adventure = adventures, + booksToRemove = booksToRemove, + storiesToRemove = storiesToRemove, + linesToRemove = lines.groupBy { it.id }.keys.toList(), + books = books, + stories = stories, + lines = lines, ) } @Throws(IncompatibleSheetStructure::class, Exception::class) - suspend fun fetchAdventures( - bookTitle: String, + suspend fun fetchStories( documentId: String, ) { val database = database.adventureDao() - val bookStore = database.findAdventures(bookTitle = bookTitle) - val storyToRemove: MutableMap, Boolean> = bookStore.associate { - (it.bookTitle to it.adventureTitle) to true - }.toMutableMap() + val storiesToRemove = database.findStories(documentId = documentId) + .map { it.id } + .toMutableList() - val adventures = fetchAdventuresContent( - database = database, - storyToRemove = storyToRemove, - book = AdventureBook( - title = bookTitle, - documentId = documentId, - icon = null, - ), - ) + val stories: List = fetchAdventureStory(documentId = documentId) + .mapIndexed { index, story -> + // convert to StoryBdo + val update = adventureDboFactory.convertToDbo( + story = story, + index = index, + documentId = documentId, + ).also { + // Flag this story to not delete it + storiesToRemove.remove(element = it.id) + } + val cache = database.findStory( + documentId = documentId, + title = story.title, + ) + when { + cache == null -> update + cache.revision < update.revision -> update + else -> null + } + } + .mapNotNull { it } + + val lines: List = stories.flatMap { story -> + fetchAdventureLine( + documentId = story.documentId, + storyTitle = story.title, + ).mapIndexed { index, line -> + adventureDboFactory.convertToDbo( + adventureLine = line, + index = index, + documentId = story.documentId, + storyTitle = story.title, + ) + } + } database.update( booksToRemove = emptyList(), - storiesToRemove = storyToRemove - .filter { it.value } - .map { AdventureDao.StoryPartialId(it.key.first, it.key.second) }, - adventure = adventures, + storiesToRemove = storiesToRemove, + linesToRemove = lines.groupBy { it.id }.keys.toList(), + books = emptyList(), + stories = stories, + lines = lines, ) } - private suspend fun fetchAdventuresContent( - database: AdventureDao, - storyToRemove: MutableMap, Boolean>, - book: AdventureBook - ): List>> { - val stories = fetchAdventureStory( - documentId = book.documentId, - ) - return stories.mapNotNull { story -> - // tag this story to keep it in the database - storyToRemove[book.title to story.title] = false - - val adventure = adventureDboFactory.convertToAdventureDbo( - book = book, - story = story, - index = stories.indexOf(story), - ) - val cache = database.findAdventure( - bookTitle = book.title, - adventureTitle = story.title, - ) - - if (cache == null || cache.revision < adventure.revision) { - val lines = fetchAdventureLine( - documentId = book.documentId, - adventureTitle = story.title - ).mapIndexed { index, line -> - adventureDboFactory.convertToStoryDbo( - book = book, - story = story, - index = index, - line = line - ) - } - adventure to lines - } else { - null - } - } - } - fun find(bookTitle: String): List { return adventure.value.filter { adventure -> adventure.bookTitle == bookTitle @@ -157,7 +185,7 @@ class AdventureRepository @Inject constructor( fun find(bookTitle: String, adventureTitle: String): Adventure? { return adventure.value.firstOrNull { adventure -> - adventure.bookTitle == bookTitle && adventure.adventureTitle == adventureTitle + adventure.bookTitle == bookTitle && adventure.storyTitle == adventureTitle } } @@ -179,11 +207,11 @@ class AdventureRepository @Inject constructor( private suspend fun fetchAdventureLine( documentId: String, - adventureTitle: String, + storyTitle: String, ): List { return try { googleRepository.fetch { sheet -> - val request = sheet.get(documentId, adventureTitle) + val request = sheet.get(documentId, storyTitle) adventureStoryLineParser.parse(sheet = request.execute()) } } catch (exception: Exception) { diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/navigation/screens/ComposableAdventureChapter.kt b/app/src/main/java/com/pixelized/rplexicon/ui/navigation/screens/ComposableAdventureStory.kt similarity index 97% rename from app/src/main/java/com/pixelized/rplexicon/ui/navigation/screens/ComposableAdventureChapter.kt rename to app/src/main/java/com/pixelized/rplexicon/ui/navigation/screens/ComposableAdventureStory.kt index 85132a1..d036524 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/navigation/screens/ComposableAdventureChapter.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/navigation/screens/ComposableAdventureStory.kt @@ -10,7 +10,7 @@ import androidx.navigation.NavType import androidx.navigation.navArgument import com.pixelized.rplexicon.ui.navigation.NavigationAnimation import com.pixelized.rplexicon.ui.navigation.animatedComposable -import com.pixelized.rplexicon.ui.screens.adventure.chapter.AdventureChaptersScreen +import com.pixelized.rplexicon.ui.screens.adventure.chapter.AdventureStoriesScreen import com.pixelized.rplexicon.utilitary.extentions.string.ARG private const val ROUTE = "adventureDetail" @@ -49,7 +49,7 @@ fun NavGraphBuilder.composableAdventureChapters() { }, ) ) { - AdventureChaptersScreen() + AdventureStoriesScreen() } } diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/book/AdventureBooksViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/book/AdventureBooksViewModel.kt index b7df7c6..e719248 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/book/AdventureBooksViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/book/AdventureBooksViewModel.kt @@ -52,11 +52,7 @@ class AdventureBooksViewModel @Inject constructor( init { viewModelScope.launch(Dispatchers.Default) { - try { - adventureRepository.fetchAdventures() - } catch (exception: Exception) { - _error.emit(value = Structure(ADVENTURE)) - } + update() } } @@ -66,7 +62,7 @@ class AdventureBooksViewModel @Inject constructor( _isLoading.value = true } withContext(Dispatchers.Default) { - adventureRepository.fetchAdventures() + adventureRepository.fetchBooks() } } catch (_: Exception) { _error.emit(value = Structure(ADVENTURE)) diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureChapter.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureChapter.kt index e66ef2a..33a357e 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureChapter.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureChapter.kt @@ -6,7 +6,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding -import androidx.compose.material.minimumInteractiveComponentSize +import androidx.compose.material3.minimumInteractiveComponentSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -23,25 +23,25 @@ import com.pixelized.rplexicon.utilitary.annotateWithDropCap import com.pixelized.rplexicon.utilitary.extentions.lexicon @Stable -sealed class AdventureChapterUio { +sealed class AdventureStoriesUio { @Stable data class AdventureCategory( val title: String, - ) : AdventureChapterUio() + ) : AdventureStoriesUio() @Stable data class AdventureItem( val bookTitle: String, val adventureTitle: String, - ) : AdventureChapterUio() + ) : AdventureStoriesUio() } @Composable fun AdventureChapterCategory( modifier: Modifier = Modifier, paddingValues: PaddingValues = PaddingValues(horizontal = 16.dp), - category: AdventureChapterUio.AdventureCategory, + category: AdventureStoriesUio.AdventureCategory, ) { Text( modifier = modifier.padding(paddingValues = paddingValues), @@ -57,7 +57,7 @@ fun AdventureChapterCategory( fun AdventureChapterItem( modifier: Modifier = Modifier, paddingValues: PaddingValues = PaddingValues(horizontal = 16.dp), - item: AdventureChapterUio.AdventureItem, + item: AdventureStoriesUio.AdventureItem, onClick: () -> Unit, ) { Row( @@ -94,7 +94,7 @@ private fun AdventureChapterCategoryPreview() { LexiconTheme { Surface { AdventureChapterCategory( - category = AdventureChapterUio.AdventureCategory( + category = AdventureStoriesUio.AdventureCategory( title = "Les chroniques d'une orc", ), ) @@ -109,7 +109,7 @@ private fun AdventureChapterItemPreview() { LexiconTheme { Surface { AdventureChapterItem( - item = AdventureChapterUio.AdventureItem( + item = AdventureStoriesUio.AdventureItem( bookTitle = "Les chroniques d'une orc", adventureTitle = "La traque", ), diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureChaptersScreen.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureStoriesScreen.kt similarity index 90% rename from app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureChaptersScreen.kt rename to app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureStoriesScreen.kt index c818a6e..ebfca94 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureChaptersScreen.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureStoriesScreen.kt @@ -46,8 +46,8 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalMaterialApi::class) @Composable -fun AdventureChaptersScreen( - viewModel: AdventureChaptersViewModel = hiltViewModel(), +fun AdventureStoriesScreen( + viewModel: AdventureStoriesViewModel = hiltViewModel(), ) { val screen = LocalScreenNavHost.current val scope = rememberCoroutineScope() @@ -89,8 +89,8 @@ private fun AdventureChapterContent( refreshState: PullRefreshState, refreshing: State, bookTitle: State, - chapters: State>, - onChapter: (AdventureChapterUio.AdventureItem) -> Unit, + chapters: State>, + onChapter: (AdventureStoriesUio.AdventureItem) -> Unit, onBack: () -> Unit, ) { Scaffold( @@ -125,7 +125,7 @@ private fun AdventureChapterContent( ) { itemsIndexed(items = chapters.value) { index, item -> when (item) { - is AdventureChapterUio.AdventureCategory -> { + is AdventureStoriesUio.AdventureCategory -> { AdventureChapterCategory( modifier = Modifier .padding(top = if (index != 0) 32.dp else 0.dp) @@ -134,7 +134,7 @@ private fun AdventureChapterContent( ) } - is AdventureChapterUio.AdventureItem -> { + is AdventureStoriesUio.AdventureItem -> { AdventureChapterItem( modifier = Modifier.fillMaxWidth(), item = item, @@ -175,38 +175,38 @@ private fun AdventureChapterPreview() { chapters = remember { mutableStateOf( listOf( - AdventureChapterUio.AdventureItem( + AdventureStoriesUio.AdventureItem( bookTitle = "Les chroniques d'une orc", adventureTitle = "Biographie", ), - AdventureChapterUio.AdventureCategory( + AdventureStoriesUio.AdventureCategory( title = "Mémoire d'une orc", ), - AdventureChapterUio.AdventureItem( + AdventureStoriesUio.AdventureItem( bookTitle = "Les chroniques d'une orc", adventureTitle = "La traque", ), - AdventureChapterUio.AdventureItem( + AdventureStoriesUio.AdventureItem( bookTitle = "Les chroniques d'une orc", adventureTitle = "Les six mercenaires", ), - AdventureChapterUio.AdventureItem( + AdventureStoriesUio.AdventureItem( bookTitle = "Les chroniques d'une orc", adventureTitle = "La couronne de cuivre", ), - AdventureChapterUio.AdventureItem( + AdventureStoriesUio.AdventureItem( bookTitle = "Les chroniques d'une orc", adventureTitle = "Le seigneur tout puissant", ), - AdventureChapterUio.AdventureItem( + AdventureStoriesUio.AdventureItem( bookTitle = "Les chroniques d'une orc", adventureTitle = "Vague à l'Amn", ), - AdventureChapterUio.AdventureItem( + AdventureStoriesUio.AdventureItem( bookTitle = "Tu parles d'une galère", adventureTitle = "Tu parles d'une galère", ), - AdventureChapterUio.AdventureItem( + AdventureStoriesUio.AdventureItem( bookTitle = "Les chroniques d'une orc", adventureTitle = "Liberté", ), diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureChaptersViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureStoriesViewModel.kt similarity index 85% rename from app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureChaptersViewModel.kt rename to app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureStoriesViewModel.kt index 4aec1b0..322ef12 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureChaptersViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/chapter/AdventureStoriesViewModel.kt @@ -21,7 +21,7 @@ import kotlinx.coroutines.withContext import javax.inject.Inject @HiltViewModel -class AdventureChaptersViewModel @Inject constructor( +class AdventureStoriesViewModel @Inject constructor( private val adventureRepository: AdventureRepository, savedStateHandle: SavedStateHandle, ) : ViewModel() { @@ -40,22 +40,22 @@ class AdventureChaptersViewModel @Inject constructor( .map { adventures -> adventures .filter { it.bookTitle == argument.bookTitle && it.documentId == argument.documentId } - .groupBy { it.adventureCategory ?: "" } + .groupBy { it.storyCategory ?: "" } .flatMap { entry -> val header = listOf( - AdventureChapterUio.AdventureCategory(title = entry.key) + AdventureStoriesUio.AdventureCategory(title = entry.key) ) val stories = entry.value.map { - AdventureChapterUio.AdventureItem( + AdventureStoriesUio.AdventureItem( bookTitle = it.bookTitle, - adventureTitle = it.adventureTitle, + adventureTitle = it.storyTitle, ) } header + stories } } - val chapters: State> + val chapters: State> @Composable @Stable get() = _chapters.collectAsState(initial = emptyList()) @@ -66,8 +66,7 @@ class AdventureChaptersViewModel @Inject constructor( _isLoading.value = true } withContext(Dispatchers.Default) { - adventureRepository.fetchAdventures( - bookTitle = argument.bookTitle, + adventureRepository.fetchStories( documentId = argument.documentId, ) } diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/detail/AdventureDetailScreen.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/detail/AdventureDetailScreen.kt index 1a833dd..950d935 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/detail/AdventureDetailScreen.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/detail/AdventureDetailScreen.kt @@ -52,13 +52,12 @@ import androidx.core.net.toUri import androidx.hilt.navigation.compose.hiltViewModel import com.pixelized.rplexicon.R import com.pixelized.rplexicon.ui.composable.images.BackgroundImage -import com.pixelized.rplexicon.ui.composable.images.rememberSaturationFilter import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost -import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.CHAPTER -import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.LEGEND -import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.PARAGRAPH -import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.SUB_TITLE -import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.TITLE +import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureLineUio.Style.CHAPTER +import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureLineUio.Style.LEGEND +import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureLineUio.Style.PARAGRAPH +import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureLineUio.Style.SUB_TITLE +import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureLineUio.Style.TITLE import com.pixelized.rplexicon.ui.theme.LexiconTheme @Composable @@ -101,7 +100,7 @@ private fun AdventureDetailContent( lazyListState: LazyListState = rememberLazyListState(), adventureTitle: State, background: State, - adventures: State>, + adventures: State>, onBack: () -> Unit, ) { val nestedScrollOffset = rememberSaveable { mutableFloatStateOf(0f) } @@ -260,55 +259,55 @@ private fun AdventureDetailPreview() { adventures = remember { mutableStateOf( listOf( - AdventureUio( + AdventureLineUio( text = "Péripétie", style = SUB_TITLE, ), - AdventureUio( + AdventureLineUio( text = "La traque", style = TITLE, ), - AdventureUio( + AdventureLineUio( text = "Fake Chapter", style = CHAPTER, ), - AdventureUio( + AdventureLineUio( text = "Il était temps pour moi de partir à la chasse. Il ne restait déjà plus grand chose du loup d’il y a quelques jours. J'éteignis les dernières braises de mon feu, récupérai dagues, javelot et mon outre d’eau puis dissimulai le reste de mes biens au fond de la caverne me servant d'abri…", style = PARAGRAPH, ), - AdventureUio( + AdventureLineUio( text = "Je me dirigeai vers un point d’eau où j'avais par le passé déjà aperçu des cervidés. Une fois sur place, je me mis à l’abri des regards dans un fourré et dissimulai mon odeur avec de la boue. L’attente fut longue, mais la chasse exige patience et discipline. Puis, sur l’autre rive, je repérai une harde de cerfs et l'excitation monta en moi.", style = PARAGRAPH, ), - AdventureUio( + AdventureLineUio( text = "Je pris soin de bien choisir ma cible. Tuer un mâle serait gâcher. Seule, je serais incapable de finir sa viande avant qu’elle ne pourrisse. Je choisis donc une femelle au ventre plat indiquant qu’elle n’avait pas mis bas récemment. Dès l’instant où ses lèvres touchèrent la surface de la marre, je lançai mon javelot qui frôla la biche pour se planter dans le sol derrière elle. Je bondis de ma cachette pour me précipiter sur ma proie prise de stupeur. Elle ne resta cependant pas figée longtemps. Son instinct de survie prenant le dessus, elle se cabra et s'échappa précédée par ses compagnons.", style = PARAGRAPH, ), - AdventureUio( + AdventureLineUio( text = "Des taches de sang étaient clairement visibles là où elle se trouvait quelques secondes auparavant. Elle était blessée. Mais pas suffisamment pour être immobilisée. La traque commença. L’échec ou la réussite ne serait plus déterminé que par nos endurances respectives. Tous mes sens en éveil je me lançai sur sa piste. Les branches brisées par une course effrénée d’animaux apeurés, des traces de sang expulsées par la contraction de muscles bandés, des empreintes laissées dans le sol meuble d’une tourbière ; tous ces indices que seul un œil expérimenté pouvait déceler me permirent de retrouver la harde.", style = PARAGRAPH, ), - AdventureUio( + AdventureLineUio( text = "Les effluves de mon corps suant sous l’effort me firent repérer, me privant de l'opportunité de tirer à nouveau. Je pus cependant apercevoir ma proie parmi ses congénères. La pauvre bête boitait, l’arrière cuisse bien entamée par mon arme. Ce marathon arriverait tôt ou tard à son terme et j’étais désormais certaine que cette biche assoiffée courait à sa perte. Je continuai donc la traque sans relâche, ce manège se répétant plusieurs fois. Les cerfs plaçant toujours la future victime au centre de la harde, ils m’empêchait de l’atteindre.", style = PARAGRAPH, ), - AdventureUio( + AdventureLineUio( text = "Mais même l’organisme le plus endurci ne pouvait continuer indéfiniment cette fuite désespérée tout en se vidant de son sang. Je trouvai bientôt ma proie couchée sur le flanc, exténuée, la respiration saccadée, un pas de plus lui aurait coûté la vie. Ses compagnons l’avaient abandonnée lorsqu’elle s'était effondrée, comprenant qu’il n’y avait plus rien à faire pour elle. La fin de sa course signifiait aussi la fin de mon plaisir. La mise à mort d’une créature paniquée n’étant pas quelque chose que j’apprécie, je la tuai rapidement d’un coup de dague sûr en plein cœur, m’étant ainsi un terme à ses souffrances.", style = PARAGRAPH, ), - AdventureUio( + AdventureLineUio( text = "Bien qu’exténuée par cette épreuve j’étais heureuse. Sa force ferait bientôt partie de moi. Après la mort de l’animal, je pris soin de l'égorger avant de la prendre sur mes épaules. Je sentis son sang encore chaud ruisseler le long de mon dos. Il quittait son corps lentement, comme si la vie rechignait à abandonner cette chair déjà saisie par la rigidité. C’est ainsi chargée du fruit de ma chasse que sur le chemin du retour je sentis l’odeur de la chair calcinée.", style = PARAGRAPH, ), - AdventureUio( + AdventureLineUio( text = "Je me dirigeai lentement vers cette puanteur, prenant garde de ne faire aucun bruit, jusqu'à ce que j’entendis les bribes d’une conversation. Consciente de mes limites et de crainte d’être repérée, je n’approchai pas suffisamment pour les voir. Ils s'exprimaient en commun. L’un d’eux avait un fort accent nain. Un autre était indubitablement d’ascendance elfique et le dernier était humain. Ils semblaient être en désaccord quant à la direction à prendre. Ne souhaitant pas avoir maille à partir après une journée de course, je pris la décision de les contourner. Les cadavres encore fumants d’une troupe orc confirmèrent que j’avais fait le bon choix. Ces vagabonds n'étaient pas de ceux avec qui il fallait jouer.", style = PARAGRAPH, ), - AdventureUio( + AdventureLineUio( text = "Une fois de retour à mon refuge, je pris le temps de préparer l’animal, coupant les morceaux de viande pour faciliter leur cuisson et leur transport. J’avais fait mon choix. Je ne resterais pas encore longtemps dans cette région où la guerre se frayait déjà un chemin. Je m'enfoncerais plus loin dans les terres, m’éloignant toujours un peu plus de la civilisation.", style = PARAGRAPH, ), - AdventureUio( + AdventureLineUio( text = "Les chroniques de Brulkhaï - 16 ans", style = LEGEND, ), diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/detail/AdventureDetailViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/detail/AdventureDetailViewModel.kt index c1d26b0..f7426ed 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/detail/AdventureDetailViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/detail/AdventureDetailViewModel.kt @@ -7,7 +7,7 @@ import androidx.lifecycle.ViewModel import com.pixelized.rplexicon.data.model.adventure.AdventureLine.Format import com.pixelized.rplexicon.data.repository.adventure.AdventureRepository import com.pixelized.rplexicon.ui.navigation.screens.adventureDetailArgument -import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style +import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureLineUio.Style import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @@ -25,11 +25,11 @@ class AdventureDetailViewModel @Inject constructor( ) val background = derivedStateOf { - _detail.value?.adventureBackground + _detail.value?.storyBackground } val adventure = derivedStateOf { _detail.value?.story?.map { line -> - AdventureUio( + AdventureLineUio( text = line.text, style = when (line.format) { Format.TITLE -> Style.TITLE diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/detail/AdventureLine.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/detail/AdventureLine.kt index e9ac42e..9991e05 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/detail/AdventureLine.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/adventure/detail/AdventureLine.kt @@ -10,19 +10,19 @@ import androidx.compose.runtime.Stable import androidx.compose.ui.Modifier import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.dp -import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.ANNEX -import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.CHAPTER -import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.DIALOGUE -import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.LEGEND -import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.PARAGRAPH -import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.SUB_TITLE -import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureUio.Style.TITLE +import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureLineUio.Style.ANNEX +import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureLineUio.Style.CHAPTER +import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureLineUio.Style.DIALOGUE +import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureLineUio.Style.LEGEND +import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureLineUio.Style.PARAGRAPH +import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureLineUio.Style.SUB_TITLE +import com.pixelized.rplexicon.ui.screens.adventure.detail.AdventureLineUio.Style.TITLE import com.pixelized.rplexicon.utilitary.annotateMajWithDropCap import com.pixelized.rplexicon.utilitary.annotateWithDropCap import com.pixelized.rplexicon.utilitary.extentions.lexicon @Stable -data class AdventureUio( +data class AdventureLineUio( val text: String, val style: Style, ) { @@ -41,7 +41,7 @@ data class AdventureUio( fun AdventureLine( modifier: Modifier = Modifier, paddingValues: PaddingValues, - item: AdventureUio, + item: AdventureLineUio, ) { Text( modifier = Modifier @@ -80,8 +80,8 @@ fun AdventureLine( @Stable fun rememberPaddingValues( - current: AdventureUio, - previous: AdventureUio?, + current: AdventureLineUio, + previous: AdventureLineUio?, ): PaddingValues { return PaddingValues( top = when (current.style) {