feature cambiar password y refactor

This commit is contained in:
erik-everardo 2024-01-26 22:59:19 -06:00
parent e33d1c279a
commit 381274a1b6
12 changed files with 100 additions and 34 deletions

View File

@ -0,0 +1,14 @@
package com.isolaatti.auth
import android.content.Context
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import com.isolaatti.BuildConfig
const val RecoverPasswordRelativePath = "/recuperacion_cuenta"
fun openForgotPassword(context: Context) {
CustomTabsIntent.Builder()
.setShowTitle(true)
.build()
.launchUrl(context, Uri.parse("${BuildConfig.backend}$RecoverPasswordRelativePath"))
}

View File

@ -1,6 +1,8 @@
package com.isolaatti.auth.domain package com.isolaatti.auth.domain
import android.content.Context import android.content.Context
import android.content.Intent
import com.isolaatti.MainActivity
import com.isolaatti.auth.data.local.TokenStorage import com.isolaatti.auth.data.local.TokenStorage
import com.isolaatti.settings.domain.AccountSettingsRepository import com.isolaatti.settings.domain.AccountSettingsRepository
import dagger.hilt.android.qualifiers.ActivityContext import dagger.hilt.android.qualifiers.ActivityContext
@ -8,10 +10,14 @@ import javax.inject.Inject
class SignOutUC @Inject constructor( class SignOutUC @Inject constructor(
@ActivityContext private val context: Context, @ActivityContext private val context: Context,
private val tokenStorage: TokenStorage, private val tokenStorage: TokenStorage
private val accountSettingsRepository: AccountSettingsRepository
) { ) {
operator fun invoke() { operator fun invoke() {
tokenStorage.removeToken()
val loginIntent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
context.startActivity(loginIntent)
} }
} }

View File

@ -0,0 +1,4 @@
package com.isolaatti.common
@JvmInline
value class ResultDto<T>(val result: T)

View File

@ -1,18 +1,16 @@
package com.isolaatti.login package com.isolaatti.login
import android.app.Activity import android.app.Activity
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.isolaatti.BuildConfig
import com.isolaatti.R import com.isolaatti.R
import com.isolaatti.auth.openForgotPassword
import com.isolaatti.databinding.ActivityLoginBinding import com.isolaatti.databinding.ActivityLoginBinding
import com.isolaatti.sign_up.ui.SignUpActivity import com.isolaatti.sign_up.ui.SignUpActivity
import com.isolaatti.utils.Resource import com.isolaatti.utils.Resource
@ -92,7 +90,7 @@ class LogInActivity : AppCompatActivity() {
} }
viewBinding.forgotPasswordBtn.setOnClickListener { viewBinding.forgotPasswordBtn.setOnClickListener {
openForgotPassword() openForgotPassword(this)
} }
viewBinding.signUpBtn.setOnClickListener { viewBinding.signUpBtn.setOnClickListener {
@ -101,17 +99,12 @@ class LogInActivity : AppCompatActivity() {
} }
private fun openForgotPassword() {
CustomTabsIntent.Builder()
.setShowTitle(true)
.build()
.launchUrl(this, Uri.parse("${BuildConfig.backend}/recuperacion_cuenta"))
}
private fun showWrongPasswordErrorMessage() { private fun showWrongPasswordErrorMessage() {
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setMessage(R.string.wrong_password) .setMessage(R.string.wrong_password)
.setNeutralButton(R.string.forgot_password) {_,_ -> openForgotPassword()} .setNeutralButton(R.string.forgot_password) {_,_ -> openForgotPassword(this)}
.setPositiveButton(R.string.dismiss, null) .setPositiveButton(R.string.dismiss, null)
.show() .show()
} }

View File

@ -33,7 +33,7 @@ class Module {
} }
@Provides @Provides
fun provideAccountSettingsRepository(tokenStorage: TokenStorage, accountSettingsApi: AccountSettingsApi): AccountSettingsRepository { fun provideAccountSettingsRepository(accountSettingsApi: AccountSettingsApi): AccountSettingsRepository {
return AccountSettingsRepositoryImpl(tokenStorage, accountSettingsApi) return AccountSettingsRepositoryImpl(accountSettingsApi)
} }
} }

View File

