From dc2e13359cbf433c1f43d8f7f0324e180febce6f Mon Sep 17 00:00:00 2001 From: JoYehyun Date: Thu, 19 Sep 2024 11:39:52 +0900 Subject: [PATCH 01/17] =?UTF-8?q?chore:=20DataStore=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/gradle/libs.versions.toml | 2 ++ android/local/build.gradle.kts | 3 +++ 2 files changed, 5 insertions(+) diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index e470786ca..91fe7289d 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -31,6 +31,7 @@ coil = "2.6.0" glide = "4.14.2" ktlint = "12.1.0" splash-screen = "1.0.1" +datastore = "1.0.0" # Google & Firebase google-services = "4.4.2" @@ -81,6 +82,7 @@ room = { module = "androidx.room:room-runtime", version.ref = "room" } room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" } room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" } flexbox = { module = "com.google.android.flexbox:flexbox", version.ref = "flexbox" } +datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" } # Google & Firebase firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebase" } diff --git a/android/local/build.gradle.kts b/android/local/build.gradle.kts index 24d1001f9..50a307d85 100644 --- a/android/local/build.gradle.kts +++ b/android/local/build.gradle.kts @@ -2,6 +2,7 @@ plugins { alias(libs.plugins.kotlin.android) alias(libs.plugins.android.library) alias(libs.plugins.kotlin.kapt) + alias(libs.plugins.kotlinx.serialization) } android { @@ -46,12 +47,14 @@ android { dependencies { implementation(libs.kotlin.coroutines.android) + implementation(libs.kotlin.serialization.json) // third-party implementation(libs.timber) // room implementation(libs.room) implementation(libs.room.ktx) kapt(libs.room.compiler) + implementation(libs.datastore.preferences) // unit test testImplementation(libs.bundles.unit.test) testImplementation(libs.kotlin.test) From 0b292fa63424eeeec9fca324050d4a66f17f5583 Mon Sep 17 00:00:00 2001 From: JoYehyun Date: Thu, 19 Sep 2024 11:41:07 +0900 Subject: [PATCH 02/17] =?UTF-8?q?feat:=20Datastore=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/datasource/LocalBattleDataSource.kt | 35 +++++++++++ .../helper/data/model/PokemonWithSkillIds.kt | 9 +++ .../data/repository/BattleRepository.kt | 14 +++++ .../repository/DefaultBattleRepository.kt | 42 +++++++++++++- .../data/repository/DefaultDexRepository.kt | 7 +++ .../helper/data/repository/DexRepository.kt | 2 + .../rogue/helper/local/datastore/.gitkeep | 0 .../helper/local/datastore/BattleDataStore.kt | 58 +++++++++++++++++++ .../data/repository/FakeDexRepository.kt | 4 ++ 9 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt create mode 100644 android/data/src/main/java/poke/rogue/helper/data/model/PokemonWithSkillIds.kt delete mode 100644 android/local/src/main/java/poke/rogue/helper/local/datastore/.gitkeep create mode 100644 android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt diff --git a/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt b/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt new file mode 100644 index 000000000..3fdca4a50 --- /dev/null +++ b/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt @@ -0,0 +1,35 @@ +package poke.rogue.helper.data.datasource + +import android.content.Context +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import poke.rogue.helper.data.model.PokemonWithSkillIds +import poke.rogue.helper.data.model.toData +import poke.rogue.helper.local.datastore.BattleDataStore + +class LocalBattleDataSource(private val battleDataStore: BattleDataStore) { + suspend fun savePokemonWithSkill( + pokemonId: String, + skillId: String, + ) { + battleDataStore.savePokemonWithSkill(pokemonId, skillId) + } + + suspend fun savePokemon(pokemonId: String) = battleDataStore.savePokemon(pokemonId) + + fun pokemonWithSkill(): Flow = battleDataStore.pokemonWithSkillId().map { it?.toData() } + + fun pokemonId(): Flow = battleDataStore.pokemonId() + + companion object { + private var instance: LocalBattleDataSource? = null + + fun instance(context: Context): LocalBattleDataSource { + return instance ?: LocalBattleDataSource( + BattleDataStore(context), + ).also { + instance = it + } + } + } +} diff --git a/android/data/src/main/java/poke/rogue/helper/data/model/PokemonWithSkillIds.kt b/android/data/src/main/java/poke/rogue/helper/data/model/PokemonWithSkillIds.kt new file mode 100644 index 000000000..35b832dc1 --- /dev/null +++ b/android/data/src/main/java/poke/rogue/helper/data/model/PokemonWithSkillIds.kt @@ -0,0 +1,9 @@ +package poke.rogue.helper.data.model + +import poke.rogue.helper.local.datastore.SavedPokemonWithSkill + +data class PokemonWithSkillIds(val pokemonId: String, val skillId: String) + +fun SavedPokemonWithSkill.toData() = PokemonWithSkillIds(pokemonId, skillId) + +data class PokemonWithSkill(val pokemon: Pokemon, val skill: BattleSkill) diff --git a/android/data/src/main/java/poke/rogue/helper/data/repository/BattleRepository.kt b/android/data/src/main/java/poke/rogue/helper/data/repository/BattleRepository.kt index ba16f8fb0..b96a0824e 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/repository/BattleRepository.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/repository/BattleRepository.kt @@ -1,7 +1,10 @@ package poke.rogue.helper.data.repository +import kotlinx.coroutines.flow.Flow import poke.rogue.helper.data.model.BattlePrediction import poke.rogue.helper.data.model.BattleSkill +import poke.rogue.helper.data.model.Pokemon +import poke.rogue.helper.data.model.PokemonWithSkill import poke.rogue.helper.data.model.Weather interface BattleRepository { @@ -15,4 +18,15 @@ interface BattleRepository { mySkillId: String, opponentPokemonId: String, ): BattlePrediction + + suspend fun savePokemon(pokemonId: String) + + suspend fun savePokemonWithSkill( + pokemonId: String, + skillId: String, + ) + + suspend fun savedPokemon(): Flow + + suspend fun savedPokemonWithSkill(): Flow } diff --git a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt index ef970ea1f..92476865e 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt @@ -1,11 +1,21 @@ package poke.rogue.helper.data.repository +import android.content.Context +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import poke.rogue.helper.data.datasource.LocalBattleDataSource import poke.rogue.helper.data.datasource.RemoteBattleDataSource import poke.rogue.helper.data.model.BattlePrediction import poke.rogue.helper.data.model.BattleSkill +import poke.rogue.helper.data.model.Pokemon +import poke.rogue.helper.data.model.PokemonWithSkill import poke.rogue.helper.data.model.Weather -class DefaultBattleRepository(private val remoteBattleDataSource: RemoteBattleDataSource) : BattleRepository { +class DefaultBattleRepository( + private val localBattleDataSource: LocalBattleDataSource, + private val remoteBattleDataSource: RemoteBattleDataSource, + private val pokemonRepository: DexRepository, +) : BattleRepository { override suspend fun weathers(): List = remoteBattleDataSource.weathers() override suspend fun availableSkills(dexNumber: Long): List = remoteBattleDataSource.availableSkills(dexNumber).distinct() @@ -23,11 +33,37 @@ class DefaultBattleRepository(private val remoteBattleDataSource: RemoteBattleDa opponentPokemonId = opponentPokemonId, ) + override suspend fun savePokemon(pokemonId: String) = localBattleDataSource.savePokemon(pokemonId) + + override suspend fun savePokemonWithSkill( + pokemonId: String, + skillId: String, + ) = localBattleDataSource.savePokemonWithSkill(pokemonId, skillId) + + override suspend fun savedPokemon(): Flow = + localBattleDataSource.pokemonId().map { it?.let { pokemonRepository.pokemon(it) } } + + override suspend fun savedPokemonWithSkill(): Flow = + localBattleDataSource.pokemonWithSkill().map { + it?.let { pokemonWithSkill -> + val pokemon = pokemonRepository.pokemon(pokemonWithSkill.pokemonId) + val skill = + availableSkills(pokemon.dexNumber).find { + it.id == pokemonWithSkill.skillId + } ?: error("아이디에 해당하는 스킬이 존재하지 않습니다. id: ${pokemonWithSkill.skillId}") + PokemonWithSkill(pokemon, skill) + } + } + companion object { private var instance: BattleRepository? = null - fun instance(): BattleRepository { - return instance ?: DefaultBattleRepository(RemoteBattleDataSource.instance()).also { + fun instance(context: Context): BattleRepository { + return instance ?: DefaultBattleRepository( + LocalBattleDataSource.instance(context), + RemoteBattleDataSource.instance(), + DefaultDexRepository.instance(), + ).also { instance = it } } diff --git a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultDexRepository.kt b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultDexRepository.kt index 047463c3a..73b708546 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultDexRepository.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultDexRepository.kt @@ -59,6 +59,13 @@ class DefaultDexRepository( } } + override suspend fun pokemon(id: String): Pokemon { + cachedPokemons.find { it.id == id }?.let { + return it + } + return pokemons().find { it.id == id } ?: error("아이디에 해당하는 포켓몬이 존재하지 않습니다. id : $id") + } + private suspend fun pokemonDetail( id: String, allBiomes: List, diff --git a/android/data/src/main/java/poke/rogue/helper/data/repository/DexRepository.kt b/android/data/src/main/java/poke/rogue/helper/data/repository/DexRepository.kt index 58e6bf3b7..d4010fa2f 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/repository/DexRepository.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/repository/DexRepository.kt @@ -17,4 +17,6 @@ interface DexRepository { ): List suspend fun pokemonDetail(id: String): PokemonDetail + + suspend fun pokemon(id: String): Pokemon } diff --git a/android/local/src/main/java/poke/rogue/helper/local/datastore/.gitkeep b/android/local/src/main/java/poke/rogue/helper/local/datastore/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt b/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt new file mode 100644 index 000000000..dc9e37fe4 --- /dev/null +++ b/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt @@ -0,0 +1,58 @@ +package poke.rogue.helper.local.datastore + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json + +@Serializable +data class SavedPokemonWithSkill(val pokemonId: String, val skillId: String) + +class BattleDataStore(private val context: Context) { + private val Context.dataStore: DataStore by preferencesDataStore(name = BATTLE_PREFERENCE_NAME) + + suspend fun savePokemonWithSkill( + pokemonId: String, + skillId: String, + ) { + val data = SavedPokemonWithSkill(pokemonId, skillId) + val saved = Json.encodeToString(data) + context.dataStore.edit { + it[POKEMON_WITH_SKILL_SELECTION_KEY] = saved + } + } + + suspend fun savePokemon(pokemonId: String) { + context.dataStore.edit { + it[POKEMON_SELECTION_KEY] = pokemonId + } + } + + fun pokemonWithSkillId(): Flow = + context.dataStore.data.map { preference -> + val data = preference[POKEMON_WITH_SKILL_SELECTION_KEY] + if (data != null) { + Json.decodeFromString(data) + } else { + null + } + } + + fun pokemonId(): Flow = + context.dataStore.data.map { preferences -> + preferences[POKEMON_SELECTION_KEY] + } + + private companion object { + const val BATTLE_PREFERENCE_NAME = "battle" + val POKEMON_WITH_SKILL_SELECTION_KEY = stringPreferencesKey("pokemon_with_skill_selection") + val POKEMON_SELECTION_KEY = stringPreferencesKey("pokemon_selection") + } +} diff --git a/android/testing/src/main/java/poke/rogue/helper/testing/data/repository/FakeDexRepository.kt b/android/testing/src/main/java/poke/rogue/helper/testing/data/repository/FakeDexRepository.kt index 03541e479..495d51a5d 100644 --- a/android/testing/src/main/java/poke/rogue/helper/testing/data/repository/FakeDexRepository.kt +++ b/android/testing/src/main/java/poke/rogue/helper/testing/data/repository/FakeDexRepository.kt @@ -51,6 +51,10 @@ class FakeDexRepository : DexRepository { weight = 6.9, ) + override suspend fun pokemon(id: String): Pokemon { + TODO("Not yet implemented") + } + private fun List.toFilteredPokemons( sort: PokemonSort, filters: List, From 4ed8df2bae4a37e2424093d80e5600955bbe28f2 Mon Sep 17 00:00:00 2001 From: JoYehyun Date: Thu, 19 Sep 2024 11:43:20 +0900 Subject: [PATCH 03/17] =?UTF-8?q?feat:=20=EC=A0=80=EC=9E=A5=EB=90=9C=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20UI=20?= =?UTF-8?q?=EC=97=90=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/battle/BattleActivity.kt | 2 +- .../battle/BattleSelectionsState.kt | 2 +- .../presentation/battle/BattleViewModel.kt | 74 ++++++++++++------- .../selection/skill/SkillSelectionFragment.kt | 2 +- 4 files changed, 51 insertions(+), 29 deletions(-) diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt index 8757a9b85..8d7c96ef6 100644 --- a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt @@ -22,7 +22,7 @@ import poke.rogue.helper.presentation.util.view.setImage class BattleActivity : ToolbarActivity(R.layout.activity_battle) { private val viewModel by viewModels { - BattleViewModel.factory(DefaultBattleRepository.instance()) + BattleViewModel.factory(DefaultBattleRepository.instance(this)) } private val weatherAdapter by lazy { WeatherSpinnerAdapter(this) diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleSelectionsState.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleSelectionsState.kt index a59fc9019..269062b86 100644 --- a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleSelectionsState.kt +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleSelectionsState.kt @@ -12,7 +12,7 @@ data class BattleSelectionsState( ) { val allSelected: Boolean get() = - minePokemon.isSelected() && skill.isSelected() && opponentPokemon.isSelected() + minePokemon.isSelected() && skill.isSelected() && opponentPokemon.isSelected() && weather.isSelected() companion object { val DEFAULT = diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt index c2c7e4c06..529c5d320 100644 --- a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -23,6 +24,7 @@ import poke.rogue.helper.presentation.battle.model.SelectionData import poke.rogue.helper.presentation.battle.model.SelectionMode import poke.rogue.helper.presentation.battle.model.SkillSelectionUiModel import poke.rogue.helper.presentation.battle.model.WeatherUiModel +import poke.rogue.helper.presentation.battle.model.toSelectionUi import poke.rogue.helper.presentation.battle.model.toUi class BattleViewModel( @@ -58,19 +60,23 @@ class BattleViewModel( init { initWeathers() + initSavedSelection() } private suspend fun fetchBattlePredictionResult(): BattlePredictionUiModel { with(selectedState.value) { - val weatherId = weather.selectedData()?.id - val myPokemonId = minePokemon.selectedData()?.id - val mySkillId = skill.selectedData()?.id - val opponentPokemonId = opponentPokemon.selectedData()?.id + val weatherId = requireNotNull(weather.selectedData()?.id) { "날씨는 null일 수 없습니다." } + val myPokemonId = + requireNotNull(minePokemon.selectedData()?.id) { "내 포켓몬은 null일 수 없습니다." } + val mySkillId = requireNotNull(skill.selectedData()?.id) { "내 스킬은 null일 수 없습니다." } + val opponentPokemonId = + requireNotNull(opponentPokemon.selectedData()?.id) { "상대 포켓몬은 null일 수 없습니다." } + return battleRepository.calculatedBattlePrediction( - weatherId = "$weatherId", - myPokemonId = "$myPokemonId", - mySkillId = "$mySkillId", - opponentPokemonId = "$opponentPokemonId", + weatherId = weatherId, + myPokemonId = myPokemonId, + mySkillId = mySkillId, + opponentPokemonId = opponentPokemonId, ).toUi() } } @@ -82,6 +88,21 @@ class BattleViewModel( } } + private fun initSavedSelection() { + viewModelScope.launch { + launch { + battleRepository.savedPokemon().first()?.let { + updateOpponentPokemon(it.toSelectionUi()) + } + } + launch { + battleRepository.savedPokemonWithSkill().first()?.let { (pokemon, skill) -> + updateMyPokemon(pokemon.toSelectionUi(), skill.toUi()) + } + } + } + } + fun updateWeather(newWeather: WeatherUiModel) { viewModelScope.launch { val selectedWeather = BattleSelectionUiState.Selected(newWeather) @@ -91,19 +112,24 @@ class BattleViewModel( fun updatePokemonSelection(selection: SelectionData) { when (selection) { - is SelectionData.WithSkill -> - { - updateMyPokemon( - selection.selectedPokemon, - selection.selectedSkill, - ) - logger.logPokemonSkillSelection(selection) + is SelectionData.WithSkill -> { + val (selectedPokemon, selectedSkill) = selection + updateMyPokemon(selectedPokemon, selectedSkill) + viewModelScope.launch { + battleRepository.savePokemonWithSkill(selectedPokemon.id, selectedSkill.id) } + logger.logPokemonSkillSelection(selection) + } is SelectionData.WithoutSkill -> { - updateOpponentPokemon(selection.selectedPokemon) + val selectedPokemon = selection.selectedPokemon + updateOpponentPokemon(selectedPokemon) + viewModelScope.launch { + battleRepository.savePokemon(selectedPokemon.id) + } logger.logBattlePokemonSelection(selection) } + is SelectionData.NoSelection -> {} } } @@ -112,19 +138,15 @@ class BattleViewModel( pokemon: PokemonSelectionUiModel, skill: SkillSelectionUiModel, ) { - viewModelScope.launch { - val selectedPokemon = BattleSelectionUiState.Selected(pokemon) - val selectedSkill = BattleSelectionUiState.Selected(skill) - _selectedState.value = - selectedState.value.copy(minePokemon = selectedPokemon, skill = selectedSkill) - } + val selectedPokemon = BattleSelectionUiState.Selected(pokemon) + val selectedSkill = BattleSelectionUiState.Selected(skill) + _selectedState.value = + selectedState.value.copy(minePokemon = selectedPokemon, skill = selectedSkill) } private fun updateOpponentPokemon(pokemon: PokemonSelectionUiModel) { - viewModelScope.launch { - val selectedPokemon = BattleSelectionUiState.Selected(pokemon) - _selectedState.value = selectedState.value.copy(opponentPokemon = selectedPokemon) - } + val selectedPokemon = BattleSelectionUiState.Selected(pokemon) + _selectedState.value = selectedState.value.copy(opponentPokemon = selectedPokemon) } override fun navigateToSelection(selectionMode: SelectionMode) { diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/selection/skill/SkillSelectionFragment.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/selection/skill/SkillSelectionFragment.kt index 96595326b..adff2cac8 100644 --- a/android/app/src/main/java/poke/rogue/helper/presentation/battle/selection/skill/SkillSelectionFragment.kt +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/selection/skill/SkillSelectionFragment.kt @@ -24,7 +24,7 @@ class SkillSelectionFragment : private val sharedViewModel: BattleSelectionViewModel by activityViewModels() private val viewModel: SkillSelectionViewModel by viewModels { SkillSelectionViewModel.factory( - DefaultBattleRepository.instance(), + DefaultBattleRepository.instance(requireContext()), sharedViewModel.previousSelection as? SelectionData.WithSkill, ) } From b385899443efe71b4afcb0c329099230194c46e6 Mon Sep 17 00:00:00 2001 From: JoYehyun Date: Thu, 19 Sep 2024 12:57:19 +0900 Subject: [PATCH 04/17] =?UTF-8?q?refactor:=20Skill=20=EC=BA=90=EC=8B=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/DefaultBattleRepository.kt | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt index 92476865e..35c46fbe9 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt @@ -16,9 +16,16 @@ class DefaultBattleRepository( private val remoteBattleDataSource: RemoteBattleDataSource, private val pokemonRepository: DexRepository, ) : BattleRepository { + private val cachedSkills: HashMap> = hashMapOf() + override suspend fun weathers(): List = remoteBattleDataSource.weathers() - override suspend fun availableSkills(dexNumber: Long): List = remoteBattleDataSource.availableSkills(dexNumber).distinct() + override suspend fun availableSkills(dexNumber: Long): List = + cachedSkills[dexNumber] ?: run { + val skills = remoteBattleDataSource.availableSkills(dexNumber).distinct() + cachedSkills[dexNumber] = skills + skills + } override suspend fun calculatedBattlePrediction( weatherId: String, @@ -47,14 +54,19 @@ class DefaultBattleRepository( localBattleDataSource.pokemonWithSkill().map { it?.let { pokemonWithSkill -> val pokemon = pokemonRepository.pokemon(pokemonWithSkill.pokemonId) - val skill = - availableSkills(pokemon.dexNumber).find { - it.id == pokemonWithSkill.skillId - } ?: error("아이디에 해당하는 스킬이 존재하지 않습니다. id: ${pokemonWithSkill.skillId}") + val skill = skill(pokemon.dexNumber, pokemonWithSkill.skillId) PokemonWithSkill(pokemon, skill) } } + private suspend fun skill( + dexNumber: Long, + skillId: String, + ): BattleSkill = + availableSkills(dexNumber).find { + it.id == skillId + } ?: error("아이디에 해당하는 스킬이 존재하지 않습니다. id: $skillId") + companion object { private var instance: BattleRepository? = null From d36c3b9d27d89ca233849d44ecb4636dc47ec42c Mon Sep 17 00:00:00 2001 From: JoYehyun Date: Thu, 19 Sep 2024 13:09:40 +0900 Subject: [PATCH 05/17] =?UTF-8?q?refactor:=20Test=20=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rogue/helper/testing/data/repository/FakeDexRepository.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/android/testing/src/main/java/poke/rogue/helper/testing/data/repository/FakeDexRepository.kt b/android/testing/src/main/java/poke/rogue/helper/testing/data/repository/FakeDexRepository.kt index 495d51a5d..a43e1352f 100644 --- a/android/testing/src/main/java/poke/rogue/helper/testing/data/repository/FakeDexRepository.kt +++ b/android/testing/src/main/java/poke/rogue/helper/testing/data/repository/FakeDexRepository.kt @@ -51,9 +51,7 @@ class FakeDexRepository : DexRepository { weight = 6.9, ) - override suspend fun pokemon(id: String): Pokemon { - TODO("Not yet implemented") - } + override suspend fun pokemon(id: String): Pokemon = pokemons().find { it.id == id } ?: error("존재하지 않는 포켓몬 ID : $id") private fun List.toFilteredPokemons( sort: PokemonSort, From 056118608301e43cd4faca5248d87e3af976a103 Mon Sep 17 00:00:00 2001 From: JoYehyun Date: Tue, 24 Sep 2024 15:41:38 +0900 Subject: [PATCH 06/17] =?UTF-8?q?refactor:=20context=20=EC=B0=B8=EC=A1=B0?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../battle/selection/skill/SkillSelectionFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/selection/skill/SkillSelectionFragment.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/selection/skill/SkillSelectionFragment.kt index adff2cac8..8e3ec276e 100644 --- a/android/app/src/main/java/poke/rogue/helper/presentation/battle/selection/skill/SkillSelectionFragment.kt +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/selection/skill/SkillSelectionFragment.kt @@ -24,7 +24,7 @@ class SkillSelectionFragment : private val sharedViewModel: BattleSelectionViewModel by activityViewModels() private val viewModel: SkillSelectionViewModel by viewModels { SkillSelectionViewModel.factory( - DefaultBattleRepository.instance(requireContext()), + DefaultBattleRepository.instance(requireContext().applicationContext), sharedViewModel.previousSelection as? SelectionData.WithSkill, ) } From cce9368bfb738826cca2a8d8293c3cc449e6e788 Mon Sep 17 00:00:00 2001 From: JoYehyun Date: Tue, 24 Sep 2024 15:41:59 +0900 Subject: [PATCH 07/17] =?UTF-8?q?refactor:=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rogue/helper/presentation/battle/BattleSelectionsState.kt | 2 +- .../rogue/helper/data/datasource/LocalBattleDataSource.kt | 4 +++- .../java/poke/rogue/helper/local/datastore/BattleDataStore.kt | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleSelectionsState.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleSelectionsState.kt index 269062b86..e6487da0c 100644 --- a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleSelectionsState.kt +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleSelectionsState.kt @@ -12,7 +12,7 @@ data class BattleSelectionsState( ) { val allSelected: Boolean get() = - minePokemon.isSelected() && skill.isSelected() && opponentPokemon.isSelected() && weather.isSelected() + listOf(minePokemon, skill, opponentPokemon, weather).all { it.isSelected() } companion object { val DEFAULT = diff --git a/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt b/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt index 3fdca4a50..957eeb4e6 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt @@ -15,7 +15,9 @@ class LocalBattleDataSource(private val battleDataStore: BattleDataStore) { battleDataStore.savePokemonWithSkill(pokemonId, skillId) } - suspend fun savePokemon(pokemonId: String) = battleDataStore.savePokemon(pokemonId) + suspend fun savePokemon(pokemonId: String) { + battleDataStore.savePokemon(pokemonId) + } fun pokemonWithSkill(): Flow = battleDataStore.pokemonWithSkillId().map { it?.toData() } diff --git a/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt b/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt index dc9e37fe4..92147fa07 100644 --- a/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt +++ b/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt @@ -1,3 +1,4 @@ + package poke.rogue.helper.local.datastore import android.content.Context From 8cec8bc84b9149a0107edf763ef94b0e55f5626b Mon Sep 17 00:00:00 2001 From: JoYehyun Date: Tue, 24 Sep 2024 16:05:56 +0900 Subject: [PATCH 08/17] =?UTF-8?q?refactor:=20=EC=95=84=EC=9D=B4=EB=94=94?= =?UTF-8?q?=20=EC=A0=80=EC=9E=A5=EA=B0=92=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../helper/local/datastore/BattleDataStore.kt | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt b/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt index 92147fa07..8d4b6e173 100644 --- a/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt +++ b/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt @@ -9,11 +9,7 @@ import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -import kotlinx.serialization.Serializable -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json -@Serializable data class SavedPokemonWithSkill(val pokemonId: String, val skillId: String) class BattleDataStore(private val context: Context) { @@ -23,37 +19,38 @@ class BattleDataStore(private val context: Context) { pokemonId: String, skillId: String, ) { - val data = SavedPokemonWithSkill(pokemonId, skillId) - val saved = Json.encodeToString(data) context.dataStore.edit { - it[POKEMON_WITH_SKILL_SELECTION_KEY] = saved + it[PAIR_POKEMON_SELECTION_KEY] = pokemonId + it[PAIR_SKILL_SELECTION_KEY] = skillId } } suspend fun savePokemon(pokemonId: String) { context.dataStore.edit { - it[POKEMON_SELECTION_KEY] = pokemonId + it[SINGLE_POKEMON_SELECTION_KEY] = pokemonId } } fun pokemonWithSkillId(): Flow = context.dataStore.data.map { preference -> - val data = preference[POKEMON_WITH_SKILL_SELECTION_KEY] - if (data != null) { - Json.decodeFromString(data) - } else { + val pokemonId = preference[PAIR_POKEMON_SELECTION_KEY] + val skillId = preference[PAIR_SKILL_SELECTION_KEY] + if (pokemonId == null || skillId == null) { null + } else { + SavedPokemonWithSkill(pokemonId, skillId) } } fun pokemonId(): Flow = context.dataStore.data.map { preferences -> - preferences[POKEMON_SELECTION_KEY] + preferences[SINGLE_POKEMON_SELECTION_KEY] } private companion object { const val BATTLE_PREFERENCE_NAME = "battle" - val POKEMON_WITH_SKILL_SELECTION_KEY = stringPreferencesKey("pokemon_with_skill_selection") - val POKEMON_SELECTION_KEY = stringPreferencesKey("pokemon_selection") + val PAIR_POKEMON_SELECTION_KEY = stringPreferencesKey("pair_pokemon_selection") + val PAIR_SKILL_SELECTION_KEY = stringPreferencesKey("pair_skill_selection") + val SINGLE_POKEMON_SELECTION_KEY = stringPreferencesKey("single_pokemon_selection") } } From 4a7b93f33783a5d4d161c823137408336d9578e9 Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 26 Sep 2024 01:09:46 +0900 Subject: [PATCH 09/17] =?UTF-8?q?feat:=20Spinner=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94=20=EC=8B=9C=200=EC=9C=BC=EB=A1=9C=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=20=EC=84=A0=ED=83=9D=EB=90=98=EB=8A=94=EA=B1=B0=20?= =?UTF-8?q?=EB=B0=A9=EC=A7=80=ED=95=98=EB=8A=94=20ItemSelectListener=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/WeatherItemSelectedListener.kt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 android/app/src/main/java/poke/rogue/helper/presentation/battle/view/WeatherItemSelectedListener.kt diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/view/WeatherItemSelectedListener.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/view/WeatherItemSelectedListener.kt new file mode 100644 index 000000000..fb1251982 --- /dev/null +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/view/WeatherItemSelectedListener.kt @@ -0,0 +1,27 @@ +package poke.rogue.helper.presentation.battle.view + +import android.view.View +import android.widget.AdapterView + +inline fun itemSelectListener(crossinline onSelected: (T) -> Unit): AdapterView.OnItemSelectedListener { + var isSpinnerInitialized = false + return object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long, + ) { + if (isSpinnerInitialized) { + val selectedData = parent?.getItemAtPosition(position) + val castedData = + requireNotNull(selectedData as? T) { "Selected data is not a ${T::class.simpleName}" } + onSelected(castedData) + } else { + isSpinnerInitialized = true + } + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } +} \ No newline at end of file From fc45fa2263010a3333a3e36653f1dc1553d128d7 Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 26 Sep 2024 01:10:20 +0900 Subject: [PATCH 10/17] =?UTF-8?q?feat:=20=EB=82=A0=EC=94=A8=EC=A0=80?= =?UTF-8?q?=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/battle/BattleActivity.kt | 29 ++++++++----------- .../presentation/battle/BattleViewModel.kt | 13 +++++++++ .../data/datasource/LocalBattleDataSource.kt | 6 ++++ .../data/repository/BattleRepository.kt | 4 +++ .../repository/DefaultBattleRepository.kt | 16 ++++++++-- .../helper/local/datastore/BattleDataStore.kt | 13 ++++++++- 6 files changed, 61 insertions(+), 20 deletions(-) diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt index 8d7c96ef6..9a656ef53 100644 --- a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt @@ -3,8 +3,6 @@ package poke.rogue.helper.presentation.battle import WeatherSpinnerAdapter import android.app.Activity import android.os.Bundle -import android.view.View -import android.widget.AdapterView import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.appcompat.widget.Toolbar @@ -15,10 +13,12 @@ import poke.rogue.helper.presentation.base.toolbar.ToolbarActivity import poke.rogue.helper.presentation.battle.model.SelectionData import poke.rogue.helper.presentation.battle.model.WeatherUiModel import poke.rogue.helper.presentation.battle.selection.BattleSelectionActivity +import poke.rogue.helper.presentation.battle.view.itemSelectListener import poke.rogue.helper.presentation.util.context.colorOf import poke.rogue.helper.presentation.util.parcelable import poke.rogue.helper.presentation.util.repeatOnStarted import poke.rogue.helper.presentation.util.view.setImage +import timber.log.Timber class BattleActivity : ToolbarActivity(R.layout.activity_battle) { private val viewModel by viewModels { @@ -57,21 +57,9 @@ class BattleActivity : ToolbarActivity(R.layout.activity_ private fun initSpinner() { binding.spinnerWeather.adapter = weatherAdapter - binding.spinnerWeather.onItemSelectedListener = - object : AdapterView.OnItemSelectedListener { - override fun onItemSelected( - parent: AdapterView<*>, - view: View?, - position: Int, - id: Long, - ) { - val selectedWeather = parent.getItemAtPosition(position) as WeatherUiModel - viewModel.updateWeather(selectedWeather) - } - - override fun onNothingSelected(parent: AdapterView<*>?) { - } - } + binding.spinnerWeather.onItemSelectedListener = itemSelectListener { + viewModel.updateWeather(it) + } } private fun initObserver() { @@ -80,6 +68,13 @@ class BattleActivity : ToolbarActivity(R.layout.activity_ weatherAdapter.updateWeathers(it) } } + repeatOnStarted { + viewModel.weatherPos + .collect { + Timber.tag("weatherPos").d("weatherPos: $it") + binding.spinnerWeather.setSelection(it) + } + } repeatOnStarted { viewModel.selectedState.collect { diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt index 529c5d320..57c37e22a 100644 --- a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt @@ -8,6 +8,8 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -37,6 +39,16 @@ class BattleViewModel( private val _selectedState = MutableStateFlow(BattleSelectionsState.DEFAULT) val selectedState = _selectedState.asStateFlow() + val weatherPos: StateFlow = combine( + battleRepository.selectedWeatherStream(), + weathers, + ) { weather, weathers -> + if (weather == null || weathers.isEmpty()) return@combine null + if (weathers.any { it.id == weather.id }.not()) return@combine null + weathers.indexOfFirst { it.id == weather.id } + }.filterNotNull() + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 0) + private val _navigateToSelection = MutableSharedFlow() val navigateToSelection = _navigateToSelection.asSharedFlow() @@ -107,6 +119,7 @@ class BattleViewModel( viewModelScope.launch { val selectedWeather = BattleSelectionUiState.Selected(newWeather) _selectedState.value = selectedState.value.copy(weather = selectedWeather) + battleRepository.saveWeather(newWeather.id) } } diff --git a/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt b/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt index 957eeb4e6..6de6ab9bc 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt @@ -19,6 +19,12 @@ class LocalBattleDataSource(private val battleDataStore: BattleDataStore) { battleDataStore.savePokemon(pokemonId) } + suspend fun saveWeather(weatherId: String) { + battleDataStore.saveWeather(weatherId) + } + + fun weatherId(): Flow = battleDataStore.weatherId() + fun pokemonWithSkill(): Flow = battleDataStore.pokemonWithSkillId().map { it?.toData() } fun pokemonId(): Flow = battleDataStore.pokemonId() diff --git a/android/data/src/main/java/poke/rogue/helper/data/repository/BattleRepository.kt b/android/data/src/main/java/poke/rogue/helper/data/repository/BattleRepository.kt index b96a0824e..e2e2548c5 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/repository/BattleRepository.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/repository/BattleRepository.kt @@ -26,6 +26,10 @@ interface BattleRepository { skillId: String, ) + suspend fun saveWeather(weatherId: String) + + fun selectedWeatherStream(): Flow + suspend fun savedPokemon(): Flow suspend fun savedPokemonWithSkill(): Flow diff --git a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt index 35c46fbe9..5e97db811 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt @@ -16,7 +16,7 @@ class DefaultBattleRepository( private val remoteBattleDataSource: RemoteBattleDataSource, private val pokemonRepository: DexRepository, ) : BattleRepository { - private val cachedSkills: HashMap> = hashMapOf() + private val cachedSkills: MutableMap> = hashMapOf() override suspend fun weathers(): List = remoteBattleDataSource.weathers() @@ -40,7 +40,8 @@ class DefaultBattleRepository( opponentPokemonId = opponentPokemonId, ) - override suspend fun savePokemon(pokemonId: String) = localBattleDataSource.savePokemon(pokemonId) + override suspend fun savePokemon(pokemonId: String) = + localBattleDataSource.savePokemon(pokemonId) override suspend fun savePokemonWithSkill( pokemonId: String, @@ -59,6 +60,17 @@ class DefaultBattleRepository( } } + override suspend fun saveWeather(weatherId: String) = + localBattleDataSource.saveWeather(weatherId) + + override fun selectedWeatherStream(): Flow = + localBattleDataSource.weatherId().map { + if (it == null) { + return@map null + } + weathers().find { weather -> weather.id == it } + } + private suspend fun skill( dexNumber: Long, skillId: String, diff --git a/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt b/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt index 8d4b6e173..fc3f9a707 100644 --- a/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt +++ b/android/local/src/main/java/poke/rogue/helper/local/datastore/BattleDataStore.kt @@ -1,4 +1,3 @@ - package poke.rogue.helper.local.datastore import android.content.Context @@ -31,6 +30,17 @@ class BattleDataStore(private val context: Context) { } } + suspend fun saveWeather(weatherId: String) { + context.dataStore.edit { + it[WEATHER_SELECTION_KEY] = weatherId + } + } + + fun weatherId(): Flow = + context.dataStore.data.map { preferences -> + preferences[WEATHER_SELECTION_KEY] + } + fun pokemonWithSkillId(): Flow = context.dataStore.data.map { preference -> val pokemonId = preference[PAIR_POKEMON_SELECTION_KEY] @@ -49,6 +59,7 @@ class BattleDataStore(private val context: Context) { private companion object { const val BATTLE_PREFERENCE_NAME = "battle" + val WEATHER_SELECTION_KEY = stringPreferencesKey("weather_selection") val PAIR_POKEMON_SELECTION_KEY = stringPreferencesKey("pair_pokemon_selection") val PAIR_SKILL_SELECTION_KEY = stringPreferencesKey("pair_skill_selection") val SINGLE_POKEMON_SELECTION_KEY = stringPreferencesKey("single_pokemon_selection") From 590b2c696801b6d529594c875106e1189c5f4436 Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 26 Sep 2024 10:47:09 +0900 Subject: [PATCH 11/17] =?UTF-8?q?fix:=20BattleActivity=20=20Context=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../poke/rogue/helper/presentation/battle/BattleActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt index 9a656ef53..821e5d04d 100644 --- a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt @@ -22,7 +22,7 @@ import timber.log.Timber class BattleActivity : ToolbarActivity(R.layout.activity_battle) { private val viewModel by viewModels { - BattleViewModel.factory(DefaultBattleRepository.instance(this)) + BattleViewModel.factory(DefaultBattleRepository.instance(applicationContext)) } private val weatherAdapter by lazy { WeatherSpinnerAdapter(this) From aaaa3677e83413e057343bce2ceff4fd2b03370c Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 26 Sep 2024 10:57:03 +0900 Subject: [PATCH 12/17] =?UTF-8?q?refactor:=20xxStream=20postFix=20?= =?UTF-8?q?=EB=B6=99=EC=9D=B4=EA=B8=B0,=20suspend=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rogue/helper/presentation/battle/BattleViewModel.kt | 6 +++--- .../poke/rogue/helper/data/repository/BattleRepository.kt | 6 +++--- .../rogue/helper/data/repository/DefaultBattleRepository.kt | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt index 57c37e22a..9bbc86ec5 100644 --- a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt @@ -40,7 +40,7 @@ class BattleViewModel( val selectedState = _selectedState.asStateFlow() val weatherPos: StateFlow = combine( - battleRepository.selectedWeatherStream(), + battleRepository.savedWeatherStream(), weathers, ) { weather, weathers -> if (weather == null || weathers.isEmpty()) return@combine null @@ -103,12 +103,12 @@ class BattleViewModel( private fun initSavedSelection() { viewModelScope.launch { launch { - battleRepository.savedPokemon().first()?.let { + battleRepository.savedPokemonStream().first()?.let { updateOpponentPokemon(it.toSelectionUi()) } } launch { - battleRepository.savedPokemonWithSkill().first()?.let { (pokemon, skill) -> + battleRepository.savedPokemonWithSkillStream().first()?.let { (pokemon, skill) -> updateMyPokemon(pokemon.toSelectionUi(), skill.toUi()) } } diff --git a/android/data/src/main/java/poke/rogue/helper/data/repository/BattleRepository.kt b/android/data/src/main/java/poke/rogue/helper/data/repository/BattleRepository.kt index e2e2548c5..5dff8a364 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/repository/BattleRepository.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/repository/BattleRepository.kt @@ -28,9 +28,9 @@ interface BattleRepository { suspend fun saveWeather(weatherId: String) - fun selectedWeatherStream(): Flow + fun savedWeatherStream(): Flow - suspend fun savedPokemon(): Flow + fun savedPokemonStream(): Flow - suspend fun savedPokemonWithSkill(): Flow + fun savedPokemonWithSkillStream(): Flow } diff --git a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt index 5e97db811..8de60d436 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt @@ -48,10 +48,10 @@ class DefaultBattleRepository( skillId: String, ) = localBattleDataSource.savePokemonWithSkill(pokemonId, skillId) - override suspend fun savedPokemon(): Flow = + override fun savedPokemonStream(): Flow = localBattleDataSource.pokemonId().map { it?.let { pokemonRepository.pokemon(it) } } - override suspend fun savedPokemonWithSkill(): Flow = + override fun savedPokemonWithSkillStream(): Flow = localBattleDataSource.pokemonWithSkill().map { it?.let { pokemonWithSkill -> val pokemon = pokemonRepository.pokemon(pokemonWithSkill.pokemonId) @@ -63,7 +63,7 @@ class DefaultBattleRepository( override suspend fun saveWeather(weatherId: String) = localBattleDataSource.saveWeather(weatherId) - override fun selectedWeatherStream(): Flow = + override fun savedWeatherStream(): Flow = localBattleDataSource.weatherId().map { if (it == null) { return@map null From bfa43f8997880367059fe970203794bef0634ada Mon Sep 17 00:00:00 2001 From: murjune Date: Thu, 26 Sep 2024 10:58:07 +0900 Subject: [PATCH 13/17] ktlintFormat --- .../presentation/battle/BattleActivity.kt | 7 ++++--- .../presentation/battle/BattleViewModel.kt | 19 ++++++++++--------- .../view/WeatherItemSelectedListener.kt | 2 +- .../repository/DefaultBattleRepository.kt | 9 +++------ 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt index 821e5d04d..60657ca4f 100644 --- a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleActivity.kt @@ -57,9 +57,10 @@ class BattleActivity : ToolbarActivity(R.layout.activity_ private fun initSpinner() { binding.spinnerWeather.adapter = weatherAdapter - binding.spinnerWeather.onItemSelectedListener = itemSelectListener { - viewModel.updateWeather(it) - } + binding.spinnerWeather.onItemSelectedListener = + itemSelectListener { + viewModel.updateWeather(it) + } } private fun initObserver() { diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt index 9bbc86ec5..89fd9ee6f 100644 --- a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt @@ -39,15 +39,16 @@ class BattleViewModel( private val _selectedState = MutableStateFlow(BattleSelectionsState.DEFAULT) val selectedState = _selectedState.asStateFlow() - val weatherPos: StateFlow = combine( - battleRepository.savedWeatherStream(), - weathers, - ) { weather, weathers -> - if (weather == null || weathers.isEmpty()) return@combine null - if (weathers.any { it.id == weather.id }.not()) return@combine null - weathers.indexOfFirst { it.id == weather.id } - }.filterNotNull() - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 0) + val weatherPos: StateFlow = + combine( + battleRepository.savedWeatherStream(), + weathers, + ) { weather, weathers -> + if (weather == null || weathers.isEmpty()) return@combine null + if (weathers.any { it.id == weather.id }.not()) return@combine null + weathers.indexOfFirst { it.id == weather.id } + }.filterNotNull() + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 0) private val _navigateToSelection = MutableSharedFlow() val navigateToSelection = _navigateToSelection.asSharedFlow() diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/view/WeatherItemSelectedListener.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/view/WeatherItemSelectedListener.kt index fb1251982..84aad3d67 100644 --- a/android/app/src/main/java/poke/rogue/helper/presentation/battle/view/WeatherItemSelectedListener.kt +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/view/WeatherItemSelectedListener.kt @@ -24,4 +24,4 @@ inline fun itemSelectListener(crossinline onSelected: (T) -> Unit): override fun onNothingSelected(parent: AdapterView<*>?) {} } -} \ No newline at end of file +} diff --git a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt index 8de60d436..8845783a6 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt @@ -40,16 +40,14 @@ class DefaultBattleRepository( opponentPokemonId = opponentPokemonId, ) - override suspend fun savePokemon(pokemonId: String) = - localBattleDataSource.savePokemon(pokemonId) + override suspend fun savePokemon(pokemonId: String) = localBattleDataSource.savePokemon(pokemonId) override suspend fun savePokemonWithSkill( pokemonId: String, skillId: String, ) = localBattleDataSource.savePokemonWithSkill(pokemonId, skillId) - override fun savedPokemonStream(): Flow = - localBattleDataSource.pokemonId().map { it?.let { pokemonRepository.pokemon(it) } } + override fun savedPokemonStream(): Flow = localBattleDataSource.pokemonId().map { it?.let { pokemonRepository.pokemon(it) } } override fun savedPokemonWithSkillStream(): Flow = localBattleDataSource.pokemonWithSkill().map { @@ -60,8 +58,7 @@ class DefaultBattleRepository( } } - override suspend fun saveWeather(weatherId: String) = - localBattleDataSource.saveWeather(weatherId) + override suspend fun saveWeather(weatherId: String) = localBattleDataSource.saveWeather(weatherId) override fun savedWeatherStream(): Flow = localBattleDataSource.weatherId().map { From 8777ef08d986974f2f4ef1a42a03f4d0bc6edd1a Mon Sep 17 00:00:00 2001 From: murjune Date: Fri, 27 Sep 2024 13:18:42 +0900 Subject: [PATCH 14/17] =?UTF-8?q?fix:=20weather=20=EA=B0=80=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=EC=97=90=20=EC=98=81=ED=96=A5=20=EC=95=88=EC=A3=BC?= =?UTF-8?q?=EB=8D=98=EA=B1=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../poke/rogue/helper/presentation/battle/BattleViewModel.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt index 89fd9ee6f..29f478119 100644 --- a/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt +++ b/android/app/src/main/java/poke/rogue/helper/presentation/battle/BattleViewModel.kt @@ -46,6 +46,10 @@ class BattleViewModel( ) { weather, weathers -> if (weather == null || weathers.isEmpty()) return@combine null if (weathers.any { it.id == weather.id }.not()) return@combine null + val selectedWeather = weathers.first { it.id == weather.id } + // update selected weather + _selectedState.value = selectedState.value.copy(weather = BattleSelectionUiState.Selected(selectedWeather)) + // return position weathers.indexOfFirst { it.id == weather.id } }.filterNotNull() .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 0) From 98e30f1a8ba12f71d09356f5335fe3f09957234a Mon Sep 17 00:00:00 2001 From: murjune Date: Tue, 8 Oct 2024 14:30:29 +0900 Subject: [PATCH 15/17] =?UTF-8?q?refactor=20:=20Flow=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20(Stream)?= =?UTF-8?q?=20=EB=B6=99=EC=9D=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rogue/helper/data/datasource/LocalBattleDataSource.kt | 6 +++--- .../rogue/helper/data/repository/DefaultBattleRepository.kt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt b/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt index 6de6ab9bc..a2fbb6c84 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/datasource/LocalBattleDataSource.kt @@ -23,11 +23,11 @@ class LocalBattleDataSource(private val battleDataStore: BattleDataStore) { battleDataStore.saveWeather(weatherId) } - fun weatherId(): Flow = battleDataStore.weatherId() + fun weatherIdStream(): Flow = battleDataStore.weatherId() - fun pokemonWithSkill(): Flow = battleDataStore.pokemonWithSkillId().map { it?.toData() } + fun pokemonWithSkillStream(): Flow = battleDataStore.pokemonWithSkillId().map { it?.toData() } - fun pokemonId(): Flow = battleDataStore.pokemonId() + fun pokemonIdStream(): Flow = battleDataStore.pokemonId() companion object { private var instance: LocalBattleDataSource? = null diff --git a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt index 8845783a6..e5469c698 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt @@ -47,10 +47,10 @@ class DefaultBattleRepository( skillId: String, ) = localBattleDataSource.savePokemonWithSkill(pokemonId, skillId) - override fun savedPokemonStream(): Flow = localBattleDataSource.pokemonId().map { it?.let { pokemonRepository.pokemon(it) } } + override fun savedPokemonStream(): Flow = localBattleDataSource.pokemonIdStream().map { it?.let { pokemonRepository.pokemon(it) } } override fun savedPokemonWithSkillStream(): Flow = - localBattleDataSource.pokemonWithSkill().map { + localBattleDataSource.pokemonWithSkillStream().map { it?.let { pokemonWithSkill -> val pokemon = pokemonRepository.pokemon(pokemonWithSkill.pokemonId) val skill = skill(pokemon.dexNumber, pokemonWithSkill.skillId) @@ -61,7 +61,7 @@ class DefaultBattleRepository( override suspend fun saveWeather(weatherId: String) = localBattleDataSource.saveWeather(weatherId) override fun savedWeatherStream(): Flow = - localBattleDataSource.weatherId().map { + localBattleDataSource.weatherIdStream().map { if (it == null) { return@map null } From 0d2998fba681dbf10532beb6f5a6ac799288033b Mon Sep 17 00:00:00 2001 From: murjune Date: Tue, 8 Oct 2024 14:30:56 +0900 Subject: [PATCH 16/17] refactor: hashMap -> mutableMap --- .../rogue/helper/data/repository/DefaultBattleRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt index e5469c698..821cc7594 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt @@ -16,7 +16,7 @@ class DefaultBattleRepository( private val remoteBattleDataSource: RemoteBattleDataSource, private val pokemonRepository: DexRepository, ) : BattleRepository { - private val cachedSkills: MutableMap> = hashMapOf() + private val cachedSkills: MutableMap> = mutableMapOf() override suspend fun weathers(): List = remoteBattleDataSource.weathers() From f7ba61df164a7d967228bbc91ec2651ed167a3f6 Mon Sep 17 00:00:00 2001 From: murjune Date: Tue, 8 Oct 2024 14:31:46 +0900 Subject: [PATCH 17/17] ktLintFormat --- .../rogue/helper/data/repository/DefaultBattleRepository.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt index 821cc7594..5cfb30b30 100644 --- a/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt +++ b/android/data/src/main/java/poke/rogue/helper/data/repository/DefaultBattleRepository.kt @@ -47,7 +47,10 @@ class DefaultBattleRepository( skillId: String, ) = localBattleDataSource.savePokemonWithSkill(pokemonId, skillId) - override fun savedPokemonStream(): Flow = localBattleDataSource.pokemonIdStream().map { it?.let { pokemonRepository.pokemon(it) } } + override fun savedPokemonStream(): Flow = + localBattleDataSource.pokemonIdStream().map { + it?.let { pokemonRepository.pokemon(it) } + } override fun savedPokemonWithSkillStream(): Flow = localBattleDataSource.pokemonWithSkillStream().map {