@@ -12,6 +12,8 @@ package org.mifospay.feature.auth.signup
12
12
import androidx.lifecycle.SavedStateHandle
13
13
import androidx.lifecycle.viewModelScope
14
14
import kotlinx.coroutines.Job
15
+ import kotlinx.coroutines.async
16
+ import kotlinx.coroutines.awaitAll
15
17
import kotlinx.coroutines.flow.update
16
18
import kotlinx.coroutines.launch
17
19
import org.mifospay.core.common.DataState
@@ -34,7 +36,8 @@ import org.mifospay.core.ui.utils.PasswordStrengthResult
34
36
import org.mifospay.feature.auth.signup.SignUpAction.Internal.ReceivePasswordStrengthResult
35
37
36
38
private const val KEY_STATE = " signup_state"
37
- private const val MIN_PASSWORD_LENGTH = 8
39
+ private const val MIN_PASSWORD_LENGTH = 12
40
+ private const val MAX_PASSWORD_LENGTH = 50
38
41
39
42
class SignupViewModel (
40
43
private val userRepository : UserRepository ,
@@ -172,7 +175,7 @@ class SignupViewModel(
172
175
173
176
private fun handlePasswordInput (action : SignUpAction .PasswordInputChange ) {
174
177
// Update input:
175
- mutableStateFlow.update { it.copy(passwordInput = action.password) }
178
+ mutableStateFlow.update { it.copy(passwordInput = action.password, passwordError = null ) }
176
179
// Update password strength:
177
180
passwordStrengthJob.cancel()
178
181
if (action.password.isEmpty()) {
@@ -203,7 +206,11 @@ class SignupViewModel(
203
206
}
204
207
}
205
208
206
- is PasswordStrengthResult .Error -> {}
209
+ is PasswordStrengthResult .Error -> {
210
+ mutableStateFlow.update {
211
+ it.copy(passwordError = result.message.toString(), passwordStrengthState = PasswordStrengthState .NONE )
212
+ }
213
+ }
207
214
}
208
215
}
209
216
@@ -288,15 +295,31 @@ class SignupViewModel(
288
295
}
289
296
}
290
297
298
+ state.passwordInput.length > MAX_PASSWORD_LENGTH -> {
299
+ mutableStateFlow.update {
300
+ it.copy(
301
+ dialogState = SignUpDialog .Error (
302
+ " Password must be less than $MAX_PASSWORD_LENGTH characters long." ,
303
+ ),
304
+ )
305
+ }
306
+ }
307
+
291
308
! state.isPasswordMatch -> {
292
309
mutableStateFlow.update {
293
310
it.copy(dialogState = SignUpDialog .Error (" Passwords do not match." ))
294
311
}
295
312
}
296
313
297
314
! state.isPasswordStrong -> {
315
+ val errorMessage = state.passwordError?.takeIf { it.isNotBlank() }
316
+ ? : " Please ensure password contains :" +
317
+ " \n - At least one uppercase character" +
318
+ " \n - At least one lowercase character" +
319
+ " \n - At least one numeric digit" +
320
+ " \n - At least one special character"
298
321
mutableStateFlow.update {
299
- it.copy(dialogState = SignUpDialog .Error (" Password is weak. " ))
322
+ it.copy(dialogState = SignUpDialog .Error (errorMessage.lines().joinToString( " \n " ) { " - $it " } ))
300
323
}
301
324
}
302
325
@@ -347,50 +370,47 @@ class SignupViewModel(
347
370
it.copy(dialogState = SignUpDialog .Loading )
348
371
}
349
372
350
- // 0. Unique Mobile Number (checked in MOBILE VERIFICATION ACTIVITY)
351
- // 1. Check for unique external id and username
352
- // 2. Create user
353
- // 3. Create Client
354
- // 4. Update User and connect client with user
355
- checkForUsernameExists(state.userNameInput)
373
+ val fieldsToCheck = mapOf (
374
+ " Username" to state.userNameInput,
375
+ " Mobile Number" to state.mobileNumberInput,
376
+ )
377
+ checkUniqueFields(fieldsToCheck)
356
378
}
357
379
358
- private fun checkForUsernameExists ( username : String ) {
380
+ private fun checkUniqueFields ( fields : Map < String , String > ) {
359
381
viewModelScope.launch {
360
- val result = searchRepository.searchResources(
361
- username,
362
- Constants .CLIENTS ,
363
- false ,
364
- )
365
-
366
- when (result) {
367
- is DataState .Error -> {
368
- val message = result.exception.message.toString()
369
- mutableStateFlow.update {
370
- it.copy(dialogState = SignUpDialog .Error (message))
371
- }
382
+ val results = fields.map { (label, value) ->
383
+ async {
384
+ val result = searchRepository.searchResources(value, Constants .CLIENTS , false )
385
+ label to result
372
386
}
387
+ }.awaitAll()
373
388
374
- is DataState .Success -> {
375
- if (result.data.isEmpty()) {
376
- // Username is unique
377
- val newUser = NewUser (
378
- state.userNameInput,
379
- state.firstNameInput,
380
- state.lastNameInput,
381
- state.emailInput,
382
- state.passwordInput,
383
- )
384
-
385
- createUser(newUser)
386
- } else {
387
- mutableStateFlow.update {
388
- it.copy(dialogState = SignUpDialog .Error (" Username already exists." ))
389
- }
389
+ val errorMessages = results.mapNotNull { (label, result) ->
390
+ when (result) {
391
+ is DataState .Success -> {
392
+ if (result.data.isNotEmpty()) " $label already exists." else null
390
393
}
394
+ is DataState .Error ->
395
+ result.exception.message
396
+ ? : " Error checking $label ."
397
+ else -> null
391
398
}
399
+ }
392
400
393
- is DataState .Loading -> Unit
401
+ if (errorMessages.isNotEmpty()) {
402
+ mutableStateFlow.update {
403
+ it.copy(dialogState = SignUpDialog .Error (errorMessages.joinToString(" \n " )))
404
+ }
405
+ } else {
406
+ val newUser = NewUser (
407
+ state.userNameInput,
408
+ state.firstNameInput,
409
+ state.lastNameInput,
410
+ state.emailInput,
411
+ state.passwordInput,
412
+ )
413
+ createUser(newUser)
394
414
}
395
415
}
396
416
}
@@ -509,6 +529,7 @@ data class SignUpState(
509
529
val businessNameInput : String = " " ,
510
530
val dialogState : SignUpDialog ? = null ,
511
531
val passwordStrengthState : PasswordStrengthState = PasswordStrengthState .NONE ,
532
+ val passwordError : String? = null ,
512
533
) : Parcelable {
513
534
@IgnoredOnParcel
514
535
val isPasswordStrong: Boolean
0 commit comments