diff --git a/src/app/commoncomponents/IdentifierUsernameTextEdit.qml b/src/app/commoncomponents/IdentifierUsernameTextEdit.qml new file mode 100644 index 0000000000000000000000000000000000000000..e6298092bfffadb044df331ee6cad93c6dae78ba --- /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 2feec902aaf4feb207500a6e0c91c239cd9be9ce..e4a8e433fb53537a3a9941fbb2399df13b6e0c00 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(); }