diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 8d6413958..bcc79b4ed 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -101,6 +101,7 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE qml/AvatarNotification.qml qml/ReasonDialog.qml qml/NewPollDialog.qml + qml/UserMenu.qml DEPENDENCIES QtCore QtQuick diff --git a/src/app/qml/UserMenu.qml b/src/app/qml/UserMenu.qml new file mode 100644 index 000000000..3986c821a --- /dev/null +++ b/src/app/qml/UserMenu.qml @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2025 Joshua Goins +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +import QtQuick +import QtQuick.Controls as QQC2 +import QtQuick.Layouts + +import org.kde.kirigami as Kirigami +import org.kde.kirigamiaddons.components as KirigamiComponents + +import org.kde.neochat +import org.kde.neochat.settings +import org.kde.neochat.devtools + +KirigamiComponents.ConvergentContextMenu { + id: root + + required property NeoChatConnection connection + required property Kirigami.ApplicationWindow window + required property var author + + QQC2.Action { + text: i18nc("@action:button", "Open Profile") + icon.name: "im-user-symbolic" + onTriggered: RoomManager.resolveResource(root.author.uri) + } + + QQC2.Action { + text: i18nc("@action:button", "Mention") + icon.name: "username-copy-symbolic" + onTriggered: { + RoomManager.currentRoom.mainCache.mentionAdded(root.author.id); + } + } +} diff --git a/src/chatbar/ChatBar.qml b/src/chatbar/ChatBar.qml index 93af4be30..80abde9a0 100644 --- a/src/chatbar/ChatBar.qml +++ b/src/chatbar/ChatBar.qml @@ -55,6 +55,19 @@ QQC2.Control { } } + Connections { + target: currentRoom.mainCache + + function onMentionAdded(mention: string): void { + // add mention text + textField.append(mention + " "); + // move cursor to the end + textField.cursorPosition = textField.text.length; + // move the focus back to the chat bar + textField.forceActiveFocus(Qt.OtherFocusReason); + } + } + /** * @brief The list of actions in the ChatBar. * diff --git a/src/libneochat/chatbarcache.h b/src/libneochat/chatbarcache.h index dd369351d..be89e36f1 100644 --- a/src/libneochat/chatbarcache.h +++ b/src/libneochat/chatbarcache.h @@ -195,6 +195,7 @@ Q_SIGNALS: void relationIdChanged(const QString &oldEventId, const QString &newEventId); void threadIdChanged(const QString &oldThreadId, const QString &newThreadId); void attachmentPathChanged(); + void mentionAdded(const QString &mention); private: QString m_text = QString(); diff --git a/src/timeline/AuthorComponent.qml b/src/timeline/AuthorComponent.qml index e8df80f21..7f2a4aacc 100644 --- a/src/timeline/AuthorComponent.qml +++ b/src/timeline/AuthorComponent.qml @@ -41,21 +41,43 @@ RowLayout { implicitHeight: Math.max(nameButton.implicitHeight, timeLabel.implicitHeight) - QQC2.AbstractButton { + QQC2.Label { id: nameButton - contentItem: QQC2.Label { - text: root.author.disambiguatedName - color: root.author.color - textFormat: Text.PlainText - font.weight: Font.Bold - elide: Text.ElideRight + + text: root.author.disambiguatedName + color: root.author.color + textFormat: Text.PlainText + font.weight: Font.Bold + elide: Text.ElideRight + + function openUserMenu(): void { + const menu = Qt.createComponent("org.kde.neochat", "UserMenu").createObject(root, { + connection: root.connection, + window: QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow, + author: root.author, + }); + menu.popup(root); } - Accessible.name: contentItem.text - onClicked: RoomManager.resolveResource(root.author.uri) HoverHandler { cursorShape: Qt.PointingHandCursor } + + // tapping to open profile + TapHandler { + onTapped: RoomManager.resolveResource(root.author.uri) + } + + // right-clicking/long-press for context menu + TapHandler { + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus + acceptedButtons: Qt.RightButton + onTapped: nameButton.openUserMenu() + } + TapHandler { + acceptedDevices: PointerDevice.TouchScreen + onTapped: nameButton.openUserMenu() + } } Item { Layout.fillWidth: true @@ -73,16 +95,4 @@ RowLayout { id: timeHoverHandler } } - - TapHandler { - acceptedButtons: Qt.LeftButton - acceptedDevices: PointerDevice.TouchScreen - onLongPressed: RoomManager.viewEventMenu(root.eventId, root.Message.room, root.author, root.Message.selectedText, root.Message.hoveredLink); - } - TapHandler { - acceptedButtons: Qt.RightButton - acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus - gesturePolicy: TapHandler.WithinBounds - onTapped: RoomManager.viewEventMenu(root.eventId, root.Message.room, root.author, root.Message.selectedText, root.Message.hoveredLink); - } } diff --git a/src/timeline/DelegateContextMenu.qml b/src/timeline/DelegateContextMenu.qml index 3fb132f8f..bc405554b 100644 --- a/src/timeline/DelegateContextMenu.qml +++ b/src/timeline/DelegateContextMenu.qml @@ -142,14 +142,6 @@ KirigamiComponents.ConvergentContextMenu { } } - component ShowUserAction: Kirigami.Action { - text: i18nc("@action:inmenu", "Show User") - icon.name: "username-copy" - onTriggered: { - RoomManager.resolveResource(author.id) - } - } - component PinMessageAction: Kirigami.Action { readonly property bool pinned: currentRoom.isEventPinned(root.eventId) diff --git a/src/timeline/FileDelegateContextMenu.qml b/src/timeline/FileDelegateContextMenu.qml index d29fc7965..994eb4d56 100644 --- a/src/timeline/FileDelegateContextMenu.qml +++ b/src/timeline/FileDelegateContextMenu.qml @@ -94,8 +94,6 @@ DelegateContextMenu { DelegateContextMenu.ReportMessageAction {} - DelegateContextMenu.ShowUserAction {} - Kirigami.Action { separator: true visible: viewSourceAction.visible diff --git a/src/timeline/MessageDelegate.qml b/src/timeline/MessageDelegate.qml index 12a7d03de..cf3e1927c 100644 --- a/src/timeline/MessageDelegate.qml +++ b/src/timeline/MessageDelegate.qml @@ -149,6 +149,7 @@ MessageDelegateBase { TapHandler { acceptedButtons: Qt.RightButton + gesturePolicy: TapHandler.ReleaseWithinBounds onTapped: _private.showMessageMenu() } @@ -159,7 +160,7 @@ MessageDelegateBase { } } - avatarComponent: KirigamiComponents.AvatarButton { + avatarComponent: KirigamiComponents.Avatar { id: avatar implicitWidth: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2 implicitHeight: width @@ -170,7 +171,34 @@ MessageDelegateBase { asynchronous: true QQC2.ToolTip.text: root.author.htmlSafeDisambiguatedName - onClicked: RoomManager.resolveResource(root.author.uri) + function openUserMenu(): void { + const menu = Qt.createComponent("org.kde.neochat", "UserMenu").createObject(root, { + connection: root.connection, + window: QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow, + author: root.author, + }); + menu.popup(root); + } + + HoverHandler { + cursorShape: Qt.PointingHandCursor + } + + // tapping to open profile + TapHandler { + onTapped: RoomManager.resolveResource(root.author.uri) + } + + // right-clicking/long-press for context menu + TapHandler { + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus + acceptedButtons: Qt.RightButton + onTapped: avatar.openUserMenu() + } + TapHandler { + acceptedDevices: PointerDevice.TouchScreen + onTapped: avatar.openUserMenu() + } } sectionComponent: SectionDelegate { diff --git a/src/timeline/MessageDelegateContextMenu.qml b/src/timeline/MessageDelegateContextMenu.qml index d7adf2a70..c22e35001 100644 --- a/src/timeline/MessageDelegateContextMenu.qml +++ b/src/timeline/MessageDelegateContextMenu.qml @@ -102,7 +102,6 @@ DelegateContextMenu { } DelegateContextMenu.PinMessageAction {} DelegateContextMenu.ReportMessageAction {} - DelegateContextMenu.ShowUserAction {} Kirigami.Action { separator: true visible: viewSourceAction.visible