Hook sending messages back up

This commit is contained in:
James Graham
2026-02-21 12:12:11 +00:00
parent d2d48110cb
commit c6313d2951
10 changed files with 94 additions and 98 deletions

View File

@@ -7,7 +7,9 @@
#include <QVariantList> #include <QVariantList>
#include "accountmanager.h" #include "accountmanager.h"
#include "blockcache.h"
#include "chatbarcache.h" #include "chatbarcache.h"
#include "enums/messagecomponenttype.h"
#include "models/actionsmodel.h" #include "models/actionsmodel.h"
#include "server.h" #include "server.h"
@@ -89,8 +91,9 @@ void ActionsTest::testActions()
QFETCH(std::optional<Quotient::RoomMessageEvent::MsgType>, type); QFETCH(std::optional<Quotient::RoomMessageEvent::MsgType>, type);
auto cache = new ChatBarCache(this); auto cache = new ChatBarCache(this);
cache->setText(command); cache->cache() += Block::CacheItem{.type = MessageComponentType::Text, .content = QTextDocumentFragment::fromMarkdown(command)};
auto result = ActionsModel::handleAction(room, cache); auto result = ActionsModel::handleAction(room, cache);
qWarning() << result << resultText;
QCOMPARE(resultText, std::get<std::optional<QString>>(result)); QCOMPARE(resultText, std::get<std::optional<QString>>(result));
QCOMPARE(type, std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result)); QCOMPARE(type, std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result));
} }

View File

