desktop(community-settings): add overview and members settings

this is first iteration for: #4938
This commit is contained in:
Patryk Osmaczko 2022-03-23 16:38:23 +01:00 committed by Iuri Matias
parent 4b22a709ba
commit 2850ee75ee
10 changed files with 1150 additions and 196 deletions

View File

@ -1,202 +1,50 @@
import QtQuick 2.13 import QtQuick 2.14
import QtQuick.Controls 2.13 import QtQuick.Controls 2.14
import Qt.labs.settings 1.0 import QtQuick.Layouts 1.14
import utils 1.0 import utils 1.0
import shared 1.0
import shared.panels 1.0
import shared.popups 1.0
import shared.status 1.0
import shared.views.chat 1.0
import StatusQ.Layout 0.1
import StatusQ.Popups 0.1
import "views" import "views"
import "panels"
import "panels/communities"
import "popups"
import "helpers"
import "controls"
import "stores" import "stores"
StatusAppThreePanelLayout { StackLayout {
id: root id: root
property var contactsStore property var contactsStore
property bool hasAddedContacts: root.contactsStore.myContactsModel.count > 0
// Not Refactored
property var messageStore
property RootStore rootStore: RootStore { property RootStore rootStore: RootStore {
contactsStore: root.contactsStore contactsStore: root.contactsStore
} }
property Component pinnedMessagesListPopupComponent property alias chatView: chatView
property var emojiPopup
property bool stickersLoaded: false
signal profileButtonClicked()
signal openAppSearch()
Connections { clip: true
target: root.rootStore.stickersStore.stickersModule
onStickerPacksLoaded: {
root.stickersLoaded = true;
}
}
Connections { ChatView {
target: root.rootStore.chatCommunitySectionModule id: chatView
onActiveItemChanged: {
root.rootStore.openCreateChat = false;
}
}
// property var onActivated: function () {
// root.rootStore.chatsModelInst.channelView.restorePreviousActiveChannel();
// chatColumn.onActivated();
// }
leftPanel: Loader {
id: contactColumnLoader
sourceComponent: root.rootStore.chatCommunitySectionModule.isCommunity()?
communtiyColumnComponent :
contactsColumnComponent
}
centerPanel: ChatColumnView {
id: chatColumn
parentModule: root.rootStore.chatCommunitySectionModule
rootStore: root.rootStore
contactsStore: root.contactsStore contactsStore: root.contactsStore
chatSectionModule: root.rootStore.chatCommunitySectionModule
pinnedMessagesPopupComponent: root.pinnedMessagesListPopupComponent
stickersLoaded: root.stickersLoaded
emojiPopup: root.emojiPopup
//chatGroupsListViewCount: contactColumnLoader.item.chatGroupsListViewCount
onOpenStickerPackPopup: {
Global.openPopup(statusStickerPackClickPopup, {packId: stickerPackId} )
}
onOpenAppSearch: {
root.openAppSearch();
}
}
showRightPanel: {
if (root.rootStore.openCreateChat ||
!localAccountSensitiveSettings.showOnlineUsers ||
!localAccountSensitiveSettings.expandUsersList) {
return false
}
let chatContentModule = root.rootStore.currentChatContentModule()
if (!chatContentModule
|| chatContentModule.chatDetails.type === Constants.chatType.publicChat)
{
// New communities have no chats, so no chatContentModule or it is a public chat
return false
}
// Check if user list is available as an option for particular chat content module
return chatContentModule.chatDetails.isUsersListAvailable
}
rightPanel: userListComponent
Component {
id: userListComponent
UserListPanel {
rootStore: root.rootStore rootStore: root.rootStore
label: localAccountSensitiveSettings.communitiesEnabled &&
root.rootStore.chatCommunitySectionModule.isCommunity() ? onCommunityInfoButtonClicked: root.currentIndex = 1
//% "Members" onCommunityManageButtonClicked: root.currentIndex = 1
qsTrId("members-label") :
qsTr("Last seen")
messageContextMenu: quickActionMessageOptionsMenu
usersModule: {
let chatContentModule = root.rootStore.currentChatContentModule()
if (!chatContentModule || !chatContentModule.usersModule) {
// New communities have no chats, so no chatContentModule
return {}
}
return chatContentModule.usersModule
}
}
} }
Component { Loader {
id: contactsColumnComponent active: root.rootStore.chatCommunitySectionModule.isCommunity()
ContactsColumnView {
chatSectionModule: root.rootStore.chatCommunitySectionModule
store: root.rootStore
contactsStore: root.contactsStore
emojiPopup: root.emojiPopup
onOpenProfileClicked: {
root.profileButtonClicked();
}
onOpenAppSearch: { sourceComponent: CommunitySettingsView {
root.openAppSearch() rootStore: root.rootStore
} chatCommunitySectionModule: root.rootStore.chatCommunitySectionModule
} community: root.rootStore.mainModuleInst ? root.rootStore.mainModuleInst.activeSection
} || {} : {}
Component { onBackToCommunityClicked: root.currentIndex = 0
id: communtiyColumnComponent
CommunityColumnView {
communitySectionModule: root.rootStore.chatCommunitySectionModule
store: root.rootStore
emojiPopup: root.emojiPopup
hasAddedContacts: root.hasAddedContacts
pinnedMessagesPopupComponent: root.pinnedMessagesListPopupComponent
}
}
Component { // TODO: remove me when migration to new settings is done
id: groupInfoPopupComponent onOpenLegacyPopupClicked: Global.openPopup(communityProfilePopup, {
GroupInfoPopup { "store": root.rootStore,
chatSectionModule: root.rootStore.chatCommunitySectionModule "community": community,
store: root.rootStore "communitySectionModule": chatCommunitySectionModule
pinnedMessagesPopupComponent: root.pinnedMessagesListPopupComponent })
}
}
Component {
id: statusStickerPackClickPopup
StatusStickerPackClickPopup{
store: root.rootStore
onClosed: {
destroy();
}
}
}
ConfirmationDialog {
id: removeContactConfirmationDialog
// % "Remove contact"
header.title: qsTrId("remove-contact")
//% "Are you sure you want to remove this contact?"
confirmationText: qsTrId("are-you-sure-you-want-to-remove-this-contact-")
onConfirmButtonClicked: {
let pk = chatColumn.contactToRemove
if (Utils.getContactDetailsAsJson(pk).isContact) {
root.contactsStore.removeContact(pk)
}
removeContactConfirmationDialog.parentPopup.close();
removeContactConfirmationDialog.close();
}
}
MessageContextMenuView {
id: quickActionMessageOptionsMenu
store: root.rootStore
onOpenProfileClicked: {
Global.openProfilePopup(publicKey)
}
onCreateOneToOneChat: {
Global.changeAppSectionBySectionType(Constants.appSection.chat)
root.rootStore.chatCommunitySectionModule.createOneToOneChat(communityId, chatId, ensName)
} }
} }
} }

