Warning, /network/neochat/src/qml/MessageDelegateContextMenu.qml is written in an unsupported language. File is not indexed.

0001 // SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
0002 // SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
0003 // SPDX-License-Identifier: GPL-3.0-only
0004 
0005 import QtQuick
0006 import QtQuick.Controls as QQC2
0007 import QtQuick.Layouts
0008 import org.kde.kirigami as Kirigami
0009 import org.kde.kirigamiaddons.components as KirigamiComponents
0010 import org.kde.kirigamiaddons.formcard as FormCard
0011 
0012 import org.kde.neochat
0013 import org.kde.neochat.config
0014 
0015 /**
0016  * @brief The base menu for most message types.
0017  *
0018  * This menu supports showing a list of actions to be shown for a particular event
0019  * delegate in a message timeline. The menu supports both desktop and mobile menus
0020  * with different visuals appropriate to the platform.
0021  *
0022  * The menu supports both a list of main actions and the ability to define sub menus
0023  * using the nested action parameter.
0024  *
0025  * For event types that need alternate actions this class can be used as a base and
0026  * the actions and nested actions can be overwritten to show the alternate items.
0027  */
0028 Loader {
0029     id: root
0030 
0031     /**
0032      * @brief The curent connection for the account accessing the event.
0033      */
0034     required property NeoChatConnection connection
0035 
0036     /**
0037      * @brief The matrix ID of the message event.
0038      */
0039     required property string eventId
0040 
0041     /**
0042      * @brief The message author.
0043      *
0044      * This should consist of the following:
0045      *  - id - The matrix ID of the author.
0046      *  - isLocalUser - Whether the author is the local user.
0047      *  - avatarSource - The mxc URL for the author's avatar in the current room.
0048      *  - avatarMediaId - The media ID of the author's avatar.
0049      *  - avatarUrl - The mxc URL for the author's avatar.
0050      *  - displayName - The display name of the author.
0051      *  - display - The name of the author.
0052      *  - color - The color for the author.
0053      *  - object - The Quotient::User object for the author.
0054      *
0055      * @sa Quotient::User
0056      */
0057     required property var author
0058 
0059     /**
0060      * @brief The delegate type of the message.
0061      */
0062     required property int delegateType
0063 
0064     /**
0065      * @brief The display text of the message as plain text.
0066      */
0067     required property string plainText
0068 
0069     /**
0070      * @brief The display text of the message as rich text.
0071      */
0072     property string htmlText: ""
0073 
0074     /**
0075      * @brief The text the user currently has selected.
0076      */
0077     property string selectedText: ""
0078 
0079     /**
0080      * @brief The list of menu item actions that have sub-actions.
0081      *
0082      * Each action will be instantiated as a single line that open a sub menu.
0083      */
0084     property list<Kirigami.Action> nestedActions
0085 
0086     /**
0087      * @brief The main list of menu item actions.
0088      *
0089      * Each action will be instantiated as a single line in the menu.
0090      */
0091     property list<Kirigami.Action> actions: [
0092         Kirigami.Action {
0093             text: i18n("Edit")
0094             icon.name: "document-edit"
0095             onTriggered: {
0096                 currentRoom.editCache.editId = eventId;
0097                 currentRoom.mainCache.replyId = "";
0098             }
0099             visible: author.isLocalUser && (root.delegateType === DelegateType.Emote || root.delegateType === DelegateType.Message)
0100         },
0101         Kirigami.Action {
0102             text: i18n("Reply")
0103             icon.name: "mail-replied-symbolic"
0104             onTriggered: {
0105                 currentRoom.mainCache.replyId = eventId;
0106                 currentRoom.editCache.editId = "";
0107             }
0108         },
0109         Kirigami.Action {
0110             text: i18nc("@action:inmenu As in 'Forward this message'", "Forward")
0111             icon.name: "mail-forward-symbolic"
0112             onTriggered: {
0113                 let page = applicationWindow().pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/ChooseRoomDialog.qml", {
0114                     connection: root.connection
0115                 }, {
0116                     title: i18nc("@title", "Forward Message"),
0117                     width: Kirigami.Units.gridUnit * 25
0118                 });
0119                 page.chosen.connect(function (targetRoomId) {
0120                     root.connection.room(targetRoomId).postHtmlMessage(root.plainText, root.htmlText.length > 0 ? root.htmlText : root.plainText);
0121                     page.closeDialog();
0122                 });
0123             }
0124         },
0125         Kirigami.Action {
0126             visible: author.isLocalUser || currentRoom.canSendState("redact")
0127             text: i18n("Remove")
0128             icon.name: "edit-delete-remove"
0129             icon.color: "red"
0130             onTriggered: applicationWindow().pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/RemoveSheet.qml", {
0131                 room: currentRoom,
0132                 eventId: eventId
0133             }, {
0134                 title: i18nc("@title", "Remove Message"),
0135                 width: Kirigami.Units.gridUnit * 25
0136             })
0137         },
0138         Kirigami.Action {
0139             text: i18n("Copy")
0140             icon.name: "edit-copy"
0141             onTriggered: Clipboard.saveText(root.selectedText.length > 0 ? root.selectedText : root.plainText)
0142         },
0143         Kirigami.Action {
0144             text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report")
0145             icon.name: "dialog-warning-symbolic"
0146             visible: !author.isLocalUser
0147             onTriggered: applicationWindow().pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/ReportSheet.qml", {
0148                 room: currentRoom,
0149                 eventId: eventId
0150             }, {
0151                 title: i18nc("@title", "Report Message"),
0152                 width: Kirigami.Units.gridUnit * 25
0153             })
0154         },
0155         Kirigami.Action {
0156             visible: Config.developerTools
0157             text: i18n("View Source")
0158             icon.name: "code-context"
0159             onTriggered: RoomManager.viewEventSource(root.eventId)
0160         },
0161         Kirigami.Action {
0162             text: i18n("Copy Link")
0163             icon.name: "edit-copy"
0164             onTriggered: {
0165                 Clipboard.saveText("https://matrix.to/#/" + currentRoom.id + "/" + root.eventId);
0166             }
0167         }
0168     ]
0169 
0170     Component {
0171         id: regularMenu
0172 
0173         QQC2.Menu {
0174             id: menu
0175             Instantiator {
0176                 model: root.nestedActions
0177                 delegate: QQC2.Menu {
0178                     id: menuItem
0179                     visible: modelData.visible
0180                     title: modelData.text
0181 
0182                     Instantiator {
0183                         model: modelData.children
0184                         delegate: QQC2.MenuItem {
0185                             text: modelData.text
0186                             icon.name: modelData.icon.name
0187                             onTriggered: modelData.trigger()
0188                         }
0189                         onObjectAdded: (index, object) => {
0190                             menuItem.insertItem(0, object);
0191                         }
0192                     }
0193                 }
0194                 onObjectAdded: (index, object) => {
0195                     object.visible = false;
0196                     menu.addMenu(object);
0197                 }
0198             }
0199 
0200             Repeater {
0201                 model: root.actions
0202                 QQC2.MenuItem {
0203                     visible: modelData.visible
0204                     action: modelData
0205                     onClicked: root.item.close()
0206                 }
0207             }
0208             QQC2.Menu {
0209                 id: webshortcutmenu
0210                 title: i18n("Search for '%1'", webshortcutmodel.trunkatedSearchText)
0211                 property bool isVisible: webshortcutmodel.enabled
0212                 Component.onCompleted: {
0213                     webshortcutmenu.parent.visible = isVisible;
0214                 }
0215                 onIsVisibleChanged: webshortcutmenu.parent.visible = isVisible
0216                 Instantiator {
0217                     model: WebShortcutModel {
0218                         id: webshortcutmodel
0219                         selectedText: root.selectedText.length > 0 ? root.selectedText : root.plainText
0220                         onOpenUrl: RoomManager.resolveResource(url)
0221                     }
0222                     delegate: QQC2.MenuItem {
0223                         text: model.display
0224                         icon.name: model.decoration
0225                         onTriggered: webshortcutmodel.trigger(model.edit)
0226                     }
0227                     onObjectAdded: (index, object) => webshortcutmenu.insertItem(0, object)
0228                 }
0229                 QQC2.MenuSeparator {}
0230                 QQC2.MenuItem {
0231                     text: i18n("Configure Web Shortcuts...")
0232                     icon.name: "configure"
0233                     visible: !Controller.isFlatpak
0234                     onTriggered: webshortcutmodel.configureWebShortcuts()
0235                 }
0236             }
0237         }
0238     }
0239     Component {
0240         id: mobileMenu
0241 
0242         Kirigami.OverlayDrawer {
0243             id: drawer
0244             height: stackView.implicitHeight
0245             edge: Qt.BottomEdge
0246             padding: 0
0247             leftPadding: 0
0248             rightPadding: 0
0249             bottomPadding: 0
0250             topPadding: 0
0251 
0252             parent: applicationWindow().overlay
0253 
0254             QQC2.StackView {
0255                 id: stackView
0256                 width: parent.width
0257                 implicitHeight: currentItem.implicitHeight
0258 
0259                 Component {
0260                     id: nestedActionsComponent
0261                     ColumnLayout {
0262                         id: actionLayout
0263                         property string title: ""
0264                         property list<Kirigami.Action> actions
0265                         width: parent.width
0266                         spacing: 0
0267                         RowLayout {
0268                             QQC2.ToolButton {
0269                                 icon.name: 'draw-arrow-back'
0270                                 onClicked: stackView.pop()
0271                             }
0272                             Kirigami.Heading {
0273                                 level: 3
0274                                 Layout.fillWidth: true
0275                                 text: actionLayout.title
0276                                 wrapMode: Text.WordWrap
0277                             }
0278                         }
0279                         Repeater {
0280                             id: listViewAction
0281                             model: actionLayout.actions
0282 
0283                             FormCard.FormButtonDelegate {
0284                                 icon.name: modelData.icon.name
0285                                 icon.color: modelData.icon.color ?? undefined
0286                                 enabled: modelData.enabled
0287                                 visible: modelData.visible
0288                                 text: modelData.text
0289                                 onClicked: {
0290                                     modelData.triggered();
0291                                     root.item.close();
0292                                 }
0293                             }
0294                         }
0295                     }
0296                 }
0297                 initialItem: ColumnLayout {
0298                     id: popupContent
0299                     width: parent.width
0300                     spacing: 0
0301                     RowLayout {
0302                         id: headerLayout
0303                         Layout.fillWidth: true
0304                         Layout.margins: Kirigami.Units.largeSpacing
0305                         spacing: Kirigami.Units.largeSpacing
0306                         KirigamiComponents.Avatar {
0307                             id: avatar
0308                             source: author.avatarSource
0309                             Layout.preferredWidth: Kirigami.Units.gridUnit * 2
0310                             Layout.preferredHeight: Kirigami.Units.gridUnit * 2
0311                             Layout.alignment: Qt.AlignTop
0312                         }
0313                         ColumnLayout {
0314                             Layout.fillWidth: true
0315                             Kirigami.Heading {
0316                                 level: 3
0317                                 Layout.fillWidth: true
0318                                 text: currentRoom.htmlSafeMemberName(author.id)
0319                                 wrapMode: Text.WordWrap
0320                             }
0321                             QQC2.Label {
0322                                 text: plainText
0323                                 Layout.fillWidth: true
0324                                 wrapMode: Text.WordWrap
0325 
0326                                 onLinkActivated: RoomManager.resolveResource(link, "join")
0327                             }
0328                         }
0329                     }
0330                     Kirigami.Separator {
0331                         Layout.fillWidth: true
0332                     }
0333                     RowLayout {
0334                         spacing: 0
0335                         Layout.fillWidth: true
0336                         Layout.preferredHeight: Kirigami.Units.gridUnit * 2.5
0337                         Repeater {
0338                             model: ["👍", "👎️", "😄", "🎉", "🚀", "👀"]
0339                             delegate: QQC2.ItemDelegate {
0340                                 Layout.fillWidth: true
0341                                 Layout.fillHeight: true
0342 
0343                                 contentItem: Kirigami.Heading {
0344                                     horizontalAlignment: Text.AlignHCenter
0345                                     verticalAlignment: Text.AlignVCenter
0346 
0347                                     font.family: "emoji"
0348                                     text: modelData
0349                                 }
0350 
0351                                 onClicked: {
0352                                     currentRoom.toggleReaction(eventId, modelData);
0353                                     root.item.close();
0354                                 }
0355                             }
0356                         }
0357                     }
0358                     Kirigami.Separator {
0359                         Layout.fillWidth: true
0360                     }
0361                     Repeater {
0362                         id: listViewAction
0363                         model: root.actions
0364 
0365                         FormCard.FormButtonDelegate {
0366                             icon.name: modelData.icon.name
0367                             icon.color: modelData.icon.color ?? undefined
0368                             enabled: modelData.enabled
0369                             visible: modelData.visible
0370                             text: modelData.text
0371                             onClicked: {
0372                                 modelData.triggered();
0373                                 root.item.close();
0374                             }
0375                         }
0376                     }
0377 
0378                     Repeater {
0379                         model: root.nestedActions
0380 
0381                         FormCard.FormButtonDelegate {
0382                             action: modelData
0383                             visible: modelData.visible
0384                             onClicked: {
0385                                 stackView.push(nestedActionsComponent, {
0386                                     title: modelData.text,
0387                                     actions: modelData.children
0388                                 });
0389                             }
0390                         }
0391                     }
0392                 }
0393             }
0394         }
0395     }
0396 
0397     asynchronous: true
0398     sourceComponent: Kirigami.Settings.isMobile ? mobileMenu : regularMenu
0399 
0400     function open() {
0401         active = true;
0402     }
0403 
0404     onStatusChanged: if (status == Loader.Ready) {
0405         if (Kirigami.Settings.isMobile) {
0406             item.open();
0407         } else {
0408             item.popup();
0409         }
0410     }
0411 }