Skip to content

Commit

Permalink
Create spell compendium feature and edit spells (#239)
Browse files Browse the repository at this point in the history
* Create spell compendium feature and edit spells

* Adjust layout and implement spell search
  • Loading branch information
alexandregpereira authored Jan 23, 2024
1 parent ba86ff4 commit 75e63aa
Show file tree
Hide file tree
Showing 55 changed files with 1,104 additions and 29 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ dependencies {
implementation(project(":feature:sync:android"))
implementation(project(":feature:search:android"))
implementation(project(":feature:settings:android"))
implementation(project(":feature:spell-compendium:android"))
implementation(project(":feature:spell-detail:android"))
implementation(project(":ui:core"))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import br.alexandregpereira.hunter.monster.lore.detail.di.monsterLoreDetailModul
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.compendium.di.spellCompendiumModule
import br.alexandregpereira.hunter.spell.detail.di.spellDetailModule
import br.alexandregpereira.hunter.sync.di.syncModule
import com.google.firebase.analytics.ktx.analytics
Expand Down Expand Up @@ -83,7 +84,8 @@ class HunterApplication : Application() {
monsterContentManagerModule +
monsterContentPreviewModule +
syncModule +
monsterRegistrationModule
monsterRegistrationModule +
spellCompendiumModule
)
modules(
analyticsModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ 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.compendium.SpellCompendiumFeature
import br.alexandregpereira.hunter.spell.detail.SpellDetailFeature
import br.alexandregpereira.hunter.sync.SyncFeature

Expand Down Expand Up @@ -71,6 +72,10 @@ fun MainScreen(

MonsterRegistrationFeature(contentPadding = contentPadding)

SpellCompendiumFeature(
contentPadding = contentPadding,
)

SpellDetailFeature(
contentPadding = contentPadding,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ interface EventDispatcher<Event> {
fun dispatchEvent(event: Event)
}

interface EventResultDispatcher<Event, Result> {

fun dispatchEventResult(event: Event): Flow<Result>
}

interface EventListener<Event> {

val events: Flow<Event>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,7 @@ private class DefaultStateHolderWithRecovery<State>(
}
}
}

internal fun <State> StateHolder<State>.setState(block: State.() -> State) {
(this as MutableStateHolder).setState(block)
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ internal class SpellDaoImpl(
override suspend fun getSpells(
indexes: List<String>
): List<SpellEntity> = withContext(dispatcher) {
spellQueries.getSpells(indexes).executeAsList().map { it.asSpellEntity() }
spellQueries.getSpellsByIds(indexes).executeAsList().map { it.asSpellEntity() }
}

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

private fun SpellDatabaseEntity.asSpellEntity(): SpellEntity {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@ DELETE FROM SpellEntity;
getSpell:
SELECT * FROM SpellEntity WHERE spellIndex == ?;

getSpells:
getSpellsByIds:
SELECT * FROM SpellEntity WHERE spellIndex IN ?;

getSpells:
SELECT * FROM SpellEntity ORDER BY level ASC, name ASC;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package br.alexandregpereira.hunter.domain.locale

internal expect fun Int.formatToNumber(): String
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@

package br.alexandregpereira.hunter.domain.model

import br.alexandregpereira.hunter.domain.locale.formatToNumber
import br.alexandregpereira.hunter.domain.monster.spell.model.SchoolOfMagic
import br.alexandregpereira.hunter.domain.monster.spell.model.SpellPreview
import br.alexandregpereira.hunter.domain.monster.spell.model.SpellUsage
import br.alexandregpereira.hunter.domain.monster.spell.model.Spellcasting
import br.alexandregpereira.hunter.domain.monster.spell.model.SpellcastingType
import java.text.NumberFormat
import kotlin.native.ObjCName

@ObjCName(name = "Monster", exact = true)
Expand Down Expand Up @@ -116,7 +116,7 @@ fun Monster.xpFormatted(): String {
val xpString = when {
xp < 1000 -> xp.toString()
else -> {
val xpFormatted = NumberFormat.getIntegerInstance().format(xp)
val xpFormatted = xp.formatToNumber()
.dropLastWhile { it == '0' }
.let { if (it.last().isDigit().not()) it.dropLast(1) else it }
"${xpFormatted}k"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package br.alexandregpereira.hunter.domain.locale

internal actual fun Int.formatToNumber(): String {
return this.toString()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package br.alexandregpereira.hunter.domain.locale

import java.text.NumberFormat

internal actual fun Int.formatToNumber(): String {
return NumberFormat.getIntegerInstance().format(this)
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ interface SpellLocalRepository {
fun saveSpells(spells: List<Spell>): Flow<Unit>
fun getLocalSpell(index: String): Flow<Spell>
fun getLocalSpells(indexes: List<String>): Flow<List<Spell>>
fun getLocalSpells(): Flow<List<Spell>>
fun deleteLocalSpells(): Flow<Unit>
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ internal class DefaultSpellLocalRepository(
}
}

override fun getLocalSpells(): Flow<List<Spell>> {
return localDataSource.getSpells().map { spells ->
spells.map { it.toDomain() }
}
}

override fun deleteLocalSpells(): Flow<Unit> {
return localDataSource.deleteSpells()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ internal class DefaultSpellRepository(
return localRepository.getLocalSpells(indexes)
}

override fun getLocalSpells(): Flow<List<Spell>> {
return localRepository.getLocalSpells()
}

override fun deleteLocalSpells(): Flow<Unit> {
return localRepository.deleteLocalSpells()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,8 @@ internal class DefaultSpellLocalDataSource(
override fun getSpells(indexes: List<String>): Flow<List<SpellEntity>> = flow {
emit(spellDao.getSpells(indexes))
}

override fun getSpells(): Flow<List<SpellEntity>> = flow {
emit(spellDao.getSpells())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ internal interface SpellLocalDataSource {
fun getSpell(index: String): Flow<SpellEntity>
fun deleteSpells(): Flow<Unit>
fun getSpells(indexes: List<String>): Flow<List<SpellEntity>>
fun getSpells(): Flow<List<SpellEntity>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ interface SpellDao {
suspend fun deleteAll()

suspend fun getSpells(indexes: List<String>): List<SpellEntity>

suspend fun getSpells(): List<SpellEntity>
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ internal fun MonsterRegistrationForm(
item(key = "spells") {
MonsterSpellcastingsForm(
spellcastings = monster.spellcastings,
onChanged = { intent.onMonsterChanged(monster.copy(spellcastings = it)) },
onSpellClick = intent::onSpellClick,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ private fun MonsterRegistrationScreenPreview() {
}

override fun onSaved() {}

override fun onSpellClick(spellIndex: String) {}
}
MonsterRegistrationScreen(
state = state.value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import br.alexandregpereira.hunter.monster.registration.R
@Composable
internal fun AddButton(
modifier: Modifier = Modifier,
text: String = "",
onClick: () -> Unit = {},
) = Row(
verticalAlignment = Alignment.CenterVertically,
Expand All @@ -28,13 +29,16 @@ internal fun AddButton(
.fillMaxWidth()
.clickable { onClick() },
) {
val string = text.ifEmpty {
stringResource(R.string.monster_registration_add_new)
}
Icon(
painter = painterResource(R.drawable.ic_add),
contentDescription = stringResource(R.string.monster_registration_add_new),
contentDescription = string,
modifier = Modifier.padding(end = 8.dp),
)
Text(
text = stringResource(R.string.monster_registration_add_new),
text = string,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,116 @@
package br.alexandregpereira.hunter.monster.registration.ui.form

import androidx.compose.material.Text
import androidx.compose.foundation.clickable
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import br.alexandregpereira.hunter.domain.model.Action
import br.alexandregpereira.hunter.domain.monster.spell.model.SpellPreview
import br.alexandregpereira.hunter.domain.monster.spell.model.SpellUsage
import br.alexandregpereira.hunter.domain.monster.spell.model.Spellcasting
import br.alexandregpereira.hunter.domain.monster.spell.model.SpellcastingType
import br.alexandregpereira.hunter.monster.registration.R
import br.alexandregpereira.hunter.monster.registration.ui.changeAt
import br.alexandregpereira.hunter.ui.compose.AppTextField
import br.alexandregpereira.hunter.ui.compose.ClickableField
import br.alexandregpereira.hunter.ui.compose.Form
import br.alexandregpereira.hunter.ui.compose.PickerField

@Composable
internal fun MonsterSpellcastingsForm(
spellcastings: List<Spellcasting>,
modifier: Modifier = Modifier,
onChanged: (List<Action>) -> Unit = {}
onSpellClick: (String) -> Unit = {},
onChanged: (List<Spellcasting>) -> Unit = {}
) = Form(modifier, stringResource(R.string.monster_registration_spells)) {
Text(
text = stringResource(R.string.monster_registration_work_in_progress),
fontSize = 14.sp,
fontWeight = FontWeight.Light,
fontStyle = FontStyle.Italic,
)
if (spellcastings.isEmpty()) {
AddButton(text = stringResource(R.string.monster_registration_add_spellcasting_type))
return@Form
}
val newSpellcastings = spellcastings.toMutableList()
val options = SpellcastingType.entries
val optionStrings = SpellcastingType.entries.map { it.toState().getStringName() }

spellcastings.forEachIndexed { index, spellcasting ->
PickerField(
value = spellcasting.type.toState().getStringName(),
label = stringResource(R.string.monster_registration_spellcasting_type_label),
options = optionStrings,
onValueChange = { optionIndex ->
onChanged(newSpellcastings.changeAt(index) { copy(type = options[optionIndex]) })
}
)
AppTextField(
text = spellcasting.description,
label = stringResource(R.string.monster_registration_description),
multiline = true,
onValueChange = { newValue ->
onChanged(newSpellcastings.changeAt(index) { copy(description = newValue) })
}
)

MonsterSpellsUsageForm(
spellsUsage = spellcasting.usages,
onSpellClick = onSpellClick,
onChanged = { newSpellsUsage ->
onChanged(newSpellcastings.changeAt(index) { copy(usages = newSpellsUsage) })
}
)

AddButton(text = stringResource(R.string.monster_registration_add_spellcasting_type))
}
}

@Composable
internal fun MonsterSpellsUsageForm(
spellsUsage: List<SpellUsage>,
onSpellClick: (String) -> Unit = {},
onChanged: (List<SpellUsage>) -> Unit = {}
) {
val newSpellsUsage = spellsUsage.toMutableList()

AddButton(text = stringResource(R.string.monster_registration_add_spell_group))

spellsUsage.forEachIndexed { index, spellUsage ->
AppTextField(
text = spellUsage.group,
label = stringResource(R.string.monster_registration_spell_group),
onValueChange = { newValue ->
onChanged(newSpellsUsage.changeAt(index) { copy(group = newValue) })
}
)

MonsterSpellsForm(spells = spellUsage.spells, onSpellClick = onSpellClick)

AddButton(text = stringResource(R.string.monster_registration_add_spell_group))
}
}

@Composable
internal fun MonsterSpellsForm(
spells: List<SpellPreview>,
onSpellClick: (String) -> Unit = {}
) {
spells.forEach { spell ->
ClickableField(
text = spell.name,
label = stringResource(R.string.monster_registration_spell_label),
onClick = { onSpellClick(spell.index) },
)
}

AddButton(text = stringResource(R.string.monster_registration_add_spell))
}

private fun SpellcastingType.toState() = when (this) {
SpellcastingType.SPELLCASTER -> SpellcastingTypeState.SPELLCASTER
SpellcastingType.INNATE -> SpellcastingTypeState.INNATE
}

private enum class SpellcastingTypeState(val stringRes: Int) {
SPELLCASTER(R.string.monster_registration_spellcasting_caster_type),
INNATE(R.string.monster_registration_spellcasting_innate_type),
}

@Composable
private fun SpellcastingTypeState.getStringName() = stringResource(stringRes)

Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,13 @@
<string name="monster_registration_speed_type_swim">Natação</string>
<string name="monster_registration_speed_type_climb">Escalagem</string>
<string name="monster_registration_speed_type_burrow">Escavação</string>

<string name="monster_registration_spellcasting_caster_type">Conjuração</string>
<string name="monster_registration_spellcasting_innate_type">Conjuração Inata</string>
<string name="monster_registration_spellcasting_type_label">Tipo de Conjuração</string>
<string name="monster_registration_spell_group">Grupo de Magia</string>
<string name="monster_registration_spell_label">Magia</string>
<string name="monster_registration_add_spell_group">Adicionar grupo de magia</string>
<string name="monster_registration_add_spell">Adicionar magia</string>
<string name="monster_registration_add_spellcasting_type">Adicionar tipo de conjuração</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,13 @@
<string name="monster_registration_speed_type_swim">Swim</string>
<string name="monster_registration_speed_type_climb">Climb</string>
<string name="monster_registration_speed_type_burrow">Burrow</string>

<string name="monster_registration_spellcasting_caster_type">Spellcaster</string>
<string name="monster_registration_spellcasting_innate_type">Innate Spellcaster</string>
<string name="monster_registration_spellcasting_type_label">Spellcasting Type</string>
<string name="monster_registration_spell_group">Spell Group</string>
<string name="monster_registration_spell_label">Spell</string>
<string name="monster_registration_add_spell_group">Add spell group</string>
<string name="monster_registration_add_spell">Add spell</string>
<string name="monster_registration_add_spellcasting_type">Add spellcasting type</string>
</resources>
Loading

0 comments on commit 75e63aa

Please sign in to comment.