View File

@ -0,0 +1,150 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtGraphicalEffects 1.13
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
Item {
id: root
// required
property string title
property Component content
// optional
property string previousPage
property bool dirty: false
readonly property Item contentItem: contentLoader.item
signal previousPageClicked
signal saveChangesClicked
signal resetChangesClicked
function reloadContent() {
contentLoader.active = false
contentLoader.active = true
}
function notifyDirty() {
toastAnimation.running = true
saveChangesButton.forceActiveFocus()
}
implicitWidth: layout.implicitWidth
implicitHeight: layout.implicitHeight
ColumnLayout {
id: layout
anchors.fill: parent
spacing: 16
Item {
implicitHeight: 32
StatusBaseText {
visible: root.previousPage
text: "<- " + root.previousPage
color: Theme.palette.primaryColor1
font.pixelSize: 15
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.previousPageClicked()
}
}
}
StatusBaseText {
Layout.leftMargin: 32
text: root.title
color: Theme.palette.directColor1
font.pixelSize: 26
font.bold: true
}
Loader {
id: contentLoader
Layout.fillWidth: true
Layout.fillHeight: true
sourceComponent: root.content
}
}
Rectangle {
id: toastMessage
anchors {
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
bottomMargin: 16
}
height: toastContent.height + 16
width: toastContent.width + 16
opacity: root.dirty ? 1 : 0
color: Theme.palette.statusToastMessage.backgroundColor
radius: 8
border.color: Theme.palette.dangerColor2
border.width: 2
layer.enabled: true
layer.effect: DropShadow {
verticalOffset: 3
radius: 8
samples: 15
fast: true
cached: true
color: Theme.palette.dangerColor2
spread: 0.1
}
NumberAnimation on border.width {
id: toastAnimation
from: 0
to: 4
loops: 2
duration: 600
onFinished: toastMessage.border.width = 2
}
RowLayout {
id: toastContent
x: 8
y: 8
StatusBaseText {
text: qsTr("Changes detected!")
color: Theme.palette.directColor1
}
StatusButton {
text: qsTr("Cancel")
type: StatusBaseButton.Type.Danger
onClicked: root.resetChangesClicked()
}
StatusButton {
id: saveChangesButton
text: qsTr("Save changes")
onClicked: root.saveChangesClicked()
}
}
Behavior on opacity {
NumberAnimation {}
}
}
}

