Skip to content

Commit

Permalink
Implement edit monster feature (#223)
Browse files Browse the repository at this point in the history
* wip

* Update monster detail options when changing monster page

* wip

* Rollback MonsterDaoImpl

* Improve insert function to delete by indexes

* Implement new Form field types

* Add actions form

* Adjust forms

* Create KMP UUID

* Create Damage and condition forms

* Add add button

* Add strings to strings file
  • Loading branch information
alexandregpereira authored Jan 12, 2024
1 parent ae105a9 commit 61b281f
Show file tree
Hide file tree
Showing 127 changed files with 2,744 additions and 348 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ dependencies {
implementation(project(":feature:monster-content-manager:android"))
implementation(project(":feature:monster-detail:android"))
implementation(project(":feature:monster-lore-detail:android"))
implementation(project(":feature:monster-registration:android"))
implementation(project(":feature:sync:android"))
implementation(project(":feature:search:android"))
implementation(project(":feature:settings:android"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import br.alexandregpereira.hunter.monster.compendium.di.monsterCompendiumModule
import br.alexandregpereira.hunter.monster.content.di.monsterContentManagerModule
import br.alexandregpereira.hunter.monster.content.preview.di.monsterContentPreviewModule
import br.alexandregpereira.hunter.monster.lore.detail.di.monsterLoreDetailModule
import br.alexandregpereira.hunter.monster.registration.di.monsterRegistrationModule
import br.alexandregpereira.hunter.search.di.searchModule
import br.alexandregpereira.hunter.settings.di.settingsModule
import br.alexandregpereira.hunter.spell.detail.di.spellDetailModule
Expand Down Expand Up @@ -81,7 +82,8 @@ class HunterApplication : Application() {
monsterLoreDetailModule +
monsterContentManagerModule +
monsterContentPreviewModule +
syncModule
syncModule +
monsterRegistrationModule
)
modules(
analyticsModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import br.alexandregpereira.hunter.folder.insert.FolderInsertFeature
import br.alexandregpereira.hunter.folder.preview.FolderPreviewFeature
import br.alexandregpereira.hunter.monster.content.MonsterContentManagerFeature
import br.alexandregpereira.hunter.monster.lore.detail.MonsterLoreDetailFeature
import br.alexandregpereira.hunter.monster.registration.MonsterRegistrationFeature
import br.alexandregpereira.hunter.spell.detail.SpellDetailFeature
import br.alexandregpereira.hunter.sync.SyncFeature

Expand Down Expand Up @@ -68,6 +69,8 @@ fun MainScreen(

MonsterLoreDetailFeature(contentPadding = contentPadding)

MonsterRegistrationFeature(contentPadding = contentPadding)

SpellDetailFeature(
contentPadding = contentPadding,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ interface EventListener<Event> {

interface EventManager<Event> : EventDispatcher<Event>, EventListener<Event>

internal class DefaultEventManager<Event> : EventManager<Event> {
fun <Event> EventManager(): EventManager<Event> = DefaultEventManager()

private class DefaultEventManager<Event> : EventManager<Event> {

private val _events: MutableSharedFlow<Event> = MutableSharedFlow(
extraBufferCapacity = 1,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package br.alexandregpereira.hunter.event.systembar

import br.alexandregpereira.hunter.event.DefaultEventManager
import br.alexandregpereira.hunter.event.EventDispatcher
import br.alexandregpereira.hunter.event.EventManager

class BottomBarEventManager : EventManager<BottomBarEvent> by DefaultEventManager()
class BottomBarEventManager : EventManager<BottomBarEvent> by EventManager()

sealed class BottomBarEvent {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ interface MutableActionHandler<Action> : ActionHandler<Action> {
fun sendAction(action: Action)
}

class DefaultActionHandler<Action> : MutableActionHandler<Action> {
fun <Action> MutableActionHandler(): MutableActionHandler<Action> {
return MutableActionHandlerImpl()
}

private class MutableActionHandlerImpl<Action> : MutableActionHandler<Action> {

private val _action = MutableSharedFlow<Action>(
extraBufferCapacity = 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,33 @@

package br.alexandregpereira.hunter.state

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

abstract class UiModel<State>(
initialState: State
) : StateHolder<State> {

protected val scope = CoroutineScope(
SupervisorJob() + Dispatchers.Main.immediate
)

private val _state = MutableStateFlow(initialState)
override val state: StateFlow<State> = _state

protected fun setState(block: State.() -> State) {
_state.value = state.value.block()
}

fun onCleared() {
scope.cancel()
}
}

interface StateHolder<State> {

val state: StateFlow<State>
Expand All @@ -29,13 +53,11 @@ interface MutableStateHolder<State>: StateHolder<State> {
fun setState(block: State.() -> State)
}

@Suppress("FunctionName")
fun <State> DefaultMutableStateHolder(initialState: State): MutableStateHolder<State> {
fun <State> MutableStateHolder(initialState: State): MutableStateHolder<State> {
return DefaultStateHolderImpl(initialState)
}

@Suppress("FunctionName")
fun <State> DefaultMutableStateHolder(
fun <State> MutableStateHolder(
stateRecovery: StateRecovery<State>
): MutableStateHolder<State> {
return DefaultStateHolderWithRecovery(stateRecovery)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package br.alexandregpereira.hunter.state

interface StateHolderParams<Params> {

var value: Params
}

fun <Params> StateHolderParams(initialParams: Params): StateHolderParams<Params> =
StateHolderParamsImpl(initialParams)

private class StateHolderParamsImpl<Params>(
initialParams: Params
) : StateHolderParams<Params> {

override var value: Params = initialParams
}
5 changes: 5 additions & 0 deletions core/uuid/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
plugins {
kotlin("multiplatform")
}

configureJvmTargets()
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package br.alexandregpereira.hunter.uuid

expect fun generateUUID(): String
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package br.alexandregpereira.hunter.uuid

import platform.Foundation.NSUUID

actual fun generateUUID(): String = NSUUID().UUIDString()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package br.alexandregpereira.hunter.uuid

import java.util.UUID

actual fun generateUUID(): String = UUID.randomUUID().toString()
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package br.alexandregpereira.hunter.data.database.dao

internal fun <T> List<List<T>>.reduceList(): List<T> {
return this.reduceOrNull { acc, list -> acc + list } ?: emptyList()
}

internal fun <T, R> List<T>.mapAndReduce(transform: T.() -> List<R>): List<R> {
return this.map(transform).reduceList()
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,14 @@ internal class MonsterDaoImpl(
emptyList()
}

override suspend fun insert(monsters: List<MonsterCompleteEntity>) = withContext(dispatcher) {
override suspend fun insert(monsters: List<MonsterCompleteEntity>, deleteAll: Boolean) = withContext(dispatcher) {
monsterQueries.transaction {
if (deleteAll) {
monsterQueries.deleteAll()
deleteAllEntries(getMonstersByIsNotClone())
} else {
deleteAllEntries(monsters)
}
monsterQueries.insert(monsters.map { it.monster })
abilityScoreQueries.insert(monsters.mapAndReduce { abilityScores })
actionQueries.run {
Expand Down Expand Up @@ -149,64 +155,65 @@ internal class MonsterDaoImpl(
}
}

override suspend fun deleteAll() = withContext(dispatcher) {
monsterQueries.transaction {
abilityScoreQueries.deleteAll()
actionQueries.deleteAll()
conditionQueries.deleteAll()
damageResistanceQueries.deleteAll()
damageImmunityQueries.deleteAll()
damageVulnerabilityQueries.deleteAll()
damageDiceQueries.deleteAll()
savingThrowQueries.deleteAll()
skillQueries.deleteAll()
specialAbilityQueries.deleteAll()
speedQueries.deleteAll()
speedValueQueries.deleteAll()
monsterQueries.deleteAll()
reactionQueries.deleteAll()
spellcastingQueries.deleteAll()
spellcastingSpellUsageCrossRefQueries.deleteAll()
spellUsageQueries.deleteAll()
spellUsageCrossRefQueries.deleteAll()
legendaryActionQueries.deleteAll()
}
}

private fun <T> List<List<T>>.reduceList(): List<T> {
return this.reduceOrNull { acc, list -> acc + list } ?: emptyList()
private fun deleteAllEntries(monsters: List<MonsterCompleteEntity>) {
val monsterIndexes = monsters.map { it.monster.index }
val actionsIds = monsters.mapAndReduce { actions.map { it.action.id } }
val speedIds = monsters.mapNotNull { it.speed?.speed?.id }
val spellcastings = monsters.mapAndReduce { spellcastings }
val spellcastingIds = spellcastings.map { it.spellcasting.spellcastingId }
val spellUsageIds = spellcastings.mapAndReduce { usages.map { it.spellUsage.spellUsageId } }

abilityScoreQueries.deleteWithMonsterIndex(monsterIndexes)
actionQueries.deleteWithMonsterIndex(monsterIndexes)
conditionQueries.deleteWithMonsterIndex(monsterIndexes)
damageResistanceQueries.deleteWithMonsterIndex(monsterIndexes)
damageImmunityQueries.deleteWithMonsterIndex(monsterIndexes)
damageVulnerabilityQueries.deleteWithMonsterIndex(monsterIndexes)
damageDiceQueries.deleteWithActionId(actionsIds)
savingThrowQueries.deleteWithMonsterIndex(monsterIndexes)
skillQueries.deleteWithMonsterIndex(monsterIndexes)
specialAbilityQueries.deleteWithMonsterIndex(monsterIndexes)
speedQueries.deleteWithMonsterIndex(monsterIndexes)
speedValueQueries.deleteWithSpeedId(speedIds)
reactionQueries.deleteWithMonsterIndex(monsterIndexes)
spellcastingQueries.deleteWithMonsterIndex(monsterIndexes)
spellcastingSpellUsageCrossRefQueries.deleteWithSpellcastingId(spellcastingIds)
spellUsageQueries.deleteWithSpellcastingId(spellcastingIds)
spellUsageCrossRefQueries.deleteWithSpellUsageId(spellUsageIds)
legendaryActionQueries.deleteWithMonsterIndex(monsterIndexes)
}

private fun <T, R> List<T>.mapAndReduce(transform: T.() -> List<R>): List<R> {
return this.map(transform).reduceList()
private fun getMonstersByIsNotClone(): List<MonsterCompleteEntity> {
return monsterQueries.getMonstersThatIsNotCloned()
.executeAsList()
.queryMonsterCompleteEntities()
}

private suspend fun List<MonsterDatabaseEntity>.queryMonsterCompleteEntities(): List<MonsterCompleteEntity> =
private fun List<MonsterDatabaseEntity>.queryMonsterCompleteEntities(): List<MonsterCompleteEntity> =
map { monster ->
val speed = getSpeed(dispatcher, monster, speedQueries, speedValueQueries)
val abilityScores = getAbilityScores(dispatcher, monster, abilityScoreQueries)
val actions = getActions(dispatcher, monster, actionQueries, damageDiceQueries)
val reactions = getReactions(dispatcher, monster, reactionQueries)
val specialAbilities = getSpecialAbilities(dispatcher, monster, specialAbilityQueries)
val speed = getSpeed(monster, speedQueries, speedValueQueries)
val abilityScores = getAbilityScores(monster, abilityScoreQueries)
val actions = getActions(monster, actionQueries, damageDiceQueries)
val reactions = getReactions(monster, reactionQueries)
val specialAbilities = getSpecialAbilities(monster, specialAbilityQueries)
val legendaryActions = getLegendaryActions(
dispatcher, monster, legendaryActionQueries, damageDiceQueries
monster, legendaryActionQueries, damageDiceQueries
)
val spellcastings = getSpellcastings(
dispatcher,
monster,
spellcastingQueries,
spellUsageQueries,
spellUsageCrossRefQueries
)
val savingThrows = getSavingThrows(dispatcher, monster, savingThrowQueries)
val skills = getSkills(dispatcher, monster, skillQueries)
val savingThrows = getSavingThrows(monster, savingThrowQueries)
val skills = getSkills(monster, skillQueries)
val damageVulnerabilities = getDamageVulnerabilities(
dispatcher, monster, damageVulnerabilityQueries
monster, damageVulnerabilityQueries
)
val damageResistances =
getDamageResistances(dispatcher, monster, damageResistanceQueries)
val damageImmunities = getDamageImmunities(dispatcher, monster, damageImmunityQueries)
val conditionImmunities = getConditionImmunities(dispatcher, monster, conditionQueries)
getDamageResistances(monster, damageResistanceQueries)
val damageImmunities = getDamageImmunities(monster, damageImmunityQueries)
val conditionImmunities = getConditionImmunities(monster, conditionQueries)

MonsterCompleteEntity(
monster = monster.toLocalEntity(),
Expand Down
Loading

0 comments on commit 61b281f

Please sign in to comment.