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
This commit is contained in:
Lukáš Tinkl 2024-10-22 16:14:43 +02:00 committed by Lukáš Tinkl
parent d317df032d
commit 158154bcca
14 changed files with 104 additions and 144 deletions

View File

@ -61,22 +61,21 @@ Control {
function getTagColor(isReadonly) { function getTagColor(isReadonly) {
if(isReadonly) if(isReadonly)
return Theme.palette.baseColor1 return Theme.palette.baseColor1
return mouseArea.containsMouse ? Theme.palette.miscColor1 : Theme.palette.primaryColor1 return root.hovered ? Theme.palette.miscColor1 : Theme.palette.primaryColor1
} }
} }
implicitHeight: 30 implicitHeight: 30
horizontalPadding: d.tagMargins horizontalPadding: d.tagMargins
font.pixelSize: 15 font.pixelSize: Theme.primaryTextFontSize
font.family: Theme.baseFont.name font.family: Theme.baseFont.name
background: Rectangle { background: Rectangle {
color: d.getTagColor(root.isReadonly) color: d.getTagColor(root.isReadonly)
radius: 8 radius: Theme.radius
} }
contentItem: RowLayout { contentItem: RowLayout {
id: tagRow
spacing: 2 spacing: 2
StatusIcon { StatusIcon {
@ -99,7 +98,6 @@ Control {
height: d.tagIconsSize height: d.tagIconsSize
icon: "close" icon: "close"
MouseArea { MouseArea {
id: mouseArea
enabled: !root.isReadonly enabled: !root.isReadonly
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true

View File

@ -73,7 +73,6 @@ Item {
Layout.leftMargin: Theme.padding Layout.leftMargin: Theme.padding
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
visible: text !== "" visible: text !== ""
font.pixelSize: 15
color: Theme.palette.baseColor1 color: Theme.palette.baseColor1
} }
Item { Item {
@ -115,7 +114,7 @@ Item {
Layout.minimumWidth: 4 Layout.minimumWidth: 4
Layout.fillHeight: true Layout.fillHeight: true
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
font.pixelSize: 15 font.pixelSize: Theme.primaryTextFontSize
color: Theme.palette.directColor1 color: Theme.palette.directColor1
selectByMouse: true selectByMouse: true
@ -193,7 +192,7 @@ Item {
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
Layout.rightMargin: Theme.padding Layout.rightMargin: Theme.padding
visible: text !== "" visible: text !== ""
font.pixelSize: 10 font.pixelSize: Theme.asideTextFontSize
color: Theme.palette.dangerColor1 color: Theme.palette.dangerColor1
} }
} }
@ -210,14 +209,12 @@ Item {
StatusButton { StatusButton {
objectName: "inlineSelectorConfirmButton" objectName: "inlineSelectorConfirmButton"
Layout.alignment: Qt.AlignVCenter
enabled: (listView.count > 0) enabled: (listView.count > 0)
text: qsTr("Confirm") text: qsTr("Confirm")
onClicked: root.confirmed() onClicked: root.confirmed()
} }
StatusButton { StatusButton {
Layout.alignment: Qt.AlignVCenter
text: qsTr("Cancel") text: qsTr("Cancel")
type: StatusBaseButton.Type.Danger type: StatusBaseButton.Type.Danger
onClicked: root.rejected() onClicked: root.rejected()

View File

@ -216,7 +216,6 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.left: accountImage.right anchors.left: accountImage.right
anchors.leftMargin: Theme.smallPadding anchors.leftMargin: Theme.smallPadding
font.pixelSize: 15
} }
MouseArea { MouseArea {

View File

@ -1,3 +1,4 @@
SuggestionBoxPanel 1.0 SuggestionBoxPanel.qml SuggestionBoxPanel 1.0 SuggestionBoxPanel.qml
UserListPanel 1.0 UserListPanel.qml UserListPanel 1.0 UserListPanel.qml
ChatAnchorButtonsPanel 1.0 ChatAnchorButtonsPanel.qml ChatAnchorButtonsPanel 1.0 ChatAnchorButtonsPanel.qml
InlineSelectorPanel 1.0 InlineSelectorPanel.qml

View File

@ -290,7 +290,6 @@ Item {
&& !d.sendingInProgress && !d.sendingInProgress
} }
store: root.rootStore
usersModel: d.activeUsersStore.usersModel usersModel: d.activeUsersStore.usersModel
sharedStore: root.sharedRootStore sharedStore: root.sharedRootStore

View File

@ -2,7 +2,6 @@ import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtQml.Models 2.15 import QtQml.Models 2.15
import QtGraphicalEffects 1.15
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
@ -46,7 +45,6 @@ Page {
Behavior on anchors.bottomMargin { NumberAnimation { duration: 30 }} Behavior on anchors.bottomMargin { NumberAnimation { duration: 30 }}
background: Rectangle { background: Rectangle {
anchors.fill: parent
color: Theme.palette.statusAppLayout.rightPanelBackgroundColor color: Theme.palette.statusAppLayout.rightPanelBackgroundColor
} }
@ -73,14 +71,14 @@ Page {
const ensName = member.displayName.includes(".eth") ? member.displayName : "" const ensName = member.displayName.includes(".eth") ? member.displayName : ""
root.rootStore.chatCommunitySectionModule.createOneToOneChat("", member.pubKey, ensName) root.rootStore.chatCommunitySectionModule.createOneToOneChat("", member.pubKey, ensName)
} else { } else {
var groupName = ""; var groupName = []
var pubKeys = []; var pubKeys = []
for (var i = 0; i < model.count; i++) { for (var i = 0; i < model.count; i++) {
const member = model.get(i) const member = model.get(i)
groupName += (member.displayName + (i === model.count - 1 ? "" : "&")) groupName.push(member.displayName)
pubKeys.push(member.pubKey) 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.alignment: Qt.AlignTop
Layout.leftMargin: Theme.halfPadding Layout.leftMargin: Theme.halfPadding
visible: contactsList.visible visible: contactsList.visible
font.pixelSize: 15
text: qsTr("Contacts") text: qsTr("Contacts")
color: Theme.palette.baseColor1 color: Theme.palette.baseColor1
} }
@ -182,7 +179,6 @@ Page {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
font.pixelSize: 15
color: Theme.palette.baseColor1 color: Theme.palette.baseColor1
text: qsTr("You can only send direct messages to your Contacts.\n 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.") 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.")

View File

@ -1,6 +1,6 @@
import QtQuick 2.14 import QtQuick 2.15
import QtQuick.Controls 2.14 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.14 import QtQuick.Layouts 1.15
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Theme 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.Controls 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import "../panels" import AppLayouts.Chat.stores 1.0
import "../stores"
import "private"
import utils 1.0 import utils 1.0
import SortFilterProxyModel 0.2 import SortFilterProxyModel 0.2
import "private"
MembersSelectorBase { MembersSelectorBase {
id: root id: root
@ -55,7 +55,7 @@ MembersSelectorBase {
readonly property string _pubKey: model.pubKey readonly property string _pubKey: model.pubKey
height: ListView.view.height height: ListView.view.height
text: root.tagText(model.localNickname, model.displayName, model.alias) text: model.preferredDisplayName
isReadonly: { isReadonly: {
if (model.memberRole === Constants.memberRole.owner) return true if (model.memberRole === Constants.memberRole.owner) return true

View File

@ -1,6 +1,6 @@
import QtQuick 2.14 import QtQuick 2.15
import QtQuick.Controls 2.14 import QtQuick.Controls 2.15
import QtQml.Models 2.2 import QtQml.Models 2.15
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
@ -9,8 +9,6 @@ import "private"
import utils 1.0 import utils 1.0
import SortFilterProxyModel 0.2
MembersSelectorBase { MembersSelectorBase {
id: root id: root
@ -39,20 +37,18 @@ MembersSelectorBase {
} }
onTextPasted: (text) => { onTextPasted: (text) => {
// When pated, process text immediately // When pasted, process text immediately
contactLookupDelayTimer.stop() // when pasting, textChanged is still emited first contactLookupDelayTimer.stop() // when pasting, textChanged is still emitted first
d.lookupContact(text) d.lookupContact(text)
} }
model: SortFilterProxyModel { model: d.selectedMembers
sourceModel: d.selectedMembers
}
delegate: StatusTagItem { delegate: StatusTagItem {
readonly property string _pubKey: model.pubKey readonly property string _pubKey: model.pubKey
height: ListView.view.height height: ListView.view.height
text: root.tagText(model.localNickname, model.displayName, model.alias) text: model.localNickname || model.displayName
onClosed: root.entryRemoved(this) onClosed: root.entryRemoved(this)
} }
@ -129,11 +125,7 @@ MembersSelectorBase {
return false return false
} }
d.selectedMembers.append({ d.selectedMembers.append({pubKey, displayName, localNickname})
"pubKey": pubKey,
"displayName": displayName,
"localNickname": localNickname
})
return true return true
} }

View File

@ -1,16 +1,13 @@
import QtQuick 2.14 import QtQuick 2.15
import QtQuick.Controls 2.14 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.14 import QtQuick.Layouts 1.15
import StatusQ 0.1
import StatusQ.Core 0.1 import StatusQ.Core 0.1
import StatusQ.Core.Theme 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.stores 1.0 as ChatStores
import AppLayouts.Chat.panels 1.0
import utils 1.0 import utils 1.0
import shared.controls.delegates 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 readonly property int membersLimit: 20 // see: https://github.com/status-im/status-mobile/issues/13066
property bool limitReached: model.count >= membersLimit property bool limitReached: model.count >= membersLimit
function tagText(localNickname, displayName, aliasName) {
return localNickname || displayName || aliasName
}
property string pastedChatKey: "" property string pastedChatKey: ""
label.text: qsTr("To:") label.text: qsTr("To:")
@ -54,41 +47,32 @@ InlineSelectorPanel {
return true return true
} }
function isPastedProfileLinkToContact(pubkey) {
return root.pastedChatKey === pubkey
}
filters: [ filters: [
ExpressionFilter { FastExpressionFilter {
enabled: root.edit.text !== "" && root.pastedChatKey == "" enabled: root.edit.text !== "" && root.pastedChatKey == ""
expression: { expression: {
root.edit.text // ensure expression is reevaluated when edit.text changes root.edit.text // ensure expression is reevaluated when edit.text changes
return _suggestionsModel.searchPredicate(model.displayName, model.localNickname, model.alias) return _suggestionsModel.searchPredicate(model.displayName, model.localNickname, model.alias)
} }
expectedRoles: ["displayName", "localNickname", "alias"]
}, },
ExpressionFilter { FastExpressionFilter {
expression: { expression: {
root.model.count // ensure expression is reevaluated when members model changes root.model.count // ensure expression is reevaluated when members model changes
return _suggestionsModel.notAMemberPredicate(model.pubKey) return _suggestionsModel.notAMemberPredicate(model.pubKey)
} }
expectedRoles: ["pubKey"]
}, },
ExpressionFilter { ValueFilter {
enabled: root.pastedChatKey != "" roleName: "pubKey"
expression: { value: root.pastedChatKey
root.pastedChatKey // ensure expression is reevaluated when members model changes enabled: root.pastedChatKey !== ""
return _suggestionsModel.isPastedProfileLinkToContact(model.pubKey)
}
} }
] ]
proxyRoles: ExpressionRole {
name: "title"
expression: model.localNickname || model.displayName || model.alias
}
sorters: StringSorter { sorters: StringSorter {
roleName: "title" roleName: "preferredDisplayName"
numericMode: true caseSensitivity: Qt.CaseInsensitive
} }
} }

View File

@ -1,7 +1,7 @@
import QtQuick 2.13 import QtQuick 2.15
import QtQuick.Controls 2.13 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.13 import QtQuick.Layouts 1.15
import QtQml.Models 2.14 import QtQml.Models 2.15
import shared.controls 1.0 import shared.controls 1.0
import shared.panels 1.0 import shared.panels 1.0
@ -26,11 +26,11 @@ StatusDialog {
title: qsTr("Edit group name and image") title: qsTr("Edit group name and image")
width: 480 width: 480
height: 610 padding: 0
QtObject { QtObject {
id: d 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: { onOpened: {
@ -47,8 +47,13 @@ StatusDialog {
imageEditor.dataImage = activeGroupImageData imageEditor.dataImage = activeGroupImageData
} }
ColumnLayout { StatusScrollView {
id: scrollView
anchors.fill: parent anchors.fill: parent
contentWidth: availableWidth
ColumnLayout {
width: scrollView.availableWidth
spacing: 20 spacing: 20
StatusInput { StatusInput {
@ -64,7 +69,7 @@ StatusDialog {
errorMessage: Utils.getErrorMessage(groupName.errors, qsTr("group name")) errorMessage: Utils.getErrorMessage(groupName.errors, qsTr("group name"))
}, },
StatusRegularExpressionValidator { StatusRegularExpressionValidator {
regularExpression: Constants.regularExpressions.alphanumericalExpanded regularExpression: Constants.regularExpressions.alphanumericalExpanded2
errorMessage: Constants.errorMessages.alphanumericalExpandedRegExp errorMessage: Constants.errorMessages.alphanumericalExpandedRegExp
} }
] ]
@ -73,7 +78,6 @@ StatusDialog {
StatusBaseText { StatusBaseText {
id: imgText id: imgText
text: qsTr("Group image") text: qsTr("Group image")
leftPadding: groupName.leftPadding - root.padding
} }
EditCroppedImagePanel { EditCroppedImagePanel {
@ -91,13 +95,13 @@ StatusDialog {
StatusBaseText { StatusBaseText {
id: colorText id: colorText
text: qsTr("Standard colours") text: qsTr("Standard colours")
leftPadding: groupName.leftPadding - root.padding
} }
StatusColorSelectorGrid { StatusColorSelectorGrid {
id: colorSelectionGrid id: colorSelectionGrid
objectName: "groupChatEdit_color" objectName: "groupChatEdit_color"
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
Layout.topMargin: -(parent.spacing / 3)
diameter: 40 diameter: 40
selectorDiameter: 16 selectorDiameter: 16
columns: 6 columns: 6
@ -109,6 +113,7 @@ StatusDialog {
height: 10 height: 10
} }
} }
}
footer: StatusDialogFooter { footer: StatusDialogFooter {
rightButtons: ObjectModel { rightButtons: ObjectModel {

View File

@ -39,7 +39,6 @@ Rectangle {
signal dismissLinkPreview(int index) signal dismissLinkPreview(int index)
property var usersModel property var usersModel
property ChatStores.RootStore store
property SharedStores.RootStore sharedStore property SharedStores.RootStore sharedStore
property var emojiPopup: null property var emojiPopup: null

View File

@ -54,16 +54,6 @@ StatusMenu {
onTriggered: root.displayProfilePopup(root.chatId) 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 { StatusAction {
objectName: "addRemoveFromGroupStatusAction" objectName: "addRemoveFromGroupStatusAction"
text: root.amIChatAdmin ? qsTr("Add / remove from group") : qsTr("Add to group") text: root.amIChatAdmin ? qsTr("Add / remove from group") : qsTr("Add to group")

View File

@ -933,7 +933,6 @@ Loader {
suggestionsOpened = false suggestionsOpened = false
} }
store: root.rootStore
usersModel: root.usersStore.usersModel usersModel: root.usersStore.usersModel
sharedStore: root.sharedRootStore sharedStore: root.sharedRootStore
emojiPopup: root.emojiPopup emojiPopup: root.emojiPopup
@ -957,7 +956,7 @@ Loader {
linkPreviewModel: root.linkPreviewModel linkPreviewModel: root.linkPreviewModel
gifLinks: root.gifLinks gifLinks: root.gifLinks
playAnimations: root.Window.window.active && root.messageStore.isChatActive playAnimations: root.Window.active && root.messageStore.isChatActive
isOnline: root.rootStore.mainModuleInst.isOnline isOnline: root.rootStore.mainModuleInst.isOnline
highlightLink: delegate.hoveredLink highlightLink: delegate.hoveredLink
onImageClicked: (image, mouse, imageSource, url) => { onImageClicked: (image, mouse, imageSource, url) => {

View File

@ -659,6 +659,7 @@ QtObject {
readonly property var alphanumerical: /^$|^[a-zA-Z0-9]+$/ readonly property var alphanumerical: /^$|^[a-zA-Z0-9]+$/
readonly property var alphanumericalExpanded: /^$|^[a-zA-Z0-9\-_\.\u0020]+$/ readonly property var alphanumericalExpanded: /^$|^[a-zA-Z0-9\-_\.\u0020]+$/
readonly property var alphanumericalExpanded1: /^[a-zA-Z0-9\-_]+(?: [a-zA-Z0-9\-_]+)*$/ 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 alphanumericalWithSpace: /^$|^[a-zA-Z0-9\s]+$/
readonly property var asciiPrintable: /^$|^[!-~]+$/ readonly property var asciiPrintable: /^$|^[!-~]+$/
readonly property var ascii: /^$|^[\x00-\x7F]+$/ readonly property var ascii: /^$|^[\x00-\x7F]+$/