diff --git a/imports/NeoChat/Component/ChatBox/AttachmentPane.qml b/imports/NeoChat/Component/ChatBox/AttachmentPane.qml index f07aba717..372cd598a 100644 --- a/imports/NeoChat/Component/ChatBox/AttachmentPane.qml +++ b/imports/NeoChat/Component/ChatBox/AttachmentPane.qml @@ -14,17 +14,14 @@ import org.kde.neochat 1.0 Loader { id: root - property string attachmentPath: "" - property var attachmentMimetype: FileType.mimeTypeForUrl(attachmentPath) + property var attachmentMimetype: FileType.mimeTypeForUrl(ChatBoxHelper.attachmentPath) readonly property bool hasImage: attachmentMimetype.valid && FileType.supportedImageFormats.includes(attachmentMimetype.preferredSuffix) - signal clearAttachmentTriggered() - active: visible sourceComponent: Component { Pane { id: attachmentPane - property string baseFileName: attachmentPath.toString().substring(attachmentPath.toString().lastIndexOf('/') + 1, attachmentPath.length) + property string baseFileName: ChatBoxHelper.attachmentPath.toString().substring(ChatBoxHelper.attachmentPath.toString().lastIndexOf('/') + 1, ChatBoxHelper.attachmentPath.length) Kirigami.Theme.colorSet: Kirigami.Theme.View contentItem: Item { @@ -49,7 +46,7 @@ Loader { asynchronous: true cache: false // Cache is not needed. Images will rarely be shown repeatedly. smooth: height == preferredHeight && parent.height == parent.implicitHeight // Don't smooth until height animation stops - source: hasImage ? attachmentPath : "" + source: hasImage ? ChatBoxHelper.attachmentPath : "" visible: hasImage fillMode: Image.PreserveAspectFit @@ -165,14 +162,14 @@ Loader { Component { id: imageEditorPage ImageEditorPage { - imagePath: attachmentPath + imagePath: ChatBoxHelper.attachmentPath } } onClicked: { let imageEditor = applicationWindow().pageStack.layers.push(imageEditorPage); imageEditor.newPathChanged.connect(function(newPath) { applicationWindow().pageStack.layers.pop(); - attachmentPath = newPath; + ChatBoxHelper.attachmentPath = newPath; }); } ToolTip.text: text @@ -183,9 +180,7 @@ Loader { icon.name: "dialog-cancel" text: i18n("Cancel") display: AbstractButton.IconOnly - onClicked: { - clearAttachmentTriggered(); - } + onClicked: ChatBoxHelper.clearAttachment(); ToolTip.text: text ToolTip.visible: hovered } diff --git a/imports/NeoChat/Component/ChatBox/ChatBar.qml b/imports/NeoChat/Component/ChatBox/ChatBar.qml index 01068eb4b..916fc4317 100644 --- a/imports/NeoChat/Component/ChatBox/ChatBar.qml +++ b/imports/NeoChat/Component/ChatBox/ChatBar.qml @@ -15,7 +15,7 @@ ToolBar { id: chatBar property string replyEventId: "" property string editEventId: "" - property string inputFieldText: currentRoom ? currentRoom.cachedInput : "" + property alias inputFieldText: inputField.text property alias textField: inputField property alias emojiPaneOpened: emojiButton.checked @@ -24,7 +24,6 @@ ToolBar { // This use an hack to define: https://doc.qt.io/qt-5/qml-var.html#property-value-initialization-semantics property var userAutocompleted: ({}) - signal attachTriggered(string localPath) signal closeAllTriggered() signal inputFieldForceActiveFocusTriggered() signal messageSent() @@ -220,14 +219,14 @@ ToolBar { } Item { - visible: !isReply && (!hasAttachment || uploadingBusySpinner.running) + visible: !ChatBoxHelper.isReplying && (!ChatBoxHelper.hasAttachment || uploadingBusySpinner.running) implicitWidth: uploadButton.implicitWidth implicitHeight: uploadButton.implicitHeight ToolButton { id: uploadButton anchors.fill: parent // Matrix does not allow sending attachments in replies - visible: !isReply && !hasAttachment && !uploadingBusySpinner.running + visible: !ChatBoxHelper.isReplying && !ChatBoxHelper.hasAttachment && !uploadingBusySpinner.running icon.name: "mail-attachment" text: i18n("Attach an image or file") display: AbstractButton.IconOnly @@ -239,7 +238,7 @@ ToolBar { var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay) fileDialog.chosen.connect((path) => { if (!path) { return } - attachTriggered(path) + ChatBoxHelper.attachmentPath = path; }) fileDialog.open() } @@ -318,13 +317,21 @@ ToolBar { if (!Clipboard.saveImage(localPath)) { return; } - attachTriggered(localPath) + ChatBoxHelper.attachmentPath = localPath; } function postMessage() { checkForFancyEffectsReason(); - roomManager.actionsHandler.postMessage(inputField.text.trim(), attachmentPath, - replyEventId, editEventId, userAutocompleted); + if (ChatBoxHelper.hasAttachment) { + // send attachment but don't reset the text + roomManager.actionsHandler.postMessage("", ChatBoxHelper.attachmentPath, + ChatBoxHelper.replyEventId, ChatBoxHelper.editEventId, {}); + currentRoom.markAllMessagesAsRead(); + messageSent(); + return; + } + roomManager.actionsHandler.postMessage(inputField.text.trim(), ChatBoxHelper.attachmentPath, + ChatBoxHelper.replyEventId, ChatBoxHelper.editEventId, userAutocompleted); currentRoom.markAllMessagesAsRead(); inputField.clear(); inputField.text = Qt.binding(function() { diff --git a/imports/NeoChat/Component/ChatBox/ChatBox.qml b/imports/NeoChat/Component/ChatBox/ChatBox.qml index aa408d467..0d42036c4 100644 --- a/imports/NeoChat/Component/ChatBox/ChatBox.qml +++ b/imports/NeoChat/Component/ChatBox/ChatBox.qml @@ -13,19 +13,8 @@ import org.kde.neochat 1.0 Item { id: root - readonly property bool isReply: replyEventId.length > 0 - property var replyUser - property alias replyEventId: chatBar.replyEventId - property string replyContent: "" - - readonly property bool hasAttachment: attachmentPath.length > 0 - property string attachmentPath: "" - property alias inputFieldText: chatBar.inputFieldText - readonly property bool isEdit: editEventId.length > 0 - property alias editEventId: chatBar.editEventId - signal fancyEffectsReasonFound(string fancyEffect) Kirigami.Theme.colorSet: Kirigami.Theme.View @@ -76,6 +65,7 @@ Item { sourceComponent: EmojiPicker{ textArea: chatBar.textField emojiModel: EmojiModel { id: emojiModel } + onChosen: addText(emoji) } Behavior on height { NumberAnimation { @@ -97,10 +87,8 @@ Item { ReplyPane { id: replyPane - visible: isReply || isEdit - isEdit: root.isEdit - user: root.replyUser - content: root.replyContent + visible: ChatBoxHelper.isReplying || ChatBoxHelper.isEditing + user: ChatBoxHelper.replyUser width: parent.width height: visible ? implicitHeight : 0 anchors.bottom: attachmentSeparator.top @@ -123,8 +111,7 @@ Item { AttachmentPane { id: attachmentPane - attachmentPath: root.attachmentPath - visible: hasAttachment + visible: ChatBoxHelper.hasAttachment width: parent.width height: visible ? implicitHeight : 0 anchors.bottom: chatBarSeparator.top @@ -159,36 +146,9 @@ Item { easing.type: Easing.OutCubic } } - } - Connections { - target: replyPane - function onClearEditReplyTriggered() { - if (isEdit) { - clearEdit() - } - if (isReply) { - clearReply() - } - } - } - - Connections { - target: attachmentPane - function onClearAttachmentTriggered() { - clearAttachment() - } - } - - Connections { - target: chatBar - function onAttachTriggered(localPath) { - attach(localPath) - } - function onCloseAllTriggered() { - closeAll() - } - function onMessageSent() { + onCloseAllTriggered: closeAll() + onMessageSent: { closeAll() checkForFancyEffectsReason() } @@ -225,70 +185,41 @@ Item { root.inputFieldText = inputFieldText.substr(0, inputField.cursorPosition) + str + inputFieldText.substr(inputField.cursorPosition) } - function clearText() { - // ChatBar's TextArea syncs currentRoom.cachedInput with the TextArea's text property - root.inputFieldText = "" - } - function focusInputField() { chatBar.inputFieldForceActiveFocusTriggered() } - function edit(editContent, editFormatedContent, editEventId) { - // Set the input field in edit mode - root.inputFieldText = editContent; - root.editEventId = editEventId; - root.replyContent = editContent; + Connections { + target: ChatBoxHelper - // clean autocompletion list - chatBar.userAutocompleted = {}; - - // Fill autocompletion list with values extracted from message. - // We can't just iterate on every user in the list and try to - // find matching display name since some users have display name - // matching frequent words and this will marks too many words as - // mentions. - const regex = /([^<]*)<\/a>/g; - - let match; - while ((match = regex.exec(editFormatedContent.toString())) !== null) { - chatBar.userAutocompleted[match[2]] = match[1]; + function onShouldClearText() { + root.inputFieldText = ""; } - } - function clearEdit() { - // Clear input when edits are cancelled. - // Cached input will be - clearText() - clearReply() - root.editEventId = ""; - } + function onEditing(editContent, editFormatedContent) { + // Set the input field in edit mode + root.inputFieldText = editContent; + //root.replyContent = editContent; - function attach(localPath) { - root.attachmentPath = localPath - } + // clean autocompletion list + chatBar.userAutocompleted = {}; - function clearAttachment() { - root.attachmentPath = "" - } + // Fill autocompletion list with values extracted from message. + // We can't just iterate on every user in the list and try to + // find matching display name since some users have display name + // matching frequent words and this will marks too many words as + // mentions. + const regex = /([^<]*)<\/a>/g; - function clearReply() { - replyUser = null; - root.replyContent = ""; - root.replyEventId = ""; - // Don't clear input when replies are cancelled + let match; + while ((match = regex.exec(editFormatedContent.toString())) !== null) { + chatBar.userAutocompleted[match[2]] = match[1]; + } + } } function closeAll() { - if (hasAttachment) { - clearAttachment(); - } - if (isEdit) { - clearEdit(); - } - if (isReply) { - clearReply(); - } + ChatBoxHelper.clear(); chatBar.emojiPaneOpened = false; } } diff --git a/imports/NeoChat/Component/ChatBox/ReplyPane.qml b/imports/NeoChat/Component/ChatBox/ReplyPane.qml index 21074dd60..b86a49a93 100644 --- a/imports/NeoChat/Component/ChatBox/ReplyPane.qml +++ b/imports/NeoChat/Component/ChatBox/ReplyPane.qml @@ -7,15 +7,13 @@ import QtQuick 2.15 import QtQuick.Layouts 1.15 import QtQuick.Controls 2.15 import org.kde.kirigami 2.14 as Kirigami +import org.kde.neochat 1.0 Loader { id: root - property bool isEdit: false + readonly property bool isEdit: ChatBoxHelper.isEditing property var user: null - property string content: "" - property string avatarMediaUrl: user ? "image://mxc/" + replyUser.avatarMediaId : "" - - signal clearEditReplyTriggered() + property string avatarMediaUrl: user ? "image://mxc/" + user.avatarMediaId : "" active: visible sourceComponent: Pane { @@ -78,9 +76,9 @@ Loader { topPadding: 0 bottomPadding: 0 text: { - let stylesheet = "" - let userName = user ? "" + user.displayName + "" : "" - return stylesheet + content + const stylesheet = ""; + const content = ChatBoxHelper.isReplying ? ChatBoxHelper.replyEventContent : ChatBoxHelper.editContent; + return stylesheet + content; } selectByMouse: true selectByKeyboard: true @@ -101,9 +99,7 @@ Loader { icon.name: "dialog-cancel" text: i18n("Cancel") display: AbstractButton.IconOnly - onClicked: { - clearEditReplyTriggered() - } + onClicked: ChatBoxHelper.clearEditReply() ToolTip.text: text ToolTip.visible: hovered } diff --git a/imports/NeoChat/Component/Timeline/TextDelegate.qml b/imports/NeoChat/Component/Timeline/TextDelegate.qml index d6445966e..9c79348b0 100644 --- a/imports/NeoChat/Component/Timeline/TextDelegate.qml +++ b/imports/NeoChat/Component/Timeline/TextDelegate.qml @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: GPL-3.0-only */ -import QtQuick 2.12 +import QtQuick 2.15 import QtQuick.Controls 2.12 as QQC2 import QtQuick.Layouts 1.12 diff --git a/imports/NeoChat/Component/Timeline/TimelineContainer.qml b/imports/NeoChat/Component/Timeline/TimelineContainer.qml index 36c6a4dae..a77bf59b4 100644 --- a/imports/NeoChat/Component/Timeline/TimelineContainer.qml +++ b/imports/NeoChat/Component/Timeline/TimelineContainer.qml @@ -33,8 +33,6 @@ Item { signal saveFileAs() signal openExternally() signal replyClicked(string eventID) - signal replyToMessageClicked(var replyUser, string replyContent, string eventID) - signal edit(string message, string formattedBody, string eventId) property alias hovered: controlContainer.hovered @@ -49,7 +47,7 @@ Item { updateHoverComponent(); } } - + // updates the global hover component to point to this delegate, and update its position function updateHoverComponent() { hoverComponent.x = column.mapToItem(page, hoverComponentX, hoverComponentY).x; @@ -57,14 +55,14 @@ Item { hoverComponent.hovered = Qt.binding(() => controlContainer.hovered); hoverComponent.showEdit = author.id === Controller.activeConnection.localUserId && (model.eventType === "emote" || model.eventType === "message"); hoverComponent.updateFunction = updateHoverComponent; - + hoverComponent.editClicked = () => { if (hoverComponent.showEdit) { - edit(message, model.formattedBody, eventId); - } + ChatBoxHelper.edit(message, formattedBody, eventId) + } }; hoverComponent.replyClicked = () => { - replyToMessage(author, message, eventId); + ChatBoxHelper.replyToMessage(eventId, message, author); }; hoverComponent.reacted = emoji => { currentRoom.toggleReaction(eventId, emoji); @@ -85,6 +83,7 @@ Item { parent.x = 0; } } + onXChanged: if (x !== 0) { applicationWindow().pageStack.interactive = false; } else { @@ -146,7 +145,7 @@ Item { } // bubble - QQC2.Control { + QQC2.ItemDelegate { id: controlContainer Layout.maximumWidth: mainColumn.width - Kirigami.Units.gridUnit * 2 - Kirigami.Units.largeSpacing * 2 implicitHeight: contentItem.implicitHeight diff --git a/imports/NeoChat/Page/RoomPage.qml b/imports/NeoChat/Page/RoomPage.qml index ab5df0cbf..2e69284b7 100644 --- a/imports/NeoChat/Page/RoomPage.qml +++ b/imports/NeoChat/Page/RoomPage.qml @@ -255,7 +255,7 @@ Kirigami.ScrollablePage { fileDialog.chosen.connect(function(path) { if (!path) return - chatBox.attach(path) + ChatBoxHelper.attachmentPath = path; }) fileDialog.open() @@ -273,10 +273,12 @@ Kirigami.ScrollablePage { icon.name: 'insert-image' text: i18n("Clipboard image") onClicked: { - var localPath = Platform.StandardPaths.writableLocation(Platform.StandardPaths.CacheLocation) + "/screenshots/" + (new Date()).getTime() + ".png" - if (!Clipboard.saveImage(localPath)) return - chatBox.attach(localPath) - attachDialog.close() + const localPath = Platform.StandardPaths.writableLocation(Platform.StandardPaths.CacheLocation) + "/screenshots/" + (new Date()).getTime() + ".png" + if (!Clipboard.saveImage(localPath)) { + return; + } + ChatBoxHelper.attachmentPath = localPath; + attachDialog.close(); } } } @@ -356,8 +358,6 @@ Kirigami.ScrollablePage { isLoaded: timelineDelegateChooser.delegateLoaded isEmote: true onReplyClicked: goToEvent(eventID) - onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId); - onEdit: chatBox.edit(message, formattedBody, eventId) hoverComponent: hoverActions @@ -386,8 +386,6 @@ Kirigami.ScrollablePage { isLoaded: timelineDelegateChooser.delegateLoaded onReplyClicked: goToEvent(eventID) - onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId); - onEdit: chatBox.edit(message, formattedBody, eventId) hoverComponent: hoverActions @@ -413,8 +411,6 @@ Kirigami.ScrollablePage { width: messageListView.width - Kirigami.Units.largeSpacing isLoaded: timelineDelegateChooser.delegateLoaded onReplyClicked: goToEvent(eventID) - onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId); - onEdit: chatBox.edit(message, formattedBody, eventId) hoverComponent: hoverActions innerObject: TextDelegate { @@ -432,7 +428,6 @@ Kirigami.ScrollablePage { isLoaded: timelineDelegateChooser.delegateLoaded onReplyClicked: goToEvent(eventID) - onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId); hoverComponent: hoverActions @@ -453,7 +448,6 @@ Kirigami.ScrollablePage { isLoaded: timelineDelegateChooser.delegateLoaded onReplyClicked: goToEvent(eventID) - onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId); hoverComponent: hoverActions cardBackground: false @@ -476,7 +470,6 @@ Kirigami.ScrollablePage { isLoaded: timelineDelegateChooser.delegateLoaded onReplyClicked: goToEvent(eventID) - onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId); innerObject: AudioDelegate { Layout.fillWidth: true @@ -501,7 +494,6 @@ Kirigami.ScrollablePage { isLoaded: timelineDelegateChooser.delegateLoaded onReplyClicked: goToEvent(eventID) - onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId); innerObject: VideoDelegate { Layout.fillWidth: true @@ -532,7 +524,6 @@ Kirigami.ScrollablePage { isLoaded: timelineDelegateChooser.delegateLoaded onReplyClicked: goToEvent(eventID) - onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId); innerObject: FileDelegate { Layout.fillWidth: true @@ -613,7 +604,7 @@ Kirigami.ScrollablePage { DropArea { id: dropAreaFile anchors.fill: parent - onDropped: chatBox.attach(drop.urls[0]) + onDropped: ChatBoxHelper.attachmentPath = drop.urls[0] } QQC2.Pane { @@ -767,7 +758,7 @@ Kirigami.ScrollablePage { }).open(); }); contextMenu.reply.connect(function(replyUser, replyContent) { - replyToMessage(replyUser, replyContent, eventId); + ChatBoxHelper.replyToMessage(eventId, replyContent, replyUser); }) contextMenu.remove.connect(function() { currentRoom.redactEvent(eventId); @@ -788,19 +779,11 @@ Kirigami.ScrollablePage { }).open(); }); contextMenu.reply.connect(function(replyUser, replyContent) { - replyToMessage(replyUser, replyContent, eventId); + ChatBoxHelper.replyToMessage(eventId, replyContent, replyUser); }) contextMenu.remove.connect(function() { currentRoom.redactEvent(eventId); }) contextMenu.open(); } - - function replyToMessage(replyUser, replyContent, eventId) { - chatBox.editEventId = ""; - chatBox.replyUser = replyUser; - chatBox.replyEventId = eventId; - chatBox.replyContent = replyContent; - chatBox.focusInputField(); - } } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 15ade8edc..3ca23148b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,6 +22,7 @@ add_executable(neochat filetypesingleton.cpp login.cpp stickerevent.cpp + chatboxhelper.cpp ../res.qrc ) diff --git a/src/chatboxhelper.cpp b/src/chatboxhelper.cpp new file mode 100644 index 000000000..2452ae0d7 --- /dev/null +++ b/src/chatboxhelper.cpp @@ -0,0 +1,162 @@ +// SPDX-FileCopyrightText: 2020 Carl Schwan +// SPDX-License-Identifier: GPl-3.0-or-later + +#include "chatboxhelper.h" +#include + +ChatBoxHelper::ChatBoxHelper(QObject *parent) + : QObject(parent) +{ +} + +bool ChatBoxHelper::isEditing() const +{ + return !m_editEventId.isEmpty(); +} + +QString ChatBoxHelper::editEventId() const +{ + return m_editEventId; +} + +void ChatBoxHelper::setEditEventId(const QString& editEventId) +{ + if (m_editEventId == editEventId) { + return; + } + + m_editEventId = editEventId; + Q_EMIT editEventIdChanged(m_editEventId); + Q_EMIT isEditingChanged(!m_editEventId.isEmpty()); +} + +QString ChatBoxHelper::editContent() const +{ + return m_editContent; +} + +void ChatBoxHelper::setEditContent(const QString& editContent) +{ + if (m_editContent == editContent) { + return; + } + + m_editContent = editContent; + Q_EMIT editContentChanged(); +} + +QString ChatBoxHelper::replyEventId() const +{ + return m_replyEventId; +} + +void ChatBoxHelper::setReplyEventId(const QString& replyEventId) +{ + if (m_replyEventId == replyEventId) { + return; + } + + m_replyEventId = replyEventId; + Q_EMIT replyEventIdChanged(m_replyEventId); +} + +QString ChatBoxHelper::replyEventContent() const +{ + return m_replyEventContent; +} + +void ChatBoxHelper::setReplyEventContent(const QString& replyEventContent) +{ + if (m_replyEventContent == replyEventContent) { + return; + } + + m_replyEventContent = replyEventContent; + Q_EMIT replyEventContentChanged(m_replyEventContent); + Q_EMIT isReplyingChanged(!m_replyEventContent.isEmpty()); +} + +bool ChatBoxHelper::isReplying() const +{ + return !m_replyEventId.isEmpty(); +} + +QString ChatBoxHelper::attachmentPath() const +{ + return m_attachmentPath; +} + +void ChatBoxHelper::setAttachmentPath(const QString& attachmentPath) +{ + if (m_attachmentPath == attachmentPath) { + return; + } + + m_attachmentPath = attachmentPath; + Q_EMIT attachmentPathChanged(m_attachmentPath); + Q_EMIT hasAttachmentChanged(!m_attachmentPath.isEmpty()); +} + +bool ChatBoxHelper::hasAttachment() const +{ + return !m_attachmentPath.isEmpty(); +} + +void ChatBoxHelper::replyToMessage(const QString &replyEventId, const QString &replyEvent, const QVariant &replyUser) +{ + setEditEventId(QString()); + setEditContent(QString()); + setReplyEventId(replyEventId); + setReplyEventContent(replyEvent); + setReplyUser(replyUser); +} + +QVariant ChatBoxHelper::replyUser() const +{ + return m_replyUser; +} + +void ChatBoxHelper::setReplyUser(const QVariant &replyUser) +{ + if (m_replyUser == replyUser) { + return; + } + m_replyUser = replyUser; + Q_EMIT replyUserChanged(); +} + +void ChatBoxHelper::clear() +{ + setEditEventId(QString()); + setEditContent(QString()); + setReplyEventId(QString()); + setReplyEventContent(QString()); + setAttachmentPath(QString()); + setReplyUser(QVariant()); +} + +void ChatBoxHelper::edit(const QString& message, const QString& formattedBody, const QString& eventId) +{ + setEditEventId(eventId); + setEditContent(message); + Q_EMIT editing(message, formattedBody); +} + +void ChatBoxHelper::clearEditReply() +{ + setEditEventId(QString()); + setEditContent(QString()); + setReplyEventId(QString()); + setReplyEventContent(QString()); + setReplyUser(QVariant()); + Q_EMIT shouldClearText(); +} + +void ChatBoxHelper::clearAttachment() +{ + setAttachmentPath(QString()); +} + + + + diff --git a/src/chatboxhelper.h b/src/chatboxhelper.h new file mode 100644 index 000000000..55fc44600 --- /dev/null +++ b/src/chatboxhelper.h @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2020 Carl Schwan +// SPDX-License-Identifier: GPl-3.0-or-later + +#pragma once + +#include +#include + +/// Helper singleton for keeping the chatbar state in sync in the application. +class ChatBoxHelper : public QObject +{ + Q_OBJECT + /// True, iff the user is currently editing one of their previous message. + Q_PROPERTY(bool isEditing READ isEditing NOTIFY isEditingChanged) + Q_PROPERTY(QString editEventId READ editEventId WRITE setEditEventId NOTIFY editEventIdChanged) + Q_PROPERTY(QString editContent READ editContent WRITE setEditContent NOTIFY editContentChanged) + + Q_PROPERTY(bool isReplying READ isReplying NOTIFY isReplyingChanged) + Q_PROPERTY(QString replyEventId READ replyEventId WRITE setReplyEventId NOTIFY replyEventIdChanged) + Q_PROPERTY(QString replyEventContent READ replyEventContent WRITE setReplyEventContent NOTIFY replyEventContentChanged) + Q_PROPERTY(QVariant replyUser READ replyUser WRITE setReplyUser NOTIFY replyUserChanged) + + Q_PROPERTY(QString attachmentPath READ attachmentPath WRITE setAttachmentPath NOTIFY attachmentPathChanged) + Q_PROPERTY(bool hasAttachment READ hasAttachment NOTIFY hasAttachmentChanged) + +public: + ChatBoxHelper(QObject *parent = nullptr); + ~ChatBoxHelper() = default; + + bool isEditing() const; + QString editEventId() const; + QString editContent() const; + + QString replyEventId() const; + QString replyEventContent() const; + QVariant replyUser() const; + bool isReplying() const; + + QString attachmentPath() const; + bool hasAttachment() const; + + void setEditEventId(const QString& editEventId); + void setEditContent(const QString& editContent); + void setReplyEventId(const QString& replyEventId); + void setReplyEventContent(const QString& replyEventContent); + void setAttachmentPath(const QString& attachmentPath); + void setReplyUser(const QVariant &replyUser); + + Q_INVOKABLE void replyToMessage(const QString &replyEventid, const QString &replyEvent, const QVariant &replyUser); + Q_INVOKABLE void edit(const QString &message, const QString &formattedBody, const QString &eventId); + Q_INVOKABLE void clear(); + Q_INVOKABLE void clearEditReply(); + Q_INVOKABLE void clearAttachment(); + +Q_SIGNALS: + void isEditingChanged(bool isEditing); + void editEventIdChanged(const QString& editEventId); + void editContentChanged(); + void replyEventIdChanged(const QString& replyEventId); + void replyEventContentChanged(const QString& replyEventContent); + void replyUserChanged(); + void isReplyingChanged(bool isReplying); + void attachmentPathChanged(const QString& attachmentPath); + void hasAttachmentChanged(bool hasAttachment); + void editing(const QString &message, const QString &formattedBody); + void shouldClearText(); + +private: + QString m_editEventId; + QString m_editContent; + QString m_replyEventId; + QString m_replyEventContent; + QVariant m_replyUser; + QString m_attachmentPath; +}; diff --git a/src/main.cpp b/src/main.cpp index e84c0a89c..bf8fb9a96 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -47,6 +47,7 @@ #include "userdirectorylistmodel.h" #include "userlistmodel.h" #include "actionshandler.h" +#include "chatboxhelper.h" using namespace Quotient; @@ -108,12 +109,14 @@ int main(int argc, char *argv[]) FileTypeSingleton fileTypeSingleton; Login *login = new Login(); + ChatBoxHelper chatBoxHelper; qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Controller", &Controller::instance()); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Clipboard", &clipboard); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Config", config); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "FileType", &fileTypeSingleton); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "LoginHelper", login); + qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "ChatBoxHelper", &chatBoxHelper); qmlRegisterType("org.kde.neochat", 1, 0, "AccountListModel"); qmlRegisterType("org.kde.neochat", 1, 0, "ActionsHandler"); qmlRegisterType("org.kde.neochat", 1, 0, "ChatDocumentHandler");