-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FEAT/#5] 1주차 심화과제 (XML) #7
base: develop-xml
Are you sure you want to change the base?
Changes from all commits
499c08f
a499fec
91d353a
e2fa1a8
64c9ba0
f175e11
d2083f2
83f0679
a2752a4
2341e06
ec44355
4d867c5
7f8b14a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,9 +5,9 @@ import kotlinx.android.parcel.Parcelize | |
|
||
@Parcelize | ||
data class User( | ||
val id: String, | ||
val password: String, | ||
val nickname: String, | ||
val phoneNumber: String | ||
val id: String? = "", | ||
val password: String? = "", | ||
val nickname: String? = "", | ||
val phoneNumber: String? = "" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. c: 트레일링 콤마에 대해 아실까요? |
||
) : Parcelable | ||
|
||
Comment on lines
6
to
13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 기본값이 empty 인데 nullable한 타입인 이유는 null로 넣어주는 상황이 있어서 그런건가요? |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,23 +1,50 @@ | ||||||
package com.sopt.now.presentation.ui | ||||||
|
||||||
import android.content.Intent | ||||||
import android.os.Bundle | ||||||
import androidx.activity.OnBackPressedCallback | ||||||
import androidx.appcompat.app.AppCompatActivity | ||||||
import com.sopt.now.R | ||||||
import com.sopt.now.data.User | ||||||
import com.sopt.now.databinding.ActivityMainBinding | ||||||
import com.sopt.now.presentation.utils.KeyStorage | ||||||
import com.sopt.now.presentation.utils.getSafeParcelable | ||||||
import com.sopt.now.presentation.utils.showToast | ||||||
|
||||||
class MainActivity : AppCompatActivity() { | ||||||
private lateinit var binding: ActivityMainBinding | ||||||
private var user: User? = null | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
private var backPressedTime: Long = 0 | ||||||
|
||||||
override fun onCreate(savedInstanceState: Bundle?) { | ||||||
super.onCreate(savedInstanceState) | ||||||
binding = ActivityMainBinding.inflate(layoutInflater) | ||||||
setContentView(binding.root) | ||||||
|
||||||
getUserInfo() | ||||||
showUserInfo() | ||||||
|
||||||
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) { | ||||||
override fun handleOnBackPressed() { | ||||||
if (System.currentTimeMillis() - backPressedTime < 2000) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
안정성 말고 별 차이는 존재하지 않지만 간격 측정이나 타이머 같은 경우 이렇게 쓰기도 한답니다~ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. c: 매직넘버의 상수화에 대해 아시나요? |
||||||
isEnabled = false | ||||||
onBackPressedDispatcher.onBackPressed() | ||||||
|
||||||
val intent = Intent(Intent.ACTION_MAIN) | ||||||
intent.addCategory(Intent.CATEGORY_HOME) | ||||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP | ||||||
startActivity(intent) | ||||||
finish() | ||||||
Comment on lines
+33
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. c: 함수로 분리해줘도 괜찮을 것 같아요 |
||||||
} else { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. c: 'else 예약어를 쓰지 않는다' 라는 말을 아시나요? when으로 분기해도 가독성이 좋아보일듯요 |
||||||
showToast( | ||||||
context = this@MainActivity, | ||||||
message = getString(R.string.mypage_back_handler_caution) | ||||||
) | ||||||
backPressedTime = System.currentTimeMillis() | ||||||
} | ||||||
} | ||||||
}) | ||||||
Comment on lines
+27
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 요거 모든 액티비티에서 사용된다면 확장함수로 빼는 방법도 좋을 것 같슴다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. base의 함정에 빠지지 않도록 신중히 결정해보세요! |
||||||
|
||||||
} | ||||||
|
||||||
private fun getUserInfo() { | ||||||
|
@@ -37,4 +64,4 @@ class MainActivity : AppCompatActivity() { | |||||
tvMyPagePhoneNumberContent.text = user?.phoneNumber | ||||||
} | ||||||
} | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ package com.sopt.now.presentation.ui.auth.signin | |
import android.content.Intent | ||
import android.os.Bundle | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.lifecycle.ViewModelProvider | ||
import com.sopt.now.R | ||
import com.sopt.now.data.User | ||
import com.sopt.now.databinding.ActivitySigninBinding | ||
|
@@ -14,34 +15,43 @@ import com.sopt.now.presentation.utils.showToast | |
|
||
class SignInActivity : AppCompatActivity() { | ||
private lateinit var binding: ActivitySigninBinding | ||
private var user: User? = null | ||
private lateinit var viewModel: SignInViewModel | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. c: 조금 게으르게 선언은 어떠심 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. by viewModels() 알아보세요! |
||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
binding = ActivitySigninBinding.inflate(layoutInflater) | ||
setContentView(binding.root) | ||
viewModel = ViewModelProvider(this).get(SignInViewModel::class.java) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
getUserInfo() | ||
onSignInClicked() | ||
onSignUpClicked() | ||
} | ||
|
||
private fun getUserInfo() { | ||
user = intent.getSafeParcelable(KeyStorage.USER_INFO, User::class.java) | ||
val user = intent.getSafeParcelable(KeyStorage.USER_INFO, User::class.java) | ||
viewModel.setUser(user) | ||
} | ||
Comment on lines
31
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. viewModel에서 SavedStateHandle라는걸 쓰면 Intent에 직접 접근해서 값을 Setting할 수 있습니다. |
||
|
||
private fun setupObservers() { | ||
viewModel.signInState.observe(this) { isSuccess -> | ||
if (isSuccess) { | ||
showToast(this, getString(R.string.signin_signin_success)) | ||
navigateToMain(viewModel.user.value) | ||
} else { | ||
showToast(this, getString(R.string.signin_signin_failure)) | ||
} | ||
} | ||
} | ||
|
||
private fun onSignInClicked() { | ||
binding.btnSignInSignIn.setOnClickListener { | ||
with(binding) { | ||
val inputId = etSignInId.text.toString() | ||
val inputPassword = etSignInPw.text.toString() | ||
|
||
if (inputId == user?.id && inputPassword == user?.password) { | ||
showToast(this@SignInActivity, getString(R.string.signin_signin_success)) | ||
navigateToMain() | ||
} else { | ||
showToast(this@SignInActivity, getString(R.string.signin_signin_failure)) | ||
} | ||
viewModel.validateSignIn(inputId, inputPassword) | ||
} | ||
setupObservers() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ViewModel의 |
||
} | ||
} | ||
|
||
|
@@ -51,7 +61,7 @@ class SignInActivity : AppCompatActivity() { | |
} | ||
} | ||
|
||
private fun navigateToMain() { | ||
private fun navigateToMain(user: User?) { | ||
val intent = Intent(this@SignInActivity, MainActivity::class.java) | ||
intent.putExtra(KeyStorage.USER_INFO, user) | ||
startActivity(intent) | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,22 @@ | ||||||||||||||||||||||||
package com.sopt.now.presentation.ui.auth.signin | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
import androidx.lifecycle.LiveData | ||||||||||||||||||||||||
import androidx.lifecycle.MutableLiveData | ||||||||||||||||||||||||
import androidx.lifecycle.ViewModel | ||||||||||||||||||||||||
import com.sopt.now.data.User | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
class SignInViewModel : ViewModel() { | ||||||||||||||||||||||||
private val _user = MutableLiveData<User?>() | ||||||||||||||||||||||||
val user: LiveData<User?> = _user | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
private val _signInState = MutableLiveData(false) | ||||||||||||||||||||||||
val signInState: LiveData<Boolean> = _signInState | ||||||||||||||||||||||||
Comment on lines
+8
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Kotlin 2.0을 쓴다면? explicit backing fields 를 활용할 수 있어욥
Suggested change
|
||||||||||||||||||||||||
|
||||||||||||||||||||||||
fun setUser(user: User?) { | ||||||||||||||||||||||||
_user.value = user | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
fun validateSignIn(inputId: String, inputPassword: String) { | ||||||||||||||||||||||||
_signInState.value = inputId == _user.value?.id && inputPassword == _user.value?.password | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
c: _가 붙는 의미가 무엇일까요? |
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ package com.sopt.now.presentation.ui.auth.signup | |
import android.content.Intent | ||
import android.os.Bundle | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.lifecycle.ViewModelProvider | ||
import com.sopt.now.R | ||
import com.sopt.now.data.User | ||
import com.sopt.now.databinding.ActivitySignupBinding | ||
|
@@ -12,14 +13,28 @@ import com.sopt.now.presentation.utils.showToast | |
|
||
class SignUpActivity : AppCompatActivity() { | ||
private lateinit var binding: ActivitySignupBinding | ||
private lateinit var viewModel: SignUpViewModel | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
binding = ActivitySignupBinding.inflate(layoutInflater) | ||
setContentView(binding.root) | ||
viewModel = ViewModelProvider(this).get(SignUpViewModel::class.java) | ||
|
||
onSignUpClicked() | ||
} | ||
|
||
private fun setupObservers() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. c: 여러개의 옵저버를 세팅해주고 있는지 함수명에 대해 고민해봅시다! |
||
viewModel.signUpState.observe(this) { isSuccess -> | ||
if (isSuccess) { | ||
showToast(this@SignUpActivity, getString(R.string.signup_signup_success)) | ||
navigateToSignIn(viewModel.user.value) | ||
} else { | ||
showToast(this@SignUpActivity, getString(R.string.signup_signup_failure)) | ||
} | ||
} | ||
} | ||
|
||
private fun onSignUpClicked() { | ||
binding.btnSignUpSignUp.setOnClickListener { | ||
with(binding) { | ||
|
@@ -29,19 +44,14 @@ class SignUpActivity : AppCompatActivity() { | |
etSignUpNickname.text.toString(), | ||
etSignUpPhoneNumber.text.toString() | ||
) | ||
|
||
if (SignUpValidation.isSignUpValid(user) | ||
) { | ||
showToast(this@SignUpActivity, getString(R.string.signup_signup_success)) | ||
navigateToSignIn(user) | ||
} else { | ||
showToast(this@SignUpActivity, getString(R.string.signup_signup_failure)) | ||
} | ||
viewModel.setUser(user) | ||
} | ||
viewModel.validateSignUp() | ||
setupObservers() | ||
} | ||
Comment on lines
+49
to
51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 클릭 될때마다 옵저버가 구독되면 어떤 일이 발생할까요?! |
||
} | ||
|
||
private fun navigateToSignIn(user: User) { | ||
private fun navigateToSignIn(user: User?) { | ||
val intent = Intent(this@SignUpActivity, SignInActivity::class.java) | ||
intent.putExtra(KeyStorage.USER_INFO, user) | ||
startActivity(intent) | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,63 @@ | ||||||||||
package com.sopt.now.presentation.ui.auth.signup | ||||||||||
|
||||||||||
import androidx.lifecycle.LiveData | ||||||||||
import androidx.lifecycle.MutableLiveData | ||||||||||
import androidx.lifecycle.ViewModel | ||||||||||
import com.sopt.now.data.User | ||||||||||
import java.util.regex.Pattern | ||||||||||
|
||||||||||
class SignUpViewModel() : ViewModel() { | ||||||||||
private val _user = MutableLiveData<User?>() | ||||||||||
val user: LiveData<User?> = _user | ||||||||||
|
||||||||||
private val _signUpState = MutableLiveData(false) | ||||||||||
val signUpState: LiveData<Boolean> = _signUpState | ||||||||||
|
||||||||||
fun setUser(user: User) { | ||||||||||
_user.value = user | ||||||||||
} | ||||||||||
|
||||||||||
private fun isIdValid(): Boolean { | ||||||||||
return ID_VALIDATION_PATTERN.matcher(_user.value?.id ?: "").matches() | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. orEmpty() 에 대해 찾아보시면 좋을 것 같네요 ㅎ.ㅎ |
||||||||||
} | ||||||||||
|
||||||||||
private fun isPasswordValid(): Boolean { | ||||||||||
return PW_VALIDATION_PATTERN.matcher(_user.value?.password ?: "").matches() | ||||||||||
} | ||||||||||
Comment on lines
+24
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
이렇게 쓸 수도 있습니당 |
||||||||||
|
||||||||||
private fun isNicknameValid(): Boolean { | ||||||||||
return NICKNAME_VALIDATION_PATTERN.matcher(_user.value?.nickname ?: "").matches() | ||||||||||
} | ||||||||||
|
||||||||||
private fun isPhoneNumberValid(): Boolean { | ||||||||||
return PHONE_NUMBER_VALIDATION_PATTERN.matcher(_user.value?.phoneNumber ?: "").matches() | ||||||||||
} | ||||||||||
|
||||||||||
fun validateSignUp() { | ||||||||||
_signUpState.value = isIdValid() && | ||||||||||
isPasswordValid() && | ||||||||||
isNicknameValid() && | ||||||||||
isPhoneNumberValid() | ||||||||||
} | ||||||||||
Comment on lines
+28
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 도메인이 생긴다면 이 로직은 어디에 두고 어떻게 사용할지 고민하는 것도 재밌는 고민이 될 것 같습니다 |
||||||||||
|
||||||||||
companion object { | ||||||||||
private const val ID_MIN_LENGTH = 6 | ||||||||||
private const val ID_MAX_LENGTH = 10 | ||||||||||
private const val PW_MIN_LENGTH = 8 | ||||||||||
private const val PW_MAX_LENGTH = 12 | ||||||||||
|
||||||||||
private const val ID_VALIDATION_REGEX = "^[a-zA-Z0-9]{$ID_MIN_LENGTH,$ID_MAX_LENGTH}$" | ||||||||||
private val ID_VALIDATION_PATTERN: Pattern = Pattern.compile(ID_VALIDATION_REGEX) | ||||||||||
|
||||||||||
private const val PW_VALIDATION_REGEX = "^[a-zA-Z0-9]{$PW_MIN_LENGTH,$PW_MAX_LENGTH}$" | ||||||||||
private val PW_VALIDATION_PATTERN: Pattern = Pattern.compile(PW_VALIDATION_REGEX) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 코틀린에서는 Pattern 말고 Regex를 이용해 정규식을 사용할 수 있는데 이에 대해 공부해보셔도 좋을 것 같아요! |
||||||||||
|
||||||||||
private const val NICKNAME_VALIDATION_REGEX = "^\\S+$" | ||||||||||
private val NICKNAME_VALIDATION_PATTERN: Pattern = | ||||||||||
Pattern.compile(NICKNAME_VALIDATION_REGEX) | ||||||||||
|
||||||||||
private const val PHONE_NUMBER_VALIDATION_REGEX = "^010-\\d{4}-\\d{4}$" | ||||||||||
private val PHONE_NUMBER_VALIDATION_PATTERN: Pattern = | ||||||||||
Pattern.compile(PHONE_NUMBER_VALIDATION_REGEX) | ||||||||||
} | ||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
c: empty값을 기본적으로 부여하는 이유가 있으실까요?