Skip to content

Commit

Permalink
Merge pull request #21 from Nexters/feature/authentication
Browse files Browse the repository at this point in the history
코드 정리
  • Loading branch information
yxnsx authored Aug 12, 2023
2 parents b1b1315 + 3b25148 commit 342f00f
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 67 deletions.
28 changes: 28 additions & 0 deletions app/src/main/java/com/keyme/app/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,46 @@
package com.keyme.app

import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.keyme.app.ui.KeymeApp
import com.keyme.presentation.UiEvent
import com.keyme.presentation.UiEventManager
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import javax.inject.Inject

@AndroidEntryPoint
class MainActivity : ComponentActivity() {

@Inject
lateinit var uiEventManager: UiEventManager

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
KeymeApp()
}

handleUiEvent()
}

private fun handleUiEvent() {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
uiEventManager.uiEvent.collectLatest {
when (it) {
is UiEvent.Toast -> {
Toast.makeText(this@MainActivity, it.message, Toast.LENGTH_SHORT).show()
}
}
}
}
}
}
}
14 changes: 8 additions & 6 deletions app/src/main/java/com/keyme/app/ui/KeymeApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ fun KeymeApp() {

KeymeTheme {
Scaffold(
// bottomBar = {
// KeymeBottomBar(
// currentDestination = appState.currentDestination,
// onNavigateToDestination = appState::navigate,
// )
// },
bottomBar = {
if (appState.isSignIn) {
KeymeBottomBar(
currentDestination = appState.currentDestination,
onNavigateToDestination = appState::navigate,
)
}
},
) { innerPadding ->
NavHost(
navController = appState.navController,
Expand Down
34 changes: 32 additions & 2 deletions app/src/main/java/com/keyme/app/ui/KeymeAppState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ package com.keyme.app.ui

import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavDestination
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
Expand All @@ -11,19 +16,44 @@ import androidx.navigation.compose.rememberNavController
import com.keyme.app.navigation.TopLevelDestination
import com.keyme.presentation.navigation.KeymeNavigationDestination
import com.keyme.presentation.signin.SignInDestination
import com.keyme.presentation.signin.SignInViewModel
import com.keyme.presentation.signin.enums.SignInStateEnum
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

@Composable
fun rememberKeymeAppState(
signInViewModel: SignInViewModel = hiltViewModel(),
navController: NavHostController = rememberNavController(),
) = remember(navController) { KeymeAppState(navController) }
): KeymeAppState {
val coroutineScope = rememberCoroutineScope()
return remember(navController) {
KeymeAppState(coroutineScope, navController, signInViewModel)
}
}

@Stable
class KeymeAppState(val navController: NavHostController) {
class KeymeAppState(
private val coroutineScope: CoroutineScope,
val navController: NavHostController,
private val signInViewModel: SignInViewModel,
) {
val currentDestination: NavDestination?
@Composable get() = navController.currentBackStackEntryAsState().value?.destination

val startDestination = SignInDestination

var isSignIn by mutableStateOf(false)

init {
coroutineScope.launch {
signInViewModel.keymeSignInState.collectLatest {
isSignIn = it == SignInStateEnum.MY_DAILY
}
}
}

fun navigate(destination: KeymeNavigationDestination) {
if (destination is TopLevelDestination) {
navController.navigate(destination.route) {
Expand Down
25 changes: 25 additions & 0 deletions presentation/src/main/java/com/keyme/presentation/BaseViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,38 @@ package com.keyme.presentation

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.keyme.domain.entity.ApiResult
import com.keyme.domain.entity.onApiError
import com.keyme.domain.entity.onFailure
import com.keyme.domain.entity.onSuccess
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import timber.log.Timber
import javax.inject.Inject

abstract class BaseViewModel : ViewModel() {

val baseViewModelScope = viewModelScope + CoroutineExceptionHandler { _, throwable ->
Timber.e(throwable)
}

@Inject
lateinit var uiEventManager: UiEventManager

fun <T> apiCall(apiRequest: suspend () -> ApiResult<T>, action: (T) -> Unit) {
baseViewModelScope.launch {
apiRequest().onSuccess {
action(it)
}.onApiError { code, message ->
baseViewModelScope.launch {
uiEventManager.onEvent(UiEvent.Toast("($code) $message"))
}
}.onFailure {
baseViewModelScope.launch {
uiEventManager.onEvent(UiEvent.Toast(it.message ?: ""))
}
}
}
}
}
20 changes: 20 additions & 0 deletions presentation/src/main/java/com/keyme/presentation/UiEvent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.keyme.presentation

import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import javax.inject.Inject
import javax.inject.Singleton

sealed interface UiEvent {
class Toast(val message: String) : UiEvent
}

@Singleton
class UiEventManager @Inject constructor() {
private val _uiEvent = MutableSharedFlow<UiEvent>()
val uiEvent = _uiEvent.asSharedFlow()

suspend fun onEvent(event: UiEvent) {
_uiEvent.emit(event)
}
}
17 changes: 0 additions & 17 deletions presentation/src/main/java/com/keyme/presentation/UiState.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand All @@ -26,12 +25,12 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.kakao.sdk.auth.model.OAuthToken
import com.kakao.sdk.common.model.ClientError
import com.kakao.sdk.common.model.ClientErrorCause
import com.kakao.sdk.user.UserApiClient
import com.keyme.presentation.R
import com.keyme.presentation.UiState
import com.keyme.presentation.designsystem.component.KeymeText
import com.keyme.presentation.designsystem.component.KeymeTextType
import com.keyme.presentation.designsystem.theme.keyme_white
Expand All @@ -46,20 +45,13 @@ fun SignInRoute(
viewModel: SignInViewModel = hiltViewModel(),
) {
val context = LocalContext.current
val signInState: UiState<SignInStateEnum> by viewModel.keymeSignInState.collectAsState()
val signInState by viewModel.keymeSignInState.collectAsStateWithLifecycle()

when (val state = signInState) {
is UiState.Loading -> {}
is UiState.Success<SignInStateEnum> -> {
when (state.data) {
SignInStateEnum.NICKNAME -> navigateToNickname.invoke()
// SignInStateEnum.KEYME_TEST -> navigateToKeymeTest.invoke()
// SignInStateEnum.MY_DAILY -> navigateToMyDaily.invoke()
else -> {}
}
}
is UiState.ApiError -> {}
is UiState.Failure -> {}
when (signInState) {
SignInStateEnum.NICKNAME -> navigateToNickname.invoke()
// SignInStateEnum.KEYME_TEST -> navigateToKeymeTest.invoke()
// SignInStateEnum.MY_DAILY -> navigateToMyDaily.invoke()
else -> {}
}

SignInScreen(
Expand Down Expand Up @@ -168,6 +160,7 @@ private fun signInWithKakao(
true -> {
/* TODO: Handle error */
}

false -> {
/* TODO: Handle error */
}
Expand All @@ -180,6 +173,7 @@ private fun signInWithKakao(
context = context,
callback = kakaoSignInCallback,
)

false -> UserApiClient.instance.loginWithKakaoAccount(
context = context,
callback = kakaoSignInCallback,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,31 @@
package com.keyme.presentation.signin

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.keyme.domain.entity.onApiError
import com.keyme.domain.entity.onFailure
import com.keyme.domain.entity.onSuccess
import com.keyme.domain.usecase.SignInUseCase
import com.keyme.presentation.UiState
import com.keyme.presentation.BaseViewModel
import com.keyme.presentation.signin.enums.SignInStateEnum
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.asStateFlow
import javax.inject.Inject

@HiltViewModel
class SignInViewModel @Inject constructor(
private val signInUseCase: SignInUseCase,
) : ViewModel() {
) : BaseViewModel() {

private val _keymeSignInState = MutableStateFlow<UiState<SignInStateEnum>>(UiState.Loading)
val keymeSignInState: StateFlow<UiState<SignInStateEnum>> = _keymeSignInState
private val _keymeSignInState = MutableStateFlow(SignInStateEnum.NICKNAME)
val keymeSignInState: StateFlow<SignInStateEnum> = _keymeSignInState.asStateFlow()

fun signInWithKeyme(
token: String,
) {
viewModelScope.launch {
signInUseCase.invoke(token)
.onSuccess {
_keymeSignInState.value = UiState.Success(
when {
it.nickname.isNullOrBlank() -> SignInStateEnum.NICKNAME
it.onboardingTestResultId == null -> SignInStateEnum.KEYME_TEST
else -> SignInStateEnum.MY_DAILY
},
)
}
.onApiError { code, message ->
_keymeSignInState.value = UiState.ApiError(code, message)
}
.onFailure {
_keymeSignInState.value = UiState.Failure(it)
}
apiCall(apiRequest = { signInUseCase.invoke(token) }) {
_keymeSignInState.value = when {
it.nickname.isNullOrBlank() -> SignInStateEnum.NICKNAME
it.onboardingTestResultId == null -> SignInStateEnum.KEYME_TEST
else -> SignInStateEnum.MY_DAILY
}
}
}
}

0 comments on commit 342f00f

Please sign in to comment.