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

View File

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