feat(CreateCommunityPopup): validation and ensuring min. 1 tag added

- enable the Next button, and delay the validation after it's been clicked
- visually display required fields also for logo, banner, and tags (min
1 tag is required now)
- when pasting over limit, chop the text to the maximum length, instead
of just leaving the text field empty
- do not let the popup autoclose on clicking outside or pressing Esc
- add a StoryBook page
- minor cleanups and alignments to the latest Figma designs/flows

Fixes #13966
Fixes #16479
Fixes #14902
This commit is contained in:
Lukáš Tinkl 2024-10-08 22:22:22 +02:00 committed by Lukáš Tinkl
parent 5518df3af9
commit 26dddcaff9
25 changed files with 399 additions and 102 deletions

View File

@ -0,0 +1,181 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import StatusQ 0.1
import Storybook 1.0
import Models 1.0
import mainui 1.0
import shared.stores 1.0 as SharedStores
import AppLayouts.Communities.popups 1.0
import AppLayouts.Communities.controls 1.0
import AppLayouts.Communities.stores 1.0 as CommunitiesStores
import AppLayouts.stores 1.0 as AppLayoutStores
SplitView {
id: root
Logs { id: logs }
orientation: Qt.Vertical
property var dialog
function createAndOpenDialog() {
dialog = dlgComponent.createObject(popupBg)
dialog.open()
}
Component.onCompleted: createAndOpenDialog()
Item {
SplitView.fillWidth: true
SplitView.fillHeight: true
PopupBackground {
id: popupBg
anchors.fill: parent
Button {
anchors.centerIn: parent
text: "Reopen"
onClicked: createAndOpenDialog()
}
}
Popups {
popupParent: root
sharedRootStore: SharedStores.RootStore {}
rootStore: AppLayoutStores.RootStore {}
communityTokensStore: SharedStores.CommunityTokensStore {}
isDevBuild: ctrlIsDevBuild.checked
}
Component {
id: dlgComponent
CreateCommunityPopup {
id: dialog
anchors.centerIn: parent
destroyOnClose: true
modal: false
isDiscordImport: isDiscordCheckBox.checked
isDevBuild: ctrlIsDevBuild.checked
QtObject {
id: localAppSettings
readonly property bool testEnvironment: dialog.isDevBuild
}
store: CommunitiesStores.CommunitiesStore {
readonly property string communityTags: ModelsData.communityTags
function createCommunity() {
logs.logEvent("createCommunity")
}
property string discordImportChannelName
readonly property bool discordImportInProgress: false
readonly property bool discordDataExtractionInProgress: false
readonly property int discordImportErrorsCount: 0
readonly property int discordImportWarningsCount: 0
readonly property int discordOldestMessageTimestamp: 0
property var discordFileList: ListModel {
readonly property int selectedCount: count
property bool selectedFilesValid
}
property var discordCategoriesModel: ListModel {}
property var discordChannelsModel: ListModel {
property bool hasSelectedItems
readonly property int count: 32 // hide the parsing/loading spinner
}
function setFileListItems(filePaths) {
for (const filePath of filePaths) {
discordFileList.append({"filePath": filePath, errorMessage: ""})
}
}
function removeFileListItem(path) {
for (let i = 0; i < discordFileList.count; i++) {
const item = discordFileList.get(i)
if (item.filePath === path)
discordFileList.remove(i)
}
}
function clearFileList() {
discordFileList.clear()
discordFileList.selectedFilesValid = false
}
function clearDiscordCategoriesAndChannels() {
discordCategoriesModel.clear()
discordChannelsModel.clear()
discordChannelsModel.hasSelectedItems = false
}
function requestExtractChannelsAndCategories() {
discordFileList.selectedFilesValid = true
}
function toggleOneDiscordChannel(id) {
logs.logEvent("toggleOneDiscordChannel", ["id"], arguments)
}
function resetDiscordImport() {
logs.logEvent("resetDiscordImport")
}
function requestCancelDiscordChannelImport(id) {
logs.logEvent("requestCancelDiscordChannelImport", ["id"], arguments)
}
function requestImportDiscordChannel(args, timestamp) {
logs.logEvent("requestImportDiscordChannel", ["args", "timestamp"], arguments)
}
}
}
}
}
LogsAndControlsPanel {
SplitView.minimumHeight: 100
SplitView.preferredHeight: 200
logsView.logText: logs.logText
ColumnLayout {
Switch {
id: isDiscordCheckBox
text: "Discord import"
onToggled: {
if (!!dialog && dialog.opened)
dialog.close()
}
}
Switch {
id: ctrlIsDevBuild
text: "Dev build"
onToggled: {
if (!!dialog && dialog.opened)
dialog.close()
}
}
}
}
}
// category: Popups
// https://www.figma.com/design/qHfFm7C9LwtXpfdbxssCK3/Kuba%E2%8E%9CDesktop---Communities?node-id=52741-266926&node-type=frame&t=PkDxeWSXoiZbIQXv-0
// https://www.figma.com/design/qHfFm7C9LwtXpfdbxssCK3/Kuba%E2%8E%9CDesktop---Communities?node-id=2636-359221&node-type=frame&t=PkDxeWSXoiZbIQXv-0
// https://www.figma.com/design/qHfFm7C9LwtXpfdbxssCK3/Kuba%E2%8E%9CDesktop---Communities?node-id=52741-267155&node-type=frame&t=PkDxeWSXoiZbIQXv-0