@@ -14,6 +14,7 @@
#include <KLocalizedString> #include <KLocalizedString>
#include "accountmanager.h" #include "accountmanager.h"
#include "blockcache.h"
#include "chatbarcache.h" #include "chatbarcache.h"
#include "neochatroom.h" #include "neochatroom.h"
@@ -77,7 +78,7 @@ void ChatBarCacheTest::empty()
{ {
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room)); QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
QCOMPARE(chatBarCache->text(), QString()); QCOMPARE(chatBarCache->cache().toString(), QString());
QCOMPARE(chatBarCache->isReplying(), false); QCOMPARE(chatBarCache->isReplying(), false);
QCOMPARE(chatBarCache->replyId(), QString()); QCOMPARE(chatBarCache->replyId(), QString());
QCOMPARE(chatBarCache->isEditing(), false); QCOMPARE(chatBarCache->isEditing(), false);
@@ -123,11 +124,11 @@ void ChatBarCacheTest::badParent()
void ChatBarCacheTest::reply() void ChatBarCacheTest::reply()
{ {
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room)); QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
chatBarCache->setText(u"some text"_s); chatBarCache->cache() += Block::CacheItem{.type = MessageComponentType::Text, .content = QTextDocumentFragment::fromMarkdown(u"some text"_s)};
chatBarCache->setAttachmentPath(u"some/path"_s); chatBarCache->setAttachmentPath(u"some/path"_s);
chatBarCache->setReplyId(eventId); chatBarCache->setReplyId(eventId);
QCOMPARE(chatBarCache->text(), u"some text"_s); QCOMPARE(chatBarCache->cache().toString(), u"some text"_s);
QCOMPARE(chatBarCache->isReplying(), true); QCOMPARE(chatBarCache->isReplying(), true);
QCOMPARE(chatBarCache->replyId(), eventId); QCOMPARE(chatBarCache->replyId(), eventId);
QCOMPARE(chatBarCache->isEditing(), false); QCOMPARE(chatBarCache->isEditing(), false);
@@ -141,11 +142,11 @@ void ChatBarCacheTest::reply()
void ChatBarCacheTest::replyMissingUser() void ChatBarCacheTest::replyMissingUser()
{ {
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room)); QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
chatBarCache->setText(u"some text"_s); chatBarCache->cache() += Block::CacheItem{.type = MessageComponentType::Text, .content = QTextDocumentFragment::fromMarkdown(u"some text"_s)};
chatBarCache->setAttachmentPath(u"some/path"_s); chatBarCache->setAttachmentPath(u"some/path"_s);
chatBarCache->setReplyId(eventId); chatBarCache->setReplyId(eventId);
QCOMPARE(chatBarCache->text(), u"some text"_s); QCOMPARE(chatBarCache->cache().toString(), u"some text"_s);
QCOMPARE(chatBarCache->isReplying(), true); QCOMPARE(chatBarCache->isReplying(), true);
QCOMPARE(chatBarCache->replyId(), eventId); QCOMPARE(chatBarCache->replyId(), eventId);
QCOMPARE(chatBarCache->isEditing(), false); QCOMPARE(chatBarCache->isEditing(), false);
@@ -172,7 +173,7 @@ void ChatBarCacheTest::edit()
{ {
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room)); QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
chatBarCache->setText(u"some text"_s); chatBarCache->cache() += Block::CacheItem{.type = MessageComponentType::Text, .content = QTextDocumentFragment::fromMarkdown(u"some text"_s)};
chatBarCache->setAttachmentPath(u"some/path"_s); chatBarCache->setAttachmentPath(u"some/path"_s);
connect(chatBarCache.get(), &ChatBarCache::relationIdChanged, this, [this](const QString &oldEventId, const QString &newEventId) { connect(chatBarCache.get(), &ChatBarCache::relationIdChanged, this, [this](const QString &oldEventId, const QString &newEventId) {
QCOMPARE(oldEventId, QString()); QCOMPARE(oldEventId, QString());
@@ -180,7 +181,7 @@ void ChatBarCacheTest::edit()
}); });
chatBarCache->setEditId(eventId); chatBarCache->setEditId(eventId);
QCOMPARE(chatBarCache->text(), u"some text"_s); QCOMPARE(chatBarCache->cache().toString(), u"some text"_s);
QCOMPARE(chatBarCache->isReplying(), false); QCOMPARE(chatBarCache->isReplying(), false);
QCOMPARE(chatBarCache->replyId(), QString()); QCOMPARE(chatBarCache->replyId(), QString());
QCOMPARE(chatBarCache->isEditing(), true); QCOMPARE(chatBarCache->isEditing(), true);
@@ -193,11 +194,11 @@ void ChatBarCacheTest::edit()
void ChatBarCacheTest::attachment() void ChatBarCacheTest::attachment()
{ {
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room)); QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
chatBarCache->setText(u"some text"_s); chatBarCache->cache() += Block::CacheItem{.type = MessageComponentType::Text, .content = QTextDocumentFragment::fromMarkdown(u"some text"_s)};
chatBarCache->setEditId(eventId); chatBarCache->setEditId(eventId);
chatBarCache->setAttachmentPath(u"some/path"_s); chatBarCache->setAttachmentPath(u"some/path"_s);
QCOMPARE(chatBarCache->text(), u"some text"_s); QCOMPARE(chatBarCache->cache().toString(), u"some text"_s);
QCOMPARE(chatBarCache->isReplying(), false); QCOMPARE(chatBarCache->isReplying(), false);
QCOMPARE(chatBarCache->replyId(), QString()); QCOMPARE(chatBarCache->replyId(), QString());
QCOMPARE(chatBarCache->isEditing(), false); QCOMPARE(chatBarCache->isEditing(), false);

View File

@@ -3,10 +3,65 @@
#include "blockcache.h" #include "blockcache.h"
#include <QRegularExpression>
#include "chattextitemhelper.h" #include "chattextitemhelper.h"
using namespace Block; using namespace Block;
inline QString formatQuote(const QString &input)
{
QString stringOut;
auto splitString = input.split(u"\n\n"_s, Qt::SkipEmptyParts);
for (auto &string : splitString) {
if (string.startsWith(u'*')) {
string.removeFirst();
}
if (string.startsWith(u'\"')) {
string.removeFirst();
}
if (string.endsWith(u'*')) {
string.removeLast();
}
if (string.endsWith(u'\"')) {
string.removeLast();
}
if (!stringOut.isEmpty()) {
stringOut += u"\n"_s;
}
stringOut += u"> "_s + string;
}
return stringOut;
}
inline QString formatCode(const QString &input)
{
return u"```\n%1\n```"_s.arg(input).replace(u"\n\n"_s, u"\n"_s);
}
inline QString trim(QString string)
{
while (string.startsWith(u"\n"_s)) {
string.removeFirst();
}
while (string.endsWith(u"\n"_s)) {
string.removeLast();
}
return string;
}
QString CacheItem::toString() const
{
auto newText = trim(content.toMarkdown(QTextDocument::MarkdownDialectGitHub));
newText.replace(QRegularExpression(u"(?<!\n)\n(?!\n)"_s), u" "_s);
if (type == MessageComponentType::Quote) {
newText = formatQuote(newText);
} else if (type == MessageComponentType::Code) {
newText = formatCode(newText);
}
return newText;
}
void Cache::fill(QList<MessageComponent> components) void Cache::fill(QList<MessageComponent> components)
{ {
std::ranges::for_each(components, [this](const MessageComponent &component) { std::ranges::for_each(components, [this](const MessageComponent &component) {
@@ -23,3 +78,15 @@ void Cache::fill(QList<MessageComponent> components)
}); });
}); });
} }
QString Cache::toString() const
{
QString text;
std::ranges::for_each(constBegin(), constEnd(), [&text](const CacheItem &item) {
if (!text.isEmpty()) {
text += u"\n\n"_s;
}
text += item.toString();
});
return text;
}

