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

[AN/USER] 홈 화면 회전시 이전에 선택한 화면을 그대로 보여준다. (#534) #544

Merged
merged 11 commits into from
Oct 16, 2023
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.festago.festago.presentation.ui.home
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
Expand All @@ -18,26 +20,27 @@ import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class HomeActivity : AppCompatActivity() {

private var _binding: ActivityHomeBinding? = null
private val binding get() = _binding!!
private val binding by lazy { ActivityHomeBinding.inflate(layoutInflater) }

private val vm: HomeViewModel by viewModels()

private lateinit var resultLauncher: ActivityResultLauncher<Intent>

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initBinding()
initView()
initObserve()
initActivityResult()
}

private fun initBinding() {
_binding = ActivityHomeBinding.inflate(layoutInflater)
setContentView(binding.root)
}

private fun initView() {
binding.bnvHome.setOnItemSelectedListener {
vm.loadHomeItem(getItemType(it.itemId))
vm.selectItem(getItemType(it.itemId))
true
}

Expand All @@ -52,13 +55,20 @@ class HomeActivity : AppCompatActivity() {
repeatOnStarted(this) {
vm.event.collect { event ->
when (event) {
is HomeEvent.ShowFestivalList -> showFestivalList()
is HomeEvent.ShowTicketList -> showTicketList()
is HomeEvent.ShowMyPage -> showMyPage()
is HomeEvent.ShowSignIn -> showSignIn()
}
}
}

repeatOnStarted(this) {
vm.selectedItem.collect { homeItemType ->
when (homeItemType) {
HomeItemType.FESTIVAL_LIST -> showFestivalList()
HomeItemType.TICKET_LIST -> showTicketList()
HomeItemType.MY_PAGE -> showMyPage()
}
}
}
}

private fun getItemType(menuItemId: Int): HomeItemType {
Expand Down Expand Up @@ -86,7 +96,7 @@ class HomeActivity : AppCompatActivity() {
}

private fun showSignIn() {
startActivity(SignInActivity.getIntent(this))
resultLauncher.launch(SignInActivity.getIntent(this))
}

private inline fun <reified T : Fragment> changeFragment() {
Expand All @@ -109,6 +119,15 @@ class HomeActivity : AppCompatActivity() {
fragmentTransaction.commit()
}

private fun initActivityResult() {
resultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == SignInActivity.RESULT_NOT_SIGN_IN) {
binding.bnvHome.selectedItemId = R.id.item_festival
}
}
}

companion object {
fun getIntent(context: Context): Intent {
return Intent(context, HomeActivity::class.java)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package com.festago.festago.presentation.ui.home

sealed interface HomeEvent {
object ShowFestivalList : HomeEvent
object ShowTicketList : HomeEvent
object ShowMyPage : HomeEvent
object ShowSignIn : HomeEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package com.festago.festago.presentation.ui.home

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.festago.festago.presentation.ui.home.HomeItemType.FESTIVAL_LIST
import com.festago.festago.presentation.ui.home.HomeItemType.MY_PAGE
import com.festago.festago.presentation.ui.home.HomeItemType.TICKET_LIST
import com.festago.festago.repository.AuthRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

Expand All @@ -19,13 +19,23 @@ class HomeViewModel @Inject constructor(private val authRepository: AuthReposito
private val _event = MutableSharedFlow<HomeEvent>()
val event: SharedFlow<HomeEvent> = _event.asSharedFlow()

fun loadHomeItem(homeItemType: HomeItemType) {
private val _selectedItem = MutableStateFlow(HomeItemType.FESTIVAL_LIST)
val selectedItem: StateFlow<HomeItemType> = _selectedItem.asStateFlow()

fun selectItem(homeItemType: HomeItemType) {
when (homeItemType) {
HomeItemType.FESTIVAL_LIST -> _selectedItem.value = homeItemType
HomeItemType.TICKET_LIST -> selectItemWhenSignIn(HomeItemType.TICKET_LIST)
HomeItemType.MY_PAGE -> selectItemWhenSignIn(HomeItemType.MY_PAGE)
}
}

private fun selectItemWhenSignIn(homeItemType: HomeItemType) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

함수명이 조금 어색하게 느껴진다고 생각하는데 베르의 의견은 어떤가요??
selectItemOrSignIn와 같이 다른 함수들과 비슷하게 진행하는건 어떤가요??

Copy link
Member Author

Choose a reason for hiding this comment

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

좋아요!

viewModelScope.launch {
when {
homeItemType == FESTIVAL_LIST -> _event.emit(HomeEvent.ShowFestivalList)
!authRepository.isSigned -> _event.emit(HomeEvent.ShowSignIn)
homeItemType == TICKET_LIST -> _event.emit(HomeEvent.ShowTicketList)
homeItemType == MY_PAGE -> _event.emit(HomeEvent.ShowMyPage)
if (!authRepository.isSigned) {
_event.emit(HomeEvent.ShowSignIn)
} else {
_selectedItem.emit(homeItemType)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (!authRepository.isSigned) {
_event.emit(HomeEvent.ShowSignIn)
} else {
_selectedItem.emit(homeItemType)
if (authRepository.isSigned) {
_selectedItem.emit(homeItemType)
} else {
_event.emit(HomeEvent.ShowSignIn)

흐름상 !을 빼도 괜찮을 것같은데 어떻게 생각하시나요??

}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@ import android.os.Bundle
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import androidx.activity.OnBackPressedCallback
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.festago.festago.R
import com.festago.festago.databinding.ActivitySignInBinding
import com.festago.festago.presentation.ui.customview.OkDialogFragment
import com.festago.festago.presentation.ui.home.HomeActivity
import com.festago.festago.presentation.util.repeatOnStarted
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch

@AndroidEntryPoint
class SignInActivity : AppCompatActivity() {
Expand All @@ -40,20 +39,28 @@ class SignInActivity : AppCompatActivity() {
binding.lifecycleOwner = this
binding.vm = vm
initComment()
initBackPressed()
}

private fun initObserve() {
repeatOnStarted(this) {
vm.event.collect {
handleEvent(it)
private fun initBackPressed() {
val callback = object : OnBackPressedCallback(true) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

백버튼도 커스텀이 가능하군요

override fun handleOnBackPressed() {
setResult(RESULT_NOT_SIGN_IN, intent)
finish()
}
}
this.onBackPressedDispatcher.addCallback(this, callback)
}

private fun handleEvent(event: SignInEvent) = when (event) {
SignInEvent.ShowSignInPage -> handleSignInEvent()
SignInEvent.SignInSuccess -> handleSuccessEvent()
SignInEvent.SignInFailure -> handleFailureEvent()
private fun initObserve() {
repeatOnStarted(this) {
vm.event.collect { event ->
when (event) {
is SignInEvent.SignInSuccess -> handleSuccessEvent()
is SignInEvent.SignInFailure -> handleFailureEvent()
}
Comment on lines +58 to +61
Copy link
Collaborator

Choose a reason for hiding this comment

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

handleEvent로 분리 부탁드립니다!

Copy link
Member

Choose a reason for hiding this comment

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

저는 지금도 충분히 깔끔하다고 생각합니다ㅎㅎ uistate, event 둘다 있다면 모를까 지금은 분리하면 오히려 가독성이 떨어질 것 같아요!

}
}
}

private fun initComment() {
Expand All @@ -70,12 +77,6 @@ class SignInActivity : AppCompatActivity() {
binding.tvLoginDescription.text = spannableStringBuilder
}

private fun handleSignInEvent() {
lifecycleScope.launch {
vm.signIn()
}
}

private fun handleSuccessEvent() {
showHomeWithFinish()
}
Expand All @@ -102,7 +103,7 @@ class SignInActivity : AppCompatActivity() {
private const val COLOR_SPAN_END_INDEX = 4

private const val FAILURE_SIGN_IN = "로그인에 실패했습니다."

const val RESULT_NOT_SIGN_IN = 1
fun getIntent(context: Context): Intent {
return Intent(context, SignInActivity::class.java)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.festago.festago.presentation.ui.signin

sealed interface SignInEvent {
object ShowSignInPage : SignInEvent
object SignInSuccess : SignInEvent
object SignInFailure : SignInEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ class SignInViewModel @Inject constructor(
private val _event = MutableSharedFlow<SignInEvent>()
val event: SharedFlow<SignInEvent> = _event

fun signInKakao() {
viewModelScope.launch {
_event.emit(SignInEvent.ShowSignInPage)
}
}

fun signIn() {
viewModelScope.launch {
authRepository.signIn().onSuccess {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
android:layout_width="0dp"
android:layout_height="48dp"
android:background="@drawable/bg_mypage_kakao_login"
android:onClick="@{() -> vm.signInKakao()}"
android:onClick="@{() -> vm.signIn()}"
android:text="@string/mypage_btn_kakao"
android:textColor="@color/kakao_label"
android:textSize="16sp"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,85 +38,104 @@ class HomeViewModelTest {
}

@Test
fun `축제 목록을 요청했을 때 토큰이 있으면 축제 목록이 보인다`() = runTest {
fun `축제 목록을 요청했을 때 토큰이 있으면 축제 목록이 보이고 이벤트가 발생하지 않는다`() = runTest {
Copy link
Member

Choose a reason for hiding this comment

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

축제 목록 요청시에는 사용자 인증 유무 확인이 필요 없는 거 아닌가요?_?

Copy link
Member Author

Choose a reason for hiding this comment

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

필요없다는 것도 테스트로 확인하고 싶었어요!

// given
`사용자 인증 유무가 다음과 같을 때`(true)

// when
vm.event.test {
vm.loadHomeItem(HomeItemType.FESTIVAL_LIST)
// when
vm.selectItem(HomeItemType.FESTIVAL_LIST)

// then
assertThat(awaitItem()).isExactlyInstanceOf(HomeEvent.ShowFestivalList::class.java)
assertThat(vm.selectedItem.value).isEqualTo(HomeItemType.FESTIVAL_LIST)

// and
expectNoEvents()
Copy link
Member

Choose a reason for hiding this comment

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

이벤트가 발생하지 않은 것도 확인할 수 있군요 👍

}
}

@Test
fun `축제 목록을 요청했을 때 토큰이 없어도 축제 목록이 보인다`() = runTest {
fun `축제 목록을 요청했을 때 토큰이 없어도 축제 목록이 보이고 이벤트가 발생하지 않는다`() = runTest {
// given
`사용자 인증 유무가 다음과 같을 때`(false)

vm.event.test {
// when
vm.loadHomeItem(HomeItemType.FESTIVAL_LIST)
vm.selectItem(HomeItemType.FESTIVAL_LIST)

// then
assertThat(awaitItem()).isExactlyInstanceOf(HomeEvent.ShowFestivalList::class.java)
assertThat(vm.selectedItem.value).isEqualTo(HomeItemType.FESTIVAL_LIST)

// and
expectNoEvents()
}
}

@Test
fun `티켓 목록을 요청했을 때 토큰이 있으면 티켓 목록 보기 이벤트가 발생한다`() = runTest {
fun `티켓 목록을 요청했을 때 토큰이 있으면 티켓 목록이 보이고 이벤트가 발생하지 않는다`() = runTest {
// given
`사용자 인증 유무가 다음과 같을 때`(true)

vm.event.test {
// when
vm.loadHomeItem(HomeItemType.TICKET_LIST)
vm.selectItem(HomeItemType.TICKET_LIST)

// then
assertThat(awaitItem()).isExactlyInstanceOf(HomeEvent.ShowTicketList::class.java)
assertThat(vm.selectedItem.value).isEqualTo(HomeItemType.TICKET_LIST)

// and
expectNoEvents()
}
}

@Test
fun `티켓 목록을 요청했을 때 토큰이 있으면 로그인 보기 이벤트가 발생한다`() = runTest {
fun `티켓 목록을 요청했을 때 토큰이 있으면 로그인 이벤트가 발생하고 선택된 화면은 축제 목록 그대로이다`() = runTest {
// given
`사용자 인증 유무가 다음과 같을 때`(false)

vm.event.test {
// when
vm.loadHomeItem(HomeItemType.TICKET_LIST)
vm.selectItem(HomeItemType.TICKET_LIST)

// then
assertThat(awaitItem()).isExactlyInstanceOf(HomeEvent.ShowSignIn::class.java)

// and
assertThat(vm.selectedItem.value).isEqualTo(HomeItemType.FESTIVAL_LIST)
}
}

@Test
fun `마이페이지를 요청했을 때 토큰이 있으면 마이페이지 보기 이벤트가 발생한다`() = runTest {
fun `마이페이지를 요청했을 때 토큰이 있으면 마이페이지가 보이고 이벤트가 발생하지 않는다`() = runTest {
Copy link
Member

Choose a reason for hiding this comment

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

테스트 이름과 내용이 일치하지 않아요..! 마이페이지 테스트인데 TICKET_LIST를 테스트하고 있습니다ㅋㅋ

Copy link
Member Author

Choose a reason for hiding this comment

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

앗 수정하겠습니다!

// given
`사용자 인증 유무가 다음과 같을 때`(true)

vm.event.test {
// when
vm.loadHomeItem(HomeItemType.MY_PAGE)
vm.selectItem(HomeItemType.TICKET_LIST)

// then
assertThat(awaitItem()).isExactlyInstanceOf(HomeEvent.ShowMyPage::class.java)
assertThat(vm.selectedItem.value).isEqualTo(HomeItemType.TICKET_LIST)

// and
expectNoEvents()
}
}

@Test
fun `마이페이즈를 요청했을 때 토큰이 없으면 로그인 보기 이벤트가 발생한다`() = runTest {
fun `마이페이즈를 요청했을 때 토큰이 없으면 로그인 보기 이벤트가 발생하고 선택된 화면은 축제 목록 그대로이다`() = runTest {
// given
`사용자 인증 유무가 다음과 같을 때`(false)

vm.event.test {
// when
vm.loadHomeItem(HomeItemType.MY_PAGE)
vm.selectItem(HomeItemType.MY_PAGE)

// then
assertThat(awaitItem()).isExactlyInstanceOf(HomeEvent.ShowSignIn::class.java)

// and
assertThat(vm.selectedItem.value).isEqualTo(HomeItemType.FESTIVAL_LIST)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,4 @@ class SignInViewModelTest {
assertThat(awaitItem()).isExactlyInstanceOf(SignInEvent.SignInFailure::class.java)
}
}

@Test
fun `로그인을 요청하면 로그인 화면을 보여주는 이벤트가 발생한다`() = runTest {
// given

vm.event.test {
// when
vm.signInKakao()

// then
assertThat(awaitItem()).isExactlyInstanceOf(SignInEvent.ShowSignInPage::class.java)
}
}
}