View File

@ -0,0 +1,251 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
import QtQuick.Dialogs 1.3
import QtGraphicalEffects 1.13
import utils 1.0
import shared.panels 1.0
import shared.popups 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Layout 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
Flickable {
id: root
property alias name: nameInput.text
property alias description: descriptionTextInput.text
property alias color: colorDialog.color
property alias image: addImageButton.selectedImage
readonly property alias imageAx: imageCropperModal.aX
readonly property alias imageAy: imageCropperModal.aY
readonly property alias imageBx: imageCropperModal.bX
readonly property alias imageBy: imageCropperModal.bY
contentWidth: layout.width
contentHeight: layout.height
clip: true
interactive: contentHeight > height
flickableDirection: Flickable.VerticalFlick
ColumnLayout {
id: layout
width: root.width
spacing: 12
StatusInput {
id: nameInput
Layout.fillWidth: true
leftPadding: 0
rightPadding: 0
label: qsTr("Community name")
charLimit: 30
input.placeholderText: qsTr("A catchy name")
validators: [
StatusMinLengthValidator {
minLength: 1
errorMessage: Utils.getErrorMessage(nameInput.errors,
qsTr("community name"))
}
]
validationMode: StatusInput.ValidationMode.Always
Component.onCompleted: nameInput.input.forceActiveFocus(Qt.MouseFocusReason)
}
StatusInput {
id: descriptionTextInput
Layout.fillWidth: true
leftPadding: 0
rightPadding: 0
label: qsTr("Description")
charLimit: 140
input.placeholderText: qsTr("What your community is about")
input.multiline: true
input.implicitHeight: 88
validators: [
StatusMinLengthValidator {
minLength: 1
errorMessage: Utils.getErrorMessage(
descriptionTextInput.errors,
qsTr("community description"))
}
]
validationMode: StatusInput.ValidationMode.Always
}
ColumnLayout {
Layout.fillWidth: true
spacing: 8
StatusBaseText {
//% "Thumbnail image"
text: qsTrId("thumbnail-image")
font.pixelSize: 15
color: Theme.palette.directColor1
}
Item {
Layout.fillWidth: true
implicitHeight: addImageButton.height + 32
Rectangle {
id: addImageButton
property string selectedImage: ""
anchors.centerIn: parent
color: imagePreview.visible ? "transparent" : Style.current.inputBackground
width: 128
height: width
radius: width / 2
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 {
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.centerIn: parent
SVGImage {
id: imageImg
source: Style.svg("images_icon")
width: 20
height: 18
anchors.horizontalCenter: parent.horizontalCenter
}
StatusBaseText {
id: uploadText
//% "Upload"
text: qsTrId("upload")
anchors.top: imageImg.bottom
anchors.topMargin: 5
font.pixelSize: 15
color: Theme.palette.baseColor1
}
}
StatusRoundButton {
type: StatusRoundButton.Type.Secondary
icon.name: "add"
anchors.top: parent.top
anchors.right: parent.right
anchors.rightMargin: Style.current.halfPadding
highlighted: sensor.containsMouse
}
MouseArea {
id: sensor
hoverEnabled: true
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: imageDialog.open()
}
ImageCropperModal {
id: imageCropperModal
selectedImage: addImageButton.selectedImage
ratio: "1:1"
}
}
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 8
StatusBaseText {
text: qsTrId("Community colour")
font.pixelSize: 15
color: Theme.palette.directColor1
}
StatusPickerButton {
Layout.fillWidth: true
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,
Utils.Validate.NoEmpty | Utils.Validate.TextHexColor)
}
}
ColorDialog {
id: colorDialog
property bool colorSelected: true
color: Theme.palette.primaryColor1
onAccepted: colorSelected = true
}
}
}
Item {
Layout.fillHeight: true
}
}
}

