Skip to content

Commit

Permalink
Implement share content feature (#303)
Browse files Browse the repository at this point in the history
* Implement share content feature

* fix text field max width size constraint

* Remove name field from SharedMonsterLore

* Improve contentToImportShort

* Fix jvm migration and sync monster after imported creatures

* Fix jvm database migration when has no database
  • Loading branch information
alexandregpereira authored Jul 19, 2024
1 parent a790975 commit f305339
Show file tree
Hide file tree
Showing 89 changed files with 1,476 additions and 204 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ multiplatform {
implementation(project(":feature:folder-list:event"))
implementation(project(":feature:folder-preview:event"))
implementation(project(":feature:monster-content-manager:event"))
implementation(project(":feature:share-content:event"))
implementation(project(":domain:monster:event"))

implementation(project(":feature:folder-detail:compose"))
Expand All @@ -72,6 +73,7 @@ multiplatform {
implementation(project(":feature:monster-registration:compose"))
implementation(project(":feature:search:compose"))
implementation(project(":feature:settings:compose"))
implementation(project(":feature:share-content:compose"))
implementation(project(":feature:spell-compendium:compose"))
implementation(project(":feature:spell-detail:compose"))
implementation(project(":feature:sync:compose"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import br.alexandregpereira.hunter.app.BottomBarItemIcon.FOLDERS
import br.alexandregpereira.hunter.app.BottomBarItemIcon.SEARCH
import br.alexandregpereira.hunter.app.BottomBarItemIcon.SETTINGS
import br.alexandregpereira.hunter.app.MainViewEvent.BottomNavigationItemClick
import br.alexandregpereira.hunter.app.event.AppEventDispatcher
import br.alexandregpereira.hunter.event.folder.detail.FolderDetailResultListener
import br.alexandregpereira.hunter.event.folder.detail.collectOnVisibilityChanges
import br.alexandregpereira.hunter.event.folder.list.FolderListResultListener
Expand Down Expand Up @@ -37,6 +38,7 @@ internal class MainViewModel(
private val bottomBarEventManager: BottomBarEventManager,
private val appLocalization: AppReactiveLocalization,
private val stateRecovery: StateRecovery,
appEventDispatcher: AppEventDispatcher,
) : UiModel<MainViewState>(MainViewState()) {

init {
Expand All @@ -47,6 +49,7 @@ internal class MainViewModel(
observeFolderListResults()
observeMonsterContentManagerEvents()
observeLanguageChanges()
appEventDispatcher.observeEvents()
}

private fun observeLanguageChanges() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package br.alexandregpereira.hunter.app.di

import br.alexandregpereira.hunter.analytics.di.analyticsModule
import br.alexandregpereira.hunter.app.MainViewModel
import br.alexandregpereira.hunter.app.event.appEventModule
import br.alexandregpereira.hunter.data.di.dataModules
import br.alexandregpereira.hunter.detail.di.featureMonsterDetailModule
import br.alexandregpereira.hunter.domain.di.domainModules
Expand All @@ -19,6 +20,7 @@ import br.alexandregpereira.hunter.monster.lore.detail.di.featureMonsterLoreDeta
import br.alexandregpereira.hunter.monster.registration.di.featureMonsterRegistrationModule
import br.alexandregpereira.hunter.search.di.featureSearchModule
import br.alexandregpereira.hunter.settings.di.featureSettingsModule
import br.alexandregpereira.hunter.shareContent.featureShareContentModule
import br.alexandregpereira.hunter.spell.compendium.di.featureSpellCompendiumModule
import br.alexandregpereira.hunter.spell.detail.di.featureSpellDetailModule
import br.alexandregpereira.hunter.sync.di.featureSyncModule
Expand Down Expand Up @@ -48,12 +50,14 @@ internal fun KoinApplication.initKoinModules() {
featureSettingsModule,
featureSpellCompendiumModule,
featureSpellDetailModule,
featureShareContentModule,
)
modules(
analyticsModule,
bottomBarEventModule,
localizationModule,
monsterEventModule,
appEventModule,
)
}

Expand All @@ -74,6 +78,7 @@ private val appModule = module {
bottomBarEventManager = get(),
appLocalization = get(),
stateRecovery = get(named(AppStateRecoveryQualifier)),
appEventDispatcher = get(),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package br.alexandregpereira.hunter.app.event

import br.alexadregpereira.hunter.shareContent.event.ShareContentEvent
import br.alexadregpereira.hunter.shareContent.event.ShareContentEventDispatcher
import br.alexandregpereira.hunter.monster.event.MonsterEvent
import br.alexandregpereira.hunter.monster.event.MonsterEventDispatcher
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

interface AppEventDispatcher {
fun observeEvents()
}

internal class AppEventDispatcherImpl(
private val shareContentEventDispatcher: ShareContentEventDispatcher,
private val monsterEventDispatcher: MonsterEventDispatcher,
) : AppEventDispatcher {

private val scope = MainScope()

override fun observeEvents() {
observeShareContentEvents()
}

private fun observeShareContentEvents() {
shareContentEventDispatcher.events.filterIsInstance<ShareContentEvent.Import.OnFinish>()
.onEach {
monsterEventDispatcher.dispatchEvent(MonsterEvent.OnCompendiumChanges)
}.launchIn(scope)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package br.alexandregpereira.hunter.app.event

import org.koin.dsl.module

val appEventModule = module {
single<AppEventDispatcher> { AppEventDispatcherImpl(get(), get()) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ 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.shareContent.ShareContentExportMonsterFeature
import br.alexandregpereira.hunter.shareContent.ShareContentImportFeature
import br.alexandregpereira.hunter.spell.compendium.SpellCompendiumFeature
import br.alexandregpereira.hunter.spell.detail.SpellDetailFeature
import br.alexandregpereira.hunter.sync.SyncFeature
Expand Down Expand Up @@ -80,6 +82,10 @@ internal fun AppMainScreen(

FolderInsertFeature(contentPadding = contentPadding)

ShareContentExportMonsterFeature(contentPadding = contentPadding)

ShareContentImportFeature(contentPadding = contentPadding)

SyncFeature()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package br.alexandregpereira.hunter.event.v2

import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow

interface EventDispatcher<Event> : EventListener<Event> {

fun dispatchEvent(event: Event)
}

interface EventListener<Event> {

val events: Flow<Event>
}

fun <Event> EventDispatcher(): EventDispatcher<Event> = DefaultEventManager()

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

private val _events: MutableSharedFlow<Event> = MutableSharedFlow(
extraBufferCapacity = 10,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
override val events: Flow<Event> = _events

override fun dispatchEvent(event: Event) {
_events.tryEmit(event)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ internal fun MonsterEntity.toDatabaseEntity(): MonsterDatabaseEntity {
MonsterEntityStatus.Original -> 0L
MonsterEntityStatus.Clone -> 1L
MonsterEntityStatus.Edited -> 2L
MonsterEntityStatus.Imported -> 3L
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ internal class SpellDaoImpl(
damageType = it.damageType,
school = it.school,
description = it.description,
higherLevel = it.higherLevel
higherLevel = it.higherLevel,
status = it.status.toLong(),
)
)
}
Expand Down Expand Up @@ -86,7 +87,8 @@ internal class SpellDaoImpl(
damageType = damageType,
school = school,
description = description,
higherLevel = higherLevel
higherLevel = higherLevel,
status = status.toInt(),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ getMonsters:
SELECT * FROM MonsterEntity;

getMonstersEdited:
SELECT * FROM MonsterEntity WHERE isClone == 2;
SELECT * FROM MonsterEntity WHERE isClone > 0;
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
CREATE TABLE IF NOT EXISTS SpellEntity (`spellIndex` TEXT NOT NULL, `name` TEXT NOT NULL, `level` INTEGER NOT NULL, `castingTime` TEXT NOT NULL, `components` TEXT NOT NULL, `duration` TEXT NOT NULL, `range` TEXT NOT NULL, `ritual` INTEGER NOT NULL, `concentration` INTEGER NOT NULL, `savingThrowType` TEXT, `damageType` TEXT, `school` TEXT NOT NULL, `description` TEXT NOT NULL, `higherLevel` TEXT, PRIMARY KEY(`spellIndex`));
CREATE TABLE IF NOT EXISTS SpellEntity (`spellIndex` TEXT NOT NULL, `name` TEXT NOT NULL, `level` INTEGER NOT NULL, `castingTime` TEXT NOT NULL, `components` TEXT NOT NULL, `duration` TEXT NOT NULL, `range` TEXT NOT NULL, `ritual` INTEGER NOT NULL, `concentration` INTEGER NOT NULL, `savingThrowType` TEXT, `damageType` TEXT, `school` TEXT NOT NULL, `description` TEXT NOT NULL, `higherLevel` TEXT, `status` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`spellIndex`));

insert:
INSERT OR REPLACE INTO SpellEntity VALUES ?;

deleteAll:
DELETE FROM SpellEntity;
DELETE FROM SpellEntity WHERE status == 0;

getSpell:
SELECT * FROM SpellEntity WHERE spellIndex == ?;
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE SpellEntity ADD COLUMN status INTEGER NOT NULL DEFAULT 0;
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,49 @@ import org.koin.core.scope.Scope
import java.io.File

internal actual fun Scope.createSqlDriver(): SqlDriver {
val databaseFile = getDatabaseFile()
val driver: SqlDriver = JdbcSqliteDriver("jdbc:sqlite:${databaseFile.absolutePath}")

if (databaseFile.exists()) {
val currentVersion = driver.getDatabaseVersion()
val schemaVersion = Database.Schema.version
if (schemaVersion > currentVersion) {
Database.Schema.migrate(driver, currentVersion, schemaVersion)
driver.setDatabaseVersion(schemaVersion)
println("init: migrated from $currentVersion to $schemaVersion")
} else {
Database.Schema.create(driver)
}
} else {
driver.setDatabaseVersion(Database.Schema.version)
Database.Schema.create(driver)
}

return driver
}

private fun getDatabaseFile(): File {
val userFolder = System.getProperty("user.home")
val appDataFolder = File(userFolder, ".monster-compendium")
if (appDataFolder.exists().not()) {
appDataFolder.mkdirs()
}
val databasePath = File(appDataFolder, "hunter-database.db")
val driver: SqlDriver = JdbcSqliteDriver("jdbc:sqlite:${databasePath.absolutePath}")
Database.Schema.create(driver)
return driver
return File(appDataFolder, "hunter-database.db")
}

private fun SqlDriver.getDatabaseVersion(): Int {
val sqlCursor = this.executeQuery(
identifier = null,
sql = "PRAGMA user_version;",
parameters = 0,
binders = null
)

val initialDatabaseVersion = 25
return sqlCursor.getLong(0)?.toInt()
?.takeUnless { it == 0 } ?: initialDatabaseVersion
}

private fun SqlDriver.setDatabaseVersion(version: Int) {
this.execute(null, "PRAGMA user_version = $version;", 0, null)
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
package br.alexandregpereira.hunter.data.monster.lore.local.dao

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.MonsterLoreEntryEntity

interface MonsterLoreDao {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@

package br.alexandregpereira.hunter.domain.model

import kotlin.native.ObjCName

@ObjCName(name = "AbilityDescription", exact = true)
data class AbilityDescription(
val name: String,
val description: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,12 @@

package br.alexandregpereira.hunter.domain.model

import kotlin.native.ObjCName

@ObjCName(name = "AbilityScore", exact = true)
data class AbilityScore(
val type: AbilityScoreType,
val value: Int,
val modifier: Int
)

@ObjCName(name = "AbilityScoreType", exact = true)
enum class AbilityScoreType {
STRENGTH,
DEXTERITY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,14 @@
package br.alexandregpereira.hunter.domain.model

import br.alexandregpereira.hunter.uuid.generateUUID
import kotlin.native.ObjCName

@ObjCName(name = "Action", exact = true)
data class Action(
val id: String,
val damageDices: List<DamageDice>,
val attackBonus: Int?,
val abilityDescription: AbilityDescription
)

@ObjCName(name = "DamageDice", exact = true)
data class DamageDice(
val dice: String,
val damage: Damage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@
package br.alexandregpereira.hunter.domain.model

import br.alexandregpereira.hunter.uuid.generateUUID
import kotlin.native.ObjCName

@ObjCName(name = "Condition", exact = true)
data class Condition(
val index: String,
val type: ConditionType,
Expand All @@ -39,7 +37,6 @@ data class Condition(
}
}

@ObjCName(name = "ConditionType", exact = true)
enum class ConditionType {
BLINDED,
CHARMED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,12 @@

package br.alexandregpereira.hunter.domain.model

import kotlin.native.ObjCName

@ObjCName(name = "Damage", exact = true)
data class Damage(
val index: String,
val type: DamageType,
val name: String
)

@ObjCName(name = "DamageType", exact = true)
enum class DamageType {
ACID,
BLUDGEONING,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ data class Monster(
}

enum class MonsterStatus {
Original, Edited, Clone
Original, Edited, Clone, Imported
}

private fun Float.getChallengeRatingFormatted(): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@

package br.alexandregpereira.hunter.domain.model

import kotlin.native.ObjCName

@ObjCName(name = "MonsterType", exact = true)
enum class MonsterType {
ABERRATION,
BEAST,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@
package br.alexandregpereira.hunter.domain.model

import br.alexandregpereira.hunter.uuid.generateUUID
import kotlin.native.ObjCName

@ObjCName(name = "SavingThrow", exact = true)
data class SavingThrow(
val index: String,
val modifier: Int,
Expand Down
Loading

0 comments on commit f305339

Please sign in to comment.