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 }