Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,76 @@ package org.sopt.dosopttemplate.presentation.auth

import android.content.Intent
import android.os.Bundle
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import androidx.activity.viewModels
import androidx.core.content.ContextCompat
import dagger.hilt.android.AndroidEntryPoint
import org.sopt.dosopttemplate.R
import org.sopt.dosopttemplate.data.datasource.local.DoSoptStorage
import org.sopt.dosopttemplate.databinding.ActivitySignupBinding
import org.sopt.dosopttemplate.util.UiState
import org.sopt.dosopttemplate.util.binding.BindingActivity
import org.sopt.dosopttemplate.util.extension.setOnSingleClickListener
import org.sopt.dosopttemplate.util.extension.setVisible
import org.sopt.dosopttemplate.util.extension.showSnackbar
import java.util.regex.Pattern

@AndroidEntryPoint
class SignUpActivity : BindingActivity<ActivitySignupBinding>(R.layout.activity_signup) {

private val signUpViewModel: SignUpViewModel by viewModels()
private lateinit var id: String
private lateinit var pw: String
private lateinit var name: String
private lateinit var mbti: String

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.signupViewModel = signUpViewModel

clickSignUpEnd()
observeId()
observePw()
observeSignUpButton()
}

private fun observeSignUpButton() {
signUpViewModel.isEnabledLoginButton.observe(this) {
binding.btnSignupEnd.isEnabled = it
}
}

private fun observePw() {
signUpViewModel.pw.observe(this) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

observe가 상태변화가 있을 만한 곳에 넣는 거 맞나요? 어떤 기준으로 observe를 쓰는지 궁금합니당

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상태변화가 있을 만한 곳이라기 보다는 상태변화 감지가 필요한 곳이 맞는것 같습니다!

이 경우에는 pw가 비밀번호 입력에 대한 LiveData이므로 상태가 변화할 때 마다 감지되어서
비밀번호 입력 조건에 부합한지 확인 후에 오류메세지의 visible처리를 해야되므로 필요하죠!

여기도 함수 분리가 필요해 보이긴 하는데 귀찮음 이슈로 패스....

if (signUpViewModel.isValidatePassWord(it)) {
binding.tvSignupPasswordErrorMessage.setVisible(INVISIBLE)
binding.etSignupPw.backgroundTintList =
ContextCompat.getColorStateList(this, R.color.Gray_500)
} else {
binding.tvSignupPasswordErrorMessage.setVisible(VISIBLE)
binding.etSignupPw.backgroundTintList =
ContextCompat.getColorStateList(this, R.color.Pink_500)
}
}
}

private fun observeId() {
signUpViewModel.id.observe(this) {
if (signUpViewModel.isValidateId(it)) {
binding.tvSignupIdErrorMessage.setVisible(INVISIBLE)
binding.etSignupId.backgroundTintList =
ContextCompat.getColorStateList(this, R.color.Gray_500)
} else {
binding.tvSignupIdErrorMessage.setVisible(VISIBLE)
binding.etSignupId.backgroundTintList =
ContextCompat.getColorStateList(this, R.color.Pink_500)
}
}
}

private fun clickSignUpEnd() {
binding.btnSignupEnd.setOnSingleClickListener {
getSignUpInfo()
if (isValidateForm()) {
signUpViewModel.signUp(id, pw, name)
observeSignUp()
} else {
showSnackbar(binding.root, getString(R.string.signup_error_message))
}
signUpViewModel.signUp()
observeSignUp()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

observeSignUp 함수만 onCreate에서 실행시켜주지 않은 이유가 있나염?? 걍 진짜 궁금합니당

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

버튼이 조건에 안맞으면 항상 비활성화 상태라 여기다 두었는데 그냥 onCreate에 두는게 더 명확해 보이네요! 감삼다~

}
}

