Skip to content

Commit

Permalink
Add image form on monster registration (#294)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandregpereira authored Jul 12, 2024
1 parent 0c58b20 commit 1234fbe
Show file tree
Hide file tree
Showing 13 changed files with 275 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ plugins {
}

multiplatform {
androidMain {
implementation(libs.viewmodel.savedstate)
}
androidMain()

commonMain {
implementation(project(":core:analytics"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import br.alexandregpereira.hunter.monster.registration.ui.form.MonsterActionsFo
import br.alexandregpereira.hunter.monster.registration.ui.form.MonsterConditionsForm
import br.alexandregpereira.hunter.monster.registration.ui.form.MonsterDamagesForm
import br.alexandregpereira.hunter.monster.registration.ui.form.MonsterHeaderForm
import br.alexandregpereira.hunter.monster.registration.ui.form.MonsterImageForm
import br.alexandregpereira.hunter.monster.registration.ui.form.MonsterProficiencyForm
import br.alexandregpereira.hunter.monster.registration.ui.form.MonsterSavingThrowsForm
import br.alexandregpereira.hunter.monster.registration.ui.form.MonsterSpeedValuesForm
Expand Down Expand Up @@ -119,6 +120,11 @@ private fun MonsterRegistrationForm(
infoState = monster.info,
onMonsterChanged = { intent.onMonsterChanged(monster.copy(info = it)) },
)
MonsterImageForm(
keys = monster.keys,
infoState = monster.info,
onMonsterChanged = { intent.onMonsterChanged(monster.copy(info = it)) },
)
MonsterStatsForm(
keys = monster.keys,
stats = monster.stats,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package br.alexandregpereira.hunter.monster.registration.ui.form

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.lazy.LazyListScope
import br.alexandregpereira.hunter.monster.registration.MonsterInfoState
import br.alexandregpereira.hunter.monster.registration.ui.strings
Expand Down Expand Up @@ -40,32 +39,6 @@ internal fun LazyListScope.MonsterHeaderForm(
}
)
}
formItem(key = keys.next()) {
AppTextField(
text = infoState.imageUrl,
label = strings.imageUrl,
onValueChange = {
onMonsterChanged(infoState.copy(imageUrl = it))
}
)
}
formItem(key = keys.next()) {
val isDarkTheme = isSystemInDarkTheme()
val color = if (isDarkTheme) infoState.backgroundColorDark else {
infoState.backgroundColorLight
}
AppTextField(
text = color,
label = strings.imageBackgroundColor,
onValueChange = {
onMonsterChanged(
if (isDarkTheme) infoState.copy(backgroundColorDark = it) else {
infoState.copy(backgroundColorLight = it)
}
)
}
)
}
formItem(key = keys.next()) {
PickerField(
value = infoState.type,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package br.alexandregpereira.hunter.monster.registration.ui.form

import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment.Companion.TopCenter
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import br.alexandregpereira.hunter.monster.registration.MonsterInfoState
import br.alexandregpereira.hunter.monster.registration.ui.strings
import br.alexandregpereira.hunter.ui.compose.AppSwitch
import br.alexandregpereira.hunter.ui.compose.AppTextField
import br.alexandregpereira.hunter.ui.compose.Form
import br.alexandregpereira.hunter.ui.compose.MonsterCoilImage
import br.alexandregpereira.hunter.ui.util.toColor

@Suppress("FunctionName")
internal fun LazyListScope.MonsterImageForm(
keys: Iterator<String>,
infoState: MonsterInfoState,
onMonsterChanged: (MonsterInfoState) -> Unit = {}
) {
FormLazy(
titleKey = keys.next(),
title = { strings.imageFormTitle }
) {
formItem(key = keys.next()) {
Form {
val isDarkTheme = isSystemInDarkTheme()
var isDarkThemeMutable by remember { mutableStateOf(isDarkTheme) }
val darkColor =
remember(infoState.backgroundColorDark) { infoState.backgroundColorDark.toColor() }
val lightColor =
remember(infoState.backgroundColorLight) { infoState.backgroundColorLight.toColor() }
val color by animateColorAsState(
targetValue = if (isDarkThemeMutable) darkColor else {
lightColor
}
)
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = TopCenter
) {
val imageWidth by animateDpAsState(targetValue = if (infoState.isImageHorizontal) 360.dp else 270.dp)
val imageHeight by animateDpAsState(targetValue = if (infoState.isImageHorizontal) 270.dp else 360.dp)
MonsterCoilImage(
imageUrl = infoState.imageUrl,
contentDescription = infoState.name,
backgroundColor = color,
shape = RoundedCornerShape(16.dp),
modifier = Modifier.height(imageHeight).width(imageWidth),
)
}
AppSwitch(
checked = isDarkThemeMutable,
description = strings.darkThemeSwitchLabel,
onCheckedChange = {
isDarkThemeMutable = it
},
modifier = Modifier,
)
AppSwitch(
checked = infoState.isImageHorizontal,
description = strings.imageHorizontalSwitchLabel,
onCheckedChange = {
onMonsterChanged(infoState.copy(isImageHorizontal = it))
},
modifier = Modifier,
)
}
}
formItem(key = keys.next()) {
AppTextField(
text = infoState.imageUrl,
label = strings.imageUrl,
onValueChange = {
onMonsterChanged(infoState.copy(imageUrl = it))
}
)
}
formItem(key = keys.next()) {
AppTextField(
text = infoState.backgroundColorLight,
label = strings.imageBackgroundColorLight,
onValueChange = {
onMonsterChanged(
infoState.copy(
backgroundColorLight = it,
)
)
}
)
}
formItem(key = keys.next()) {
AppTextField(
text = infoState.backgroundColorDark,
label = strings.imageBackgroundColorDark,
onValueChange = {
onMonsterChanged(
infoState.copy(
backgroundColorDark = it
)
)
}
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ data class MonsterInfoState(
val imageUrl: String= "",
val backgroundColorLight: String = "",
val backgroundColorDark: String = "",
val isImageHorizontal: Boolean = false,
val typeIndex: Int = 0,
val typeOptions: List<String> = emptyList(),
) {
Expand Down Expand Up @@ -191,6 +192,7 @@ data class SpellPreviewState(

internal enum class SectionTitle {
Header,
Image,
Stats,
Speed,
AbilityScores,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ interface MonsterRegistrationStrings {
val specialAbilities: String
val reactions: String
val legendaryActions: String
val imageBackgroundColor: String
val imageBackgroundColorLight: String
val imageBackgroundColorDark: String
val speedTypeWalk: String
val speedTypeFly: String
val speedTypeSwim: String
Expand All @@ -109,6 +110,9 @@ interface MonsterRegistrationStrings {
val removeReaction: String
val add: String
val remove: String
val imageFormTitle: String
val imageHorizontalSwitchLabel: String
val darkThemeSwitchLabel: String
}

internal data class MonsterRegistrationEnStrings(
Expand Down Expand Up @@ -190,7 +194,8 @@ internal data class MonsterRegistrationEnStrings(
override val specialAbilities: String = "Special Abilities",
override val reactions: String = "Reactions",
override val legendaryActions: String = "Legendary Actions",
override val imageBackgroundColor: String = "Image Background Color",
override val imageBackgroundColorLight: String = "Background Color Light",
override val imageBackgroundColorDark: String = "Background Color Dark",
override val speedTypeWalk: String = "Speed",
override val speedTypeFly: String = "Fly",
override val speedTypeSwim: String = "Swim",
Expand All @@ -217,6 +222,9 @@ internal data class MonsterRegistrationEnStrings(
override val removeReaction: String = "Remove reaction",
override val add: String = "Add",
override val remove: String = "Remove",
override val imageFormTitle: String = "Image",
override val imageHorizontalSwitchLabel: String = "Horizontal Image",
override val darkThemeSwitchLabel: String = "Dark Theme",
) : MonsterRegistrationStrings

internal data class MonsterRegistrationPtStrings(
Expand Down Expand Up @@ -298,7 +306,8 @@ internal data class MonsterRegistrationPtStrings(
override val specialAbilities: String = "Habilidades Especiais",
override val reactions: String = "Reações",
override val legendaryActions: String = "Ações Lendárias",
override val imageBackgroundColor: String = "Cor de Fundo da Imagem",
override val imageBackgroundColorLight: String = "Cor de Fundo Light",
override val imageBackgroundColorDark: String = "Cor de Fundo Dark",
override val speedTypeWalk: String = "Deslocamento",
override val speedTypeFly: String = "Voo",
override val speedTypeSwim: String = "Natação",
Expand All @@ -325,6 +334,9 @@ internal data class MonsterRegistrationPtStrings(
override val removeReaction: String = "Remover reação",
override val add: String = "Adicionar",
override val remove: String = "Remover",
override val imageFormTitle: String = "Imagem",
override val imageHorizontalSwitchLabel: String = "Imagem Horizontal",
override val darkThemeSwitchLabel: String = "Tema Escuro",
) : MonsterRegistrationStrings

fun MonsterRegistrationStrings(): MonsterRegistrationStrings = MonsterRegistrationEnStrings()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ internal fun Monster.editBy(
backgroundColor = monster.imageData.backgroundColor.copy(
light = state.info.backgroundColorLight,
dark = state.info.backgroundColorDark
)
),
isHorizontal = state.info.isImageHorizontal,
),
type = MonsterType.entries[state.info.typeIndex],
stats = monster.stats.copy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ internal fun Metadata.asState(strings: MonsterRegistrationStrings): MonsterState
imageUrl = monster.imageData.url,
backgroundColorLight = monster.imageData.backgroundColor.light,
backgroundColorDark = monster.imageData.backgroundColor.dark,
isImageHorizontal = monster.imageData.isHorizontal,
typeIndex = MonsterType.entries.indexOf(monster.type),
typeOptions = MonsterType.entries.map { it.name(strings) },
),
Expand Down Expand Up @@ -319,6 +320,7 @@ private fun SpellcastingType.name(strings: MonsterRegistrationStrings): String {
internal fun SectionTitle.name(strings: MonsterRegistrationStrings): String {
return when (this) {
SectionTitle.Header -> strings.edit
SectionTitle.Image -> strings.imageFormTitle
SectionTitle.Stats -> strings.stats
SectionTitle.Speed -> strings.speed
SectionTitle.AbilityScores -> strings.abilityScores
Expand All @@ -345,9 +347,12 @@ private fun MonsterState.createKeys(): List<String> {
add("monsterHeader-name")
add("monsterHeader-subtitle")
add("monsterHeader-group")
add("monsterHeader-imageUrl")
add("monsterHeader-imageBackgroundColor")
add("monsterHeader-type")
add(SectionTitle.Image.name)
add("monsterHeader-image")
add("monsterHeader-image-url")
add("monsterHeader-image-color-light")
add("monsterHeader-image-color-dark")
add(SectionTitle.Stats.name)
add("stats-armorClass")
add("stats-hitPoints")
Expand Down
15 changes: 0 additions & 15 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ coil3 = "3.0.0-alpha06"
compose_activity = '1.9.0'
compose = '1.6.8'
compose_plugin = '1.6.11'
compose_material = '1.6.8'
core_ktx = '1.13.1'
espresso_core = "3.6.1"
firebase_bom = "33.1.1"
Expand All @@ -22,11 +21,9 @@ kotlin_coroutines = '1.8.1'
kotlin_datetime = "0.6.0"
kotlinx_serialization = "1.6.3"
ktor = "2.3.12"
lifecycle = "2.8.3"
material = "1.12.0"
mockk = "1.13.9"
multiplatform-settings = "1.1.1"
okhttp3_logging_interceptor = "4.12.0"
sqldelight = "1.5.5"

[libraries]
Expand All @@ -36,14 +33,9 @@ coil-compose-core = { module = "io.coil-kt.coil3:coil-compose-core", version.ref
coil-network-ktor = { module = "io.coil-kt.coil3:coil-network-ktor", version.ref = "coil3" }
coil-mp = { module = "io.coil-kt.coil3:coil", version.ref = "coil3" }
compose-activity = { module = "androidx.activity:activity-compose", version.ref = "compose_activity" }
compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "compose" }
compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
compose-ui-test = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose" }
compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "compose" }
compose-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" }
compose-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" }
compose-util = { module = "androidx.compose.ui:ui-util", version.ref = "compose" }
compose-material = { module = "androidx.compose.material:material", version.ref = "compose_material" }
core-ktx = { module = "androidx.core:core-ktx", version.ref = "core_ktx" }
core-testing = { module = "androidx.arch.core:core-testing", version.ref = "arch_core_testing" }
espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso_core" }
Expand All @@ -58,12 +50,10 @@ gradle-sqldelight = { module = "com.squareup.sqldelight:gradle-plugin", version.
junit-ext = { module = "androidx.test.ext:junit", version.ref = "junit_ext" }
junit = { module = "junit:junit", version.ref = "junit" }
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
koin-android-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koin" }
koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin_compose" }
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
koin-test = { module = "io.insert-koin:koin-test-junit4", version.ref = "koin" }
kotlin-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlin_coroutines_test" }
kotlin-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlin_coroutines" }
kotlin-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin_coroutines" }
kotlin-coroutines-desktop = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlin_coroutines" }
kotlin-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlin_datetime" }
Expand All @@ -77,18 +67,13 @@ ktor-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
material = { module = "com.google.android.material:material", version.ref = "material" }
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
multiplatform-settings = { module = "com.russhwolf:multiplatform-settings", version.ref = "multiplatform-settings" }
logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp3_logging_interceptor" }
sqldelight-android = { module = "com.squareup.sqldelight:android-driver", version.ref = "sqldelight" }
sqldelight-ios = { module = "com.squareup.sqldelight:native-driver", version.ref = "sqldelight" }
sqldelight-jvm = { module = "com.squareup.sqldelight:sqlite-driver", version.ref = "sqldelight" }
viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
viewmodel-savedstate = { module = "androidx.lifecycle:lifecycle-viewmodel-savedstate", version.ref = "lifecycle" }

[bundles]
compose = ["compose-ui", "compose-tooling", "compose-tooling-preview", "compose-material", "compose-activity", "compose-util"]
instrumentedtest = ["espresso", "junit-ext"]
unittest = ["kotlin-coroutines-test", "core-testing", "mockk", "junit", "kotlin-test"]
viewmodel-bundle = ["viewmodel"]

[plugins]
compose = { id = "org.jetbrains.compose", version.ref = "compose_plugin" }
Expand Down
Loading

0 comments on commit 1234fbe

Please sign in to comment.