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 }