Hook sending messages back up
This commit is contained in:
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user