From fe7cf0a595b1d21dc0daa4603853b6bb47de06fb Mon Sep 17 00:00:00 2001 From: James Graham Date: Tue, 24 Dec 2024 17:14:00 +0000 Subject: [PATCH] Add button to thread to reply ![image](/uploads/bf2b6857f797480b429db02c5d01a4f7/image.png){width=254 height=147} --- src/enums/messagecomponenttype.h | 1 + src/models/messagecontentmodel.cpp | 1 + src/models/messagecontentmodel.h | 2 + src/models/threadmodel.cpp | 21 +++++----- src/models/threadmodel.h | 1 + src/qml/HoverActions.qml | 2 +- src/timeline/CMakeLists.txt | 1 + src/timeline/MessageComponentChooser.qml | 8 ++++ src/timeline/ReplyButtonComponent.qml | 50 ++++++++++++++++++++++++ 9 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 src/timeline/ReplyButtonComponent.qml diff --git a/src/enums/messagecomponenttype.h b/src/enums/messagecomponenttype.h index 62abb39ef..9c49aaeb4 100644 --- a/src/enums/messagecomponenttype.h +++ b/src/enums/messagecomponenttype.h @@ -53,6 +53,7 @@ public: LinkPreview, /**< A preview of a URL in the message. */ LinkPreviewLoad, /**< A loading dialog for a link preview. */ ChatBar, /**< A text edit for editing a message. */ + ReplyButton, /**< A button to reply in the current thread. */ Verification, /**< A user verification session start message. */ Loading, /**< The component is loading. */ Other, /**< Anything that cannot be classified as another type. */ diff --git a/src/models/messagecontentmodel.cpp b/src/models/messagecontentmodel.cpp index a2a3e2d8d..d08fa92e4 100644 --- a/src/models/messagecontentmodel.cpp +++ b/src/models/messagecontentmodel.cpp @@ -405,6 +405,7 @@ QHash MessageContentModel::roleNames() const roles[ReplyEventIdRole] = "replyEventId"; roles[ReplyAuthorRole] = "replyAuthor"; roles[ReplyContentModelRole] = "replyContentModel"; + roles[ThreadRootRole] = "threadRoot"; roles[LinkPreviewerRole] = "linkPreviewer"; roles[ChatBarCacheRole] = "chatBarCache"; return roles; diff --git a/src/models/messagecontentmodel.h b/src/models/messagecontentmodel.h index 877702445..92411199f 100644 --- a/src/models/messagecontentmodel.h +++ b/src/models/messagecontentmodel.h @@ -62,6 +62,8 @@ public: ReplyAuthorRole, /**< The author of the event that was replied to. */ ReplyContentModelRole, /**< The MessageContentModel for the reply event. */ + ThreadRootRole, /**< The thread root event ID for the event. */ + LinkPreviewerRole, /**< The link preview details. */ ChatBarCacheRole, /**< The ChatBarCache to use. */ }; diff --git a/src/models/threadmodel.cpp b/src/models/threadmodel.cpp index f553d6275..1afb13dce 100644 --- a/src/models/threadmodel.cpp +++ b/src/models/threadmodel.cpp @@ -178,8 +178,14 @@ QVariant ThreadChatBarModel::data(const QModelIndex &idx, int role) const return {}; } + const auto threadModel = dynamic_cast(parent()); + if (threadModel == nullptr) { + qWarning() << "ThreadChatBarModel created with incorrect parent, a ThreadModel must be set as the parent on creation."; + return {}; + } + if (role == ComponentTypeRole) { - return MessageComponentType::ChatBar; + return m_room->threadCache()->threadId() == threadModel->threadRootId() ? MessageComponentType::ChatBar : MessageComponentType::ReplyButton; } if (role == ChatBarCacheRole) { if (m_room == nullptr) { @@ -187,20 +193,16 @@ QVariant ThreadChatBarModel::data(const QModelIndex &idx, int role) const } return QVariant::fromValue(m_room->threadCache()); } + if (role == ThreadRootRole) { + return threadModel->threadRootId(); + } return {}; } int ThreadChatBarModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) - if (m_room == nullptr) { - return 0; - } - const auto threadModel = dynamic_cast(this->parent()); - if (threadModel != nullptr) { - return m_room->threadCache()->threadId() == threadModel->threadRootId() ? 1 : 0; - } - return 0; + return 1; } QHash ThreadChatBarModel::roleNames() const @@ -208,6 +210,7 @@ QHash ThreadChatBarModel::roleNames() const return { {ComponentTypeRole, "componentType"}, {ChatBarCacheRole, "chatBarCache"}, + {ThreadRootRole, "threadRoot"}, }; } diff --git a/src/models/threadmodel.h b/src/models/threadmodel.h index 0a24f3f09..1ec29cb53 100644 --- a/src/models/threadmodel.h +++ b/src/models/threadmodel.h @@ -41,6 +41,7 @@ public: enum Roles { ComponentTypeRole = MessageContentModel::ComponentTypeRole, /**< The type of component to visualise the message. */ ChatBarCacheRole = MessageContentModel::ChatBarCacheRole, /**< The ChatBarCache to use. */ + ThreadRootRole = MessageContentModel::ThreadRootRole, /**< The thread root event ID for the thread. */ }; Q_ENUM(Roles) diff --git a/src/qml/HoverActions.qml b/src/qml/HoverActions.qml index a7dfd8b5a..856cbd3ec 100644 --- a/src/qml/HoverActions.qml +++ b/src/qml/HoverActions.qml @@ -44,7 +44,7 @@ QQC2.Control { leftPadding: 0 rightPadding: 0 - visible: (root.hovered || root.showActions || showActionsTimer.running) && !Kirigami.Settings.isMobile + visible: (root.hovered || root.showActions || showActionsTimer.running) && !Kirigami.Settings.isMobile && (!root.delegate.isThreaded || !NeoChatConfig.threads) onVisibleChanged: { if (visible) { // HACK: delay disapearing by 200ms, otherwise this can create some glitches diff --git a/src/timeline/CMakeLists.txt b/src/timeline/CMakeLists.txt index 001307bfa..57db5717c 100644 --- a/src/timeline/CMakeLists.txt +++ b/src/timeline/CMakeLists.txt @@ -46,6 +46,7 @@ ecm_add_qml_module(timeline GENERATE_PLUGIN_SOURCE PollComponent.qml QuoteComponent.qml ReplyAuthorComponent.qml + ReplyButtonComponent.qml ReplyComponent.qml StateComponent.qml TextComponent.qml diff --git a/src/timeline/MessageComponentChooser.qml b/src/timeline/MessageComponentChooser.qml index 88471acf8..af960ce49 100644 --- a/src/timeline/MessageComponentChooser.qml +++ b/src/timeline/MessageComponentChooser.qml @@ -210,6 +210,14 @@ DelegateChooser { } } + DelegateChoice { + roleValue: MessageComponentType.ReplyButton + delegate: ReplyButtonComponent { + room: root.room + maxContentWidth: root.maxContentWidth + } + } + DelegateChoice { roleValue: MessageComponentType.Verification delegate: MimeComponent { diff --git a/src/timeline/ReplyButtonComponent.qml b/src/timeline/ReplyButtonComponent.qml new file mode 100644 index 000000000..7dc60664b --- /dev/null +++ b/src/timeline/ReplyButtonComponent.qml @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2023 James Graham +// 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.delegates as Delegates + +import org.kde.neochat +import org.kde.neochat.chatbar + +/** + * @brief A component to show a reply button for threads in a message bubble. + */ +Delegates.RoundedItemDelegate { + id: root + + /** + * @brief The NeoChatRoom the delegate is being displayed in. + */ + required property NeoChatRoom room + + /** + * @brief The thread root ID. + */ + required property string threadRoot + + /** + * @brief The maximum width that the bubble's content can be. + */ + property real maxContentWidth: -1 + + Layout.fillWidth: true + Layout.maximumWidth: root.maxContentWidth + + leftInset: 0 + rightInset: 0 + + icon.name: "mail-reply-custom" + text: i18nc("@action:button", "Reply") + + onClicked: { + root.room.threadCache.replyId = ""; + root.room.threadCache.threadId = root.threadRoot; + root.room.mainCache.clearRelations(); + root.room.editCache.clearRelations(); + } +}