diff --git a/domain/app/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/database/dao/MonsterLoreDaoImpl.kt b/domain/app/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/database/dao/MonsterLoreDaoImpl.kt index 7757d3ed4..d51b2e9ae 100644 --- a/domain/app/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/database/dao/MonsterLoreDaoImpl.kt +++ b/domain/app/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/database/dao/MonsterLoreDaoImpl.kt @@ -19,18 +19,17 @@ package br.alexandregpereira.hunter.data.database.dao import br.alexandregpereira.hunter.data.monster.lore.local.dao.MonsterLoreDao import br.alexandregpereira.hunter.data.monster.lore.local.entity.MonsterLoreCompleteEntity import br.alexandregpereira.hunter.data.monster.lore.local.entity.MonsterLoreEntity +import br.alexandregpereira.hunter.data.monster.lore.local.entity.MonsterLoreEntityStatus import br.alexandregpereira.hunter.data.monster.lore.local.entity.MonsterLoreEntryEntity import br.alexandregpereira.hunter.database.MonsterLoreCompleteEntityView import br.alexandregpereira.hunter.database.MonsterLoreEntryQueries import br.alexandregpereira.hunter.database.MonsterLoreQueries -import br.alexandregpereira.hunter.database.MonsterQueries import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext import br.alexandregpereira.hunter.database.MonsterLoreEntity as MonsterLoreDatabaseEntity import br.alexandregpereira.hunter.database.MonsterLoreEntryEntity as MonsterLoreEntryDatabaseEntity internal class MonsterLoreDaoImpl( - private val monsterQueries: MonsterQueries, private val queries: MonsterLoreQueries, private val monsterLoreEntryQueries: MonsterLoreEntryQueries, private val dispatcher: CoroutineDispatcher @@ -48,14 +47,20 @@ internal class MonsterLoreDaoImpl( queries.getMonsterLore(monsterIndex).executeAsList().asEntities().first() } + override suspend fun getMonstersLoreEdited(): List { + return withContext(dispatcher) { + queries.getMonstersLoreEdited().executeAsList().asEntities() + } + } + override suspend fun insert( monstersLore: List, deleteAll: Boolean ) = withContext(dispatcher) { queries.transaction { val monsterIndexes = if (deleteAll) { - monsterQueries.getMonstersThatIsNotCloned().executeAsList() - .map { it.index }.also { monsterIndexes -> + queries.getOriginalMonstersLore().executeAsList() + .map { it.monsterLoreIndex }.also { monsterIndexes -> queries.deleteWithIndexes(monsterIndexes) } } else { @@ -67,7 +72,11 @@ internal class MonsterLoreDaoImpl( monstersLore.map { it.monsterLore }.forEach { queries.insert( MonsterLoreDatabaseEntity( - monsterLoreIndex = it.monsterLoreIndex + monsterLoreIndex = it.monsterLoreIndex, + status = when (it.status) { + MonsterLoreEntityStatus.Original -> 0L + MonsterLoreEntityStatus.Imported -> 1L + } ) ) } @@ -88,7 +97,11 @@ internal class MonsterLoreDaoImpl( private fun List.asEntities(): List { return map { MonsterLoreEntity( - monsterLoreIndex = it.monsterLoreIndex + monsterLoreIndex = it.monsterLoreIndex, + status = when (it.status) { + 0L -> MonsterLoreEntityStatus.Original + else -> MonsterLoreEntityStatus.Imported + }, ) to MonsterLoreEntryEntity( id = it.id, title = it.title, diff --git a/domain/app/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/database/dao/SpellDaoImpl.kt b/domain/app/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/database/dao/SpellDaoImpl.kt index 672a90ec8..2f53698a7 100644 --- a/domain/app/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/database/dao/SpellDaoImpl.kt +++ b/domain/app/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/database/dao/SpellDaoImpl.kt @@ -72,6 +72,10 @@ internal class SpellDaoImpl( spellQueries.getSpells().executeAsList().map { it.asSpellEntity() } } + override suspend fun getSpellsEdited(): List = withContext(dispatcher) { + spellQueries.getSpellsEdited().executeAsList().map { it.asSpellEntity() } + } + private fun SpellDatabaseEntity.asSpellEntity(): SpellEntity { return SpellEntity( spellIndex = spellIndex, diff --git a/domain/app/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/di/DatabaseModule.kt b/domain/app/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/di/DatabaseModule.kt index 4627ed3d1..b83b3e22d 100644 --- a/domain/app/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/di/DatabaseModule.kt +++ b/domain/app/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/di/DatabaseModule.kt @@ -76,7 +76,6 @@ val databaseModule = module { factory { val database = get() MonsterLoreDaoImpl( - monsterQueries = database.monsterQueries, queries = database.monsterLoreQueries, monsterLoreEntryQueries = database.monsterLoreEntryQueries, dispatcher = getDispatcherIO() diff --git a/domain/app/data/src/commonMain/sqldelight/br/alexandregpereira/hunter/database/MonsterLore.sq b/domain/app/data/src/commonMain/sqldelight/br/alexandregpereira/hunter/database/MonsterLore.sq index 0dccf824d..e3a90ecb2 100644 --- a/domain/app/data/src/commonMain/sqldelight/br/alexandregpereira/hunter/database/MonsterLore.sq +++ b/domain/app/data/src/commonMain/sqldelight/br/alexandregpereira/hunter/database/MonsterLore.sq @@ -1,10 +1,10 @@ -CREATE TABLE IF NOT EXISTS MonsterLoreEntity (`monsterLoreIndex` TEXT NOT NULL, PRIMARY KEY(`monsterLoreIndex`)); +CREATE TABLE IF NOT EXISTS MonsterLoreEntity (`monsterLoreIndex` TEXT NOT NULL, `status` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`monsterLoreIndex`)); insert: INSERT OR REPLACE INTO MonsterLoreEntity VALUES ?; deleteWithIndexes: -DELETE FROM MonsterLoreEntity WHERE monsterLoreIndex IN ?; +DELETE FROM MonsterLoreEntity WHERE monsterLoreIndex IN ? AND status == 0; getMonstersLore: SELECT * FROM MonsterLoreCompleteEntityView @@ -14,6 +14,12 @@ WHERE monsterLoreIndex IN ? getMonsterLore: SELECT * FROM MonsterLoreCompleteEntityView WHERE monsterLoreIndex == ?; +getMonstersLoreEdited: +SELECT * FROM MonsterLoreCompleteEntityView WHERE status > 0; + +getOriginalMonstersLore: +SELECT * FROM MonsterLoreCompleteEntityView WHERE status == 0; + CREATE VIEW IF NOT EXISTS MonsterLoreCompleteEntityView AS SELECT * FROM MonsterLoreEntity INNER JOIN MonsterLoreEntryEntity diff --git a/domain/app/data/src/commonMain/sqldelight/br/alexandregpereira/hunter/database/Spell.sq b/domain/app/data/src/commonMain/sqldelight/br/alexandregpereira/hunter/database/Spell.sq index 056410a2b..2805132e0 100644 --- a/domain/app/data/src/commonMain/sqldelight/br/alexandregpereira/hunter/database/Spell.sq +++ b/domain/app/data/src/commonMain/sqldelight/br/alexandregpereira/hunter/database/Spell.sq @@ -14,3 +14,6 @@ SELECT * FROM SpellEntity WHERE spellIndex IN ?; getSpells: SELECT * FROM SpellEntity ORDER BY level ASC, name ASC; + +getSpellsEdited: +SELECT * FROM SpellEntity WHERE status > 0; diff --git a/domain/app/data/src/commonMain/sqldelight/databases/27.db b/domain/app/data/src/commonMain/sqldelight/databases/27.db new file mode 100644 index 000000000..57c3ea8a4 Binary files /dev/null and b/domain/app/data/src/commonMain/sqldelight/databases/27.db differ diff --git a/domain/app/data/src/commonMain/sqldelight/migrations/26.sqm b/domain/app/data/src/commonMain/sqldelight/migrations/26.sqm new file mode 100644 index 000000000..644f931fb --- /dev/null +++ b/domain/app/data/src/commonMain/sqldelight/migrations/26.sqm @@ -0,0 +1 @@ +ALTER TABLE MonsterLoreEntity ADD COLUMN status INTEGER NOT NULL DEFAULT 0; diff --git a/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/GetMonstersLoreEdited.kt b/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/GetMonstersLoreEdited.kt new file mode 100644 index 000000000..2b43db1f2 --- /dev/null +++ b/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/GetMonstersLoreEdited.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2023 Alexandre Gomes Pereira + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package br.alexandregpereira.hunter.domain.monster.lore + +import br.alexandregpereira.hunter.domain.monster.lore.model.MonsterLore +import kotlinx.coroutines.flow.Flow + +class GetMonstersLoreEdited( + private val repository: MonsterLoreLocalRepository +) { + + operator fun invoke(): Flow> { + return repository.getMonstersLoreEdited() + } +} diff --git a/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/MonsterLoreLocalRepository.kt b/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/MonsterLoreLocalRepository.kt index a33f2d754..9e22906cc 100644 --- a/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/MonsterLoreLocalRepository.kt +++ b/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/MonsterLoreLocalRepository.kt @@ -25,5 +25,7 @@ interface MonsterLoreLocalRepository { fun getLocalMonstersLore(indexes: List): Flow> + fun getMonstersLoreEdited(): Flow> + fun save(monstersLore: List, isSync: Boolean): Flow } diff --git a/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/SyncMonstersLoreUseCase.kt b/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/SyncMonstersLoreUseCase.kt index d59751db3..239396d41 100644 --- a/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/SyncMonstersLoreUseCase.kt +++ b/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/SyncMonstersLoreUseCase.kt @@ -16,6 +16,7 @@ package br.alexandregpereira.hunter.domain.monster.lore +import br.alexandregpereira.hunter.domain.monster.lore.model.MonsterLore import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow @@ -26,14 +27,15 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.reduce import kotlinx.coroutines.flow.single +@OptIn(ExperimentalCoroutinesApi::class) class SyncMonstersLoreUseCase( private val repository: MonsterLoreRepository, private val saveMonstersLore: SaveMonstersLoreUseCase, private val alternativeSourceRepository: MonsterLoreSourceRepository, private val settingsRepository: MonsterLoreSettingsRepository, + private val getMonstersLoreEdited: GetMonstersLoreEdited, ) { - @OptIn(ExperimentalCoroutinesApi::class) operator fun invoke(): Flow { return alternativeSourceRepository.getMonsterLoreSources() .catch { emit(emptyList()) } @@ -46,8 +48,18 @@ class SyncMonstersLoreUseCase( .catch { emit(emptyList()) } }?.reduce { accumulator, value -> accumulator + value } ?: emptyList() } + .removeMonstersLoreEdited() .flatMapLatest { monstersLore -> saveMonstersLore(monstersLore, isSync = true) } } + + private fun Flow>.removeMonstersLoreEdited(): Flow> { + return map { monstersLore -> + val monstersLoreEditedIndexes = getMonstersLoreEdited().single() + .map { it.index } + .toSet() + monstersLore.filterNot { it.index in monstersLoreEditedIndexes } + } + } } diff --git a/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/di/DomainModule.kt b/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/di/DomainModule.kt index 61f13031e..ef1f57beb 100644 --- a/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/di/DomainModule.kt +++ b/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/di/DomainModule.kt @@ -18,6 +18,7 @@ package br.alexandregpereira.hunter.domain.monster.lore.di import br.alexandregpereira.hunter.domain.monster.lore.GetMonsterLoreUseCase import br.alexandregpereira.hunter.domain.monster.lore.GetMonstersLoreByIdsUseCase +import br.alexandregpereira.hunter.domain.monster.lore.GetMonstersLoreEdited import br.alexandregpereira.hunter.domain.monster.lore.SaveMonstersLoreUseCase import br.alexandregpereira.hunter.domain.monster.lore.SyncMonstersLoreUseCase import org.koin.dsl.module @@ -25,6 +26,7 @@ import org.koin.dsl.module val monsterLoreDomainModule = module { factory { GetMonsterLoreUseCase(get()) } factory { GetMonstersLoreByIdsUseCase(get()) } - factory { SyncMonstersLoreUseCase(get(), get(), get(), get()) } + factory { SyncMonstersLoreUseCase(get(), get(), get(), get(), get()) } factory { SaveMonstersLoreUseCase(get()) } + factory { GetMonstersLoreEdited(get()) } } diff --git a/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/model/MonsterLore.kt b/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/model/MonsterLore.kt index ebc038585..e22b38660 100644 --- a/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/model/MonsterLore.kt +++ b/domain/monster-lore/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/monster/lore/model/MonsterLore.kt @@ -20,4 +20,9 @@ data class MonsterLore( val index: String, val name: String, val entries: List, + val status: MonsterLoreStatus, ) + +enum class MonsterLoreStatus { + Original, Imported +} diff --git a/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/DefaultMonsterLoreLocalRepository.kt b/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/DefaultMonsterLoreLocalRepository.kt index c974ab0f6..84b2badae 100644 --- a/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/DefaultMonsterLoreLocalRepository.kt +++ b/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/DefaultMonsterLoreLocalRepository.kt @@ -38,6 +38,12 @@ internal class DefaultMonsterLoreLocalRepository( } } + override fun getMonstersLoreEdited(): Flow> { + return monsterLoreLocalDataSource.getMonstersLoreEdited().map { monsters -> + monsters.map { it.toDomain() } + } + } + override fun save(monstersLore: List, isSync: Boolean): Flow { return monsterLoreLocalDataSource.save(monstersLore.map { it.toEntity() }, isSync) } diff --git a/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/DefaultMonsterLoreRepository.kt b/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/DefaultMonsterLoreRepository.kt index d62b6b945..f46e50e71 100644 --- a/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/DefaultMonsterLoreRepository.kt +++ b/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/DefaultMonsterLoreRepository.kt @@ -45,4 +45,8 @@ internal class DefaultMonsterLoreRepository( ): Flow> { return remoteRepository.getRemoteMonstersLore(sourceAcronym, lang) } + + override fun getMonstersLoreEdited(): Flow> { + return localRepository.getMonstersLoreEdited() + } } diff --git a/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/MonsterLoreLocalDataSource.kt b/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/MonsterLoreLocalDataSource.kt index 4c0b5dc52..afcd89140 100644 --- a/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/MonsterLoreLocalDataSource.kt +++ b/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/MonsterLoreLocalDataSource.kt @@ -40,6 +40,12 @@ internal class MonsterLoreLocalDataSource( } } + fun getMonstersLoreEdited(): Flow> { + return flow { + emit(monsterLoreDao.getMonstersLoreEdited()) + } + } + fun save( monsters: List, isSync: Boolean = false diff --git a/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/dao/MonsterLoreDao.kt b/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/dao/MonsterLoreDao.kt index 8b1056c87..22012e60a 100644 --- a/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/dao/MonsterLoreDao.kt +++ b/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/dao/MonsterLoreDao.kt @@ -24,6 +24,8 @@ interface MonsterLoreDao { suspend fun getMonsterLore(monsterIndex: String): MonsterLoreCompleteEntity + suspend fun getMonstersLoreEdited(): List + suspend fun insert( monstersLore: List, deleteAll: Boolean diff --git a/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/entity/MonsterLoreEntity.kt b/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/entity/MonsterLoreEntity.kt index 27df4d785..332e084df 100644 --- a/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/entity/MonsterLoreEntity.kt +++ b/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/entity/MonsterLoreEntity.kt @@ -17,5 +17,10 @@ package br.alexandregpereira.hunter.data.monster.lore.local.entity data class MonsterLoreEntity( - val monsterLoreIndex: String + val monsterLoreIndex: String, + val status: MonsterLoreEntityStatus, ) + +enum class MonsterLoreEntityStatus { + Original, Imported +} diff --git a/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/mapper/MonsterLoreEntityMapper.kt b/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/mapper/MonsterLoreEntityMapper.kt index 008a9b36e..5f226f280 100644 --- a/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/mapper/MonsterLoreEntityMapper.kt +++ b/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/local/mapper/MonsterLoreEntityMapper.kt @@ -18,15 +18,21 @@ package br.alexandregpereira.hunter.data.monster.lore.local.mapper import br.alexandregpereira.hunter.data.monster.lore.local.entity.MonsterLoreCompleteEntity import br.alexandregpereira.hunter.data.monster.lore.local.entity.MonsterLoreEntity +import br.alexandregpereira.hunter.data.monster.lore.local.entity.MonsterLoreEntityStatus import br.alexandregpereira.hunter.data.monster.lore.local.entity.MonsterLoreEntryEntity import br.alexandregpereira.hunter.domain.monster.lore.model.MonsterLore import br.alexandregpereira.hunter.domain.monster.lore.model.MonsterLoreEntry +import br.alexandregpereira.hunter.domain.monster.lore.model.MonsterLoreStatus internal fun MonsterLoreCompleteEntity.toDomain(): MonsterLore { return MonsterLore( index = monsterLore.monsterLoreIndex, name = "", - entries = entries.map { it.toDomain() } + entries = entries.map { it.toDomain() }, + status = when (monsterLore.status) { + MonsterLoreEntityStatus.Original -> MonsterLoreStatus.Original + MonsterLoreEntityStatus.Imported -> MonsterLoreStatus.Imported + } ) } @@ -40,7 +46,11 @@ internal fun MonsterLoreEntryEntity.toDomain(): MonsterLoreEntry { internal fun MonsterLore.toEntity(): MonsterLoreCompleteEntity { return MonsterLoreCompleteEntity( monsterLore = MonsterLoreEntity( - monsterLoreIndex = index + monsterLoreIndex = index, + status = when (status) { + MonsterLoreStatus.Original -> MonsterLoreEntityStatus.Original + MonsterLoreStatus.Imported -> MonsterLoreEntityStatus.Imported + } ), entries = entries.map { it.toEntity(index) } ) diff --git a/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/remote/mapper/MonsterLoreDtoMapper.kt b/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/remote/mapper/MonsterLoreDtoMapper.kt index 40faae7a7..64d26577d 100644 --- a/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/remote/mapper/MonsterLoreDtoMapper.kt +++ b/domain/monster-lore/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/monster/lore/remote/mapper/MonsterLoreDtoMapper.kt @@ -20,12 +20,14 @@ import br.alexandregpereira.hunter.data.monster.lore.remote.model.MonsterLoreDto import br.alexandregpereira.hunter.data.monster.lore.remote.model.MonsterLoreEntryDto import br.alexandregpereira.hunter.domain.monster.lore.model.MonsterLore import br.alexandregpereira.hunter.domain.monster.lore.model.MonsterLoreEntry +import br.alexandregpereira.hunter.domain.monster.lore.model.MonsterLoreStatus internal fun MonsterLoreDto.toDomain(): MonsterLore { return MonsterLore( index = index, name = "", - entries = entries.map { it.toDomain() } + entries = entries.map { it.toDomain() }, + status = MonsterLoreStatus.Original, ) } diff --git a/domain/monster/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/usecase/SyncMonstersUseCase.kt b/domain/monster/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/usecase/SyncMonstersUseCase.kt index 9cf4a8850..446afdb66 100644 --- a/domain/monster/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/usecase/SyncMonstersUseCase.kt +++ b/domain/monster/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/usecase/SyncMonstersUseCase.kt @@ -59,7 +59,7 @@ class SyncMonstersUseCase internal constructor( } .reduce { accumulator, value -> accumulator + value } } - .filterModifiedMonsters() + .removeModifiedMonsters() .flatMapLatest { monsters -> saveMonstersUseCase(monsters = monsters, isSync = true) }.flatMapLatest { @@ -92,7 +92,7 @@ class SyncMonstersUseCase internal constructor( it.appendMonsterImages(monsterImages) } - private fun Flow>.filterModifiedMonsters(): Flow> = map { monsters -> + private fun Flow>.removeModifiedMonsters(): Flow> = map { monsters -> val monstersEditedIndexes = localRepository.getMonsterPreviewsEdited().single() .map { it.index } .toSet() diff --git a/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/GetSpellsEdited.kt b/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/GetSpellsEdited.kt new file mode 100644 index 000000000..73e949ff4 --- /dev/null +++ b/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/GetSpellsEdited.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2022 Alexandre Gomes Pereira + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package br.alexandregpereira.hunter.domain.spell + +import br.alexandregpereira.hunter.domain.spell.model.Spell +import kotlinx.coroutines.flow.Flow + +class GetSpellsEdited( + private val spellRepository: SpellLocalRepository +) { + + operator fun invoke(): Flow> { + return spellRepository.getLocalSpellsEdited() + } +} diff --git a/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/SpellLocalRepository.kt b/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/SpellLocalRepository.kt index a062d970e..4fec59adc 100644 --- a/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/SpellLocalRepository.kt +++ b/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/SpellLocalRepository.kt @@ -26,4 +26,5 @@ interface SpellLocalRepository { fun getLocalSpells(indexes: List): Flow> fun getLocalSpells(): Flow> fun deleteLocalSpells(): Flow + fun getLocalSpellsEdited(): Flow> } diff --git a/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/SyncSpellsUseCase.kt b/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/SyncSpellsUseCase.kt index 142f92d58..3f0ec1177 100644 --- a/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/SyncSpellsUseCase.kt +++ b/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/SyncSpellsUseCase.kt @@ -16,23 +16,32 @@ package br.alexandregpereira.hunter.domain.spell +import br.alexandregpereira.hunter.domain.spell.model.Spell import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.single +@OptIn(ExperimentalCoroutinesApi::class) class SyncSpellsUseCase( private val repository: SpellRepository, - private val spellSettingsRepository: SpellSettingsRepository + private val spellSettingsRepository: SpellSettingsRepository, + private val getSpellsEdited: GetSpellsEdited, ) { - @OptIn(ExperimentalCoroutinesApi::class) operator fun invoke(): Flow { return spellSettingsRepository.getLanguage().flatMapLatest { lang -> repository.getRemoteSpells(lang) - }.flatMapLatest { spells -> + }.removeSpellsEdited().flatMapLatest { spells -> repository.deleteLocalSpells().flatMapLatest { repository.saveSpells(spells) } } } + + private fun Flow>.removeSpellsEdited(): Flow> = map { spells -> + val spellsEditedIndexes = getSpellsEdited().single().map { it.index }.toSet() + spells.filterNot { it.index in spellsEditedIndexes } + } } diff --git a/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/di/DomainModule.kt b/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/di/DomainModule.kt index 4f9c8a178..2e94e66a3 100644 --- a/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/di/DomainModule.kt +++ b/domain/spell/core/src/commonMain/kotlin/br/alexandregpereira/hunter/domain/spell/di/DomainModule.kt @@ -18,6 +18,7 @@ package br.alexandregpereira.hunter.domain.spell.di import br.alexandregpereira.hunter.domain.spell.GetSpellUseCase import br.alexandregpereira.hunter.domain.spell.GetSpellsByIdsUseCase +import br.alexandregpereira.hunter.domain.spell.GetSpellsEdited import br.alexandregpereira.hunter.domain.spell.SaveSpells import br.alexandregpereira.hunter.domain.spell.SyncSpellsUseCase import org.koin.dsl.module @@ -25,6 +26,7 @@ import org.koin.dsl.module val spellDomainModule = module { factory { GetSpellsByIdsUseCase(get()) } factory { GetSpellUseCase(get()) } - factory { SyncSpellsUseCase(get(), get()) } + factory { SyncSpellsUseCase(get(), get(), get()) } factory { SaveSpells(get()) } + factory { GetSpellsEdited(get()) } } diff --git a/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/DefaultSpellLocalRepository.kt b/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/DefaultSpellLocalRepository.kt index e772e83f2..92db7e343 100644 --- a/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/DefaultSpellLocalRepository.kt +++ b/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/DefaultSpellLocalRepository.kt @@ -48,6 +48,12 @@ internal class DefaultSpellLocalRepository( } } + override fun getLocalSpellsEdited(): Flow> { + return localDataSource.getSpellsEdited().map { spells -> + spells.map { it.toDomain() } + } + } + override fun deleteLocalSpells(): Flow { return localDataSource.deleteSpells() } diff --git a/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/DefaultSpellRepository.kt b/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/DefaultSpellRepository.kt index 5ffd650f1..faa20a35a 100644 --- a/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/DefaultSpellRepository.kt +++ b/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/DefaultSpellRepository.kt @@ -50,4 +50,8 @@ internal class DefaultSpellRepository( override fun deleteLocalSpells(): Flow { return localRepository.deleteLocalSpells() } + + override fun getLocalSpellsEdited(): Flow> { + return localRepository.getLocalSpellsEdited() + } } diff --git a/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/local/DefaultSpellLocalDataSource.kt b/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/local/DefaultSpellLocalDataSource.kt index 4a5a6c9de..85a8d9b6d 100644 --- a/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/local/DefaultSpellLocalDataSource.kt +++ b/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/local/DefaultSpellLocalDataSource.kt @@ -44,4 +44,8 @@ internal class DefaultSpellLocalDataSource( override fun getSpells(): Flow> = flow { emit(spellDao.getSpells()) } + + override fun getSpellsEdited(): Flow> = flow { + emit(spellDao.getSpellsEdited()) + } } diff --git a/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/local/SpellLocalDataSource.kt b/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/local/SpellLocalDataSource.kt index f517ece61..d60c2737f 100644 --- a/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/local/SpellLocalDataSource.kt +++ b/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/local/SpellLocalDataSource.kt @@ -26,4 +26,5 @@ internal interface SpellLocalDataSource { fun deleteSpells(): Flow fun getSpells(indexes: List): Flow> fun getSpells(): Flow> + fun getSpellsEdited(): Flow> } diff --git a/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/local/dao/SpellDao.kt b/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/local/dao/SpellDao.kt index cdba44411..5d736ebdf 100644 --- a/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/local/dao/SpellDao.kt +++ b/domain/spell/data/src/commonMain/kotlin/br/alexandregpereira/hunter/data/spell/local/dao/SpellDao.kt @@ -29,4 +29,6 @@ interface SpellDao { suspend fun getSpells(indexes: List): List suspend fun getSpells(): List + + suspend fun getSpellsEdited(): List } diff --git a/feature/monster-compendium/compose/src/commonMain/kotlin/br/alexandregpereira/hunter/monster/compendium/ui/MonterCompendium.kt b/feature/monster-compendium/compose/src/commonMain/kotlin/br/alexandregpereira/hunter/monster/compendium/ui/MonterCompendium.kt index 790e3ed87..9d9c63cd0 100644 --- a/feature/monster-compendium/compose/src/commonMain/kotlin/br/alexandregpereira/hunter/monster/compendium/ui/MonterCompendium.kt +++ b/feature/monster-compendium/compose/src/commonMain/kotlin/br/alexandregpereira/hunter/monster/compendium/ui/MonterCompendium.kt @@ -35,6 +35,7 @@ internal fun MonsterCompendium( items = items, listState = listState, contentPadding = contentPadding, + enableHorizontalImage = true, onItemCLick = onItemCLick, onItemLongCLick = onItemLongCLick ) diff --git a/feature/monster-detail/state-holder/src/commonMain/kotlin/br/alexandregpereira/hunter/monster/detail/MonsterDetailStateHolder.kt b/feature/monster-detail/state-holder/src/commonMain/kotlin/br/alexandregpereira/hunter/monster/detail/MonsterDetailStateHolder.kt index 4e3e96461..c2d84c060 100644 --- a/feature/monster-detail/state-holder/src/commonMain/kotlin/br/alexandregpereira/hunter/monster/detail/MonsterDetailStateHolder.kt +++ b/feature/monster-detail/state-holder/src/commonMain/kotlin/br/alexandregpereira/hunter/monster/detail/MonsterDetailStateHolder.kt @@ -21,7 +21,6 @@ import br.alexadregpereira.hunter.shareContent.event.ShareContentEventDispatcher import br.alexandregpereira.hunter.domain.model.Monster import br.alexandregpereira.hunter.domain.model.MonsterStatus import br.alexandregpereira.hunter.event.EventDispatcher -import br.alexandregpereira.hunter.event.EventListener import br.alexandregpereira.hunter.event.folder.insert.FolderInsertEvent import br.alexandregpereira.hunter.event.folder.insert.FolderInsertEventDispatcher import br.alexandregpereira.hunter.event.monster.lore.detail.MonsterLoreDetailEvent @@ -52,7 +51,6 @@ import br.alexandregpereira.hunter.monster.event.MonsterEventDispatcher import br.alexandregpereira.hunter.monster.event.collectOnMonsterCompendiumChanges import br.alexandregpereira.hunter.monster.event.collectOnVisibilityChanges import br.alexandregpereira.hunter.monster.registration.event.MonsterRegistrationEvent -import br.alexandregpereira.hunter.monster.registration.event.MonsterRegistrationResult import br.alexandregpereira.hunter.spell.detail.event.SpellDetailEvent import br.alexandregpereira.hunter.spell.detail.event.SpellDetailEventDispatcher import br.alexandregpereira.hunter.state.UiModel @@ -66,6 +64,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.cancellable import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flatMapConcat +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn @@ -173,7 +172,7 @@ class MonsterDetailStateHolder internal constructor( if (scrolled && monsterIndex != this.monsterIndex) { initialMonsterListPositionIndex = state.value.monsters.indexOfFirst { it.index == monsterIndex - } + }.takeIf { it >= 0 } ?: initialMonsterListPositionIndex if (enableMonsterPageChangesEventDispatch) { analytics.trackMonsterPageChanged(monsterIndex, scrolled) monsterEventDispatcher.dispatchEvent(OnMonsterPageChanges(monsterIndex)) @@ -351,14 +350,15 @@ class MonsterDetailStateHolder internal constructor( } } else flowOf(currentState to currentMonsterIndex) }.flowOn(dispatcher) - .map { (state, monsterIndex) -> + .flatMapLatest { (state, monsterIndex) -> + flowOf(state).emitState().map { monsterIndex } + } + .onEach { monsterIndex -> onMonsterChanged(monsterIndex, scrolled = true) if (monsterIndexes.isNotEmpty()) { monsterEventDispatcher.dispatchEvent(OnCompendiumChanges()) } - state } - .emitState() .launchIn(scope) } diff --git a/feature/monster-lore-detail/compose/src/commonMain/kotlin/br/alexandregpereira/hunter/monster/lore/detail/ui/MonsterLoreDetail.kt b/feature/monster-lore-detail/compose/src/commonMain/kotlin/br/alexandregpereira/hunter/monster/lore/detail/ui/MonsterLoreDetail.kt index 1ecf54e4b..4707c02ab 100644 --- a/feature/monster-lore-detail/compose/src/commonMain/kotlin/br/alexandregpereira/hunter/monster/lore/detail/ui/MonsterLoreDetail.kt +++ b/feature/monster-lore-detail/compose/src/commonMain/kotlin/br/alexandregpereira/hunter/monster/lore/detail/ui/MonsterLoreDetail.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import br.alexandregpereira.hunter.domain.monster.lore.model.MonsterLore import br.alexandregpereira.hunter.domain.monster.lore.model.MonsterLoreEntry +import br.alexandregpereira.hunter.domain.monster.lore.model.MonsterLoreStatus import br.alexandregpereira.hunter.ui.compose.ScreenHeader import br.alexandregpereira.hunter.ui.compose.Window import org.jetbrains.compose.ui.tooling.preview.Preview @@ -63,7 +64,8 @@ private fun MonsterLoreDetailPreview() = Window { title = "Title", description = "asdas asdasd asd asd \nas dasasdas as x as asd d as" ) - ) + ), + status = MonsterLoreStatus.Original ) ) } diff --git a/feature/share-content/compose/src/commonMain/kotlin/br/alexandregpereira/hunter/shareContent/domain/mapper/ShareMonsterLoreToDomainMapper.kt b/feature/share-content/compose/src/commonMain/kotlin/br/alexandregpereira/hunter/shareContent/domain/mapper/ShareMonsterLoreToDomainMapper.kt index 0f24fed8e..679932804 100644 --- a/feature/share-content/compose/src/commonMain/kotlin/br/alexandregpereira/hunter/shareContent/domain/mapper/ShareMonsterLoreToDomainMapper.kt +++ b/feature/share-content/compose/src/commonMain/kotlin/br/alexandregpereira/hunter/shareContent/domain/mapper/ShareMonsterLoreToDomainMapper.kt @@ -2,6 +2,7 @@ package br.alexandregpereira.hunter.shareContent.domain.mapper import br.alexandregpereira.hunter.domain.monster.lore.model.MonsterLore import br.alexandregpereira.hunter.domain.monster.lore.model.MonsterLoreEntry +import br.alexandregpereira.hunter.domain.monster.lore.model.MonsterLoreStatus import br.alexandregpereira.hunter.shareContent.domain.model.ShareMonsterLore internal fun ShareMonsterLore.toMonsterLore(): MonsterLore { @@ -13,6 +14,7 @@ internal fun ShareMonsterLore.toMonsterLore(): MonsterLore { title = it.title, description = it.description, ) - } + }, + status = MonsterLoreStatus.Imported, ) } diff --git a/ui/monster-compendium/src/commonMain/kotlin/br/alexandregpereira/hunter/ui/compendium/monster/MonterCompendium.kt b/ui/monster-compendium/src/commonMain/kotlin/br/alexandregpereira/hunter/ui/compendium/monster/MonterCompendium.kt index f3235f033..8fd96a90a 100644 --- a/ui/monster-compendium/src/commonMain/kotlin/br/alexandregpereira/hunter/ui/compendium/monster/MonterCompendium.kt +++ b/ui/monster-compendium/src/commonMain/kotlin/br/alexandregpereira/hunter/ui/compendium/monster/MonterCompendium.kt @@ -38,6 +38,7 @@ fun MonsterCompendium( items: List, modifier: Modifier = Modifier, animateItems: Boolean = false, + enableHorizontalImage: Boolean = false, listState: LazyGridState = rememberLazyGridState(), contentPadding: PaddingValues = PaddingValues(0.dp), onItemCLick: (index: String) -> Unit = {}, @@ -63,7 +64,7 @@ fun MonsterCompendium( isHorizontalReading = true, key = { it.getMonsterCardState().index }, isHorizontalCard = { - it.getMonsterCardState().imageState.isHorizontal && + enableHorizontalImage && it.getMonsterCardState().imageState.isHorizontal && currentWidth - 56.dp >= cardWidth.dp * 2 }, cardContent = { item -> @@ -75,7 +76,7 @@ fun MonsterCompendium( backgroundColor = monsterCardState.imageState.backgroundColor.getColor( isSystemInDarkTheme() ), - isHorizontal = monsterCardState.imageState.isHorizontal, + isHorizontal = enableHorizontalImage && monsterCardState.imageState.isHorizontal, challengeRating = monsterCardState.imageState.challengeRating, onCLick = { onItemCLick(monsterCardState.index) }, onLongCLick = { onItemLongCLick(monsterCardState.index) }