회원가입, 로그인 페이지 추가
This commit is contained in:
12
app/src/main/java/kr/co/vividnext/sodalive/user/Gender.kt
Normal file
12
app/src/main/java/kr/co/vividnext/sodalive/user/Gender.kt
Normal file
@@ -0,0 +1,12 @@
|
||||
package kr.co.vividnext.sodalive.user
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
enum class Gender {
|
||||
@SerializedName("MALE")
|
||||
MALE,
|
||||
@SerializedName("FEMALE")
|
||||
FEMALE,
|
||||
@SerializedName("NONE")
|
||||
NONE
|
||||
}
|
28
app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt
Normal file
28
app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt
Normal file
@@ -0,0 +1,28 @@
|
||||
package kr.co.vividnext.sodalive.user
|
||||
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest
|
||||
import kr.co.vividnext.sodalive.user.login.LoginRequest
|
||||
import kr.co.vividnext.sodalive.user.login.LoginResponse
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.Multipart
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Part
|
||||
|
||||
interface UserApi {
|
||||
@POST("/member/login")
|
||||
fun login(@Body request: LoginRequest): Single<ApiResponse<LoginResponse>>
|
||||
|
||||
@POST("/member/signup")
|
||||
@Multipart
|
||||
fun signUp(
|
||||
@Part profileImage: MultipartBody.Part?,
|
||||
@Part("request") request: RequestBody
|
||||
): Single<ApiResponse<LoginResponse>>
|
||||
|
||||
@POST("/member/forgot-password")
|
||||
fun findPassword(@Body request: ForgotPasswordRequest): Single<ApiResponse<Any>>
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package kr.co.vividnext.sodalive.user
|
||||
|
||||
import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest
|
||||
import kr.co.vividnext.sodalive.user.login.LoginRequest
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
|
||||
class UserRepository(private val userApi: UserApi) {
|
||||
fun login(request: LoginRequest) = userApi.login(request)
|
||||
|
||||
fun signUp(profileImage: MultipartBody.Part?, request: RequestBody) = userApi.signUp(
|
||||
profileImage,
|
||||
request
|
||||
)
|
||||
|
||||
fun findPassword(request: ForgotPasswordRequest) = userApi.findPassword(request = request)
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
package kr.co.vividnext.sodalive.user.find_password
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import com.jakewharton.rxbinding4.widget.textChanges
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityFindPasswordBinding
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class FindPasswordActivity : BaseActivity<ActivityFindPasswordBinding>(
|
||||
ActivityFindPasswordBinding::inflate
|
||||
) {
|
||||
|
||||
private val viewModel: FindPasswordViewModel by inject()
|
||||
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
bindData()
|
||||
}
|
||||
|
||||
override fun setupView() {
|
||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||
binding.toolbar.tvBack.text = "비밀번호 재설정"
|
||||
binding.toolbar.tvBack.setOnClickListener { finish() }
|
||||
|
||||
binding.tvServiceCenter.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
Uri.parse("http://pf.kakao.com/_sZaeb")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
binding.tvFindPassword.setOnClickListener { viewModel.findPassword { finish() } }
|
||||
}
|
||||
|
||||
private fun bindData() {
|
||||
compositeDisposable.add(
|
||||
binding.etEmail.textChanges().skip(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
viewModel.email = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
viewModel.toastLiveData.observe(this) {
|
||||
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
|
||||
}
|
||||
|
||||
viewModel.isLoading.observe(this) {
|
||||
if (it) {
|
||||
loadingDialog.show(screenWidth, "")
|
||||
} else {
|
||||
loadingDialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
package kr.co.vividnext.sodalive.user.find_password
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.orhanobut.logger.Logger
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kr.co.vividnext.sodalive.base.BaseViewModel
|
||||
import kr.co.vividnext.sodalive.user.UserRepository
|
||||
|
||||
class FindPasswordViewModel(private val repository: UserRepository) : BaseViewModel() {
|
||||
|
||||
var email = ""
|
||||
|
||||
private var _isLoading = MutableLiveData(false)
|
||||
val isLoading: LiveData<Boolean>
|
||||
get() = _isLoading
|
||||
|
||||
private val _toastLiveData = MutableLiveData<String?>()
|
||||
val toastLiveData: LiveData<String?>
|
||||
get() = _toastLiveData
|
||||
|
||||
fun findPassword(onSuccess: () -> Unit) {
|
||||
if (email.isBlank()) {
|
||||
_toastLiveData.postValue("이메일을 입력하세요.")
|
||||
return
|
||||
}
|
||||
|
||||
_isLoading.value = true
|
||||
val request = ForgotPasswordRequest(email = email)
|
||||
compositeDisposable.add(
|
||||
repository.findPassword(request)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
if (it.success) {
|
||||
_toastLiveData.postValue(
|
||||
"임시 비밀번호가 입력하신 이메일로 발송되었습니다. 이메일을 확인해 주세요."
|
||||
)
|
||||
onSuccess()
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package kr.co.vividnext.sodalive.user.find_password
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class ForgotPasswordRequest(@SerializedName("email") val email: String)
|
@@ -0,0 +1,123 @@
|
||||
package kr.co.vividnext.sodalive.user.login
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.widget.Toast
|
||||
import com.jakewharton.rxbinding4.widget.textChanges
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityLoginBinding
|
||||
import kr.co.vividnext.sodalive.main.MainActivity
|
||||
import kr.co.vividnext.sodalive.user.find_password.FindPasswordActivity
|
||||
import kr.co.vividnext.sodalive.user.signup.SignUpActivity
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class LoginActivity : BaseActivity<ActivityLoginBinding>(ActivityLoginBinding::inflate) {
|
||||
|
||||
private val viewModel: LoginViewModel by inject()
|
||||
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
bindData()
|
||||
}
|
||||
|
||||
override fun setupView() {
|
||||
binding.tvToolbar.text = "로그인"
|
||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||
|
||||
binding.tvLogin.setOnClickListener {
|
||||
viewModel.login {
|
||||
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
|
||||
finishAffinity()
|
||||
val nextIntent = Intent(applicationContext, MainActivity::class.java)
|
||||
val extras = intent.getBundleExtra(Constants.EXTRA_DATA)
|
||||
?: if (intent.extras != null) {
|
||||
intent.extras
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (extras != null) {
|
||||
nextIntent.putExtra(Constants.EXTRA_DATA, extras)
|
||||
}
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
startActivity(nextIntent)
|
||||
}
|
||||
}
|
||||
|
||||
binding.tvSignUp.setOnClickListener {
|
||||
val nextIntent = Intent(applicationContext, SignUpActivity::class.java)
|
||||
val extras = intent.getBundleExtra(Constants.EXTRA_DATA)
|
||||
?: if (intent.extras != null) {
|
||||
intent.extras
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (extras != null) {
|
||||
nextIntent.putExtra(Constants.EXTRA_DATA, extras)
|
||||
}
|
||||
startActivity(nextIntent)
|
||||
}
|
||||
|
||||
binding.tvForgotPassword.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(
|
||||
applicationContext,
|
||||
FindPasswordActivity::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
binding.tvVisiblePassword.setOnClickListener { viewModel.onClickVisiblePassword() }
|
||||
}
|
||||
|
||||
private fun bindData() {
|
||||
compositeDisposable.add(
|
||||
binding.etEmail.textChanges().skip(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
viewModel.email = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
binding.etPassword.textChanges().skip(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
viewModel.password = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
viewModel.toastLiveData.observe(this) {
|
||||
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
|
||||
}
|
||||
|
||||
viewModel.isLoading.observe(this) {
|
||||
if (it) {
|
||||
loadingDialog.show(screenWidth)
|
||||
} else {
|
||||
loadingDialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.visiblePasswordLiveData.observe(this) {
|
||||
binding.tvVisiblePassword.isSelected = it
|
||||
if (it) {
|
||||
binding.etPassword.inputType =
|
||||
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||
} else {
|
||||
binding.etPassword.inputType =
|
||||
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package kr.co.vividnext.sodalive.user.login
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class LoginRequest(
|
||||
@SerializedName("email") val email: String,
|
||||
@SerializedName("password") val password: String
|
||||
)
|
@@ -0,0 +1,11 @@
|
||||
package kr.co.vividnext.sodalive.user.login
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class LoginResponse(
|
||||
@SerializedName("userId") val userId: Long,
|
||||
@SerializedName("token") val token: String,
|
||||
@SerializedName("nickname") val nickname: String,
|
||||
@SerializedName("email") val email: String,
|
||||
@SerializedName("profileImage") val profileImage: String
|
||||
)
|
@@ -0,0 +1,78 @@
|
||||
package kr.co.vividnext.sodalive.user.login
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.orhanobut.logger.Logger
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kr.co.vividnext.sodalive.base.BaseViewModel
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.user.UserRepository
|
||||
|
||||
class LoginViewModel(private val repository: UserRepository) : BaseViewModel() {
|
||||
|
||||
var email = ""
|
||||
var password = ""
|
||||
|
||||
private val _visiblePasswordLiveData = MutableLiveData(false)
|
||||
val visiblePasswordLiveData: LiveData<Boolean>
|
||||
get() = _visiblePasswordLiveData
|
||||
|
||||
private val _toastLiveData = MutableLiveData<String?>()
|
||||
val toastLiveData: LiveData<String?>
|
||||
get() = _toastLiveData
|
||||
|
||||
private var _isLoading = MutableLiveData(false)
|
||||
val isLoading: LiveData<Boolean>
|
||||
get() = _isLoading
|
||||
|
||||
fun login(onSuccess: (String?) -> Unit) {
|
||||
if (email.isBlank()) {
|
||||
_toastLiveData.postValue("이메일을 입력하세요.")
|
||||
return
|
||||
}
|
||||
|
||||
if (password.isBlank()) {
|
||||
_toastLiveData.postValue("비밃번호를 입력하세요.")
|
||||
return
|
||||
}
|
||||
|
||||
_isLoading.value = true
|
||||
val request = LoginRequest(email, password)
|
||||
compositeDisposable.add(
|
||||
repository.login(request)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
_isLoading.value = false
|
||||
if (it.success && it.data != null) {
|
||||
SharedPreferenceManager.token = it.data.token
|
||||
SharedPreferenceManager.email = it.data.email
|
||||
SharedPreferenceManager.userId = it.data.userId
|
||||
SharedPreferenceManager.nickname = it.data.nickname
|
||||
SharedPreferenceManager.profileImage = it.data.profileImage
|
||||
onSuccess(it.message)
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue(
|
||||
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
_isLoading.value = false
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun onClickVisiblePassword() {
|
||||
_visiblePasswordLiveData.postValue(!_visiblePasswordLiveData.value!!)
|
||||
}
|
||||
}
|
@@ -0,0 +1,254 @@
|
||||
package kr.co.vividnext.sodalive.user.signup
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import coil.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
||||
import com.jakewharton.rxbinding4.widget.textChanges
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.common.RealPathUtil
|
||||
import kr.co.vividnext.sodalive.databinding.ActivitySignupBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.main.MainActivity
|
||||
import kr.co.vividnext.sodalive.settings.TermsActivity
|
||||
import kr.co.vividnext.sodalive.user.Gender
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class SignUpActivity : BaseActivity<ActivitySignupBinding>(ActivitySignupBinding::inflate) {
|
||||
|
||||
private val viewModel: SignUpViewModel by inject()
|
||||
private val onBackPressedCallback = object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
onClickBackButton()
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
|
||||
private val imageResult =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
val resultCode = result.resultCode
|
||||
val data = result.data
|
||||
|
||||
if (resultCode == RESULT_OK) {
|
||||
// Image Uri will not be null for RESULT_OK
|
||||
val fileUri = data?.data!!
|
||||
binding.ivProfile.background = null
|
||||
binding.ivProfile.load(fileUri) {
|
||||
crossfade(true)
|
||||
transformations(RoundedCornersTransformation(13.3f.dpToPx()))
|
||||
}
|
||||
viewModel.profileImageUri = fileUri
|
||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
||||
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
|
||||
|
||||
viewModel.getRealPathFromURI = {
|
||||
RealPathUtil.getRealPath(applicationContext, it)
|
||||
}
|
||||
|
||||
bindData()
|
||||
}
|
||||
|
||||
override fun setupView() {
|
||||
binding.toolbar.tvBack.text = "회원가입"
|
||||
binding.toolbar.tvBack.setOnClickListener { onClickBackButton() }
|
||||
|
||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||
|
||||
binding.ivPhotoPicker.setOnClickListener {
|
||||
ImagePicker.with(this)
|
||||
.crop()
|
||||
.galleryOnly()
|
||||
.galleryMimeTypes( // Exclude gif images
|
||||
mimeTypes = arrayOf(
|
||||
"image/png",
|
||||
"image/jpg",
|
||||
"image/jpeg"
|
||||
)
|
||||
)
|
||||
.createIntent { imageResult.launch(it) }
|
||||
}
|
||||
|
||||
binding.tvMale.setOnClickListener {
|
||||
viewModel.changeGender(Gender.MALE)
|
||||
}
|
||||
|
||||
binding.tvFemale.setOnClickListener {
|
||||
viewModel.changeGender(Gender.FEMALE)
|
||||
}
|
||||
|
||||
binding.tvNone.setOnClickListener {
|
||||
viewModel.changeGender(Gender.NONE)
|
||||
}
|
||||
|
||||
binding.tvTermsOfService.setOnClickListener {
|
||||
val intent = Intent(applicationContext, TermsActivity::class.java)
|
||||
intent.putExtra("terms", "terms")
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.tvPrivacyPolicy.setOnClickListener {
|
||||
val intent = Intent(applicationContext, TermsActivity::class.java)
|
||||
intent.putExtra("terms", "privacy")
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.ivTermsOfService.setOnClickListener {
|
||||
viewModel.onClickCheckboxTermsOfService()
|
||||
}
|
||||
|
||||
binding.ivPrivacyPolicy.setOnClickListener {
|
||||
viewModel.onClickCheckboxPrivacyPolicy()
|
||||
}
|
||||
|
||||
binding.tvSignUp.setOnClickListener {
|
||||
viewModel.signUp {
|
||||
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
|
||||
finishAffinity()
|
||||
val nextIntent = Intent(applicationContext, MainActivity::class.java)
|
||||
val extras = intent.getBundleExtra(Constants.EXTRA_DATA)
|
||||
?: if (intent.extras != null) {
|
||||
intent.extras
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (extras != null) {
|
||||
nextIntent.putExtra(Constants.EXTRA_DATA, extras)
|
||||
}
|
||||
startActivity(nextIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindData() {
|
||||
compositeDisposable.add(
|
||||
binding.etEmail.textChanges().skip(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
viewModel.email = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
binding.etPassword.textChanges().skip(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
viewModel.password = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
binding.etPasswordRe.textChanges().skip(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
viewModel.passwordRe = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
binding.etNickname.textChanges().skip(1)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
viewModel.nickname = it.toString()
|
||||
}
|
||||
)
|
||||
|
||||
viewModel.isAgreeTermsOfServiceLiveData.observe(this) {
|
||||
binding.ivTermsOfService.isSelected = it
|
||||
}
|
||||
|
||||
viewModel.isAgreePrivacyPolicyLiveData.observe(this) {
|
||||
binding.ivPrivacyPolicy.isSelected = it
|
||||
}
|
||||
|
||||
viewModel.genderLiveData.observe(this) {
|
||||
binding.tvMale.isSelected = false
|
||||
binding.tvFemale.isSelected = false
|
||||
binding.tvNone.isSelected = false
|
||||
|
||||
when (it) {
|
||||
Gender.MALE -> binding.tvMale.isSelected = true
|
||||
Gender.FEMALE -> binding.tvFemale.isSelected = true
|
||||
Gender.NONE -> binding.tvNone.isSelected = true
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.signUpErrorLiveData.observe(this) {
|
||||
Toast.makeText(applicationContext, it.message, Toast.LENGTH_LONG).show()
|
||||
|
||||
when (it.errorProperty) {
|
||||
"email" -> {
|
||||
viewModel.setStep(step = SignUpViewModel.EmailSignUpStep.STEP_1)
|
||||
binding.etEmail.error = it.message
|
||||
binding.etEmail.requestFocus()
|
||||
}
|
||||
|
||||
"password" -> {
|
||||
viewModel.setStep(step = SignUpViewModel.EmailSignUpStep.STEP_1)
|
||||
binding.etPassword.error = it.message
|
||||
binding.etPassword.requestFocus()
|
||||
}
|
||||
|
||||
"nickname" -> {
|
||||
binding.etNickname.error = it.message
|
||||
binding.etNickname.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.isLoading.observe(this) {
|
||||
if (it) {
|
||||
loadingDialog.show(screenWidth)
|
||||
} else {
|
||||
loadingDialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.toastLiveData.observe(this) {
|
||||
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
|
||||
}
|
||||
|
||||
viewModel.stepLiveData.observe(this) {
|
||||
if (it == SignUpViewModel.EmailSignUpStep.STEP_2) {
|
||||
binding.toolbar.tvBack.text = "프로필 설정"
|
||||
binding.tvSignUp.text = "회원가입"
|
||||
binding.llStep1.visibility = View.GONE
|
||||
binding.llStep2.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.toolbar.tvBack.text = "회원가입"
|
||||
binding.tvSignUp.text = "다음"
|
||||
binding.llStep1.visibility = View.VISIBLE
|
||||
binding.llStep2.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onClickBackButton() {
|
||||
if (viewModel.stepLiveData.value!! == SignUpViewModel.EmailSignUpStep.STEP_2) {
|
||||
viewModel.setStep(SignUpViewModel.EmailSignUpStep.STEP_1)
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package kr.co.vividnext.sodalive.user.signup
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class SignUpError(
|
||||
@SerializedName("errorProperty") val errorProperty: String,
|
||||
@SerializedName("message") val message: String
|
||||
)
|
@@ -0,0 +1,14 @@
|
||||
package kr.co.vividnext.sodalive.user.signup
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kr.co.vividnext.sodalive.user.Gender
|
||||
|
||||
data class SignUpRequest(
|
||||
@SerializedName("email") val email: String,
|
||||
@SerializedName("password") val password: String,
|
||||
@SerializedName("nickname") val nickname: String,
|
||||
@SerializedName("gender") val gender: Gender,
|
||||
@SerializedName("isAgreeTermsOfService") val isAgreeTermsOfService: Boolean,
|
||||
@SerializedName("isAgreePrivacyPolicy") val isAgreePrivacyPolicy: Boolean,
|
||||
@SerializedName("container") val container: String = "aos"
|
||||
)
|
@@ -0,0 +1,241 @@
|
||||
package kr.co.vividnext.sodalive.user.signup
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.core.util.PatternsCompat
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.orhanobut.logger.Logger
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kr.co.vividnext.sodalive.base.BaseViewModel
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.user.Gender
|
||||
import kr.co.vividnext.sodalive.user.UserRepository
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.io.File
|
||||
|
||||
class SignUpViewModel(private val repository: UserRepository) : BaseViewModel() {
|
||||
enum class EmailSignUpStep {
|
||||
@SerializedName("STEP_1") STEP_1,
|
||||
@SerializedName("STEP_2") STEP_2
|
||||
}
|
||||
|
||||
var email = ""
|
||||
var password = ""
|
||||
var passwordRe = ""
|
||||
var nickname = ""
|
||||
var profileImageUri: Uri? = null
|
||||
|
||||
private val _genderLiveData = MutableLiveData(Gender.NONE)
|
||||
val genderLiveData: LiveData<Gender>
|
||||
get() = _genderLiveData
|
||||
|
||||
private val _isAgreeTermsOfServiceLiveData = MutableLiveData(false)
|
||||
val isAgreeTermsOfServiceLiveData: LiveData<Boolean>
|
||||
get() = _isAgreeTermsOfServiceLiveData
|
||||
|
||||
private val _isAgreePrivacyPolicyLiveData = MutableLiveData(false)
|
||||
val isAgreePrivacyPolicyLiveData: LiveData<Boolean>
|
||||
get() = _isAgreePrivacyPolicyLiveData
|
||||
|
||||
private val _signUpErrorLiveData = MutableLiveData<SignUpError>()
|
||||
val signUpErrorLiveData: LiveData<SignUpError>
|
||||
get() = _signUpErrorLiveData
|
||||
|
||||
private val _toastLiveData = MutableLiveData<String?>()
|
||||
val toastLiveData: LiveData<String?>
|
||||
get() = _toastLiveData
|
||||
|
||||
private var _isLoading = MutableLiveData(false)
|
||||
val isLoading: LiveData<Boolean>
|
||||
get() = _isLoading
|
||||
|
||||
private val _stepLiveData = MutableLiveData(EmailSignUpStep.STEP_1)
|
||||
val stepLiveData: LiveData<EmailSignUpStep>
|
||||
get() = _stepLiveData
|
||||
|
||||
lateinit var getRealPathFromURI: (Uri) -> String?
|
||||
|
||||
fun setStep(step: EmailSignUpStep = EmailSignUpStep.STEP_2) {
|
||||
_stepLiveData.postValue(step)
|
||||
}
|
||||
|
||||
fun signUp(onSuccess: (String?) -> Unit) {
|
||||
if (stepLiveData.value!! == EmailSignUpStep.STEP_1) {
|
||||
if (validationStep1()) return
|
||||
|
||||
setStep()
|
||||
return
|
||||
}
|
||||
|
||||
if (validationStep2()) return
|
||||
|
||||
val request = SignUpRequest(
|
||||
email = email,
|
||||
password = password,
|
||||
nickname = nickname,
|
||||
gender = _genderLiveData.value!!,
|
||||
isAgreeTermsOfService = _isAgreeTermsOfServiceLiveData.value!!,
|
||||
isAgreePrivacyPolicy = _isAgreePrivacyPolicyLiveData.value!!
|
||||
)
|
||||
|
||||
val requestJson = Gson().toJson(request)
|
||||
|
||||
val profileImage = if (profileImageUri != null) {
|
||||
val file = File(getRealPathFromURI(profileImageUri!!))
|
||||
MultipartBody.Part.createFormData(
|
||||
"profileImage",
|
||||
file.name,
|
||||
file.asRequestBody("image/*".toMediaType())
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
repository.signUp(
|
||||
profileImage,
|
||||
requestJson.toRequestBody("text/plain".toMediaType())
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
if (it.success && it.data != null) {
|
||||
SharedPreferenceManager.token = it.data.token
|
||||
SharedPreferenceManager.email = it.data.email
|
||||
SharedPreferenceManager.userId = it.data.userId
|
||||
SharedPreferenceManager.nickname = it.data.nickname
|
||||
SharedPreferenceManager.profileImage = it.data.profileImage
|
||||
_isLoading.value = false
|
||||
onSuccess(it.message)
|
||||
} else {
|
||||
_isLoading.value = false
|
||||
if (it.errorProperty != null && it.message != null) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(it.errorProperty, it.message)
|
||||
)
|
||||
} else if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
} else {
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
it.message?.let { message -> Logger.e(message) }
|
||||
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun validationStep1(): Boolean {
|
||||
if (email.isBlank()) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(
|
||||
"email",
|
||||
"이메일을 입력하세요."
|
||||
)
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if (password.isBlank()) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(
|
||||
"password",
|
||||
"비밀번호를 입력하세요."
|
||||
)
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if (password != passwordRe) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(
|
||||
"password",
|
||||
"비밀번호가 일치하지 않습니다."
|
||||
)
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if (
|
||||
!_isAgreePrivacyPolicyLiveData.value!! ||
|
||||
!_isAgreeTermsOfServiceLiveData.value!!
|
||||
) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(
|
||||
"",
|
||||
"약관에 동의하셔야 회원가입이 가능합니다."
|
||||
)
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if (!PatternsCompat.EMAIL_ADDRESS.matcher(email).matches()) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(
|
||||
"email",
|
||||
"올바른 이메일을 입력해 주세요"
|
||||
)
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if (
|
||||
"^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d$@!%*#?&]{8,}$"
|
||||
.toRegex()
|
||||
.matches(password)
|
||||
.not()
|
||||
) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(
|
||||
"password",
|
||||
"영문, 숫자 포함 8자 이상의 비밀번호를 입력해 주세요."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun validationStep2(): Boolean {
|
||||
if (nickname.isBlank() || nickname.length < 2) {
|
||||
_signUpErrorLiveData.postValue(
|
||||
SignUpError(
|
||||
"nickname",
|
||||
"닉네임은 2자 이상 입력해 주세요."
|
||||
)
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun onClickCheckboxTermsOfService() {
|
||||
_isAgreeTermsOfServiceLiveData.postValue(!_isAgreeTermsOfServiceLiveData.value!!)
|
||||
}
|
||||
|
||||
fun onClickCheckboxPrivacyPolicy() {
|
||||
_isAgreePrivacyPolicyLiveData.postValue(!_isAgreePrivacyPolicyLiveData.value!!)
|
||||
}
|
||||
|
||||
fun changeGender(gender: Gender) {
|
||||
_genderLiveData.postValue(gender)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user