View File

@ -1,4 +1,4 @@
import QtQuick 2.14
import QtQuick 2.15
import StatusQ 0.1
import StatusQ.Core 0.1
@ -76,7 +76,7 @@ Item {
delegate: StatusCommunityTag {
emoji: model.emoji
name: model.name
removable: root.mode === StatusCommunityTags.ShowSelectedOnly && root.active
removable: root.mode === StatusCommunityTags.ShowSelectedOnly && root.active && repeater.count > 1
highlighted: root.mode === StatusCommunityTags.Highlight && model.selected
onClicked: root.clicked(model)

View File

@ -419,7 +419,7 @@ Item {
let utf8Length = Utils.encodeUtf8(text).length
if (utf8Length > root.maximumLength) {
var cursor = cursorPosition
text = previousText
text = text.slice(0, maximumLength)
if (cursor > edit.length) {
cursorPosition = edit.length
} else {

View File

@ -1,10 +1,8 @@
import QtQuick 2.13
import QtQuick 2.15
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import "./"
Column {
id: root
@ -27,7 +25,6 @@ Column {
id: title
width: parent.width
verticalAlignment: Text.AlignVCenter
font.pixelSize: 15
color: Theme.palette.baseColor1
}

View File

@ -97,8 +97,8 @@ Item {
*/
property alias errorMessageCmp: errorMessage
/*!
\qmlproperty string StatusInput::label
This property sets the label text.
\qmlproperty int StatusInput::labelPadding
This property sets the padding of the label text.
*/
property int labelPadding: 8
/*!
@ -118,7 +118,7 @@ Item {
property int charLimit: 0
/*!
\qmlproperty string StatusInput::charLimitLabel
This property overrides the char default chart limit text format.
This property overrides the default char limit text.
*/
property string charLimitLabel: ""
/*!
@ -157,12 +157,12 @@ Item {
*/
property real maximumHeight: 44
/*!
\qmlproperty list StatusBaseInput::validators
\qmlproperty list<StatusValidator> StatusBaseInput::validators
This property sets the list of validators to be considered.
*/
property list<StatusValidator> validators
/*!
\qmlproperty list StatusBaseInput::validators
\qmlproperty list<StatusAsyncValidator> StatusBaseInput::asyncValidators
This property sets the list of async validators to be considered.
*/
property list<StatusAsyncValidator> asyncValidators
@ -250,7 +250,7 @@ Item {
*/
property var errors: ({})
/*!
\qmlproperty var StatusBaseInput::errors
\qmlproperty var StatusBaseInput::asyncErrors
This property holds the validation async errors.
*/
property var asyncErrors: ({})
@ -343,8 +343,9 @@ Item {
root.validatedValue = root.text
}
}
/*!
\qmlmethod
\qmlmethod updateAsyncValidity(validatorName, value, result)
This function updates the text input async validation.
*/
function updateAsyncValidity(validatorName, value, result) {
@ -435,7 +436,7 @@ Item {
id: charLimitLabelItem
Layout.alignment: Qt.AlignVCenter
height: visible ? contentHeight : 0
visible: root.charLimit > 0
visible: root.charLimit > 0 || !!root.charLimitLabel
text: root.charLimitLabel ? root.charLimitLabel : "%1 / %2".arg(Utils.encodeUtf8(statusBaseInput.text).length).arg(root.charLimit)
font.pixelSize: 12
color: statusBaseInput.enabled ? Theme.palette.baseColor1 : Theme.palette.directColor6

View File

@ -29,6 +29,8 @@ StatusButton {
Down
}
property bool isError
horizontalPadding: 16
verticalPadding: 3
spacing: 4
@ -37,6 +39,8 @@ StatusButton {
background: Rectangle {
radius: 8
color: root.bgColor
border.color: Theme.palette.dangerColor1
border.width: root.isError ? 1 : 0
}
opacity: !root.interactive || !root.enabled ? 0.5 : 1
contentItem: RowLayout {
@ -62,16 +66,11 @@ StatusButton {
elide: Text.ElideRight
}
StatusIcon {
icon: "tiny/chevron-right"
icon: "next"
visible: root.type === StatusPickerButton.PickerType.Next
color: !Qt.colorEqual(root.contentColor, Theme.palette.baseColor1) ? root.contentColor : Theme.palette.directColor1
width: root.icon.width
height: root.icon.height
}
}
HoverHandler {
enabled: root.enabled
cursorShape: Qt.PointingHandCursor
}
}

View File

@ -8,8 +8,8 @@ StatusValidator {
errorMessage: {
minLength === 1 ?
"Please enter a value" :
`The value must be at least ${minLength} characters.`
qsTr("Please enter a value") :
qsTr("The value must be at least %n character(s).", "", minLength)
}
validate: function (value) {
@ -19,4 +19,3 @@ StatusValidator {
}
}
}

View File

@ -1,4 +1,4 @@
import QtQuick 2.14
import QtQuick 2.15
QtObject {
property string name: ""

View File

@ -1,6 +1,7 @@
import QtQuick 2.0
import QtTest 1.0
import QtQuick 2.15
import QtTest 1.15
import StatusQ 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
@ -24,7 +25,7 @@ Item {
property StatusInput testControl: null
name: "RegexValidationTest"
name: "StatusInput-RegexValidationTest"
when: windowShown
@ -110,7 +111,7 @@ Item {
property StatusInput testControl: null
name: "BindedValuesTest"
name: "StatusInput-BindedValuesTest"
when: windowShown
property QtObject dataObject: QtObject {
@ -167,6 +168,35 @@ Item {
verify(qtOuput.qtOuput().length === 0, `No output expected. Found:\n"${qtOuput.qtOuput()}"\n`)
}
// regression test for https://github.com/status-im/status-desktop/issues/16479
function test_paste_text() {
verify(!!testControl)
const textToPaste = "1234567890abcdef" // 16 chars
ClipboardUtils.setText(textToPaste)
// clear
testControl.input.edit.clear()
testControl.charLimit = 0
keySequence(StandardKey.Paste)
// verify we can paste the full text
compare(testControl.input.edit.length, textToPaste.length)
compare(testControl.input.edit.text, textToPaste)
compare(testControl.input.dirty, true)
// clear again, and set a lower limit
testControl.input.edit.clear()
testControl.charLimit = 10
keySequence(StandardKey.Paste)
// verify we can paste (some) text and it gets truncated to the charLimit
compare(testControl.input.edit.length, testControl.charLimit)
compare(testControl.input.edit.text, textToPaste.slice(0, testControl.charLimit))
}
}
MonitorQtOutput {

View File

@ -27,12 +27,16 @@ Item {
implicitHeight: layout.childrenRect.height
function validate() {
editor.isError = !hasSelectedImage
}
ColumnLayout {
id: layout
anchors.fill: parent
spacing: 19
spacing: 16
StatusBaseText {
text: qsTr("Community banner")
@ -67,6 +71,17 @@ Item {
additionalTextPixelSize: Theme.tertiaryTextFontSize
}
}
StatusBaseText {
Layout.fillWidth: true
Layout.topMargin: -layout.spacing/2
visible: editor.isError
text: qsTr("Upload a community banner")
font.pixelSize: Theme.tertiaryTextFontSize
color: Theme.palette.dangerColor1
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
}
}
}

View File

@ -19,8 +19,6 @@ ColumnLayout {
spacing: 8
implicitHeight: childrenRect.height
StatusBaseText {
Layout.fillWidth: true
text: root.title
@ -35,6 +33,9 @@ ColumnLayout {
bgColor: root.color
contentColor: Theme.palette.white
text: root.color.toString()
font.weight: Font.Normal
icon.width: 24
icon.height: 24
onClicked: root.pick()
onTextChanged: {

View File

@ -1,4 +1,4 @@
import QtQuick 2.14
import QtQuick 2.15
import utils 1.0

View File

@ -21,6 +21,7 @@ Control {
readonly property alias isDescriptionDirty: descriptionTextInput.input.dirty
readonly property alias isLogoSelected: logoPicker.hasSelectedImage
readonly property alias isBannerSelected: bannerPicker.hasSelectedImage
readonly property alias areTagsSelected: tagsPicker.hasSelectedTags
property alias name: nameInput.text
property alias description: descriptionTextInput.text
@ -38,31 +39,50 @@ Control {
implicitWidth: 608
contentItem: ColumnLayout {
function validate(isDevBuild = false) {
if (!nameInput.validate(true))
nameInput.input.dirty = true
if (!descriptionTextInput.validate(true))
descriptionTextInput.input.dirty = true
if (!isDevBuild) {
logoPicker.validate()
bannerPicker.validate()
tagsPicker.validate()
}
return nameInput.valid && descriptionTextInput.valid &&
(isDevBuild ? true : logoPicker.hasSelectedImage && bannerPicker.hasSelectedImage && tagsPicker.hasSelectedTags)
}
contentItem: ColumnLayout {
spacing: 16
NameInput {
id: nameInput
input.edit.objectName: "communityNameInput"
Layout.fillWidth: true
Component.onCompleted: nameInput.input.forceActiveFocus(Qt.MouseFocusReason)
input.tabNavItem: descriptionTextInput.input.edit
Component.onCompleted: nameInput.input.forceActiveFocus()
}
DescriptionInput {
id: descriptionTextInput
input.edit.objectName: "communityDescriptionInput"
input.tabNavItem: nameInput.input.edit
Layout.fillWidth: true
}
LogoPicker {
id: logoPicker
objectName: "communityLogoPicker"
onHasSelectedImageChanged: validate()
Layout.fillWidth: true
}
BannerPicker {
id: bannerPicker
objectName: "communityBannerPicker"
onHasSelectedImageChanged: validate()
Layout.fillWidth: true
Layout.topMargin: -8 //Closer by design
}
@ -109,12 +129,13 @@ Control {
width: 640
replaceItem: TagsPanel {
Component.onCompleted: {
tags = tagsPicker.tags;
selectedTags = tagsPicker.selectedTags;
tags = tagsPicker.tags
selectedTags = tagsPicker.selectedTags
}
onAccepted: {
tagsPicker.selectedTags = selectedTags;
close();
tagsPicker.selectedTags = selectedTags
tagsPicker.validate()
close()
}
}
onClosed: destroy()

View File

@ -1,9 +1,8 @@
import QtQuick 2.14
import QtQuick 2.15
import utils 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1

View File

@ -27,6 +27,10 @@ Item {
implicitHeight: layout.childrenRect.height
function validate() {
editor.isError = !hasSelectedImage
}
ColumnLayout {
id: layout
@ -37,7 +41,6 @@ Item {
id: label
Layout.fillWidth: true
text: qsTr("Community logo")
font.pixelSize: 15
color: Theme.palette.directColor1
horizontalAlignment: Qt.AlignLeft
}
@ -59,6 +62,16 @@ Item {
visible: !editor.userSelectedImage && !root.imageData
}
}
StatusBaseText {
Layout.fillWidth: true
visible: editor.isError
text: qsTr("Upload a community logo")
font.pixelSize: Theme.tertiaryTextFontSize
color: Theme.palette.dangerColor1
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
}
}
}

View File

@ -1,9 +1,8 @@
import QtQuick 2.14
import QtQuick 2.15
import utils 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1

View File

@ -31,7 +31,6 @@ ColumnLayout {
StatusCheckBox {
id: archiveSupportToggle
width: (parent.width-12)
checked: false
leftSide: false
padding: 0
anchors.verticalCenter: parent.verticalCenter
@ -39,11 +38,7 @@ ColumnLayout {
StatusToolTip {
text: qsTr('For this Community Setting to work, you also need to activate "Archive Protocol Enabled" in Advanced Settings')
visible: hoverHandler.hovered
}
HoverHandler {
id: hoverHandler
enabled: true
visible: parent.hovered
}
}
}
@ -65,7 +60,7 @@ ColumnLayout {
ColumnLayout {
Layout.preferredWidth: parent.width
Layout.topMargin: 22
spacing: 0
spacing: 4
StatusCheckBox {
id: requestToJoinToggle
Layout.fillWidth: true
@ -78,9 +73,8 @@ ColumnLayout {
}
StatusBaseText {
id: warningText
Layout.fillWidth: true
Layout.rightMargin: 12
Layout.rightMargin: 64
visible: requestToJoinToggle.checked
wrapMode: Text.WordWrap
text: qsTr("Warning: Only token gated communities (or token gated channels inside non-token gated community) are encrypted")

View File

@ -1,5 +1,5 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick 2.15
import QtQuick.Layouts 1.15
import utils 1.0
@ -15,14 +15,19 @@ ColumnLayout {
property string tags
property string selectedTags
readonly property bool hasSelectedTags: localAppSettings.testEnvironment || selectedTags !== ""
signal pick()
implicitHeight: childrenRect.height
spacing: 14
spacing: 8
onSelectedTagsChanged: d.handleSelectedTags()
Component.onCompleted: d.handleSelectedTags()
function validate() {
pickerButton.isError = !hasSelectedTags
}
QtObject {
id: d
@ -33,7 +38,7 @@ ColumnLayout {
d.tagsModel.clear();
for (const key of Object.keys(obj)) {
if (array.indexOf(key) != -1) {
if (array.indexOf(key) !== -1) {
d.tagsModel.append({ name: key, emoji: obj[key], selected: false });
}
}
@ -42,15 +47,19 @@ ColumnLayout {
StatusBaseText {
text: qsTr("Tags")
font.pixelSize: 15
color: Theme.palette.directColor1
}
StatusPickerButton {
id: pickerButton
bgColor: d.tagsModel.count === 0 ? Theme.palette.baseColor2 : "transparent"
text: d.tagsModel.count === 0 ? qsTr("Choose tags describing the community") : ""
onClicked: root.pick()
font.weight: Font.Normal
icon.width: 24
icon.height: 24
Layout.fillWidth: true
Layout.preferredHeight: 44
StatusCommunityTags {
anchors.verticalCenter: parent.verticalCenter
@ -60,4 +69,14 @@ ColumnLayout {
contentWidth: width
}
}
StatusBaseText {
Layout.fillWidth: true
visible: pickerButton.isError
text: qsTr("Add at least 1 tag")
font.pixelSize: Theme.tertiaryTextFontSize
color: Theme.palette.dangerColor1
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignRight
}
}

View File

@ -1,15 +1,11 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtGraphicalEffects 1.13
import QtQuick 2.15
import QtQuick.Controls 2.15
import StatusQ.Core 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1 as StatusQControls
import shared.panels 1.0
import shared.status 1.0
import utils 1.0
Rectangle {
@ -61,7 +57,6 @@ Rectangle {
anchors.top: parent.top
anchors.topMargin: 48
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 15
color: Theme.palette.directColor1
wrapMode: Text.WordWrap
anchors.right: parent.right

View File

@ -1,6 +1,6 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.12
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
@ -69,7 +69,7 @@ StatusScrollView {
// TODO: editingFinished() signal instead of this crutch
property bool locked: false
implicitWidth: 256
Layout.preferredWidth: 256
validators: [
StatusRegularExpressionValidator {
regularExpression: /^#(?:[0-9a-fA-F]{3}){1,2}$/
@ -91,22 +91,18 @@ StatusScrollView {
StatusBaseText {
text: qsTr("Preview")
font.pixelSize: 15
}
Rectangle {
implicitHeight: 48
Layout.fillWidth: true
Layout.preferredHeight: 44
radius: 10
color: root.color
Layout.fillWidth: true
StatusBaseText {
id: preview
x: 16
y: 16
anchors.centerIn: parent
text: qsTr("White text should be legible on top of this colour")
color: Theme.palette.white
font.pixelSize: 15
}
}
@ -114,7 +110,6 @@ StatusScrollView {
id: colorSelectionGrid
titleText: qsTr("Standard colours")
title.color: Theme.palette.directColor1
title.font.pixelSize: 15
columns: 8
model: Theme.palette.communityColorsArray
selectedColorIndex: -1

View File

@ -173,7 +173,9 @@ StackLayout {
Connections {
target: root
onCommunityIdChanged: reset()
function onCommunityIdChanged() {
reset()
}
}
}

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
@ -23,6 +23,7 @@ StatusScrollView {
property var rightButtons: StatusButton {
objectName: "confirmCommunityTagsButton"
text: qsTr("Confirm Community Tags")
enabled: d.countSelectedTags > 0
onClicked: {
var selectedTags = [];
for (let i = 0; i < d.tagsModel.count; ++i) {
@ -39,12 +40,12 @@ StatusScrollView {
function updateSelectedTags() {
var array = selectedTags.length ? JSON.parse(selectedTags) : [];
d.cntSelectedTags = 0;
d.countSelectedTags = 0;
for (let i = 0; i < d.tagsModel.count; ++i) {
let item = d.tagsModel.get(i);
if (array.indexOf(item.name) != -1) {
if (array.indexOf(item.name) !== -1) {
item.selected = true;
d.cntSelectedTags++;
d.countSelectedTags++;
} else {
item.selected = false;
}
@ -55,7 +56,7 @@ StatusScrollView {
onTagsChanged: {
var obj = JSON.parse(tags);
d.cntSelectedTags = 0;
d.countSelectedTags = 0;
d.tagsModel.clear();
for (const key of Object.keys(obj)) {
d.tagsModel.append({ name: key, emoji: obj[key], selected: false });
@ -70,7 +71,7 @@ StatusScrollView {
QtObject {
id: d
property int cntSelectedTags: 0
property int countSelectedTags: 0
property ListModel tagsModel: ListModel {}
}
@ -83,7 +84,6 @@ StatusScrollView {
id: tagsFilter
label: qsTr("Select tags that will fit your Community")
labelPadding: Style.current.bigPadding
font.pixelSize: 15
input.asset.name: "search"
placeholderText: qsTr("Search tags")
Layout.fillWidth: true
@ -96,9 +96,9 @@ StatusScrollView {
StatusCommunityTags {
filterString: tagsFilter.text
model: d.tagsModel
enabled: d.cntSelectedTags < maxSelectedTags
enabled: d.countSelectedTags < maxSelectedTags
onClicked: {
d.cntSelectedTags++;
d.countSelectedTags++;
item.selected = true;
}
Layout.fillWidth: true
@ -112,12 +112,11 @@ StatusScrollView {
RowLayout {
StatusBaseText {
text: qsTr("Selected tags")
font.pixelSize: 15
Layout.fillWidth: true
}
StatusBaseText {
text: qsTr("%1 / %2").arg(d.cntSelectedTags).arg(maxSelectedTags)
text: qsTr("%1 / %2").arg(d.countSelectedTags).arg(maxSelectedTags)
color: Theme.palette.baseColor1
font.pixelSize: 13
}
@ -127,10 +126,21 @@ StatusScrollView {
model: d.tagsModel
mode: StatusCommunityTags.ShowSelectedOnly
onClicked: {
d.cntSelectedTags--;
d.countSelectedTags--;
item.selected = false;
}
Layout.fillWidth: true
Layout.bottomMargin: Style.current.padding
}
StatusBaseText {
Layout.fillWidth: true
Layout.bottomMargin: Style.current.padding
text: qsTr("No tags selected yet")
color: Theme.palette.baseColor1
visible: d.countSelectedTags === 0
font.pixelSize: 13
horizontalAlignment: Qt.AlignHCenter
}
}
}

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 QtQuick.Dialogs 1.3
import utils 1.0
@ -30,6 +30,8 @@ StatusStackModal {
qsTr("Create New Community")
width: 640
closePolicy: Popup.NoAutoClose // explicit [x] click needed, or via the `close()` method
nextButton: StatusButton {
objectName: "createCommunityNextBtn"
font.weight: Font.Medium
@ -55,8 +57,6 @@ StatusStackModal {
if (typeof (nextAction) == "function") {
return nextAction()
}
if (!root.isDiscordImport)
d.createCommunity()
}
}
@ -363,7 +363,12 @@ StatusStackModal {
StatusScrollView {
id: generalView
contentWidth: availableWidth
readonly property bool canGoNext: generalViewLayout.isNameValid && generalViewLayout.isDescriptionValid && (root.isDevBuild || (generalViewLayout.isLogoSelected && generalViewLayout.isBannerSelected))
readonly property var nextAction: () => {
if (generalViewLayout.validate(root.isDevBuild)) {
root.currentIndex++
}
}
padding: 0
clip: false
@ -389,8 +394,20 @@ StatusStackModal {
ColumnLayout {
id: introOutroMessageView
spacing: 11
readonly property bool canGoNext: introMessageInput.valid && outroMessageInput.valid
spacing: 16
readonly property var nextAction: () => {
if (!introMessageInput.validate(true))
introMessageInput.input.dirty = true
if (!outroMessageInput.validate(true))
outroMessageInput.input.dirty = true
if (introMessageInput.valid && outroMessageInput.valid) {
if (root.isDiscordImport)
root.currentIndex++
else
d.createCommunity()
}
}
IntroMessageInput {
id: introMessageInput

View File

@ -35,6 +35,7 @@ Item {
property bool userSelectedImage: false
readonly property bool nothingToShow: state === d.noImageState
property bool isError
readonly property alias cropWorkflow : imageCropWorkflow
@ -119,6 +120,9 @@ Item {
icon.name: "edit_pencil"
width: 40
height: 40
readonly property real rotationRadius: root.roundedImage ? parent.width/2 : imageCropEditor.radius
transform: [
Translate {
@ -154,10 +158,16 @@ Item {
radius: roundedImage ? Math.max(width, height)/2 : croppedPreview.radius
color: Style.current.inputBackground
border.color: Theme.palette.dangerColor1
border.width: root.isError ? 1 : 0
StatusRoundButton {
id: addButton
icon.name: "add"
width: 40
height: 40
readonly property real rotationRadius: root.roundedImage ? parent.width/2 : imageCropEditor.radius
transform: [

View File

@ -10,7 +10,7 @@ import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
/*!
/brief Image icon and ulopad text hints for banner and logo
/brief Image icon and upload text hints for banner and logo
*/
Control {
id: root