Skip to content

Commit

Permalink
feat: 회원가입 이메일 인증 추가 및 state 데이터 바인딩
Browse files Browse the repository at this point in the history
  • Loading branch information
ootr47 committed Mar 25, 2024
1 parent 85e5514 commit c4e79a4
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ class EmailVerificationFragment : Fragment() {
if (isFindPassword != null) {
emailVerificationViewModel.updateType(isFindPassword)
} else {
Toast.makeText(requireActivity(), getString(R.string.undefined_error), Toast.LENGTH_LONG).show()
Toast.makeText(
requireActivity(),
getString(R.string.undefined_error),
Toast.LENGTH_LONG
).show()
requireActivity().finish()
}

Expand Down Expand Up @@ -89,7 +93,7 @@ class EmailVerificationFragment : Fragment() {
EmailVerificationEvent.NotFoundEmail -> {
showDialogWithAction(
getString(R.string.error_request_verification_code),
getString(R.string.invalid_email)
getString(R.string.not_found_email)
)
}

Expand All @@ -107,17 +111,17 @@ class EmailVerificationFragment : Fragment() {
)
}

EmailVerificationEvent.UndefinedError -> {
EmailVerificationEvent.ExpireToken -> {
showDialogWithAction(
getString(R.string.error),
getString(R.string.undefined_error)
getString(R.string.error_verify_email),
getString(R.string.expire_verification_code)
)
}

EmailVerificationEvent.ExpireToken -> {
else -> {
showDialogWithAction(
getString(R.string.error_verify_email),
getString(R.string.expire_verification_code)
getString(R.string.error),
getString(R.string.undefined_error)
)
}
}
Expand All @@ -133,7 +137,10 @@ class EmailVerificationFragment : Fragment() {
val seconds = timeLeft - TimeUnit.MINUTES.toSeconds(minutes)

emailVerificationViewModel.updateTimer(
getString(R.string.finish_send_verification_code, String.format("%02d:%02d", minutes, seconds))
getString(
R.string.finish_send_verification_code,
String.format("%02d:%02d", minutes, seconds)
)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ class EmailVerificationViewModel @Inject constructor(
private val authRepository: AuthRepository
) : ViewModel() {

enum class EmailVerificationType {
REGISTER_VERIFICATION,
VERIFICATION
}

data class EmailVerificationState(
val email: String = "",
val verificationCode: String = "",
Expand All @@ -39,6 +44,7 @@ class EmailVerificationViewModel @Inject constructor(
data object NotFoundEmail : EmailVerificationEvent()
data object ExpireToken : EmailVerificationEvent()
data object WrongVerificationCode : EmailVerificationEvent()
data object DuplicatedEmail : EmailVerificationEvent()
data object OverVerificationLimit : EmailVerificationEvent()
data object UndefinedError : EmailVerificationEvent()
}
Expand All @@ -49,17 +55,26 @@ class EmailVerificationViewModel @Inject constructor(
private val _event = MutableSharedFlow<EmailVerificationEvent>()
val event = _event.asSharedFlow()

fun requestVerificationCode() {
fun requestVerificationCode(type: EmailVerificationType) {
_state.value = _state.value.copy(isRequestedVerificationCode = true)

viewModelScope.launch {
when (val response = authRepository.requestVerificationCode(_state.value.email)) {
val response = if (type == EmailVerificationType.VERIFICATION) {
authRepository.requestVerificationCode(_state.value.email)
} else {
authRepository.requestRegisterVerificationCode(_state.value.email)
}
when (response) {
is RepositoryResult.Error -> {
when (response.errorState) {
AuthErrorState.NOT_FOUND -> {
_event.emit(EmailVerificationEvent.NotFoundEmail)
}

AuthErrorState.DUPLICATED_EMAIL -> {
_event.emit(EmailVerificationEvent.DuplicatedEmail)
}

AuthErrorState.OVER_LIMIT -> {
_event.emit(EmailVerificationEvent.OverVerificationLimit)
}
Expand Down
141 changes: 76 additions & 65 deletions android/app/src/main/java/app/priceguard/ui/signup/SignupActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package app.priceguard.ui.signup

import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
Expand All @@ -11,32 +12,36 @@ import androidx.databinding.DataBindingUtil
import app.priceguard.R
import app.priceguard.databinding.ActivitySignupBinding
import app.priceguard.ui.home.HomeActivity
import app.priceguard.ui.login.findpassword.EmailVerificationViewModel
import app.priceguard.ui.signup.SignupViewModel.SignupEvent
import app.priceguard.ui.signup.SignupViewModel.SignupUIState
import app.priceguard.ui.util.SystemNavigationColorState
import app.priceguard.ui.util.applySystemNavigationBarColor
import app.priceguard.ui.util.drawable.getCircularProgressIndicatorDrawable
import app.priceguard.ui.util.lifecycle.repeatOnStarted
import app.priceguard.ui.util.showConfirmDialog
import app.priceguard.ui.util.showDialogWithAction
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.AppBarLayout.Behavior.DragCallback
import com.google.android.material.button.MaterialButton
import com.google.android.material.progressindicator.CircularProgressIndicatorSpec
import com.google.android.material.progressindicator.IndeterminateDrawable
import dagger.hilt.android.AndroidEntryPoint
import java.util.concurrent.TimeUnit

@AndroidEntryPoint
class SignupActivity : AppCompatActivity() {

private lateinit var binding: ActivitySignupBinding
private val signupViewModel: SignupViewModel by viewModels()
private val emailVerificationViewModel: EmailVerificationViewModel by viewModels()
private lateinit var circularProgressIndicator: IndeterminateDrawable<CircularProgressIndicatorSpec>

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.applySystemNavigationBarColor(SystemNavigationColorState.SURFACE)
binding = DataBindingUtil.setContentView(this, R.layout.activity_signup)
binding.vm = signupViewModel
binding.verificationViewModel = emailVerificationViewModel
binding.lifecycleOwner = this
circularProgressIndicator = getCircularProgressIndicatorDrawable(this@SignupActivity)
setNavigationButton()
Expand Down Expand Up @@ -89,15 +94,24 @@ class SignupActivity : AppCompatActivity() {
}

SignupEvent.DuplicatedEmail -> {
showConfirmDialog(getString(R.string.error), getString(R.string.duplicate_email))
showConfirmDialog(
getString(R.string.error),
getString(R.string.duplicate_email)
)
}

SignupEvent.InvalidRequest -> {
showConfirmDialog(getString(R.string.error), getString(R.string.invalid_parameter))
showConfirmDialog(
getString(R.string.error),
getString(R.string.invalid_parameter)
)
}

SignupEvent.UndefinedError -> {
showConfirmDialog(getString(R.string.error), getString(R.string.undefined_error))
showConfirmDialog(
getString(R.string.error),
getString(R.string.undefined_error)
)
}

SignupEvent.TokenUpdateError, SignupEvent.FirebaseError -> {
Expand Down Expand Up @@ -128,85 +142,82 @@ class SignupActivity : AppCompatActivity() {
}

private fun observeState() {
repeatOnStarted {
signupViewModel.state.collect { state ->
updateNameTextFieldUI(state)
updateEmailTextFieldUI(state)
updatePasswordTextFieldUI(state)
updateRetypePasswordTextFieldUI(state)
}
}

repeatOnStarted {
signupViewModel.eventFlow.collect { event ->
handleSignupEvent(event)
}
}
}

private fun updateNameTextFieldUI(state: SignupUIState) {
when (state.isNameError) {
true -> {
binding.tilSignupName.error = getString(R.string.invalid_name)
}

else -> {
binding.tilSignupName.error = null
}
}
}

private fun updateEmailTextFieldUI(state: SignupUIState) {
when (state.isEmailError) {
null -> {
binding.tilSignupEmail.error = null
binding.tilSignupEmail.helperText = " "
}
repeatOnStarted {
emailVerificationViewModel.event.collect { event ->
when (event) {
EmailVerificationViewModel.EmailVerificationEvent.SuccessRequestVerificationCode -> {
Toast.makeText(
this@SignupActivity,
getString(R.string.sent_verification_code),
Toast.LENGTH_LONG
).show()
startTimer(180)
}

true -> {
binding.tilSignupEmail.error = getString(R.string.invalid_email)
}
EmailVerificationViewModel.EmailVerificationEvent.DuplicatedEmail -> {
showDialogWithAction(
getString(R.string.error_email),
getString(R.string.duplicate_email)
)
}

false -> {
binding.tilSignupEmail.error = null
binding.tilSignupEmail.helperText = getString(R.string.valid_email)
}
}
}
EmailVerificationViewModel.EmailVerificationEvent.WrongVerificationCode -> {
showDialogWithAction(
getString(R.string.error_verify_email),
getString(R.string.match_error_verification_code)
)
}

private fun updatePasswordTextFieldUI(state: SignupUIState) {
when (state.isPasswordError) {
null -> {
binding.tilSignupPassword.error = null
binding.tilSignupPassword.helperText = " "
}
EmailVerificationViewModel.EmailVerificationEvent.OverVerificationLimit -> {
showDialogWithAction(
getString(R.string.error_request_verification_code),
getString(R.string.request_verification_code_limit_max_5)
)
}

true -> {
binding.tilSignupPassword.error = getString(R.string.invalid_password)
}
EmailVerificationViewModel.EmailVerificationEvent.ExpireToken -> {
showDialogWithAction(
getString(R.string.error_verify_email),
getString(R.string.expire_verification_code)
)
}

false -> {
binding.tilSignupPassword.error = null
binding.tilSignupPassword.helperText = getString(R.string.valid_password)
else -> {
showDialogWithAction(
getString(R.string.error),
getString(R.string.undefined_error)
)
}
}
}
}
}

private fun updateRetypePasswordTextFieldUI(state: SignupUIState) {
when (state.isRetypePasswordError) {
null -> {
binding.tilSignupRetypePassword.error = null
binding.tilSignupRetypePassword.helperText = " "
}
private fun startTimer(totalTimeInSeconds: Int) {
val countDownTimer = object : CountDownTimer((totalTimeInSeconds * 1000).toLong(), 1000) {
override fun onTick(millisUntilFinished: Long) {
val timeLeft = millisUntilFinished / 1000
val minutes = TimeUnit.SECONDS.toMinutes(timeLeft)
val seconds = timeLeft - TimeUnit.MINUTES.toSeconds(minutes)

true -> {
binding.tilSignupRetypePassword.error = getString(R.string.password_mismatch)
emailVerificationViewModel.updateTimer(
getString(
R.string.finish_send_verification_code,
String.format("%02d:%02d", minutes, seconds)
)
)
}

false -> {
binding.tilSignupRetypePassword.error = null
binding.tilSignupRetypePassword.helperText = getString(R.string.password_match)
override fun onFinish() {
emailVerificationViewModel.updateTimer(getString(R.string.expired))
emailVerificationViewModel.updateRetryVerificationCodeEnabled(true)
}
}
countDownTimer.start()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ class SignupViewModel @Inject constructor(
private val _state: MutableStateFlow<SignupUIState> = MutableStateFlow(SignupUIState())
val state: StateFlow<SignupUIState> = _state.asStateFlow()

private val _eventFlow: MutableSharedFlow<SignupEvent> = MutableSharedFlow(replay = 0)
private val _eventFlow: MutableSharedFlow<SignupEvent> = MutableSharedFlow()
val eventFlow: SharedFlow<SignupEvent> = _eventFlow.asSharedFlow()

fun signup() {
fun signup(verificationCode: String) {
if (_state.value.isSignupStarted || _state.value.isSignupFinished) {
Log.d("Signup", "Signup already requested. Skipping")
return
Expand All @@ -70,7 +70,7 @@ class SignupViewModel @Inject constructor(
Log.d("ViewModel", "Event Start Sent")

val result =
authRepository.signUp(_state.value.email, _state.value.name, _state.value.password)
authRepository.signUp(_state.value.email, _state.value.name, _state.value.password, verificationCode)

when (result) {
is RepositoryResult.Success -> {
Expand Down
Loading

0 comments on commit c4e79a4

Please sign in to comment.