refactor(Communities): use `StatusModal` in `CreateCommunityPopup`

This replaces the modal popup with `StatusModal` in the `CreateCommunityPopup`
and also ensures that it adheres to the design.

There are still things to be refactored in here, primarily form control components.
Those will be tackled in future commits once they have been built in StatusQ

Closes #2882
This commit is contained in:
Pascal Precht 2021-07-16 12:11:03 +02:00 committed by Iuri Matias
parent 9795890544
commit 9eb752885d
3 changed files with 301 additions and 298 deletions

View File

@ -4,9 +4,14 @@ import QtGraphicalEffects 1.13
import QtQuick.Dialogs 1.3 import QtQuick.Dialogs 1.3
import "../../../../imports" import "../../../../imports"
import "../../../../shared" import "../../../../shared"
import "../../../../shared/status"
ModalPopup { import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups 0.1
StatusModal {
property QtObject community: chatsModel.communities.activeCommunity property QtObject community: chatsModel.communities.activeCommunity
property bool isEdit: false property bool isEdit: false
@ -23,320 +28,313 @@ ModalPopup {
| Utils.Validate.TextHexColor | Utils.Validate.TextHexColor
id: popup id: popup
height: 600
onOpened: { onOpened: {
if (isEdit) { if (isEdit) {
nameInput.text = community.name; contentComponent.communityName.text = community.name;
descriptionTextArea.text = community.description; contentComponent.communityDescription.text = community.description;
colorPicker.defaultColor = community.communityColor; contentComponent.communityColor.color = community.communityColor;
contentComponent.communityColor.colorSelected = true
if (community.largeImage) { if (community.largeImage) {
addImageButton.selectedImage = community.largeImage contentComponent.communityImage.selectedImage = community.largeImage
} }
membershipRequirementSettingPopup.checkedMembership = community.access membershipRequirementSettingPopup.checkedMembership = community.access
} }
nameInput.forceActiveFocus(Qt.MouseFocusReason) contentComponent.communityName.forceActiveFocus(Qt.MouseFocusReason)
} }
onClosed: destroy() onClosed: destroy()
function isFormValid() { function isFormValid() {
return Utils.validateAndReturnError(nameInput.text, return Utils.validateAndReturnError(contentComponent.communityName.text,
communityNameValidator, communityNameValidator,
//% "community name" //% "community name"
qsTrId("community-name"), qsTrId("community-name"),
maxCommunityNameLength) === "" maxCommunityNameLength) === ""
&& Utils.validateAndReturnError(descriptionTextArea.text, && Utils.validateAndReturnError(contentComponent.communityDescription.text,
communityDescValidator, communityDescValidator,
//% "community decription" //% "community decription"
qsTrId("community-decription"), qsTrId("community-decription"),
maxCommunityDescLength) === "" maxCommunityDescLength) === ""
&& Utils.validateAndReturnError(colorPicker.text, && Utils.validateAndReturnError(contentComponent.communityColor.color.toString().toUpperCase(),
communityColorValidator) === "" communityColorValidator) === ""
} }
title: isEdit ? header.title: isEdit ?
//% "Edit community" //% "Edit community"
qsTrId("edit-community") : qsTrId("edit-community") :
//% "New community" //% "New community"
qsTrId("new-community") qsTrId("new-community")
ScrollView { content: ScrollView {
property ScrollBar vScrollBar: ScrollBar.vertical
id: scrollView id: scrollView
anchors.fill: parent
rightPadding: Style.current.padding property ScrollBar vScrollBar: ScrollBar.vertical
anchors.rightMargin: -Style.current.padding
anchors.topMargin: -Style.current.padding property alias communityName: nameInput
leftPadding: Style.current.padding property alias communityDescription: descriptionTextArea
topPadding: Style.current.padding property alias communityColor: colorDialog
anchors.leftMargin: -Style.current.padding property alias communityImage: addImageButton
property alias imageCropperModal: imageCropperModal
contentHeight: content.height contentHeight: content.height
bottomPadding: 8
height: Math.min(content.height, 432)
width: popup.width
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
clip: true clip: true
function scrollBackUp() { function scrollBackUp() {
vScrollBar.setPosition(0) vScrollBar.setPosition(0)
} }
Item { Column {
id: content id: content
height: childrenRect.height + 100 // Bottom padding width: popup.width
width: parent.width
Input { Item {
id: nameInput width: parent.width
//% "Name your community" height: 76
label: qsTrId("name-your-community") Input {
//% "A catchy name" id: nameInput
placeholderText: qsTrId("name-your-community-placeholder")
maxLength: maxCommunityNameLength
onTextEdited: { width: parent.width - 32
validationError = Utils.validateAndReturnError(text, anchors.centerIn: parent
communityNameValidator,
//% "community name"
qsTrId("community-name"),
maxCommunityNameLength)
}
}
StyledTextArea { //% "A catchy name"
id: descriptionTextArea placeholderText: qsTrId("name-your-community-placeholder")
//% "Give it a short description" maxLength: maxCommunityNameLength
label: qsTrId("give-a-short-description-community")
//% "What your community is about"
placeholderText: qsTrId("what-your-community-is-about")
anchors.top: nameInput.bottom onTextEdited: {
anchors.topMargin: Style.current.bigPadding validationError = Utils.validateAndReturnError(text,
customHeight: 88 communityNameValidator,
textField.wrapMode: TextEdit.Wrap qsTr("community name"),
maxCommunityNameLength)
onTextChanged: {
if(text.length > maxCommunityDescLength)
{
textField.remove(maxCommunityDescLength, text.length)
return
} }
validationError = Utils.validateAndReturnError(text,
communityDescValidator,
//% "community decription"
qsTrId("community-decription"),
maxCommunityDescLength)
} }
} }
StyledText { Item {
id: charLimit height: descriptionTextArea.height + 26
text: `${descriptionTextArea.text.length}/${maxCommunityDescLength}`
anchors.top: descriptionTextArea.bottom anchors.left: parent.left
anchors.topMargin: !descriptionTextArea.validationError ? 5 : - Style.current.smallPadding anchors.right: parent.right
anchors.right: descriptionTextArea.right anchors.leftMargin: 16
font.pixelSize: 12 anchors.rightMargin: 16
color: !descriptionTextArea.validationError ? Style.current.textColor : Style.current.danger
StyledTextArea {
id: descriptionTextArea
label: qsTr("Description")
//% "What your community is about"
placeholderText: qsTrId("what-your-community-is-about")
customHeight: 88
textField.wrapMode: TextEdit.Wrap
onTextChanged: {
if(text.length > maxCommunityDescLength)
{
textField.remove(maxCommunityDescLength, text.length)
return
}
validationError = Utils.validateAndReturnError(text,
communityDescValidator,
qsTr("community decription"),
maxCommunityDescLength)
}
}
StyledText {
id: charLimit
text: `${descriptionTextArea.text.length}/${maxCommunityDescLength}`
anchors.right: descriptionTextArea.right
font.pixelSize: 12
color: !descriptionTextArea.validationError ? Style.current.textColor : Style.current.danger
}
} }
StyledText { StatusBaseText {
id: thumbnailText id: thumbnailText
//% "Thumbnail image" //% "Thumbnail image"
text: qsTrId("thumbnail-image") text: qsTrId("thumbnail-image")
anchors.top: charLimit.bottom
anchors.topMargin: Style.current.smallPadding
font.pixelSize: 13 font.pixelSize: 13
color: Style.current.textColor color: Theme.palette.directColor1
font.weight: Font.Medium anchors.left: parent.left
anchors.leftMargin: 16
} }
Item {
Rectangle { width: parent.width
id: addImageButton height: addImageButton.height + 32
color: imagePreview.visible ? "transparent" : Style.current.inputBackground
width: 128
height: width
radius: width / 2
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: thumbnailText.bottom
anchors.topMargin: Style.current.padding
property string selectedImage: ""
FileDialog {
id: imageDialog
//% "Please choose an image"
title: qsTrId("please-choose-an-image")
folder: shortcuts.pictures
nameFilters: [
//% "Image files (*.jpg *.jpeg *.png)"
qsTrId("image-files----jpg---jpeg---png-")
]
onAccepted: {
addImageButton.selectedImage = imageDialog.fileUrls[0]
imageCropperModal.open()
}
}
Rectangle { Rectangle {
id: imagePreviewCropper id: addImageButton
clip: true color: imagePreview.visible ? "transparent" : Style.current.inputBackground
width: parent.width width: 128
height: parent.height
radius: parent.width / 2
visible: !!addImageButton.selectedImage
Image {
id: imagePreview
visible: !!addImageButton.selectedImage
source: addImageButton.selectedImage
fillMode: Image.PreserveAspectFit
width: parent.width
height: parent.height
}
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
anchors.centerIn: parent
width: imageCropperModal.width
height: imageCropperModal.height
radius: width / 2
}
}
}
Item {
id: addImageCenter
visible: !imagePreview.visible
width: uploadText.width
height: childrenRect.height
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
SVGImage {
id: imageImg
source: "../../../img/images_icon.svg"
width: 20
height: 18
anchors.horizontalCenter: parent.horizontalCenter
}
StyledText {
id: uploadText
//% "Upload"
text: qsTrId("upload")
anchors.top: imageImg.bottom
anchors.topMargin: 5
font.pixelSize: 15
color: Style.current.secondaryText
}
}
Rectangle {
color: Style.current.primary
width: 40
height: width height: width
radius: width / 2 radius: width / 2
anchors.top: parent.top anchors.centerIn: parent
anchors.right: parent.right property string selectedImage: ""
anchors.rightMargin: Style.current.halfPadding
SVGImage { FileDialog {
source: "../../../img/plusSign.svg" id: imageDialog
anchors.horizontalCenter: parent.horizontalCenter //% "Please choose an image"
anchors.verticalCenter: parent.verticalCenter title: qsTrId("please-choose-an-image")
width: 13 folder: shortcuts.pictures
height: 13 nameFilters: [
//% "Image files (*.jpg *.jpeg *.png)"
qsTrId("image-files----jpg---jpeg---png-")
]
onAccepted: {
addImageButton.selectedImage = imageDialog.fileUrls[0]
imageCropperModal.open()
}
} }
}
MouseArea { Rectangle {
anchors.fill: parent id: imagePreviewCropper
cursorShape: Qt.PointingHandCursor clip: true
onClicked: imageDialog.open() width: parent.width
} height: parent.height
radius: parent.width / 2
visible: !!addImageButton.selectedImage
ImageCropperModal { Image {
id: imageCropperModal id: imagePreview
selectedImage: addImageButton.selectedImage visible: !!addImageButton.selectedImage
ratio: "1:1" source: addImageButton.selectedImage
} fillMode: Image.PreserveAspectFit
width: parent.width
height: parent.height
}
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
anchors.centerIn: parent
width: imageCropperModal.width
height: imageCropperModal.height
radius: width / 2
}
}
}
} Item {
StyledText { id: addImageCenter
id: imageValidation visible: !imagePreview.visible
visible: text && text !== "" width: uploadText.width
anchors.top: addImageButton.bottom height: childrenRect.height
anchors.topMargin: Style.current.smallPadding anchors.centerIn: parent
anchors.left: parent.left
anchors.right: parent.right
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
color: Style.current.danger
}
Input { SVGImage {
property string defaultColor: Style.current.blue id: imageImg
source: "../../../img/images_icon.svg"
width: 20
height: 18
anchors.horizontalCenter: parent.horizontalCenter
}
id: colorPicker StyledText {
//% "Community color" id: uploadText
label: qsTrId("community-color") //% "Upload"
//% "Pick a color" text: qsTrId("upload")
placeholderText: qsTrId("pick-a-color") anchors.top: imageImg.bottom
anchors.top: imageValidation.bottom anchors.topMargin: 5
anchors.topMargin: Style.current.smallPadding font.pixelSize: 15
textField.text: defaultColor color: Style.current.secondaryText
textField.onReleased: colorDialog.open() }
}
onTextChanged: { StatusRoundButton {
validationError = Utils.validateAndReturnError(text, communityColorValidator) type: StatusRoundButton.Type.Secondary
} icon.name: "add"
anchors.top: parent.top
anchors.right: parent.right
anchors.rightMargin: Style.current.halfPadding
highlighted: sensor.containsMouse
}
StatusIconButton { MouseArea {
icon.name: "caret" id: sensor
iconRotation: -90 hoverEnabled: true
iconColor: Style.current.textColor anchors.fill: parent
icon.width: 13 cursorShape: Qt.PointingHandCursor
icon.height: 7 onClicked: imageDialog.open()
anchors.right: parent.right }
anchors.rightMargin: Style.current.smallPadding
anchors.top: parent.top
anchors.topMargin: colorPicker.textField.height / 2 - height / 2 + Style.current.bigPadding
onClicked: colorDialog.open()
}
ColorDialog { ImageCropperModal {
id: colorDialog id: imageCropperModal
//% "Please choose a color" selectedImage: addImageButton.selectedImage
title: qsTrId("please-choose-a-color") ratio: "1:1"
color: colorPicker.defaultColor
onAccepted: {
colorPicker.text = colorDialog.color
} }
} }
} }
Separator { StatusBaseText {
id: separator1 text: qsTr("Community colour")
anchors.top: colorPicker.bottom font.pixelSize: 13
anchors.topMargin: isEdit ? 0 : Style.current.bigPadding color: Theme.palette.directColor1
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: -Style.current.padding anchors.leftMargin: 16
anchors.right: parent.right }
anchors.rightMargin: -Style.current.padding
Item {
anchors.horizontalCenter: parent.horizontalCenter
height: colorSelectorButton.height + 16
width: parent.width - 32
StatusPickerButton {
id: colorSelectorButton
anchors.top: parent.top
anchors.topMargin: 10
property string validationError: ""
bgColor: colorDialog.colorSelected ?
colorDialog.color : Theme.palette.baseColor2
contentColor: colorDialog.colorSelected ? Theme.palette.indirectColor1 : Theme.palette.baseColor1
text: colorDialog.colorSelected ?
colorDialog.color.toString().toUpperCase() :
qsTr("Pick a color")
onClicked: colorDialog.open();
onTextChanged: {
if (colorDialog.colorSelected) {
validationError = Utils.validateAndReturnError(text, communityColorValidator)
}
}
ColorDialog {
id: colorDialog
property bool colorSelected: false
color: Theme.palette.primaryColor1
onAccepted: colorSelected = true
}
}
StatusBaseText {
text: colorSelectorButton.validationError
visible: !!text
color: Theme.palette.dangerColor1
anchors.top: colorSelectorButton.bottom
anchors.topMargin: 4
anchors.right: colorSelectorButton.right
}
}
StatusModalDivider {
topPadding: 8
bottomPadding: 8
visible: !isEdit visible: !isEdit
} }
StatusSettingsLineButton { StatusListItem {
id: membershipRequirementSetting anchors.horizontalCenter: parent.horizontalCenter
// TODO: remove 'isEnabled: false' when we no longer need to force visible: !isEdit
// "request access" membership title: qsTr("Membership requirement")
isEnabled: false label: {
anchors.top: separator1.bottom
anchors.topMargin: Style.current.halfPadding
//% "Membership requirement"
text: qsTrId("membership-title")
currentValue: {
switch (membershipRequirementSettingPopup.checkedMembership) { switch (membershipRequirementSettingPopup.checkedMembership) {
//% "Require invite from another member" //% "Require invite from another member"
case Constants.communityChatInvitationOnlyAccess: return qsTrId("membership-invite") case Constants.communityChatInvitationOnlyAccess: return qsTrId("membership-invite")
@ -346,23 +344,26 @@ ModalPopup {
default: return qsTrId("membership-free") default: return qsTrId("membership-free")
} }
} }
onClicked: { sensor.onClicked: membershipRequirementSettingPopup.open()
membershipRequirementSettingPopup.open() components: [
} StatusIcon {
icon: "chevron-down"
rotation: 270
color: Theme.palette.baseColor1
}
]
} }
StyledText { StatusBaseText {
visible: !isEdit visible: !isEdit
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
id: privateExplanation
anchors.top: membershipRequirementSetting.bottom
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
anchors.topMargin: isEdit ? 0 : Style.current.halfPadding
font.pixelSize: 13 font.pixelSize: 13
color: Style.current.secondaryText color: Theme.palette.baseColor1
width: parent.width * 0.78 width: parent.width * 0.78
//% "You can require new members to meet certain criteria before they can join. This can be changed at any time" text: qsTr("You can require new members to meet certain criteria before they can join. This can be changed at any time")
text: qsTrId("membership-none-placeholder") anchors.left: parent.left
anchors.leftMargin: 16
} }
// Feature commented temporarily // Feature commented temporarily
@ -410,23 +411,11 @@ ModalPopup {
*/ */
} }
MembershipRequirementPopup {
id: membershipRequirementSettingPopup
// TODO: remove the 'checkedMemership' setting when we no longer need
// to force "require approval" membership
checkedMembership: Constants.communityChatOnRequestAccess
}
} }
footer: Item { leftButtons: [
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: btnCreateEdit.height
StatusRoundButton { StatusRoundButton {
id: btnBack id: btnBack
anchors.left: parent.left
visible: isEdit visible: isEdit
icon.name: "arrow-right" icon.name: "arrow-right"
icon.width: 20 icon.width: 20
@ -434,6 +423,9 @@ ModalPopup {
rotation: 180 rotation: 180
onClicked: popup.destroy() onClicked: popup.destroy()
} }
]
rightButtons: [
StatusButton { StatusButton {
id: btnCreateEdit id: btnCreateEdit
enabled: isFormValid() enabled: isFormValid()
@ -442,38 +434,42 @@ ModalPopup {
qsTrId("Save") : qsTrId("Save") :
//% "Create" //% "Create"
qsTrId("create") qsTrId("create")
anchors.right: parent.right
onClicked: { onClicked: {
if (!isFormValid()) { if (!isFormValid()) {
scrollView.scrollBackUp() popup.contentComponent.scrollBackUp()
return return
} }
let error = false; let error = false;
if(isEdit) { if(isEdit) {
error = chatsModel.communities.editCommunity(community.id, error = chatsModel.communities.editCommunity(
Utils.filterXSS(nameInput.text), community.id,
Utils.filterXSS(descriptionTextArea.text), Utils.filterXSS(popup.contentComponent.communityName.text),
membershipRequirementSettingPopup.checkedMembership, Utils.filterXSS(popup.contentComponent.communityDescription.text),
false, membershipRequirementSettingPopup.checkedMembership,
colorPicker.text, false,
// to retain the existing image, pass "" for the image path popup.contentComponent.communityColor.color.toString().toUpperCase(),
addImageButton.selectedImage === community.largeImage ? "" : addImageButton.selectedImage, // to retain the existing image, pass "" for the image path
imageCropperModal.aX, popup.contentComponent.communityImage.selectedImage === community.largeImage ? "" :
imageCropperModal.aY, popup.contentComponent.communityImage.selectedImage,
imageCropperModal.bX, popup.contentComponent.imageCropperModal.aX,
imageCropperModal.bY) popup.contentComponent.imageCropperModal.aY,
popup.contentComponent.imageCropperModal.bX,
popup.contentComponent.imageCropperModal.bY
)
} else { } else {
error = chatsModel.communities.createCommunity(Utils.filterXSS(nameInput.text), error = chatsModel.communities.createCommunity(
Utils.filterXSS(descriptionTextArea.text), Utils.filterXSS(popup.contentComponent.communityName.text),
membershipRequirementSettingPopup.checkedMembership, Utils.filterXSS(popup.contentComponent.communityDescription.text),
false, // ensOnlySwitch.switchChecked, // TODO: membershipRequirementSettingPopup.checkedMembership,
colorPicker.text, false, // ensOnlySwitch.switchChecked, // TODO:
addImageButton.selectedImage, popup.contentComponent.communityColor.color.toString().toUpperCase(),
imageCropperModal.aX, popup.contentComponent.communityImage.selectedImage,
imageCropperModal.aY, popup.contentComponent.imageCropperModal.aX,
imageCropperModal.bX, popup.contentComponent.imageCropperModal.aY,
imageCropperModal.bY) popup.contentComponent.imageCropperModal.bX,
popup.contentComponent.imageCropperModal.bY
)
} }
if (error) { if (error) {
@ -483,15 +479,22 @@ ModalPopup {
popup.close() popup.close()
} }
MessageDialog {
id: creatingError
//% "Error creating the community"
title: qsTrId("error-creating-the-community")
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
}
} }
]
MessageDialog {
id: creatingError
//% "Error creating the community"
title: qsTrId("error-creating-the-community")
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
}
MembershipRequirementPopup {
id: membershipRequirementSettingPopup
// TODO: remove the 'checkedMemership' setting when we no longer need
// to force "require approval" membership
checkedMembership: Constants.communityChatOnRequestAccess
} }
} }

View File

@ -210,6 +210,7 @@ Item {
Component { Component {
id: createCommunitiesPopupComponent id: createCommunitiesPopupComponent
CreateCommunityPopup { CreateCommunityPopup {
anchors.centerIn: parent
onClosed: { onClosed: {
destroy() destroy()
} }

View File

@ -174,9 +174,8 @@ StatusAppLayout {
} }
StatusMenuItem { StatusMenuItem {
enabled: false//chatsModel.communities.observedCommunity.admin enabled: chatsModel.communities.observedCommunity.admin
//% "Edit Community" text: qsTr("Edit Community")
text: qsTrId("edit-community")
icon.name: "edit" icon.name: "edit"
onTriggered: openPopup(editCommunityPopup, {community: chatsModel.communities.observedCommunity}) onTriggered: openPopup(editCommunityPopup, {community: chatsModel.communities.observedCommunity})
} }