View File

@@ -14,6 +14,8 @@ namespace Block
struct CacheItem { struct CacheItem {
MessageComponentType::Type type = MessageComponentType::Other; MessageComponentType::Type type = MessageComponentType::Other;
QTextDocumentFragment content; QTextDocumentFragment content;
QString toString() const;
}; };
class Cache : private QList<CacheItem> class Cache : private QList<CacheItem>
@@ -22,7 +24,10 @@ public:
using QList<CacheItem>::constBegin, QList<CacheItem>::constEnd; using QList<CacheItem>::constBegin, QList<CacheItem>::constEnd;
using QList<CacheItem>::isEmpty; using QList<CacheItem>::isEmpty;
using QList<CacheItem>::clear; using QList<CacheItem>::clear;
using QList<CacheItem>::append, QList<CacheItem>::operator+=, QList<CacheItem>::operator<<;
void fill(QList<MessageComponent> components); void fill(QList<MessageComponent> components);
QString toString() const;
}; };
} }

View File

@@ -38,29 +38,16 @@ Block::Cache &ChatBarCache::cache()
return m_cache; return m_cache;
} }
QString ChatBarCache::text() const
{
return m_text;
}
void ChatBarCache::setText(const QString &text)
{
if (text == m_text) {
return;
}
m_text = text;
Q_EMIT textChanged();
}
QString ChatBarCache::sendText() const QString ChatBarCache::sendText() const
{ {
const auto cacheText = m_cache.toString();
if (!attachmentPath().isEmpty()) { if (!attachmentPath().isEmpty()) {
QUrl url(attachmentPath()); QUrl url(attachmentPath());
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString(); auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
return text().isEmpty() ? path.mid(path.lastIndexOf(u'/') + 1) : text(); return cacheText.isEmpty() ? path.mid(path.lastIndexOf(u'/') + 1) : cacheText;
} }
return text(); return cacheText;
} }
bool ChatBarCache::isReplying() const bool ChatBarCache::isReplying() const
@@ -267,13 +254,16 @@ void ChatBarCache::postMessage()
auto content = std::make_unique<Quotient::EventContent::TextContent>(sendText, u"text/html"_s); auto content = std::make_unique<Quotient::EventContent::TextContent>(sendText, u"text/html"_s);
room->post<Quotient::RoomMessageEvent>(text(), *std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result), std::move(content), relatesTo); room->post<Quotient::RoomMessageEvent>(m_cache.toString(),
*std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result),
std::move(content),
relatesTo);
clearCache(); clearCache();
} }
void ChatBarCache::clearCache() void ChatBarCache::clearCache()
{ {
setText({}); m_cache.clear();
m_savedText = QString(); m_savedText = QString();
clearRelations(); clearRelations();
} }

View File

