ChatCache
Move the functionality to cache the contents of a chat bar from the room directly and to a new ChatCache object. This works pretty much the same with a few extra check and balances, this also made it easy to put a test suite around the functionality so I did. The current functionality should be identical to what exists. This is in prep for threads which will require managing even more caches if we create one per thread.
This commit is contained in:
@@ -41,3 +41,7 @@ License: CC0-1.0
|
||||
Files: .flatpak-manifest.json
|
||||
Copyright: 2020-2022 Tobias Fella <tobias.fella@kde.org>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: autotests/data/*
|
||||
Copyright: none
|
||||
License: CC0-1.0
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_definitions(-DDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" )
|
||||
|
||||
ecm_add_test(
|
||||
neochatroomtest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
@@ -32,3 +34,9 @@ ecm_add_test(
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME eventhandlertest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
chatbarcachetest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME chatbarcachetest
|
||||
)
|
||||
|
||||
157
autotests/chatbarcachetest.cpp
Normal file
157
autotests/chatbarcachetest.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
|
||||
#include <Quotient/syncdata.h>
|
||||
#include <qtestcase.h>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class TestRoom : public NeoChatRoom
|
||||
{
|
||||
public:
|
||||
using NeoChatRoom::NeoChatRoom;
|
||||
|
||||
void update(SyncRoomData &&data, bool fromCache = false)
|
||||
{
|
||||
Room::updateData(std::move(data), fromCache);
|
||||
}
|
||||
};
|
||||
|
||||
class ChatBarCacheTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
TestRoom *room = nullptr;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void empty();
|
||||
void noRoom();
|
||||
void badParent();
|
||||
void reply();
|
||||
void edit();
|
||||
void attachment();
|
||||
};
|
||||
|
||||
void ChatBarCacheTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||
|
||||
QFile testMinSyncFile;
|
||||
testMinSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-min-sync.json"));
|
||||
testMinSyncFile.open(QIODevice::ReadOnly);
|
||||
const auto testMinSyncJson = QJsonDocument::fromJson(testMinSyncFile.readAll());
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testMinSyncJson.object());
|
||||
room->update(std::move(roomData));
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::empty()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||
|
||||
QCOMPARE(chatBarCache->text(), QString());
|
||||
QCOMPARE(chatBarCache->isReplying(), false);
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(nullptr));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::noRoom()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache());
|
||||
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
|
||||
|
||||
// These should return empty even though a reply ID has been set because the
|
||||
// ChatBarCache has no parent.
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationUser(), QVariantMap());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::badParent()
|
||||
{
|
||||
QScopedPointer<QObject> badParent(new QObject());
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(badParent.get()));
|
||||
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
|
||||
|
||||
// These should return empty even though a reply ID has been set because the
|
||||
// ChatBarCache has no parent.
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationUser(), QVariantMap());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::reply()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||
chatBarCache->setText(QLatin1String("some text"));
|
||||
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
|
||||
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
|
||||
|
||||
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
|
||||
QCOMPARE(chatBarCache->isReplying(), true);
|
||||
QCOMPARE(chatBarCache->replyId(), QLatin1String("$153456789:example.org"));
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(room->user(QLatin1String("@example:example.org"))));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QLatin1String("This is an example\ntext message"));
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::edit()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||
chatBarCache->setText(QLatin1String("some text"));
|
||||
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
|
||||
chatBarCache->setEditId(QLatin1String("$153456789:example.org"));
|
||||
|
||||
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
|
||||
QCOMPARE(chatBarCache->isReplying(), false);
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), true);
|
||||
QCOMPARE(chatBarCache->editId(), QLatin1String("$153456789:example.org"));
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(room->user(QLatin1String("@example:example.org"))));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QLatin1String("This is an example\ntext message"));
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::attachment()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||
chatBarCache->setText(QLatin1String("some text"));
|
||||
chatBarCache->setEditId(QLatin1String("$153456789:example.org"));
|
||||
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
|
||||
|
||||
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
|
||||
QCOMPARE(chatBarCache->isReplying(), false);
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(nullptr));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QLatin1String("some/path"));
|
||||
}
|
||||
|
||||
QTEST_MAIN(ChatBarCacheTest)
|
||||
#include "chatbarcachetest.moc"
|
||||
87
autotests/data/test-min-sync.json
Normal file
87
autotests/data/test-min-sync.json
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"account_data": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"tags": {
|
||||
"u.work": {
|
||||
"order": 0.9
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.tag"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"custom_config_key": "custom_config_value"
|
||||
},
|
||||
"type": "org.example.custom.room.config"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ephemeral": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"user_ids": [
|
||||
"@alice:matrix.org",
|
||||
"@bob:example.com"
|
||||
]
|
||||
},
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"type": "m.typing"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Alice Margatroid",
|
||||
"membership": "join",
|
||||
"reason": "Looking for support"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"m.heroes": [
|
||||
"@alice:example.com",
|
||||
"@bob:example.com"
|
||||
],
|
||||
"m.invited_member_count": 0,
|
||||
"m.joined_member_count": 2
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an example\ntext message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example<br>text message</b>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$153456789:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
}
|
||||
@@ -135,6 +135,8 @@ add_library(neochat STATIC
|
||||
enums/delegatetype.h
|
||||
roomlastmessageprovider.cpp
|
||||
roomlastmessageprovider.h
|
||||
chatbarcache.cpp
|
||||
chatbarcache.h
|
||||
)
|
||||
|
||||
qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
|
||||
@@ -38,45 +38,33 @@ void ActionsHandler::setRoom(NeoChatRoom *room)
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
void ActionsHandler::handleNewMessage()
|
||||
void ActionsHandler::handleMessageEvent(ChatBarCache *chatBarCache)
|
||||
{
|
||||
checkEffects(m_room->chatBoxText());
|
||||
if (!m_room->chatBoxAttachmentPath().isEmpty()) {
|
||||
QUrl url(m_room->chatBoxAttachmentPath());
|
||||
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
|
||||
m_room->uploadFile(QUrl(path), m_room->chatBoxText().isEmpty() ? path.mid(path.lastIndexOf(u'/') + 1) : m_room->chatBoxText());
|
||||
m_room->setChatBoxAttachmentPath({});
|
||||
m_room->setChatBoxText({});
|
||||
if (!chatBarCache) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString handledText = m_room->chatBoxText();
|
||||
handledText = handleMentions(handledText);
|
||||
handleMessage(m_room->chatBoxText(), handledText);
|
||||
checkEffects(chatBarCache->text());
|
||||
if (!chatBarCache->attachmentPath().isEmpty()) {
|
||||
QUrl url(chatBarCache->attachmentPath());
|
||||
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
|
||||
m_room->uploadFile(QUrl(path), chatBarCache->text().isEmpty() ? path.mid(path.lastIndexOf(u'/') + 1) : chatBarCache->text());
|
||||
chatBarCache->setAttachmentPath({});
|
||||
chatBarCache->setText({});
|
||||
return;
|
||||
}
|
||||
|
||||
QString handledText = chatBarCache->text();
|
||||
handledText = handleMentions(handledText, chatBarCache->mentions());
|
||||
handleMessage(m_room->mainCache()->text(), handledText, chatBarCache);
|
||||
}
|
||||
|
||||
void ActionsHandler::handleEdit()
|
||||
{
|
||||
checkEffects(m_room->editText());
|
||||
|
||||
QString handledText = m_room->editText();
|
||||
handledText = handleMentions(handledText, true);
|
||||
handleMessage(m_room->editText(), handledText, true);
|
||||
}
|
||||
|
||||
QString ActionsHandler::handleMentions(QString handledText, const bool &isEdit)
|
||||
QString ActionsHandler::handleMentions(QString handledText, QVector<Mention> *mentions)
|
||||
{
|
||||
if (!m_room) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
QVector<Mention> *mentions;
|
||||
if (isEdit) {
|
||||
mentions = m_room->editMentions();
|
||||
} else {
|
||||
mentions = m_room->mentions();
|
||||
}
|
||||
|
||||
std::sort(mentions->begin(), mentions->end(), [](const auto &a, const auto &b) -> bool {
|
||||
return a.cursor.anchor() > b.cursor.anchor();
|
||||
});
|
||||
@@ -94,7 +82,7 @@ QString ActionsHandler::handleMentions(QString handledText, const bool &isEdit)
|
||||
return handledText;
|
||||
}
|
||||
|
||||
void ActionsHandler::handleMessage(const QString &text, QString handledText, const bool &isEdit)
|
||||
void ActionsHandler::handleMessage(const QString &text, QString handledText, ChatBarCache *chatBarCache)
|
||||
{
|
||||
if (NeoChatConfig::allowQuickEdit()) {
|
||||
QRegularExpression sed(QStringLiteral("^s/([^/]*)/([^/]*)(/g)?$"));
|
||||
@@ -134,7 +122,7 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, con
|
||||
for (const auto &action : ActionsModel::instance().allActions()) {
|
||||
if (handledText.indexOf(action.prefix) == 1
|
||||
&& (handledText.indexOf(" "_ls) == action.prefix.length() + 1 || handledText.length() == action.prefix.length() + 1)) {
|
||||
handledText = action.handle(handledText.mid(action.prefix.length() + 1).trimmed(), m_room);
|
||||
handledText = action.handle(handledText.mid(action.prefix.length() + 1).trimmed(), m_room, chatBarCache);
|
||||
if (action.messageType.has_value()) {
|
||||
messageType = *action.messageType;
|
||||
}
|
||||
@@ -161,7 +149,7 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, con
|
||||
return;
|
||||
}
|
||||
|
||||
m_room->postMessage(text, handledText, messageType, m_room->chatBoxReplyId(), isEdit ? m_room->chatBoxEditId() : QString());
|
||||
m_room->postMessage(text, handledText, messageType, chatBarCache->replyId(), chatBarCache->editId());
|
||||
}
|
||||
|
||||
void ActionsHandler::checkEffects(const QString &text)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
@@ -51,21 +52,15 @@ Q_SIGNALS:
|
||||
void showEffect(const QString &effect);
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
/**
|
||||
* @brief Pre-process text and send message.
|
||||
* @brief Pre-process text and send message event.
|
||||
*/
|
||||
void handleNewMessage();
|
||||
|
||||
/**
|
||||
* @brief Pre-process text and send edit.
|
||||
*/
|
||||
void handleEdit();
|
||||
void handleMessageEvent(ChatBarCache *chatBarCache);
|
||||
|
||||
private:
|
||||
NeoChatRoom *m_room = nullptr;
|
||||
void checkEffects(const QString &text);
|
||||
|
||||
QString handleMentions(QString handledText, const bool &isEdit = false);
|
||||
void handleMessage(const QString &text, QString handledText, const bool &isEdit = false);
|
||||
QString handleMentions(QString handledText, QVector<Mention> *mentions);
|
||||
void handleMessage(const QString &text, QString handledText, ChatBarCache *chatBarCache);
|
||||
};
|
||||
|
||||
153
src/chatbarcache.cpp
Normal file
153
src/chatbarcache.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "chatbarcache.h"
|
||||
|
||||
#include "eventhandler.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
ChatBarCache::ChatBarCache(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QString ChatBarCache::text() const
|
||||
{
|
||||
return m_text;
|
||||
}
|
||||
|
||||
void ChatBarCache::setText(const QString &text)
|
||||
{
|
||||
if (text == m_text) {
|
||||
return;
|
||||
}
|
||||
m_text = text;
|
||||
Q_EMIT textChanged();
|
||||
}
|
||||
|
||||
bool ChatBarCache::isReplying() const
|
||||
{
|
||||
return m_relationType == Reply && !m_relationId.isEmpty();
|
||||
}
|
||||
|
||||
QString ChatBarCache::replyId() const
|
||||
{
|
||||
if (m_relationType != Reply) {
|
||||
return {};
|
||||
}
|
||||
return m_relationId;
|
||||
}
|
||||
|
||||
void ChatBarCache::setReplyId(const QString &replyId)
|
||||
{
|
||||
if (m_relationType == Reply && m_relationId == replyId) {
|
||||
return;
|
||||
}
|
||||
m_relationId = replyId;
|
||||
if (m_relationId.isEmpty()) {
|
||||
m_relationType = None;
|
||||
} else {
|
||||
m_relationType = Reply;
|
||||
}
|
||||
m_attachmentPath = QString();
|
||||
Q_EMIT relationIdChanged();
|
||||
}
|
||||
|
||||
bool ChatBarCache::isEditing() const
|
||||
{
|
||||
return m_relationType == Edit && !m_relationId.isEmpty();
|
||||
}
|
||||
|
||||
QString ChatBarCache::editId() const
|
||||
{
|
||||
if (m_relationType != Edit) {
|
||||
return {};
|
||||
}
|
||||
return m_relationId;
|
||||
}
|
||||
|
||||
void ChatBarCache::setEditId(const QString &editId)
|
||||
{
|
||||
if (m_relationType == Edit && m_relationId == editId) {
|
||||
return;
|
||||
}
|
||||
m_relationId = editId;
|
||||
if (m_relationId.isEmpty()) {
|
||||
m_relationType = None;
|
||||
} else {
|
||||
m_relationType = Edit;
|
||||
}
|
||||
m_attachmentPath = QString();
|
||||
Q_EMIT relationIdChanged();
|
||||
}
|
||||
|
||||
QVariantMap ChatBarCache::relationUser() const
|
||||
{
|
||||
if (parent() == nullptr) {
|
||||
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return {};
|
||||
}
|
||||
auto room = dynamic_cast<NeoChatRoom *>(parent());
|
||||
if (room == nullptr) {
|
||||
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return {};
|
||||
}
|
||||
if (m_relationId.isEmpty()) {
|
||||
return room->getUser(nullptr);
|
||||
}
|
||||
return room->getUser(room->user((*room->findInTimeline(m_relationId))->senderId()));
|
||||
}
|
||||
|
||||
QString ChatBarCache::relationMessage() const
|
||||
{
|
||||
if (parent() == nullptr) {
|
||||
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return {};
|
||||
}
|
||||
if (m_relationId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
auto room = dynamic_cast<NeoChatRoom *>(parent());
|
||||
if (room == nullptr) {
|
||||
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return {};
|
||||
}
|
||||
EventHandler eventhandler;
|
||||
eventhandler.setRoom(room);
|
||||
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
|
||||
eventhandler.setEvent(&**event);
|
||||
return eventhandler.getPlainBody();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString ChatBarCache::attachmentPath() const
|
||||
{
|
||||
return m_attachmentPath;
|
||||
}
|
||||
|
||||
void ChatBarCache::setAttachmentPath(const QString &attachmentPath)
|
||||
{
|
||||
if (attachmentPath == m_attachmentPath) {
|
||||
return;
|
||||
}
|
||||
m_attachmentPath = attachmentPath;
|
||||
m_relationType = None;
|
||||
m_relationId = QString();
|
||||
Q_EMIT attachmentPathChanged();
|
||||
}
|
||||
|
||||
QVector<Mention> *ChatBarCache::mentions()
|
||||
{
|
||||
return &m_mentions;
|
||||
}
|
||||
|
||||
QString ChatBarCache::savedText() const
|
||||
{
|
||||
return m_savedText;
|
||||
}
|
||||
|
||||
void ChatBarCache::setSavedText(const QString &savedText)
|
||||
{
|
||||
m_savedText = savedText;
|
||||
}
|
||||
185
src/chatbarcache.h
Normal file
185
src/chatbarcache.h
Normal file
@@ -0,0 +1,185 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QTextCursor>
|
||||
|
||||
/**
|
||||
* @brief Defines a user mention in the current chat or edit text.
|
||||
*/
|
||||
struct Mention {
|
||||
QTextCursor cursor; /**< Contains the mention's text and position in the text. */
|
||||
QString text; /**< The inserted text of the mention. */
|
||||
int start = 0; /**< Start position of the mention. */
|
||||
int position = 0; /**< End position of the mention. */
|
||||
QString id; /**< The id the mention (used to create link when sending the message). */
|
||||
};
|
||||
|
||||
/**
|
||||
* @class ChatBarCache
|
||||
*
|
||||
* A class to cache data from a chat bar.
|
||||
*
|
||||
* A chat bar can be anything that allows users to compose or edit message, it doesn't
|
||||
* necessarily have to use the ChatBar component, e.g. MessageEditComponent.
|
||||
*
|
||||
* This object is intended to allow the current contents of a chat bar to be cached
|
||||
* between different rooms, i.e. there is an expectation that each NeoChatRoom could
|
||||
* have a separate cache for each chat bar.
|
||||
*
|
||||
* @note The NeoChatRoom which this component is created in is expected to be set
|
||||
* as it's parent. This is necessary for certain functions which need to get
|
||||
* relevant room information.
|
||||
*
|
||||
* @sa ChatBar, MessageEditComponent, NeoChatRoom
|
||||
*/
|
||||
class ChatBarCache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
/**
|
||||
* @brief The text in the chat bar.
|
||||
*
|
||||
* Due to problems with QTextDocument, unlike the other properties here,
|
||||
* text is *not* used to store the text when switching rooms.
|
||||
*/
|
||||
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the chat bar is currently replying to a message.
|
||||
*/
|
||||
Q_PROPERTY(bool isReplying READ isReplying NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The Matrix message ID of an event being replied to, if any.
|
||||
*
|
||||
* Will return empty if the RelationType is currently set to None or Edit.
|
||||
*
|
||||
* @note Replying, editing and attachments are exclusive so setting this will
|
||||
* clear an edit or attachment.
|
||||
*
|
||||
* @sa RelationType
|
||||
*/
|
||||
Q_PROPERTY(QString replyId READ replyId WRITE setReplyId NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the chat bar is currently editing a message.
|
||||
*/
|
||||
Q_PROPERTY(bool isEditing READ isEditing NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The Matrix message ID of an event being edited, if any.
|
||||
*
|
||||
* Will return empty if the RelationType is currently set to None or Reply.
|
||||
*
|
||||
* @note Replying, editing and attachments are exclusive so setting this will
|
||||
* clear an reply or attachment.
|
||||
*
|
||||
* @sa RelationType
|
||||
*/
|
||||
Q_PROPERTY(QString editId READ editId WRITE setEditId NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Get the user for the message being replied to.
|
||||
*
|
||||
* This is different to getting a Quotient::User object
|
||||
* as neither of those can provide details like the displayName or avatarMediaId
|
||||
* without the room context as these can vary from room to room.
|
||||
*
|
||||
* Returns an empty user if not replying to a message.
|
||||
*
|
||||
* The user QVariantMap has the following properties:
|
||||
* - isLocalUser - Whether the user is the local user.
|
||||
* - id - The matrix ID of the user.
|
||||
* - displayName - Display name in the context of this room.
|
||||
* - avatarSource - The mxc URL for the user's avatar in the current room.
|
||||
* - avatarMediaId - Avatar id in the context of this room.
|
||||
* - color - Color for the user.
|
||||
* - object - The Quotient::User object for the user.
|
||||
*
|
||||
* @sa getUser, Quotient::User
|
||||
*/
|
||||
Q_PROPERTY(QVariantMap relationUser READ relationUser NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The content of the related message.
|
||||
*
|
||||
* Will be QString() if no related message.
|
||||
*/
|
||||
Q_PROPERTY(QString relationMessage READ relationMessage NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The local path for a file to send, if any.
|
||||
*
|
||||
* @note Replying, editing and attachments are exclusive so setting this will
|
||||
* clear an edit or reply.
|
||||
*/
|
||||
Q_PROPERTY(QString attachmentPath READ attachmentPath WRITE setAttachmentPath NOTIFY attachmentPathChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Describes the type of relation which relationId can refer to.
|
||||
*
|
||||
* A chat bar can only be relating to a single message at a time making these
|
||||
* exclusive.
|
||||
*/
|
||||
enum RelationType {
|
||||
Reply, /**< The current relation is a message being replied to. */
|
||||
Edit, /**< The current relation is a message being edited. */
|
||||
None, /**< There is currently no relation event */
|
||||
};
|
||||
Q_ENUM(RelationType)
|
||||
|
||||
explicit ChatBarCache(QObject *parent = nullptr);
|
||||
|
||||
QString text() const;
|
||||
void setText(const QString &text);
|
||||
|
||||
bool isReplying() const;
|
||||
QString replyId() const;
|
||||
void setReplyId(const QString &replyId);
|
||||
|
||||
bool isEditing() const;
|
||||
QString editId() const;
|
||||
void setEditId(const QString &editId);
|
||||
|
||||
QVariantMap relationUser() const;
|
||||
|
||||
QString relationMessage() const;
|
||||
|
||||
QString attachmentPath() const;
|
||||
void setAttachmentPath(const QString &attachmentPath);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the mentions for the current chat bar text.
|
||||
*/
|
||||
QVector<Mention> *mentions();
|
||||
|
||||
/**
|
||||
* @brief Get the saved chat bar text.
|
||||
*/
|
||||
QString savedText() const;
|
||||
|
||||
/**
|
||||
* @brief Save the chat bar text.
|
||||
*/
|
||||
void setSavedText(const QString &savedText);
|
||||
|
||||
Q_SIGNALS:
|
||||
void textChanged();
|
||||
void relationIdChanged();
|
||||
void attachmentPathChanged();
|
||||
|
||||
private:
|
||||
QString m_text = QString();
|
||||
QString m_relationId = QString();
|
||||
RelationType m_relationType = RelationType::None;
|
||||
QString m_attachmentPath = QString();
|
||||
QVector<Mention> m_mentions;
|
||||
QString m_savedText;
|
||||
};
|
||||
@@ -57,11 +57,12 @@ public:
|
||||
setFormat(error.first, error.second.size(), errorFormat);
|
||||
}
|
||||
}
|
||||
auto room = dynamic_cast<ChatDocumentHandler *>(parent())->room();
|
||||
auto handler = dynamic_cast<ChatDocumentHandler *>(parent());
|
||||
auto room = handler->room();
|
||||
if (!room) {
|
||||
return;
|
||||
}
|
||||
auto mentions = room->mentions();
|
||||
auto mentions = handler->chatBarCache()->mentions();
|
||||
mentions->erase(std::remove_if(mentions->begin(),
|
||||
mentions->end(),
|
||||
[this](auto &mention) {
|
||||
@@ -103,15 +104,10 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
||||
m_completionModel->setRoom(m_room);
|
||||
static QPointer<NeoChatRoom> previousRoom = nullptr;
|
||||
if (previousRoom) {
|
||||
disconnect(previousRoom, &NeoChatRoom::chatBoxTextChanged, this, nullptr);
|
||||
disconnect(previousRoom, &NeoChatRoom::editTextChanged, this, nullptr);
|
||||
disconnect(m_chatBarCache, &ChatBarCache::textChanged, this, nullptr);
|
||||
}
|
||||
previousRoom = m_room;
|
||||
connect(m_room, &NeoChatRoom::chatBoxTextChanged, this, [this]() {
|
||||
int start = completionStartIndex();
|
||||
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::editTextChanged, this, [this]() {
|
||||
connect(m_chatBarCache, &ChatBarCache::textChanged, this, [this]() {
|
||||
int start = completionStartIndex();
|
||||
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||
});
|
||||
@@ -215,6 +211,20 @@ void ChatDocumentHandler::setRoom(NeoChatRoom *room)
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
ChatBarCache *ChatDocumentHandler::chatBarCache() const
|
||||
{
|
||||
return m_chatBarCache;
|
||||
}
|
||||
|
||||
void ChatDocumentHandler::setChatBarCache(ChatBarCache *chatBarCache)
|
||||
{
|
||||
if (m_chatBarCache == chatBarCache) {
|
||||
return;
|
||||
}
|
||||
m_chatBarCache = chatBarCache;
|
||||
Q_EMIT chatBarCacheChanged();
|
||||
}
|
||||
|
||||
void ChatDocumentHandler::complete(int index)
|
||||
{
|
||||
if (m_completionModel->autoCompletionType() == CompletionModel::User) {
|
||||
@@ -303,11 +313,7 @@ QString ChatDocumentHandler::getText() const
|
||||
if (!m_room) {
|
||||
return QString();
|
||||
}
|
||||
if (m_isEdit) {
|
||||
return m_room->editText();
|
||||
} else {
|
||||
return m_room->chatBoxText();
|
||||
}
|
||||
return m_chatBarCache->text();
|
||||
}
|
||||
|
||||
void ChatDocumentHandler::pushMention(const Mention mention) const
|
||||
@@ -315,11 +321,7 @@ void ChatDocumentHandler::pushMention(const Mention mention) const
|
||||
if (!m_room) {
|
||||
return;
|
||||
}
|
||||
if (m_isEdit) {
|
||||
m_room->editMentions()->push_back(mention);
|
||||
} else {
|
||||
m_room->mentions()->push_back(mention);
|
||||
}
|
||||
m_chatBarCache->mentions()->push_back(mention);
|
||||
}
|
||||
|
||||
QColor ChatDocumentHandler::mentionColor() const
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <QQuickTextDocument>
|
||||
#include <QTextCursor>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "models/completionmodel.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
@@ -102,6 +103,11 @@ class ChatDocumentHandler : public QObject
|
||||
*/
|
||||
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||
|
||||
/**
|
||||
* @brief The cache for the chat bar the text document is being handled for.
|
||||
*/
|
||||
Q_PROPERTY(ChatBarCache *chatBarCache READ chatBarCache WRITE setChatBarCache NOTIFY chatBarCacheChanged)
|
||||
|
||||
/**
|
||||
* @brief The color to highlight user mentions.
|
||||
*/
|
||||
@@ -133,6 +139,9 @@ public:
|
||||
[[nodiscard]] NeoChatRoom *room() const;
|
||||
void setRoom(NeoChatRoom *room);
|
||||
|
||||
[[nodiscard]] ChatBarCache *chatBarCache() const;
|
||||
void setChatBarCache(ChatBarCache *chatBarCache);
|
||||
|
||||
Q_INVOKABLE void complete(int index);
|
||||
|
||||
void updateCompletions();
|
||||
@@ -149,6 +158,7 @@ Q_SIGNALS:
|
||||
void documentChanged();
|
||||
void cursorPositionChanged();
|
||||
void roomChanged();
|
||||
void chatBarCacheChanged();
|
||||
void completionModelChanged();
|
||||
void selectionStartChanged();
|
||||
void selectionEndChanged();
|
||||
@@ -163,6 +173,7 @@ private:
|
||||
QPointer<QQuickTextDocument> m_document;
|
||||
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
QPointer<ChatBarCache> m_chatBarCache;
|
||||
bool completionVisible = false;
|
||||
|
||||
QColor m_mentionColor;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "actionsmodel.h"
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "controller.h"
|
||||
#include "neochatroom.h"
|
||||
#include "roommanager.h"
|
||||
@@ -19,7 +20,7 @@ QStringList rainbowColors{"#ff2b00"_ls, "#ff5500"_ls, "#ff8000"_ls, "#ffaa00"_ls
|
||||
"#00d4ff"_ls, "#00aaff"_ls, "#007fff"_ls, "#0055ff"_ls, "#002bff"_ls, "#0000ff"_ls, "#2a00ff"_ls, "#5500ff"_ls, "#7f00ff"_ls,
|
||||
"#aa00ff"_ls, "#d400ff"_ls, "#ff00ff"_ls, "#ff00d4"_ls, "#ff00aa"_ls, "#ff0080"_ls, "#ff0055"_ls, "#ff002b"_ls, "#ff0000"_ls};
|
||||
|
||||
auto leaveRoomLambda = [](const QString &text, NeoChatRoom *room) {
|
||||
auto leaveRoomLambda = [](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
if (text.isEmpty()) {
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Info, i18n("Leaving this room."));
|
||||
room->connection()->leaveRoom(room);
|
||||
@@ -45,7 +46,7 @@ auto leaveRoomLambda = [](const QString &text, NeoChatRoom *room) {
|
||||
return QString();
|
||||
};
|
||||
|
||||
auto roomNickLambda = [](const QString &text, NeoChatRoom *room) {
|
||||
auto roomNickLambda = [](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
if (text.isEmpty()) {
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("No new nickname provided, no changes will happen."));
|
||||
} else {
|
||||
@@ -57,7 +58,7 @@ auto roomNickLambda = [](const QString &text, NeoChatRoom *room) {
|
||||
QVector<ActionsModel::Action> actions{
|
||||
Action{
|
||||
QStringLiteral("shrug"),
|
||||
[](const QString &message, NeoChatRoom *) {
|
||||
[](const QString &message, NeoChatRoom *, ChatBarCache *) {
|
||||
return QStringLiteral("¯\\\\_(ツ)_/¯ %1").arg(message);
|
||||
},
|
||||
true,
|
||||
@@ -67,7 +68,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("lenny"),
|
||||
[](const QString &message, NeoChatRoom *) {
|
||||
[](const QString &message, NeoChatRoom *, ChatBarCache *) {
|
||||
return QStringLiteral("( ͡° ͜ʖ ͡°) %1").arg(message);
|
||||
},
|
||||
true,
|
||||
@@ -77,7 +78,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("tableflip"),
|
||||
[](const QString &message, NeoChatRoom *) {
|
||||
[](const QString &message, NeoChatRoom *, ChatBarCache *) {
|
||||
return QStringLiteral("(╯°□°)╯︵ ┻━┻ %1").arg(message);
|
||||
},
|
||||
true,
|
||||
@@ -87,7 +88,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("unflip"),
|
||||
[](const QString &message, NeoChatRoom *) {
|
||||
[](const QString &message, NeoChatRoom *, ChatBarCache *) {
|
||||
return QStringLiteral("┬──┬ ノ( ゜-゜ノ) %1").arg(message);
|
||||
},
|
||||
true,
|
||||
@@ -97,7 +98,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("rainbow"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *chatBarCache) {
|
||||
QString rainbowText;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
rainbowText += QStringLiteral("<font color='%2'>%3</font>").arg(rainbowColors[i % rainbowColors.length()], text.at(i));
|
||||
@@ -106,8 +107,8 @@ QVector<ActionsModel::Action> actions{
|
||||
room->postMessage(QStringLiteral("/rainbow %1").arg(text),
|
||||
rainbowText,
|
||||
RoomMessageEvent::MsgType::Text,
|
||||
room->chatBoxReplyId(),
|
||||
room->chatBoxEditId());
|
||||
chatBarCache->replyId(),
|
||||
chatBarCache->editId());
|
||||
return QString();
|
||||
},
|
||||
false,
|
||||
@@ -117,7 +118,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("rainbowme"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *chatBarCache) {
|
||||
QString rainbowText;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
rainbowText += QStringLiteral("<font color='%2'>%3</font>").arg(rainbowColors[i % rainbowColors.length()], text.at(i));
|
||||
@@ -126,8 +127,8 @@ QVector<ActionsModel::Action> actions{
|
||||
room->postMessage(QStringLiteral("/rainbow %1").arg(text),
|
||||
rainbowText,
|
||||
RoomMessageEvent::MsgType::Emote,
|
||||
room->chatBoxReplyId(),
|
||||
room->chatBoxEditId());
|
||||
chatBarCache->replyId(),
|
||||
chatBarCache->editId());
|
||||
return QString();
|
||||
},
|
||||
false,
|
||||
@@ -137,7 +138,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("plain"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
room->postMessage(text, text.toHtmlEscaped(), RoomMessageEvent::MsgType::Text, {}, {});
|
||||
return QString();
|
||||
},
|
||||
@@ -148,13 +149,13 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("spoiler"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *chatBarCache) {
|
||||
// Ideally, we would just return rainbowText and let that do the rest, but the colors don't survive markdownToHTML.
|
||||
room->postMessage(QStringLiteral("/spoiler %1").arg(text),
|
||||
QStringLiteral("<span data-mx-spoiler>%1</span>").arg(text),
|
||||
RoomMessageEvent::MsgType::Text,
|
||||
room->chatBoxReplyId(),
|
||||
room->chatBoxEditId());
|
||||
chatBarCache->replyId(),
|
||||
chatBarCache->editId());
|
||||
return QString();
|
||||
},
|
||||
false,
|
||||
@@ -164,7 +165,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("me"),
|
||||
[](const QString &text, NeoChatRoom *) {
|
||||
[](const QString &text, NeoChatRoom *, ChatBarCache *) {
|
||||
return text;
|
||||
},
|
||||
true,
|
||||
@@ -174,7 +175,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("notice"),
|
||||
[](const QString &text, NeoChatRoom *) {
|
||||
[](const QString &text, NeoChatRoom *, ChatBarCache *) {
|
||||
return text;
|
||||
},
|
||||
true,
|
||||
@@ -184,7 +185,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("invite"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
static const QRegularExpression mxidRegex(
|
||||
QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
|
||||
auto regexMatch = mxidRegex.match(text);
|
||||
@@ -220,7 +221,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("join"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"));
|
||||
auto regexMatch = roomRegex.match(text);
|
||||
if (!regexMatch.hasMatch()) {
|
||||
@@ -244,7 +245,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("knock"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
auto parts = text.split(QLatin1String(" "));
|
||||
QString roomName = parts[0];
|
||||
QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"));
|
||||
@@ -276,7 +277,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("j"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"));
|
||||
auto regexMatch = roomRegex.match(text);
|
||||
if (!regexMatch.hasMatch()) {
|
||||
@@ -315,7 +316,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("nick"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
if (text.isEmpty()) {
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("No new nickname provided, no changes will happen."));
|
||||
} else {
|
||||
@@ -346,7 +347,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("ignore"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
static const QRegularExpression mxidRegex(
|
||||
QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
|
||||
auto regexMatch = mxidRegex.match(text);
|
||||
@@ -374,7 +375,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("unignore"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
static const QRegularExpression mxidRegex(
|
||||
QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
|
||||
auto regexMatch = mxidRegex.match(text);
|
||||
@@ -402,9 +403,8 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("react"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
QString replyEventId = room->chatBoxReplyId();
|
||||
if (replyEventId.isEmpty()) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *chatBarCache) {
|
||||
if (chatBarCache->replyId().isEmpty()) {
|
||||
for (auto it = room->messageEvents().crbegin(); it != room->messageEvents().crend(); it++) {
|
||||
const auto &evt = **it;
|
||||
if (const auto event = eventCast<const RoomMessageEvent>(&evt)) {
|
||||
@@ -413,7 +413,7 @@ QVector<ActionsModel::Action> actions{
|
||||
}
|
||||
}
|
||||
}
|
||||
room->toggleReaction(replyEventId, text);
|
||||
room->toggleReaction(chatBarCache->replyId(), text);
|
||||
return QString();
|
||||
},
|
||||
false,
|
||||
@@ -423,7 +423,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("ban"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
auto parts = text.split(QLatin1String(" "));
|
||||
static const QRegularExpression mxidRegex(
|
||||
QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
|
||||
@@ -462,7 +462,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("unban"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
static const QRegularExpression mxidRegex(
|
||||
QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
|
||||
auto regexMatch = mxidRegex.match(text);
|
||||
@@ -495,7 +495,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("kick"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
auto parts = text.split(QLatin1String(" "));
|
||||
static const QRegularExpression mxidRegex(
|
||||
QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <QAbstractListModel>
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
|
||||
class ChatBarCache;
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
@@ -28,7 +29,7 @@ public:
|
||||
/**
|
||||
* @brief The function to execute when the action is triggered.
|
||||
*/
|
||||
std::function<QString(const QString &, NeoChatRoom *)> handle;
|
||||
std::function<QString(const QString &, NeoChatRoom *, ChatBarCache *)> handle;
|
||||
/**
|
||||
* @brief Whether the action is a message type action.
|
||||
*
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <Quotient/jobs/downloadfilejob.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "clipboard.h"
|
||||
#include "controller.h"
|
||||
#include "eventhandler.h"
|
||||
@@ -65,6 +66,9 @@ using namespace Quotient;
|
||||
NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinState)
|
||||
: Room(connection, std::move(roomId), joinState)
|
||||
{
|
||||
m_mainCache = new ChatBarCache(this);
|
||||
m_editCache = new ChatBarCache(this);
|
||||
|
||||
connect(connection, &Connection::accountDataChanged, this, &NeoChatRoom::updatePushNotificationState);
|
||||
connect(this, &Room::fileTransferCompleted, this, [this] {
|
||||
setFileUploadingProgress(0);
|
||||
@@ -1571,128 +1575,14 @@ void NeoChatRoom::copyEventMedia(const QString &eventId)
|
||||
}
|
||||
}
|
||||
|
||||
QString NeoChatRoom::chatBoxText() const
|
||||
ChatBarCache *NeoChatRoom::mainCache() const
|
||||
{
|
||||
return m_chatBoxText;
|
||||
return m_mainCache;
|
||||
}
|
||||
|
||||
void NeoChatRoom::setChatBoxText(const QString &text)
|
||||
ChatBarCache *NeoChatRoom::editCache() const
|
||||
{
|
||||
m_chatBoxText = text;
|
||||
Q_EMIT chatBoxTextChanged();
|
||||
}
|
||||
|
||||
QString NeoChatRoom::editText() const
|
||||
{
|
||||
return m_editText;
|
||||
}
|
||||
|
||||
void NeoChatRoom::setEditText(const QString &text)
|
||||
{
|
||||
m_editText = text;
|
||||
Q_EMIT editTextChanged();
|
||||
}
|
||||
|
||||
QString NeoChatRoom::chatBoxReplyId() const
|
||||
{
|
||||
return m_chatBoxReplyId;
|
||||
}
|
||||
|
||||
void NeoChatRoom::setChatBoxReplyId(const QString &replyId)
|
||||
{
|
||||
if (replyId == m_chatBoxReplyId) {
|
||||
return;
|
||||
}
|
||||
m_chatBoxReplyId = replyId;
|
||||
Q_EMIT chatBoxReplyIdChanged();
|
||||
}
|
||||
|
||||
QString NeoChatRoom::chatBoxEditId() const
|
||||
{
|
||||
return m_chatBoxEditId;
|
||||
}
|
||||
|
||||
void NeoChatRoom::setChatBoxEditId(const QString &editId)
|
||||
{
|
||||
if (editId == m_chatBoxEditId) {
|
||||
return;
|
||||
}
|
||||
m_chatBoxEditId = editId;
|
||||
Q_EMIT chatBoxEditIdChanged();
|
||||
}
|
||||
|
||||
QVariantMap NeoChatRoom::chatBoxReplyUser() const
|
||||
{
|
||||
if (m_chatBoxReplyId.isEmpty()) {
|
||||
return emptyUser;
|
||||
}
|
||||
return getUser(user((*findInTimeline(m_chatBoxReplyId))->senderId()));
|
||||
}
|
||||
|
||||
QString NeoChatRoom::chatBoxReplyMessage() const
|
||||
{
|
||||
if (m_chatBoxReplyId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
EventHandler eventhandler;
|
||||
eventhandler.setRoom(this);
|
||||
eventhandler.setEvent(&**findInTimeline(m_chatBoxReplyId));
|
||||
return eventhandler.getPlainBody();
|
||||
}
|
||||
|
||||
QVariantMap NeoChatRoom::chatBoxEditUser() const
|
||||
{
|
||||
if (m_chatBoxEditId.isEmpty()) {
|
||||
return emptyUser;
|
||||
}
|
||||
return getUser(user((*findInTimeline(m_chatBoxEditId))->senderId()));
|
||||
}
|
||||
|
||||
QString NeoChatRoom::chatBoxEditMessage() const
|
||||
{
|
||||
if (m_chatBoxEditId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
EventHandler eventhandler;
|
||||
eventhandler.setRoom(this);
|
||||
if (auto event = findInTimeline(m_chatBoxEditId); event != historyEdge()) {
|
||||
eventhandler.setEvent(&**event);
|
||||
return eventhandler.getPlainBody();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString NeoChatRoom::chatBoxAttachmentPath() const
|
||||
{
|
||||
return m_chatBoxAttachmentPath;
|
||||
}
|
||||
|
||||
void NeoChatRoom::setChatBoxAttachmentPath(const QString &attachmentPath)
|
||||
{
|
||||
m_chatBoxAttachmentPath = attachmentPath;
|
||||
Q_EMIT chatBoxAttachmentPathChanged();
|
||||
}
|
||||
|
||||
QVector<Mention> *NeoChatRoom::mentions()
|
||||
{
|
||||
return &m_mentions;
|
||||
}
|
||||
|
||||
QVector<Mention> *NeoChatRoom::editMentions()
|
||||
{
|
||||
return &m_editMentions;
|
||||
}
|
||||
|
||||
QString NeoChatRoom::savedText() const
|
||||
{
|
||||
return m_savedText;
|
||||
}
|
||||
|
||||
void NeoChatRoom::setSavedText(const QString &savedText)
|
||||
{
|
||||
m_savedText = savedText;
|
||||
return m_editCache;
|
||||
}
|
||||
|
||||
void NeoChatRoom::replyLastMessage()
|
||||
@@ -1721,7 +1611,7 @@ void NeoChatRoom::replyLastMessage()
|
||||
// For any message that isn't an edit return the id of the current message
|
||||
eventId = (*it)->id();
|
||||
}
|
||||
setChatBoxReplyId(eventId);
|
||||
mainCache()->setReplyId(eventId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1755,7 +1645,7 @@ void NeoChatRoom::editLastMessage()
|
||||
// For any message that isn't an edit return the id of the current message
|
||||
eventId = (*it)->id();
|
||||
}
|
||||
setChatBoxEditId(eventId);
|
||||
editCache()->setEditId(eventId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace Quotient
|
||||
class User;
|
||||
}
|
||||
|
||||
class ChatBarCache;
|
||||
|
||||
class PushNotificationState : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -40,17 +42,6 @@ public:
|
||||
Q_ENUM(State)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Defines a user mention in the current chat or edit text.
|
||||
*/
|
||||
struct Mention {
|
||||
QTextCursor cursor; /**< Contains the mention's text and position in the text. */
|
||||
QString text; /**< The inserted text of the mention. */
|
||||
int start = 0; /**< Start position of the mention. */
|
||||
int position = 0; /**< End position of the mention. */
|
||||
QString id; /**< The id the mention (used to create link when sending the message). */
|
||||
};
|
||||
|
||||
/**
|
||||
* @class NeoChatRoom
|
||||
*
|
||||
@@ -311,94 +302,14 @@ class NeoChatRoom : public Quotient::Room
|
||||
Q_PROPERTY(int spaceParentPowerLevel READ spaceParentPowerLevel WRITE setSpaceParentPowerLevel NOTIFY spaceParentPowerLevelChanged)
|
||||
|
||||
/**
|
||||
* @brief The current text in the chatbox for the room.
|
||||
*
|
||||
* Due to problems with QTextDocument, unlike the other properties here,
|
||||
* chatBoxText is *not* used to store the text when switching rooms.
|
||||
* @brief The cache for the main chat bar in the room.
|
||||
*/
|
||||
Q_PROPERTY(QString chatBoxText READ chatBoxText WRITE setChatBoxText NOTIFY chatBoxTextChanged)
|
||||
Q_PROPERTY(ChatBarCache *mainCache READ mainCache CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The text for any message currently being edited in the room.
|
||||
* @brief The cache for the edit chat bar in the room.
|
||||
*/
|
||||
Q_PROPERTY(QString editText READ editText WRITE setEditText NOTIFY editTextChanged)
|
||||
|
||||
/**
|
||||
* @brief The event id of a message being replied to.
|
||||
*
|
||||
* Will be QString() if not replying to a message.
|
||||
*/
|
||||
Q_PROPERTY(QString chatBoxReplyId READ chatBoxReplyId WRITE setChatBoxReplyId NOTIFY chatBoxReplyIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The event id of a message being edited.
|
||||
*
|
||||
* Will be QString() if not editing to a message.
|
||||
*/
|
||||
Q_PROPERTY(QString chatBoxEditId READ chatBoxEditId WRITE setChatBoxEditId NOTIFY chatBoxEditIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Get the user for the message being replied to.
|
||||
*
|
||||
* This is different to getting a Quotient::User object
|
||||
* as neither of those can provide details like the displayName or avatarMediaId
|
||||
* without the room context as these can vary from room to room.
|
||||
*
|
||||
* Returns an empty user if not replying to a message.
|
||||
*
|
||||
* The user QVariantMap has the following properties:
|
||||
* - isLocalUser - Whether the user is the local user.
|
||||
* - id - The matrix ID of the user.
|
||||
* - displayName - Display name in the context of this room.
|
||||
* - avatarSource - The mxc URL for the user's avatar in the current room.
|
||||
* - avatarMediaId - Avatar id in the context of this room.
|
||||
* - color - Color for the user.
|
||||
* - object - The Quotient::User object for the user.
|
||||
*
|
||||
* @sa getUser, Quotient::User
|
||||
*/
|
||||
Q_PROPERTY(QVariantMap chatBoxReplyUser READ chatBoxReplyUser NOTIFY chatBoxReplyIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The content of the message being replied to.
|
||||
*
|
||||
* Will be QString() if not replying to a message.
|
||||
*/
|
||||
Q_PROPERTY(QString chatBoxReplyMessage READ chatBoxReplyMessage NOTIFY chatBoxReplyIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Get the user for the message being edited.
|
||||
*
|
||||
* This is different to getting a Quotient::User object
|
||||
* as neither of those can provide details like the displayName or avatarMediaId
|
||||
* without the room context as these can vary from room to room.
|
||||
*
|
||||
* Returns an empty user if not replying to a message.
|
||||
*
|
||||
* The user QVariantMap has the following properties:
|
||||
* - isLocalUser - Whether the user is the local user.
|
||||
* - id - The matrix ID of the user.
|
||||
* - displayName - Display name in the context of this room.
|
||||
* - avatarSource - The mxc URL for the user's avatar in the current room.
|
||||
* - avatarMediaId - Avatar id in the context of this room.
|
||||
* - color - Color for the user.
|
||||
* - object - The Quotient::User object for the user.
|
||||
*
|
||||
* @sa getUser, Quotient::User
|
||||
*/
|
||||
Q_PROPERTY(QVariantMap chatBoxEditUser READ chatBoxEditUser NOTIFY chatBoxEditIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The content of the message being edited.
|
||||
*
|
||||
* Will be QString() if not editing a message.
|
||||
*/
|
||||
Q_PROPERTY(QString chatBoxEditMessage READ chatBoxEditMessage NOTIFY chatBoxEditIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The file path of the attachment to be sent.
|
||||
*/
|
||||
Q_PROPERTY(QString chatBoxAttachmentPath READ chatBoxAttachmentPath WRITE setChatBoxAttachmentPath NOTIFY chatBoxAttachmentPathChanged)
|
||||
Q_PROPERTY(ChatBarCache *editCache READ editCache CONSTANT)
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -805,46 +716,9 @@ public:
|
||||
[[nodiscard]] int spaceParentPowerLevel() const;
|
||||
void setSpaceParentPowerLevel(const int &newPowerLevel);
|
||||
|
||||
QString chatBoxText() const;
|
||||
void setChatBoxText(const QString &text);
|
||||
ChatBarCache *mainCache() const;
|
||||
|
||||
QString editText() const;
|
||||
void setEditText(const QString &text);
|
||||
|
||||
QString chatBoxReplyId() const;
|
||||
void setChatBoxReplyId(const QString &replyId);
|
||||
|
||||
QVariantMap chatBoxReplyUser() const;
|
||||
QString chatBoxReplyMessage() const;
|
||||
|
||||
QString chatBoxEditId() const;
|
||||
void setChatBoxEditId(const QString &editId);
|
||||
|
||||
QVariantMap chatBoxEditUser() const;
|
||||
QString chatBoxEditMessage() const;
|
||||
|
||||
QString chatBoxAttachmentPath() const;
|
||||
void setChatBoxAttachmentPath(const QString &attachmentPath);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the mentions for the current chatbox text.
|
||||
*/
|
||||
QVector<Mention> *mentions();
|
||||
|
||||
/**
|
||||
* @brief Retrieve the mentions for the current edit text.
|
||||
*/
|
||||
QVector<Mention> *editMentions();
|
||||
|
||||
/**
|
||||
* @brief Get the saved chatbox text for the room.
|
||||
*/
|
||||
QString savedText() const;
|
||||
|
||||
/**
|
||||
* @brief Save the chatbox text for the room.
|
||||
*/
|
||||
void setSavedText(const QString &savedText);
|
||||
ChatBarCache *editCache() const;
|
||||
|
||||
/**
|
||||
* @brief Reply to the last message sent in the timeline.
|
||||
@@ -916,14 +790,9 @@ private:
|
||||
|
||||
std::unique_ptr<Quotient::RoomEvent> m_cachedEvent;
|
||||
|
||||
QString m_chatBoxText;
|
||||
QString m_editText;
|
||||
QString m_chatBoxReplyId;
|
||||
QString m_chatBoxEditId;
|
||||
QString m_chatBoxAttachmentPath;
|
||||
QVector<Mention> m_mentions;
|
||||
QVector<Mention> m_editMentions;
|
||||
QString m_savedText;
|
||||
ChatBarCache *m_mainCache;
|
||||
ChatBarCache *m_editCache;
|
||||
|
||||
QCache<QString, PollHandler> m_polls;
|
||||
std::vector<Quotient::event_ptr_tt<Quotient::RoomEvent>> m_extraEvents;
|
||||
|
||||
@@ -946,11 +815,6 @@ Q_SIGNALS:
|
||||
void displayNameChanged();
|
||||
void pushNotificationStateChanged(PushNotificationState::State state);
|
||||
void showMessage(MessageType messageType, const QString &message);
|
||||
void chatBoxTextChanged();
|
||||
void editTextChanged();
|
||||
void chatBoxReplyIdChanged();
|
||||
void chatBoxEditIdChanged();
|
||||
void chatBoxAttachmentPathChanged();
|
||||
void canEncryptRoomChanged();
|
||||
void joinRuleChanged();
|
||||
void historyVisibilityChanged();
|
||||
|
||||
@@ -39,6 +39,7 @@ QQC2.Control {
|
||||
* @brief The current room that user is viewing.
|
||||
*/
|
||||
required property NeoChatRoom currentRoom
|
||||
onCurrentRoomChanged: _private.chatBarCache = currentRoom.mainCache
|
||||
|
||||
/**
|
||||
* @brief The QQC2.TextArea object.
|
||||
@@ -62,7 +63,7 @@ QQC2.Control {
|
||||
property bool isBusy: root.currentRoom && root.currentRoom.hasFileUploading
|
||||
|
||||
// Matrix does not allow sending attachments in replies
|
||||
visible: root.currentRoom.chatBoxReplyId.length === 0 && root.currentRoom.chatBoxAttachmentPath.length === 0
|
||||
visible: _private.chatBarCache.isReplying && _private.chatBarCache.attachmentPath.length === 0
|
||||
icon.name: "mail-attachment"
|
||||
text: i18n("Attach an image or file")
|
||||
displayHint: Kirigami.DisplayHint.IconOnly
|
||||
@@ -76,7 +77,7 @@ QQC2.Control {
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
root.currentRoom.chatBoxAttachmentPath = path;
|
||||
_private.chatBarCache.attachmentPath = path;
|
||||
})
|
||||
fileDialog.open()
|
||||
}
|
||||
@@ -169,7 +170,7 @@ QQC2.Control {
|
||||
leftPadding: LayoutMirroring.enabled ? actionsRow.width : Kirigami.Units.largeSpacing
|
||||
rightPadding: LayoutMirroring.enabled ? Kirigami.Units.largeSpacing : actionsRow.width + x * 2 + Kirigami.Units.largeSpacing * 2
|
||||
|
||||
placeholderText: root.currentRoom.usesEncryption ? i18n("Send an encrypted message…") : root.currentRoom.chatBoxAttachmentPath.length > 0 ? i18n("Set an attachment caption...") : i18n("Send a message…")
|
||||
placeholderText: root.currentRoom.usesEncryption ? i18n("Send an encrypted message…") : _private.chatBarCache.attachmentPath.length > 0 ? i18n("Set an attachment caption...") : i18n("Send a message…")
|
||||
verticalAlignment: TextEdit.AlignVCenter
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
@@ -189,7 +190,7 @@ QQC2.Control {
|
||||
root.currentRoom.sendTypingNotification(textExists)
|
||||
textExists ? repeatTimer.start() : repeatTimer.stop()
|
||||
}
|
||||
root.currentRoom.chatBoxText = text
|
||||
_private.chatBarCache.text = text
|
||||
}
|
||||
onCursorRectangleChanged: chatBarScrollView.ensureVisible(cursorRectangle)
|
||||
onSelectedTextChanged: {
|
||||
@@ -285,25 +286,25 @@ QQC2.Control {
|
||||
anchors.rightMargin: root.width > chatBarSizeHelper.currentWidth ? 0 : (chatBarScrollView.QQC2.ScrollBar.vertical.visible ? Kirigami.Units.largeSpacing * 3.5 : Kirigami.Units.largeSpacing)
|
||||
|
||||
active: visible
|
||||
visible: root.currentRoom.chatBoxReplyId.length > 0 || root.currentRoom.chatBoxAttachmentPath.length > 0
|
||||
sourceComponent: root.currentRoom.chatBoxReplyId.length > 0 ? replyPane : attachmentPane
|
||||
visible: _private.chatBarCache.isReplying || _private.chatBarCache.attachmentPath.length > 0
|
||||
sourceComponent: _private.chatBarCache.isReplying ? replyPane : attachmentPane
|
||||
}
|
||||
Component {
|
||||
id: replyPane
|
||||
ReplyPane {
|
||||
userName: root.currentRoom.chatBoxReplyUser.displayName
|
||||
userColor: root.currentRoom.chatBoxReplyUser.color
|
||||
userAvatar: root.currentRoom.chatBoxReplyUser.avatarSource
|
||||
text: root.currentRoom.chatBoxReplyMessage
|
||||
userName: _private.chatBarCache.relationUser.displayName
|
||||
userColor: _private.chatBarCache.relationUser.color
|
||||
userAvatar: _private.chatBarCache.relationUser.avatarSource
|
||||
text: _private.chatBarCache.relationMessage
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: attachmentPane
|
||||
AttachmentPane {
|
||||
attachmentPath: root.currentRoom.chatBoxAttachmentPath
|
||||
attachmentPath: _private.chatBarCache.attachmentPath
|
||||
|
||||
onAttachmentCancelled: {
|
||||
root.currentRoom.chatBoxAttachmentPath = "";
|
||||
_private.chatBarCache.attachmentPath = "";
|
||||
root.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
@@ -349,14 +350,14 @@ QQC2.Control {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: (root.width - chatBarSizeHelper.currentWidth) / 2 + Kirigami.Units.largeSpacing + (chatBarScrollView.QQC2.ScrollBar.vertical.visible && !(root.width > chatBarSizeHelper.currentWidth) ? Kirigami.Units.largeSpacing * 2.5 : 0)
|
||||
|
||||
visible: root.currentRoom.chatBoxReplyId.length > 0
|
||||
visible: _private.chatBarCache.isReplying
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
action: Kirigami.Action {
|
||||
text: i18nc("@action:button", "Cancel reply")
|
||||
icon.name: "dialog-close"
|
||||
onTriggered: {
|
||||
root.currentRoom.chatBoxReplyId = "";
|
||||
root.currentRoom.chatBoxAttachmentPath = "";
|
||||
_private.chatBarCache.replyId = "";
|
||||
_private.chatBarCache.attachmentPath = "";
|
||||
root.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
@@ -472,16 +473,16 @@ QQC2.Control {
|
||||
if (localPath.length === 0) {
|
||||
return false;
|
||||
}
|
||||
root.currentRoom.chatBoxAttachmentPath = localPath;
|
||||
_private.chatBarCache.attachmentPath = localPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
function postMessage() {
|
||||
actionsHandler.handleNewMessage();
|
||||
actionsHandler.handleMessageEvent(_private.chatBarCache);
|
||||
repeatTimer.stop()
|
||||
root.currentRoom.markAllMessagesAsRead();
|
||||
textField.clear();
|
||||
root.currentRoom.chatBoxReplyId = "";
|
||||
_private.chatBarCache.replyId = "";
|
||||
messageSent()
|
||||
}
|
||||
|
||||
@@ -558,7 +559,7 @@ QQC2.Control {
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
root.currentRoom.chatBoxAttachmentPath = path;
|
||||
_private.chatBarCache.attachmentPath = path;
|
||||
})
|
||||
|
||||
fileDialog.open()
|
||||
@@ -581,7 +582,7 @@ QQC2.Control {
|
||||
if (!Clipboard.saveImage(localPath)) {
|
||||
return;
|
||||
}
|
||||
root.currentRoom.chatBoxAttachmentPath = localPath;
|
||||
_private.chatBarCache.attachmentPath = localPath;
|
||||
attachDialog.close();
|
||||
}
|
||||
}
|
||||
@@ -595,4 +596,10 @@ QQC2.Control {
|
||||
parentWindow: Window.window
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: _private
|
||||
property ChatBarCache chatBarCache
|
||||
onChatBarCacheChanged: documentHandler.chatBarCache = chatBarCache
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ MessageDelegateContextMenu {
|
||||
text: i18n("Reply")
|
||||
icon.name: "mail-replied-symbolic"
|
||||
onTriggered: {
|
||||
currentRoom.chatBoxReplyId = eventId
|
||||
currentRoom.chatBoxEditId = ""
|
||||
currentRoom.mainCache.replyId = eventId;
|
||||
currentRoom.editCache.editId = "";
|
||||
RoomManager.requestFullScreenClose()
|
||||
}
|
||||
},
|
||||
|
||||
@@ -93,8 +93,8 @@ Loader {
|
||||
text: i18n("Edit")
|
||||
icon.name: "document-edit"
|
||||
onTriggered: {
|
||||
currentRoom.chatBoxEditId = eventId;
|
||||
currentRoom.chatBoxReplyId = "";
|
||||
currentRoom.editCache.editId = eventId;
|
||||
currentRoom.mainCache.replyId = "";
|
||||
}
|
||||
visible: author.isLocalUser && (root.delegateType === DelegateType.Emote || root.delegateType === DelegateType.Message)
|
||||
},
|
||||
@@ -102,8 +102,8 @@ Loader {
|
||||
text: i18n("Reply")
|
||||
icon.name: "mail-replied-symbolic"
|
||||
onTriggered: {
|
||||
currentRoom.chatBoxReplyId = eventId;
|
||||
currentRoom.chatBoxEditId = "";
|
||||
currentRoom.mainCache.replyId = eventId;
|
||||
currentRoom.editCache.editId = "";
|
||||
}
|
||||
},
|
||||
Kirigami.Action {
|
||||
|
||||
@@ -13,7 +13,10 @@ QQC2.TextArea {
|
||||
id: root
|
||||
|
||||
required property NeoChatRoom room
|
||||
onRoomChanged: room.chatBoxEditIdChanged.connect(updateEditText)
|
||||
onRoomChanged: {
|
||||
_private.chatBarCache = room.editCache
|
||||
_private.chatBarCache.relationIdChanged.connect(_private.updateEditText)
|
||||
}
|
||||
|
||||
property string messageId
|
||||
|
||||
@@ -26,7 +29,7 @@ QQC2.TextArea {
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
onTextChanged: {
|
||||
room.editText = text
|
||||
_private.chatBarCache.text = text
|
||||
}
|
||||
|
||||
Keys.onEnterPressed: {
|
||||
@@ -88,7 +91,7 @@ QQC2.TextArea {
|
||||
text: i18nc("@action:button", "Cancel edit")
|
||||
icon.name: "dialog-close"
|
||||
onTriggered: {
|
||||
room.chatBoxEditId = "";
|
||||
_private.chatBarCache.editId = "";
|
||||
}
|
||||
shortcut: "Escape"
|
||||
}
|
||||
@@ -134,16 +137,22 @@ QQC2.TextArea {
|
||||
}
|
||||
|
||||
function postEdit() {
|
||||
actionsHandler.handleEdit();
|
||||
actionsHandler.handleMessageEvent(_private.chatBarCache);
|
||||
root.clear();
|
||||
room.chatBoxEditId = "";
|
||||
_private.chatBarCache.editId = "";
|
||||
}
|
||||
|
||||
function updateEditText() {
|
||||
if (room.chatBoxEditId == messageId && room.chatBoxEditMessage.length > 0) {
|
||||
root.text = room.chatBoxEditMessage
|
||||
forceActiveFocus();
|
||||
root.cursorPosition = root.length;
|
||||
QtObject {
|
||||
id: _private
|
||||
property ChatBarCache chatBarCache
|
||||
onChatBarCacheChanged: documentHandler.chatBarCache = chatBarCache
|
||||
|
||||
function updateEditText() {
|
||||
if (chatBarCache.isEditing && chatBarCache.relationMessage.length > 0) {
|
||||
root.text = chatBarCache.relationMessage
|
||||
root.forceActiveFocus();
|
||||
root.cursorPosition = root.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ MessageDelegate {
|
||||
RichLabel {
|
||||
id: label
|
||||
Layout.fillWidth: true
|
||||
visible: currentRoom.chatBoxEditId !== root.eventId
|
||||
visible: currentRoom.editCache.editId !== root.eventId
|
||||
|
||||
isReply: root.isReply
|
||||
|
||||
@@ -59,7 +59,7 @@ MessageDelegate {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: item ? item.minimumHeight : -1
|
||||
Layout.preferredWidth: item ? item.preferredWidth : -1
|
||||
visible: currentRoom.chatBoxEditId === root.eventId
|
||||
visible: currentRoom.editCache.editId === root.eventId
|
||||
active: visible
|
||||
sourceComponent: MessageEditComponent {
|
||||
room: currentRoom
|
||||
|
||||
@@ -182,7 +182,7 @@ QQC2.ScrollView {
|
||||
DropArea {
|
||||
id: dropAreaFile
|
||||
anchors.fill: parent
|
||||
onDropped: root.currentRoom.chatBoxAttachmentPath = drop.urls[0]
|
||||
onDropped: root.currentRoom.mainCache.attachmentPath = drop.urls[0]
|
||||
;
|
||||
enabled: !Controller.isFlatpak
|
||||
}
|
||||
@@ -250,12 +250,13 @@ QQC2.ScrollView {
|
||||
}
|
||||
}
|
||||
onEditClicked: {
|
||||
root.currentRoom.chatBoxEditId = delegate.eventId;
|
||||
root.currentRoom.chatBoxReplyId = "";
|
||||
root.currentRoom.editCache.editId = delegate.eventId;
|
||||
root.currentRoom.mainCache.replyId = "";
|
||||
|
||||
}
|
||||
onReplyClicked: {
|
||||
root.currentRoom.chatBoxReplyId = delegate.eventId;
|
||||
root.currentRoom.chatBoxEditId = "";
|
||||
root.currentRoom.mainCache.replyId = delegate.eventId;
|
||||
root.currentRoom.editCache.editId = "";
|
||||
root.focusChatBox();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "roommanager.h"
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "controller.h"
|
||||
#include "enums/delegatetype.h"
|
||||
#include "models/messageeventmodel.h"
|
||||
@@ -190,15 +191,15 @@ void RoomManager::openRoomForActiveConnection()
|
||||
|
||||
void RoomManager::enterRoom(NeoChatRoom *room)
|
||||
{
|
||||
if (m_currentRoom && !m_currentRoom->chatBoxEditId().isEmpty()) {
|
||||
m_currentRoom->setChatBoxEditId({});
|
||||
if (m_currentRoom && !m_currentRoom->editCache()->editId().isEmpty()) {
|
||||
m_currentRoom->editCache()->setEditId({});
|
||||
}
|
||||
if (m_currentRoom && m_chatDocumentHandler) {
|
||||
// We're doing these things here because it is critical that they are switched at the same time
|
||||
if (m_chatDocumentHandler->document()) {
|
||||
m_currentRoom->setSavedText(m_chatDocumentHandler->document()->textDocument()->toPlainText());
|
||||
m_currentRoom->mainCache()->setSavedText(m_chatDocumentHandler->document()->textDocument()->toPlainText());
|
||||
m_chatDocumentHandler->setRoom(room);
|
||||
m_chatDocumentHandler->document()->textDocument()->setPlainText(room->savedText());
|
||||
m_chatDocumentHandler->document()->textDocument()->setPlainText(room->mainCache()->savedText());
|
||||
} else {
|
||||
m_chatDocumentHandler->setRoom(room);
|
||||
}
|
||||
@@ -232,13 +233,13 @@ void RoomManager::enterSpaceHome(NeoChatRoom *spaceRoom)
|
||||
return;
|
||||
}
|
||||
// If replacing a normal room message timeline make sure any edit is cancelled.
|
||||
if (m_currentRoom && !m_currentRoom->chatBoxEditId().isEmpty()) {
|
||||
m_currentRoom->setChatBoxEditId({});
|
||||
if (m_currentRoom && !m_currentRoom->editCache()->editId().isEmpty()) {
|
||||
m_currentRoom->editCache()->setEditId({});
|
||||
}
|
||||
// Save the chatbar text for the current room if any before switching
|
||||
if (m_currentRoom && m_chatDocumentHandler) {
|
||||
if (m_chatDocumentHandler->document()) {
|
||||
m_currentRoom->setSavedText(m_chatDocumentHandler->document()->textDocument()->toPlainText());
|
||||
m_currentRoom->mainCache()->setSavedText(m_chatDocumentHandler->document()->textDocument()->toPlainText());
|
||||
}
|
||||
}
|
||||
m_lastCurrentRoom = std::exchange(m_currentRoom, spaceRoom);
|
||||
|
||||
Reference in New Issue
Block a user