View File

@ -0,0 +1,169 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Popups 0.1
import utils 1.0
import shared.controls.chat 1.0
import "../../layouts"
SettingsPageLayout {
id: root
property var membersModel
property bool editable: true
property int pendingRequests
signal membershipRequestsClicked()
signal userProfileClicked(string id)
signal kickUserClicked(string id)
signal banUserClicked(string id)
title: qsTr("Members")
content: ColumnLayout {
spacing: 8
StatusInput {
id: memberSearch
Layout.fillWidth: true
leftPadding: 0
rightPadding: 0
input.placeholderText: qsTr("Member name")
}
StatusContactRequestsIndicatorListItem {
id: memberRequestsButton
Layout.fillWidth: true
visible: root.editable && root.pendingRequests > 0
//% "Membership requests"
title: qsTrId("membership-requests")
requestsCount: root.pendingRequests
sensor.onClicked: root.membershipRequestsClicked()
}
Rectangle {
Layout.fillWidth: true
implicitHeight: 1
visible: memberRequestsButton.visible
color: Theme.palette.statusPopupMenu.separatorColor
}
StatusBaseText {
Layout.alignment: Qt.AlignHCenter
visible: memberList.count === 0
//% "Community members will appear here"
text: qsTrId("community-members-will-appear-here")
font.pixelSize: 15
color: Theme.palette.baseColor1
}
StatusBaseText {
Layout.alignment: Qt.AlignHCenter
visible: !!memberSearch.input.text && memberList.height == 0
//% "No contacts found"
text: qsTrId("no-contacts-found")
font.pixelSize: 15
color: Theme.palette.baseColor1
}
ListView {
id: memberList
Layout.fillWidth: true
Layout.fillHeight: true
model: root.membersModel
clip: true
// TODO: use StatusMemberListItem (it does not behave correctly right now)
delegate: StatusListItem {
id: memberItem
readonly property bool itsMe: model.id.toLowerCase() === userProfile.pubKey.toLowerCase()
readonly property bool isOnline: model.onlineStatus === Constants.userStatus.online
width: memberList.width
// FIXME: use QSortFilterProxyModel instead
visible: memberSearch.input.text === "" || title.toLowerCase().includes(memberSearch.input.text.toLowerCase())
height: visible ? implicitHeight : 0
title: {
if (memberItem.itsMe) {
//% "You"
return qsTrId("You")
}
return !model.name.endsWith(".eth") ? model.name : Utils.removeStatusEns(model.name)
}
subTitle: model.id.substring(0, 5) + "..." + model.id.substring(model.id.length - 3)
statusListItemIcon {
name: model.name
badge {
visible: true
color: memberItem.isOnline ? Theme.palette.successColor1 : Theme.palette.baseColor1
}
}
image {
width: 40
height: 40
source: model.icon
isIdenticon: false
}
icon {
width: 40
height: 40
color: Style.current.background
textColor: Style.current.secondaryText
letterSize: Math.max(4, root.imageWidth / 2.4)
charactersLen: 2
isLetterIdenticon: true
}
ringSettings {
ringSpecModel: model.isAdded ? "" : Utils.getColorHashAsJson(model.id)
ringPxSize: Math.max(icon.width / 24.0)
}
onClicked: root.userProfileClicked(model.id)
components: [
StatusButton {
visible: root.editable && !memberItem.itsMe
text: qsTr("Ban")
type: StatusBaseButton.Type.Danger
size: StatusBaseButton.Size.Tiny
onClicked: root.banUserClicked(model.id)
},
StatusButton {
visible: root.editable && !memberItem.itsMe
text: qsTr("Kick")
type: StatusBaseButton.Type.Danger
size: StatusBaseButton.Size.Tiny
onClicked: root.kickUserClicked(model.id)
}
]
}
}
}
}

