From fea935fb63cb05eb2d9a58d0d2723baf7d8f07f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20K=C3=BCrten?= <sebastian@mobanisto.de>
Date: Mon, 21 Feb 2022 17:14:58 +0100
Subject: [PATCH] Refactor both password forms for code reuse

---
 .../briar/desktop/login/RegistrationScreen.kt | 67 ++++++++++-------
 .../desktop/settings/ChangePasswordDialog.kt  | 74 +++++++------------
 2 files changed, 67 insertions(+), 74 deletions(-)

diff --git a/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt
index 0397637deb..02846c31c3 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/login/RegistrationScreen.kt
@@ -33,6 +33,7 @@ import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment.Companion.Center
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusDirection
+import androidx.compose.ui.focus.FocusManager
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.platform.LocalFocusManager
@@ -64,10 +65,10 @@ fun RegistrationScreen(
                 buttonEnabled = viewHolder.buttonEnabled.value
             ) {
                 NicknameForm(
-                    viewHolder.nickname.value,
-                    viewHolder::setNickname,
-                    viewHolder.nicknameTooLongError.value,
-                    viewHolder::goToPassword
+                    nickname = viewHolder.nickname.value,
+                    setNickname = viewHolder::setNickname,
+                    nicknameTooLongError = viewHolder.nicknameTooLongError.value,
+                    onSubmit = viewHolder::goToPassword
                 )
             }
         INSERT_PASSWORD ->
@@ -77,16 +78,26 @@ fun RegistrationScreen(
                 buttonClick = viewHolder::signUp,
                 buttonEnabled = viewHolder.buttonEnabled.value
             ) {
+                val initialFocusRequester = remember { FocusRequester() }
+                val focusManager = LocalFocusManager.current
                 PasswordForm(
-                    viewHolder.password.value,
-                    viewHolder::setPassword,
-                    viewHolder.passwordConfirmation.value,
-                    viewHolder::setPasswordConfirmation,
-                    viewHolder.passwordStrength.value,
-                    viewHolder.passwordTooWeakError.value,
-                    viewHolder.passwordMatchError.value,
-                    viewHolder::signUp
+                    focusManager = focusManager,
+                    focusRequester = initialFocusRequester,
+                    keyLabelPassword = "startup.field.password",
+                    keyLabelPasswordConfirmation = "startup.field.password_confirmation",
+                    password = viewHolder.password.value,
+                    setPassword = viewHolder::setPassword,
+                    passwordConfirmation = viewHolder.passwordConfirmation.value,
+                    setPasswordConfirmation = viewHolder::setPasswordConfirmation,
+                    passwordStrength = viewHolder.passwordStrength.value,
+                    passwordTooWeakError = viewHolder.passwordTooWeakError.value,
+                    passwordsDontMatchError = viewHolder.passwordMatchError.value,
+                    onSubmit = viewHolder::signUp
                 )
+
+                LaunchedEffect(Unit) {
+                    initialFocusRequester.requestFocus()
+                }
             }
         CREATING -> LoadingView(i18n("startup.database.creating"))
         CREATED -> {} // case handled by BriarUi
