From adfafd9ddab49c0cb7deda2d929aafd6904f0455 Mon Sep 17 00:00:00 2001
From: cberthet <capucine.berthet@savoirfairelinux.com>
Date: Thu, 23 Nov 2023 17:33:20 -0500
Subject: [PATCH] JamiIdentifier: resolve display issues with new
 UsernameTextEdit

GitLab: #1440

Change-Id: Ia3e8900d89b9e74525080cc208ef54c30200d2b1
---
 .../IdentifierUsernameTextEdit.qml            | 193 ++++++++++++++++++
 src/app/commoncomponents/JamiIdentifier.qml   |  58 ++++--
 2 files changed, 234 insertions(+), 17 deletions(-)
 create mode 100644 src/app/commoncomponents/IdentifierUsernameTextEdit.qml

diff --git a/src/app/commoncomponents/IdentifierUsernameTextEdit.qml b/src/app/commoncomponents/IdentifierUsernameTextEdit.qml
new file mode 100644
index 000000000..e6298092b
--- /dev/null
+++ b/src/app/commoncomponents/IdentifierUsernameTextEdit.qml
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2022-2023 Savoir-faire Linux Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Qt5Compat.GraphicalEffects
+import net.jami.Adapters 1.1
+import net.jami.Constants 1.1
+import net.jami.Models 1.1
+
+Item {
+    id: root
+
+    property string prefixIconSrc: {
+        switch (nameRegistrationState) {
+        case UsernameTextEdit.NameRegistrationState.FREE:
+            return JamiResources.circled_green_check_svg;
+        case UsernameTextEdit.NameRegistrationState.INVALID:
+        case UsernameTextEdit.NameRegistrationState.TAKEN:
+            return JamiResources.circled_red_cross_svg;
+        case UsernameTextEdit.NameRegistrationState.BLANK:
+        default:
+            return JamiResources.person_24dp_svg;
+        }
+    }
+
+    property color prefixIconColor: {
+        switch (nameRegistrationState) {
+        case UsernameTextEdit.NameRegistrationState.FREE:
+            return "#009980";
+        case UsernameTextEdit.NameRegistrationState.INVALID:
+        case UsernameTextEdit.NameRegistrationState.TAKEN:
+            return "#CC0022";
+        case UsernameTextEdit.NameRegistrationState.BLANK:
+        default:
+            return JamiTheme.editLineColor;
+        }
+    }
+
+    property bool isActive: false
+    property string infohash: CurrentAccount.uri
+    property string accountId: CurrentAccount.id
+    property string registeredName: CurrentAccount.registeredName
+    property string staticText: root.isActive ? registeredName : (registeredName ? registeredName : infohash)
+
+    property bool editMode: false
+
+    signal keyPressed
+    signal activeChanged(bool active)
+
+    property string dynamicText
+
+    property QtObject textValidator: RegularExpressionValidator {
+        regularExpression: /[A-Za-z0-9-]{0,32}/
+    }
+
+    enum NameRegistrationState {
+        BLANK,
+        INVALID,
+        TAKEN,
+        FREE,
+        SEARCHING
+    }
+    property int nameRegistrationState: UsernameTextEdit.NameRegistrationState.BLANK
+
+    property bool inputIsValid: dynamicText.length === 0 || nameRegistrationState === UsernameTextEdit.NameRegistrationState.FREE
+
+    signal accepted
+
+    property bool btnHovered: false
+
+    Connections {
+        target: CurrentAccount
+
+        function onRegisteredNameChanged() {
+            root.editMode = false;
+        }
+    }
+
+    Connections {
+        id: registeredNameFoundConnection
+
+        target: NameDirectory
+        enabled: dynamicText.length !== 0
+
+        function onRegisteredNameFound(status, address, name) {
+            if (dynamicText === name) {
+                switch (status) {
+                case NameDirectory.LookupStatus.NOT_FOUND:
+                    nameRegistrationState = UsernameTextEdit.NameRegistrationState.FREE;
+                    break;
+                case NameDirectory.LookupStatus.ERROR:
+                case NameDirectory.LookupStatus.INVALID_NAME:
+                case NameDirectory.LookupStatus.INVALID:
+                    nameRegistrationState = UsernameTextEdit.NameRegistrationState.INVALID;
+                    break;
+                case NameDirectory.LookupStatus.SUCCESS:
+                    nameRegistrationState = UsernameTextEdit.NameRegistrationState.TAKEN;
+                    break;
+                }
+            }
+        }
+    }
+
+    Timer {
+        id: lookupTimer
+
+        repeat: false
+        interval: JamiTheme.usernameTextEditlookupInterval
+
+        onTriggered: {
+            if (dynamicText.length !== 0) {
+                nameRegistrationState = UsernameTextEdit.NameRegistrationState.SEARCHING;
+                NameDirectory.lookupName(root.accountId, dynamicText);
+            } else {
+                nameRegistrationState = UsernameTextEdit.NameRegistrationState.BLANK;
+            }
+        }
+    }
+
+    onDynamicTextChanged: lookupTimer.restart()
+
+    onActiveChanged: function (active) {
+        root.isActive = active;
+    }
+
+    onFocusChanged: {
+        if (focus)
+            editCompField.forceActiveFocus();
+    }
+
+    function startEditing() {
+        if (!registeredName) {
+            root.editMode = true;
+            editCompField.forceActiveFocus();
+            nameRegistrationState = UsernameTextEdit.NameRegistrationState.BLANK;
+        }
+    }
+
+    MaterialTextField {
+        id: editCompField
+
+        anchors.fill: parent
+
+        visible: root.editMode
+
+        onVisibleChanged: {
+            if (visible) {
+                forceActiveFocus();
+                text = staticText;
+            }
+        }
+        focus: true
+        infoTipText: JamiStrings.usernameToolTip
+        prefixIconSrc: root.prefixIconSrc
+        prefixIconColor: root.prefixIconColor
+        suffixIconSrc: JamiResources.outline_info_24dp_svg
+        suffixIconColor: JamiTheme.buttonTintedBlue
+        textColor: JamiTheme.tintedBlue
+
+        font.pixelSize: JamiTheme.jamiIdSmallFontSize
+
+        placeholderText: JamiStrings.chooseAUsername
+        onAccepted: root.accepted()
+        onRejected: root.editMode = false;
+        onKeyPressed: root.keyPressed()
+        onTextChanged: dynamicText = text;
+
+        inputIsValid: root.inputIsValid
+        onFocusChanged: {
+            if (!focus && root.editMode && !root.btnHovered)
+                rejected();
+            activeChanged(root.editMode);
+        }
+        onIsActiveChanged: activeChanged(isActive)
+        validator: root.textValidator
+        readOnly: !root.editMode
+    }
+}
diff --git a/src/app/commoncomponents/JamiIdentifier.qml b/src/app/commoncomponents/JamiIdentifier.qml
index 2feec902a..e4a8e433f 100644
--- a/src/app/commoncomponents/JamiIdentifier.qml
+++ b/src/app/commoncomponents/JamiIdentifier.qml
@@ -29,6 +29,10 @@ Item {
     property color backgroundColor: JamiTheme.welcomeBlockColor
     property color contentColor: JamiTheme.tintedBlue
     property bool centered: true
+    property bool validated: false
+    property bool outsideClic: false
+    property bool justChanged: false
+    property bool clic : false
     height: getHeight()
 
     function getHeight() {
@@ -38,9 +42,10 @@ Item {
     Connections {
         target: CurrentAccount
         function onIdChanged(id) {
-            if (!usernameTextEdit.readOnly) {
-                usernameTextEdit.readOnly = true;
+            if (usernameTextEdit.editMode) {
+                usernameTextEdit.editMode = false;
             }
+
         }
     }
 
@@ -53,8 +58,7 @@ Item {
         RoundedBorderRectangle {
             id: leftRect
             fillColor: JamiTheme.jamiIdBackgroundColor
-            Layout.preferredWidth: childrenRect.width
-            Layout.maximumWidth: jamiId.width - rightRect.width
+            Layout.preferredWidth: usernameTextEdit.visible ? childrenRect.width + JamiTheme.pushButtonMargins : childrenRect.width
             Layout.preferredHeight: childrenRect.height
             radius: {
                 "tl": 5,
@@ -71,24 +75,24 @@ Item {
                     Layout.preferredHeight: 40
                     containerHeight: 40
                     containerWidth: 40
+                    Layout.fillHeight: true
                     Layout.leftMargin: JamiTheme.pushButtonMargins
                     source: JamiResources.jami_id_logo_svg
                     color: JamiTheme.tintedBlue
                 }
 
-                UsernameTextEdit {
+                IdentifierUsernameTextEdit {
                     id: usernameTextEdit
-                    visible: !readOnly
+                    visible: editMode
                     Layout.preferredHeight: 40
+                    Layout.preferredWidth: 300
                     Layout.alignment: Qt.AlignVCenter
-                    textColor: JamiTheme.tintedBlue
-                    fontPixelSize: staticText.length > 16 || dynamicText.length > 16 ? JamiTheme.jamiIdSmallFontSize : JamiTheme.bigFontSize
+                    Layout.fillWidth: true
+
                     editMode: false
-                    isPersistent: false
-                    readOnly: true
 
                     onAccepted: {
-                        usernameTextEdit.readOnly = true;
+                        usernameTextEdit.editMode = false;
                         if (dynamicText === '') {
                             return;
                         }
@@ -96,22 +100,31 @@ Item {
                                 "registeredName": dynamicText
                             });
                         dlg.accepted.connect(function () {
-                                usernameTextEdit.nameRegistrationState = UsernameTextEdit.NameRegistrationState.BLANK;
+                                usernameTextEdit.nameRegistrationState = IdentifierUsernameTextEdit.NameRegistrationState.BLANK;
                             });
+                        dynamicText = '';
                     }
                 }
                 Label{
                     id: usernameLabel
-                    visible: usernameTextEdit.readOnly
-                    Layout.alignment: Qt.AlignVCenter
+
+                    visible: !usernameTextEdit.editMode
+
+                    verticalAlignment: Text.AlignVCenter
+
                     Layout.rightMargin: JamiTheme.pushButtonMargins
+                    Layout.bottomMargin: text === registeredName ? 5 : 0
                     Layout.maximumWidth: leftRect.width - 50
+                    Layout.fillHeight: true
                     elide: Text.ElideRight
                     color: JamiTheme.tintedBlue
                     font.pixelSize : text.length > 16 ? JamiTheme.jamiIdSmallFontSize : JamiTheme.bigFontSize
                     property string registeredName: CurrentAccount.registeredName
                     property string infohash: CurrentAccount.uri
                     text: registeredName ? registeredName : infohash
+                    onRegisteredNameChanged: {
+                        text = registeredName ? registeredName : infohash
+                    }
                 }
             }
         }
@@ -148,21 +161,32 @@ Item {
                         if (!usernameTextEdit.editMode)
                             return true;
                         switch (usernameTextEdit.nameRegistrationState) {
-                        case UsernameTextEdit.NameRegistrationState.BLANK:
                         case UsernameTextEdit.NameRegistrationState.FREE:
                             return true;
                         case UsernameTextEdit.NameRegistrationState.SEARCHING:
                         case UsernameTextEdit.NameRegistrationState.INVALID:
                         case UsernameTextEdit.NameRegistrationState.TAKEN:
+                        case UsernameTextEdit.NameRegistrationState.BLANK:
                             return false;
                         }
                     }
+                    hoverEnabled: enabled
+
+                    onHoveredChanged: {
+                        if (hovered) {
+                            usernameTextEdit.btnHovered = true;
+                        } else {
+                            usernameTextEdit.btnHovered = false;
+                        }
+                    }
+
                     source: usernameTextEdit.editMode ? JamiResources.check_black_24dp_svg : JamiResources.assignment_ind_black_24dp_svg
                     toolTipText: JamiStrings.chooseUsername
                     onClicked: {
-                        if (usernameTextEdit.readOnly) {
+                        usernameTextEdit.forceActiveFocus();
+                        if (!usernameTextEdit.editMode) {
                             usernameTextEdit.startEditing();
-                            usernameTextEdit.readOnly = false;
+                            usernameTextEdit.editMode = true;
                         } else {
                             usernameTextEdit.accepted();
                         }
-- 
GitLab