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