@@ -98,7 +109,7 @@ fun NicknameForm(
     nickname: String,
     setNickname: (String) -> Unit,
     nicknameTooLongError: Boolean,
-    onEnter: () -> Unit,
+    onSubmit: () -> Unit,
 ) {
     val initialFocusRequester = remember { FocusRequester() }
 
@@ -111,7 +122,7 @@ fun NicknameForm(
         errorMessage = i18n("startup.error.name_too_long"),
         keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
         modifier = Modifier.fillMaxWidth().focusRequester(initialFocusRequester),
-        onEnter = onEnter
+        onEnter = onSubmit
     )
 
     LaunchedEffect(Unit) {
@@ -119,8 +130,17 @@ fun NicknameForm(
     }
 }
 
+/**
+ * This is used here in the RegistrationScreen but also on [org.briarproject.briar.desktop.settings.ChangePasswordDialog].
+ *
+ * You can pass an optional [focusRequester] if the first of both password fields should request focus using a modifier.
+ */
 @Composable
 fun PasswordForm(
+    focusManager: FocusManager,
+    focusRequester: FocusRequester? = null,
+    keyLabelPassword: String,
+    keyLabelPasswordConfirmation: String,
     password: String,
     setPassword: (String) -> Unit,
     passwordConfirmation: String,
@@ -128,11 +148,8 @@ fun PasswordForm(
     passwordStrength: Float,
     passwordTooWeakError: Boolean,
     passwordsDontMatchError: Boolean,
-    onEnter: () -> Unit,
+    onSubmit: () -> Unit,
 ) {
-    val initialFocusRequester = remember { FocusRequester() }
-    val focusManager = LocalFocusManager.current
-
     Box(
         modifier = Modifier.fillMaxWidth().requiredHeight(24.dp),
         contentAlignment = Center
@@ -143,29 +160,27 @@ fun PasswordForm(
     OutlinedPasswordTextField(
         value = password,
         onValueChange = setPassword,
-        label = { Text(i18n("startup.field.password")) },
+        label = { Text(i18n(keyLabelPassword)) },
         singleLine = true,
         isError = passwordTooWeakError,
         showErrorWhen = AFTER_FOCUS_LOST_ONCE,
         errorMessage = i18n("startup.error.password_too_weak"),
         keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Next),
-        modifier = Modifier.fillMaxWidth().focusRequester(initialFocusRequester),
+        modifier = Modifier.fillMaxWidth().apply {
+            if (focusRequester != null) focusRequester(focusRequester)
+        },
         onEnter = { focusManager.moveFocus(FocusDirection.Next) },
     )
     OutlinedPasswordTextField(
         value = passwordConfirmation,
         onValueChange = setPasswordConfirmation,
-        label = { Text(i18n("startup.field.password_confirmation")) },
+        label = { Text(i18n(keyLabelPasswordConfirmation)) },
         singleLine = true,
         isError = passwordsDontMatchError,
         showErrorWhen = AFTER_FIRST_FOCUSSED,
         errorMessage = i18n("startup.error.passwords_not_match"),
         keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done),
         modifier = Modifier.fillMaxWidth(),
-        onEnter = onEnter,
+        onEnter = onSubmit,
     )
-
-    LaunchedEffect(Unit) {
-        initialFocusRequester.requestFocus()
-    }
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/settings/ChangePasswordDialog.kt b/src/main/kotlin/org/briarproject/briar/desktop/settings/ChangePasswordDialog.kt
index d9b6b5bba0..afe98b3c5d 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/settings/ChangePasswordDialog.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/settings/ChangePasswordDialog.kt
@@ -18,11 +18,9 @@
 
 package org.briarproject.briar.desktop.settings
 
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.IntrinsicSize
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.requiredHeight
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.material.AlertDialog
@@ -38,7 +36,6 @@ import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusDirection
 import androidx.compose.ui.focus.FocusRequester
@@ -46,10 +43,9 @@ import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
-import androidx.compose.ui.unit.dp
 import org.briarproject.bramble.api.crypto.DecryptionResult
 import org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_PASSWORD
-import org.briarproject.briar.desktop.login.StrengthMeter
+import org.briarproject.briar.desktop.login.PasswordForm
 import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 import org.briarproject.briar.desktop.utils.PreviewUtils.preview
 import java.lang.Float.min
@@ -162,17 +158,17 @@ fun ChangePasswordDialog(
         },
         text = {
             PasswordForm(
-                oldPassword,
-                setOldPassword,
-                password,
-                setPassword,
-                passwordConfirmation,
-                setPasswordConfirmation,
-                passwordStrength,
-                passwordTooWeakError,
-                passwordsDontMatchError,
-                onSubmit,
-                submitError,
+                oldPassword = oldPassword,
+                setOldPassword = setOldPassword,
+                password = password,
+                setPassword = setPassword,
+                passwordConfirmation = passwordConfirmation,
+                setPasswordConfirmation = setPasswordConfirmation,
+                passwordStrength = passwordStrength,
+                passwordTooWeakError = passwordTooWeakError,
+                passwordsDontMatchError = passwordsDontMatchError,
+                onSubmit = onSubmit,
+                submitError = submitError,
             )
         },
         dismissButton = {
@@ -205,7 +201,7 @@ fun PasswordForm(
     passwordTooWeakError: Boolean,
     passwordsDontMatchError: Boolean,
     onSubmit: () -> Unit,
-    onSubmitError: DecryptionResult?,
+    submitError: DecryptionResult?,
 ) {
     val initialFocusRequester = remember { FocusRequester() }
     val focusManager = LocalFocusManager.current
@@ -216,43 +212,25 @@ fun PasswordForm(
             onValueChange = setOldPassword,
             label = { Text(i18n("settings.security.password.current")) },
             singleLine = true,
-            isError = onSubmitError == INVALID_PASSWORD,
+            isError = submitError == INVALID_PASSWORD,
             showErrorWhen = InitialFocusState.FROM_START,
             errorMessage = i18n("startup.error.password_wrong"),
             keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Next),
             modifier = Modifier.fillMaxWidth().focusRequester(initialFocusRequester),
             onEnter = { focusManager.moveFocus(FocusDirection.Next) },
         )
-        Box(
-            modifier = Modifier.fillMaxWidth().requiredHeight(24.dp),
-            contentAlignment = Alignment.Center
-        ) {
-            if (password.isNotEmpty())
-                StrengthMeter(passwordStrength, Modifier.fillMaxWidth())
-        }
-        OutlinedPasswordTextField(
-            value = password,
-            onValueChange = setPassword,
-            label = { Text(i18n("settings.security.password.choose")) },
-            singleLine = true,
-            isError = passwordTooWeakError,
-            showErrorWhen = InitialFocusState.AFTER_FOCUS_LOST_ONCE,
-            errorMessage = i18n("startup.error.password_too_weak"),
-            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Next),
-            modifier = Modifier.fillMaxWidth(),
-            onEnter = { focusManager.moveFocus(FocusDirection.Next) },
-        )
-        OutlinedPasswordTextField(
-            value = passwordConfirmation,
-            onValueChange = setPasswordConfirmation,
-            label = { Text(i18n("settings.security.password.confirm")) },
-            singleLine = true,
-            isError = passwordsDontMatchError,
-            showErrorWhen = InitialFocusState.AFTER_FIRST_FOCUSSED,
-            errorMessage = i18n("startup.error.passwords_not_match"),
-            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done),
-            modifier = Modifier.fillMaxWidth(),
-            onEnter = onSubmit,
+        PasswordForm(
+            focusManager = focusManager,
+            keyLabelPassword = "settings.security.password.choose",
+            keyLabelPasswordConfirmation = "settings.security.password.confirm",
+            password = password,
+            setPassword = setPassword,
+            passwordConfirmation = passwordConfirmation,
+            setPasswordConfirmation = setPasswordConfirmation,
+            passwordStrength = passwordStrength,
+            passwordTooWeakError = passwordTooWeakError,
+            passwordsDontMatchError = passwordsDontMatchError,
+            onSubmit = onSubmit,
         )
     }
 
-- 
GitLab