Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[0.2.0-alph/AN-UI,Fix] 화면 나갔다가 5초 이후에 들어오면 오둥이 보이는 버그 수정 + clearFocus #225

Merged
merged 8 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
android:name=".presentation.tip.TipActivity"
android:exported="false" />
<activity
android:name=".presentation.dex.PokemonActivity"
android:name=".presentation.dex.PokemonListActivity"
android:exported="true" />
<activity
android:name=".presentation.home.HomeActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class NetworkErrorActivity :
private fun checkNetworkConnect() {
binding.btnNetworkErrorRetry.setOnSingleClickListener {
if (isNetworkConnected()) {
RefreshEventBus.send()
RefreshEventBus.refresh()
finish()
return@setOnSingleClickListener
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,22 @@ package poke.rogue.helper.presentation.dex

import android.content.res.Configuration
import android.os.Bundle
import android.view.View
import androidx.activity.viewModels
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.commit
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.GridLayoutManager
import poke.rogue.helper.R
import poke.rogue.helper.data.repository.DefaultDexRepository
import poke.rogue.helper.databinding.FragmentPokemonListBinding
import poke.rogue.helper.presentation.base.error.ErrorHandleFragment
import poke.rogue.helper.databinding.ActivityPokemonListBinding
import poke.rogue.helper.presentation.base.error.ErrorHandleActivity
import poke.rogue.helper.presentation.base.error.ErrorHandleViewModel
import poke.rogue.helper.presentation.dex.detail.PokemonDetailActivity
import poke.rogue.helper.presentation.util.activity.hideKeyboard
import poke.rogue.helper.presentation.util.repeatOnStarted
import poke.rogue.helper.presentation.util.view.GridSpacingItemDecoration
import poke.rogue.helper.presentation.util.view.dp

class PokemonListFragment :
ErrorHandleFragment<FragmentPokemonListBinding>(R.layout.fragment_pokemon_list) {
class PokemonListActivity :
ErrorHandleActivity<ActivityPokemonListBinding>(R.layout.activity_pokemon_list) {
private val viewModel by viewModels<PokemonListViewModel> {
PokemonListViewModel.factory(
DefaultDexRepository.instance(),
Expand All @@ -34,16 +33,15 @@ class PokemonListFragment :
override val toolbar: Toolbar
get() = binding.toolbarDex

override fun onViewCreated(
view: View,
savedInstanceState: Bundle?,
) {
super.onViewCreated(view, savedInstanceState)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding.viewModel = viewModel
binding.lifecycleOwner = viewLifecycleOwner

binding.lifecycleOwner = this
initAdapter()
initObservers()
binding.root.setOnClickListener {
hideKeyboard()
}
}

private fun initAdapter() {
Expand All @@ -70,14 +68,13 @@ class PokemonListFragment :
}
repeatOnStarted {
viewModel.navigateToDetailEvent.collect { pokemonId ->
parentFragmentManager.commit {
startActivity(PokemonDetailActivity.intent(requireContext(), pokemonId))
}
hideKeyboard()
startActivity(PokemonDetailActivity.intent(this, pokemonId))
}
}
}

companion object {
val TAG: String = PokemonListFragment::class.java.simpleName
val TAG: String = PokemonListActivity::class.java.simpleName
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import poke.rogue.helper.analytics.AnalyticsLogger
import poke.rogue.helper.analytics.analyticsLogger
import poke.rogue.helper.data.exception.PokeException
import poke.rogue.helper.data.model.Pokemon
import poke.rogue.helper.data.repository.DexRepository
import poke.rogue.helper.presentation.base.BaseViewModelFactory
Expand All @@ -35,33 +34,26 @@ class PokemonListViewModel(
) : ErrorHandleViewModel(logger), PokemonListNavigateHandler, PokemonQueryHandler {
private val searchQuery = MutableStateFlow("")

private val _isLoading = MutableStateFlow(false)
val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
val uiState: StateFlow<List<PokemonUiModel>> =
refreshEvent
merge(refreshEvent.map { "" }, searchQuery)
.onStart {
_isLoading.value = true
emit(Unit)
if (isEmpty.value) {
_isLoading.value = true
}
}
.flatMapLatest {
searchQuery
.debounce(300L)
.mapLatest { query ->
queriedPokemons(query)
}
.catch { e ->
handlePokemonError(e)
}
}.onEach {
_isLoading.value = false
.debounce(300L)
.mapLatest { query ->
queriedPokemons(query)
}
.stateIn(
viewModelScope + errorHandler,
SharingStarted.WhileSubscribed(5000),
emptyList(),
)
private val _isLoading = MutableStateFlow(false)
val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

val isEmpty: StateFlow<Boolean> =
uiState.map { it.isEmpty() && !isLoading.value }
.stateIn(
Expand All @@ -74,10 +66,18 @@ class PokemonListViewModel(
val navigateToDetailEvent = _navigateToDetailEvent.asSharedFlow()

private suspend fun queriedPokemons(query: String): List<PokemonUiModel> {
if (query.isEmpty()) {
return pokemonListRepository.pokemons().map(Pokemon::toUi)
return try {
if (query.isEmpty()) {
pokemonListRepository.pokemons().map(Pokemon::toUi)
} else {
pokemonListRepository.pokemons(query).map(Pokemon::toUi)
}
} catch (e: PokeException) {
handlePokemonError(e)
emptyList()
} finally {
_isLoading.value = false
}
return pokemonListRepository.pokemons(query).map(Pokemon::toUi)
}

override fun navigateToPokemonDetail(pokemonId: Long) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import poke.rogue.helper.databinding.ActivityHomeBinding
import poke.rogue.helper.presentation.ability.AbilityActivity
import poke.rogue.helper.presentation.base.toolbar.ToolbarActivity
import poke.rogue.helper.presentation.biome.BiomeActivity
import poke.rogue.helper.presentation.dex.PokemonActivity
import poke.rogue.helper.presentation.dex.PokemonListActivity
import poke.rogue.helper.presentation.tip.TipActivity
import poke.rogue.helper.presentation.type.TypeActivity
import poke.rogue.helper.presentation.util.context.startActivity
Expand Down Expand Up @@ -52,7 +52,7 @@ class HomeActivity : ToolbarActivity<ActivityHomeBinding>(R.layout.activity_home
}

is HomeNavigateEvent.ToDex ->
startActivity<PokemonActivity> {
startActivity<PokemonListActivity> {
logger.logClickEvent(NAVIGATE_TO_DEX)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package poke.rogue.helper.presentation.util.activity

import android.app.Activity
import android.content.Context
import android.view.inputmethod.InputMethodManager

fun Activity.hideKeyboard() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오홍

val inputMethodManager =
getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
currentFocus?.let { view ->
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
view.clearFocus()
}
}

fun Activity.show() {
val inputMethodManager =
getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
currentFocus?.let { view ->
inputMethodManager.showSoftInput(view, 0)
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
package poke.rogue.helper.presentation.util.event

import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.util.concurrent.atomic.AtomicBoolean

interface EventFlow<out T> : Flow<T> {
Expand Down Expand Up @@ -49,33 +45,3 @@ private class EventFlowSlot<T>(val value: T) {

fun markConsumed(): Boolean = consumed.getAndSet(true)
}

fun main() {
runBlocking {
val eventFlow = MutableSharedFlow<String>(3)
eventFlow.emit("Hello")
eventFlow.emit("Hello2")
eventFlow.emit("Hello3")
launch {
var count = 2
eventFlow.collect {
count--
println(it)
if (count == 0) cancel()
}
}
launch {
var count = 2
eventFlow.collect {
count--
println(it)
if (count == 0) cancel()
}
}
delay(100)
launch {
println("Second collector")
eventFlow.collect { println(it) }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import kotlinx.coroutines.launch

object RefreshEventBus {
private val coroutineScope: CoroutineScope = CoroutineScope(SupervisorJob())
private val _event = MutableEventFlow<Unit>(capacity = 1)
private val _event = MutableEventFlow<Unit>()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

capacity 는 왜 없어졌나요?
궁금해서 여쭤봅니당

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DefaultArgument로 빼서 없앴습니다 ㅋㅋㅋㅋ

val event: EventFlow<Unit> = _event.asEventFlow()

fun send() {
fun refresh() {
coroutineScope.launch {
_event.emit(Unit)
}
Expand Down
21 changes: 0 additions & 21 deletions android/app/src/main/res/layout/activity_pokemon.xml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".presentation.dex.PokemonListFragment">
tools:context=".presentation.dex.PokemonListActivity">

<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_dex"
Expand Down Expand Up @@ -42,7 +42,7 @@
android:id="@+id/rv_pokemon_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:layout_marginTop="20dp"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_dex"
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다!~

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package poke.rogue.helper.presentation.util.event

import io.kotest.matchers.shouldBe
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test

class EventFlowTest {
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `EventFlow 는 소비할 때까지 element 가 삭제되지 않는다`() =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EventFlow 테스트는 이런식으로 하는거군요 ! 👍

runTest(UnconfinedTestDispatcher()) {
// given
val eventFlow = MutableEventFlow<Int>()
// when
eventFlow.emit(1)
delay(10)
// then
eventFlow
.onEach {
println(">>> onEach: $it")
it shouldBe 1
}
.launchIn(backgroundScope)
}

@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `EventFlow 는 element 를 공유하지 않는다`() =
runTest {
// given
val eventFlow = MutableEventFlow<Int>()
// when
eventFlow.emit(1)
delay(10)
// then
backgroundScope.launch {
launch {
eventFlow.collect {
println(">>> collect: $it")
it shouldBe 1
}
}
launch {
eventFlow.collect {
println(">>> Never Collect AnyThing")
it shouldBe Int.MAX_VALUE
}
}
}
advanceTimeBy(100)
}
}
Loading