Expand All @@ -62,40 +96,9 @@ class SignUpActivity : BindingActivity<ActivitySignupBinding>(R.layout.activity_

private fun getSignUpInfo() {
with(binding) {
id = etSignupId.text.toString()
pw = etSignupPw.text.toString()
name = etSignupName.text.toString()
mbti = etSignupMbti.text.toString()
}
}

private fun isValidateForm(): Boolean {

return isValidateId(id) && isValidatePassWord(pw) && isValidateNickName(name) &&
isValidateMbti(mbti)
}

private fun isValidateId(id: String): Boolean {
return id.isNotBlank() && ID_REGEX.matcher(id).matches()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

왕 근데 새삼,, 삭제된 함수들도 진짜 간결하구 야무지네요.... 삭제됐지만 배워야겠는데여

private fun isValidatePassWord(pw: String): Boolean {
return pw.isNotBlank() && PW_REGEX.matcher(pw).matches()
}

private fun isValidateNickName(name: String): Boolean {
return name.isNotBlank()
}

private fun isValidateMbti(mbti: String): Boolean {
return mbti.isNotBlank()
}

companion object {
private const val ID_PATTERN = "(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]{6,10}"
private const val PW_PATTERN =
"(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#%^&*()])[a-zA-Z0-9!@#%^&*()]{8,12}"
val ID_REGEX: Pattern = Pattern.compile(ID_PATTERN)
val PW_REGEX: Pattern = Pattern.compile(PW_PATTERN)
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package org.sopt.dosopttemplate.presentation.auth

import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.map
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import org.sopt.dosopttemplate.data.repository.auth.AuthRepository
import org.sopt.dosopttemplate.util.UiState
import org.sopt.dosopttemplate.util.extension.addSourceList
import java.util.regex.Pattern
import javax.inject.Inject

@HiltViewModel
Expand All @@ -18,17 +22,57 @@ class SignUpViewModel @Inject constructor(
private val _signUpResult = MutableLiveData<UiState<Boolean>>()
val signUpResult: LiveData<UiState<Boolean>> = _signUpResult

fun signUp(username: String, password: String, nickname: String) {
val id = MutableLiveData("")
val pw = MutableLiveData("")
val nickname = MutableLiveData("")
val mbti = MutableLiveData("")

val isEnabledLoginButton = MediatorLiveData<Boolean>().apply {
addSourceList(id, pw, nickname, mbti) { isValidSignUpInfo() }
}
Comment on lines +30 to +32
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 확장함수 진짜 짱이네염 배워갑니둥,,,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

편안해지죠


val nicknameLength = nickname.map {
it.length
}

fun signUp() {
viewModelScope.launch {
authRepository.postSignUpInfo(
username = username,
password = password,
nickname = nickname
username = id.value.toString(),
password = pw.value.toString(),
nickname = nickname.value.toString()
).onSuccess { _signUpResult.value = UiState.Success(true) }

.onFailure { throwable ->
_signUpResult.value = throwable.message?.let { UiState.Failure(it) }
}
}
}

private fun isValidSignUpInfo() = isValidateId(id.value) && isValidatePassWord(pw.value)
&& isValidateNickName() && isValidateMbti()

fun isValidateId(id: String?): Boolean {
return id.isNullOrEmpty() || ID_REGEX.matcher(id).matches()
}

fun isValidatePassWord(pw: String?): Boolean {
return pw.isNullOrEmpty() || PW_REGEX.matcher(pw).matches()
}

private fun isValidateNickName(): Boolean {
return nickname.value?.isNotBlank() ?: false
}

private fun isValidateMbti(): Boolean {
return mbti.value?.isNotBlank() ?: false
}

companion object {
private const val ID_PATTERN = "(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]{6,10}"
private const val PW_PATTERN =
"(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#%^&*()])[a-zA-Z0-9!@#%^&*()]{6,12}"
val ID_REGEX: Pattern = Pattern.compile(ID_PATTERN)
val PW_REGEX: Pattern = Pattern.compile(PW_PATTERN)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
package org.sopt.dosopttemplate.util.binding

import android.view.View
import android.widget.TextView
import androidx.databinding.BindingAdapter

object BindingAdapter {
@JvmStatic
@BindingAdapter("setSignupZeroErrorTextVisible")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요런식으루 작성하는 건 먼가요?! Adapter를 Binding하는데 이런식으로 쓰는 건가요?? 아님 어댑터 안의 어떤 요소를 바인딩하는 것인지.. 아니면 어댑터에 끼워줄 애를 이렇게 넣는 것인지..

fun setSignupZeroErrorTextVisible(view: TextView, length: Int) {
if (length == 0) {
view.visibility = View.VISIBLE
} else {
view.visibility = View.INVISIBLE
}
}

}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 파일은 어떤 역할을 하는건가요?? 이해가 어렵...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MediatorLiveData에 대한 확장함수 입니다!
궁금하면 안드 확장함수 모음집에 남겨뒀으니 보고 적용해봐도 좋을것 같네요!

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.sopt.dosopttemplate.util.extension

import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData

fun <T> MediatorLiveData<T>.addSourceList(
vararg liveDataArgument: MutableLiveData<*>,
onChanged: () -> T
) {
liveDataArgument.forEach {
this.addSource(it) { value = onChanged() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ fun showSnackbar(view: View, message: String, isShort: Boolean = true) {
Snackbar.make(view, message, duration).show()
}

fun View.setVisible(visibility: Int) {
this.visibility = visibility
}


inline fun View.setOnSingleClickListener(
delay: Long = 500L,
crossinline block: (View) -> Unit,
Expand Down
43 changes: 43 additions & 0 deletions app/src/main/res/layout/activity_signup.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

<data>

<variable
name="signupViewModel"
type="org.sopt.dosopttemplate.presentation.auth.SignUpViewModel" />

</data>

<androidx.constraintlayout.widget.ConstraintLayout
Expand Down Expand Up @@ -55,10 +59,23 @@
android:hint="@string/signup_nickname_hint"
android:imeOptions="actionNext"
android:inputType="text"
android:text="@={signupViewModel.nickname}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_signup_name" />

<TextView
android:id="@+id/tv_signup_nickname_error_message"
style="@style/Caption2"
setSignupZeroErrorTextVisible="@{signupViewModel.nicknameLength}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/signup_nickname_empty"
android:textColor="@color/Pink_500"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="@+id/et_signup_name"
app:layout_constraintTop_toBottomOf="@+id/et_signup_name" />

<TextView
android:id="@+id/tv_signup_id"
style="@style/Headline"
Expand All @@ -81,10 +98,22 @@
android:hint="@string/signup_id_hint"
android:imeOptions="actionNext"
android:inputType="text"
android:text="@={signupViewModel.id}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_signup_id" />

<TextView
android:id="@+id/tv_signup_id_error_message"
style="@style/Caption2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/signup_id_error"
android:textColor="@color/Pink_500"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="@+id/et_signup_id"
app:layout_constraintTop_toBottomOf="@+id/et_signup_id" />

<TextView
android:id="@+id/tv_signup_pw"
style="@style/Headline"
Expand All @@ -106,10 +135,22 @@
android:hint="@string/signup_pw_hint"
android:imeOptions="actionNext"
android:inputType="textPassword"
android:text="@={signupViewModel.pw}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_signup_pw" />

<TextView
android:id="@+id/tv_signup_password_error_message"
style="@style/Caption2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/signup_pw_error"
android:textColor="@color/Pink_500"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="@+id/et_signup_pw"
app:layout_constraintTop_toBottomOf="@+id/et_signup_pw" />

<TextView
android:id="@+id/tv_signup_mbti"
style="@style/Headline"
Expand All @@ -131,6 +172,7 @@
android:hint="@string/signup_mbti_hint"
android:imeOptions="actionDone"
android:inputType="text"
android:text="@={signupViewModel.mbti}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_signup_mbti" />
Expand All @@ -145,6 +187,7 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="30dp"
android:layout_marginBottom="40dp"
android:enabled="false"
android:text="@string/signup_complete"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
<string name="signup_complete_message">회원가입이 완료되었습니다</string>
<string name="signup_error_message">올바른 정보를 입력해주세요</string>
<string name="signup_fail_message">회원가입에 실패하였습니다.</string>
<string name="signup_id_error">숫자와 영문자 조합으로 6~10자를 사용해주세요</string>
<string name="signup_pw_error">영문,숫자, 특수문자를 포함한 6~12글자 이내를 사용해주세요</string>
<string name="signup_nickname_empty">닉네임이 입력되지 않았습니다</string>

<!-- 메인 페이지 -->
<string name="mainpage_id">ID</string>
Expand Down