Skip to content

Commit

Permalink
[feature] #9 - SignIn 재구현
Browse files Browse the repository at this point in the history
  • Loading branch information
sayyyho committed Dec 8, 2024
1 parent 70cb373 commit 466c5c6
Show file tree
Hide file tree
Showing 13 changed files with 337 additions and 181 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.sopt.and.components
package org.sopt.and.presentation.components

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.sopt.and.components
package org.sopt.and.presentation.components

import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Text
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.sopt.and.components
package org.sopt.and.presentation.components

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ fun Navigation(
) {
composable<Routes.SignIn> {
SignInScreen(
navigateToSignUp = { navController.navigate(route = Routes.SignUp) },
navigateToMyInfo = {
onNavigateToSignUp = { navController.navigate(route = Routes.SignUp) },
signViewModel = ,
onNavigateToMain = {
navigationViewModel.changeBottomNavigationVisibility()
navController.navigate(Routes.MyInfo)
}
Expand All @@ -61,7 +62,8 @@ fun Navigation(

composable<Routes.SignUp> {
SignUpScreen(
navigateToSignIn = {
signViewModel = ,
onNavigateToSignIn = {
navController.navigate(
route = Routes.SignIn,
navOptions = navOptions {
Expand All @@ -78,7 +80,7 @@ fun Navigation(
MyScreen(
paddingValues = innerPadding,
myHobby = myInfoUiState.myHobby,
getMyHobby = myInfoViewModel::getMyHobby
getMyHobby = myInfoViewModel::getMyHobby,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,25 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import kotlinx.coroutines.launch

import org.sopt.and.R
import org.sopt.and.components.AuthSignButton
import org.sopt.and.presentation.viewmodel.SignViewModel
import org.sopt.and.presentation.components.AuthSignButton

import org.sopt.and.components.CustomTextField
import org.sopt.and.components.SignTopBar
import org.sopt.and.presentation.components.CustomTextField
import org.sopt.and.presentation.components.SignTopBar
import org.sopt.and.presentation.viewmodelfactory.SignUpViewModelFactory

@Composable
fun SignInScreen(
signViewModel: SignViewModel,
onNavigateToMain: () -> Unit,
onNavigateToSignUp: () -> Unit
) {
val signUpViewModel: SignUpViewModel = viewModel(
factory = SignUpViewModelFactory()
)
val signUpUiState by signUpViewModel.uiState.collectAsStateWithLifecycle()
val snackbarHostState = remember { SnackbarHostState() }
val coroutineScope = rememberCoroutineScope()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.sopt.and.presentation.signin

data class SignInUiState(
val signInUsername: String = "",
val signInPassword: String = "",
val isSignInPasswordVisible: Boolean = false
)

sealed class SignInResult {
object Initial : SignInResult()
object Success : SignInResult()
object FailurePasswordLength : SignInResult()
object FailureWrongPassword : SignInResult()
}
130 changes: 130 additions & 0 deletions app/src/main/java/org/sopt/and/presentation/signin/SignInViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package org.sopt.and.presentation.signin

import android.content.Context
import androidx.compose.material3.SnackbarHostState
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import org.sopt.and.R
import org.sopt.and.data.service.AppContext
import org.sopt.and.data.service.TokenManager
import org.sopt.and.domain.model.SignInInformationEntity
import org.sopt.and.domain.model.SignInResponseEntity
import org.sopt.and.domain.usecase.SignInUseCase
import org.sopt.and.presentation.util.Utils.showSnackbar

class SignInViewModel(
private val signInUseCase: SignInUseCase
) : ViewModel() {
private val tokenManager = TokenManager(AppContext.get())

private val _uiState = MutableStateFlow(SignInUiState())
val uiState: StateFlow<SignInUiState> = _uiState.asStateFlow()

private val _signInResult = MutableStateFlow<SignInResult>(SignInResult.Initial)
val signInResult: StateFlow<SignInResult> = _signInResult.asStateFlow()

private fun initSignInResult() {
_signInResult.value = SignInResult.Initial
}

fun setSignInUsername(signInUsername: String) {
_uiState.value = _uiState.value.copy(
signInUsername = signInUsername
)
}

fun setSignInPassword(signInPassword: String) {
_uiState.value = _uiState.value.copy(
signInPassword = signInPassword
)
}

fun changeSignInPasswordVisibility() {
_uiState.value = _uiState.value.copy(
isSignInPasswordVisible = !_uiState.value.isSignInPasswordVisible
)
}

fun signIn(
signInUsername: String,
signInPassword: String
) {
viewModelScope.launch {
signInUseCase(
request = SignInInformationEntity(
username = signInUsername,
password = signInPassword
)
).onSuccess { signInResponseEntity: SignInResponseEntity ->
if (signInResponseEntity.status == 200) {
_signInResult.value = SignInResult.Success
signInResponseEntity.token?.let { token ->
tokenManager.saveToken(token)
}
} else if (signInResponseEntity.code == SignInFailureCase.FAILURE_LENGTH.errorCode
&& signInResponseEntity.status == SignInFailureCase.FAILURE_LENGTH.statusCode
) {
_signInResult.value = SignInResult.FailurePasswordLength
} else if (signInResponseEntity.code == SignInFailureCase.FAILURE_WRONG_PASSWORD.errorCode
&& signInResponseEntity.status == SignInFailureCase.FAILURE_WRONG_PASSWORD.statusCode
) {
_signInResult.value = SignInResult.FailureWrongPassword
}
}
}
}

fun confirmLogin(
snackbarHostState: SnackbarHostState,
navigateToMyInfo: () -> Unit,
context: Context,
scope: CoroutineScope
) {
when (signInResult.value) {
is SignInResult.Success -> {
context.showSnackbar(
scope = scope,
snackbarHostState = snackbarHostState,
message = R.string.sign_in_success_message,
)
navigateToMyInfo()
initSignInResult()
}

is SignInResult.FailurePasswordLength -> {
context.showSnackbar(
scope = scope,
snackbarHostState = snackbarHostState,
message = R.string.sign_in_failed_password_length,
)
initSignInResult()
}

is SignInResult.FailureWrongPassword -> {
context.showSnackbar(
scope = scope,
snackbarHostState = snackbarHostState,
message = R.string.sign_in_failed_wrong_password,
)
initSignInResult()
}

else -> {}
}
}
}

data class SignInFailureCase(
val statusCode: Int,
val errorCode: String
) {
companion object {
val FAILURE_LENGTH = SignInFailureCase(statusCode = 400, errorCode = "01")
val FAILURE_WRONG_PASSWORD = SignInFailureCase(statusCode = 403, errorCode = "01")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.sopt.and.R
import org.sopt.and.components.AuthSignButton
import org.sopt.and.presentation.components.AuthSignButton
import org.sopt.and.presentation.viewmodel.SignViewModel
import org.sopt.and.components.CustomTextField
import org.sopt.and.components.SignTopBar
import org.sopt.and.presentation.components.CustomTextField
import org.sopt.and.presentation.components.SignTopBar

@Composable
fun SignUpScreen(
Expand Down
47 changes: 47 additions & 0 deletions app/src/main/java/org/sopt/and/presentation/util/Utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.sopt.and.presentation.util

import android.content.Context
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.compose.material3.SnackbarHostState
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.sopt.and.R

object Utils {
const val MIN_PASSWORD_LENGTH = 8
const val MAX_PASSWORD_LENGTH = 20

const val MYINFO_SCREEN_INDEX = 2
const val SEARCH_SCREEN_INDEX = 1
const val HOME_SCREEN_INDEX = 0

const val GREETING_FIRST_LINE_FOCUS_START_INDEX = 0
const val GREETING_FIRST_LINE_FOCUS_END_INDEX = 9
const val GREETING_FIRST_LINE_END_INDEX = 12
const val GREETING_SECOND_LINE_FOCUS_START_INDEX = 13
const val GREETING_SECOND_LINE_FOCUS_END_INDEX = 24
const val GREETING_SECOND_LINE_END_INDEX = 29


fun transformationPasswordVisual(isVisible: Boolean): VisualTransformation =
if (isVisible) VisualTransformation.None else PasswordVisualTransformation()

fun Context.showToast(
@StringRes message: Int
) = Toast.makeText(
this,
this.getString(message),
Toast.LENGTH_SHORT
).show()

fun Context.showSnackbar(
scope: CoroutineScope,
snackbarHostState: SnackbarHostState,
@StringRes message: Int
) = scope.launch {
snackbarHostState.showSnackbar(message = getString(message))
}
}
Loading

0 comments on commit 466c5c6

Please sign in to comment.