@ -1,5 +1,6 @@
package com.isolaatti.settings.data.remote package com.isolaatti.settings.data.remote
import com.isolaatti.common.ResultDto
import retrofit2.Call import retrofit2.Call
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
@ -15,7 +16,7 @@ interface AccountSettingsApi {
@Body changePasswordDto: ChangePasswordDto, @Body changePasswordDto: ChangePasswordDto,
@Query("signOut") signOut: Boolean, @Query("signOut") signOut: Boolean,
@Query("signOutCurrent") signOutCurrent: Boolean @Query("signOutCurrent") signOutCurrent: Boolean
): Call<ChangePasswordResponseDto> ): Call<ResultDto<ChangePasswordResponseDto>>
@POST("account/sign_out") @POST("account/sign_out")
fun signOut(): Call<Any> fun signOut(): Call<Any>

View File

@ -5,7 +5,6 @@ data class ChangePasswordResponseDto(
val reason: String? val reason: String?
) { ) {
companion object { companion object {
const val ReasonUserDoesNotExist = "user_does_not_exist";
const val ReasonOldPasswordMismatch = "old_password_mismatch"; const val ReasonOldPasswordMismatch = "old_password_mismatch";
const val ReasonNewPasswordInvalid = "new_password_invalid"; const val ReasonNewPasswordInvalid = "new_password_invalid";
} }

View File

@ -1,6 +1,5 @@
package com.isolaatti.settings.data.repository package com.isolaatti.settings.data.repository
import com.isolaatti.auth.data.local.TokenStorage
import com.isolaatti.settings.data.remote.AccountSettingsApi import com.isolaatti.settings.data.remote.AccountSettingsApi
import com.isolaatti.settings.data.remote.ChangePasswordDto import com.isolaatti.settings.data.remote.ChangePasswordDto
import com.isolaatti.settings.data.remote.ChangePasswordResponseDto import com.isolaatti.settings.data.remote.ChangePasswordResponseDto
@ -14,7 +13,6 @@ import retrofit2.awaitResponse
import javax.inject.Inject import javax.inject.Inject
class AccountSettingsRepositoryImpl @Inject constructor( class AccountSettingsRepositoryImpl @Inject constructor(
private val tokenStorage: TokenStorage,
private val accountSettingsApi: AccountSettingsApi private val accountSettingsApi: AccountSettingsApi
) : AccountSettingsRepository { ) : AccountSettingsRepository {
override fun logout(): Flow<Resource<Boolean>> = flow { override fun logout(): Flow<Resource<Boolean>> = flow {
@ -26,8 +24,6 @@ class AccountSettingsRepositoryImpl @Inject constructor(
} else { } else {
emit(Resource.Error(Resource.Error.mapErrorCode(response.code()))) emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
} }
tokenStorage.removeToken()
} catch (exception: Exception) { } catch (exception: Exception) {
emit(Resource.Error(Resource.Error.ErrorType.NetworkError)) emit(Resource.Error(Resource.Error.ErrorType.NetworkError))
} }
@ -66,7 +62,7 @@ class AccountSettingsRepositoryImpl @Inject constructor(
try { try {
val response = accountSettingsApi.changePassword(ChangePasswordDto(oldPassword, newPassword), signOut, signOutCurrent).awaitResponse() val response = accountSettingsApi.changePassword(ChangePasswordDto(oldPassword, newPassword), signOut, signOutCurrent).awaitResponse()
if(response.isSuccessful) { if(response.isSuccessful) {
emit(Resource.Success(response.body())) emit(Resource.Success(response.body()?.result))
} else { } else {
emit(Resource.Error(Resource.Error.mapErrorCode(response.code()))) emit(Resource.Error(Resource.Error.mapErrorCode(response.code())))
} }

View File

@ -9,17 +9,20 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.isolaatti.MainActivity
import com.isolaatti.R import com.isolaatti.R
import com.isolaatti.auth.domain.SignOutUC
import com.isolaatti.databinding.FragmentAccountSettingsBinding import com.isolaatti.databinding.FragmentAccountSettingsBinding
import com.isolaatti.settings.presentation.AccountSettingsViewModel import com.isolaatti.settings.presentation.AccountSettingsViewModel
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class AccountSettingsFragment : Fragment() { class AccountSettingsFragment : Fragment() {
private lateinit var binding: FragmentAccountSettingsBinding private lateinit var binding: FragmentAccountSettingsBinding
private val viewModel: AccountSettingsViewModel by viewModels() private val viewModel: AccountSettingsViewModel by viewModels()
@Inject
lateinit var signOutUC: SignOutUC
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -51,10 +54,7 @@ class AccountSettingsFragment : Fragment() {
viewModel.loggedOut.observe(viewLifecycleOwner) { viewModel.loggedOut.observe(viewLifecycleOwner) {
if(it) { if(it) {
val loginIntent = Intent(requireContext(), MainActivity::class.java).apply { signOutUC()
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
startActivity(loginIntent)
viewModel.loggedOut.value = false viewModel.loggedOut.value = false
} }
} }

View File

@ -12,7 +12,9 @@ import androidx.navigation.fragment.findNavController
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.isolaatti.R import com.isolaatti.R
import com.isolaatti.auth.domain.SignOutUC import com.isolaatti.auth.domain.SignOutUC
import com.isolaatti.auth.openForgotPassword
import com.isolaatti.databinding.FragmentSettingsChangePasswordBinding import com.isolaatti.databinding.FragmentSettingsChangePasswordBinding
import com.isolaatti.settings.data.remote.ChangePasswordResponseDto
import com.isolaatti.settings.presentation.ChangePasswordViewModel import com.isolaatti.settings.presentation.ChangePasswordViewModel
import com.isolaatti.utils.Resource import com.isolaatti.utils.Resource
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -109,8 +111,8 @@ class ChangePasswordFragment : Fragment() {
viewBinding.newPasswordInputText.error = if(valid) null else getString(R.string.password_req) viewBinding.newPasswordInputText.error = if(valid) null else getString(R.string.password_req)
} }
viewModel.passwordChangeResource.observe(viewLifecycleOwner) { viewModel.passwordChangeResource.observe(viewLifecycleOwner) { resource ->
when(it) { when(resource) {
is Resource.Loading -> { is Resource.Loading -> {
lockControls(true) lockControls(true)
showLoadingDialog() showLoadingDialog()
@ -124,14 +126,62 @@ class ChangePasswordFragment : Fragment() {
is Resource.Success -> { is Resource.Success -> {
loadingDialog?.dismiss() loadingDialog?.dismiss()
loadingDialog = null loadingDialog = null
if(resource.data?.success == true) {
handlePasswordChangeSuccess()
} else {
handlePasswordChangeError(resource.data?.reason)
}
}
}
}
}
private fun handlePasswordChangeSuccess() {
if(viewModel.signOutCurrent) { if(viewModel.signOutCurrent) {
signOut() signOut()
} else { } else {
findNavController().popBackStack() findNavController().popBackStack()
} }
} }
private fun handlePasswordChangeError(error: String?) {
when(error) {
ChangePasswordResponseDto.ReasonOldPasswordMismatch -> {
viewBinding.switcher.showNext()
showOldPasswordIsNotCorrectDialog()
}
ChangePasswordResponseDto.ReasonNewPasswordInvalid -> {
showNewPasswordIsInvalid()
}
// No special handling for "user_does_not_exist" as this
// error should not be faced by app user
else -> {
showUnknownErrorDialog()
} }
} }
} }
private fun showOldPasswordIsNotCorrectDialog() {
MaterialAlertDialogBuilder(requireContext())
.setMessage(R.string.old_password_not_correct)
.setNeutralButton(R.string.recover_password){_, _ ->
openForgotPassword(requireContext())
}
.setPositiveButton(R.string.dismiss, null)
.show()
}
private fun showNewPasswordIsInvalid() {
MaterialAlertDialogBuilder(requireContext())
.setMessage(R.string.new_password_is_invalid)
.setPositiveButton(R.string.dismiss, null)
.show()
}
private fun showUnknownErrorDialog() {
MaterialAlertDialogBuilder(requireContext())
.setMessage(R.string.unknown_error)
.setPositiveButton(R.string.dismiss, null)
.show()
}
} }

View File

@ -142,7 +142,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/sign_out_this_device" android:text="@string/sign_out_this_device"
android:layout_marginHorizontal="16dp" android:layout_marginHorizontal="16dp"
android:enabled="false" android:enabled="true"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0" app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View File

@ -162,4 +162,7 @@
<string name="continuar">Continue</string> <string name="continuar">Continue</string>
<string name="password_req">Password must contain at least 8 characters an have the following: 19, a-z, A-Z, special character.</string> <string name="password_req">Password must contain at least 8 characters an have the following: 19, a-z, A-Z, special character.</string>
<string name="changing_password">Changing password…</string> <string name="changing_password">Changing password…</string>
<string name="recover_password">Recover password</string>
<string name="old_password_not_correct">Password not changed, old password did not match. If you don\'t remember your password, you can always recover it.</string>
<string name="new_password_is_invalid">New password is invalid. Please check it meets the requirements</string>
</resources> </resources>