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) {
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

View File

@ -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()

View File

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

View File

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

View File

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

View File

@ -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.")

View File

@ -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

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}
}
}

View File

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

View File

@ -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")

View File

@ -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) => {

View File

@ -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]+$/