status-desktop/ui/app/AppLayouts/Chat/views/CommunityColumnView.qml

620 lines
22 KiB
QML
Raw Normal View History

2020-12-11 20:29:46 +00:00
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Dialogs 1.2
import QtGraphicalEffects 1.13
2020-12-11 20:29:46 +00:00
import QtQuick.Layouts 1.13
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 1.0
import shared.popups 1.0
import shared.status 1.0
import "../popups/community"
import "../panels/communities"
// FIXME: Rework me to use ColumnLayout instead of anchors!!
Item {
id: root
objectName: "communityColumnView"
width: 304
height: parent.height
// Important:
// We're here in case of CommunitySection
// This module is set from `ChatLayout` (each `ChatLayout` has its own communitySectionModule)
property var communitySectionModule
property var emojiPopup
property var store
property bool hasAddedContacts: false
property var communityData: store.mainModuleInst ? store.mainModuleInst.activeSection || {} : {}
property Component membershipRequestPopup
2020-12-11 20:29:46 +00:00
signal infoButtonClicked
signal manageButtonClicked
MouseArea {
enabled: communityData.amISectionAdmin
anchors.fill: parent
z: 0
acceptedButtons: Qt.RightButton
onClicked: {
adminPopupMenu.showInviteButton = true
adminPopupMenu.x = mouse.x + 4
adminPopupMenu.y = mouse.y + 4
adminPopupMenu.open()
}
}
StatusChatInfoButton {
2020-12-11 20:29:46 +00:00
id: communityHeader
objectName: "communityHeaderButton"
readonly property int nbMembers: communityData.members.count
title: communityData.name
subTitle: qsTr("%n member(s)", "", nbMembers)
asset.name: communityData.image
asset.color: communityData.color
asset.isImage: true
onClicked: if (communityData.amISectionAdmin) { root.infoButtonClicked() }
anchors.top: parent.top
anchors.topMargin: Style.current.smallPadding
anchors.left: parent.left
anchors.leftMargin: Style.current.halfPadding
anchors.right: (implicitWidth > parent.width - 50) ? adHocChatButton.left : undefined
anchors.rightMargin: Style.current.halfPadding
type: StatusChatInfoButton.Type.OneToOneChat
hoverEnabled: communityData.amISectionAdmin
}
StatusIconTabButton {
id: adHocChatButton
icon.name: "edit"
objectName: "startChatButton"
icon.color: Theme.palette.directColor1
anchors.verticalCenter: communityHeader.verticalCenter
anchors.right: parent.right
anchors.rightMargin: Style.current.padding
checked: root.store.openCreateChat
highlighted: root.store.openCreateChat
onClicked: {
if (root.store.openCreateChat) {
Global.closeCreateChatView()
} else {
Global.openCreateChatView()
}
}
StatusToolTip {
text: qsTr("Start chat")
visible: parent.hovered
orientation: StatusToolTip.Orientation.Bottom
y: parent.height + 12
}
}
StatusButton {
id: joinCommunityButton
property bool invitationPending: root.store.isCommunityRequestPending(communityData.id)
anchors.top: communityHeader.bottom
anchors.topMargin: 8
anchors.bottomMargin: Style.current.halfPadding
anchors.horizontalCenter: parent.horizontalCenter
visible: !communityData.joined
text: {
if (invitationPending) return qsTr("Membership request pending...")
return root.communityData.access === Constants.communityChatOnRequestAccess ?
qsTr("Request to join") : qsTr("Join Community")
}
onClicked: {
communityIntroDialog.open()
}
Connections {
target: root.store.communitiesModuleInst
onCommunityAccessRequested: function (communityId) {
if (communityId === communityData.id) {
joinCommunityButton.invitationPending = root.store.isCommunityRequestPending(communityData.id)
}
}
}
CommunityIntroDialog {
id: communityIntroDialog
isInvitationPending: joinCommunityButton.invitationPending
name: communityData.name
introMessage: communityData.introMessage
imageSrc: communityData.image
accessType: communityData.access
onJoined: root.store.requestToJoinCommunity(communityData.id, root.store.userProfileInst.name)
onCancelMembershipRequest: {
root.store.cancelPendingRequest(communityData.id)
joinCommunityButton.invitationPending = root.store.isCommunityRequestPending(communityData.id)
}
}
}
Loader {
id: membershipRequests
property int nbRequests: root.communityData.pendingRequestsToJoin.count || 0
anchors.top: joinCommunityButton.visible ? joinCommunityButton.bottom : communityHeader.bottom
anchors.topMargin: active ? 8 : 0
anchors.horizontalCenter: parent.horizontalCenter
active: communityData.amISectionAdmin && nbRequests > 0
height: nbRequests > 0 ? 64 : 0
sourceComponent: Component {
StatusContactRequestsIndicatorListItem {
title: qsTr("Membership requests")
requestsCount: membershipRequests.nbRequests
onClicked: Global.openPopup(root.membershipRequestPopup, {
communitySectionModule: root.communitySectionModule
})
}
}
}
2022-12-01 16:58:37 +00:00
StatusMenu {
id: adminPopupMenu
enabled: communityData.amISectionAdmin
property bool showInviteButton: false
onClosed: adminPopupMenu.showInviteButton = false
2022-12-01 16:58:37 +00:00
StatusAction {
objectName: "createCommunityChannelBtn"
text: qsTr("Create channel")
icon.name: "channel"
onTriggered: Global.openPopup(createChannelPopup)
}
2022-12-01 16:58:37 +00:00
StatusAction {
objectName: "createCommunityCategoryBtn"
text: qsTr("Create category")
icon.name: "channel-category"
onTriggered: Global.openPopup(createCategoryPopup)
}
StatusMenuSeparator {
visible: invitePeopleBtn.enabled
}
2022-12-01 16:58:37 +00:00
StatusAction {
id: invitePeopleBtn
text: qsTr("Invite people")
icon.name: "share-ios"
enabled: communityData.canManageUsers && adminPopupMenu.showInviteButton
onTriggered: {
Global.openInviteFriendsToCommunityPopup(root.communityData,
root.communitySectionModule,
null)
}
}
}
StatusScrollView {
id: scrollView
anchors.top: membershipRequests.bottom
anchors.topMargin: Style.current.padding
anchors.bottom: createChatOrCommunity.top
anchors.horizontalCenter: parent.horizontalCenter
topPadding: Style.current.padding
width: parent.width
2020-12-11 20:29:46 +00:00
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
contentHeight: communityChatListAndCategories.height
+ bannerColumn.height
+ Style.current.bigPadding
StatusChatListAndCategories {
id: communityChatListAndCategories
anchors.horizontalCenter: parent.horizontalCenter
draggableItems: communityData.amISectionAdmin
draggableCategories: communityData.amISectionAdmin
model: root.communitySectionModule.model
highlightItem: !root.store.openCreateChat
onChatItemSelected: {
Global.closeCreateChatView()
if(categoryId === "")
root.communitySectionModule.setActiveItem(id, "")
else
root.communitySectionModule.setActiveItem(categoryId, id)
}
showCategoryActionButtons: communityData.amISectionAdmin
showPopupMenu: communityData.amISectionAdmin && communityData.canManageUsers
onChatItemUnmuted: root.communitySectionModule.unmuteChat(id)
2022-02-01 22:41:45 +00:00
onChatItemReordered: function(categoryId, chatId, from, to){
root.store.reorderCommunityChat(categoryId, chatId, to)
}
2022-01-25 14:51:38 +00:00
onChatListCategoryReordered: root.store.reorderCommunityCategories(categoryId, to)
onCategoryAddButtonClicked: Global.openPopup(createChannelPopup, {
categoryId: id
})
2022-12-01 16:58:37 +00:00
popupMenu: StatusMenu {
StatusAction {
text: qsTr("Create channel")
icon.name: "channel"
enabled: communityData.amISectionAdmin
onTriggered: Global.openPopup(createChannelPopup)
}
2022-12-01 16:58:37 +00:00
StatusAction {
text: qsTr("Create category")
icon.name: "channel-category"
enabled: communityData.amISectionAdmin
2022-01-28 00:02:06 +00:00
onTriggered: Global.openPopup(createCategoryPopup)
}
2020-12-11 20:29:46 +00:00
StatusMenuSeparator {}
2022-12-01 16:58:37 +00:00
StatusAction {
text: qsTr("Invite people")
icon.name: "share-ios"
enabled: communityData.canManageUsers
onTriggered: {
Global.openInviteFriendsToCommunityPopup(root.communityData,
root.communitySectionModule,
null)
}
}
}
2022-12-01 16:58:37 +00:00
categoryPopupMenu: StatusMenu {
property var categoryItem
openHandler: function (id) {
let jsonObj = root.communitySectionModule.getItemAsJson(id)
let obj = JSON.parse(jsonObj)
if (obj.error) {
console.error("error parsing chat item json object, id: ", id, " error: ", obj.error)
close()
return
}
categoryItem = obj
}
2022-12-01 16:58:37 +00:00
StatusAction {
text: categoryItem.muted ? qsTr("Unmute category") : qsTr("Mute category")
icon.name: "notification"
onTriggered: {
if (categoryItem.muted) {
root.communitySectionModule.unmuteCategory(categoryItem.itemId)
} else {
root.communitySectionModule.muteCategory(categoryItem.itemId)
}
}
}
2022-12-01 16:58:37 +00:00
StatusAction {
objectName: "editCategoryMenuItem"
enabled: communityData.amISectionAdmin
text: qsTr("Edit Category")
icon.name: "edit"
onTriggered: {
Global.openPopup(createCategoryPopup, {
isEdit: true,
2022-02-01 15:31:05 +00:00
channels: [],
categoryId: categoryItem.categoryId,
categoryName: categoryItem.name
})
}
}
StatusMenuSeparator {
visible: communityData.amISectionAdmin
}
2022-12-01 16:58:37 +00:00
StatusAction {
objectName: "deleteCategoryMenuItem"
enabled: communityData.amISectionAdmin
text: qsTr("Delete Category")
icon.name: "delete"
2022-12-01 16:58:37 +00:00
type: StatusAction.Type.Danger
onTriggered: {
Global.openPopup(deleteCategoryConfirmationDialogComponent, {
title: qsTr("Delete %1 category").arg(categoryItem.name),
confirmationText: qsTr("Are you sure you want to delete %1 category? Channels inside the category wont be deleted.")
.arg(categoryItem.name),
categoryId: categoryItem.categoryId
})
}
}
}
chatListPopupMenu: ChatContextMenuView {
id: chatContextMenuView
emojiPopup: root.emojiPopup
openHandler: function (id) {
let jsonObj = root.communitySectionModule.getItemAsJson(id)
let obj = JSON.parse(jsonObj)
if (obj.error) {
console.error("error parsing chat item json object, id: ", id, " error: ", obj.error)
close()
return
}
currentFleet = root.communitySectionModule.getCurrentFleet()
isCommunityChat = root.communitySectionModule.isCommunity()
amIChatAdmin = obj.amIChatAdmin
chatId = obj.itemId
chatName = obj.name
chatDescription = obj.description
chatIcon = obj.icon
chatEmoji = obj.emoji
chatColor = obj.color
chatType = obj.type
chatMuted = obj.muted
channelPosition = obj.position
chatCategoryId = obj.categoryId
}
onMuteChat: {
root.communitySectionModule.muteChat(chatId)
}
onUnmuteChat: {
root.communitySectionModule.unmuteChat(chatId)
}
2021-12-08 11:10:34 +00:00
onMarkAllMessagesRead: {
root.communitySectionModule.markAllMessagesRead(chatId)
2021-12-08 11:10:34 +00:00
}
2021-12-08 13:06:31 +00:00
onClearChatHistory: {
root.communitySectionModule.clearChatHistory(chatId)
}
onRequestAllHistoricMessages: {
// Not Refactored Yet - Check in the `master` branch if this is applicable here.
}
onLeaveChat: {
root.communitySectionModule.leaveChat(chatId)
}
onDeleteCommunityChat: root.store.removeCommunityChat(chatId)
onDownloadMessages: {
root.communitySectionModule.downloadMessages(chatId, file)
}
onDisplayProfilePopup: {
Global.openProfilePopup(publicKey)
}
onEditCommunityChannel: {
communitySectionModule.editCommunityChannel(
chatId,
newName,
newDescription,
newEmoji,
newColor,
newCategory,
channelPosition // TODO change this to the signal once it is modifiable
)
}
}
2021-05-16 15:16:42 +00:00
}
Column {
id: bannerColumn
width: scrollView.availableWidth
anchors.top: communityChatListAndCategories.bottom
anchors.topMargin: Style.current.padding
spacing: Style.current.bigPadding
Loader {
active: communityData.amISectionAdmin &&
(!localAccountSensitiveSettings.hiddenCommunityWelcomeBanners ||
!localAccountSensitiveSettings.hiddenCommunityWelcomeBanners.includes(communityData.id))
width: parent.width
height: {
// I dont know why, the binding doesn't work well if this isn't here
item && item.height
return active ? item.height : 0
}
sourceComponent: Component {
CommunityWelcomeBannerPanel {
activeCommunity: communityData
store: root.store
hasAddedContacts: root.hasAddedContacts
communitySectionModule: root.communitySectionModule
onManageCommunityClicked: root.manageButtonClicked()
}
}
} // Loader
Loader {
active: communityData.amISectionAdmin &&
(!localAccountSensitiveSettings.hiddenCommunityChannelAndCategoriesBanners ||
!localAccountSensitiveSettings.hiddenCommunityChannelAndCategoriesBanners.includes(communityData.id))
width: parent.width
height: {
// I dont know why, the binding doesn't work well if this isn't here
item && item.height
return active ? item.height : 0
}
sourceComponent: Component {
CommunityChannelsAndCategoriesBannerPanel {
id: channelsAndCategoriesBanner
communityId: communityData.id
onAddMembersClicked: {
Global.openPopup(createChannelPopup);
}
onAddCategoriesClicked: {
Global.openPopup(createCategoryPopup);
}
}
}
} // Loader
Loader {
active: communityData.amISectionAdmin &&
(!localAccountSensitiveSettings.hiddenCommunityBackUpBanners ||
!localAccountSensitiveSettings.hiddenCommunityBackUpBanners.includes(communityData.id))
width: parent.width
height: {
// I dont know why, the binding doesn't work well if this isn't here
item && item.height
return active ? item.height : 0
}
sourceComponent: Component {
BackUpCommuntyBannerPanel {
id: backupBanner
communityId: communityData.id
onBackupButtonClicked: {
Global.openPopup(transferOwnershipPopup, {
privateKey: communitySectionModule.exportCommunity(communityData.id),
store: root.store
})
}
}
}
} // Loader
} // Column
} // ScrollView
Loader {
id: createChatOrCommunity
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: Style.current.padding
active: communityData.amISectionAdmin
sourceComponent: Component {
StatusBaseText {
id: createChannelOrCategoryBtn
objectName: "createChannelOrCategoryBtn"
color: Theme.palette.baseColor1
height: visible ? implicitHeight : 0
text: qsTr("Create channel or category")
font.underline: true
font.pixelSize: 13
textFormat: Text.RichText
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
adminPopupMenu.showInviteButton = false
adminPopupMenu.popup()
adminPopupMenu.y = Qt.binding(() => root.height - adminPopupMenu.height
- createChannelOrCategoryBtn.height - 20)
}
}
}
}
}
Component {
id: createChannelPopup
CreateChannelPopup {
anchors.centerIn: parent
emojiPopup: root.emojiPopup
onCreateCommunityChannel: function (chName, chDescription, chEmoji, chColor,
chCategoryId) {
root.store.createCommunityChannel(chName, chDescription, chEmoji, chColor,
chCategoryId)
}
onClosed: {
destroy()
}
}
}
Component {
id: createCategoryPopup
CreateCategoryPopup {
anchors.centerIn: parent
store: root.store
onClosed: {
destroy()
}
}
}
Component {
id: deleteCategoryConfirmationDialogComponent
ConfirmationDialog {
property string categoryId
confirmButtonObjectName: "confirmDeleteCategoryButton"
btnType: "warn"
showCancelButton: true
onClosed: {
destroy()
}
onCancelButtonClicked: {
close();
}
onConfirmButtonClicked: function(){
const error = root.store.deleteCommunityCategory(categoryId);
if (error) {
deleteError.text = error
return deleteError.open()
}
close();
}
}
}
MessageDialog {
id: deleteError
title: qsTr("Error deleting the category")
icon: StandardIcon.Critical
standardButtons: StandardButton.Ok
}
Component {
id: transferOwnershipPopup
TransferOwnershipPopup {
anchors.centerIn: parent
onClosed: {
let hiddenBannerIds = localAccountSensitiveSettings.hiddenCommunityBackUpBanners || []
if (hiddenBannerIds.includes(root.store.activeCommunity.id)) {
return
}
hiddenBannerIds.push(root.store.activeCommunity.id)
localAccountSensitiveSettings.hiddenCommunityBackUpBanners = hiddenBannerIds
}
}
}
Connections {
target: root.store.mainModuleInst
onOpenCommunityMembershipRequestsPopup:{
if(root.store.getMySectionId() != sectionId)
return
Global.openPopup(membershipRequestPopup, {
communitySectionModule: root.communitySectionModule
})
}
}
2020-12-11 20:29:46 +00:00
}