@@ -39,14 +39,6 @@ class ChatBarCache : public QObject
QML_ELEMENT QML_ELEMENT
QML_UNCREATABLE("") 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. * @brief Whether the chat bar is currently replying to a message.
*/ */
@@ -139,10 +131,7 @@ public:
explicit ChatBarCache(QObject *parent = nullptr); explicit ChatBarCache(QObject *parent = nullptr);
Block::Cache &cache(); Block::Cache &cache();
QString text() const;
QString sendText() const; QString sendText() const;
void setText(const QString &text);
bool isReplying() const; bool isReplying() const;
QString replyId() const; QString replyId() const;
@@ -189,7 +178,6 @@ public:
Q_INVOKABLE void drop(QList<QUrl> urls, const QString &transferPortal); Q_INVOKABLE void drop(QList<QUrl> urls, const QString &transferPortal);
Q_SIGNALS: Q_SIGNALS:
void textChanged();
void relationIdChanged(const QString &oldEventId, const QString &newEventId); void relationIdChanged(const QString &oldEventId, const QString &newEventId);
void threadIdChanged(const QString &oldThreadId, const QString &newThreadId); void threadIdChanged(const QString &oldThreadId, const QString &newThreadId);
void attachmentPathChanged(); void attachmentPathChanged();
@@ -199,8 +187,6 @@ Q_SIGNALS:
private: private:
Block::Cache m_cache; Block::Cache m_cache;
QString m_text = QString();
QString m_relationId = QString(); QString m_relationId = QString();
RelationType m_relationType = RelationType::None; RelationType m_relationType = RelationType::None;
QString m_threadId = QString(); QString m_threadId = QString();

View File

@@ -7,7 +7,6 @@
#include <QQuickTextDocument> #include <QQuickTextDocument>
#include <QTextCursor> #include <QTextCursor>
#include <QTextDocumentFragment>
#include <Kirigami/Platform/PlatformTheme> #include <Kirigami/Platform/PlatformTheme>
#include <qtextdocument.h> #include <qtextdocument.h>

View File

@@ -6,7 +6,6 @@
#include <QObject> #include <QObject>
#include <QQuickItem> #include <QQuickItem>
#include <QTextDocumentFragment> #include <QTextDocumentFragment>
#include <qtextdocumentfragment.h>
#include "enums/chatbartype.h" #include "enums/chatbartype.h"
#include "enums/richformat.h" #include "enums/richformat.h"

View File

@@ -596,59 +596,6 @@ void ChatBarMessageContentModel::updateCache() const
m_room->cacheForType(m_type)->cache().fill(m_components); m_room->cacheForType(m_type)->cache().fill(m_components);
} }
inline QString formatQuote(const QString &input)
{
QString stringOut;
auto splitString = input.split(u"\n\n"_s, Qt::SkipEmptyParts);
for (auto &string : splitString) {
if (string.startsWith(u'*')) {
string.removeFirst();
}
if (string.startsWith(u'\"')) {
string.removeFirst();
}
if (string.endsWith(u'*')) {
string.removeLast();
}
if (string.endsWith(u'\"')) {
string.removeLast();
}
if (!stringOut.isEmpty()) {
stringOut += u"\n"_s;
}
stringOut += u"> "_s + string;
}
return stringOut;
}
inline QString formatCode(const QString &input)
{
return u"```\n%1\n```"_s.arg(input).replace(u"\n\n"_s, u"\n"_s);
}
QString ChatBarMessageContentModel::messageText() const
{
QString text;
for (const auto &component : m_components) {
if (MessageComponentType::isTextType(component.type)) {
if (const auto textItem = textItemForComponent(component)) {
auto newText = textItem->markdownText();
newText.replace(QRegularExpression(u"(?<!\n)\n(?!\n)"_s), u" "_s);
if (component.type == MessageComponentType::Quote) {
newText = formatQuote(newText);
} else if (component.type == MessageComponentType::Code) {
newText = formatCode(newText);
}
if (!text.isEmpty()) {
text += u"\n\n"_s;
}
text += newText;
}
}
}
return text;
}
void ChatBarMessageContentModel::postMessage() void ChatBarMessageContentModel::postMessage()
{ {
if (m_type == ChatBarType::None || !m_room) { if (m_type == ChatBarType::None || !m_room) {

View File

@@ -150,7 +150,6 @@ private:
void handleBlockTransition(bool up); void handleBlockTransition(bool up);
void updateCache() const; void updateCache() const;
QString messageText() const;
bool m_sendMessageWithEnter = true; bool m_sendMessageWithEnter = true;