Skip to content

Commit 5413eef

Browse files
forgot password feature added
1 parent 9745012 commit 5413eef

File tree

13 files changed

+420
-121
lines changed

13 files changed

+420
-121
lines changed

app/src/main/java/com/github/code/gambit/data/remote/services/auth/AuthService.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ interface AuthService {
1010
suspend fun signUp(authData: AuthData): ServiceResult<Unit>
1111
suspend fun logOut(): ServiceResult<Unit>
1212
suspend fun resetPassword(oldPassword: String, newPassword: String): ServiceResult<Unit>
13+
suspend fun forgotPassword(userEmail: String): ServiceResult<Unit>
14+
suspend fun changePassword(newPassword: String, confirmationCode: String): ServiceResult<Unit>
1315
suspend fun updateUserName(fullName: String): ServiceResult<String>
1416
suspend fun confirmSignUp(authData: AuthData): ServiceResult<Unit>
1517
suspend fun fetchSession(): ServiceResult<AWSCognitoAuthSession>

app/src/main/java/com/github/code/gambit/data/remote/services/auth/AuthServiceImpl.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,27 @@ class AuthServiceImpl : AuthService {
5858
}
5959
}
6060

61+
override suspend fun forgotPassword(userEmail: String): ServiceResult<Unit> {
62+
return try {
63+
Amplify.Auth.resetPassword(userEmail)
64+
ServiceResult.Success(Unit)
65+
} catch (e: java.lang.Exception) {
66+
ServiceResult.Error(e)
67+
}
68+
}
69+
70+
override suspend fun changePassword(
71+
newPassword: String,
72+
confirmationCode: String
73+
): ServiceResult<Unit> {
74+
return try {
75+
Amplify.Auth.confirmResetPassword(newPassword, confirmationCode)
76+
ServiceResult.Success(Unit)
77+
} catch (e: java.lang.Exception) {
78+
ServiceResult.Error(e)
79+
}
80+
}
81+
6182
override suspend fun updateUserName(fullName: String): ServiceResult<String> {
6283
return try {
6384
val attribute = AuthUserAttribute(AuthUserAttributeKey.name(), fullName)

app/src/main/java/com/github/code/gambit/helper/auth/AuthState.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.github.code.gambit.helper.auth
33
sealed class AuthState<out T> {
44
data class Success<out T>(val data: T) : AuthState<T>()
55
data class Error(val reason: String) : AuthState<Nothing>()
6+
data class ResendStatus(val success: Boolean) : AuthState<Nothing>()
67
object Loading : AuthState<Nothing>()
78
object Confirmation : AuthState<Nothing>()
89
object CodeMissMatch : AuthState<Nothing>()

app/src/main/java/com/github/code/gambit/repositories/auth/AuthRepository.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,7 @@ interface AuthRepository {
1010
suspend fun signUp(authData: AuthData): ServiceResult<Unit>
1111
suspend fun logOut(): ServiceResult<Unit>
1212
suspend fun signUpConfirmation(authData: AuthData): ServiceResult<User>
13+
suspend fun resendConfirmationCode(userEmail: String): ServiceResult<Unit>
14+
suspend fun forgotPassword(userEmail: String): ServiceResult<Unit>
15+
suspend fun resetForgotPassword(authData: AuthData): ServiceResult<User>
1316
}

app/src/main/java/com/github/code/gambit/repositories/auth/AuthRepositoryImpl.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import com.github.code.gambit.utility.sharedpreference.UserManager
1313
import java.lang.Exception
1414

1515
class AuthRepositoryImpl
16-
1716
constructor(
1817
private val authService: AuthService,
1918
private val networkDataSource: NetworkDataSource,
@@ -82,4 +81,19 @@ constructor(
8281
(confirmationResult as ServiceResult.Success)
8382
return login(authData)
8483
}
84+
85+
override suspend fun resendConfirmationCode(userEmail: String): ServiceResult<Unit> {
86+
return authService.resentConfirmationCode(userEmail)
87+
}
88+
89+
override suspend fun forgotPassword(userEmail: String): ServiceResult<Unit> {
90+
return authService.forgotPassword(userEmail)
91+
}
92+
93+
override suspend fun resetForgotPassword(authData: AuthData): ServiceResult<User> {
94+
return when (val res = authService.changePassword(authData.password, authData.confirmationCode!!)) {
95+
is ServiceResult.Error -> res
96+
is ServiceResult.Success -> login(authData)
97+
}
98+
}
8599
}

app/src/main/java/com/github/code/gambit/ui/fragment/auth/AuthFragment.kt

Lines changed: 26 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,25 @@
11
package com.github.code.gambit.ui.fragment.auth
22

3-
import `in`.aabhasjindal.otptextview.OtpTextView
4-
import android.animation.Animator
5-
import android.animation.AnimatorListenerAdapter
6-
import android.app.Dialog
7-
import android.content.DialogInterface
8-
import android.graphics.drawable.ColorDrawable
93
import android.os.Bundle
10-
import android.view.KeyEvent
114
import android.view.View
12-
import android.view.ViewAnimationUtils
13-
import android.widget.TextView
145
import android.widget.Toast
15-
import androidx.core.content.ContextCompat
166
import androidx.fragment.app.Fragment
177
import androidx.fragment.app.viewModels
188
import androidx.navigation.fragment.findNavController
199
import com.github.code.gambit.R
2010
import com.github.code.gambit.data.model.User
21-
import com.github.code.gambit.databinding.EmailVerificationLayoutBinding
2211
import com.github.code.gambit.databinding.FragmentAuthBinding
2312
import com.github.code.gambit.helper.auth.AuthData
2413
import com.github.code.gambit.helper.auth.AuthState
14+
import com.github.code.gambit.ui.fragment.auth.confirmationcomponent.ConfirmationComponent
2515
import com.github.code.gambit.utility.SystemManager
2616
import com.github.code.gambit.utility.extention.exitFullscreen
27-
import com.github.code.gambit.utility.extention.setStatusColor
28-
import com.github.code.gambit.utility.extention.show
17+
import com.github.code.gambit.utility.extention.longToast
2918
import com.github.code.gambit.utility.extention.snackbar
3019
import com.google.android.material.tabs.TabLayoutMediator
3120
import dagger.hilt.android.AndroidEntryPoint
3221
import javax.inject.Inject
3322
import javax.inject.Named
34-
import kotlin.math.hypot
3523

3624
@AndroidEntryPoint
3725
class AuthFragment : Fragment(R.layout.fragment_auth) {
@@ -45,9 +33,7 @@ class AuthFragment : Fragment(R.layout.fragment_auth) {
4533

4634
private lateinit var authData: AuthData
4735

48-
private lateinit var mOtpTextView: OtpTextView
49-
private lateinit var dialogView: View
50-
private lateinit var dialog: Dialog
36+
private lateinit var confirmationComponent: ConfirmationComponent
5137

5238
@Inject
5339
lateinit var permissionManager: SystemManager
@@ -83,6 +69,18 @@ class AuthFragment : Fragment(R.layout.fragment_auth) {
8369
)
8470
}.attach()
8571

72+
confirmationComponent = ConfirmationComponent.bind(requireContext())
73+
74+
confirmationComponent.getOtp().observe(viewLifecycleOwner) {
75+
it?.let {
76+
confirmSignUp(it)
77+
}
78+
}
79+
80+
confirmationComponent.setResendCallback { email ->
81+
viewModel.setEvent(AuthEvent.ResendCode(email))
82+
}
83+
8684
binding.buttonSubmit.setOnClickListener {
8785
disableInteraction()
8886
val fg = (binding.fragmentContainer.adapter as AuthFragmentAdapter).getFragment(
@@ -110,113 +108,38 @@ class AuthFragment : Fragment(R.layout.fragment_auth) {
110108
}
111109
is AuthState.Confirmation -> {
112110
binding.progressBar.hide()
113-
showConfirmationDialog()
111+
confirmationComponent.show(authData.email)
114112
}
115113
is AuthState.Success<User> -> {
116114
binding.progressBar.hide()
117115
binding.root.snackbar("Welcome ${it.data.name}")
118-
if (this::dialog.isInitialized && dialog.isShowing) {
119-
revealShow(false, exit = true) { navigateToHome() }
116+
if (this::confirmationComponent.isInitialized && confirmationComponent.isShowing()) {
117+
confirmationComponent.exit { navigateToHome() }
120118
} else {
121119
navigateToHome()
122120
}
123121
}
124122
is AuthState.Error -> {
125123
enableInteraction()
126124
binding.progressBar.hide()
127-
if (this::dialog.isInitialized && dialog.isShowing) {
128-
revealShow(false, exit = true)
125+
if (this::confirmationComponent.isInitialized && confirmationComponent.isShowing()) {
126+
confirmationComponent.exit()
129127
}
130128
binding.root.snackbar(it.reason)
131129
}
130+
is AuthState.ResendStatus -> {
131+
if (it.success) {
132+
longToast("Code send on your email")
133+
}
134+
}
132135
AuthState.CodeMissMatch -> {
133-
mOtpTextView.showError()
136+
confirmationComponent.showError("Invalid code")
134137
}
135138
}
136139
}
137140
)
138141
}
139142

140-
private fun showConfirmationDialog() {
141-
dialog = Dialog(requireContext(), R.style.Theme_VTransfer)
142-
EmailVerificationLayoutBinding.inflate(layoutInflater)
143-
dialog.setContentView(R.layout.email_verification_layout)
144-
dialog.window?.setStatusColor(
145-
ContextCompat.getColor(
146-
requireContext(),
147-
R.color.secondary
148-
)
149-
)
150-
dialog.window?.setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT))
151-
val otpTextView: OtpTextView = dialog.findViewById(R.id.otp_view)
152-
val progressBar: View = dialog.findViewById(R.id.progress_bar)
153-
mOtpTextView = otpTextView
154-
val validate: TextView = dialog.findViewById(R.id.validate)
155-
dialogView = dialog.findViewById(R.id.root)
156-
157-
validate.setOnClickListener {
158-
val otp = otpTextView.otp
159-
when {
160-
otp == null -> {
161-
dialogView.snackbar("Code can't be empty")
162-
}
163-
otp.length != 6 -> {
164-
dialogView.snackbar("Invalid code length")
165-
}
166-
else -> {
167-
progressBar.show()
168-
validate.isClickable = false
169-
confirmSignUp(otp)
170-
}
171-
}
172-
}
173-
174-
dialog.setOnShowListener { revealShow(true) }
175-
176-
dialog.setOnKeyListener(
177-
DialogInterface.OnKeyListener { _, i, _ ->
178-
if (i == KeyEvent.KEYCODE_BACK) {
179-
revealShow(false)
180-
return@OnKeyListener true
181-
}
182-
false
183-
}
184-
)
185-
186-
dialog.show()
187-
}
188-
189-
private fun revealShow(b: Boolean, exit: Boolean = false, exitFunction: () -> Unit = {}) {
190-
val view = dialogView.findViewById<View>(R.id.root)
191-
val w = view.width
192-
val h = view.height
193-
val endRadius = hypot(w.toDouble(), h.toDouble()).toInt()
194-
val cx = (view.width / 2)
195-
val cy = 0
196-
if (b) {
197-
val revealAnimator =
198-
ViewAnimationUtils.createCircularReveal(view, cx, cy, 0f, endRadius.toFloat())
199-
view.visibility = View.VISIBLE
200-
revealAnimator.duration = 700
201-
revealAnimator.start()
202-
} else {
203-
val anim =
204-
ViewAnimationUtils.createCircularReveal(view, cx, cy, endRadius.toFloat(), 0f)
205-
anim.addListener(object : AnimatorListenerAdapter() {
206-
override fun onAnimationEnd(animation: Animator) {
207-
super.onAnimationEnd(animation)
208-
dialog.dismiss()
209-
view.visibility = View.INVISIBLE
210-
if (exit) {
211-
exitFunction()
212-
}
213-
}
214-
})
215-
anim.duration = 700
216-
anim.start()
217-
}
218-
}
219-
220143
private fun signUp(authData: AuthData) {
221144
this.authData = authData
222145
viewModel.setEvent(AuthEvent.SignUpEvent(authData))

app/src/main/java/com/github/code/gambit/ui/fragment/auth/AuthViewModel.kt

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.github.code.gambit.helper.auth.AuthState
1212
import com.github.code.gambit.repositories.auth.AuthRepository
1313
import dagger.hilt.android.lifecycle.HiltViewModel
1414
import kotlinx.coroutines.launch
15+
import java.lang.Exception
1516
import javax.inject.Inject
1617

1718
@HiltViewModel
@@ -38,6 +39,21 @@ constructor(
3839
is AuthEvent.SignUpEvent -> {
3940
signUp(event.authData)
4041
}
42+
is AuthEvent.ForgotPassword -> {
43+
forgotPassword(event.userEmail)
44+
}
45+
is AuthEvent.ResetForgotPassword -> {
46+
resetForgotPassword(event.userEmail, event.newPassword, event.confirmationCode)
47+
}
48+
is AuthEvent.ResendCode -> {
49+
when (val it = authRepository.resendConfirmationCode(event.userEmail)) {
50+
is ServiceResult.Error -> {
51+
postError(it.exception)
52+
postValue(AuthState.ResendStatus(false))
53+
}
54+
is ServiceResult.Success -> postValue(AuthState.ResendStatus(true))
55+
}
56+
}
4157
}
4258
}
4359
}
@@ -67,18 +83,50 @@ constructor(
6783
val res = authRepository.signUpConfirmation(authData)
6884
if (res is ServiceResult.Error) {
6985
if (res.exception.cause is CodeMismatchException) {
70-
_authState.postValue(AuthState.CodeMissMatch)
86+
postValue(AuthState.CodeMissMatch)
7187
} else {
72-
_authState.postValue(AuthState.Error(res.exception.message!!))
88+
postError(res.exception)
7389
}
7490
} else {
75-
_authState.postValue(AuthState.Success((res as ServiceResult.Success).data))
91+
postValue(AuthState.Success((res as ServiceResult.Success).data))
7692
}
7793
}
94+
95+
private suspend fun forgotPassword(userEmail: String) {
96+
_authState.value = AuthState.Loading
97+
when (val res = authRepository.forgotPassword(userEmail)) {
98+
is ServiceResult.Error -> postError(res.exception)
99+
is ServiceResult.Success -> postValue(AuthState.Confirmation)
100+
}
101+
}
102+
103+
private suspend fun resetForgotPassword(userEmail: String, newPassword: String, confirmationCode: String) {
104+
when (val res = authRepository.resetForgotPassword(AuthData("", userEmail, newPassword, null, confirmationCode))) {
105+
is ServiceResult.Error -> {
106+
if (res.exception.cause is CodeMismatchException) {
107+
postValue(AuthState.CodeMissMatch)
108+
} else {
109+
postError(res.exception)
110+
}
111+
}
112+
is ServiceResult.Success -> postValue(AuthState.Success(res.data))
113+
}
114+
}
115+
116+
private fun postError(exception: Exception) {
117+
_authState.postValue(AuthState.Error(exception.localizedMessage!!))
118+
}
119+
120+
private fun postValue(state: AuthState<User>) {
121+
_authState.postValue(state)
122+
}
78123
}
79124

80125
sealed class AuthEvent {
81126
data class LoginEvent(val authData: AuthData) : AuthEvent()
82127
data class SignUpEvent(val authData: AuthData) : AuthEvent()
83128
data class ConfirmationEvent(val authData: AuthData) : AuthEvent()
129+
data class ResendCode(val userEmail: String) : AuthEvent()
130+
data class ForgotPassword(val userEmail: String) : AuthEvent()
131+
data class ResetForgotPassword(val userEmail: String, val newPassword: String, val confirmationCode: String) : AuthEvent()
84132
}

0 commit comments

Comments
 (0)