View File

@ -0,0 +1,155 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import "../../layouts"
StackLayout {
id: root
property string name
property string description
property string image
property color color
property bool editable: false
property bool owned: false
signal edited(Item item) // item containing edited fields (name, description, image, color)
clip: true
SettingsPageLayout {
title: qsTr("Overview")
content: ColumnLayout {
spacing: 16
RowLayout {
Layout.fillWidth: true
spacing: 16
StatusSmartIdenticon {
name: root.name
icon {
width: 80
height: 80
isLetterIdenticon: !root.image
color: root.color
letterSize: width / 2.4
}
image {
width: 80
height: 80
source: root.image
}
}
ColumnLayout {
Layout.fillWidth: true
StatusBaseText {
id: nameText
Layout.fillWidth: true
font.pixelSize: 24
color: Theme.palette.directColor1
wrapMode: Text.WordWrap
text: root.name
}
StatusBaseText {
id: descriptionText
Layout.fillWidth: true
font.pixelSize: 15
color: Theme.palette.directColor1
wrapMode: Text.WordWrap
text: root.description
}
}
StatusButton {
visible: root.editable
text: qsTr("Edit Community")
onClicked: root.currentIndex = 1
}
}
Rectangle {
Layout.fillWidth: true
implicitHeight: 1
visible: root.editable
color: Theme.palette.statusPopupMenu.separatorColor
}
RowLayout {
Layout.fillWidth: true
visible: root.owned
StatusIcon {
icon: "info"
}
StatusBaseText {
Layout.fillWidth: true
text: qsTr("This node is the Community Owner Node. For your Community to function correctly try to keep this computer with Status running and onlinie as much as possible.")
font.pixelSize: 15
color: Theme.palette.directColor1
wrapMode: Text.WordWrap
}
}
Item {
Layout.fillHeight: true
}
}
}
SettingsPageLayout {
id: editCommunityPage
previousPage: qsTr("Overview")
title: qsTr("Edit Community")
content: CommunityEditSettingsPanel {
name: root.name
description: root.description
color: root.color
image: root.image
Component.onCompleted: {
editCommunityPage.dirty =
Qt.binding(() => {
return root.name != name ||
root.description != description ||
root.image != image ||
root.color != color
})
}
}
onPreviousPageClicked: {
if (dirty) {
notifyDirty()
} else {
root.currentIndex = 0
}
}
onSaveChangesClicked: {
root.currentIndex = 0
root.edited(contentItem)
reloadContent()
}
onResetChangesClicked: reloadContent()
}
}

View File

@ -26,6 +26,8 @@ Rectangle {
property var communitySectionModule property var communitySectionModule
property bool hasAddedContacts property bool hasAddedContacts
signal manageCommunityClicked()
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
@ -104,10 +106,7 @@ Rectangle {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: Style.current.padding anchors.bottomMargin: Style.current.padding
onClicked: Global.openPopup(communityProfilePopup, {
store: rootStore, onClicked: root.manageCommunityClicked()
community: root.activeCommunity,
communitySectionModule: root.communitySectionModule
})
} }
} }

View File

