Use singleton to pass edit/reply content to chatbox

This significantly reduce the complexity of everything.
This commit is contained in:
Carl Schwan
2021-03-20 14:00:29 +00:00
parent c7df3f903a
commit 743c9972b9
11 changed files with 315 additions and 163 deletions

View File

@@ -14,17 +14,14 @@ import org.kde.neochat 1.0
Loader { Loader {
id: root id: root
property string attachmentPath: "" property var attachmentMimetype: FileType.mimeTypeForUrl(ChatBoxHelper.attachmentPath)
property var attachmentMimetype: FileType.mimeTypeForUrl(attachmentPath)
readonly property bool hasImage: attachmentMimetype.valid && FileType.supportedImageFormats.includes(attachmentMimetype.preferredSuffix) readonly property bool hasImage: attachmentMimetype.valid && FileType.supportedImageFormats.includes(attachmentMimetype.preferredSuffix)
signal clearAttachmentTriggered()
active: visible active: visible
sourceComponent: Component { sourceComponent: Component {
Pane { Pane {
id: attachmentPane 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 Kirigami.Theme.colorSet: Kirigami.Theme.View
contentItem: Item { contentItem: Item {
@@ -49,7 +46,7 @@ Loader {
asynchronous: true asynchronous: true
cache: false // Cache is not needed. Images will rarely be shown repeatedly. 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 smooth: height == preferredHeight && parent.height == parent.implicitHeight // Don't smooth until height animation stops
source: hasImage ? attachmentPath : "" source: hasImage ? ChatBoxHelper.attachmentPath : ""
visible: hasImage visible: hasImage
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
@@ -165,14 +162,14 @@ Loader {
Component { Component {
id: imageEditorPage id: imageEditorPage
ImageEditorPage { ImageEditorPage {
imagePath: attachmentPath imagePath: ChatBoxHelper.attachmentPath
} }
} }
onClicked: { onClicked: {
let imageEditor = applicationWindow().pageStack.layers.push(imageEditorPage); let imageEditor = applicationWindow().pageStack.layers.push(imageEditorPage);
imageEditor.newPathChanged.connect(function(newPath) { imageEditor.newPathChanged.connect(function(newPath) {
applicationWindow().pageStack.layers.pop(); applicationWindow().pageStack.layers.pop();
attachmentPath = newPath; ChatBoxHelper.attachmentPath = newPath;
}); });
} }
ToolTip.text: text ToolTip.text: text
@@ -183,9 +180,7 @@ Loader {
icon.name: "dialog-cancel" icon.name: "dialog-cancel"
text: i18n("Cancel") text: i18n("Cancel")
display: AbstractButton.IconOnly display: AbstractButton.IconOnly
onClicked: { onClicked: ChatBoxHelper.clearAttachment();
clearAttachmentTriggered();
}
ToolTip.text: text ToolTip.text: text
ToolTip.visible: hovered ToolTip.visible: hovered
} }

View File

@@ -15,7 +15,7 @@ ToolBar {
id: chatBar id: chatBar
property string replyEventId: "" property string replyEventId: ""
property string editEventId: "" property string editEventId: ""
property string inputFieldText: currentRoom ? currentRoom.cachedInput : "" property alias inputFieldText: inputField.text
property alias textField: inputField property alias textField: inputField
property alias emojiPaneOpened: emojiButton.checked 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 // This use an hack to define: https://doc.qt.io/qt-5/qml-var.html#property-value-initialization-semantics
property var userAutocompleted: ({}) property var userAutocompleted: ({})
signal attachTriggered(string localPath)
signal closeAllTriggered() signal closeAllTriggered()
signal inputFieldForceActiveFocusTriggered() signal inputFieldForceActiveFocusTriggered()
signal messageSent() signal messageSent()
@@ -220,14 +219,14 @@ ToolBar {
} }
Item { Item {
visible: !isReply && (!hasAttachment || uploadingBusySpinner.running) visible: !ChatBoxHelper.isReplying && (!ChatBoxHelper.hasAttachment || uploadingBusySpinner.running)
implicitWidth: uploadButton.implicitWidth implicitWidth: uploadButton.implicitWidth
implicitHeight: uploadButton.implicitHeight implicitHeight: uploadButton.implicitHeight
ToolButton { ToolButton {
id: uploadButton id: uploadButton
anchors.fill: parent anchors.fill: parent
// Matrix does not allow sending attachments in replies // Matrix does not allow sending attachments in replies
visible: !isReply && !hasAttachment && !uploadingBusySpinner.running visible: !ChatBoxHelper.isReplying && !ChatBoxHelper.hasAttachment && !uploadingBusySpinner.running
icon.name: "mail-attachment" icon.name: "mail-attachment"
text: i18n("Attach an image or file") text: i18n("Attach an image or file")
display: AbstractButton.IconOnly display: AbstractButton.IconOnly
@@ -239,7 +238,7 @@ ToolBar {
var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay) var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay)
fileDialog.chosen.connect((path) => { fileDialog.chosen.connect((path) => {
if (!path) { return } if (!path) { return }
attachTriggered(path) ChatBoxHelper.attachmentPath = path;
}) })
fileDialog.open() fileDialog.open()
} }
@@ -318,13 +317,21 @@ ToolBar {
if (!Clipboard.saveImage(localPath)) { if (!Clipboard.saveImage(localPath)) {
return; return;
} }
attachTriggered(localPath) ChatBoxHelper.attachmentPath = localPath;
} }
function postMessage() { function postMessage() {
checkForFancyEffectsReason(); checkForFancyEffectsReason();
roomManager.actionsHandler.postMessage(inputField.text.trim(), attachmentPath, if (ChatBoxHelper.hasAttachment) {
replyEventId, editEventId, userAutocompleted); // 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(); currentRoom.markAllMessagesAsRead();
inputField.clear(); inputField.clear();
inputField.text = Qt.binding(function() { inputField.text = Qt.binding(function() {

View File

@@ -13,19 +13,8 @@ import org.kde.neochat 1.0
Item { Item {
id: root 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 property alias inputFieldText: chatBar.inputFieldText
readonly property bool isEdit: editEventId.length > 0
property alias editEventId: chatBar.editEventId
signal fancyEffectsReasonFound(string fancyEffect) signal fancyEffectsReasonFound(string fancyEffect)
Kirigami.Theme.colorSet: Kirigami.Theme.View Kirigami.Theme.colorSet: Kirigami.Theme.View
@@ -76,6 +65,7 @@ Item {
sourceComponent: EmojiPicker{ sourceComponent: EmojiPicker{
textArea: chatBar.textField textArea: chatBar.textField
emojiModel: EmojiModel { id: emojiModel } emojiModel: EmojiModel { id: emojiModel }
onChosen: addText(emoji)
} }
Behavior on height { Behavior on height {
NumberAnimation { NumberAnimation {
@@ -97,10 +87,8 @@ Item {
ReplyPane { ReplyPane {
id: replyPane id: replyPane
visible: isReply || isEdit visible: ChatBoxHelper.isReplying || ChatBoxHelper.isEditing
isEdit: root.isEdit user: ChatBoxHelper.replyUser
user: root.replyUser
content: root.replyContent
width: parent.width width: parent.width
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
anchors.bottom: attachmentSeparator.top anchors.bottom: attachmentSeparator.top
@@ -123,8 +111,7 @@ Item {
AttachmentPane { AttachmentPane {
id: attachmentPane id: attachmentPane
attachmentPath: root.attachmentPath visible: ChatBoxHelper.hasAttachment
visible: hasAttachment
width: parent.width width: parent.width
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
anchors.bottom: chatBarSeparator.top anchors.bottom: chatBarSeparator.top
@@ -159,36 +146,9 @@ Item {
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
} }
}
Connections { onCloseAllTriggered: closeAll()
target: replyPane onMessageSent: {
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() {
closeAll() closeAll()
checkForFancyEffectsReason() checkForFancyEffectsReason()
} }
@@ -225,70 +185,41 @@ Item {
root.inputFieldText = inputFieldText.substr(0, inputField.cursorPosition) + str + inputFieldText.substr(inputField.cursorPosition) 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() { function focusInputField() {
chatBar.inputFieldForceActiveFocusTriggered() chatBar.inputFieldForceActiveFocusTriggered()
} }
function edit(editContent, editFormatedContent, editEventId) { Connections {
// Set the input field in edit mode target: ChatBoxHelper
root.inputFieldText = editContent;
root.editEventId = editEventId;
root.replyContent = editContent;
// clean autocompletion list function onShouldClearText() {
chatBar.userAutocompleted = {}; root.inputFieldText = "";
// 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 href=\"https:\/\/matrix.to\/#\/(@[a-zA-Z09]*:[a-zA-Z09.]*)\">([^<]*)<\/a>/g;
let match;
while ((match = regex.exec(editFormatedContent.toString())) !== null) {
chatBar.userAutocompleted[match[2]] = match[1];
} }
}
function clearEdit() { function onEditing(editContent, editFormatedContent) {
// Clear input when edits are cancelled. // Set the input field in edit mode
// Cached input will be root.inputFieldText = editContent;
clearText() //root.replyContent = editContent;
clearReply()
root.editEventId = "";
}
function attach(localPath) { // clean autocompletion list
root.attachmentPath = localPath chatBar.userAutocompleted = {};
}
function clearAttachment() { // Fill autocompletion list with values extracted from message.
root.attachmentPath = "" // 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 href=\"https:\/\/matrix.to\/#\/(@[a-zA-Z09]*:[a-zA-Z09.]*)\">([^<]*)<\/a>/g;
function clearReply() { let match;
replyUser = null; while ((match = regex.exec(editFormatedContent.toString())) !== null) {
root.replyContent = ""; chatBar.userAutocompleted[match[2]] = match[1];
root.replyEventId = ""; }
// Don't clear input when replies are cancelled }
} }
function closeAll() { function closeAll() {
if (hasAttachment) { ChatBoxHelper.clear();
clearAttachment();
}
if (isEdit) {
clearEdit();
}
if (isReply) {
clearReply();
}
chatBar.emojiPaneOpened = false; chatBar.emojiPaneOpened = false;
} }
} }

View File

@@ -7,15 +7,13 @@ import QtQuick 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import org.kde.kirigami 2.14 as Kirigami import org.kde.kirigami 2.14 as Kirigami
import org.kde.neochat 1.0
Loader { Loader {
id: root id: root
property bool isEdit: false readonly property bool isEdit: ChatBoxHelper.isEditing
property var user: null property var user: null
property string content: "" property string avatarMediaUrl: user ? "image://mxc/" + user.avatarMediaId : ""
property string avatarMediaUrl: user ? "image://mxc/" + replyUser.avatarMediaId : ""
signal clearEditReplyTriggered()
active: visible active: visible
sourceComponent: Pane { sourceComponent: Pane {
@@ -78,9 +76,9 @@ Loader {
topPadding: 0 topPadding: 0
bottomPadding: 0 bottomPadding: 0
text: { text: {
let stylesheet = "<style> a{color:"+Kirigami.Theme.linkColor+";}.user-pill{}</style>" const stylesheet = "<style> a{color:"+Kirigami.Theme.linkColor+";}.user-pill{}</style>";
let userName = user ? "<font color=\""+ user.color +"\">" + user.displayName + "</font>" : "" const content = ChatBoxHelper.isReplying ? ChatBoxHelper.replyEventContent : ChatBoxHelper.editContent;
return stylesheet + content return stylesheet + content;
} }
selectByMouse: true selectByMouse: true
selectByKeyboard: true selectByKeyboard: true
@@ -101,9 +99,7 @@ Loader {
icon.name: "dialog-cancel" icon.name: "dialog-cancel"
text: i18n("Cancel") text: i18n("Cancel")
display: AbstractButton.IconOnly display: AbstractButton.IconOnly
onClicked: { onClicked: ChatBoxHelper.clearEditReply()
clearEditReplyTriggered()
}
ToolTip.text: text ToolTip.text: text
ToolTip.visible: hovered ToolTip.visible: hovered
} }

View File

@@ -3,7 +3,7 @@
* *
* SPDX-License-Identifier: GPL-3.0-only * SPDX-License-Identifier: GPL-3.0-only
*/ */
import QtQuick 2.12 import QtQuick 2.15
import QtQuick.Controls 2.12 as QQC2 import QtQuick.Controls 2.12 as QQC2
import QtQuick.Layouts 1.12 import QtQuick.Layouts 1.12

View File

@@ -33,8 +33,6 @@ Item {
signal saveFileAs() signal saveFileAs()
signal openExternally() signal openExternally()
signal replyClicked(string eventID) 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 property alias hovered: controlContainer.hovered
@@ -49,7 +47,7 @@ Item {
updateHoverComponent(); updateHoverComponent();
} }
} }
// updates the global hover component to point to this delegate, and update its position // updates the global hover component to point to this delegate, and update its position
function updateHoverComponent() { function updateHoverComponent() {
hoverComponent.x = column.mapToItem(page, hoverComponentX, hoverComponentY).x; hoverComponent.x = column.mapToItem(page, hoverComponentX, hoverComponentY).x;
@@ -57,14 +55,14 @@ Item {
hoverComponent.hovered = Qt.binding(() => controlContainer.hovered); hoverComponent.hovered = Qt.binding(() => controlContainer.hovered);
hoverComponent.showEdit = author.id === Controller.activeConnection.localUserId && (model.eventType === "emote" || model.eventType === "message"); hoverComponent.showEdit = author.id === Controller.activeConnection.localUserId && (model.eventType === "emote" || model.eventType === "message");
hoverComponent.updateFunction = updateHoverComponent; hoverComponent.updateFunction = updateHoverComponent;
hoverComponent.editClicked = () => { hoverComponent.editClicked = () => {
if (hoverComponent.showEdit) { if (hoverComponent.showEdit) {
edit(message, model.formattedBody, eventId); ChatBoxHelper.edit(message, formattedBody, eventId)
} }
}; };
hoverComponent.replyClicked = () => { hoverComponent.replyClicked = () => {
replyToMessage(author, message, eventId); ChatBoxHelper.replyToMessage(eventId, message, author);
}; };
hoverComponent.reacted = emoji => { hoverComponent.reacted = emoji => {
currentRoom.toggleReaction(eventId, emoji); currentRoom.toggleReaction(eventId, emoji);
@@ -85,6 +83,7 @@ Item {
parent.x = 0; parent.x = 0;
} }
} }
onXChanged: if (x !== 0) { onXChanged: if (x !== 0) {
applicationWindow().pageStack.interactive = false; applicationWindow().pageStack.interactive = false;
} else { } else {
@@ -146,7 +145,7 @@ Item {
} }
// bubble // bubble
QQC2.Control { QQC2.ItemDelegate {
id: controlContainer id: controlContainer
Layout.maximumWidth: mainColumn.width - Kirigami.Units.gridUnit * 2 - Kirigami.Units.largeSpacing * 2 Layout.maximumWidth: mainColumn.width - Kirigami.Units.gridUnit * 2 - Kirigami.Units.largeSpacing * 2
implicitHeight: contentItem.implicitHeight implicitHeight: contentItem.implicitHeight

View File

@@ -255,7 +255,7 @@ Kirigami.ScrollablePage {
fileDialog.chosen.connect(function(path) { fileDialog.chosen.connect(function(path) {
if (!path) return if (!path) return
chatBox.attach(path) ChatBoxHelper.attachmentPath = path;
}) })
fileDialog.open() fileDialog.open()
@@ -273,10 +273,12 @@ Kirigami.ScrollablePage {
icon.name: 'insert-image' icon.name: 'insert-image'
text: i18n("Clipboard image") text: i18n("Clipboard image")
onClicked: { onClicked: {
var localPath = Platform.StandardPaths.writableLocation(Platform.StandardPaths.CacheLocation) + "/screenshots/" + (new Date()).getTime() + ".png" const localPath = Platform.StandardPaths.writableLocation(Platform.StandardPaths.CacheLocation) + "/screenshots/" + (new Date()).getTime() + ".png"
if (!Clipboard.saveImage(localPath)) return if (!Clipboard.saveImage(localPath)) {
chatBox.attach(localPath) return;
attachDialog.close() }
ChatBoxHelper.attachmentPath = localPath;
attachDialog.close();
} }
} }
} }
@@ -356,8 +358,6 @@ Kirigami.ScrollablePage {
isLoaded: timelineDelegateChooser.delegateLoaded isLoaded: timelineDelegateChooser.delegateLoaded
isEmote: true isEmote: true
onReplyClicked: goToEvent(eventID) onReplyClicked: goToEvent(eventID)
onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId);
onEdit: chatBox.edit(message, formattedBody, eventId)
hoverComponent: hoverActions hoverComponent: hoverActions
@@ -386,8 +386,6 @@ Kirigami.ScrollablePage {
isLoaded: timelineDelegateChooser.delegateLoaded isLoaded: timelineDelegateChooser.delegateLoaded
onReplyClicked: goToEvent(eventID) onReplyClicked: goToEvent(eventID)
onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId);
onEdit: chatBox.edit(message, formattedBody, eventId)
hoverComponent: hoverActions hoverComponent: hoverActions
@@ -413,8 +411,6 @@ Kirigami.ScrollablePage {
width: messageListView.width - Kirigami.Units.largeSpacing width: messageListView.width - Kirigami.Units.largeSpacing
isLoaded: timelineDelegateChooser.delegateLoaded isLoaded: timelineDelegateChooser.delegateLoaded
onReplyClicked: goToEvent(eventID) onReplyClicked: goToEvent(eventID)
onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId);
onEdit: chatBox.edit(message, formattedBody, eventId)
hoverComponent: hoverActions hoverComponent: hoverActions
innerObject: TextDelegate { innerObject: TextDelegate {
@@ -432,7 +428,6 @@ Kirigami.ScrollablePage {
isLoaded: timelineDelegateChooser.delegateLoaded isLoaded: timelineDelegateChooser.delegateLoaded
onReplyClicked: goToEvent(eventID) onReplyClicked: goToEvent(eventID)
onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId);
hoverComponent: hoverActions hoverComponent: hoverActions
@@ -453,7 +448,6 @@ Kirigami.ScrollablePage {
isLoaded: timelineDelegateChooser.delegateLoaded isLoaded: timelineDelegateChooser.delegateLoaded
onReplyClicked: goToEvent(eventID) onReplyClicked: goToEvent(eventID)
onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId);
hoverComponent: hoverActions hoverComponent: hoverActions
cardBackground: false cardBackground: false
@@ -476,7 +470,6 @@ Kirigami.ScrollablePage {
isLoaded: timelineDelegateChooser.delegateLoaded isLoaded: timelineDelegateChooser.delegateLoaded
onReplyClicked: goToEvent(eventID) onReplyClicked: goToEvent(eventID)
onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId);
innerObject: AudioDelegate { innerObject: AudioDelegate {
Layout.fillWidth: true Layout.fillWidth: true
@@ -501,7 +494,6 @@ Kirigami.ScrollablePage {
isLoaded: timelineDelegateChooser.delegateLoaded isLoaded: timelineDelegateChooser.delegateLoaded
onReplyClicked: goToEvent(eventID) onReplyClicked: goToEvent(eventID)
onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId);
innerObject: VideoDelegate { innerObject: VideoDelegate {
Layout.fillWidth: true Layout.fillWidth: true
@@ -532,7 +524,6 @@ Kirigami.ScrollablePage {
isLoaded: timelineDelegateChooser.delegateLoaded isLoaded: timelineDelegateChooser.delegateLoaded
onReplyClicked: goToEvent(eventID) onReplyClicked: goToEvent(eventID)
onReplyToMessageClicked: replyToMessage(replyUser, replyContent, eventId);
innerObject: FileDelegate { innerObject: FileDelegate {
Layout.fillWidth: true Layout.fillWidth: true
@@ -613,7 +604,7 @@ Kirigami.ScrollablePage {
DropArea { DropArea {
id: dropAreaFile id: dropAreaFile
anchors.fill: parent anchors.fill: parent
onDropped: chatBox.attach(drop.urls[0]) onDropped: ChatBoxHelper.attachmentPath = drop.urls[0]
} }
QQC2.Pane { QQC2.Pane {
@@ -767,7 +758,7 @@ Kirigami.ScrollablePage {
}).open(); }).open();
}); });
contextMenu.reply.connect(function(replyUser, replyContent) { contextMenu.reply.connect(function(replyUser, replyContent) {
replyToMessage(replyUser, replyContent, eventId); ChatBoxHelper.replyToMessage(eventId, replyContent, replyUser);
}) })
contextMenu.remove.connect(function() { contextMenu.remove.connect(function() {
currentRoom.redactEvent(eventId); currentRoom.redactEvent(eventId);
@@ -788,19 +779,11 @@ Kirigami.ScrollablePage {
}).open(); }).open();
}); });
contextMenu.reply.connect(function(replyUser, replyContent) { contextMenu.reply.connect(function(replyUser, replyContent) {
replyToMessage(replyUser, replyContent, eventId); ChatBoxHelper.replyToMessage(eventId, replyContent, replyUser);
}) })
contextMenu.remove.connect(function() { contextMenu.remove.connect(function() {
currentRoom.redactEvent(eventId); currentRoom.redactEvent(eventId);
}) })
contextMenu.open(); contextMenu.open();
} }
function replyToMessage(replyUser, replyContent, eventId) {
chatBox.editEventId = "";
chatBox.replyUser = replyUser;
chatBox.replyEventId = eventId;
chatBox.replyContent = replyContent;
chatBox.focusInputField();
}
} }

View File

@@ -22,6 +22,7 @@ add_executable(neochat
filetypesingleton.cpp filetypesingleton.cpp
login.cpp login.cpp
stickerevent.cpp stickerevent.cpp
chatboxhelper.cpp
../res.qrc ../res.qrc
) )

162
src/chatboxhelper.cpp Normal file
View File

@@ -0,0 +1,162 @@
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: GPl-3.0-or-later
#include "chatboxhelper.h"
#include <QDebug>
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());
}

75
src/chatboxhelper.h Normal file
View File

@@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: GPl-3.0-or-later
#pragma once
#include <QObject>
#include <QVariant>
/// 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;
};

View File

@@ -47,6 +47,7 @@
#include "userdirectorylistmodel.h" #include "userdirectorylistmodel.h"
#include "userlistmodel.h" #include "userlistmodel.h"
#include "actionshandler.h" #include "actionshandler.h"
#include "chatboxhelper.h"
using namespace Quotient; using namespace Quotient;
@@ -108,12 +109,14 @@ int main(int argc, char *argv[])
FileTypeSingleton fileTypeSingleton; FileTypeSingleton fileTypeSingleton;
Login *login = new Login(); Login *login = new Login();
ChatBoxHelper chatBoxHelper;
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Controller", &Controller::instance()); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Controller", &Controller::instance());
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Clipboard", &clipboard); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Clipboard", &clipboard);
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Config", config); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Config", config);
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "FileType", &fileTypeSingleton); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "FileType", &fileTypeSingleton);
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "LoginHelper", login); qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "LoginHelper", login);
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "ChatBoxHelper", &chatBoxHelper);
qmlRegisterType<AccountListModel>("org.kde.neochat", 1, 0, "AccountListModel"); qmlRegisterType<AccountListModel>("org.kde.neochat", 1, 0, "AccountListModel");
qmlRegisterType<ActionsHandler>("org.kde.neochat", 1, 0, "ActionsHandler"); qmlRegisterType<ActionsHandler>("org.kde.neochat", 1, 0, "ActionsHandler");
qmlRegisterType<ChatDocumentHandler>("org.kde.neochat", 1, 0, "ChatDocumentHandler"); qmlRegisterType<ChatDocumentHandler>("org.kde.neochat", 1, 0, "ChatDocumentHandler");