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", {room: currentRoom, eventId: eventId}, {
0131                 title: i18nc("@title", "Remove Message"),
0132                 width: Kirigami.Units.gridUnit * 25
0133             })
0134         },
0135         Kirigami.Action {
0136             text: i18n("Copy")
0137             icon.name: "edit-copy"
0138             onTriggered: Clipboard.saveText(root.selectedText.length > 0 ? root.selectedText : root.plainText)
0139         },
0140         Kirigami.Action {
0141             text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report")
0142             icon.name: "dialog-warning-symbolic"
0143             visible: !author.isLocalUser
0144             onTriggered: applicationWindow().pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/ReportSheet.qml", {room: currentRoom, eventId: eventId}, {
0145                 title: i18nc("@title", "Report Message"),
0146                 width: Kirigami.Units.gridUnit * 25
0147             })
0148         },
0149         Kirigami.Action {
0150             visible: Config.developerTools
0151             text: i18n("View Source")
0152             icon.name: "code-context"
0153             onTriggered: RoomManager.viewEventSource(root.eventId)
0154         },
0155         Kirigami.Action {
0156             text: i18n("Copy Link")
0157             icon.name: "edit-copy"
0158             onTriggered: {
0159                 Clipboard.saveText("https://matrix.to/#/" + currentRoom.id + "/" + root.eventId)
0160             }
0161         }
0162     ]
0163 
0164     Component {
0165         id: regularMenu
0166 
0167         QQC2.Menu {
0168             id: menu
0169             Instantiator {
0170                 model: root.nestedActions
0171                 delegate: QQC2.Menu {
0172                     id: menuItem
0173                     visible: modelData.visible
0174                     title: modelData.text
0175 
0176                     Instantiator {
0177                         model: modelData.children
0178                         delegate: QQC2.MenuItem {
0179                             text: modelData.text
0180                             icon.name: modelData.icon.name
0181                             onTriggered: modelData.trigger()
0182                         }
0183                         onObjectAdded: (index, object) => {menuItem.insertItem(0, object)}
0184                     }
0185                 }
0186                 onObjectAdded: (index, object) => {
0187                     object.visible = false;
0188                     menu.addMenu(object)
0189                 }
0190             }
0191 
0192             Repeater {
0193                 model: root.actions
0194                 QQC2.MenuItem {
0195                     visible: modelData.visible
0196                     action: modelData
0197                     onClicked: root.item.close();
0198                 }
0199             }
0200             QQC2.Menu {
0201                 id: webshortcutmenu
0202                 title: i18n("Search for '%1'", webshortcutmodel.trunkatedSearchText)
0203                 property bool isVisible: webshortcutmodel.enabled
0204                 Component.onCompleted: {
0205                     webshortcutmenu.parent.visible = isVisible
0206                 }
0207                 onIsVisibleChanged: webshortcutmenu.parent.visible = isVisible
0208                 Instantiator {
0209                     model: WebShortcutModel {
0210                         id: webshortcutmodel
0211                         selectedText: root.selectedText.length > 0 ? root.selectedText : root.plainText
0212                         onOpenUrl: RoomManager.resolveResource(url)
0213                     }
0214                     delegate: QQC2.MenuItem {
0215                         text: model.display
0216                         icon.name: model.decoration
0217                         onTriggered: webshortcutmodel.trigger(model.edit)
0218                     }
0219                     onObjectAdded: (index, object) => webshortcutmenu.insertItem(0, object)
0220                 }
0221                 QQC2.MenuSeparator {}
0222                 QQC2.MenuItem {
0223                     text: i18n("Configure Web Shortcuts...")
0224                     icon.name: "configure"
0225                     visible: !Controller.isFlatpak
0226                     onTriggered: webshortcutmodel.configureWebShortcuts()
0227                 }
0228             }
0229         }
0230     }
0231     Component {
0232         id: mobileMenu
0233 
0234         Kirigami.OverlayDrawer {
0235             id: drawer
0236             height: stackView.implicitHeight
0237             edge: Qt.BottomEdge
0238             padding: 0
0239             leftPadding: 0
0240             rightPadding: 0
0241             bottomPadding: 0
0242             topPadding: 0
0243 
0244             parent: applicationWindow().overlay
0245 
0246             QQC2.StackView {
0247                 id: stackView
0248                 width: parent.width
0249                 implicitHeight: currentItem.implicitHeight
0250 
0251                 Component {
0252                     id: nestedActionsComponent
0253                     ColumnLayout {
0254                         id: actionLayout
0255                         property string title: ""
0256                         property list<Kirigami.Action> actions
0257                         width: parent.width
0258                         spacing: 0
0259                         RowLayout {
0260                             QQC2.ToolButton {
0261                                 icon.name: 'draw-arrow-back'
0262                                 onClicked: stackView.pop()
0263                             }
0264                             Kirigami.Heading {
0265                                 level: 3
0266                                 Layout.fillWidth: true
0267                                 text: actionLayout.title
0268                                 wrapMode: Text.WordWrap
0269                             }
0270                         }
0271                         Repeater {
0272                             id: listViewAction
0273                             model: actionLayout.actions
0274 
0275                             FormCard.FormButtonDelegate {
0276                                 icon.name: modelData.icon.name
0277                                 icon.color: modelData.icon.color ?? undefined
0278                                 enabled: modelData.enabled
0279                                 visible: modelData.visible
0280                                 text: modelData.text
0281                                 onClicked: {
0282                                     modelData.triggered()
0283                                     root.item.close();
0284                                 }
0285                             }
0286                         }
0287                     }
0288                 }
0289                 initialItem: ColumnLayout {
0290                     id: popupContent
0291                     width: parent.width
0292                     spacing: 0
0293                     RowLayout {
0294                         id: headerLayout
0295                         Layout.fillWidth: true
0296                         Layout.margins: Kirigami.Units.largeSpacing
0297                         spacing: Kirigami.Units.largeSpacing
0298                         KirigamiComponents.Avatar {
0299                             id: avatar
0300                             source: author.avatarSource
0301                             Layout.preferredWidth: Kirigami.Units.gridUnit * 2
0302                             Layout.preferredHeight: Kirigami.Units.gridUnit * 2
0303                             Layout.alignment: Qt.AlignTop
0304                         }
0305                         ColumnLayout {
0306                             Layout.fillWidth: true
0307                             Kirigami.Heading {
0308                                 level: 3
0309                                 Layout.fillWidth: true
0310                                 text: currentRoom.htmlSafeMemberName(author.id)
0311                                 wrapMode: Text.WordWrap
0312                             }
0313                             QQC2.Label {
0314                                 text: plainText
0315                                 Layout.fillWidth: true
0316                                 wrapMode: Text.WordWrap
0317 
0318                                 onLinkActivated: RoomManager.resolveResource(link, "join");
0319                             }
0320                         }
0321                     }
0322                     Kirigami.Separator {
0323                         Layout.fillWidth: true
0324                     }
0325                     RowLayout {
0326                         spacing: 0
0327                         Layout.fillWidth: true
0328                         Layout.preferredHeight: Kirigami.Units.gridUnit * 2.5
0329                         Repeater {
0330                             model: ["👍", "👎️", "😄", "🎉", "🚀", "👀"]
0331                             delegate: QQC2.ItemDelegate {
0332                                 Layout.fillWidth: true
0333                                 Layout.fillHeight: true
0334 
0335                                 contentItem: Kirigami.Heading {
0336                                     horizontalAlignment: Text.AlignHCenter
0337                                     verticalAlignment: Text.AlignVCenter
0338 
0339                                     font.family: "emoji"
0340                                     text: modelData
0341                                 }
0342 
0343                                 onClicked: {
0344                                     currentRoom.toggleReaction(eventId, modelData);
0345                                     root.item.close();
0346                                 }
0347                             }
0348                         }
0349                     }
0350                     Kirigami.Separator {
0351                         Layout.fillWidth: true
0352                     }
0353                     Repeater {
0354                         id: listViewAction
0355                         model: root.actions
0356 
0357                         FormCard.FormButtonDelegate {
0358                             icon.name: modelData.icon.name
0359                             icon.color: modelData.icon.color ?? undefined
0360                             enabled: modelData.enabled
0361                             visible: modelData.visible
0362                             text: modelData.text
0363                             onClicked: {
0364                                 modelData.triggered()
0365                                 root.item.close();
0366                             }
0367                         }
0368                     }
0369 
0370                     Repeater {
0371                         model: root.nestedActions
0372 
0373                         FormCard.FormButtonDelegate {
0374                             action: modelData
0375                             visible: modelData.visible
0376                             onClicked: {
0377                                 stackView.push(nestedActionsComponent, {
0378                                     title: modelData.text,
0379                                     actions: modelData.children
0380                                 });
0381                             }
0382                         }
0383                     }
0384                 }
0385             }
0386         }
0387     }
0388 
0389     asynchronous: true
0390     sourceComponent: Kirigami.Settings.isMobile ? mobileMenu : regularMenu
0391 
0392     function open() {
0393         active = true;
0394     }
0395 
0396     onStatusChanged: if (status == Loader.Ready) {
0397         if (Kirigami.Settings.isMobile) {
0398             item.open();
0399         } else {
0400             item.popup();
0401         }
0402     }
0403 }
0404