@ -0,0 +1,199 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import Qt.labs.settings 1.0
import utils 1.0
import shared 1.0
import shared.panels 1.0
import shared.popups 1.0
import shared.status 1.0
import shared.views.chat 1.0
import StatusQ.Layout 0.1
import StatusQ.Popups 0.1
import "."
import "../panels"
import "../panels/communities"
import "../popups"
import "../helpers"
import "../controls"
import "../stores"
StatusAppThreePanelLayout {
id: root
property var contactsStore
property bool hasAddedContacts: root.contactsStore.myContactsModel.count > 0
// Not Refactored
property var messageStore
property RootStore rootStore
property Component pinnedMessagesListPopupComponent
property var emojiPopup
property bool stickersLoaded: false
signal communityInfoButtonClicked()
signal communityManageButtonClicked()
signal profileButtonClicked()
signal openAppSearch()
Connections {
target: root.rootStore.stickersStore.stickersModule
onStickerPacksLoaded: {
root.stickersLoaded = true;
}
}
Connections {
target: root.rootStore.chatCommunitySectionModule
onActiveItemChanged: {
root.rootStore.openCreateChat = false;
}
}
leftPanel: Loader {
id: contactColumnLoader
sourceComponent: root.rootStore.chatCommunitySectionModule.isCommunity()?
communtiyColumnComponent :
contactsColumnComponent
}
centerPanel: ChatColumnView {
id: chatColumn
parentModule: root.rootStore.chatCommunitySectionModule
rootStore: root.rootStore
contactsStore: root.contactsStore
chatSectionModule: root.rootStore.chatCommunitySectionModule
pinnedMessagesPopupComponent: root.pinnedMessagesListPopupComponent
stickersLoaded: root.stickersLoaded
emojiPopup: root.emojiPopup
onOpenStickerPackPopup: {
Global.openPopup(statusStickerPackClickPopup, {packId: stickerPackId} )
}
onOpenAppSearch: {
root.openAppSearch();
}
}
showRightPanel: {
if (root.rootStore.openCreateChat ||
!localAccountSensitiveSettings.showOnlineUsers ||
!localAccountSensitiveSettings.expandUsersList) {
return false
}
let chatContentModule = root.rootStore.currentChatContentModule()
if (!chatContentModule
|| chatContentModule.chatDetails.type === Constants.chatType.publicChat)
{
// New communities have no chats, so no chatContentModule or it is a public chat
return false
}
// Check if user list is available as an option for particular chat content module
return chatContentModule.chatDetails.isUsersListAvailable
}
rightPanel: userListComponent
Component {
id: userListComponent
UserListPanel {
rootStore: root.rootStore
label: localAccountSensitiveSettings.communitiesEnabled &&
root.rootStore.chatCommunitySectionModule.isCommunity() ?
//% "Members"
qsTrId("members-label") :
qsTr("Last seen")
messageContextMenu: quickActionMessageOptionsMenu
usersModule: {
let chatContentModule = root.rootStore.currentChatContentModule()
if (!chatContentModule || !chatContentModule.usersModule) {
// New communities have no chats, so no chatContentModule
return {}
}
return chatContentModule.usersModule
}
}
}
Component {
id: contactsColumnComponent
ContactsColumnView {
chatSectionModule: root.rootStore.chatCommunitySectionModule
store: root.rootStore
contactsStore: root.contactsStore
emojiPopup: root.emojiPopup
onOpenProfileClicked: {
root.profileButtonClicked();
}
onOpenAppSearch: {
root.openAppSearch()
}
}
}
Component {
id: communtiyColumnComponent
CommunityColumnView {
communitySectionModule: root.rootStore.chatCommunitySectionModule
store: root.rootStore
emojiPopup: root.emojiPopup
hasAddedContacts: root.hasAddedContacts
pinnedMessagesPopupComponent: root.pinnedMessagesListPopupComponent
onInfoButtonClicked: root.communityInfoButtonClicked()
onManageButtonClicked: root.communityManageButtonClicked()
}
}
Component {
id: groupInfoPopupComponent
GroupInfoPopup {
chatSectionModule: root.rootStore.chatCommunitySectionModule
store: root.rootStore
pinnedMessagesPopupComponent: root.pinnedMessagesListPopupComponent
}
}
Component {
id: statusStickerPackClickPopup
StatusStickerPackClickPopup{
store: root.rootStore
onClosed: {
destroy();
}
}
}
ConfirmationDialog {
id: removeContactConfirmationDialog
// % "Remove contact"
header.title: qsTrId("remove-contact")
//% "Are you sure you want to remove this contact?"
confirmationText: qsTrId("are-you-sure-you-want-to-remove-this-contact-")
onConfirmButtonClicked: {
let pk = chatColumn.contactToRemove
if (Utils.getContactDetailsAsJson(pk).isContact) {
root.contactsStore.removeContact(pk)
}
removeContactConfirmationDialog.parentPopup.close();
removeContactConfirmationDialog.close();
}
}
MessageContextMenuView {
id: quickActionMessageOptionsMenu
store: root.rootStore
onOpenProfileClicked: {
Global.openProfilePopup(publicKey)
}
onCreateOneToOneChat: {
Global.changeAppSectionBySectionType(Constants.appSection.chat)
root.rootStore.chatCommunitySectionModule.createOneToOneChat(communityId, chatId, ensName)
}
}
}

