diff --git a/app/src/main/java/com/eatssu/android/di/network/TokenInterceptor.kt b/app/src/main/java/com/eatssu/android/di/network/TokenInterceptor.kt index 15b0b128a..189071bce 100644 --- a/app/src/main/java/com/eatssu/android/di/network/TokenInterceptor.kt +++ b/app/src/main/java/com/eatssu/android/di/network/TokenInterceptor.kt @@ -114,10 +114,6 @@ class TokenInterceptor @Inject constructor( return chain.proceed(newRequest) } else { /** - * - * - * refreshTokenResponse : Response{protocol=http/1.1, code=401, message=, url=https://prod.eat-ssu.shop/oauths/reissue/token} - * 위 상황에서도 로그아웃 * 리프레쉬도 상한 상태 */ runBlocking { logoutUseCase() } @@ -145,27 +141,27 @@ class TokenInterceptor @Inject constructor( } if (response.code == 404) { - runBlocking { logoutUseCase() } +// runBlocking { logoutUseCase() } Timber.e("404 + 다른 유저!") - Handler(Looper.getMainLooper()).post { - Toast.makeText(context, "토큰이 만료되어 로그아웃 됩니다.", Toast.LENGTH_SHORT).show() - val intent = Intent(context, LoginActivity::class.java) // 로그인 화면으로 이동 - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) - context.startActivity(intent) - } +// Handler(Looper.getMainLooper()).post { +// Toast.makeText(context, "토큰이 만료되어 로그아웃 됩니다.", Toast.LENGTH_SHORT).show() +// val intent = Intent(context, LoginActivity::class.java) // 로그인 화면으로 이동 +// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) +// context.startActivity(intent) +// } } if (response.code == 500) { - runBlocking { logoutUseCase() } +// runBlocking { logoutUseCase() } Timber.e("500 + 다른 유저") - Handler(Looper.getMainLooper()).post { - Toast.makeText(context, "토큰이 만료되어 로그아웃 됩니다.", Toast.LENGTH_SHORT).show() - val intent = Intent(context, LoginActivity::class.java) // 로그인 화면으로 이동 - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) - context.startActivity(intent) - } +// Handler(Looper.getMainLooper()).post { +// Toast.makeText(context, "토큰이 만료되어 로그아웃 됩니다.", Toast.LENGTH_SHORT).show() +// val intent = Intent(context, LoginActivity::class.java) // 로그인 화면으로 이동 +// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) +// context.startActivity(intent) +// } } return response diff --git a/app/src/main/java/com/eatssu/android/domain/usecase/auth/GetIsAccessTokenValidUseCase.kt b/app/src/main/java/com/eatssu/android/domain/usecase/auth/GetIsAccessTokenValidUseCase.kt new file mode 100644 index 000000000..b689fea18 --- /dev/null +++ b/app/src/main/java/com/eatssu/android/domain/usecase/auth/GetIsAccessTokenValidUseCase.kt @@ -0,0 +1,13 @@ +package com.eatssu.android.domain.usecase.auth + +import com.eatssu.android.data.dto.response.BaseResponse +import com.eatssu.android.domain.repository.UserRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class GetIsAccessTokenValidUseCase @Inject constructor( + private val userRepository: UserRepository, +) { + suspend operator fun invoke(): Flow> = + userRepository.checkUserNameValidation("qkqh") //todo api 만들어지면 수정 +} \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/presentation/UiEvent.kt b/app/src/main/java/com/eatssu/android/presentation/UiEvent.kt new file mode 100644 index 000000000..050b16ee6 --- /dev/null +++ b/app/src/main/java/com/eatssu/android/presentation/UiEvent.kt @@ -0,0 +1,10 @@ +package com.eatssu.android.presentation + + +/** + * 각 Screen에 공통적인 이벤트 타입입니다. + * 이벤트 타입을 추가하고 싶다면 UiEvent를 상속받아 사용하세요. + */ +interface UiEvent { + data class ShowToast(val message: String) : UiEvent +} \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/presentation/UiState.kt b/app/src/main/java/com/eatssu/android/presentation/UiState.kt new file mode 100644 index 000000000..4a2f528f6 --- /dev/null +++ b/app/src/main/java/com/eatssu/android/presentation/UiState.kt @@ -0,0 +1,13 @@ +package com.eatssu.android.presentation + +sealed interface UiState { + object Init : UiState + + object Loading : UiState + + data class Success( + val data: T? = null, + ) : UiState + + object Error : UiState +} \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/presentation/login/IntroActivity.kt b/app/src/main/java/com/eatssu/android/presentation/login/IntroActivity.kt index 1f2b3fdb7..9ad17a90b 100644 --- a/app/src/main/java/com/eatssu/android/presentation/login/IntroActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/login/IntroActivity.kt @@ -1,13 +1,14 @@ package com.eatssu.android.presentation.login import android.os.Bundle -import android.os.Handler -import android.os.Looper import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope -import com.eatssu.android.R +import com.eatssu.android.databinding.ActivityIntroBinding +import com.eatssu.android.presentation.UiEvent +import com.eatssu.android.presentation.UiState import com.eatssu.android.presentation.main.MainActivity +import com.eatssu.android.presentation.util.showToast import com.eatssu.android.presentation.util.startActivity import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest @@ -17,36 +18,39 @@ import kotlinx.coroutines.launch class IntroActivity : AppCompatActivity() { private val introViewModel: IntroViewModel by viewModels() + private lateinit var binding: ActivityIntroBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_intro) + binding = ActivityIntroBinding.inflate(layoutInflater) + setContentView(binding.root) - // 일정 시간 지연 이후 실행하기 위한 코드 - Handler(Looper.getMainLooper()).postDelayed({ - - introViewModel.autoLogin() - - lifecycleScope.launch { - introViewModel.uiState.collectLatest { - if (it.isAutoLogined) { + lifecycleScope.launch { + introViewModel.uiState.collectLatest { state -> + when (state) { + is UiState.Success -> { startActivity() - - // 이전 키를 눌렀을 때 스플래스 스크린 화면으로 이동을 방지하기 위해 - // 이동한 다음 사용안함으로 finish 처리 finish() - } else { - startActivity() + } - // 이전 키를 눌렀을 때 스플래스 스크린 화면으로 이동을 방지하기 위해 - // 이동한 다음 사용안함으로 finish 처리 + is UiState.Error -> { + // 로그인 액티비티로 이동 + startActivity() finish() } + else -> Unit } } - }, 2000) // 시간 2초 이후 실행 - + introViewModel.uiEvent.collectLatest { event -> + when (event) { + is UiEvent.ShowToast -> { + // 에러 메시지 표시 + showToast(event.message) + } + } + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/presentation/login/IntroViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/login/IntroViewModel.kt index 5dabe8a24..68a0992c7 100644 --- a/app/src/main/java/com/eatssu/android/presentation/login/IntroViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/login/IntroViewModel.kt @@ -3,41 +3,70 @@ package com.eatssu.android.presentation.login import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.eatssu.android.domain.usecase.auth.GetAccessTokenUseCase +import com.eatssu.android.domain.usecase.auth.GetIsAccessTokenValidUseCase +import com.eatssu.android.presentation.UiEvent +import com.eatssu.android.presentation.UiState 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.asStateFlow -import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class IntroViewModel @Inject constructor( - private val getAccessTokenUseCase: GetAccessTokenUseCase + private val getAccessTokenUseCase: GetAccessTokenUseCase, + private val getIsAccessTokenValidUseCase: GetIsAccessTokenValidUseCase ) : ViewModel() { - private val _uiState: MutableStateFlow = MutableStateFlow(IntroState()) - val uiState: StateFlow = _uiState.asStateFlow() + private val _uiState: MutableStateFlow> = MutableStateFlow(UiState.Init) + val uiState: StateFlow> = _uiState.asStateFlow() + + private val _uiEvent = MutableSharedFlow() + val uiEvent: SharedFlow = _uiEvent init { autoLogin() } - fun autoLogin() { + private fun autoLogin() { viewModelScope.launch { - if (getAccessTokenUseCase().isEmpty()) { - _uiState.update { it.copy(isAutoLogined = false) } - } else { - _uiState.update { it.copy(isAutoLogined = true) } + _uiState.value = UiState.Loading + + try { + // 토큰 존재 여부 확인 + if (getAccessTokenUseCase().isEmpty()) { + _uiState.value = UiState.Error + _uiEvent.emit(UiEvent.ShowToast("로그인이 필요합니다")) + return@launch + } + + checkValid() + + } catch (e: Exception) { + _uiState.value = UiState.Error + _uiEvent.emit(UiEvent.ShowToast("오류가 발생했습니다: ${e.message}")) } } } + private fun checkValid() { + viewModelScope.launch { + getIsAccessTokenValidUseCase() + .collect { + if (it.result == true) { //토큰이 있고 유효함 + _uiState.value = UiState.Success(IntroState.ValidToken) + } else { //토큰이 있어도 유효하지 않음 + _uiState.value = UiState.Error + _uiEvent.emit(UiEvent.ShowToast("로그인이 필요합니다")) + } + } + } + } } -data class IntroState( - var toastMessage: String = "", - var loading: Boolean = true, - var error: Boolean = false, - var isAutoLogined: Boolean = false, -) \ No newline at end of file +sealed class IntroState { + object ValidToken : IntroState() +}