Skip to content

Commit

Permalink
#13 [Feat] : State, SideEffect, Event에 따른 SignUp 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
imtaejugkim committed Dec 18, 2024
1 parent 1c51977 commit 637a829
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 131 deletions.
3 changes: 3 additions & 0 deletions app/src/main/java/org/sopt/and/presentation/core/UiEvent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.sopt.and.presentation.core

interface UiEvent
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.sopt.and.presentation.core

interface UiSideEffect
3 changes: 3 additions & 0 deletions app/src/main/java/org/sopt/and/presentation/core/UiState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.sopt.and.presentation.core

interface UiState
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.sopt.and.presentation.signup

import org.sopt.and.presentation.core.UiEvent
import org.sopt.and.presentation.core.UiSideEffect
import org.sopt.and.presentation.core.UiState

// State 정의
data class SignUpState(
val username: String = "",
val password: String = "",
val hobby: String = "",
val isLoading: Boolean = false
) : UiState

// Event 정의
sealed class SignUpEvent : UiEvent {
data class UsernameChanged(val username: String) : SignUpEvent()
data class PasswordChanged(val password: String) : SignUpEvent()
data class HobbyChanged(val hobby: String) : SignUpEvent()
object SignUpClicked : SignUpEvent()
}

// SideEffect 정의
sealed class SignUpSideEffect : UiSideEffect {
data class ShowToast(val message: String) : SignUpSideEffect()
object NavigateToLogin : SignUpSideEffect()
}
220 changes: 110 additions & 110 deletions app/src/main/java/org/sopt/and/presentation/signup/SignUpScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,12 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
Expand All @@ -42,34 +38,21 @@ fun SignUpScreen(
navController: NavController,
viewModel: UserViewModel = hiltViewModel()
) {
// textStyle 변경을 위한 textFieldValue 추적
val idState = remember { mutableStateOf(TextFieldValue()) }
val passwordState = remember { mutableStateOf(TextFieldValue()) }
val hobbyState = remember { mutableStateOf(TextFieldValue()) }
val state by viewModel.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current
// 모든 textFiled가 채워졌는지 판단하는 변수
val allFieldFilled = idState.value.text.isNotEmpty() && passwordState.value.text.isNotEmpty() && hobbyState.value.text.isNotEmpty()
val registerState = viewModel.userRegisterState.collectAsStateWithLifecycle().value


LaunchedEffect(registerState) {
when (registerState) {
is RegisterState.Loading -> { }
is RegisterState.Success -> {
navController.navigate("login") {
popUpTo(0) { inclusive = true }
LaunchedEffect(Unit) {
viewModel.sideEffect.collect { effect ->
when (effect) {
is SignUpSideEffect.ShowToast -> {
context.showToast(effect.message)
}
}

is RegisterState.Failure -> {
when (registerState.code) {
"00" -> context.showToast(R.string.sign_up_error)
"01" -> context.showToast(R.string.sign_up_eight)
else -> {}
is SignUpSideEffect.NavigateToLogin -> {
navController.navigate("login") {
popUpTo(0) { inclusive = true }
}
}
}

else -> {}
}
}

Expand All @@ -85,95 +68,112 @@ fun SignUpScreen(
.padding(16.dp),
) {
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.TopEnd
modifier = Modifier.fillMaxSize()
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 44.dp)
.background(Color.Black)
.padding(16.dp),
) {
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.TopEnd
) {
Text(
text = "회원가입",
color = Color.White,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
Image(
painter = painterResource(R.drawable.ic_exit),
contentDescription = "X 버튼",
modifier = Modifier.size(24.dp)
)
}

Spacer(modifier = Modifier.weight(3f))
SignUpTitle(
firstText = stringResource(R.string.sign_up_title_top_start),
firstColor = Color.White,
secondText = stringResource(R.string.sign_up_title_top_end),
secondColor = Color.Gray
)

SignUpTitle(
firstText = stringResource(R.string.sign_up_title_bottom_start),
firstColor = Color.White,
secondText = stringResource(R.string.sign_up_title_bottom_end),
secondColor = Color.Gray
)
Spacer(modifier = Modifier.weight(2f))

IdHobbyTextField(
valueState = state.username,
onValueChange = {
viewModel.setEvent(SignUpEvent.UsernameChanged(it))
},
holderText = R.string.log_in_id,
modifier = Modifier.padding(bottom = 8.dp)
)

Spacer(modifier = Modifier.weight(0.5f))
SignUpInfoRow(
iconResId = R.drawable.ic_info,
text = stringResource(R.string.sign_up_id)
)

Spacer(modifier = Modifier.weight(1f))
PasswordField(
passwordState = state.password,
onValueChange = {
viewModel.setEvent(SignUpEvent.PasswordChanged(it))
},
modifier = Modifier.padding(top = 8.dp)
)

Spacer(modifier = Modifier.weight(0.5f))
SignUpInfoRow(
iconResId = R.drawable.ic_info,
text = stringResource(R.string.sign_up_passwd)
)

Spacer(modifier = Modifier.weight(1f))
IdHobbyTextField(
valueState = state.hobby,
onValueChange = {
viewModel.setEvent(SignUpEvent.HobbyChanged(it))
},
holderText = R.string.sign_up_hobby,
modifier = Modifier.padding(bottom = 8.dp)
)

Spacer(modifier = Modifier.weight(2f))
SocialServiceLogIn()
Spacer(modifier = Modifier.weight(8f))
}

Text(
text = "회원가입",
text = "Wavve 회원가입",
color = Color.White,
modifier = Modifier.fillMaxWidth(),
modifier = Modifier
.background(
if (state.username.isNotEmpty() && state.password.isNotEmpty() && state.hobby.isNotEmpty()) Color.Blue else Color.Gray
)
.padding(10.dp)
.align(Alignment.BottomCenter)
.fillMaxWidth()
.clickable(
enabled = state.username.isNotEmpty() && state.password.isNotEmpty() && state.hobby.isNotEmpty(),
onClick = {
viewModel.setEvent(SignUpEvent.SignUpClicked)
}
),
textAlign = TextAlign.Center
)
Image(
painter = painterResource(R.drawable.ic_exit),
contentDescription = "X 버튼",
modifier = Modifier.size(24.dp)
)
}

Spacer(modifier = Modifier.weight(3f))
SignUpTitle(
firstText = stringResource(R.string.sign_up_title_top_start),
firstColor = Color.White,
secondText = stringResource(R.string.sign_up_title_top_end),
secondColor = Color.Gray
)

SignUpTitle(
firstText = stringResource(R.string.sign_up_title_bottom_start),
firstColor = Color.White,
secondText = stringResource(R.string.sign_up_title_bottom_end),
secondColor = Color.Gray
)
Spacer(modifier = Modifier.weight(2f))
IdHobbyTextField(
valueState = idState,
holderText = R.string.log_in_id,
modifier = Modifier.padding(bottom = 8.dp)
)

Spacer(modifier = Modifier.weight(0.5f))
SignUpInfoRow(
iconResId = R.drawable.ic_info,
text = stringResource(R.string.sign_up_id)
)

Spacer(modifier = Modifier.weight(1f))
PasswordField(
passwordState = passwordState,
modifier = Modifier.padding(top = 8.dp)
)

Spacer(modifier = Modifier.weight(0.5f))
SignUpInfoRow(
iconResId = R.drawable.ic_info,
text = stringResource(R.string.sign_up_passwd)
)

Spacer(modifier = Modifier.weight(1f))
IdHobbyTextField(
valueState = hobbyState,
holderText = R.string.sign_up_hobby,
modifier = Modifier.padding(bottom = 8.dp)
)

Spacer(modifier = Modifier.weight(2f))
SocialServiceLogIn()
Spacer(modifier = Modifier.weight(8f))
}

Text(
text = "Wavve 회원가입",
color = Color.White,
modifier = Modifier
.background(
if (allFieldFilled) Color.Blue else Color.Gray,
)
.padding(10.dp)
.align(Alignment.BottomCenter)
.fillMaxWidth()
.clickable(
enabled = allFieldFilled,
onClick = {
val textId = idState.value.text
val textPasswd = passwordState.value.text
val textHobby = hobbyState.value.text
// viewModel의 signUp을 통해 success boolean 판단
viewModel.signUp(textId, textPasswd, textHobby)
}
),
textAlign = TextAlign.Center
)

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,64 @@ import kotlinx.coroutines.launch
import org.sopt.and.domain.model.UserLoginRequest
import org.sopt.and.domain.model.UserRegisterRequest
import org.sopt.and.domain.repository.UserRepository
import org.sopt.and.presentation.core.BaseViewModel
import org.sopt.and.presentation.login.LoginState
import javax.inject.Inject

@HiltViewModel
class UserViewModel @Inject constructor(
private val userRepository: UserRepository
) : ViewModel() {
) : BaseViewModel<SignUpState, SignUpSideEffect, SignUpEvent>() {
private val _userRegisterState = MutableStateFlow<RegisterState>(RegisterState.Idle)
val userRegisterState: StateFlow<RegisterState> = _userRegisterState

private val _userLoginState = MutableStateFlow<LoginState>(LoginState.Idle)
val userLoginState: StateFlow<LoginState> = _userLoginState

override fun createInitialState(): SignUpState = SignUpState()

override suspend fun handleEvent(event: SignUpEvent) {
when (event) {
is SignUpEvent.UsernameChanged -> {
setState { copy(username = event.username) }
}

is SignUpEvent.PasswordChanged -> {
setState { copy(password = event.password) }
}

is SignUpEvent.HobbyChanged -> {
setState { copy(hobby = event.hobby) }
}

is SignUpEvent.SignUpClicked -> {
signUp()
}
}
}

// 회원가입 로직
fun signUp(username: String, password: String, hobby: String) {
_userRegisterState.value = RegisterState.Loading
fun signUp() {
setState { copy(isLoading = true) }
viewModelScope.launch {
val state = uiState.value
val result = userRepository.postUserRegistering(
UserRegisterRequest(
username = username, password = password, hobby = hobby
username = state.username,
password = state.password,
hobby = state.hobby
)
)
_userRegisterState.value =
result.fold(onSuccess = { RegisterState.Success(it.no) },
onFailure = { RegisterState.Failure(it.message ?: "") })
result.fold(
onSuccess = {
setState { copy(isLoading = false) }
setSideEffect { SignUpSideEffect.NavigateToLogin }
},
onFailure = {
setState { copy(isLoading = false) }
setSideEffect { SignUpSideEffect.ShowToast(it.message ?: "회원가입 실패") }
}
)
}
}

Expand Down
Loading

0 comments on commit 637a829

Please sign in to comment.