View File

@ -34,6 +34,9 @@ Item {
//property int chatGroupsListViewCount: communityChatListAndCategories.chatList.count //property int chatGroupsListViewCount: communityChatListAndCategories.chatList.count
property Component pinnedMessagesPopupComponent property Component pinnedMessagesPopupComponent
signal infoButtonClicked
signal manageButtonClicked
StatusChatInfoToolBar { StatusChatInfoToolBar {
id: communityHeader id: communityHeader
anchors.top: parent.top anchors.top: parent.top
@ -49,11 +52,7 @@ Item {
chatInfoButton.image.source: communityData.image chatInfoButton.image.source: communityData.image
chatInfoButton.icon.color: communityData.color chatInfoButton.icon.color: communityData.color
menuButton.visible: communityData.amISectionAdmin && communityData.canManageUsers menuButton.visible: communityData.amISectionAdmin && communityData.canManageUsers
chatInfoButton.onClicked: Global.openPopup(communityProfilePopup, { chatInfoButton.onClicked: root.infoButtonClicked()
store: root.store,
community: communityData,
communitySectionModule: root.communitySectionModule
})
popupMenu: StatusPopupMenu { popupMenu: StatusPopupMenu {
StatusMenuItem { StatusMenuItem {
@ -360,6 +359,7 @@ Item {
store: root.store store: root.store
hasAddedContacts: root.hasAddedContacts hasAddedContacts: root.hasAddedContacts
communitySectionModule: root.communitySectionModule communitySectionModule: root.communitySectionModule
onManageCommunityClicked: root.manageButtonClicked()
} }
} }
} }

View File

@ -0,0 +1,183 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
import QtQuick.Dialogs 1.3
import QtGraphicalEffects 1.13
import utils 1.0
import shared.panels 1.0
import shared.popups 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Layout 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
import "../panels/communities"
import "../layouts"
StatusAppTwoPanelLayout {
id: root
// TODO: get this model from backend?
property var model: [{name: qsTr("Overview"), icon: "help"},
{name: qsTr("Members"), icon: "group-chat"},
// {name: qsTr("Permissions"), icon: "objects"},
// {name: qsTr("Tokens"), icon: "token"},
// {name: qsTr("Airdrops"), icon: "airdrop"},
// {name: qsTr("Token sales"), icon: "token-sale"},
// {name: qsTr("Subscriptions"), icon: "subscription"},
{name: qsTr("Notifications"), icon: "notification"}]
property var rootStore
property var community
property var chatCommunitySectionModule
signal backToCommunityClicked
signal openLegacyPopupClicked // TODO: remove me when migration to new settings is done
leftPanel: ColumnLayout {
anchors {
fill: parent
margins: 8
topMargin: 16
bottomMargin: 16
}
spacing: 16
StatusNavigationPanelHeadline {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Settings")
}
ListView {
id: listView
Layout.fillWidth: true
implicitHeight: contentItem.childrenRect.height
model: root.model
delegate: StatusNavigationListItem {
width: listView.width
title: modelData.name
icon.name: modelData.icon
selected: d.currentIndex == index
onClicked: d.currentIndex = index
}
}
Item {
Layout.fillHeight: true
}
// TODO: remove me when migration to new settings is done
StatusBaseText {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Open legacy popup (to be removed)")
color: Theme.palette.baseColor1
font.pixelSize: 10
font.underline: true
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.openLegacyPopupClicked()
}
}
StatusBaseText {
Layout.alignment: Qt.AlignHCenter
text: "<- " + qsTr("Back to community")
color: Theme.palette.baseColor1
font.pixelSize: 15
font.underline: true
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.backToCommunityClicked()
hoverEnabled: true
}
}
}
rightPanel: Loader {
anchors.fill: parent
anchors.margins: 16
active: root.community
sourceComponent: StackLayout {
currentIndex: d.currentIndex
CommunityOverviewSettingsPanel {
name: root.community.name
description: root.community.description
image: root.community.image
color: root.community.color
editable: root.community.amISectionAdmin
onEdited: {
root.chatCommunitySectionModule.editCommunity(
Utils.filterXSS(item.name),
Utils.filterXSS(item.description),
root.community.access,
false, // FIXME
item.color.toString().toUpperCase(),
item.image === root.community.image ? "" : item.image,
item.imageAx,
item.imageAy,
item.imageBx,
item.imageBy,
)
}
}
CommunityMembersSettingsPanel {
membersModel: root.community.members
editable: root.community.amISectionAdmin
pendingRequests: root.community.pendingRequestsToJoin ? root.community.pendingRequestsToJoin.count : 0
onUserProfileClicked: Global.openProfilePopup(id)
onKickUserClicked: root.rootStore.removeUserFromCommunity(id)
onBanUserClicked: console.debug("NOT IMPLEMENTED") // TODO: implement me
onMembershipRequestsClicked: Global.openPopup(membershipRequestPopup, {
communitySectionModule: root.chatCommunitySectionModule
})
}
SettingsPageLayout {
title: qsTr("Notifications")
content: ColumnLayout {
StatusListItem {
Layout.fillWidth: true
title: qsTr("Enabled")
icon.name: "notification"
sensor.cursorShape: Qt.ArrowCursor
components: [
StatusSwitch {
checked: !root.community.muted
onClicked: root.chatCommunitySectionModule.setCommunityMuted(!checked)
}
]
}
Item {
Layout.fillHeight: true
}
}
}
}
}
QtObject {
id: d
property int currentIndex: 0
}
}

View File

@ -511,17 +511,17 @@ Item {
Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillHeight: true Layout.fillHeight: true
pinnedMessagesListPopupComponent: pinnedMessagesPopupComponent chatView.pinnedMessagesListPopupComponent: pinnedMessagesPopupComponent
emojiPopup: statusEmojiPopup chatView.emojiPopup: statusEmojiPopup
contactsStore: appMain.rootStore.contactStore contactsStore: appMain.rootStore.contactStore
rootStore.emojiReactionsModel: appMain.rootStore.emojiReactionsModel rootStore.emojiReactionsModel: appMain.rootStore.emojiReactionsModel
onProfileButtonClicked: { chatView.onProfileButtonClicked: {
Global.changeAppSectionBySectionType(Constants.appSection.profile); Global.changeAppSectionBySectionType(Constants.appSection.profile);
} }
onOpenAppSearch: { chatView.onOpenAppSearch: {
appSearch.openSearchPopup() appSearch.openSearchPopup()
} }
@ -601,17 +601,17 @@ Item {
Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillHeight: true Layout.fillHeight: true
pinnedMessagesListPopupComponent: pinnedMessagesPopupComponent chatView.pinnedMessagesListPopupComponent: pinnedMessagesPopupComponent
emojiPopup: statusEmojiPopup chatView.emojiPopup: statusEmojiPopup
contactsStore: appMain.rootStore.contactStore contactsStore: appMain.rootStore.contactStore
rootStore.emojiReactionsModel: appMain.rootStore.emojiReactionsModel rootStore.emojiReactionsModel: appMain.rootStore.emojiReactionsModel
onProfileButtonClicked: { chatView.onProfileButtonClicked: {
Global.changeAppSectionBySectionType(Constants.appSection.profile); Global.changeAppSectionBySectionType(Constants.appSection.profile);
} }
onOpenAppSearch: { chatView.onOpenAppSearch: {
appSearch.openSearchPopup() appSearch.openSearchPopup()
} }