Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix - Sync overriding imported spells and lore #321

Merged
merged 3 commits into from
Aug 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -48,14 +47,20 @@ internal class MonsterLoreDaoImpl(
queries.getMonsterLore(monsterIndex).executeAsList().asEntities().first()
}

override suspend fun getMonstersLoreEdited(): List<MonsterLoreCompleteEntity> {
return withContext(dispatcher) {
queries.getMonstersLoreEdited().executeAsList().asEntities()
}
}

override suspend fun insert(
monstersLore: List<MonsterLoreCompleteEntity>,
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 {
Expand All @@ -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
}
)
)
}
Expand All @@ -88,7 +97,11 @@ internal class MonsterLoreDaoImpl(
private fun List<MonsterLoreCompleteEntityView>.asEntities(): List<MonsterLoreCompleteEntity> {
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ internal class SpellDaoImpl(
spellQueries.getSpells().executeAsList().map { it.asSpellEntity() }
}

override suspend fun getSpellsEdited(): List<SpellEntity> = withContext(dispatcher) {
spellQueries.getSpellsEdited().executeAsList().map { it.asSpellEntity() }
}

private fun SpellDatabaseEntity.asSpellEntity(): SpellEntity {
return SpellEntity(
spellIndex = spellIndex,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ val databaseModule = module {
factory<MonsterLoreDao> {
val database = get<Database>()
MonsterLoreDaoImpl(
monsterQueries = database.monsterQueries,
queries = database.monsterLoreQueries,
monsterLoreEntryQueries = database.monsterLoreEntryQueries,
dispatcher = getDispatcherIO()
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE MonsterLoreEntity ADD COLUMN status INTEGER NOT NULL DEFAULT 0;
Original file line number Diff line number Diff line change
@@ -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<List<MonsterLore>> {
return repository.getMonstersLoreEdited()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,7 @@ interface MonsterLoreLocalRepository {

fun getLocalMonstersLore(indexes: List<String>): Flow<List<MonsterLore>>

fun getMonstersLoreEdited(): Flow<List<MonsterLore>>

fun save(monstersLore: List<MonsterLore>, isSync: Boolean): Flow<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<Unit> {
return alternativeSourceRepository.getMonsterLoreSources()
.catch { emit(emptyList()) }
Expand All @@ -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<List<MonsterLore>>.removeMonstersLoreEdited(): Flow<List<MonsterLore>> {
return map { monstersLore ->
val monstersLoreEditedIndexes = getMonstersLoreEdited().single()
.map { it.index }
.toSet()
monstersLore.filterNot { it.index in monstersLoreEditedIndexes }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ 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

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()) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@ data class MonsterLore(
val index: String,
val name: String,
val entries: List<MonsterLoreEntry>,
val status: MonsterLoreStatus,
)

enum class MonsterLoreStatus {
Original, Imported
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ internal class DefaultMonsterLoreLocalRepository(
}
}

override fun getMonstersLoreEdited(): Flow<List<MonsterLore>> {
return monsterLoreLocalDataSource.getMonstersLoreEdited().map { monsters ->
monsters.map { it.toDomain() }
}
}

override fun save(monstersLore: List<MonsterLore>, isSync: Boolean): Flow<Unit> {
return monsterLoreLocalDataSource.save(monstersLore.map { it.toEntity() }, isSync)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,8 @@ internal class DefaultMonsterLoreRepository(
): Flow<List<MonsterLore>> {
return remoteRepository.getRemoteMonstersLore(sourceAcronym, lang)
}

override fun getMonstersLoreEdited(): Flow<List<MonsterLore>> {
return localRepository.getMonstersLoreEdited()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ internal class MonsterLoreLocalDataSource(
}
}

fun getMonstersLoreEdited(): Flow<List<MonsterLoreCompleteEntity>> {
return flow {
emit(monsterLoreDao.getMonstersLoreEdited())
}
}

fun save(
monsters: List<MonsterLoreCompleteEntity>,
isSync: Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ interface MonsterLoreDao {

suspend fun getMonsterLore(monsterIndex: String): MonsterLoreCompleteEntity

suspend fun getMonstersLoreEdited(): List<MonsterLoreCompleteEntity>

suspend fun insert(
monstersLore: List<MonsterLoreCompleteEntity>,
deleteAll: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
)
}

Expand All @@ -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) }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class SyncMonstersUseCase internal constructor(
}
.reduce { accumulator, value -> accumulator + value }
}
.filterModifiedMonsters()
.removeModifiedMonsters()
.flatMapLatest { monsters ->
saveMonstersUseCase(monsters = monsters, isSync = true)
}.flatMapLatest {
Expand Down Expand Up @@ -92,7 +92,7 @@ class SyncMonstersUseCase internal constructor(
it.appendMonsterImages(monsterImages)
}

private fun Flow<List<Monster>>.filterModifiedMonsters(): Flow<List<Monster>> = map { monsters ->
private fun Flow<List<Monster>>.removeModifiedMonsters(): Flow<List<Monster>> = map { monsters ->
val monstersEditedIndexes = localRepository.getMonsterPreviewsEdited().single()
.map { it.index }
.toSet()
Expand Down
Original file line number Diff line number Diff line change
@@ -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<List<Spell>> {
return spellRepository.getLocalSpellsEdited()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ interface SpellLocalRepository {
fun getLocalSpells(indexes: List<String>): Flow<List<Spell>>
fun getLocalSpells(): Flow<List<Spell>>
fun deleteLocalSpells(): Flow<Unit>
fun getLocalSpellsEdited(): Flow<List<Spell>>
}
Loading
Loading