From 381274a1b6ca1cf653fb23068b004b19de0efd27 Mon Sep 17 00:00:00 2001 From: erik-everardo Date: Fri, 26 Jan 2024 22:59:19 -0600 Subject: [PATCH] feature cambiar password y refactor --- .../main/java/com/isolaatti/auth/AuthUtils.kt | 14 +++++ .../com/isolaatti/auth/domain/SignOutUC.kt | 10 ++- .../java/com/isolaatti/common/ResultDto.kt | 4 ++ .../java/com/isolaatti/login/LogInActivity.kt | 15 ++--- .../java/com/isolaatti/settings/Module.kt | 4 +- .../data/remote/AccountSettingsApi.kt | 3 +- .../data/remote/ChangePasswordResponseDto.kt | 1 - .../AccountSettingsRepositoryImpl.kt | 6 +- .../settings/ui/AccountSettingsFragment.kt | 10 +-- .../settings/ui/ChangePasswordFragment.kt | 62 +++++++++++++++++-- .../fragment_settings_change_password.xml | 2 +- app/src/main/res/values/strings.xml | 3 + 12 files changed, 100 insertions(+), 34 deletions(-) create mode 100644 app/src/main/java/com/isolaatti/auth/AuthUtils.kt create mode 100644 app/src/main/java/com/isolaatti/common/ResultDto.kt diff --git a/app/src/main/java/com/isolaatti/auth/AuthUtils.kt b/app/src/main/java/com/isolaatti/auth/AuthUtils.kt new file mode 100644 index 0000000..dfbaab4 --- /dev/null +++ b/app/src/main/java/com/isolaatti/auth/AuthUtils.kt @@ -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")) +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/auth/domain/SignOutUC.kt b/app/src/main/java/com/isolaatti/auth/domain/SignOutUC.kt index 83d21fa..16a428e 100644 --- a/app/src/main/java/com/isolaatti/auth/domain/SignOutUC.kt +++ b/app/src/main/java/com/isolaatti/auth/domain/SignOutUC.kt @@ -1,6 +1,8 @@ package com.isolaatti.auth.domain import android.content.Context +import android.content.Intent +import com.isolaatti.MainActivity import com.isolaatti.auth.data.local.TokenStorage import com.isolaatti.settings.domain.AccountSettingsRepository import dagger.hilt.android.qualifiers.ActivityContext @@ -8,10 +10,14 @@ import javax.inject.Inject class SignOutUC @Inject constructor( @ActivityContext private val context: Context, - private val tokenStorage: TokenStorage, - private val accountSettingsRepository: AccountSettingsRepository + private val tokenStorage: TokenStorage ) { 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) } } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/common/ResultDto.kt b/app/src/main/java/com/isolaatti/common/ResultDto.kt new file mode 100644 index 0000000..687684a --- /dev/null +++ b/app/src/main/java/com/isolaatti/common/ResultDto.kt @@ -0,0 +1,4 @@ +package com.isolaatti.common + +@JvmInline +value class ResultDto(val result: T) diff --git a/app/src/main/java/com/isolaatti/login/LogInActivity.kt b/app/src/main/java/com/isolaatti/login/LogInActivity.kt index 839f159..9844bff 100644 --- a/app/src/main/java/com/isolaatti/login/LogInActivity.kt +++ b/app/src/main/java/com/isolaatti/login/LogInActivity.kt @@ -1,18 +1,16 @@ package com.isolaatti.login import android.app.Activity -import android.net.Uri import android.os.Bundle import android.view.View import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import androidx.browser.customtabs.CustomTabsIntent import androidx.core.widget.doOnTextChanged import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.isolaatti.BuildConfig import com.isolaatti.R +import com.isolaatti.auth.openForgotPassword import com.isolaatti.databinding.ActivityLoginBinding import com.isolaatti.sign_up.ui.SignUpActivity import com.isolaatti.utils.Resource @@ -92,7 +90,7 @@ class LogInActivity : AppCompatActivity() { } viewBinding.forgotPasswordBtn.setOnClickListener { - openForgotPassword() + openForgotPassword(this) } 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() { MaterialAlertDialogBuilder(this) .setMessage(R.string.wrong_password) - .setNeutralButton(R.string.forgot_password) {_,_ -> openForgotPassword()} + .setNeutralButton(R.string.forgot_password) {_,_ -> openForgotPassword(this)} .setPositiveButton(R.string.dismiss, null) .show() } diff --git a/app/src/main/java/com/isolaatti/settings/Module.kt b/app/src/main/java/com/isolaatti/settings/Module.kt index 3c1fc8d..57407c0 100644 --- a/app/src/main/java/com/isolaatti/settings/Module.kt +++ b/app/src/main/java/com/isolaatti/settings/Module.kt @@ -33,7 +33,7 @@ class Module { } @Provides - fun provideAccountSettingsRepository(tokenStorage: TokenStorage, accountSettingsApi: AccountSettingsApi): AccountSettingsRepository { - return AccountSettingsRepositoryImpl(tokenStorage, accountSettingsApi) + fun provideAccountSettingsRepository(accountSettingsApi: AccountSettingsApi): AccountSettingsRepository { + return AccountSettingsRepositoryImpl(accountSettingsApi) } } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/settings/data/remote/AccountSettingsApi.kt b/app/src/main/java/com/isolaatti/settings/data/remote/AccountSettingsApi.kt index 4774ed9..e371b0a 100644 --- a/app/src/main/java/com/isolaatti/settings/data/remote/AccountSettingsApi.kt +++ b/app/src/main/java/com/isolaatti/settings/data/remote/AccountSettingsApi.kt @@ -1,5 +1,6 @@ package com.isolaatti.settings.data.remote +import com.isolaatti.common.ResultDto import retrofit2.Call import retrofit2.http.Body import retrofit2.http.GET @@ -15,7 +16,7 @@ interface AccountSettingsApi { @Body changePasswordDto: ChangePasswordDto, @Query("signOut") signOut: Boolean, @Query("signOutCurrent") signOutCurrent: Boolean - ): Call + ): Call> @POST("account/sign_out") fun signOut(): Call diff --git a/app/src/main/java/com/isolaatti/settings/data/remote/ChangePasswordResponseDto.kt b/app/src/main/java/com/isolaatti/settings/data/remote/ChangePasswordResponseDto.kt index 2cd3006..e6e870b 100644 --- a/app/src/main/java/com/isolaatti/settings/data/remote/ChangePasswordResponseDto.kt +++ b/app/src/main/java/com/isolaatti/settings/data/remote/ChangePasswordResponseDto.kt @@ -5,7 +5,6 @@ data class ChangePasswordResponseDto( val reason: String? ) { companion object { - const val ReasonUserDoesNotExist = "user_does_not_exist"; const val ReasonOldPasswordMismatch = "old_password_mismatch"; const val ReasonNewPasswordInvalid = "new_password_invalid"; } diff --git a/app/src/main/java/com/isolaatti/settings/data/repository/AccountSettingsRepositoryImpl.kt b/app/src/main/java/com/isolaatti/settings/data/repository/AccountSettingsRepositoryImpl.kt index 67b46d6..af7e0b2 100644 --- a/app/src/main/java/com/isolaatti/settings/data/repository/AccountSettingsRepositoryImpl.kt +++ b/app/src/main/java/com/isolaatti/settings/data/repository/AccountSettingsRepositoryImpl.kt @@ -1,6 +1,5 @@ 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.ChangePasswordDto import com.isolaatti.settings.data.remote.ChangePasswordResponseDto @@ -14,7 +13,6 @@ import retrofit2.awaitResponse import javax.inject.Inject class AccountSettingsRepositoryImpl @Inject constructor( - private val tokenStorage: TokenStorage, private val accountSettingsApi: AccountSettingsApi ) : AccountSettingsRepository { override fun logout(): Flow> = flow { @@ -26,8 +24,6 @@ class AccountSettingsRepositoryImpl @Inject constructor( } else { emit(Resource.Error(Resource.Error.mapErrorCode(response.code()))) } - tokenStorage.removeToken() - } catch (exception: Exception) { emit(Resource.Error(Resource.Error.ErrorType.NetworkError)) } @@ -66,7 +62,7 @@ class AccountSettingsRepositoryImpl @Inject constructor( try { val response = accountSettingsApi.changePassword(ChangePasswordDto(oldPassword, newPassword), signOut, signOutCurrent).awaitResponse() if(response.isSuccessful) { - emit(Resource.Success(response.body())) + emit(Resource.Success(response.body()?.result)) } else { emit(Resource.Error(Resource.Error.mapErrorCode(response.code()))) } diff --git a/app/src/main/java/com/isolaatti/settings/ui/AccountSettingsFragment.kt b/app/src/main/java/com/isolaatti/settings/ui/AccountSettingsFragment.kt index a36b500..7619862 100644 --- a/app/src/main/java/com/isolaatti/settings/ui/AccountSettingsFragment.kt +++ b/app/src/main/java/com/isolaatti/settings/ui/AccountSettingsFragment.kt @@ -9,17 +9,20 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.isolaatti.MainActivity import com.isolaatti.R +import com.isolaatti.auth.domain.SignOutUC import com.isolaatti.databinding.FragmentAccountSettingsBinding import com.isolaatti.settings.presentation.AccountSettingsViewModel import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject @AndroidEntryPoint class AccountSettingsFragment : Fragment() { private lateinit var binding: FragmentAccountSettingsBinding private val viewModel: AccountSettingsViewModel by viewModels() + @Inject + lateinit var signOutUC: SignOutUC override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -51,10 +54,7 @@ class AccountSettingsFragment : Fragment() { viewModel.loggedOut.observe(viewLifecycleOwner) { if(it) { - val loginIntent = Intent(requireContext(), MainActivity::class.java).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - } - startActivity(loginIntent) + signOutUC() viewModel.loggedOut.value = false } } diff --git a/app/src/main/java/com/isolaatti/settings/ui/ChangePasswordFragment.kt b/app/src/main/java/com/isolaatti/settings/ui/ChangePasswordFragment.kt index 6745684..4e5a97e 100644 --- a/app/src/main/java/com/isolaatti/settings/ui/ChangePasswordFragment.kt +++ b/app/src/main/java/com/isolaatti/settings/ui/ChangePasswordFragment.kt @@ -12,7 +12,9 @@ import androidx.navigation.fragment.findNavController import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.isolaatti.R import com.isolaatti.auth.domain.SignOutUC +import com.isolaatti.auth.openForgotPassword import com.isolaatti.databinding.FragmentSettingsChangePasswordBinding +import com.isolaatti.settings.data.remote.ChangePasswordResponseDto import com.isolaatti.settings.presentation.ChangePasswordViewModel import com.isolaatti.utils.Resource import dagger.hilt.android.AndroidEntryPoint @@ -109,8 +111,8 @@ class ChangePasswordFragment : Fragment() { viewBinding.newPasswordInputText.error = if(valid) null else getString(R.string.password_req) } - viewModel.passwordChangeResource.observe(viewLifecycleOwner) { - when(it) { + viewModel.passwordChangeResource.observe(viewLifecycleOwner) { resource -> + when(resource) { is Resource.Loading -> { lockControls(true) showLoadingDialog() @@ -124,14 +126,62 @@ class ChangePasswordFragment : Fragment() { is Resource.Success -> { loadingDialog?.dismiss() loadingDialog = null - if(viewModel.signOutCurrent) { - signOut() + if(resource.data?.success == true) { + handlePasswordChangeSuccess() } else { - - findNavController().popBackStack() + handlePasswordChangeError(resource.data?.reason) } } } } } + + private fun handlePasswordChangeSuccess() { + if(viewModel.signOutCurrent) { + signOut() + } else { + 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() + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_settings_change_password.xml b/app/src/main/res/layout/fragment_settings_change_password.xml index 4956c6a..4832a77 100644 --- a/app/src/main/res/layout/fragment_settings_change_password.xml +++ b/app/src/main/res/layout/fragment_settings_change_password.xml @@ -142,7 +142,7 @@ android:layout_height="wrap_content" android:text="@string/sign_out_this_device" android:layout_marginHorizontal="16dp" - android:enabled="false" + android:enabled="true" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 70ceaac..af8c37f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -162,4 +162,7 @@ Continue Password must contain at least 8 characters an have the following: 1–9, a-z, A-Z, special character. Changing password… + Recover password + Password not changed, old password did not match. If you don\'t remember your password, you can always recover it. + New password is invalid. Please check it meets the requirements \ No newline at end of file