Skip to content

Commit

Permalink
Merge pull request #199 from rbybound/feature-aos/등록-요청-보내기
Browse files Browse the repository at this point in the history
Feature(#140, #142, #143): 회원가입 요청 보내기
  • Loading branch information
rbybound authored Nov 30, 2023
2 parents 2722936 + f82ba3b commit 3563160
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package com.boostcampwm2023.snappoint.data.remote

import com.boostcampwm2023.snappoint.data.remote.model.request.CreatePostRequest
import com.boostcampwm2023.snappoint.data.remote.model.request.LoginRequest
import com.boostcampwm2023.snappoint.data.remote.model.request.SignupRequest
import com.boostcampwm2023.snappoint.data.remote.model.response.CreatePostResponse
import com.boostcampwm2023.snappoint.data.remote.model.response.ImageResponse
import com.boostcampwm2023.snappoint.data.remote.model.response.ImageUriResponse
import com.boostcampwm2023.snappoint.data.remote.model.response.LoginResponse
import retrofit2.Response
import com.boostcampwm2023.snappoint.data.remote.model.response.SignupResponse
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
Expand Down Expand Up @@ -36,4 +37,9 @@ interface SnapPointApi {

@GET("logout")
suspend fun getLogout()

@POST("signup")
suspend fun postSignUp(
@Body signupRequest: SignupRequest
): SignupResponse
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package com.boostcampwm2023.snappoint.data.repository

import com.boostcampwm2023.snappoint.data.remote.model.request.SignupRequest
import com.boostcampwm2023.snappoint.data.remote.model.response.LoginResponse
import com.boostcampwm2023.snappoint.data.remote.model.response.SignupResponse
import kotlinx.coroutines.flow.Flow

interface LoginRepository {

fun postLogin(email: String, password: String): Flow<LoginResponse>

fun getLogout(): Flow<Unit>

fun postSignup(email: String, password: String, nickname: String): Flow<SignupResponse>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package com.boostcampwm2023.snappoint.data.repository

import com.boostcampwm2023.snappoint.data.remote.SnapPointApi
import com.boostcampwm2023.snappoint.data.remote.model.request.LoginRequest
import com.boostcampwm2023.snappoint.data.remote.model.request.SignupRequest
import com.boostcampwm2023.snappoint.data.remote.model.response.LoginResponse
import com.boostcampwm2023.snappoint.data.remote.model.response.SignupResponse
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import javax.inject.Inject
Expand All @@ -31,4 +32,22 @@ class LoginRepositoryImpl @Inject constructor(
snapPointApi.getLogout()
}
}

override fun postSignup(
email: String,
password: String,
nickname: String
): Flow<SignupResponse> {

val request: SignupRequest = SignupRequest(
email = email,
password = password,
nickname = nickname
)

return flowOf(true)
.map {
snapPointApi.postSignUp(request)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.boostcampwm2023.snappoint.presentation.signup

sealed class SignupEvent {
data object Success : SignupEvent()
data class Fail(val messageResId: Int) : SignupEvent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ package com.boostcampwm2023.snappoint.presentation.signup
import android.os.Bundle
import android.view.View
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.boostcampwm2023.snappoint.R
import com.boostcampwm2023.snappoint.databinding.FragmentSignupBinding
import com.boostcampwm2023.snappoint.presentation.base.BaseFragment
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch

@AndroidEntryPoint
class SignupFragment : BaseFragment<FragmentSignupBinding>(R.layout.fragment_signup) {
Expand All @@ -18,6 +23,8 @@ class SignupFragment : BaseFragment<FragmentSignupBinding>(R.layout.fragment_sig

initBinding()
loadText()

collectViewModelData()
}

private fun initBinding() {
Expand All @@ -35,4 +42,27 @@ class SignupFragment : BaseFragment<FragmentSignupBinding>(R.layout.fragment_sig
tilSignUpNickname.editText?.setText(viewModel.uiState.value.nickname)
}
}

private fun collectViewModelData() {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
viewModel.event.collect { event ->
when (event) {
is SignupEvent.Fail -> {
showToastMessage(event.messageResId)
}

is SignupEvent.Success -> {
showToastMessage(R.string.signup_fragment_create_account_success)
navigateToLogin()
}
}
}
}
}
}

private fun navigateToLogin() {
findNavController().popBackStack()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ data class SignupUiState(
val passwordConfirm: String = "",
val nickname: String = "",
val isSignUpInProgress: Boolean = false,
val isInputValid: Boolean = false,
val emailCode: Int? = null,
val passwordCode: Int? = null,
val passwordConfirmCode: Int? = null,
val nicknameCode: Int? = null
val isButtonEnabled: Boolean = false,
val emailErrorResId: Int? = null,
val passwordErrorResId: Int? = null,
val passwordConfirmErrorResId: Int? = null,
val nicknameErrorResId: Int? = null
)
Original file line number Diff line number Diff line change
@@ -1,34 +1,55 @@
package com.boostcampwm2023.snappoint.presentation.signup

import android.util.Log
import android.util.Patterns
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.boostcampwm2023.snappoint.R
import com.boostcampwm2023.snappoint.data.repository.LoginRepository
import com.boostcampwm2023.snappoint.presentation.util.Constants
import com.boostcampwm2023.snappoint.presentation.util.TextVerificationUtil
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.channels.BufferOverflow
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.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.update
import javax.inject.Inject

@HiltViewModel
class SignupViewModel @Inject constructor() : ViewModel() {

private val _uiState: MutableStateFlow<SignupUiState> = MutableStateFlow(SignupUiState(
email = "[email protected]",
password = "asdASD123!@#",
passwordConfirm = "asdASD123!@#",
nickname = "nickname",
isInputValid = true
))
class SignupViewModel @Inject constructor(
private val loginRepository: LoginRepository
) : ViewModel() {

private val _uiState: MutableStateFlow<SignupUiState> = MutableStateFlow(
SignupUiState(
email = "[email protected]",
password = "asdASD123!@#",
passwordConfirm = "asdASD123!@#",
nickname = "nickname",
isButtonEnabled = true
)
)
val uiState: StateFlow<SignupUiState> = _uiState.asStateFlow()

private val _event: MutableSharedFlow<SignupEvent> = MutableSharedFlow(
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val event: SharedFlow<SignupEvent> = _event.asSharedFlow()

fun updateEmail(email: String) {
_uiState.update {
it.copy(
email = email,
emailCode = takeEmailErrorCode(email)
emailErrorResId = getEmailErrorCode(email)
)
}
updateButtonState()
Expand All @@ -38,7 +59,7 @@ class SignupViewModel @Inject constructor() : ViewModel() {
_uiState.update {
it.copy(
password = password,
passwordCode = takePasswordErrorCode(password)
passwordErrorResId = getPasswordErrorCode(password)
)
}
updatePasswordConfirm()
Expand All @@ -49,7 +70,7 @@ class SignupViewModel @Inject constructor() : ViewModel() {
_uiState.update {
it.copy(
passwordConfirm = password,
passwordConfirmCode = takePasswordConfirmErrorCode(password)
passwordConfirmErrorResId = getPasswordConfirmErrorCode(password)
)
}
updateButtonState()
Expand All @@ -58,7 +79,7 @@ class SignupViewModel @Inject constructor() : ViewModel() {
private fun updatePasswordConfirm() {
_uiState.update {
it.copy(
passwordConfirmCode = takePasswordConfirmErrorCode(it.passwordConfirm)
passwordConfirmErrorResId = getPasswordConfirmErrorCode(it.passwordConfirm)
)
}
}
Expand All @@ -67,7 +88,7 @@ class SignupViewModel @Inject constructor() : ViewModel() {
_uiState.update {
it.copy(
nickname = nickname,
nicknameCode = takeNicknameErrorCode(nickname)
nicknameErrorResId = getNicknameErrorCode(nickname)
)
}
updateButtonState()
Expand All @@ -76,40 +97,83 @@ class SignupViewModel @Inject constructor() : ViewModel() {
private fun updateButtonState() {
_uiState.update {
it.copy(
isInputValid = it.emailCode == null &&
it.passwordCode == null &&
it.passwordConfirmCode == null &&
it.nicknameCode == null
isButtonEnabled = it.emailErrorResId == null &&
it.passwordErrorResId == null &&
it.passwordConfirmErrorResId == null &&
it.nicknameErrorResId == null
)
}
}

private fun takeEmailErrorCode(email: String): Int? {
private fun updateEmailErrorResId(message: String?) {
if (isMessageDuplicationError(message)) {
_uiState.update {
it.copy(
emailErrorResId = R.string.signup_fragment_error_email_duplicate,
isButtonEnabled = false
)
}
}
}

private fun getEmailErrorCode(email: String): Int? {
return if (TextVerificationUtil.isEmailValid(email)) null else R.string.signup_fragment_error_email_form
}

private fun takePasswordErrorCode(password: String): Int? {
private fun getPasswordErrorCode(password: String): Int? {
return if (TextVerificationUtil.isPasswordValid(password)) null else R.string.signup_fragment_error_password_length
}

private fun takePasswordConfirmErrorCode(password: String): Int? {
private fun getPasswordConfirmErrorCode(password: String): Int? {
return if (uiState.value.password == password) null else R.string.signup_fragment_error_password_confirm_mismatch
}

private fun takeNicknameErrorCode(nickname: String): Int? {
private fun getNicknameErrorCode(nickname: String): Int? {
return if (nickname.length > 1) null else R.string.signup_fragment_error_nickname
}

fun onSignUpButtonClicked() {
with(uiState.value) {
if (isInputValid.not()) {
if (isButtonEnabled.not()) {
return
}

val email = email
val password = password
val passwordConfirm = passwordConfirm
val nickname = nickname
loginRepository.postSignup(email, password, nickname)
.onStart {
setProgressBarState(true)
}
.onEach {
_event.emit(SignupEvent.Success)
}
.catch {
updateEmailErrorResId(it.message)
_event.emit(
SignupEvent.Fail(_uiState.value.emailErrorResId ?: R.string.signup_fragment_fail)
)
}
.onCompletion {
setProgressBarState(false)
}
.launchIn(viewModelScope)
}
}

private fun setProgressBarState(isInProgress: Boolean) {
_uiState.update {
it.copy(
isSignUpInProgress = isInProgress
)
}
}

private fun getErrorMessage(message: String?): Int? {
return when {
isMessageDuplicationError(message) -> R.string.signup_fragment_fail_duplicate
else -> null
}
}

private fun isMessageDuplicationError(message: String?): Boolean {
return message == Constants.EMAIL_DUPLICATE_ERROR
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ import com.boostcampwm2023.snappoint.BuildConfig
object Constants {
const val BOTTOM_SHEET_HALF_EXPANDED_RATIO: Float = 0.45f
const val API_KEY = BuildConfig.MAPS_API_KEY
const val EMAIL_DUPLICATE_ERROR: String = "HTTP 409 Conflict"
}
Loading

0 comments on commit 3563160

Please sign in to comment.