From 158154bcca1cb3a6d0ad13f2a72c6c4dc1a0354a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Tinkl?= Date: Tue, 22 Oct 2024 16:14:43 +0200 Subject: [PATCH] fix(RenameGroupPopup): fix name validation and scrolling - increase the length limit to 30 and allow `&`, as per the spec - wrap the popup in a scroll view - pls some minor cleanups Fixes #16523 --- .../src/StatusQ/Controls/StatusTagItem.qml | 8 +- .../Chat/panels/InlineSelectorPanel.qml | 7 +- .../Chat/panels/SuggestionBoxPanel.qml | 1 - ui/app/AppLayouts/Chat/panels/qmldir | 1 + .../AppLayouts/Chat/views/ChatColumnView.qml | 1 - .../AppLayouts/Chat/views/CreateChatView.qml | 12 +- .../Chat/views/MembersEditSelectorView.qml | 14 +-- .../Chat/views/MembersSelectorView.qml | 24 ++-- .../views/private/MembersSelectorBase.qml | 46 +++---- ui/imports/shared/popups/RenameGroupPopup.qml | 119 +++++++++--------- ui/imports/shared/status/StatusChatInput.qml | 1 - .../shared/views/chat/ChatContextMenuView.qml | 10 -- ui/imports/shared/views/chat/MessageView.qml | 3 +- ui/imports/utils/Constants.qml | 1 + 14 files changed, 104 insertions(+), 144 deletions(-) diff --git a/ui/StatusQ/src/StatusQ/Controls/StatusTagItem.qml b/ui/StatusQ/src/StatusQ/Controls/StatusTagItem.qml index ff82d09971..5b7c09fcb9 100644 --- a/ui/StatusQ/src/StatusQ/Controls/StatusTagItem.qml +++ b/ui/StatusQ/src/StatusQ/Controls/StatusTagItem.qml @@ -61,22 +61,21 @@ Control { function getTagColor(isReadonly) { if(isReadonly) return Theme.palette.baseColor1 - return mouseArea.containsMouse ? Theme.palette.miscColor1 : Theme.palette.primaryColor1 + return root.hovered ? Theme.palette.miscColor1 : Theme.palette.primaryColor1 } } implicitHeight: 30 horizontalPadding: d.tagMargins - font.pixelSize: 15 + font.pixelSize: Theme.primaryTextFontSize font.family: Theme.baseFont.name background: Rectangle { color: d.getTagColor(root.isReadonly) - radius: 8 + radius: Theme.radius } contentItem: RowLayout { - id: tagRow spacing: 2 StatusIcon { @@ -99,7 +98,6 @@ Control { height: d.tagIconsSize icon: "close" MouseArea { - id: mouseArea enabled: !root.isReadonly anchors.fill: parent hoverEnabled: true diff --git a/ui/app/AppLayouts/Chat/panels/InlineSelectorPanel.qml b/ui/app/AppLayouts/Chat/panels/InlineSelectorPanel.qml index 17cb593f54..ee1ab4024f 100644 --- a/ui/app/AppLayouts/Chat/panels/InlineSelectorPanel.qml +++ b/ui/app/AppLayouts/Chat/panels/InlineSelectorPanel.qml @@ -73,7 +73,6 @@ Item { Layout.leftMargin: Theme.padding Layout.alignment: Qt.AlignVCenter visible: text !== "" - font.pixelSize: 15 color: Theme.palette.baseColor1 } Item { @@ -115,7 +114,7 @@ Item { Layout.minimumWidth: 4 Layout.fillHeight: true verticalAlignment: Text.AlignVCenter - font.pixelSize: 15 + font.pixelSize: Theme.primaryTextFontSize color: Theme.palette.directColor1 selectByMouse: true @@ -193,7 +192,7 @@ Item { Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.rightMargin: Theme.padding visible: text !== "" - font.pixelSize: 10 + font.pixelSize: Theme.asideTextFontSize color: Theme.palette.dangerColor1 } } @@ -210,14 +209,12 @@ Item { StatusButton { objectName: "inlineSelectorConfirmButton" - Layout.alignment: Qt.AlignVCenter enabled: (listView.count > 0) text: qsTr("Confirm") onClicked: root.confirmed() } StatusButton { - Layout.alignment: Qt.AlignVCenter text: qsTr("Cancel") type: StatusBaseButton.Type.Danger onClicked: root.rejected() diff --git a/ui/app/AppLayouts/Chat/panels/SuggestionBoxPanel.qml b/ui/app/AppLayouts/Chat/panels/SuggestionBoxPanel.qml index 13c14531f5..b911c09860 100644 --- a/ui/app/AppLayouts/Chat/panels/SuggestionBoxPanel.qml +++ b/ui/app/AppLayouts/Chat/panels/SuggestionBoxPanel.qml @@ -216,7 +216,6 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter anchors.left: accountImage.right anchors.leftMargin: Theme.smallPadding - font.pixelSize: 15 } MouseArea { diff --git a/ui/app/AppLayouts/Chat/panels/qmldir b/ui/app/AppLayouts/Chat/panels/qmldir index 227d54bec3..f535bbaad5 100644 --- a/ui/app/AppLayouts/Chat/panels/qmldir +++ b/ui/app/AppLayouts/Chat/panels/qmldir @@ -1,3 +1,4 @@ SuggestionBoxPanel 1.0 SuggestionBoxPanel.qml UserListPanel 1.0 UserListPanel.qml ChatAnchorButtonsPanel 1.0 ChatAnchorButtonsPanel.qml +InlineSelectorPanel 1.0 InlineSelectorPanel.qml diff --git a/ui/app/AppLayouts/Chat/views/ChatColumnView.qml b/ui/app/AppLayouts/Chat/views/ChatColumnView.qml index 4d0f0611c7..6b80759835 100644 --- a/ui/app/AppLayouts/Chat/views/ChatColumnView.qml +++ b/ui/app/AppLayouts/Chat/views/ChatColumnView.qml @@ -290,7 +290,6 @@ Item { && !d.sendingInProgress } - store: root.rootStore usersModel: d.activeUsersStore.usersModel sharedStore: root.sharedRootStore diff --git a/ui/app/AppLayouts/Chat/views/CreateChatView.qml b/ui/app/AppLayouts/Chat/views/CreateChatView.qml index 245d6bf811..4d6ca4a9b6 100644 --- a/ui/app/AppLayouts/Chat/views/CreateChatView.qml +++ b/ui/app/AppLayouts/Chat/views/CreateChatView.qml @@ -2,7 +2,6 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import QtQml.Models 2.15 -import QtGraphicalEffects 1.15 import StatusQ.Controls 0.1 import StatusQ.Components 0.1 @@ -46,7 +45,6 @@ Page { Behavior on anchors.bottomMargin { NumberAnimation { duration: 30 }} background: Rectangle { - anchors.fill: parent color: Theme.palette.statusAppLayout.rightPanelBackgroundColor } @@ -73,14 +71,14 @@ Page { const ensName = member.displayName.includes(".eth") ? member.displayName : "" root.rootStore.chatCommunitySectionModule.createOneToOneChat("", member.pubKey, ensName) } else { - var groupName = ""; - var pubKeys = []; + var groupName = [] + var pubKeys = [] for (var i = 0; i < model.count; i++) { const member = model.get(i) - groupName += (member.displayName + (i === model.count - 1 ? "" : "&")) + groupName.push(member.displayName) pubKeys.push(member.pubKey) } - root.rootStore.chatCommunitySectionModule.createGroupChat("", groupName, JSON.stringify(pubKeys)) + root.rootStore.chatCommunitySectionModule.createGroupChat("", groupName.join("&"), JSON.stringify(pubKeys)) } } @@ -131,7 +129,6 @@ Page { Layout.alignment: Qt.AlignTop Layout.leftMargin: Theme.halfPadding visible: contactsList.visible - font.pixelSize: 15 text: qsTr("Contacts") color: Theme.palette.baseColor1 } @@ -182,7 +179,6 @@ Page { horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap - font.pixelSize: 15 color: Theme.palette.baseColor1 text: qsTr("You can only send direct messages to your Contacts.\n Send a contact request to the person you would like to chat with, you will be able to chat with them once they have accepted your contact request.") diff --git a/ui/app/AppLayouts/Chat/views/MembersEditSelectorView.qml b/ui/app/AppLayouts/Chat/views/MembersEditSelectorView.qml index cba6bd50aa..ee9018bb0e 100644 --- a/ui/app/AppLayouts/Chat/views/MembersEditSelectorView.qml +++ b/ui/app/AppLayouts/Chat/views/MembersEditSelectorView.qml @@ -1,6 +1,6 @@ -import QtQuick 2.14 -import QtQuick.Controls 2.14 -import QtQuick.Layouts 1.14 +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 @@ -8,14 +8,14 @@ import StatusQ.Core.Utils 0.1 import StatusQ.Controls 0.1 import StatusQ.Components 0.1 -import "../panels" -import "../stores" -import "private" +import AppLayouts.Chat.stores 1.0 import utils 1.0 import SortFilterProxyModel 0.2 +import "private" + MembersSelectorBase { id: root @@ -55,7 +55,7 @@ MembersSelectorBase { readonly property string _pubKey: model.pubKey height: ListView.view.height - text: root.tagText(model.localNickname, model.displayName, model.alias) + text: model.preferredDisplayName isReadonly: { if (model.memberRole === Constants.memberRole.owner) return true diff --git a/ui/app/AppLayouts/Chat/views/MembersSelectorView.qml b/ui/app/AppLayouts/Chat/views/MembersSelectorView.qml index dd077ae2fa..1b4577e6d8 100644 --- a/ui/app/AppLayouts/Chat/views/MembersSelectorView.qml +++ b/ui/app/AppLayouts/Chat/views/MembersSelectorView.qml @@ -1,6 +1,6 @@ -import QtQuick 2.14 -import QtQuick.Controls 2.14 -import QtQml.Models 2.2 +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQml.Models 2.15 import StatusQ.Controls 0.1 import StatusQ.Components 0.1 @@ -9,8 +9,6 @@ import "private" import utils 1.0 -import SortFilterProxyModel 0.2 - MembersSelectorBase { id: root @@ -39,20 +37,18 @@ MembersSelectorBase { } onTextPasted: (text) => { - // When pated, process text immediately - contactLookupDelayTimer.stop() // when pasting, textChanged is still emited first + // When pasted, process text immediately + contactLookupDelayTimer.stop() // when pasting, textChanged is still emitted first d.lookupContact(text) } - model: SortFilterProxyModel { - sourceModel: d.selectedMembers - } + model: d.selectedMembers delegate: StatusTagItem { readonly property string _pubKey: model.pubKey height: ListView.view.height - text: root.tagText(model.localNickname, model.displayName, model.alias) + text: model.localNickname || model.displayName onClosed: root.entryRemoved(this) } @@ -129,11 +125,7 @@ MembersSelectorBase { return false } - d.selectedMembers.append({ - "pubKey": pubKey, - "displayName": displayName, - "localNickname": localNickname - }) + d.selectedMembers.append({pubKey, displayName, localNickname}) return true } diff --git a/ui/app/AppLayouts/Chat/views/private/MembersSelectorBase.qml b/ui/app/AppLayouts/Chat/views/private/MembersSelectorBase.qml index b2979c8fb2..cc8b7891e8 100644 --- a/ui/app/AppLayouts/Chat/views/private/MembersSelectorBase.qml +++ b/ui/app/AppLayouts/Chat/views/private/MembersSelectorBase.qml @@ -1,16 +1,13 @@ -import QtQuick 2.14 -import QtQuick.Controls 2.14 -import QtQuick.Layouts 1.14 +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import StatusQ 0.1 import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 -import StatusQ.Core.Utils 0.1 -import StatusQ.Controls 0.1 -import StatusQ.Components 0.1 - -import "../../panels" import AppLayouts.Chat.stores 1.0 as ChatStores +import AppLayouts.Chat.panels 1.0 import utils 1.0 import shared.controls.delegates 1.0 @@ -25,10 +22,6 @@ InlineSelectorPanel { readonly property int membersLimit: 20 // see: https://github.com/status-im/status-mobile/issues/13066 property bool limitReached: model.count >= membersLimit - function tagText(localNickname, displayName, aliasName) { - return localNickname || displayName || aliasName - } - property string pastedChatKey: "" label.text: qsTr("To:") @@ -54,41 +47,32 @@ InlineSelectorPanel { return true } - function isPastedProfileLinkToContact(pubkey) { - return root.pastedChatKey === pubkey - } - filters: [ - ExpressionFilter { + FastExpressionFilter { enabled: root.edit.text !== "" && root.pastedChatKey == "" expression: { root.edit.text // ensure expression is reevaluated when edit.text changes return _suggestionsModel.searchPredicate(model.displayName, model.localNickname, model.alias) } + expectedRoles: ["displayName", "localNickname", "alias"] }, - ExpressionFilter { + FastExpressionFilter { expression: { root.model.count // ensure expression is reevaluated when members model changes return _suggestionsModel.notAMemberPredicate(model.pubKey) } + expectedRoles: ["pubKey"] }, - ExpressionFilter { - enabled: root.pastedChatKey != "" - expression: { - root.pastedChatKey // ensure expression is reevaluated when members model changes - return _suggestionsModel.isPastedProfileLinkToContact(model.pubKey) - } + ValueFilter { + roleName: "pubKey" + value: root.pastedChatKey + enabled: root.pastedChatKey !== "" } ] - proxyRoles: ExpressionRole { - name: "title" - expression: model.localNickname || model.displayName || model.alias - } - sorters: StringSorter { - roleName: "title" - numericMode: true + roleName: "preferredDisplayName" + caseSensitivity: Qt.CaseInsensitive } } diff --git a/ui/imports/shared/popups/RenameGroupPopup.qml b/ui/imports/shared/popups/RenameGroupPopup.qml index 10d4150530..53fb2be20a 100644 --- a/ui/imports/shared/popups/RenameGroupPopup.qml +++ b/ui/imports/shared/popups/RenameGroupPopup.qml @@ -1,7 +1,7 @@ -import QtQuick 2.13 -import QtQuick.Controls 2.13 -import QtQuick.Layouts 1.13 -import QtQml.Models 2.14 +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import QtQml.Models 2.15 import shared.controls 1.0 import shared.panels 1.0 @@ -26,11 +26,11 @@ StatusDialog { title: qsTr("Edit group name and image") width: 480 - height: 610 + padding: 0 QtObject { id: d - readonly property int nameCharLimit: 24 + readonly property int nameCharLimit: 30 // cf spec: https://github.com/status-im/feature-specs/blob/d66c586f13cb1fa0486544030148df68e06928f0/content/raw/chat/group_chat.md } onOpened: { @@ -47,66 +47,71 @@ StatusDialog { imageEditor.dataImage = activeGroupImageData } - ColumnLayout { + StatusScrollView { + id: scrollView anchors.fill: parent - spacing: 20 + contentWidth: availableWidth - StatusInput { - id: groupName - input.edit.objectName: "groupChatEdit_name" - Layout.alignment: Qt.AlignHCenter - label: qsTr("Name the group") - charLimit: d.nameCharLimit + ColumnLayout { + width: scrollView.availableWidth + spacing: 20 - validators: [ - StatusMinLengthValidator { - minLength: 1 - errorMessage: Utils.getErrorMessage(groupName.errors, qsTr("group name")) - }, - StatusRegularExpressionValidator { - regularExpression: Constants.regularExpressions.alphanumericalExpanded - errorMessage: Constants.errorMessages.alphanumericalExpandedRegExp - } - ] - } + StatusInput { + id: groupName + input.edit.objectName: "groupChatEdit_name" + Layout.alignment: Qt.AlignHCenter + label: qsTr("Name the group") + charLimit: d.nameCharLimit - StatusBaseText { - id: imgText - text: qsTr("Group image") - leftPadding: groupName.leftPadding - root.padding - } + validators: [ + StatusMinLengthValidator { + minLength: 1 + errorMessage: Utils.getErrorMessage(groupName.errors, qsTr("group name")) + }, + StatusRegularExpressionValidator { + regularExpression: Constants.regularExpressions.alphanumericalExpanded2 + errorMessage: Constants.errorMessages.alphanumericalExpandedRegExp + } + ] + } - EditCroppedImagePanel { - id: imageEditor - objectName: "groupChatEdit_image" - Layout.preferredWidth: 128 - Layout.preferredHeight: Layout.preferredWidth / aspectRatio - Layout.alignment: Qt.AlignHCenter + StatusBaseText { + id: imgText + text: qsTr("Group image") + } - imageFileDialogTitle: qsTr("Choose an image as logo") - title: qsTr("Edit group name and image") - acceptButtonText: qsTr("Use as an icon for this group chat") - } + EditCroppedImagePanel { + id: imageEditor + objectName: "groupChatEdit_image" + Layout.preferredWidth: 128 + Layout.preferredHeight: Layout.preferredWidth / aspectRatio + Layout.alignment: Qt.AlignHCenter - StatusBaseText { - id: colorText - text: qsTr("Standard colours") - leftPadding: groupName.leftPadding - root.padding - } + imageFileDialogTitle: qsTr("Choose an image as logo") + title: qsTr("Edit group name and image") + acceptButtonText: qsTr("Use as an icon for this group chat") + } - StatusColorSelectorGrid { - id: colorSelectionGrid - objectName: "groupChatEdit_color" - Layout.alignment: Qt.AlignHCenter - diameter: 40 - selectorDiameter: 16 - columns: 6 - selectedColorIndex: -1 - } + StatusBaseText { + id: colorText + text: qsTr("Standard colours") + } - Item { - id: spacerItem - height: 10 + StatusColorSelectorGrid { + id: colorSelectionGrid + objectName: "groupChatEdit_color" + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: -(parent.spacing / 3) + diameter: 40 + selectorDiameter: 16 + columns: 6 + selectedColorIndex: -1 + } + + Item { + id: spacerItem + height: 10 + } } } diff --git a/ui/imports/shared/status/StatusChatInput.qml b/ui/imports/shared/status/StatusChatInput.qml index 095ff6bf12..78af44d141 100644 --- a/ui/imports/shared/status/StatusChatInput.qml +++ b/ui/imports/shared/status/StatusChatInput.qml @@ -39,7 +39,6 @@ Rectangle { signal dismissLinkPreview(int index) property var usersModel - property ChatStores.RootStore store property SharedStores.RootStore sharedStore property var emojiPopup: null diff --git a/ui/imports/shared/views/chat/ChatContextMenuView.qml b/ui/imports/shared/views/chat/ChatContextMenuView.qml index b16c955cdb..27cd1ed0b8 100644 --- a/ui/imports/shared/views/chat/ChatContextMenuView.qml +++ b/ui/imports/shared/views/chat/ChatContextMenuView.qml @@ -54,16 +54,6 @@ StatusMenu { onTriggered: root.displayProfilePopup(root.chatId) } - StatusAction { - objectName: "viewMembersStatusAction" - text: qsTr("View Members") - icon.name: "group-chat" - enabled: root.chatType === Constants.chatType.privateGroupChat - onTriggered: { - localAccountSensitiveSettings.expandUsersList = !localAccountSensitiveSettings.expandUsersList; - } - } - StatusAction { objectName: "addRemoveFromGroupStatusAction" text: root.amIChatAdmin ? qsTr("Add / remove from group") : qsTr("Add to group") diff --git a/ui/imports/shared/views/chat/MessageView.qml b/ui/imports/shared/views/chat/MessageView.qml index 1350229485..9f7485de91 100644 --- a/ui/imports/shared/views/chat/MessageView.qml +++ b/ui/imports/shared/views/chat/MessageView.qml @@ -933,7 +933,6 @@ Loader { suggestionsOpened = false } - store: root.rootStore usersModel: root.usersStore.usersModel sharedStore: root.sharedRootStore emojiPopup: root.emojiPopup @@ -957,7 +956,7 @@ Loader { linkPreviewModel: root.linkPreviewModel gifLinks: root.gifLinks - playAnimations: root.Window.window.active && root.messageStore.isChatActive + playAnimations: root.Window.active && root.messageStore.isChatActive isOnline: root.rootStore.mainModuleInst.isOnline highlightLink: delegate.hoveredLink onImageClicked: (image, mouse, imageSource, url) => { diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index 7925b47c7a..6b09e30221 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -659,6 +659,7 @@ QtObject { readonly property var alphanumerical: /^$|^[a-zA-Z0-9]+$/ readonly property var alphanumericalExpanded: /^$|^[a-zA-Z0-9\-_\.\u0020]+$/ readonly property var alphanumericalExpanded1: /^[a-zA-Z0-9\-_]+(?: [a-zA-Z0-9\-_]+)*$/ + readonly property var alphanumericalExpanded2: /^$|^[a-zA-Z0-9\-_\.\u0020\&]+$/ readonly property var alphanumericalWithSpace: /^$|^[a-zA-Z0-9\s]+$/ readonly property var asciiPrintable: /^$|^[!-~]+$/ readonly property var ascii: /^$|^[\x00-\x7F]+$/