Compare commits
107 Commits
work/nvrwh
...
release/24
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c736b7d1d | ||
|
|
a2914cf7b1 | ||
|
|
05f0277512 | ||
|
|
e82a5c9442 | ||
|
|
896e09bb5b | ||
|
|
52747355a4 | ||
|
|
01f1d655e5 | ||
|
|
4b7011fe14 | ||
|
|
5550091046 | ||
|
|
fc2e751af9 | ||
|
|
36137a036c | ||
|
|
e0f3d544f0 | ||
|
|
eb802ff91f | ||
|
|
0e697fc7e3 | ||
|
|
89d546f291 | ||
|
|
c0324d53e5 | ||
|
|
25084ed46d | ||
|
|
e274e09bbc | ||
|
|
389016f126 | ||
|
|
e270f553a3 | ||
|
|
a0fef7b397 | ||
|
|
588266d7c3 | ||
|
|
258e799138 | ||
|
|
73cd253e25 | ||
|
|
eee81329a4 | ||
|
|
9b425e5a54 | ||
|
|
fb83d5cff8 | ||
|
|
21cabeb20c | ||
|
|
bed04cc59f | ||
|
|
18db04d073 | ||
|
|
0f05c1b921 | ||
|
|
765292e0e9 | ||
|
|
7c27308a59 | ||
|
|
b574e68164 | ||
|
|
66d16dd16b | ||
|
|
b1c810c242 | ||
|
|
8f0b3a16f1 | ||
|
|
043519784b | ||
|
|
aafe42c4cc | ||
|
|
d9b9b18872 | ||
|
|
18e26c9e3a | ||
|
|
19c93e9d82 | ||
|
|
af6187706e | ||
|
|
bf18fcbbba | ||
|
|
ee5502d7bf | ||
|
|
d8d6a4ef3b | ||
|
|
2803c6fd58 | ||
|
|
f2f6406403 | ||
|
|
63dc8a5857 | ||
|
|
3dafc62f04 | ||
|
|
5be112e0e2 | ||
|
|
b15ee49691 | ||
|
|
fa5872ec5a | ||
|
|
54061c744f | ||
|
|
b6dac3bbdf | ||
|
|
3a838596c5 | ||
|
|
cc22e80adc | ||
|
|
1c0a5edd2e | ||
|
|
47408d536d | ||
|
|
fc21eea7e7 | ||
|
|
ca95eb3505 | ||
|
|
639cbed343 | ||
|
|
be3cffaf3a | ||
|
|
7af520d0ae | ||
|
|
d145177014 | ||
|
|
75aa2c0e8d | ||
|
|
7eda952aa2 | ||
|
|
22d0d84fee | ||
|
|
941a724381 | ||
|
|
7e880c6663 | ||
|
|
22315b810c | ||
|
|
d0c41d5224 | ||
|
|
a7e06375fd | ||
|
|
e638f61ff1 | ||
|
|
2218b39a50 | ||
|
|
b34936a017 | ||
|
|
5b3e650854 | ||
|
|
86d85c6ce7 | ||
|
|
863e20394a | ||
|
|
908dcea75e | ||
|
|
c59d5bc75d | ||
|
|
ff4ba4df9c | ||
|
|
0c1494e74a | ||
|
|
18d4da8b42 | ||
|
|
9680cbbb38 | ||
|
|
3b5ba470b6 | ||
|
|
e1066aede2 | ||
|
|
2bf4ed26f0 | ||
|
|
b460b588a8 | ||
|
|
05270c38bf | ||
|
|
18afad83db | ||
|
|
d390433b2b | ||
|
|
0017be1c0f | ||
|
|
63eda3796d | ||
|
|
3246076a0b | ||
|
|
e905cdd151 | ||
|
|
0372074beb | ||
|
|
09e97f2bdb | ||
|
|
8e324c16f3 | ||
|
|
2bb55eece7 | ||
|
|
2877e40647 | ||
|
|
768ec242fa | ||
|
|
194751627f | ||
|
|
bc701c51d9 | ||
|
|
35939b4af4 | ||
|
|
a178b8b6ca | ||
|
|
a6994318de |
@@ -2,6 +2,5 @@
|
|||||||
; SPDX-License-Identifier: CC0-1.0
|
; SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
[BlueprintSettings]
|
[BlueprintSettings]
|
||||||
kde/applications/neochat.packageAppx=True
|
|
||||||
kde/frameworks/extra-cmake-modules.version=master
|
kde/frameworks/extra-cmake-modules.version=master
|
||||||
libs/qt.qtMajorVersion=6
|
libs/qt.qtMajorVersion=6
|
||||||
|
|||||||
@@ -13,4 +13,3 @@ include:
|
|||||||
- /gitlab-templates/craft-android-qt6-apks.yml
|
- /gitlab-templates/craft-android-qt6-apks.yml
|
||||||
- /gitlab-templates/craft-appimage-qt6.yml
|
- /gitlab-templates/craft-appimage-qt6.yml
|
||||||
- /gitlab-templates/craft-windows-x86-64-qt6.yml
|
- /gitlab-templates/craft-windows-x86-64-qt6.yml
|
||||||
- /gitlab-templates/craft-windows-appx-qt6.yml
|
|
||||||
@@ -8,13 +8,13 @@ cmake_minimum_required(VERSION 3.16)
|
|||||||
|
|
||||||
# KDE Applications version, managed by release script.
|
# KDE Applications version, managed by release script.
|
||||||
set(RELEASE_SERVICE_VERSION_MAJOR "24")
|
set(RELEASE_SERVICE_VERSION_MAJOR "24")
|
||||||
set(RELEASE_SERVICE_VERSION_MINOR "11")
|
set(RELEASE_SERVICE_VERSION_MINOR "08")
|
||||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
set(RELEASE_SERVICE_VERSION_MICRO "3")
|
||||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||||
|
|
||||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||||
|
|
||||||
set(KF_MIN_VERSION "6.5")
|
set(KF_MIN_VERSION "6.0")
|
||||||
set(QT_MIN_VERSION "6.5")
|
set(QT_MIN_VERSION "6.5")
|
||||||
|
|
||||||
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
|
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
|
||||||
|
|||||||
@@ -107,13 +107,8 @@ void ChatBarCacheTest::reply()
|
|||||||
void ChatBarCacheTest::edit()
|
void ChatBarCacheTest::edit()
|
||||||
{
|
{
|
||||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||||
|
|
||||||
chatBarCache->setText(QLatin1String("some text"));
|
chatBarCache->setText(QLatin1String("some text"));
|
||||||
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
|
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
|
||||||
connect(chatBarCache.get(), &ChatBarCache::relationIdChanged, this, [](const QString &oldEventId, const QString &newEventId) {
|
|
||||||
QCOMPARE(oldEventId, QString());
|
|
||||||
QCOMPARE(newEventId, QString(QLatin1String("$153456789:example.org")));
|
|
||||||
});
|
|
||||||
chatBarCache->setEditId(QLatin1String("$153456789:example.org"));
|
chatBarCache->setEditId(QLatin1String("$153456789:example.org"));
|
||||||
|
|
||||||
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
|
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ private:
|
|||||||
Connection *connection = nullptr;
|
Connection *connection = nullptr;
|
||||||
TestUtils::TestRoom *room = nullptr;
|
TestUtils::TestRoom *room = nullptr;
|
||||||
|
|
||||||
|
EventHandler emptyHandler = EventHandler(nullptr, nullptr);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
|
|
||||||
@@ -41,6 +43,7 @@ private Q_SLOTS:
|
|||||||
void time();
|
void time();
|
||||||
void nullTime();
|
void nullTime();
|
||||||
void timeString();
|
void timeString();
|
||||||
|
void nullTimeString();
|
||||||
void highlighted();
|
void highlighted();
|
||||||
void nullHighlighted();
|
void nullHighlighted();
|
||||||
void hidden();
|
void hidden();
|
||||||
@@ -62,6 +65,10 @@ private Q_SLOTS:
|
|||||||
void nullReplyId();
|
void nullReplyId();
|
||||||
void replyAuthor();
|
void replyAuthor();
|
||||||
void nullReplyAuthor();
|
void nullReplyAuthor();
|
||||||
|
void replyBody();
|
||||||
|
void nullReplyBody();
|
||||||
|
void replyMediaInfo();
|
||||||
|
void nullReplyMediaInfo();
|
||||||
void thread();
|
void thread();
|
||||||
void nullThread();
|
void nullThread();
|
||||||
void location();
|
void location();
|
||||||
@@ -76,136 +83,157 @@ void EventHandlerTest::initTestCase()
|
|||||||
|
|
||||||
void EventHandlerTest::eventId()
|
void EventHandlerTest::eventId()
|
||||||
{
|
{
|
||||||
QCOMPARE(EventHandler::id(room->messageEvents().at(0).get()), QStringLiteral("$153456789:example.org"));
|
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||||
|
QCOMPARE(eventHandler.getId(), QStringLiteral("$153456789:example.org"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::nullEventId()
|
void EventHandlerTest::nullEventId()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "id called with event set to nullptr.");
|
EventHandler noEventHandler(room, nullptr);
|
||||||
QCOMPARE(EventHandler::id(nullptr), QString());
|
QTest::ignoreMessage(QtWarningMsg, "getId called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.getId(), QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::authorDisplayName()
|
void EventHandlerTest::authorDisplayName()
|
||||||
{
|
{
|
||||||
QCOMPARE(EventHandler::authorDisplayName(room, room->messageEvents().at(1).get()), QStringLiteral("before"));
|
EventHandler eventHandler(room, room->messageEvents().at(1).get());
|
||||||
|
QCOMPARE(eventHandler.getAuthorDisplayName(), QStringLiteral("before"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::nullAuthorDisplayName()
|
void EventHandlerTest::nullAuthorDisplayName()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "authorDisplayName called with room set to nullptr.");
|
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_room set to nullptr.");
|
||||||
QCOMPARE(EventHandler::authorDisplayName(nullptr, nullptr), QString());
|
QCOMPARE(emptyHandler.getAuthorDisplayName(), QString());
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "authorDisplayName called with event set to nullptr.");
|
EventHandler noEventHandler(room, nullptr);
|
||||||
QCOMPARE(EventHandler::authorDisplayName(room, nullptr), QString());
|
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.getAuthorDisplayName(), QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::singleLineSidplayName()
|
void EventHandlerTest::singleLineSidplayName()
|
||||||
{
|
{
|
||||||
QCOMPARE(EventHandler::singleLineAuthorDisplayname(room, room->messageEvents().at(11).get()),
|
EventHandler eventHandler(room, room->messageEvents().at(11).get());
|
||||||
QStringLiteral("Look at me I put newlines in my display name"));
|
QCOMPARE(eventHandler.singleLineAuthorDisplayname(), QStringLiteral("Look at me I put newlines in my display name"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::nullSingleLineDisplayName()
|
void EventHandlerTest::nullSingleLineDisplayName()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "singleLineAuthorDisplayname called with room set to nullptr.");
|
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_room set to nullptr.");
|
||||||
QCOMPARE(EventHandler::singleLineAuthorDisplayname(nullptr, nullptr), QString());
|
QCOMPARE(emptyHandler.singleLineAuthorDisplayname(), QString());
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "singleLineAuthorDisplayname called with event set to nullptr.");
|
EventHandler noEventHandler(room, nullptr);
|
||||||
QCOMPARE(EventHandler::singleLineAuthorDisplayname(room, nullptr), QString());
|
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.singleLineAuthorDisplayname(), QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::time()
|
void EventHandlerTest::time()
|
||||||
{
|
{
|
||||||
const auto event = room->messageEvents().at(0).get();
|
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||||
|
|
||||||
QCOMPARE(EventHandler::time(event), QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC));
|
QCOMPARE(eventHandler.getTime(), QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC));
|
||||||
QCOMPARE(EventHandler::time(event, true, QDateTime::fromMSecsSinceEpoch(1234, Qt::UTC)), QDateTime::fromMSecsSinceEpoch(1234, Qt::UTC));
|
QCOMPARE(eventHandler.getTime(true, QDateTime::fromMSecsSinceEpoch(1234, Qt::UTC)), QDateTime::fromMSecsSinceEpoch(1234, Qt::UTC));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::nullTime()
|
void EventHandlerTest::nullTime()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "time called with event set to nullptr.");
|
EventHandler noEventHandler(room, nullptr);
|
||||||
QCOMPARE(EventHandler::time(nullptr), QDateTime());
|
QTest::ignoreMessage(QtWarningMsg, "getTime called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.getTime(), QDateTime());
|
||||||
|
|
||||||
|
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||||
QTest::ignoreMessage(QtWarningMsg, "a value must be provided for lastUpdated for a pending event.");
|
QTest::ignoreMessage(QtWarningMsg, "a value must be provided for lastUpdated for a pending event.");
|
||||||
QCOMPARE(EventHandler::time(room->messageEvents().at(0).get(), true), QDateTime());
|
QCOMPARE(eventHandler.getTime(true), QDateTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::timeString()
|
void EventHandlerTest::timeString()
|
||||||
{
|
{
|
||||||
const auto event = room->messageEvents().at(0).get();
|
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||||
|
|
||||||
KFormat format;
|
KFormat format;
|
||||||
|
|
||||||
QCOMPARE(EventHandler::timeString(event, false),
|
QCOMPARE(eventHandler.getTimeString(false),
|
||||||
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toLocalTime().time(), QLocale::ShortFormat));
|
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toLocalTime().time(), QLocale::ShortFormat));
|
||||||
QCOMPARE(EventHandler::timeString(event, true),
|
QCOMPARE(eventHandler.getTimeString(true),
|
||||||
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toLocalTime().date(), QLocale::ShortFormat));
|
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toLocalTime().date(), QLocale::ShortFormat));
|
||||||
QCOMPARE(EventHandler::timeString(event, false, QLocale::ShortFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
QCOMPARE(eventHandler.getTimeString(false, QLocale::ShortFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
||||||
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().time(), QLocale::ShortFormat));
|
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().time(), QLocale::ShortFormat));
|
||||||
QCOMPARE(EventHandler::timeString(event, true, QLocale::ShortFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
QCOMPARE(eventHandler.getTimeString(true, QLocale::ShortFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
||||||
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::ShortFormat));
|
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::ShortFormat));
|
||||||
QCOMPARE(EventHandler::timeString(event, false, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
QCOMPARE(eventHandler.getTimeString(false, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
||||||
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().time(), QLocale::LongFormat));
|
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().time(), QLocale::LongFormat));
|
||||||
QCOMPARE(EventHandler::timeString(event, true, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
QCOMPARE(eventHandler.getTimeString(true, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
||||||
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::LongFormat));
|
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::LongFormat));
|
||||||
QCOMPARE(EventHandler::timeString(event, QStringLiteral("hh:mm")),
|
QCOMPARE(eventHandler.getTimeString(QStringLiteral("hh:mm")), QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toString(QStringLiteral("hh:mm")));
|
||||||
QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toString(QStringLiteral("hh:mm")));
|
}
|
||||||
|
|
||||||
|
void EventHandlerTest::nullTimeString()
|
||||||
|
{
|
||||||
|
EventHandler noEventHandler(room, nullptr);
|
||||||
|
QTest::ignoreMessage(QtWarningMsg, "getTimeString called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.getTimeString(false), QString());
|
||||||
|
|
||||||
|
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||||
|
QTest::ignoreMessage(QtWarningMsg, "a value must be provided for lastUpdated for a pending event.");
|
||||||
|
QCOMPARE(eventHandler.getTimeString(false, QLocale::ShortFormat, true), QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::highlighted()
|
void EventHandlerTest::highlighted()
|
||||||
{
|
{
|
||||||
QCOMPARE(EventHandler::isHighlighted(room, room->messageEvents().at(2).get()), true);
|
EventHandler eventHandlerHighlight(room, room->messageEvents().at(2).get());
|
||||||
QCOMPARE(EventHandler::isHighlighted(room, room->messageEvents().at(0).get()), false);
|
QCOMPARE(eventHandlerHighlight.isHighlighted(), true);
|
||||||
|
|
||||||
|
EventHandler eventHandlerNoHighlight(room, room->messageEvents().at(0).get());
|
||||||
|
QCOMPARE(eventHandlerNoHighlight.isHighlighted(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::nullHighlighted()
|
void EventHandlerTest::nullHighlighted()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with room set to nullptr.");
|
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with m_room set to nullptr.");
|
||||||
QCOMPARE(EventHandler::isHighlighted(nullptr, nullptr), false);
|
QCOMPARE(emptyHandler.isHighlighted(), false);
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with event set to nullptr.");
|
EventHandler noEventHandler(room, nullptr);
|
||||||
QCOMPARE(EventHandler::isHighlighted(room, nullptr), false);
|
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.isHighlighted(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::hidden()
|
void EventHandlerTest::hidden()
|
||||||
{
|
{
|
||||||
QCOMPARE(EventHandler::isHidden(room, room->messageEvents().at(3).get()), true);
|
EventHandler eventHandlerHidden(room, room->messageEvents().at(3).get());
|
||||||
QCOMPARE(EventHandler::isHidden(room, room->messageEvents().at(0).get()), false);
|
QCOMPARE(eventHandlerHidden.isHidden(), true);
|
||||||
|
|
||||||
|
EventHandler eventHandlerNoHidden(room, room->messageEvents().at(0).get());
|
||||||
|
QCOMPARE(eventHandlerNoHidden.isHidden(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::nullHidden()
|
void EventHandlerTest::nullHidden()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "isHidden called with room set to nullptr.");
|
QTest::ignoreMessage(QtWarningMsg, "isHidden called with m_room set to nullptr.");
|
||||||
QCOMPARE(EventHandler::isHidden(nullptr, nullptr), false);
|
QCOMPARE(emptyHandler.isHidden(), false);
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "isHidden called with event set to nullptr.");
|
EventHandler noEventHandler(room, nullptr);
|
||||||
QCOMPARE(EventHandler::isHidden(room, nullptr), false);
|
QTest::ignoreMessage(QtWarningMsg, "isHidden called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.isHidden(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::body()
|
void EventHandlerTest::body()
|
||||||
{
|
{
|
||||||
const auto event = room->messageEvents().at(0).get();
|
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||||
|
|
||||||
QCOMPARE(EventHandler::richBody(room, event), QStringLiteral("<b>This is an example<br>text message</b>"));
|
QCOMPARE(eventHandler.getRichBody(), QStringLiteral("<b>This is an example<br>text message</b>"));
|
||||||
QCOMPARE(EventHandler::richBody(room, event, true), QStringLiteral("<b>This is an example text message</b>"));
|
QCOMPARE(eventHandler.getRichBody(true), QStringLiteral("<b>This is an example text message</b>"));
|
||||||
QCOMPARE(EventHandler::plainBody(room, event), QStringLiteral("This is an example\ntext message"));
|
QCOMPARE(eventHandler.getPlainBody(), QStringLiteral("This is an example\ntext message"));
|
||||||
QCOMPARE(EventHandler::plainBody(room, event, true), QStringLiteral("This is an example text message"));
|
QCOMPARE(eventHandler.getPlainBody(true), QStringLiteral("This is an example text message"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::nullBody()
|
void EventHandlerTest::nullBody()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "richBody called with room set to nullptr.");
|
EventHandler noEventHandler(room, nullptr);
|
||||||
QCOMPARE(EventHandler::richBody(nullptr, nullptr), QString());
|
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "richBody called with event set to nullptr.");
|
QTest::ignoreMessage(QtWarningMsg, "getRichBody called with m_event set to nullptr.");
|
||||||
QCOMPARE(EventHandler::richBody(room, nullptr), QString());
|
QCOMPARE(noEventHandler.getRichBody(), QString());
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "plainBody called with room set to nullptr.");
|
QTest::ignoreMessage(QtWarningMsg, "getPlainBody called with m_event set to nullptr.");
|
||||||
QCOMPARE(EventHandler::plainBody(nullptr, nullptr), QString());
|
QCOMPARE(noEventHandler.getPlainBody(), QString());
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "plainBody called with event set to nullptr.");
|
|
||||||
QCOMPARE(EventHandler::plainBody(room, nullptr), QString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::genericBody_data()
|
void EventHandlerTest::genericBody_data()
|
||||||
@@ -225,45 +253,54 @@ void EventHandlerTest::genericBody()
|
|||||||
QFETCH(int, eventNum);
|
QFETCH(int, eventNum);
|
||||||
QFETCH(QString, output);
|
QFETCH(QString, output);
|
||||||
|
|
||||||
QCOMPARE(EventHandler::genericBody(room->messageEvents().at(eventNum).get()), output);
|
EventHandler eventHandler(room, room->messageEvents().at(eventNum).get());
|
||||||
|
|
||||||
|
QCOMPARE(eventHandler.getGenericBody(), output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::nullGenericBody()
|
void EventHandlerTest::nullGenericBody()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "genericBody called with event set to nullptr.");
|
EventHandler noEventHandler(room, nullptr);
|
||||||
QCOMPARE(EventHandler::genericBody(nullptr), QString());
|
QTest::ignoreMessage(QtWarningMsg, "getGenericBody called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.getGenericBody(), QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::markdownBody()
|
void EventHandlerTest::markdownBody()
|
||||||
{
|
{
|
||||||
QCOMPARE(EventHandler::markdownBody(room->messageEvents().at(0).get()), QStringLiteral("This is an example\ntext message"));
|
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||||
|
|
||||||
|
QCOMPARE(eventHandler.getMarkdownBody(), QStringLiteral("This is an example\ntext message"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::markdownBodyReply()
|
void EventHandlerTest::markdownBodyReply()
|
||||||
{
|
{
|
||||||
QCOMPARE(EventHandler::markdownBody(room->messageEvents().at(5).get()), QStringLiteral("reply"));
|
EventHandler eventHandler(room, room->messageEvents().at(5).get());
|
||||||
|
|
||||||
|
QCOMPARE(eventHandler.getMarkdownBody(), QStringLiteral("reply"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::subtitle()
|
void EventHandlerTest::subtitle()
|
||||||
{
|
{
|
||||||
QCOMPARE(EventHandler::subtitleText(room, room->messageEvents().at(0).get()), QStringLiteral("after: This is an example text message"));
|
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||||
QCOMPARE(EventHandler::subtitleText(room, room->messageEvents().at(2).get()),
|
QCOMPARE(eventHandler.subtitleText(), QStringLiteral("after: This is an example text message"));
|
||||||
QStringLiteral("after: This is a highlight @bob:kde.org and this is a link https://kde.org"));
|
|
||||||
|
EventHandler eventHandler2(room, room->messageEvents().at(2).get());
|
||||||
|
QCOMPARE(eventHandler2.subtitleText(), QStringLiteral("after: This is a highlight @bob:kde.org and this is a link https://kde.org"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::nullSubtitle()
|
void EventHandlerTest::nullSubtitle()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "subtitleText called with room set to nullptr.");
|
EventHandler noEventHandler(room, nullptr);
|
||||||
QCOMPARE(EventHandler::subtitleText(nullptr, nullptr), QString());
|
QTest::ignoreMessage(QtWarningMsg, "subtitleText called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.subtitleText(), QString());
|
||||||
QTest::ignoreMessage(QtWarningMsg, "subtitleText called with event set to nullptr.");
|
|
||||||
QCOMPARE(EventHandler::subtitleText(room, nullptr), QString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::mediaInfo()
|
void EventHandlerTest::mediaInfo()
|
||||||
{
|
{
|
||||||
auto event = room->messageEvents().at(4).get();
|
auto event = room->messageEvents().at(4).get();
|
||||||
auto mediaInfo = EventHandler::mediaInfo(room, event);
|
EventHandler eventHandler(room, event);
|
||||||
|
|
||||||
|
auto mediaInfo = eventHandler.getMediaInfo();
|
||||||
auto thumbnailInfo = mediaInfo["tempInfo"_ls].toMap();
|
auto thumbnailInfo = mediaInfo["tempInfo"_ls].toMap();
|
||||||
|
|
||||||
QCOMPARE(mediaInfo["source"_ls], room->makeMediaUrl(event->id(), QUrl("mxc://kde.org/1234567"_ls)));
|
QCOMPARE(mediaInfo["source"_ls], room->makeMediaUrl(event->id(), QUrl("mxc://kde.org/1234567"_ls)));
|
||||||
@@ -283,42 +320,53 @@ void EventHandlerTest::mediaInfo()
|
|||||||
|
|
||||||
void EventHandlerTest::nullMediaInfo()
|
void EventHandlerTest::nullMediaInfo()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "mediaInfo called with room set to nullptr.");
|
QTest::ignoreMessage(QtWarningMsg, "getMediaInfo called with m_room set to nullptr.");
|
||||||
QCOMPARE(EventHandler::mediaInfo(nullptr, nullptr), QVariantMap());
|
QCOMPARE(emptyHandler.getMediaInfo(), QVariantMap());
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "mediaInfo called with event set to nullptr.");
|
EventHandler noEventHandler(room, nullptr);
|
||||||
QCOMPARE(EventHandler::mediaInfo(room, nullptr), QVariantMap());
|
QTest::ignoreMessage(QtWarningMsg, "getMediaInfo called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.getMediaInfo(), QVariantMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::hasReply()
|
void EventHandlerTest::hasReply()
|
||||||
{
|
{
|
||||||
QCOMPARE(EventHandler::hasReply(room->messageEvents().at(5).get()), true);
|
EventHandler eventHandlerReply(room, room->messageEvents().at(5).get());
|
||||||
QCOMPARE(EventHandler::hasReply(room->messageEvents().at(0).get()), false);
|
QCOMPARE(eventHandlerReply.hasReply(), true);
|
||||||
|
|
||||||
|
EventHandler eventHandlerNoReply(room, room->messageEvents().at(0).get());
|
||||||
|
QCOMPARE(eventHandlerNoReply.hasReply(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::nullHasReply()
|
void EventHandlerTest::nullHasReply()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "hasReply called with event set to nullptr.");
|
EventHandler noEventHandler(room, nullptr);
|
||||||
QCOMPARE(EventHandler::hasReply(nullptr), false);
|
QTest::ignoreMessage(QtWarningMsg, "hasReply called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.hasReply(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::replyId()
|
void EventHandlerTest::replyId()
|
||||||
{
|
{
|
||||||
QCOMPARE(EventHandler::replyId(room->messageEvents().at(5).get()), QStringLiteral("$153456789:example.org"));
|
EventHandler eventHandlerReply(room, room->messageEvents().at(5).get());
|
||||||
QCOMPARE(EventHandler::replyId(room->messageEvents().at(0).get()), QStringLiteral(""));
|
QCOMPARE(eventHandlerReply.getReplyId(), QStringLiteral("$153456789:example.org"));
|
||||||
|
|
||||||
|
EventHandler eventHandlerNoReply(room, room->messageEvents().at(0).get());
|
||||||
|
QCOMPARE(eventHandlerNoReply.getReplyId(), QStringLiteral(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::nullReplyId()
|
void EventHandlerTest::nullReplyId()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "replyId called with event set to nullptr.");
|
EventHandler noEventHandler(room, nullptr);
|
||||||
QCOMPARE(EventHandler::replyId(nullptr), QString());
|
QTest::ignoreMessage(QtWarningMsg, "getReplyId called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.getReplyId(), QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::replyAuthor()
|
void EventHandlerTest::replyAuthor()
|
||||||
{
|
{
|
||||||
auto replyEvent = room->messageEvents().at(0).get();
|
auto replyEvent = room->messageEvents().at(0).get();
|
||||||
auto replyAuthor = room->member(replyEvent->senderId());
|
auto replyAuthor = room->member(replyEvent->senderId());
|
||||||
auto eventHandlerReplyAuthor = EventHandler::replyAuthor(room, room->messageEvents().at(5).get());
|
EventHandler eventHandler(room, room->messageEvents().at(5).get());
|
||||||
|
|
||||||
|
auto eventHandlerReplyAuthor = eventHandler.getReplyAuthor();
|
||||||
|
|
||||||
QCOMPARE(eventHandlerReplyAuthor.isLocalMember(), replyAuthor.id() == room->localMember().id());
|
QCOMPARE(eventHandlerReplyAuthor.isLocalMember(), replyAuthor.id() == room->localMember().id());
|
||||||
QCOMPARE(eventHandlerReplyAuthor.id(), replyAuthor.id());
|
QCOMPARE(eventHandlerReplyAuthor.id(), replyAuthor.id());
|
||||||
@@ -327,58 +375,121 @@ void EventHandlerTest::replyAuthor()
|
|||||||
QCOMPARE(eventHandlerReplyAuthor.avatarMediaId(), replyAuthor.avatarMediaId());
|
QCOMPARE(eventHandlerReplyAuthor.avatarMediaId(), replyAuthor.avatarMediaId());
|
||||||
QCOMPARE(eventHandlerReplyAuthor.color(), replyAuthor.color());
|
QCOMPARE(eventHandlerReplyAuthor.color(), replyAuthor.color());
|
||||||
|
|
||||||
QCOMPARE(EventHandler::replyAuthor(room, room->messageEvents().at(0).get()), RoomMember());
|
EventHandler eventHandlerNoAuthor(room, room->messageEvents().at(0).get());
|
||||||
|
QCOMPARE(eventHandlerNoAuthor.getReplyAuthor(), RoomMember());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::nullReplyAuthor()
|
void EventHandlerTest::nullReplyAuthor()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "replyAuthor called with room set to nullptr.");
|
QTest::ignoreMessage(QtWarningMsg, "getReplyAuthor called with m_room set to nullptr.");
|
||||||
QCOMPARE(EventHandler::replyAuthor(nullptr, nullptr), RoomMember());
|
QCOMPARE(emptyHandler.getReplyAuthor(), RoomMember());
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "replyAuthor called with event set to nullptr. Returning empty user.");
|
EventHandler noEventHandler(room, nullptr);
|
||||||
QCOMPARE(EventHandler::replyAuthor(room, nullptr), RoomMember());
|
QTest::ignoreMessage(QtWarningMsg, "getReplyAuthor called with m_event set to nullptr. Returning empty user.");
|
||||||
|
QCOMPARE(noEventHandler.getReplyAuthor(), RoomMember());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventHandlerTest::replyBody()
|
||||||
|
{
|
||||||
|
EventHandler eventHandler(room, room->messageEvents().at(5).get());
|
||||||
|
|
||||||
|
QCOMPARE(eventHandler.getReplyRichBody(), QStringLiteral("<b>This is an example<br>text message</b>"));
|
||||||
|
QCOMPARE(eventHandler.getReplyRichBody(true), QStringLiteral("<b>This is an example text message</b>"));
|
||||||
|
QCOMPARE(eventHandler.getReplyPlainBody(), QStringLiteral("This is an example\ntext message"));
|
||||||
|
QCOMPARE(eventHandler.getReplyPlainBody(true), QStringLiteral("This is an example text message"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventHandlerTest::nullReplyBody()
|
||||||
|
{
|
||||||
|
EventHandler noEventHandler(room, nullptr);
|
||||||
|
|
||||||
|
QTest::ignoreMessage(QtWarningMsg, "getReplyRichBody called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.getReplyRichBody(), QString());
|
||||||
|
|
||||||
|
QTest::ignoreMessage(QtWarningMsg, "getReplyPlainBody called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.getReplyPlainBody(), QString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventHandlerTest::replyMediaInfo()
|
||||||
|
{
|
||||||
|
auto event = room->messageEvents().at(6).get();
|
||||||
|
auto replyEvent = room->messageEvents().at(4).get();
|
||||||
|
EventHandler eventHandler(room, event);
|
||||||
|
|
||||||
|
auto mediaInfo = eventHandler.getReplyMediaInfo();
|
||||||
|
auto thumbnailInfo = mediaInfo["tempInfo"_ls].toMap();
|
||||||
|
|
||||||
|
QCOMPARE(mediaInfo["source"_ls], room->makeMediaUrl(replyEvent->id(), QUrl("mxc://kde.org/1234567"_ls)));
|
||||||
|
QCOMPARE(mediaInfo["mimeType"_ls], QStringLiteral("video/mp4"));
|
||||||
|
QCOMPARE(mediaInfo["mimeIcon"_ls], QStringLiteral("video-mp4"));
|
||||||
|
QCOMPARE(mediaInfo["size"_ls], 62650636);
|
||||||
|
QCOMPARE(mediaInfo["duration"_ls], 10);
|
||||||
|
QCOMPARE(mediaInfo["width"_ls], 1920);
|
||||||
|
QCOMPARE(mediaInfo["height"_ls], 1080);
|
||||||
|
QCOMPARE(thumbnailInfo["source"_ls], room->makeMediaUrl(replyEvent->id(), QUrl("mxc://kde.org/2234567"_ls)));
|
||||||
|
QCOMPARE(thumbnailInfo["mimeType"_ls], QStringLiteral("image/jpeg"));
|
||||||
|
QCOMPARE(thumbnailInfo["mimeIcon"_ls], QStringLiteral("image-jpeg"));
|
||||||
|
QCOMPARE(thumbnailInfo["size"_ls], 382249);
|
||||||
|
QCOMPARE(thumbnailInfo["width"_ls], 800);
|
||||||
|
QCOMPARE(thumbnailInfo["height"_ls], 450);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventHandlerTest::nullReplyMediaInfo()
|
||||||
|
{
|
||||||
|
QTest::ignoreMessage(QtWarningMsg, "getReplyMediaInfo called with m_room set to nullptr.");
|
||||||
|
QCOMPARE(emptyHandler.getReplyMediaInfo(), QVariantMap());
|
||||||
|
|
||||||
|
EventHandler noEventHandler(room, nullptr);
|
||||||
|
QTest::ignoreMessage(QtWarningMsg, "getReplyMediaInfo called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.getReplyMediaInfo(), QVariantMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::thread()
|
void EventHandlerTest::thread()
|
||||||
{
|
{
|
||||||
QCOMPARE(EventHandler::isThreaded(room->messageEvents().at(0).get()), false);
|
EventHandler eventHandlerNoThread(room, room->messageEvents().at(0).get());
|
||||||
QCOMPARE(EventHandler::threadRoot(room->messageEvents().at(0).get()), QString());
|
QCOMPARE(eventHandlerNoThread.isThreaded(), false);
|
||||||
|
QCOMPARE(eventHandlerNoThread.threadRoot(), QString());
|
||||||
|
|
||||||
QCOMPARE(EventHandler::isThreaded(room->messageEvents().at(9).get()), true);
|
EventHandler eventHandlerThreadRoot(room, room->messageEvents().at(9).get());
|
||||||
QCOMPARE(EventHandler::threadRoot(room->messageEvents().at(9).get()), QStringLiteral("$threadroot:example.org"));
|
QCOMPARE(eventHandlerThreadRoot.isThreaded(), true);
|
||||||
QCOMPARE(EventHandler::replyId(room->messageEvents().at(9).get()), QStringLiteral("$threadroot:example.org"));
|
QCOMPARE(eventHandlerThreadRoot.threadRoot(), QStringLiteral("$threadroot:example.org"));
|
||||||
|
QCOMPARE(eventHandlerThreadRoot.getReplyId(), QStringLiteral("$threadroot:example.org"));
|
||||||
|
|
||||||
QCOMPARE(EventHandler::isThreaded(room->messageEvents().at(10).get()), true);
|
EventHandler eventHandlerThreadReply(room, room->messageEvents().at(10).get());
|
||||||
QCOMPARE(EventHandler::threadRoot(room->messageEvents().at(10).get()), QStringLiteral("$threadroot:example.org"));
|
QCOMPARE(eventHandlerThreadReply.isThreaded(), true);
|
||||||
QCOMPARE(EventHandler::replyId(room->messageEvents().at(10).get()), QStringLiteral("$threadmessage1:example.org"));
|
QCOMPARE(eventHandlerThreadReply.threadRoot(), QStringLiteral("$threadroot:example.org"));
|
||||||
|
QCOMPARE(eventHandlerThreadReply.getReplyId(), QStringLiteral("$threadmessage1:example.org"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::nullThread()
|
void EventHandlerTest::nullThread()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "isThreaded called with event set to nullptr.");
|
QTest::ignoreMessage(QtWarningMsg, "isThreaded called with m_event set to nullptr.");
|
||||||
QCOMPARE(EventHandler::isThreaded(nullptr), false);
|
QCOMPARE(emptyHandler.isThreaded(), false);
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "threadRoot called with event set to nullptr.");
|
EventHandler noEventHandler(room, nullptr);
|
||||||
QCOMPARE(EventHandler::threadRoot(nullptr), QString());
|
QTest::ignoreMessage(QtWarningMsg, "threadRoot called with m_event set to nullptr.");
|
||||||
|
QCOMPARE(noEventHandler.threadRoot(), QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::location()
|
void EventHandlerTest::location()
|
||||||
{
|
{
|
||||||
QCOMPARE(EventHandler::latitude(room->messageEvents().at(7).get()), QStringLiteral("51.7035").toFloat());
|
EventHandler eventHandler(room, room->messageEvents().at(7).get());
|
||||||
QCOMPARE(EventHandler::longitude(room->messageEvents().at(7).get()), QStringLiteral("-1.14394").toFloat());
|
|
||||||
QCOMPARE(EventHandler::locationAssetType(room->messageEvents().at(7).get()), QStringLiteral("m.pin"));
|
QCOMPARE(eventHandler.getLatitude(), QStringLiteral("51.7035").toFloat());
|
||||||
|
QCOMPARE(eventHandler.getLongitude(), QStringLiteral("-1.14394").toFloat());
|
||||||
|
QCOMPARE(eventHandler.getLocationAssetType(), QStringLiteral("m.pin"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventHandlerTest::nullLocation()
|
void EventHandlerTest::nullLocation()
|
||||||
{
|
{
|
||||||
QTest::ignoreMessage(QtWarningMsg, "latitude called with event set to nullptr.");
|
QTest::ignoreMessage(QtWarningMsg, "getLatitude called with m_event set to nullptr.");
|
||||||
QCOMPARE(EventHandler::latitude(nullptr), -100.0);
|
QCOMPARE(emptyHandler.getLatitude(), -100.0);
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "longitude called with event set to nullptr.");
|
QTest::ignoreMessage(QtWarningMsg, "getLongitude called with m_event set to nullptr.");
|
||||||
QCOMPARE(EventHandler::longitude(nullptr), -200.0);
|
QCOMPARE(emptyHandler.getLongitude(), -200.0);
|
||||||
|
|
||||||
QTest::ignoreMessage(QtWarningMsg, "locationAssetType called with event set to nullptr.");
|
QTest::ignoreMessage(QtWarningMsg, "getLocationAssetType called with m_event set to nullptr.");
|
||||||
QCOMPARE(EventHandler::locationAssetType(nullptr), QString());
|
QCOMPARE(emptyHandler.getLocationAssetType(), QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
QTEST_MAIN(EventHandlerTest)
|
QTEST_MAIN(EventHandlerTest)
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ private Q_SLOTS:
|
|||||||
void sendCustomEmojiCode_data();
|
void sendCustomEmojiCode_data();
|
||||||
void sendCustomEmojiCode();
|
void sendCustomEmojiCode();
|
||||||
|
|
||||||
void receiveSpacelessSelfClosingTag();
|
|
||||||
void receiveStripReply();
|
void receiveStripReply();
|
||||||
void receivePlainTextIn();
|
void receivePlainTextIn();
|
||||||
|
|
||||||
@@ -253,19 +252,6 @@ void TextHandlerTest::sendCustomEmojiCode()
|
|||||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextHandlerTest::receiveSpacelessSelfClosingTag()
|
|
||||||
{
|
|
||||||
const QString testInputString = QStringLiteral("Test...<br/>...ing");
|
|
||||||
const QString testRichOutputString = QStringLiteral("Test...<br/>...ing");
|
|
||||||
const QString testPlainOutputString = QStringLiteral("Test...\n...ing");
|
|
||||||
|
|
||||||
TextHandler testTextHandler;
|
|
||||||
testTextHandler.setData(testInputString);
|
|
||||||
|
|
||||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testRichOutputString);
|
|
||||||
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText), testPlainOutputString);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextHandlerTest::receiveStripReply()
|
void TextHandlerTest::receiveStripReply()
|
||||||
{
|
{
|
||||||
const QString testInputString = QStringLiteral(
|
const QString testInputString = QStringLiteral(
|
||||||
@@ -288,7 +274,6 @@ void TextHandlerTest::receiveRichInPlainOut_data()
|
|||||||
QTest::newRow("ampersand") << QStringLiteral("a & b") << QStringLiteral("a & b");
|
QTest::newRow("ampersand") << QStringLiteral("a & b") << QStringLiteral("a & b");
|
||||||
QTest::newRow("quote") << QStringLiteral(""a and b"") << QStringLiteral("\"a and b\"");
|
QTest::newRow("quote") << QStringLiteral(""a and b"") << QStringLiteral("\"a and b\"");
|
||||||
QTest::newRow("new line") << QStringLiteral("new<br>line") << QStringLiteral("new\nline");
|
QTest::newRow("new line") << QStringLiteral("new<br>line") << QStringLiteral("new\nline");
|
||||||
QTest::newRow("unescape") << QStringLiteral("can't") << QStringLiteral("can't");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextHandlerTest::receiveRichInPlainOut()
|
void TextHandlerTest::receiveRichInPlainOut()
|
||||||
@@ -463,9 +448,6 @@ void TextHandlerTest::receiveRichPlainUrl()
|
|||||||
QString testOutputStringMxId = QStringLiteral(
|
QString testOutputStringMxId = QStringLiteral(
|
||||||
"<b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a></b>");
|
"<b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a></b>");
|
||||||
|
|
||||||
QString testInputStringMxIdWithPrefix = QStringLiteral("a @user:kde.org b");
|
|
||||||
QString testOutputStringMxIdWithPrefix = QStringLiteral("a <b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> b");
|
|
||||||
|
|
||||||
TextHandler testTextHandler;
|
TextHandler testTextHandler;
|
||||||
testTextHandler.setData(testInputStringLink1);
|
testTextHandler.setData(testInputStringLink1);
|
||||||
|
|
||||||
@@ -479,9 +461,6 @@ void TextHandlerTest::receiveRichPlainUrl()
|
|||||||
|
|
||||||
testTextHandler.setData(testInputStringMxId);
|
testTextHandler.setData(testInputStringMxId);
|
||||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringMxId);
|
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringMxId);
|
||||||
|
|
||||||
testTextHandler.setData(testInputStringMxIdWithPrefix);
|
|
||||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringMxIdWithPrefix);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextHandlerTest::receiveRichEdited_data()
|
void TextHandlerTest::receiveRichEdited_data()
|
||||||
@@ -556,7 +535,7 @@ void TextHandlerTest::componentOutput_data()
|
|||||||
"someField }\nCustomQml {\n someTextProperty: someField.text\n}\n</code></pre>Sure you can, it's still local to the same file where you "
|
"someField }\nCustomQml {\n someTextProperty: someField.text\n}\n</code></pre>Sure you can, it's still local to the same file where you "
|
||||||
"defined the id")
|
"defined the id")
|
||||||
<< QList<MessageComponent>{
|
<< QList<MessageComponent>{
|
||||||
MessageComponent{MessageComponentType::Text, QStringLiteral("Ah, you mean something like<br/>"), {}},
|
MessageComponent{MessageComponentType::Text, QStringLiteral("Ah, you mean something like"), {}},
|
||||||
MessageComponent{
|
MessageComponent{
|
||||||
MessageComponentType::Code,
|
MessageComponentType::Code,
|
||||||
QStringLiteral(
|
QStringLiteral(
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class WindowControllerTest : public QObject
|
|||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void nullWindow();
|
void nullWindow();
|
||||||
|
void geometry();
|
||||||
void showAndRaise();
|
void showAndRaise();
|
||||||
void toggle();
|
void toggle();
|
||||||
|
|
||||||
@@ -29,10 +30,32 @@ void WindowControllerTest::nullWindow()
|
|||||||
auto &instance = WindowController::instance();
|
auto &instance = WindowController::instance();
|
||||||
QCOMPARE(instance.window(), nullptr);
|
QCOMPARE(instance.window(), nullptr);
|
||||||
|
|
||||||
|
instance.restoreGeometry();
|
||||||
|
instance.saveGeometry();
|
||||||
instance.showAndRaiseWindow({});
|
instance.showAndRaiseWindow({});
|
||||||
instance.toggleWindow();
|
instance.toggleWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WindowControllerTest::geometry()
|
||||||
|
{
|
||||||
|
auto &instance = WindowController::instance();
|
||||||
|
|
||||||
|
QWindow window;
|
||||||
|
window.setGeometry(0, 0, 200, 200);
|
||||||
|
instance.setWindow(&window);
|
||||||
|
QCOMPARE(instance.window(), &window);
|
||||||
|
|
||||||
|
instance.saveGeometry();
|
||||||
|
const auto stateConfig = KSharedConfig::openStateConfig();
|
||||||
|
KConfigGroup windowGroup = stateConfig->group(QStringLiteral("Window"));
|
||||||
|
QCOMPARE(KWindowConfig::hasSavedWindowSize(windowGroup), true);
|
||||||
|
|
||||||
|
window.setGeometry(0, 0, 400, 400);
|
||||||
|
QCOMPARE(window.geometry(), QRect(0, 0, 400, 400));
|
||||||
|
instance.restoreGeometry();
|
||||||
|
QCOMPARE(window.geometry(), QRect(0, 0, 200, 200));
|
||||||
|
}
|
||||||
|
|
||||||
void WindowControllerTest::showAndRaise()
|
void WindowControllerTest::showAndRaise()
|
||||||
{
|
{
|
||||||
auto &instance = WindowController::instance();
|
auto &instance = WindowController::instance();
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
<name xml:lang="eo">NeoChat</name>
|
<name xml:lang="eo">NeoChat</name>
|
||||||
<name xml:lang="es">NeoChat</name>
|
<name xml:lang="es">NeoChat</name>
|
||||||
<name xml:lang="eu">NeoChat</name>
|
<name xml:lang="eu">NeoChat</name>
|
||||||
|
<name xml:lang="fa">نئوچت</name>
|
||||||
<name xml:lang="fi">NeoChat</name>
|
<name xml:lang="fi">NeoChat</name>
|
||||||
<name xml:lang="fr">NeoChat</name>
|
<name xml:lang="fr">NeoChat</name>
|
||||||
<name xml:lang="gl">NeoChat</name>
|
<name xml:lang="gl">NeoChat</name>
|
||||||
@@ -55,6 +56,7 @@
|
|||||||
<summary xml:lang="ca">Xategeu amb els vostres amics a Matrix</summary>
|
<summary xml:lang="ca">Xategeu amb els vostres amics a Matrix</summary>
|
||||||
<summary xml:lang="ca-valencia">Xategeu amb els vostres amics a Matrix</summary>
|
<summary xml:lang="ca-valencia">Xategeu amb els vostres amics a Matrix</summary>
|
||||||
<summary xml:lang="cs">Mluvte se svými přáteli na Matrixu</summary>
|
<summary xml:lang="cs">Mluvte se svými přáteli na Matrixu</summary>
|
||||||
|
<summary xml:lang="de">Mit den Freunden auf Matrix unterhalten</summary>
|
||||||
<summary xml:lang="en-GB">Chat with your friends on matrix</summary>
|
<summary xml:lang="en-GB">Chat with your friends on matrix</summary>
|
||||||
<summary xml:lang="eo">Babilu kun viaj amikoj sur matrix</summary>
|
<summary xml:lang="eo">Babilu kun viaj amikoj sur matrix</summary>
|
||||||
<summary xml:lang="es">Charle con sus amigos en matrix</summary>
|
<summary xml:lang="es">Charle con sus amigos en matrix</summary>
|
||||||
@@ -72,6 +74,7 @@
|
|||||||
<summary xml:lang="nl">Met uw vrienden chatten op matrix</summary>
|
<summary xml:lang="nl">Met uw vrienden chatten op matrix</summary>
|
||||||
<summary xml:lang="nn">Prat med vennar på Matrix</summary>
|
<summary xml:lang="nn">Prat med vennar på Matrix</summary>
|
||||||
<summary xml:lang="pl">Rozmawiaj ze swoimi znajomymi w Matriksie</summary>
|
<summary xml:lang="pl">Rozmawiaj ze swoimi znajomymi w Matriksie</summary>
|
||||||
|
<summary xml:lang="ru">Общение с друзьями в Matrix</summary>
|
||||||
<summary xml:lang="sl">Klepet z vašimi prijatelji na matrixu</summary>
|
<summary xml:lang="sl">Klepet z vašimi prijatelji na matrixu</summary>
|
||||||
<summary xml:lang="sv">Chatta med dina vänner på Matrix</summary>
|
<summary xml:lang="sv">Chatta med dina vänner på Matrix</summary>
|
||||||
<summary xml:lang="ta">மேட்ரிக்ஸு மூலம் உங்கள் நண்பர்களிடம் பேசலாம்</summary>
|
<summary xml:lang="ta">மேட்ரிக்ஸு மூலம் உங்கள் நண்பர்களிடம் பேசலாம்</summary>
|
||||||
@@ -85,6 +88,7 @@
|
|||||||
<p xml:lang="ar">نيوتشات هو تطبيق دردشة يتيح لك الاستفادة الكاملة من شبكة Matrix. فهو يوفر لك طريقة آمنة لإرسال الرسائل النصية ومقاطع الفيديو والملفات الصوتية إلى عائلتك وزملائك وأصدقائك.</p>
|
<p xml:lang="ar">نيوتشات هو تطبيق دردشة يتيح لك الاستفادة الكاملة من شبكة Matrix. فهو يوفر لك طريقة آمنة لإرسال الرسائل النصية ومقاطع الفيديو والملفات الصوتية إلى عائلتك وزملائك وأصدقائك.</p>
|
||||||
<p xml:lang="ca">El NeoChat és una aplicació de xat que us permet aprofitar plenament la xarxa Matrix. Proporciona una manera segura d'enviar missatges de text, vídeos i arxius d'àudio a la vostra família, companys i amics.</p>
|
<p xml:lang="ca">El NeoChat és una aplicació de xat que us permet aprofitar plenament la xarxa Matrix. Proporciona una manera segura d'enviar missatges de text, vídeos i arxius d'àudio a la vostra família, companys i amics.</p>
|
||||||
<p xml:lang="ca-valencia">NeoChat és una aplicació de xat que us permet aprofitar plenament la xarxa Matrix. Proporciona una manera segura d'enviar missatges de text, vídeos i arxius d'àudio a la vostra família, companys i amics.</p>
|
<p xml:lang="ca-valencia">NeoChat és una aplicació de xat que us permet aprofitar plenament la xarxa Matrix. Proporciona una manera segura d'enviar missatges de text, vídeos i arxius d'àudio a la vostra família, companys i amics.</p>
|
||||||
|
<p xml:lang="de">NeoChat ist eine Anwendung für Unterhaltungen mit allen Vorteilen des Matrix-Netzwerkes. Sie bietet eine sichere Möglichkeit zum Versenden von Nachrichten, Videos und Audiodateien and die Familienmitglieder.</p>
|
||||||
<p xml:lang="en-GB">NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
|
<p xml:lang="en-GB">NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
|
||||||
<p xml:lang="eo">NeoChat estas babilej-apo, kiu ebligas al vi plene profiti de la Matrix-reto. Ĝi provizas al vi sekuran manieron sendi tekstmesaĝojn, filmetojn kaj sondosierojn al via familio, kolegoj kaj amikoj.</p>
|
<p xml:lang="eo">NeoChat estas babilej-apo, kiu ebligas al vi plene profiti de la Matrix-reto. Ĝi provizas al vi sekuran manieron sendi tekstmesaĝojn, filmetojn kaj sondosierojn al via familio, kolegoj kaj amikoj.</p>
|
||||||
<p xml:lang="es">NeoChat es una aplicación de chat que le permite aprovechar al máximo la red Matrix. Le proporciona un modo seguro de enviar mensajes de texto, vídeos y archivos de sonido a su familia, colegas y amigos.</p>
|
<p xml:lang="es">NeoChat es una aplicación de chat que le permite aprovechar al máximo la red Matrix. Le proporciona un modo seguro de enviar mensajes de texto, vídeos y archivos de sonido a su familia, colegas y amigos.</p>
|
||||||
@@ -101,6 +105,7 @@
|
|||||||
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
|
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
|
||||||
<p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p>
|
<p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p>
|
||||||
<p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p>
|
<p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p>
|
||||||
|
<p xml:lang="ru">NeoChat — приложение для общения, предоставляющее все преимущества сети Matrix. С его помощью можно безопасно отправлять текстовые сообщения, видеозаписи и звуковые файлы родственникам, коллегам и друзьям.</p>
|
||||||
<p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p>
|
<p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p>
|
||||||
<p xml:lang="sv">NeoChat är ett chattprogram som låter dig dra full nytta av Matrix-nätverket. Det ger dig ett säkert sätt att skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner.</p>
|
<p xml:lang="sv">NeoChat är ett chattprogram som låter dig dra full nytta av Matrix-nätverket. Det ger dig ett säkert sätt att skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner.</p>
|
||||||
<p xml:lang="tr">NeoChat, Matrix ağının tüm özelliklerini kullanan bir sohbet uygulamasıdır. Ailenize, arkadaşlarınıza ve iş arkadaşlarınıza metin iletileri, ses ve video dosyaları göndermenin kolay bir yolunu sunar.</p>
|
<p xml:lang="tr">NeoChat, Matrix ağının tüm özelliklerini kullanan bir sohbet uygulamasıdır. Ailenize, arkadaşlarınıza ve iş arkadaşlarınıza metin iletileri, ses ve video dosyaları göndermenin kolay bir yolunu sunar.</p>
|
||||||
@@ -111,6 +116,7 @@
|
|||||||
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. على هذا النحو يتم دعم كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP والخيوط وبعض جوانب التشفير من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار ، ولكن يبقى الهدف توفير الدعم النهائي للمواصفات بأكملها.</p>
|
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. على هذا النحو يتم دعم كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP والخيوط وبعض جوانب التشفير من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار ، ولكن يبقى الهدف توفير الدعم النهائي للمواصفات بأكملها.</p>
|
||||||
<p xml:lang="ca">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptatge d'extrem a extrem. Hi ha algunes altres omissions més petites a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu segueix sent proporcionar suport eventual per a tota l'especificació.</p>
|
<p xml:lang="ca">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptatge d'extrem a extrem. Hi ha algunes altres omissions més petites a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu segueix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||||
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
|
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||||
|
<p xml:lang="de">NeoChat versucht eine vollumfängliche Anwendung für die Spezifikation von Matrix zu sein. Damit wird alles der aktuellen stabilen Spezifikation mit den erwähnenswerten Ausnahmen von VoIP, Diskussionsfäden und ein paar Teilen der Ende-zu-Ende-Verschlüsselung unterstützt. Zudem sind andere kleinere Auslassungen vorhanden, da sich die Matrixspezifikation ständig weiterentwickelt. Nichtsdestotrotz soll letztendlich die gesamte Spezifikation unterstützt werden.</p>
|
||||||
<p xml:lang="en-GB">NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
|
<p xml:lang="en-GB">NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
|
||||||
<p xml:lang="eo">NeoChat celas esti plene kapabla aplikaĵo por la Matrix-specifo. Kiel tia, ĉio en la nuna stabila specifo kun la rimarkindaj esceptoj de VoIP, fadenoj kaj kelkaj aspektoj de Fin-al-Fina Ĉifrado estas subtenataj. Estas kelkaj aliaj pli malgrandaj preterlasoj pro la fakto, ke la Matrix-speco konstante evoluas, sed la celo restas provizi finfine subtenon por la tuta specifaĵo.</p>
|
<p xml:lang="eo">NeoChat celas esti plene kapabla aplikaĵo por la Matrix-specifo. Kiel tia, ĉio en la nuna stabila specifo kun la rimarkindaj esceptoj de VoIP, fadenoj kaj kelkaj aspektoj de Fin-al-Fina Ĉifrado estas subtenataj. Estas kelkaj aliaj pli malgrandaj preterlasoj pro la fakto, ke la Matrix-speco konstante evoluas, sed la celo restas provizi finfine subtenon por la tuta specifaĵo.</p>
|
||||||
<p xml:lang="es">NeoChat pretende ser una aplicación con todas las funciones para la especificación de Matrix. Como tal, admite todo en la especificación estable actual, con las notables excepciones de VoIP, subprocesos y algunas funciones de cifrado de extremo a extremo. Existen algunas omisiones menos importantes debido al hecho de que la especificación de Matrix está en constante evolución, pero el objetivo sigue siendo brindar compatibilidad final con toda la especificación.</p>
|
<p xml:lang="es">NeoChat pretende ser una aplicación con todas las funciones para la especificación de Matrix. Como tal, admite todo en la especificación estable actual, con las notables excepciones de VoIP, subprocesos y algunas funciones de cifrado de extremo a extremo. Existen algunas omisiones menos importantes debido al hecho de que la especificación de Matrix está en constante evolución, pero el objetivo sigue siendo brindar compatibilidad final con toda la especificación.</p>
|
||||||
@@ -140,6 +146,7 @@
|
|||||||
<p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يدعم نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p>
|
<p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يدعم نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p>
|
||||||
<p xml:lang="ca">A causa de la naturalesa del desenvolupament de l'especificació de Matrix, el NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
|
<p xml:lang="ca">A causa de la naturalesa del desenvolupament de l'especificació de Matrix, el NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
|
||||||
<p xml:lang="ca-valencia">A causa de la naturalea del desenvolupament de l'especificació de Matrix, NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
|
<p xml:lang="ca-valencia">A causa de la naturalea del desenvolupament de l'especificació de Matrix, NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
|
||||||
|
<p xml:lang="de">Durch die Weiterentwicklung der Matrix-Spezifikation unterstützt auch NeoChat einige als noch instabil gekennzeichnete Funktionen. Derzeit sind das:</p>
|
||||||
<p xml:lang="en-GB">Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:</p>
|
<p xml:lang="en-GB">Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:</p>
|
||||||
<p xml:lang="eo">Pro la naturo de la Matrix-specifevoluo NeoChat ankaŭ subtenas multajn malstabilajn funkciojn. Nuntempe ĉi tiuj estas:</p>
|
<p xml:lang="eo">Pro la naturo de la Matrix-specifevoluo NeoChat ankaŭ subtenas multajn malstabilajn funkciojn. Nuntempe ĉi tiuj estas:</p>
|
||||||
<p xml:lang="es">Debido a la naturaleza del desarrollo de la especificación de Matrix, NeoChat también permite numerosas funciones no estables, como:</p>
|
<p xml:lang="es">Debido a la naturaleza del desarrollo de la especificación de Matrix, NeoChat también permite numerosas funciones no estables, como:</p>
|
||||||
@@ -189,6 +196,7 @@
|
|||||||
<li xml:lang="nn">Avstemmingar – MSC3381</li>
|
<li xml:lang="nn">Avstemmingar – MSC3381</li>
|
||||||
<li xml:lang="pl">Ankiety - MSC3381</li>
|
<li xml:lang="pl">Ankiety - MSC3381</li>
|
||||||
<li xml:lang="pt">Inquéritos - MSC3381</li>
|
<li xml:lang="pt">Inquéritos - MSC3381</li>
|
||||||
|
<li xml:lang="ru">Голосования — MSC3381</li>
|
||||||
<li xml:lang="sl">Polls - MSC3381</li>
|
<li xml:lang="sl">Polls - MSC3381</li>
|
||||||
<li xml:lang="sv">Polls - MSC3381</li>
|
<li xml:lang="sv">Polls - MSC3381</li>
|
||||||
<li xml:lang="ta">வாக்கெடுப்புகள் - MSC3381</li>
|
<li xml:lang="ta">வாக்கெடுப்புகள் - MSC3381</li>
|
||||||
@@ -294,6 +302,7 @@
|
|||||||
<caption xml:lang="ar">العرض الرئيسة مع قائمة الغرف والدردشات و معلومات الغرفة</caption>
|
<caption xml:lang="ar">العرض الرئيسة مع قائمة الغرف والدردشات و معلومات الغرفة</caption>
|
||||||
<caption xml:lang="ca">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
<caption xml:lang="ca">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||||
<caption xml:lang="ca-valencia">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
<caption xml:lang="ca-valencia">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||||
|
<caption xml:lang="de">Hauptansicht mit Raumliste, Unterhaltung und Raum-Informationen</caption>
|
||||||
<caption xml:lang="en-GB">Main view with room list, chat, and room information</caption>
|
<caption xml:lang="en-GB">Main view with room list, chat, and room information</caption>
|
||||||
<caption xml:lang="eo">Ĉefa vido kun ĉambra listo, babilejo kaj ĉambra informo</caption>
|
<caption xml:lang="eo">Ĉefa vido kun ĉambra listo, babilejo kaj ĉambra informo</caption>
|
||||||
<caption xml:lang="es">Vista principal con la lista de salas, chat e información de la sala</caption>
|
<caption xml:lang="es">Vista principal con la lista de salas, chat e información de la sala</caption>
|
||||||
@@ -327,6 +336,7 @@
|
|||||||
<caption xml:lang="ar">اكتشف مجتمعات جديدة مع فضاءات ماتركس</caption>
|
<caption xml:lang="ar">اكتشف مجتمعات جديدة مع فضاءات ماتركس</caption>
|
||||||
<caption xml:lang="ca">Descobriu comunitats noves amb els espais de Matrix</caption>
|
<caption xml:lang="ca">Descobriu comunitats noves amb els espais de Matrix</caption>
|
||||||
<caption xml:lang="ca-valencia">Descobriu comunitats noves amb els espais de Matrix</caption>
|
<caption xml:lang="ca-valencia">Descobriu comunitats noves amb els espais de Matrix</caption>
|
||||||
|
<caption xml:lang="de">Neue Gemeinschaften mit den Umgebungen von Matrix erkunden</caption>
|
||||||
<caption xml:lang="en-GB">Discover new communities with Matrix Spaces</caption>
|
<caption xml:lang="en-GB">Discover new communities with Matrix Spaces</caption>
|
||||||
<caption xml:lang="eo">Malkovru novajn komunumojn per Matrix Spaces</caption>
|
<caption xml:lang="eo">Malkovru novajn komunumojn per Matrix Spaces</caption>
|
||||||
<caption xml:lang="es">Descubra nuevas comunidades con los espacios de Matrix</caption>
|
<caption xml:lang="es">Descubra nuevas comunidades con los espacios de Matrix</caption>
|
||||||
@@ -343,8 +353,10 @@
|
|||||||
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
|
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
|
||||||
<caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption>
|
<caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption>
|
||||||
<caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption>
|
<caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption>
|
||||||
|
<caption xml:lang="ru">Поиск новых сообществ с помощью Matrix Spaces</caption>
|
||||||
<caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption>
|
<caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption>
|
||||||
<caption xml:lang="sv">Upptäck nya gemenskaper med Matrix Spaces</caption>
|
<caption xml:lang="sv">Upptäck nya gemenskaper med Matrix Spaces</caption>
|
||||||
|
<caption xml:lang="ta">மேட்ரிக்ஸு இடங்களின் மூலம் புதிய சமூகங்களைக் கண்டுபிடிக்கலாம்</caption>
|
||||||
<caption xml:lang="tr">Matrix Alanlar ile yeni topluluklar keşfedin</caption>
|
<caption xml:lang="tr">Matrix Alanlar ile yeni topluluklar keşfedin</caption>
|
||||||
<caption xml:lang="uk">Пошук нових спільнот за допомогою Matrix Spaces</caption>
|
<caption xml:lang="uk">Пошук нових спільнот за допомогою Matrix Spaces</caption>
|
||||||
<caption xml:lang="x-test">xxDiscover new communities with Matrix Spacesxx</caption>
|
<caption xml:lang="x-test">xxDiscover new communities with Matrix Spacesxx</caption>
|
||||||
@@ -363,6 +375,7 @@
|
|||||||
<caption xml:lang="ar">العرض الرئيسة مع قائمة الغرف والدردشات و معلومات الغرفة</caption>
|
<caption xml:lang="ar">العرض الرئيسة مع قائمة الغرف والدردشات و معلومات الغرفة</caption>
|
||||||
<caption xml:lang="ca">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
<caption xml:lang="ca">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||||
<caption xml:lang="ca-valencia">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
<caption xml:lang="ca-valencia">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||||
|
<caption xml:lang="de">Hauptansicht mit Raumliste, Unterhaltung und Raum-Informationen</caption>
|
||||||
<caption xml:lang="en-GB">Main view with room list, chat, and room information</caption>
|
<caption xml:lang="en-GB">Main view with room list, chat, and room information</caption>
|
||||||
<caption xml:lang="eo">Ĉefa vido kun ĉambra listo, babilejo kaj ĉambra informo</caption>
|
<caption xml:lang="eo">Ĉefa vido kun ĉambra listo, babilejo kaj ĉambra informo</caption>
|
||||||
<caption xml:lang="es">Vista principal con la lista de salas, chat e información de la sala</caption>
|
<caption xml:lang="es">Vista principal con la lista de salas, chat e información de la sala</caption>
|
||||||
@@ -397,6 +410,7 @@
|
|||||||
<caption xml:lang="ca">Pantalla d'inici de sessió</caption>
|
<caption xml:lang="ca">Pantalla d'inici de sessió</caption>
|
||||||
<caption xml:lang="ca-valencia">Pantalla d'inici de sessió</caption>
|
<caption xml:lang="ca-valencia">Pantalla d'inici de sessió</caption>
|
||||||
<caption xml:lang="cs">Přihlašovací obrazovka</caption>
|
<caption xml:lang="cs">Přihlašovací obrazovka</caption>
|
||||||
|
<caption xml:lang="de">Anmeldebildschirm</caption>
|
||||||
<caption xml:lang="en-GB">Login screen</caption>
|
<caption xml:lang="en-GB">Login screen</caption>
|
||||||
<caption xml:lang="eo">Ensaluta ekrano</caption>
|
<caption xml:lang="eo">Ensaluta ekrano</caption>
|
||||||
<caption xml:lang="es">Pantalla de inicio de sesión</caption>
|
<caption xml:lang="es">Pantalla de inicio de sesión</caption>
|
||||||
@@ -429,6 +443,8 @@
|
|||||||
<content_attribute id="social-chat">intense</content_attribute>
|
<content_attribute id="social-chat">intense</content_attribute>
|
||||||
</content_rating>
|
</content_rating>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="24.08.3" date="2024-11-07"/>
|
||||||
|
<release version="24.08.2" date="2024-10-10"/>
|
||||||
<release version="24.08.1" date="2024-09-12"/>
|
<release version="24.08.1" date="2024-09-12"/>
|
||||||
<release version="24.08.0" date="2024-08-22"/>
|
<release version="24.08.0" date="2024-08-22"/>
|
||||||
<release version="24.05.2" date="2024-07-04"/>
|
<release version="24.05.2" date="2024-07-04"/>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ Name[en_GB]=NeoChat
|
|||||||
Name[eo]=NeoChat
|
Name[eo]=NeoChat
|
||||||
Name[es]=NeoChat
|
Name[es]=NeoChat
|
||||||
Name[eu]=NeoChat
|
Name[eu]=NeoChat
|
||||||
|
Name[fa]=نئوچت
|
||||||
Name[fi]=NeoChat
|
Name[fi]=NeoChat
|
||||||
Name[fr]=NeoChat
|
Name[fr]=NeoChat
|
||||||
Name[gl]=NeoChat
|
Name[gl]=NeoChat
|
||||||
|
|||||||
1243
po/ar/neochat.po
1243
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
1126
po/ast/neochat.po
1126
po/ast/neochat.po
File diff suppressed because it is too large
Load Diff
1344
po/az/neochat.po
1344
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
1409
po/ca/neochat.po
1409
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1244
po/cs/neochat.po
1244
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
1271
po/da/neochat.po
1271
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
2543
po/de/neochat.po
2543
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
1356
po/el/neochat.po
1356
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
1261
po/en_GB/neochat.po
1261
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
1314
po/eo/neochat.po
1314
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
1244
po/es/neochat.po
1244
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
1258
po/eu/neochat.po
1258
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
5383
po/fa/neochat.po
Normal file
5383
po/fa/neochat.po
Normal file
File diff suppressed because it is too large
Load Diff
1332
po/fi/neochat.po
1332
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
1253
po/fr/neochat.po
1253
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
1230
po/gl/neochat.po
1230
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
1334
po/hu/neochat.po
1334
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
1245
po/ia/neochat.po
1245
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
1354
po/id/neochat.po
1354
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
1311
po/ie/neochat.po
1311
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
1322
po/it/neochat.po
1322
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
1126
po/ja/neochat.po
1126
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
1243
po/ka/neochat.po
1243
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
1341
po/ko/neochat.po
1341
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
1126
po/lt/neochat.po
1126
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
1327
po/lv/neochat.po
1327
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
1267
po/nl/neochat.po
1267
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
1260
po/nn/neochat.po
1260
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
1343
po/pa/neochat.po
1343
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
1240
po/pl/neochat.po
1240
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
1352
po/pt/neochat.po
1352
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
1335
po/pt_BR/neochat.po
1335
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
2325
po/ru/neochat.po
2325
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
1349
po/sk/neochat.po
1349
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
1240
po/sl/neochat.po
1240
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
1235
po/sv/neochat.po
1235
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
1242
po/ta/neochat.po
1242
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
1286
po/tok/neochat.po
1286
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
1268
po/tr/neochat.po
1268
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
1251
po/uk/neochat.po
1251
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
1164
po/zh_CN/neochat.po
1164
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
1237
po/zh_TW/neochat.po
1237
po/zh_TW/neochat.po
File diff suppressed because it is too large
Load Diff
158
snapcraft.yaml
Normal file
158
snapcraft.yaml
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2024 Scarlett Moore <sgmoore@kde.org>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
---
|
||||||
|
name: neochat
|
||||||
|
base: core22
|
||||||
|
adopt-info: neochat
|
||||||
|
grade: stable
|
||||||
|
confinement: strict
|
||||||
|
apps:
|
||||||
|
neochat:
|
||||||
|
extensions:
|
||||||
|
- kde-neon-6
|
||||||
|
command: usr/bin/neochat
|
||||||
|
common-id: org.kde.neochat
|
||||||
|
desktop: usr/share/applications/org.kde.neochat.desktop
|
||||||
|
plugs:
|
||||||
|
- home
|
||||||
|
- removable-media
|
||||||
|
- audio-playback
|
||||||
|
- unity7
|
||||||
|
- network
|
||||||
|
- network-bind
|
||||||
|
- network-manager-observe
|
||||||
|
- password-manager-service
|
||||||
|
- accounts-service
|
||||||
|
|
||||||
|
compression: lzo
|
||||||
|
|
||||||
|
slots:
|
||||||
|
session-dbus-interface:
|
||||||
|
interface: dbus
|
||||||
|
name: org.kde.neochat
|
||||||
|
bus: session
|
||||||
|
|
||||||
|
parts:
|
||||||
|
olm:
|
||||||
|
source: https://gitlab.matrix.org/matrix-org/olm.git
|
||||||
|
source-depth: 1
|
||||||
|
source-tag: '3.2.12'
|
||||||
|
plugin: cmake
|
||||||
|
cmake-parameters:
|
||||||
|
- -DCMAKE_BUILD_TYPE=Release
|
||||||
|
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
prime:
|
||||||
|
- -usr/include
|
||||||
|
- -usr/lib/*/pkgconfig
|
||||||
|
- -usr/lib/*/cmake
|
||||||
|
|
||||||
|
libsecret:
|
||||||
|
source: https://gitlab.gnome.org/GNOME/libsecret.git
|
||||||
|
source-tag: '0.21.4'
|
||||||
|
source-depth: 1
|
||||||
|
plugin: meson
|
||||||
|
meson-parameters:
|
||||||
|
- --prefix=/usr
|
||||||
|
- -Doptimization=3
|
||||||
|
- -Ddebug=true
|
||||||
|
- -Dmanpage=false
|
||||||
|
- -Dvapi=false
|
||||||
|
- -Dintrospection=false
|
||||||
|
- -Dcrypto=disabled
|
||||||
|
- -Dgtk_doc=false
|
||||||
|
build-packages:
|
||||||
|
- meson
|
||||||
|
- libglib2.0-dev
|
||||||
|
- libgcrypt20-dev
|
||||||
|
prime:
|
||||||
|
- -usr/include
|
||||||
|
- -usr/lib/*/pkgconfig
|
||||||
|
|
||||||
|
qtkeychain:
|
||||||
|
after: [libsecret]
|
||||||
|
source: https://github.com/frankosterfeld/qtkeychain.git
|
||||||
|
source-tag: 0.14.3
|
||||||
|
source-depth: 1
|
||||||
|
plugin: cmake
|
||||||
|
build-environment:
|
||||||
|
- PKG_CONFIG_PATH: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET/pkgconfig:$PKG_CONFIG_PATH
|
||||||
|
cmake-parameters:
|
||||||
|
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
- -DCMAKE_BUILD_TYPE=Release
|
||||||
|
- -DBUILD_TRANSLATIONS=NO
|
||||||
|
- -DBUILD_WITH_QT6=ON
|
||||||
|
prime:
|
||||||
|
- -usr/include
|
||||||
|
- -usr/lib/*/pkgconfig
|
||||||
|
- -usr/lib/*/cmake
|
||||||
|
|
||||||
|
libquotient:
|
||||||
|
after:
|
||||||
|
- olm
|
||||||
|
- qtkeychain
|
||||||
|
source: https://github.com/quotient-im/libQuotient.git
|
||||||
|
source-tag: 0.9.1
|
||||||
|
source-depth: 1
|
||||||
|
plugin: cmake
|
||||||
|
build-packages:
|
||||||
|
- libssl-dev
|
||||||
|
cmake-parameters:
|
||||||
|
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
- -DCMAKE_BUILD_TYPE=Release
|
||||||
|
- -DBUILD_TESTING=OFF
|
||||||
|
- -DQuotient_ENABLE_E2EE=ON
|
||||||
|
- -DBUILD_WITH_QT6=ON
|
||||||
|
prime:
|
||||||
|
- -usr/include
|
||||||
|
- -usr/lib/*/pkgconfig
|
||||||
|
- -usr/lib/*/cmake
|
||||||
|
|
||||||
|
kquickimageeditor:
|
||||||
|
source: https://invent.kde.org/libraries/kquickimageeditor.git
|
||||||
|
source-tag: 'v0.3.0'
|
||||||
|
source-depth: 1
|
||||||
|
plugin: cmake
|
||||||
|
cmake-parameters:
|
||||||
|
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
- -DCMAKE_BUILD_TYPE=Release
|
||||||
|
- -DBUILD_WITH_QT6=ON
|
||||||
|
- -DBUILD_TESTING=OFF
|
||||||
|
prime:
|
||||||
|
- -usr/include
|
||||||
|
- -usr/lib/*/pkgconfig
|
||||||
|
- -usr/lib/*/cmake
|
||||||
|
|
||||||
|
neochat:
|
||||||
|
after:
|
||||||
|
- qtkeychain
|
||||||
|
- libquotient
|
||||||
|
- kquickimageeditor
|
||||||
|
parse-info:
|
||||||
|
- usr/share/metainfo/org.kde.neochat.appdata.xml
|
||||||
|
source: .
|
||||||
|
source-type: local
|
||||||
|
plugin: cmake
|
||||||
|
build-packages:
|
||||||
|
- cmark
|
||||||
|
- libcmark-dev
|
||||||
|
- libsqlite3-dev
|
||||||
|
- libvulkan-dev
|
||||||
|
- libxkbcommon-dev
|
||||||
|
- libicu-dev
|
||||||
|
- libpulse0
|
||||||
|
cmake-parameters:
|
||||||
|
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
- -DCMAKE_BUILD_TYPE=Release
|
||||||
|
- -DBUILD_TESTING=OFF
|
||||||
|
prime:
|
||||||
|
- -usr/share/man
|
||||||
|
|
||||||
|
deps:
|
||||||
|
after: [neochat]
|
||||||
|
plugin: nil
|
||||||
|
stage-packages:
|
||||||
|
- libcmark0.30.2
|
||||||
|
prime:
|
||||||
|
- usr/lib/*/libcmark.so*
|
||||||
|
|
||||||
@@ -192,14 +192,18 @@ add_library(neochat STATIC
|
|||||||
models/readmarkermodel.h
|
models/readmarkermodel.h
|
||||||
neochatroommember.cpp
|
neochatroommember.cpp
|
||||||
neochatroommember.h
|
neochatroommember.h
|
||||||
models/threadmodel.cpp
|
|
||||||
models/threadmodel.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||||
QT_QML_SINGLETON_TYPE TRUE
|
QT_QML_SINGLETON_TYPE TRUE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(ANDROID OR WIN32)
|
||||||
|
set_source_files_properties(qml/ShareActionStub.qml PROPERTIES
|
||||||
|
QT_QML_SOURCE_TYPENAME ShareAction
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat
|
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat
|
||||||
QML_FILES
|
QML_FILES
|
||||||
@@ -242,9 +246,11 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
|||||||
qml/EditMenu.qml
|
qml/EditMenu.qml
|
||||||
qml/MessageDelegateContextMenu.qml
|
qml/MessageDelegateContextMenu.qml
|
||||||
qml/FileDelegateContextMenu.qml
|
qml/FileDelegateContextMenu.qml
|
||||||
qml/FileDelegateContextMenuMobile.qml
|
|
||||||
qml/MessageSourceSheet.qml
|
qml/MessageSourceSheet.qml
|
||||||
|
qml/ReportSheet.qml
|
||||||
qml/ConfirmEncryptionDialog.qml
|
qml/ConfirmEncryptionDialog.qml
|
||||||
|
qml/RemoveSheet.qml
|
||||||
|
qml/BanSheet.qml
|
||||||
qml/RoomSearchPage.qml
|
qml/RoomSearchPage.qml
|
||||||
qml/LocationChooser.qml
|
qml/LocationChooser.qml
|
||||||
qml/TimelineView.qml
|
qml/TimelineView.qml
|
||||||
@@ -288,8 +294,6 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
|||||||
qml/ConsentDialog.qml
|
qml/ConsentDialog.qml
|
||||||
qml/AskDirectChatConfirmation.qml
|
qml/AskDirectChatConfirmation.qml
|
||||||
qml/HoverLinkIndicator.qml
|
qml/HoverLinkIndicator.qml
|
||||||
qml/AvatarNotification.qml
|
|
||||||
qml/ReasonDialog.qml
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
QtCore
|
QtCore
|
||||||
QtQuick
|
QtQuick
|
||||||
@@ -307,16 +311,12 @@ add_subdirectory(devtools)
|
|||||||
add_subdirectory(login)
|
add_subdirectory(login)
|
||||||
add_subdirectory(chatbar)
|
add_subdirectory(chatbar)
|
||||||
|
|
||||||
if(NOT ANDROID AND NOT WIN32)
|
if(UNIX)
|
||||||
qt_target_qml_sources(neochat QML_FILES qml/ShareAction.qml)
|
qt_target_qml_sources(neochat QML_FILES qml/ShareAction.qml)
|
||||||
else()
|
else()
|
||||||
set_source_files_properties(qml/ShareActionStub.qml PROPERTIES
|
|
||||||
QT_RESOURCE_ALIAS qml/ShareAction.qml
|
|
||||||
)
|
|
||||||
qt_target_qml_sources(neochat QML_FILES qml/ShareActionStub.qml)
|
qt_target_qml_sources(neochat QML_FILES qml/ShareActionStub.qml)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
configure_file(config-neochat.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-neochat.h)
|
configure_file(config-neochat.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-neochat.h)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
@@ -427,7 +427,7 @@ if (TARGET KF6::Crash)
|
|||||||
target_link_libraries(neochat PUBLIC KF6::Crash)
|
target_link_libraries(neochat PUBLIC KF6::Crash)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
kconfig_target_kcfg_file(neochat FILE neochatconfig.kcfg CLASS_NAME NeoChatConfig MUTATORS GENERATE_PROPERTIES DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR SINGLETON GENERATE_MOC QML_REGISTRATION)
|
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||||
|
|
||||||
if(NEOCHAT_FLATPAK)
|
if(NEOCHAT_FLATPAK)
|
||||||
target_compile_definitions(neochat PUBLIC NEOCHAT_FLATPAK)
|
target_compile_definitions(neochat PUBLIC NEOCHAT_FLATPAK)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ void ActionsHandler::handleMessageEvent(ChatBarCache *chatBarCache)
|
|||||||
|
|
||||||
QString handledText = chatBarCache->text();
|
QString handledText = chatBarCache->text();
|
||||||
handledText = handleMentions(handledText, chatBarCache->mentions());
|
handledText = handleMentions(handledText, chatBarCache->mentions());
|
||||||
handleMessage(chatBarCache->text(), handledText, chatBarCache);
|
handleMessage(m_room->mainCache()->text(), handledText, chatBarCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ActionsHandler::handleMentions(QString handledText, QList<Mention> *mentions)
|
QString ActionsHandler::handleMentions(QString handledText, QList<Mention> *mentions)
|
||||||
@@ -91,10 +91,18 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, Cha
|
|||||||
|
|
||||||
for (auto it = m_room->messageEvents().crbegin(); it != m_room->messageEvents().crend(); it++) {
|
for (auto it = m_room->messageEvents().crbegin(); it != m_room->messageEvents().crend(); it++) {
|
||||||
if (const auto event = eventCast<const RoomMessageEvent>(&**it)) {
|
if (const auto event = eventCast<const RoomMessageEvent>(&**it)) {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
if (event->senderId() == m_room->localMember().id() && event->has<EventContent::TextContent>()) {
|
||||||
|
#else
|
||||||
if (event->senderId() == m_room->localMember().id() && event->hasTextContent()) {
|
if (event->senderId() == m_room->localMember().id() && event->hasTextContent()) {
|
||||||
|
#endif
|
||||||
QString originalString;
|
QString originalString;
|
||||||
if (event->content()) {
|
if (event->content()) {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
originalString = event->get<EventContent::TextContent>()->body;
|
||||||
|
#else
|
||||||
originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content())->body;
|
originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content())->body;
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
originalString = event->plainBody();
|
originalString = event->plainBody();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ QQC2.Control {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
if (!repeatTimer.running && NeoChatConfig.typingNotifications) {
|
if (!repeatTimer.running && Config.typingNotifications) {
|
||||||
var textExists = text.length > 0;
|
var textExists = text.length > 0;
|
||||||
root.currentRoom.sendTypingNotification(textExists);
|
root.currentRoom.sendTypingNotification(textExists);
|
||||||
textExists ? repeatTimer.start() : repeatTimer.stop();
|
textExists ? repeatTimer.start() : repeatTimer.stop();
|
||||||
@@ -353,8 +353,8 @@ QQC2.Control {
|
|||||||
startBreakpoint: Kirigami.Units.gridUnit * 46
|
startBreakpoint: Kirigami.Units.gridUnit * 46
|
||||||
endBreakpoint: Kirigami.Units.gridUnit * 66
|
endBreakpoint: Kirigami.Units.gridUnit * 66
|
||||||
startPercentWidth: 100
|
startPercentWidth: 100
|
||||||
endPercentWidth: NeoChatConfig.compactLayout ? 100 : 85
|
endPercentWidth: Config.compactLayout ? 100 : 85
|
||||||
maxWidth: NeoChatConfig.compactLayout ? -1 : Kirigami.Units.gridUnit * 60
|
maxWidth: Config.compactLayout ? -1 : Kirigami.Units.gridUnit * 60
|
||||||
|
|
||||||
parentWidth: root.width
|
parentWidth: root.width
|
||||||
}
|
}
|
||||||
@@ -395,7 +395,7 @@ QQC2.Control {
|
|||||||
repeatTimer.stop();
|
repeatTimer.stop();
|
||||||
root.currentRoom.markAllMessagesAsRead();
|
root.currentRoom.markAllMessagesAsRead();
|
||||||
textField.clear();
|
textField.clear();
|
||||||
_private.chatBarCache.clearRelations();
|
_private.chatBarCache.replyId = "";
|
||||||
messageSent();
|
messageSent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,12 @@ QString ChatBarCache::relationMessage() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
|
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
|
||||||
return EventHandler::markdownBody(&**event);
|
EventHandler eventhandler(room, &**event);
|
||||||
|
if (isEditing()) {
|
||||||
|
return eventhandler.getMarkdownBody();
|
||||||
|
} else {
|
||||||
|
return eventhandler.getMarkdownBody().toHtmlEscaped();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -139,8 +144,8 @@ void ChatBarCache::setThreadId(const QString &threadId)
|
|||||||
if (m_threadId == threadId) {
|
if (m_threadId == threadId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto oldThreadId = std::exchange(m_threadId, threadId);
|
m_threadId = threadId;
|
||||||
Q_EMIT threadIdChanged(oldThreadId, m_threadId);
|
Q_EMIT threadIdChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ChatBarCache::attachmentPath() const
|
QString ChatBarCache::attachmentPath() const
|
||||||
@@ -160,16 +165,6 @@ void ChatBarCache::setAttachmentPath(const QString &attachmentPath)
|
|||||||
Q_EMIT relationIdChanged(oldEventId, m_relationId);
|
Q_EMIT relationIdChanged(oldEventId, m_relationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatBarCache::clearRelations()
|
|
||||||
{
|
|
||||||
const auto oldEventId = std::exchange(m_relationId, QString());
|
|
||||||
const auto oldThreadId = std::exchange(m_threadId, QString());
|
|
||||||
m_attachmentPath = QString();
|
|
||||||
Q_EMIT relationIdChanged(oldEventId, m_relationId);
|
|
||||||
Q_EMIT threadIdChanged(oldThreadId, m_threadId);
|
|
||||||
Q_EMIT attachmentPathChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<Mention> *ChatBarCache::mentions()
|
QList<Mention> *ChatBarCache::mentions()
|
||||||
{
|
{
|
||||||
return &m_mentions;
|
return &m_mentions;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ struct Mention {
|
|||||||
* A class to cache data from a chat bar.
|
* 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
|
* 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. ChatBarComponent.
|
* 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
|
* 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
|
* between different rooms, i.e. there is an expectation that each NeoChatRoom could
|
||||||
@@ -43,7 +43,7 @@ struct Mention {
|
|||||||
* as it's parent. This is necessary for certain functions which need to get
|
* as it's parent. This is necessary for certain functions which need to get
|
||||||
* relevant room information.
|
* relevant room information.
|
||||||
*
|
*
|
||||||
* @sa ChatBar, ChatBarComponent, NeoChatRoom
|
* @sa ChatBar, MessageEditComponent, NeoChatRoom
|
||||||
*/
|
*/
|
||||||
class ChatBarCache : public QObject
|
class ChatBarCache : public QObject
|
||||||
{
|
{
|
||||||
@@ -165,13 +165,6 @@ public:
|
|||||||
QString attachmentPath() const;
|
QString attachmentPath() const;
|
||||||
void setAttachmentPath(const QString &attachmentPath);
|
void setAttachmentPath(const QString &attachmentPath);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clear all relations in the cache.
|
|
||||||
*
|
|
||||||
* This includes relation ID, thread root ID and attachment path.
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE void clearRelations();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieve the mentions for the current chat bar text.
|
* @brief Retrieve the mentions for the current chat bar text.
|
||||||
*/
|
*/
|
||||||
@@ -195,7 +188,7 @@ public:
|
|||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void textChanged();
|
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();
|
||||||
void attachmentPathChanged();
|
void attachmentPathChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -26,10 +26,23 @@ void ColorSchemer::apply(int idx)
|
|||||||
c->activateScheme(c->model()->index(idx, 0));
|
c->activateScheme(c->model()->index(idx, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
int ColorSchemer::indexForCurrentScheme()
|
void ColorSchemer::apply(const QString &name)
|
||||||
{
|
{
|
||||||
return -1;
|
c->activateScheme(c->indexForScheme(name));
|
||||||
// return c->indexForSchemeId(c->activeSchemeId()).row();
|
}
|
||||||
|
|
||||||
|
int ColorSchemer::indexForScheme(const QString &name) const
|
||||||
|
{
|
||||||
|
auto index = c->indexForScheme(name).row();
|
||||||
|
if (index == -1) {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ColorSchemer::nameForIndex(int index) const
|
||||||
|
{
|
||||||
|
return c->model()->data(c->model()->index(index, 0), Qt::DisplayRole).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "moc_colorschemer.cpp"
|
#include "moc_colorschemer.cpp"
|
||||||
|
|||||||
@@ -44,11 +44,21 @@ public:
|
|||||||
Q_INVOKABLE void apply(int idx);
|
Q_INVOKABLE void apply(int idx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the row for the current color scheme.
|
* @brief Activates the KColorScheme with the given name.
|
||||||
*
|
*
|
||||||
* @sa KColorScheme
|
* @sa KColorScheme
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE int indexForCurrentScheme();
|
Q_INVOKABLE void apply(const QString &name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the index for the scheme with the given name.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE int indexForScheme(const QString &name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the name for the scheme with the given index.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE QString nameForIndex(int index) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
KColorSchemeManager *c;
|
KColorSchemeManager *c;
|
||||||
|
|||||||
@@ -13,11 +13,13 @@
|
|||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <Quotient/accountregistry.h>
|
||||||
#include <Quotient/csapi/notifications.h>
|
#include <Quotient/csapi/notifications.h>
|
||||||
#include <Quotient/qt_connection_util.h>
|
#include <Quotient/qt_connection_util.h>
|
||||||
#include <Quotient/settings.h>
|
#include <Quotient/settings.h>
|
||||||
|
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
|
#include "neochatconnection.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
#include "notificationsmanager.h"
|
#include "notificationsmanager.h"
|
||||||
#include "proxycontroller.h"
|
#include "proxycontroller.h"
|
||||||
@@ -425,28 +427,11 @@ void Controller::removeConnection(const QString &userId)
|
|||||||
|
|
||||||
bool Controller::csSupported() const
|
bool Controller::csSupported() const
|
||||||
{
|
{
|
||||||
#if Quotient_VERSION_MINOR > 8
|
#if Quotient_VERSION_MINOR > 9
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::revertToDefaultConfig()
|
|
||||||
{
|
|
||||||
const auto config = NeoChatConfig::self();
|
|
||||||
config->setDefaults();
|
|
||||||
config->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Controller::isImageShown(const QString &eventId)
|
|
||||||
{
|
|
||||||
return m_shownImages.contains(eventId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Controller::markImageShown(const QString &eventId)
|
|
||||||
{
|
|
||||||
m_shownImages.append(eventId);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "moc_controller.cpp"
|
#include "moc_controller.cpp"
|
||||||
|
|||||||
@@ -108,17 +108,6 @@ public:
|
|||||||
|
|
||||||
bool csSupported() const;
|
bool csSupported() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Revert all configuration values to their default.
|
|
||||||
*
|
|
||||||
* The parameters along with their defaults are specified in the config file
|
|
||||||
* neochatconfig.kcfg.
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE void revertToDefaultConfig();
|
|
||||||
|
|
||||||
Q_INVOKABLE bool isImageShown(const QString &eventId);
|
|
||||||
Q_INVOKABLE void markImageShown(const QString &eventId);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit Controller(QObject *parent = nullptr);
|
explicit Controller(QObject *parent = nullptr);
|
||||||
|
|
||||||
@@ -127,11 +116,13 @@ private:
|
|||||||
|
|
||||||
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const QString &account);
|
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const QString &account);
|
||||||
|
|
||||||
|
void loadSettings();
|
||||||
|
void saveSettings() const;
|
||||||
|
|
||||||
Quotient::AccountRegistry m_accountRegistry;
|
Quotient::AccountRegistry m_accountRegistry;
|
||||||
QStringList m_accountsLoading;
|
QStringList m_accountsLoading;
|
||||||
QMap<QString, QPointer<NeoChatConnection>> m_connectionsLoading;
|
QMap<QString, QPointer<NeoChatConnection>> m_connectionsLoading;
|
||||||
QString m_endpoint;
|
QString m_endpoint;
|
||||||
QStringList m_shownImages;
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void invokeLogin();
|
void invokeLogin();
|
||||||
|
|||||||
@@ -17,25 +17,25 @@ FormCard.FormCardPage {
|
|||||||
|
|
||||||
FormCard.FormCheckDelegate {
|
FormCard.FormCheckDelegate {
|
||||||
text: i18nc("@option:check", "Show hidden events in the timeline")
|
text: i18nc("@option:check", "Show hidden events in the timeline")
|
||||||
checked: NeoChatConfig.showAllEvents
|
checked: Config.showAllEvents
|
||||||
|
|
||||||
onToggled: NeoChatConfig.showAllEvents = checked
|
onToggled: Config.showAllEvents = checked
|
||||||
}
|
}
|
||||||
FormCard.FormCheckDelegate {
|
FormCard.FormCheckDelegate {
|
||||||
id: roomAccountDataVisibleCheck
|
id: roomAccountDataVisibleCheck
|
||||||
text: i18nc("@option:check Enable the matrix 'threads' feature", "Always allow device verification")
|
text: i18nc("@option:check Enable the matrix 'threads' feature", "Always allow device verification")
|
||||||
description: i18n("Allow the user to start a verification session with devices that were already verified")
|
description: i18n("Allow the user to start a verification session with devices that were already verified")
|
||||||
checked: NeoChatConfig.alwaysVerifyDevice
|
checked: Config.alwaysVerifyDevice
|
||||||
|
|
||||||
onToggled: NeoChatConfig.alwaysVerifyDevice = checked
|
onToggled: Config.alwaysVerifyDevice = checked
|
||||||
}
|
}
|
||||||
FormCard.FormCheckDelegate {
|
FormCard.FormCheckDelegate {
|
||||||
text: i18nc("@option:check", "Show focus in window header")
|
text: i18nc("@option:check", "Show focus in window header")
|
||||||
checked: NeoChatConfig.windowTitleFocus
|
checked: Config.windowTitleFocus
|
||||||
|
|
||||||
onToggled: {
|
onToggled: {
|
||||||
NeoChatConfig.windowTitleFocus = checked;
|
Config.windowTitleFocus = checked;
|
||||||
NeoChatConfig.save();
|
Config.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,23 +18,23 @@ FormCard.FormCardPage {
|
|||||||
FormCard.FormCheckDelegate {
|
FormCard.FormCheckDelegate {
|
||||||
id: roomAccountDataVisibleCheck
|
id: roomAccountDataVisibleCheck
|
||||||
text: i18nc("@option:check Enable the matrix 'threads' feature", "Threads")
|
text: i18nc("@option:check Enable the matrix 'threads' feature", "Threads")
|
||||||
checked: NeoChatConfig.threads
|
checked: Config.threads
|
||||||
|
|
||||||
onToggled: NeoChatConfig.threads = checked
|
onToggled: Config.threads = checked
|
||||||
}
|
}
|
||||||
FormCard.FormCheckDelegate {
|
FormCard.FormCheckDelegate {
|
||||||
text: i18nc("@option:check Enable the matrix 'secret backup' feature", "Secret Backup")
|
text: i18nc("@option:check Enable the matrix 'secret backup' feature", "Secret Backup")
|
||||||
checked: NeoChatConfig.secretBackup
|
checked: Config.secretBackup
|
||||||
|
|
||||||
onToggled: NeoChatConfig.secretBackup = checked
|
onToggled: Config.secretBackup = checked
|
||||||
}
|
}
|
||||||
FormCard.FormCheckDelegate {
|
FormCard.FormCheckDelegate {
|
||||||
text: i18nc("@option:check Enable the matrix feature to add a phone number as a third party ID", "Add phone numbers as 3PIDs")
|
text: i18nc("@option:check Enable the matrix feature to add a phone number as a third party ID", "Add phone numbers as 3PIDs")
|
||||||
checked: NeoChatConfig.phone3PId
|
checked: Config.phone3PId
|
||||||
|
|
||||||
onToggled: {
|
onToggled: {
|
||||||
NeoChatConfig.phone3PId = checked
|
Config.phone3PId = checked
|
||||||
NeoChatConfig.save();
|
Config.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,6 @@ public:
|
|||||||
ReadMarker, /**< The local user read marker. */
|
ReadMarker, /**< The local user read marker. */
|
||||||
Loading, /**< A delegate to tell the user more messages are being loaded. */
|
Loading, /**< A delegate to tell the user more messages are being loaded. */
|
||||||
TimelineEnd, /**< A delegate to inform that all messages are loaded. */
|
TimelineEnd, /**< A delegate to inform that all messages are loaded. */
|
||||||
Predecessor, /**< A delegate to show a room predecessor. */
|
|
||||||
Successor, /**< A delegate to show a room successor. */
|
|
||||||
Other, /**< Anything that cannot be classified as another type. */
|
Other, /**< Anything that cannot be classified as another type. */
|
||||||
};
|
};
|
||||||
Q_ENUM(Type);
|
Q_ENUM(Type);
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public:
|
|||||||
Reply, /**< A component to show a replied-to message. */
|
Reply, /**< A component to show a replied-to message. */
|
||||||
LinkPreview, /**< A preview of a URL in the message. */
|
LinkPreview, /**< A preview of a URL in the message. */
|
||||||
LinkPreviewLoad, /**< A loading dialog for a link preview. */
|
LinkPreviewLoad, /**< A loading dialog for a link preview. */
|
||||||
ChatBar, /**< A text edit for editing a message. */
|
Edit, /**< A text edit for editing a message. */
|
||||||
Verification, /**< A user verification session start message. */
|
Verification, /**< A user verification session start message. */
|
||||||
Loading, /**< The component is loading. */
|
Loading, /**< The component is loading. */
|
||||||
Other, /**< Anything that cannot be classified as another type. */
|
Other, /**< Anything that cannot be classified as another type. */
|
||||||
|
|||||||
@@ -5,17 +5,18 @@
|
|||||||
|
|
||||||
#include <QMovie>
|
#include <QMovie>
|
||||||
|
|
||||||
#include <KFormat>
|
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
|
|
||||||
|
#include <Quotient/eventitem.h>
|
||||||
#include <Quotient/events/encryptionevent.h>
|
#include <Quotient/events/encryptionevent.h>
|
||||||
#include <Quotient/events/event.h>
|
#include <Quotient/events/event.h>
|
||||||
|
#include <Quotient/events/eventcontent.h>
|
||||||
#include <Quotient/events/reactionevent.h>
|
#include <Quotient/events/reactionevent.h>
|
||||||
#include <Quotient/events/redactionevent.h>
|
#include <Quotient/events/redactionevent.h>
|
||||||
#include <Quotient/events/roomavatarevent.h>
|
#include <Quotient/events/roomavatarevent.h>
|
||||||
#include <Quotient/events/roomcanonicalaliasevent.h>
|
#include <Quotient/events/roomcanonicalaliasevent.h>
|
||||||
#include <Quotient/events/roomevent.h>
|
|
||||||
#include <Quotient/events/roommemberevent.h>
|
#include <Quotient/events/roommemberevent.h>
|
||||||
|
#include <Quotient/events/roommessageevent.h>
|
||||||
#include <Quotient/events/roompowerlevelsevent.h>
|
#include <Quotient/events/roompowerlevelsevent.h>
|
||||||
#include <Quotient/events/simplestateevents.h>
|
#include <Quotient/events/simplestateevents.h>
|
||||||
#include <Quotient/events/stickerevent.h>
|
#include <Quotient/events/stickerevent.h>
|
||||||
@@ -26,6 +27,9 @@
|
|||||||
#include "events/locationbeaconevent.h"
|
#include "events/locationbeaconevent.h"
|
||||||
#include "events/pollevent.h"
|
#include "events/pollevent.h"
|
||||||
#include "events/widgetevent.h"
|
#include "events/widgetevent.h"
|
||||||
|
#include "linkpreviewer.h"
|
||||||
|
#include "messagecomponenttype.h"
|
||||||
|
#include "models/reactionmodel.h"
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
#include "texthandler.h"
|
#include "texthandler.h"
|
||||||
@@ -33,52 +37,68 @@
|
|||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
QString EventHandler::id(const Quotient::RoomEvent *event)
|
EventHandler::EventHandler(const NeoChatRoom *room, const RoomEvent *event)
|
||||||
|
: m_room(room)
|
||||||
|
, m_event(event)
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
|
||||||
qCWarning(EventHandling) << "id called with event set to nullptr.";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return !event->id().isEmpty() ? event->id() : event->transactionId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EventHandler::authorDisplayName(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool isPending)
|
QString EventHandler::getId() const
|
||||||
{
|
{
|
||||||
if (room == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "authorDisplayName called with room set to nullptr.";
|
qCWarning(EventHandling) << "getId called with m_event set to nullptr.";
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (event == nullptr) {
|
|
||||||
qCWarning(EventHandling) << "authorDisplayName called with event set to nullptr.";
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is<RoomMemberEvent>(*event) && !event->unsignedJson()[QStringLiteral("prev_content")][QStringLiteral("displayname")].isNull()
|
return !m_event->id().isEmpty() ? m_event->id() : m_event->transactionId();
|
||||||
&& event->stateKey() == event->senderId()) {
|
}
|
||||||
auto previousDisplayName = event->unsignedJson()[QStringLiteral("prev_content")][QStringLiteral("displayname")].toString().toHtmlEscaped();
|
|
||||||
|
MessageComponentType::Type EventHandler::messageComponentType() const
|
||||||
|
{
|
||||||
|
if (m_event == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "messageComponentType called with m_event set to nullptr.";
|
||||||
|
return MessageComponentType::Other;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MessageComponentType::typeForEvent(*m_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString EventHandler::getAuthorDisplayName(bool isPending) const
|
||||||
|
{
|
||||||
|
if (m_room == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "getAuthorDisplayName called with m_room set to nullptr.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (m_event == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "getAuthorDisplayName called with m_event set to nullptr.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is<RoomMemberEvent>(*m_event) && !m_event->unsignedJson()[QStringLiteral("prev_content")][QStringLiteral("displayname")].isNull()
|
||||||
|
&& m_event->stateKey() == m_event->senderId()) {
|
||||||
|
auto previousDisplayName = m_event->unsignedJson()[QStringLiteral("prev_content")][QStringLiteral("displayname")].toString().toHtmlEscaped();
|
||||||
if (previousDisplayName.isEmpty()) {
|
if (previousDisplayName.isEmpty()) {
|
||||||
previousDisplayName = event->senderId();
|
previousDisplayName = m_event->senderId();
|
||||||
}
|
}
|
||||||
return previousDisplayName;
|
return previousDisplayName;
|
||||||
} else {
|
} else {
|
||||||
const auto author = isPending ? room->localMember() : room->member(event->senderId());
|
const auto author = isPending ? m_room->localMember() : m_room->member(m_event->senderId());
|
||||||
return author.htmlSafeDisplayName();
|
return author.htmlSafeDisplayName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EventHandler::singleLineAuthorDisplayname(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool isPending)
|
QString EventHandler::singleLineAuthorDisplayname(bool isPending) const
|
||||||
{
|
{
|
||||||
if (room == nullptr) {
|
if (m_room == nullptr) {
|
||||||
qCWarning(EventHandling) << "singleLineAuthorDisplayname called with room set to nullptr.";
|
qCWarning(EventHandling) << "getAuthorDisplayName called with m_room set to nullptr.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "singleLineAuthorDisplayname called with event set to nullptr.";
|
qCWarning(EventHandling) << "getAuthorDisplayName called with m_event set to nullptr.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto author = isPending ? room->localMember() : room->member(event->senderId());
|
const auto author = isPending ? m_room->localMember() : m_room->member(m_event->senderId());
|
||||||
auto displayName = author.displayName();
|
auto displayName = author.displayName();
|
||||||
displayName.replace(QStringLiteral("<br>\n"), QStringLiteral(" "));
|
displayName.replace(QStringLiteral("<br>\n"), QStringLiteral(" "));
|
||||||
displayName.replace(QStringLiteral("<br>"), QStringLiteral(" "));
|
displayName.replace(QStringLiteral("<br>"), QStringLiteral(" "));
|
||||||
@@ -89,10 +109,10 @@ QString EventHandler::singleLineAuthorDisplayname(const NeoChatRoom *room, const
|
|||||||
return displayName;
|
return displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDateTime EventHandler::time(const Quotient::RoomEvent *event, bool isPending, QDateTime lastUpdated)
|
QDateTime EventHandler::getTime(bool isPending, QDateTime lastUpdated) const
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "time called with event set to nullptr.";
|
qCWarning(EventHandling) << "getTime called with m_event set to nullptr.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (isPending && lastUpdated == QDateTime()) {
|
if (isPending && lastUpdated == QDateTime()) {
|
||||||
@@ -100,16 +120,24 @@ QDateTime EventHandler::time(const Quotient::RoomEvent *event, bool isPending, Q
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return isPending ? lastUpdated : event->originTimestamp();
|
return isPending ? lastUpdated : m_event->originTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EventHandler::timeString(const Quotient::RoomEvent *event, bool relative, QLocale::FormatType format, bool isPending, QDateTime lastUpdated)
|
QString EventHandler::getTimeString(bool relative, QLocale::FormatType format, bool isPending, QDateTime lastUpdated) const
|
||||||
{
|
{
|
||||||
auto ts = time(event, isPending, lastUpdated);
|
if (m_event == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "getTimeString called with m_event set to nullptr.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (isPending && lastUpdated == QDateTime()) {
|
||||||
|
qCWarning(EventHandling) << "a value must be provided for lastUpdated for a pending event.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ts = getTime(isPending, lastUpdated);
|
||||||
if (ts.isValid()) {
|
if (ts.isValid()) {
|
||||||
if (relative) {
|
if (relative) {
|
||||||
KFormat formatter;
|
return m_format.formatRelativeDate(ts.toLocalTime().date(), format);
|
||||||
return formatter.formatRelativeDate(ts.toLocalTime().date(), format);
|
|
||||||
} else {
|
} else {
|
||||||
return QLocale().toString(ts.toLocalTime().time(), format);
|
return QLocale().toString(ts.toLocalTime().time(), format);
|
||||||
}
|
}
|
||||||
@@ -117,45 +145,44 @@ QString EventHandler::timeString(const Quotient::RoomEvent *event, bool relative
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EventHandler::timeString(const Quotient::RoomEvent *event, const QString &format, bool isPending, const QDateTime &lastUpdated)
|
QString EventHandler::getTimeString(const QString &format, bool isPending, const QDateTime &lastUpdated)
|
||||||
{
|
{
|
||||||
return time(event, isPending, lastUpdated).toLocalTime().toString(format);
|
return getTime(isPending, lastUpdated).toLocalTime().toString(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventHandler::isHighlighted(const NeoChatRoom *room, const Quotient::RoomEvent *event)
|
bool EventHandler::isHighlighted()
|
||||||
{
|
{
|
||||||
if (room == nullptr) {
|
if (m_room == nullptr) {
|
||||||
qCWarning(EventHandling) << "isHighlighted called with room set to nullptr.";
|
qCWarning(EventHandling) << "isHighlighted called with m_room set to nullptr.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "isHighlighted called with event set to nullptr.";
|
qCWarning(EventHandling) << "isHighlighted called with m_event set to nullptr.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !room->isDirectChat() && room->isEventHighlighted(event);
|
return !m_room->isDirectChat() && m_room->isEventHighlighted(m_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventHandler::isHidden(const NeoChatRoom *room, const Quotient::RoomEvent *event)
|
bool EventHandler::isHidden()
|
||||||
{
|
{
|
||||||
if (room == nullptr) {
|
if (m_room == nullptr) {
|
||||||
qCWarning(EventHandling) << "isHidden called with room set to nullptr.";
|
qCWarning(EventHandling) << "isHidden called with m_room set to nullptr.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "isHidden called with event set to nullptr.";
|
qCWarning(EventHandling) << "isHidden called with m_event set to nullptr.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->isStateEvent() && !NeoChatConfig::self()->showStateEvent()) {
|
if (m_event->isStateEvent() && !NeoChatConfig::self()->showStateEvent()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto roomMemberEvent = eventCast<const RoomMemberEvent>(event)) {
|
if (auto roomMemberEvent = eventCast<const RoomMemberEvent>(m_event)) {
|
||||||
if ((roomMemberEvent->isJoin() || roomMemberEvent->isLeave()) && !NeoChatConfig::self()->showLeaveJoinEvent()) {
|
if ((roomMemberEvent->isJoin() || roomMemberEvent->isLeave()) && !NeoChatConfig::self()->showLeaveJoinEvent()) {
|
||||||
return true;
|
return true;
|
||||||
} else if (roomMemberEvent->isRename() && roomMemberEvent->prevContent() && roomMemberEvent->prevContent()->membership == roomMemberEvent->membership()
|
} else if (roomMemberEvent->isRename() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::self()->showRename()) {
|
||||||
&& !NeoChatConfig::self()->showRename()) {
|
|
||||||
return true;
|
return true;
|
||||||
} else if (roomMemberEvent->isAvatarUpdate() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave()
|
} else if (roomMemberEvent->isAvatarUpdate() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave()
|
||||||
&& !NeoChatConfig::self()->showAvatarUpdate()) {
|
&& !NeoChatConfig::self()->showAvatarUpdate()) {
|
||||||
@@ -163,33 +190,33 @@ bool EventHandler::isHidden(const NeoChatRoom *room, const Quotient::RoomEvent *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->isStateEvent() && eventCast<const StateEvent>(event)->repeatsState()) {
|
if (m_event->isStateEvent() && eventCast<const StateEvent>(m_event)->repeatsState()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// isReplacement?
|
// isReplacement?
|
||||||
if (auto e = eventCast<const RoomMessageEvent>(event)) {
|
if (auto e = eventCast<const RoomMessageEvent>(m_event)) {
|
||||||
if (!e->replacedEvent().isEmpty()) {
|
if (!e->replacedEvent().isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is<RedactionEvent>(*event) || is<ReactionEvent>(*event)) {
|
if (is<RedactionEvent>(*m_event) || is<ReactionEvent>(*m_event)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto e = eventCast<const RoomMessageEvent>(event)) {
|
if (auto e = eventCast<const RoomMessageEvent>(m_event)) {
|
||||||
if (!e->replacedEvent().isEmpty() && e->replacedEvent() != e->id()) {
|
if (!e->replacedEvent().isEmpty() && e->replacedEvent() != e->id()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (room->connection()->isIgnored(event->senderId())) {
|
if (m_room->connection()->isIgnored(m_event->senderId())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// hide ending live location beacons
|
// hide ending live location beacons
|
||||||
if (event->isStateEvent() && event->matrixType() == "org.matrix.msc3672.beacon_info"_ls && !event->contentJson()["live"_ls].toBool()) {
|
if (m_event->isStateEvent() && m_event->matrixType() == "org.matrix.msc3672.beacon_info"_ls && !m_event->contentJson()["live"_ls].toBool()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,10 +236,18 @@ QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
|
|||||||
{
|
{
|
||||||
QString body;
|
QString body;
|
||||||
|
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
if (event.has<EventContent::FileContent>()) {
|
||||||
|
#else
|
||||||
if (event.hasFileContent()) {
|
if (event.hasFileContent()) {
|
||||||
|
#endif
|
||||||
// if filename is given or body is equal to filename,
|
// if filename is given or body is equal to filename,
|
||||||
// then body is a caption
|
// then body is a caption
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
QString filename = event.get<EventContent::FileContent>()->originalName;
|
||||||
|
#else
|
||||||
QString filename = event.content()->fileInfo()->originalName;
|
QString filename = event.content()->fileInfo()->originalName;
|
||||||
|
#endif
|
||||||
QString body = event.plainBody();
|
QString body = event.plainBody();
|
||||||
if (filename.isEmpty() || filename == body) {
|
if (filename.isEmpty() || filename == body) {
|
||||||
return QString();
|
return QString();
|
||||||
@@ -220,79 +255,76 @@ QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
|
|||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
if (event.has<EventContent::TextContent>() && event.content()) {
|
||||||
|
body = event.get<EventContent::TextContent>()->body;
|
||||||
|
#else
|
||||||
if (event.hasTextContent() && event.content()) {
|
if (event.hasTextContent() && event.content()) {
|
||||||
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
body = event.plainBody();
|
body = event.plainBody();
|
||||||
}
|
}
|
||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EventHandler::richBody(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool stripNewlines)
|
QString EventHandler::getRichBody(bool stripNewlines) const
|
||||||
{
|
{
|
||||||
if (room == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "richBody called with room set to nullptr.";
|
qCWarning(EventHandling) << "getRichBody called with m_event set to nullptr.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (event == nullptr) {
|
return getBody(m_event, Qt::RichText, stripNewlines);
|
||||||
qCWarning(EventHandling) << "richBody called with event set to nullptr.";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return getBody(room, event, Qt::RichText, stripNewlines);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EventHandler::plainBody(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool stripNewlines)
|
QString EventHandler::getPlainBody(bool stripNewlines) const
|
||||||
{
|
{
|
||||||
if (room == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "plainBody called with room set to nullptr.";
|
qCWarning(EventHandling) << "getPlainBody called with m_event set to nullptr.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (event == nullptr) {
|
return getBody(m_event, Qt::PlainText, stripNewlines);
|
||||||
qCWarning(EventHandling) << "plainBody called with event set to nullptr.";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return getBody(room, event, Qt::PlainText, stripNewlines);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EventHandler::markdownBody(const Quotient::RoomEvent *event)
|
QString EventHandler::getMarkdownBody() const
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "markdownBody called with event set to nullptr.";
|
qCWarning(EventHandling) << "getMarkdownBody called with m_event set to nullptr.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!event->is<RoomMessageEvent>()) {
|
if (!m_event->is<RoomMessageEvent>()) {
|
||||||
qCWarning(EventHandling) << "markdownBody called when event isn't a RoomMessageEvent.";
|
qCWarning(EventHandling) << "getMarkdownBody called when m_event isn't a RoomMessageEvent.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto roomMessageEvent = eventCast<const RoomMessageEvent>(event);
|
const auto roomMessageEvent = eventCast<const RoomMessageEvent>(m_event);
|
||||||
|
|
||||||
QString plainBody = roomMessageEvent->plainBody();
|
QString plainBody = roomMessageEvent->plainBody();
|
||||||
plainBody.remove(TextRegex::removeReply);
|
plainBody.remove(TextRegex::removeReply);
|
||||||
return plainBody;
|
return plainBody;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent *event, Qt::TextFormat format, bool stripNewlines)
|
QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat format, bool stripNewlines) const
|
||||||
{
|
{
|
||||||
if (event->isRedacted()) {
|
if (event->isRedacted()) {
|
||||||
auto reason = event->redactedBecause()->reason();
|
auto reason = event->redactedBecause()->reason();
|
||||||
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>") : i18n("<i>[This message was deleted: %1]</i>", reason);
|
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>") : i18n("<i>[This message was deleted: %1]</i>", reason.toHtmlEscaped());
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool prettyPrint = (format == Qt::RichText);
|
const bool prettyPrint = (format == Qt::RichText);
|
||||||
|
|
||||||
return switchOnType(
|
return switchOnType(
|
||||||
*event,
|
*event,
|
||||||
[room, format, stripNewlines](const RoomMessageEvent &event) {
|
[this, format, stripNewlines](const RoomMessageEvent &event) {
|
||||||
return getMessageBody(room, event, format, stripNewlines);
|
return getMessageBody(event, format, stripNewlines);
|
||||||
},
|
},
|
||||||
[](const StickerEvent &e) {
|
[](const StickerEvent &e) {
|
||||||
return e.body();
|
return e.body();
|
||||||
},
|
},
|
||||||
[room, prettyPrint](const RoomMemberEvent &e) {
|
[this, prettyPrint](const RoomMemberEvent &e) {
|
||||||
// FIXME: Rewind to the name that was at the time of this event
|
// FIXME: Rewind to the name that was at the time of this event
|
||||||
auto subjectName = room->member(e.userId()).htmlSafeDisplayName();
|
auto subjectName = m_room->member(e.userId()).htmlSafeDisplayName();
|
||||||
if (e.membership() == Membership::Leave) {
|
if (e.membership() == Membership::Leave) {
|
||||||
if (e.prevContent() && e.prevContent()->displayName) {
|
if (e.prevContent() && e.prevContent()->displayName) {
|
||||||
subjectName = sanitized(*e.prevContent()->displayName);
|
subjectName = sanitized(*e.prevContent()->displayName);
|
||||||
@@ -304,7 +336,7 @@ QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent
|
|||||||
|
|
||||||
if (prettyPrint) {
|
if (prettyPrint) {
|
||||||
subjectName = QStringLiteral("<a href=\"https://matrix.to/#/%1\" style=\"color: %2\">%3</a>")
|
subjectName = QStringLiteral("<a href=\"https://matrix.to/#/%1\" style=\"color: %2\">%3</a>")
|
||||||
.arg(e.userId(), room->member(e.userId()).color().name(), subjectName);
|
.arg(e.userId(), m_room->member(e.userId()).color().name(), subjectName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The below code assumes senderName output in AuthorRole
|
// The below code assumes senderName output in AuthorRole
|
||||||
@@ -367,13 +399,9 @@ QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent
|
|||||||
if (e.prevContent() && e.prevContent()->membership == Membership::Ban) {
|
if (e.prevContent() && e.prevContent()->membership == Membership::Ban) {
|
||||||
return (e.senderId() != e.userId()) ? i18n("unbanned %1", subjectName) : i18n("self-unbanned");
|
return (e.senderId() != e.userId()) ? i18n("unbanned %1", subjectName) : i18n("self-unbanned");
|
||||||
}
|
}
|
||||||
if (e.senderId() == e.userId()) {
|
return (e.senderId() != e.userId())
|
||||||
return i18n("left the room");
|
? i18n("has put %1 out of the room: %2", subjectName, e.contentJson()["reason"_ls].toString().toHtmlEscaped())
|
||||||
}
|
: i18n("left the room");
|
||||||
if (const auto &reason = e.contentJson()["reason"_ls].toString().toHtmlEscaped(); !reason.isEmpty()) {
|
|
||||||
return i18n("has put %1 out of the room: %2", subjectName, reason);
|
|
||||||
}
|
|
||||||
return i18n("has put %1 out of the room", subjectName);
|
|
||||||
case Membership::Ban:
|
case Membership::Ban:
|
||||||
if (e.senderId() != e.userId()) {
|
if (e.senderId() != e.userId()) {
|
||||||
if (e.reason().isEmpty()) {
|
if (e.reason().isEmpty()) {
|
||||||
@@ -444,15 +472,20 @@ QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent
|
|||||||
i18n("Unknown event"));
|
i18n("Unknown event"));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EventHandler::getMessageBody(const NeoChatRoom *room, const RoomMessageEvent &event, Qt::TextFormat format, bool stripNewlines)
|
QString EventHandler::getMessageBody(const RoomMessageEvent &event, Qt::TextFormat format, bool stripNewlines) const
|
||||||
{
|
{
|
||||||
TextHandler textHandler;
|
TextHandler textHandler;
|
||||||
|
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
if (event.has<EventContent::FileContent>()) {
|
||||||
|
QString fileCaption = event.get<EventContent::FileContent>()->originalName;
|
||||||
|
#else
|
||||||
if (event.hasFileContent()) {
|
if (event.hasFileContent()) {
|
||||||
auto fileCaption = event.content()->fileInfo()->originalName;
|
auto fileCaption = event.content()->fileInfo()->originalName;
|
||||||
|
#endif
|
||||||
if (fileCaption.isEmpty()) {
|
if (fileCaption.isEmpty()) {
|
||||||
fileCaption = event.plainBody();
|
fileCaption = event.plainBody();
|
||||||
} else if (event.content()->fileInfo()->originalName != event.plainBody()) {
|
} else if (fileCaption != event.plainBody()) {
|
||||||
fileCaption = event.plainBody() + " | "_ls + fileCaption;
|
fileCaption = event.plainBody() + " | "_ls + fileCaption;
|
||||||
}
|
}
|
||||||
textHandler.setData(fileCaption);
|
textHandler.setData(fileCaption);
|
||||||
@@ -460,8 +493,13 @@ QString EventHandler::getMessageBody(const NeoChatRoom *room, const RoomMessageE
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString body;
|
QString body;
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
if (event.has<EventContent::TextContent>() && event.content()) {
|
||||||
|
body = event.get<EventContent::TextContent>()->body;
|
||||||
|
#else
|
||||||
if (event.hasTextContent() && event.content()) {
|
if (event.hasTextContent() && event.content()) {
|
||||||
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
body = event.plainBody();
|
body = event.plainBody();
|
||||||
}
|
}
|
||||||
@@ -476,24 +514,24 @@ QString EventHandler::getMessageBody(const NeoChatRoom *room, const RoomMessageE
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (format == Qt::RichText) {
|
if (format == Qt::RichText) {
|
||||||
return textHandler.handleRecieveRichText(inputFormat, room, &event, stripNewlines, event.isReplaced());
|
return textHandler.handleRecieveRichText(inputFormat, m_room, &event, stripNewlines, event.isReplaced());
|
||||||
} else {
|
} else {
|
||||||
return textHandler.handleRecievePlainText(inputFormat, stripNewlines);
|
return textHandler.handleRecievePlainText(inputFormat, stripNewlines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EventHandler::genericBody(const Quotient::RoomEvent *event)
|
QString EventHandler::getGenericBody() const
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "genericBody called with event set to nullptr.";
|
qCWarning(EventHandling) << "getGenericBody called with m_event set to nullptr.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (event->isRedacted()) {
|
if (m_event->isRedacted()) {
|
||||||
return i18n("<i>[This message was deleted]</i>");
|
return i18n("<i>[This message was deleted]</i>");
|
||||||
}
|
}
|
||||||
|
|
||||||
return switchOnType(
|
return switchOnType(
|
||||||
*event,
|
*m_event,
|
||||||
[](const RoomMessageEvent &e) {
|
[](const RoomMessageEvent &e) {
|
||||||
Q_UNUSED(e)
|
Q_UNUSED(e)
|
||||||
return i18n("sent a message");
|
return i18n("sent a message");
|
||||||
@@ -613,76 +651,94 @@ QString EventHandler::genericBody(const Quotient::RoomEvent *event)
|
|||||||
i18n("Unknown event"));
|
i18n("Unknown event"));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EventHandler::subtitleText(const NeoChatRoom *room, const Quotient::RoomEvent *event)
|
QString EventHandler::subtitleText() const
|
||||||
{
|
{
|
||||||
if (room == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "subtitleText called with room set to nullptr.";
|
qCWarning(EventHandling) << "subtitleText called with m_event set to nullptr.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (event == nullptr) {
|
return singleLineAuthorDisplayname() + (m_event->isStateEvent() ? QLatin1String(" ") : QLatin1String(": ")) + getPlainBody(true);
|
||||||
qCWarning(EventHandling) << "subtitleText called with event set to nullptr.";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return singleLineAuthorDisplayname(room, event) + (event->isStateEvent() ? QLatin1String(" ") : QLatin1String(": ")) + plainBody(room, event, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap EventHandler::mediaInfo(const NeoChatRoom *room, const Quotient::RoomEvent *event)
|
QVariantMap EventHandler::getMediaInfo() const
|
||||||
{
|
{
|
||||||
if (room == nullptr) {
|
if (m_room == nullptr) {
|
||||||
qCWarning(EventHandling) << "mediaInfo called with room set to nullptr.";
|
qCWarning(EventHandling) << "getMediaInfo called with m_room set to nullptr.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "mediaInfo called with event set to nullptr.";
|
qCWarning(EventHandling) << "getMediaInfo called with m_event set to nullptr.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return getMediaInfoForEvent(room, event);
|
return getMediaInfoForEvent(m_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap EventHandler::getMediaInfoForEvent(const NeoChatRoom *room, const Quotient::RoomEvent *event)
|
QVariantMap EventHandler::getMediaInfoForEvent(const Quotient::RoomEvent *event) const
|
||||||
{
|
{
|
||||||
QString eventId = event->id();
|
QString eventId = event->id();
|
||||||
|
|
||||||
// Get the file info for the event.
|
// Get the file info for the event.
|
||||||
if (event->is<RoomMessageEvent>()) {
|
if (event->is<RoomMessageEvent>()) {
|
||||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event);
|
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event);
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
if (!roomMessageEvent->has<EventContent::FileContentBase>()) {
|
||||||
|
#else
|
||||||
if (!roomMessageEvent->hasFileContent()) {
|
if (!roomMessageEvent->hasFileContent()) {
|
||||||
|
#endif
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const EventContent::FileInfo *fileInfo;
|
#if Quotient_VERSION_MINOR > 8
|
||||||
fileInfo = roomMessageEvent->content()->fileInfo();
|
const auto content = roomMessageEvent->get<EventContent::FileContentBase>();
|
||||||
QVariantMap mediaInfo = getMediaInfoFromFileInfo(room, fileInfo, eventId, false, false);
|
QVariantMap mediaInfo = getMediaInfoFromFileInfo(content.get(), eventId, false, false);
|
||||||
|
#else
|
||||||
|
const auto content = static_cast<const EventContent::FileContent *>(roomMessageEvent->content());
|
||||||
|
QVariantMap mediaInfo = getMediaInfoFromFileInfo(content, eventId, false, false);
|
||||||
|
#endif
|
||||||
// if filename isn't specifically given, it is in body
|
// if filename isn't specifically given, it is in body
|
||||||
// https://spec.matrix.org/latest/client-server-api/#mfile
|
// https://spec.matrix.org/latest/client-server-api/#mfile
|
||||||
mediaInfo["filename"_ls] = (fileInfo->originalName.isEmpty()) ? roomMessageEvent->plainBody() : fileInfo->originalName;
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
mediaInfo["filename"_ls] = content->commonInfo().originalName.isEmpty() ? roomMessageEvent->plainBody() : content->commonInfo().originalName;
|
||||||
|
#else
|
||||||
|
mediaInfo["filename"_ls] = (content->fileInfo()->originalName.isEmpty()) ? roomMessageEvent->plainBody() : content->fileInfo()->originalName;
|
||||||
|
#endif
|
||||||
|
|
||||||
return mediaInfo;
|
return mediaInfo;
|
||||||
} else if (event->is<StickerEvent>()) {
|
} else if (event->is<StickerEvent>()) {
|
||||||
const EventContent::FileInfo *fileInfo;
|
|
||||||
|
|
||||||
auto stickerEvent = eventCast<const StickerEvent>(event);
|
auto stickerEvent = eventCast<const StickerEvent>(event);
|
||||||
fileInfo = &stickerEvent->image();
|
auto content = &stickerEvent->image();
|
||||||
|
|
||||||
return getMediaInfoFromFileInfo(room, fileInfo, eventId, false, true);
|
return getMediaInfoFromFileInfo(content, eventId, false, true);
|
||||||
} else {
|
} else {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
QVariantMap EventHandler::getMediaInfoFromFileInfo(
|
||||||
const EventContent::FileInfo *fileInfo,
|
#if Quotient_VERSION_MINOR > 8
|
||||||
const QString &eventId,
|
const Quotient::EventContent::FileContentBase *fileContent,
|
||||||
bool isThumbnail,
|
#else
|
||||||
bool isSticker)
|
const Quotient::EventContent::TypedBase *fileContent,
|
||||||
|
#endif
|
||||||
|
const QString &eventId,
|
||||||
|
bool isThumbnail,
|
||||||
|
bool isSticker) const
|
||||||
{
|
{
|
||||||
QVariantMap mediaInfo;
|
QVariantMap mediaInfo;
|
||||||
|
|
||||||
// Get the mxc URL for the media.
|
// Get the mxc URL for the media.
|
||||||
if (!fileInfo->url().isValid() || fileInfo->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
if (!fileContent->url().isValid() || fileContent->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
||||||
|
#else
|
||||||
|
if (!fileContent->fileInfo()->url().isValid() || fileContent->fileInfo()->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
||||||
|
#endif
|
||||||
mediaInfo["source"_ls] = QUrl();
|
mediaInfo["source"_ls] = QUrl();
|
||||||
} else {
|
} else {
|
||||||
QUrl source = room->makeMediaUrl(eventId, fileInfo->url());
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
QUrl source = m_room->makeMediaUrl(eventId, fileContent->url());
|
||||||
|
#else
|
||||||
|
QUrl source = m_room->makeMediaUrl(eventId, fileContent->fileInfo()->url());
|
||||||
|
#endif
|
||||||
|
|
||||||
if (source.isValid()) {
|
if (source.isValid()) {
|
||||||
mediaInfo["source"_ls] = source;
|
mediaInfo["source"_ls] = source;
|
||||||
@@ -691,7 +747,7 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mimeType = fileInfo->mimeType;
|
auto mimeType = fileContent->type();
|
||||||
// Add the MIME type for the media if available.
|
// Add the MIME type for the media if available.
|
||||||
mediaInfo["mimeType"_ls] = mimeType.name();
|
mediaInfo["mimeType"_ls] = mimeType.name();
|
||||||
|
|
||||||
@@ -699,45 +755,53 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
|||||||
mediaInfo["mimeIcon"_ls] = mimeType.iconName();
|
mediaInfo["mimeIcon"_ls] = mimeType.iconName();
|
||||||
|
|
||||||
// Add media size if available.
|
// Add media size if available.
|
||||||
mediaInfo["size"_ls] = fileInfo->payloadSize;
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
mediaInfo["size"_ls] = fileContent->commonInfo().payloadSize;
|
||||||
|
#else
|
||||||
|
mediaInfo["size"_ls] = static_cast<const EventContent::FileContent *>(fileContent)->fileInfo()->payloadSize;
|
||||||
|
#endif
|
||||||
|
|
||||||
mediaInfo["isSticker"_ls] = isSticker;
|
mediaInfo["isSticker"_ls] = isSticker;
|
||||||
|
|
||||||
// Add parameter depending on media type.
|
// Add parameter depending on media type.
|
||||||
if (mimeType.name().contains(QStringLiteral("image"))) {
|
if (mimeType.name().contains(QStringLiteral("image"))) {
|
||||||
if (auto castInfo = static_cast<const EventContent::ImageContent *>(fileInfo)) {
|
if (auto castInfo = static_cast<const EventContent::ImageContent *>(fileContent)) {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
mediaInfo["width"_ls] = castInfo->imageSize.width();
|
mediaInfo["width"_ls] = castInfo->imageSize.width();
|
||||||
mediaInfo["height"_ls] = castInfo->imageSize.height();
|
mediaInfo["height"_ls] = castInfo->imageSize.height();
|
||||||
|
#else
|
||||||
|
const auto imageInfo = static_cast<const EventContent::ImageInfo *>(castInfo->fileInfo());
|
||||||
|
mediaInfo["width"_ls] = imageInfo->imageSize.width();
|
||||||
|
mediaInfo["height"_ls] = imageInfo->imageSize.height();
|
||||||
|
#endif
|
||||||
|
|
||||||
// TODO: Images in certain formats (e.g. WebP) will be erroneously marked as animated, even if they are static.
|
// TODO: Images in certain formats (e.g. WebP) will be erroneously marked as animated, even if they are static.
|
||||||
mediaInfo["animated"_ls] = QMovie::supportedFormats().contains(mimeType.preferredSuffix().toUtf8());
|
mediaInfo["animated"_ls] = QMovie::supportedFormats().contains(mimeType.preferredSuffix().toUtf8());
|
||||||
|
|
||||||
if (!isThumbnail) {
|
QVariantMap tempInfo;
|
||||||
QVariantMap tempInfo;
|
auto thumbnailInfo = getMediaInfoFromTumbnail(castInfo->thumbnail, eventId);
|
||||||
auto thumbnailInfo = getMediaInfoFromFileInfo(room, castInfo->thumbnailInfo(), eventId, true);
|
if (thumbnailInfo["source"_ls].toUrl().scheme() == "mxc"_ls) {
|
||||||
if (thumbnailInfo["source"_ls].toUrl().scheme() == "mxc"_ls) {
|
tempInfo = thumbnailInfo;
|
||||||
tempInfo = thumbnailInfo;
|
} else {
|
||||||
|
QString blurhash = castInfo->originalInfoJson["xyz.amorgan.blurhash"_ls].toString();
|
||||||
|
if (blurhash.isEmpty()) {
|
||||||
|
tempInfo["source"_ls] = QUrl();
|
||||||
} else {
|
} else {
|
||||||
QString blurhash = castInfo->originalInfoJson["xyz.amorgan.blurhash"_ls].toString();
|
tempInfo["source"_ls] = QUrl("image://blurhash/"_ls + blurhash);
|
||||||
if (blurhash.isEmpty()) {
|
|
||||||
tempInfo["source"_ls] = QUrl();
|
|
||||||
} else {
|
|
||||||
tempInfo["source"_ls] = QUrl("image://blurhash/"_ls + blurhash);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mediaInfo["tempInfo"_ls] = tempInfo;
|
|
||||||
}
|
}
|
||||||
|
mediaInfo["tempInfo"_ls] = tempInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mimeType.name().contains(QStringLiteral("video"))) {
|
if (mimeType.name().contains(QStringLiteral("video"))) {
|
||||||
if (auto castInfo = static_cast<const EventContent::VideoContent *>(fileInfo)) {
|
if (auto castInfo = static_cast<const EventContent::VideoContent *>(fileContent)) {
|
||||||
mediaInfo["width"_ls] = castInfo->imageSize.width();
|
mediaInfo["width"_ls] = castInfo->imageSize.width();
|
||||||
mediaInfo["height"_ls] = castInfo->imageSize.height();
|
mediaInfo["height"_ls] = castInfo->imageSize.height();
|
||||||
mediaInfo["duration"_ls] = castInfo->duration;
|
mediaInfo["duration"_ls] = castInfo->duration;
|
||||||
|
|
||||||
if (!isThumbnail) {
|
if (!isThumbnail) {
|
||||||
QVariantMap tempInfo;
|
QVariantMap tempInfo;
|
||||||
auto thumbnailInfo = getMediaInfoFromFileInfo(room, castInfo->thumbnailInfo(), eventId, true);
|
auto thumbnailInfo = getMediaInfoFromTumbnail(castInfo->thumbnail, eventId);
|
||||||
if (thumbnailInfo["source"_ls].toUrl().scheme() == "mxc"_ls) {
|
if (thumbnailInfo["source"_ls].toUrl().scheme() == "mxc"_ls) {
|
||||||
tempInfo = thumbnailInfo;
|
tempInfo = thumbnailInfo;
|
||||||
} else {
|
} else {
|
||||||
@@ -753,7 +817,7 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mimeType.name().contains(QStringLiteral("audio"))) {
|
if (mimeType.name().contains(QStringLiteral("audio"))) {
|
||||||
if (auto castInfo = static_cast<const EventContent::AudioContent *>(fileInfo)) {
|
if (auto castInfo = static_cast<const EventContent::AudioContent *>(fileContent)) {
|
||||||
mediaInfo["duration"_ls] = castInfo->duration;
|
mediaInfo["duration"_ls] = castInfo->duration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -761,89 +825,188 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
|||||||
return mediaInfo;
|
return mediaInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventHandler::hasReply(const Quotient::RoomEvent *event, bool showFallbacks)
|
QVariantMap EventHandler::getMediaInfoFromTumbnail(const Quotient::EventContent::Thumbnail &thumbnail, const QString &eventId) const
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
QVariantMap thumbnailInfo;
|
||||||
qCWarning(EventHandling) << "hasReply called with event set to nullptr.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto relations = event->contentPart<QJsonObject>("m.relates_to"_ls);
|
if (!thumbnail.url().isValid() || thumbnail.url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
||||||
if (!relations.isEmpty()) {
|
thumbnailInfo["source"_ls] = QUrl();
|
||||||
const bool hasReplyRelation = relations.contains("m.in_reply_to"_ls);
|
|
||||||
bool isFallingBack = relations["is_falling_back"_ls].toBool();
|
|
||||||
return hasReplyRelation && (showFallbacks ? true : !isFallingBack);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString EventHandler::replyId(const Quotient::RoomEvent *event)
|
|
||||||
{
|
|
||||||
if (event == nullptr) {
|
|
||||||
qCWarning(EventHandling) << "replyId called with event set to nullptr.";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return event->contentJson()["m.relates_to"_ls].toObject()["m.in_reply_to"_ls].toObject()["event_id"_ls].toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
Quotient::RoomMember EventHandler::replyAuthor(const NeoChatRoom *room, const Quotient::RoomEvent *event)
|
|
||||||
{
|
|
||||||
if (room == nullptr) {
|
|
||||||
qCWarning(EventHandling) << "replyAuthor called with room set to nullptr.";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (event == nullptr) {
|
|
||||||
qCWarning(EventHandling) << "replyAuthor called with event set to nullptr. Returning empty user.";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto replyPtr = room->getReplyForEvent(*event)) {
|
|
||||||
return room->member(replyPtr->senderId());
|
|
||||||
} else {
|
} else {
|
||||||
return room->member(QString());
|
QUrl source = m_room->makeMediaUrl(eventId, thumbnail.url());
|
||||||
|
|
||||||
|
if (source.isValid()) {
|
||||||
|
thumbnailInfo["source"_ls] = source;
|
||||||
|
} else {
|
||||||
|
thumbnailInfo["source"_ls] = QUrl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mimeType = thumbnail.mimeType;
|
||||||
|
// Add the MIME type for the media if available.
|
||||||
|
thumbnailInfo["mimeType"_ls] = mimeType.name();
|
||||||
|
|
||||||
|
// Add the MIME type icon if available.
|
||||||
|
thumbnailInfo["mimeIcon"_ls] = mimeType.iconName();
|
||||||
|
|
||||||
|
// Add media size if available.
|
||||||
|
thumbnailInfo["size"_ls] = thumbnail.payloadSize;
|
||||||
|
|
||||||
|
thumbnailInfo["width"_ls] = thumbnail.imageSize.width();
|
||||||
|
thumbnailInfo["height"_ls] = thumbnail.imageSize.height();
|
||||||
|
|
||||||
|
return thumbnailInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventHandler::hasReply() const
|
||||||
|
{
|
||||||
|
if (m_event == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "hasReply called with m_event set to nullptr.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !m_event->contentJson()["m.relates_to"_ls].toObject()["m.in_reply_to"_ls].toObject()["event_id"_ls].toString().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString EventHandler::getReplyId() const
|
||||||
|
{
|
||||||
|
if (m_event == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "getReplyId called with m_event set to nullptr.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return m_event->contentJson()["m.relates_to"_ls].toObject()["m.in_reply_to"_ls].toObject()["event_id"_ls].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageComponentType::Type EventHandler::replyMessageComponentType() const
|
||||||
|
{
|
||||||
|
if (m_room == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "replyMessageComponentType called with m_room set to nullptr.";
|
||||||
|
return MessageComponentType::Other;
|
||||||
|
}
|
||||||
|
if (m_event == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "replyMessageComponentType called with m_event set to nullptr.";
|
||||||
|
return MessageComponentType::Other;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto replyEvent = m_room->getReplyForEvent(*m_event);
|
||||||
|
if (replyEvent == nullptr) {
|
||||||
|
return MessageComponentType::Other;
|
||||||
|
}
|
||||||
|
return MessageComponentType::typeForEvent(*replyEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Quotient::RoomMember EventHandler::getReplyAuthor() const
|
||||||
|
{
|
||||||
|
if (m_room == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "getReplyAuthor called with m_room set to nullptr.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (m_event == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "getReplyAuthor called with m_event set to nullptr. Returning empty user.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto replyPtr = m_room->getReplyForEvent(*m_event)) {
|
||||||
|
return m_room->member(replyPtr->senderId());
|
||||||
|
} else {
|
||||||
|
return m_room->member(QString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventHandler::isThreaded(const Quotient::RoomEvent *event)
|
QString EventHandler::getReplyRichBody(bool stripNewlines) const
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
if (m_room == nullptr) {
|
||||||
qCWarning(EventHandling) << "isThreaded called with event set to nullptr.";
|
qCWarning(EventHandling) << "getReplyRichBody called with m_room set to nullptr.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (m_event == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "getReplyRichBody called with m_event set to nullptr.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto replyEvent = m_room->getReplyForEvent(*m_event);
|
||||||
|
if (replyEvent == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return getBody(replyEvent, Qt::RichText, stripNewlines);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString EventHandler::getReplyPlainBody(bool stripNewlines) const
|
||||||
|
{
|
||||||
|
if (m_room == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "getReplyPlainBody called with m_room set to nullptr.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (m_event == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "getReplyPlainBody called with m_event set to nullptr.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto replyEvent = m_room->getReplyForEvent(*m_event);
|
||||||
|
if (replyEvent == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return getBody(replyEvent, Qt::PlainText, stripNewlines);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap EventHandler::getReplyMediaInfo() const
|
||||||
|
{
|
||||||
|
if (m_room == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "getReplyMediaInfo called with m_room set to nullptr.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (m_event == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "getReplyMediaInfo called with m_event set to nullptr.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto replyPtr = m_room->getReplyForEvent(*m_event);
|
||||||
|
if (!replyPtr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return getMediaInfoForEvent(replyPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventHandler::isThreaded() const
|
||||||
|
{
|
||||||
|
if (m_event == nullptr) {
|
||||||
|
qCWarning(EventHandling) << "isThreaded called with m_event set to nullptr.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (event->contentPart<QJsonObject>("m.relates_to"_ls).contains("rel_type"_ls)
|
return (m_event->contentPart<QJsonObject>("m.relates_to"_ls).contains("rel_type"_ls)
|
||||||
&& event->contentPart<QJsonObject>("m.relates_to"_ls)["rel_type"_ls].toString() == "m.thread"_ls)
|
&& m_event->contentPart<QJsonObject>("m.relates_to"_ls)["rel_type"_ls].toString() == "m.thread"_ls)
|
||||||
|| (!event->unsignedPart<QJsonObject>("m.relations"_ls).isEmpty() && event->unsignedPart<QJsonObject>("m.relations"_ls).contains("m.thread"_ls));
|
|| (!m_event->unsignedPart<QJsonObject>("m.relations"_ls).isEmpty() && m_event->unsignedPart<QJsonObject>("m.relations"_ls).contains("m.thread"_ls));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EventHandler::threadRoot(const Quotient::RoomEvent *event)
|
QString EventHandler::threadRoot() const
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "threadRoot called with event set to nullptr.";
|
qCWarning(EventHandling) << "threadRoot called with m_event set to nullptr.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the thread root ID from m.relates_to if it exists.
|
// Get the thread root ID from m.relates_to if it exists.
|
||||||
if (event->contentPart<QJsonObject>("m.relates_to"_ls).contains("rel_type"_ls)
|
if (m_event->contentPart<QJsonObject>("m.relates_to"_ls).contains("rel_type"_ls)
|
||||||
&& event->contentPart<QJsonObject>("m.relates_to"_ls)["rel_type"_ls].toString() == "m.thread"_ls) {
|
&& m_event->contentPart<QJsonObject>("m.relates_to"_ls)["rel_type"_ls].toString() == "m.thread"_ls) {
|
||||||
return event->contentPart<QJsonObject>("m.relates_to"_ls)["event_id"_ls].toString();
|
return m_event->contentPart<QJsonObject>("m.relates_to"_ls)["event_id"_ls].toString();
|
||||||
}
|
}
|
||||||
// For thread root events they have an m.relations in the unsigned part with a m.thread object.
|
// For thread root events they have an m.relations in the unsigned part with a m.thread object.
|
||||||
// If so return the event ID as it is the root.
|
// If so return the event ID as it is the root.
|
||||||
if (!event->unsignedPart<QJsonObject>("m.relations"_ls).isEmpty() && event->unsignedPart<QJsonObject>("m.relations"_ls).contains("m.thread"_ls)) {
|
if (!m_event->unsignedPart<QJsonObject>("m.relations"_ls).isEmpty() && m_event->unsignedPart<QJsonObject>("m.relations"_ls).contains("m.thread"_ls)) {
|
||||||
return id(event);
|
return getId();
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
float EventHandler::latitude(const Quotient::RoomEvent *event)
|
float EventHandler::getLatitude() const
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "latitude called with event set to nullptr.";
|
qCWarning(EventHandling) << "getLatitude called with m_event set to nullptr.";
|
||||||
return -100.0;
|
return -100.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto geoUri = event->contentJson()["geo_uri"_ls].toString();
|
const auto geoUri = m_event->contentJson()["geo_uri"_ls].toString();
|
||||||
if (geoUri.isEmpty()) {
|
if (geoUri.isEmpty()) {
|
||||||
return -100.0; // latitude runs from -90deg to +90deg so -100 is out of range.
|
return -100.0; // latitude runs from -90deg to +90deg so -100 is out of range.
|
||||||
}
|
}
|
||||||
@@ -851,14 +1014,14 @@ float EventHandler::latitude(const Quotient::RoomEvent *event)
|
|||||||
return latitude.toFloat();
|
return latitude.toFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
float EventHandler::longitude(const Quotient::RoomEvent *event)
|
float EventHandler::getLongitude() const
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "longitude called with event set to nullptr.";
|
qCWarning(EventHandling) << "getLongitude called with m_event set to nullptr.";
|
||||||
return -200.0;
|
return -200.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto geoUri = event->contentJson()["geo_uri"_ls].toString();
|
const auto geoUri = m_event->contentJson()["geo_uri"_ls].toString();
|
||||||
if (geoUri.isEmpty()) {
|
if (geoUri.isEmpty()) {
|
||||||
return -200.0; // longitude runs from -180deg to +180deg so -200 is out of range.
|
return -200.0; // longitude runs from -180deg to +180deg so -200 is out of range.
|
||||||
}
|
}
|
||||||
@@ -866,14 +1029,14 @@ float EventHandler::longitude(const Quotient::RoomEvent *event)
|
|||||||
return latitude.toFloat();
|
return latitude.toFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EventHandler::locationAssetType(const Quotient::RoomEvent *event)
|
QString EventHandler::getLocationAssetType() const
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "locationAssetType called with event set to nullptr.";
|
qCWarning(EventHandling) << "getLocationAssetType called with m_event set to nullptr.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto assetType = event->contentJson()["org.matrix.msc3488.asset"_ls].toObject()["type"_ls].toString();
|
const auto assetType = m_event->contentJson()["org.matrix.msc3488.asset"_ls].toObject()["type"_ls].toString();
|
||||||
if (assetType.isEmpty()) {
|
if (assetType.isEmpty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,22 +3,24 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QObject>
|
||||||
#include <QString>
|
|
||||||
#include <Quotient/events/eventcontent.h>
|
#include <KFormat>
|
||||||
|
|
||||||
|
#include <Quotient/eventitem.h>
|
||||||
|
#include <Quotient/events/roomevent.h>
|
||||||
|
#include <Quotient/events/roommessageevent.h>
|
||||||
|
|
||||||
|
#include "enums/messagecomponenttype.h"
|
||||||
|
|
||||||
namespace Quotient
|
namespace Quotient
|
||||||
{
|
{
|
||||||
namespace EventContent
|
|
||||||
{
|
|
||||||
class FileInfo;
|
|
||||||
}
|
|
||||||
class RoomEvent;
|
|
||||||
class RoomMember;
|
class RoomMember;
|
||||||
class RoomMessageEvent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LinkPreviewer;
|
||||||
class NeoChatRoom;
|
class NeoChatRoom;
|
||||||
|
class ReactionModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class EventHandler
|
* @class EventHandler
|
||||||
@@ -36,14 +38,20 @@ class NeoChatRoom;
|
|||||||
*/
|
*/
|
||||||
class EventHandler
|
class EventHandler
|
||||||
{
|
{
|
||||||
|
Q_GADGET
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
EventHandler(const NeoChatRoom *room, const Quotient::RoomEvent *event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the ID of the event.
|
* @brief Return the Matrix ID of the event.
|
||||||
*
|
|
||||||
* Returns the transaction ID if the Matrix ID is empty, which may be the case
|
|
||||||
* for a pending event.
|
|
||||||
*/
|
*/
|
||||||
static QString id(const Quotient::RoomEvent *event);
|
QString getId() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The MessageComponentType to use to visualise the main event content.
|
||||||
|
*/
|
||||||
|
MessageComponentType::Type messageComponentType() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the display name of the event author.
|
* @brief Get the display name of the event author.
|
||||||
@@ -56,7 +64,7 @@ public:
|
|||||||
* @param isPending whether the event is pending as this cannot be derived from
|
* @param isPending whether the event is pending as this cannot be derived from
|
||||||
* just the event object.
|
* just the event object.
|
||||||
*/
|
*/
|
||||||
static QString authorDisplayName(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool isPending = false);
|
QString getAuthorDisplayName(bool isPending = false) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the display name of the event author but with any newlines removed.
|
* @brief Get the display name of the event author but with any newlines removed.
|
||||||
@@ -67,12 +75,12 @@ public:
|
|||||||
* @param isPending whether the event is pending as this cannot be derived from
|
* @param isPending whether the event is pending as this cannot be derived from
|
||||||
* just the event object.
|
* just the event object.
|
||||||
*/
|
*/
|
||||||
static QString singleLineAuthorDisplayname(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool isPending = false);
|
QString singleLineAuthorDisplayname(bool isPending = false) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return a QDateTime object for the event timestamp.
|
* @brief Return a QDateTime object for the event timestamp.
|
||||||
*/
|
*/
|
||||||
static QDateTime time(const Quotient::RoomEvent *event, bool isPending = false, QDateTime lastUpdated = {});
|
QDateTime getTime(bool isPending = false, QDateTime lastUpdated = {}) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return a QString for the event timestamp.
|
* @brief Return a QString for the event timestamp.
|
||||||
@@ -88,32 +96,16 @@ public:
|
|||||||
* @param lastUpdated the time the event was last updated locally as this cannot be
|
* @param lastUpdated the time the event was last updated locally as this cannot be
|
||||||
* obtained from the event.
|
* obtained from the event.
|
||||||
*/
|
*/
|
||||||
static QString timeString(const Quotient::RoomEvent *event,
|
QString getTimeString(bool relative, QLocale::FormatType format = QLocale::ShortFormat, bool isPending = false, QDateTime lastUpdated = {}) const;
|
||||||
bool relative,
|
|
||||||
QLocale::FormatType format = QLocale::ShortFormat,
|
|
||||||
bool isPending = false,
|
|
||||||
QDateTime lastUpdated = {});
|
|
||||||
|
|
||||||
/**
|
QString getTimeString(const QString &format, bool isPending = false, const QDateTime &lastUpdated = {});
|
||||||
* @brief Return a QString for the event timestamp.
|
|
||||||
*
|
|
||||||
* This is intended to return a string that is read for display in the UI without
|
|
||||||
* any further manipulation required.
|
|
||||||
*
|
|
||||||
* @param format the format to use as a string.
|
|
||||||
* @param isPending whether the event is pending as this cannot be derived from
|
|
||||||
* just the event object.
|
|
||||||
* @param lastUpdated the time the event was last updated locally as this cannot be
|
|
||||||
* obtained from the event.
|
|
||||||
*/
|
|
||||||
static QString timeString(const Quotient::RoomEvent *event, const QString &format, bool isPending = false, const QDateTime &lastUpdated = {});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether the event should be highlighted in the timeline.
|
* @brief Whether the event should be highlighted in the timeline.
|
||||||
*
|
*
|
||||||
* @note Messages in direct chats are never highlighted.
|
* @note Messages in direct chats are never highlighted.
|
||||||
*/
|
*/
|
||||||
static bool isHighlighted(const NeoChatRoom *room, const Quotient::RoomEvent *event);
|
bool isHighlighted();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether the event should be hidden in the timeline.
|
* @brief Whether the event should be hidden in the timeline.
|
||||||
@@ -122,7 +114,7 @@ public:
|
|||||||
* user has hidden all state events or if the sender has been ignored by the local
|
* user has hidden all state events or if the sender has been ignored by the local
|
||||||
* user.
|
* user.
|
||||||
*/
|
*/
|
||||||
static bool isHidden(const NeoChatRoom *room, const Quotient::RoomEvent *event);
|
bool isHidden();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The input format of the body in the message.
|
* @brief The input format of the body in the message.
|
||||||
@@ -154,7 +146,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @param stripNewlines whether the output should have new lines in it.
|
* @param stripNewlines whether the output should have new lines in it.
|
||||||
*/
|
*/
|
||||||
static QString richBody(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool stripNewlines = false);
|
QString getRichBody(bool stripNewlines = false) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Output a string for the message content ready for display in a plain text field.
|
* @brief Output a string for the message content ready for display in a plain text field.
|
||||||
@@ -170,14 +162,14 @@ public:
|
|||||||
*
|
*
|
||||||
* @param stripNewlines whether the output should have new lines in it.
|
* @param stripNewlines whether the output should have new lines in it.
|
||||||
*/
|
*/
|
||||||
static QString plainBody(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool stripNewlines = false);
|
QString getPlainBody(bool stripNewlines = false) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Output the original body for the message content, useful for editing the original message.
|
* @brief Output the original body for the message content, useful for editing the original message.
|
||||||
*
|
*
|
||||||
* The event type must be a room message event.
|
* The event type must be a room message event.
|
||||||
*/
|
*/
|
||||||
static QString markdownBody(const Quotient::RoomEvent *event);
|
QString getMarkdownBody() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Output a generic string for the message content ready for display.
|
* @brief Output a generic string for the message content ready for display.
|
||||||
@@ -190,9 +182,9 @@ public:
|
|||||||
* E.g. For a message the text will be:
|
* E.g. For a message the text will be:
|
||||||
* "sent a message"
|
* "sent a message"
|
||||||
*
|
*
|
||||||
* @sa richBody(), plainBody()
|
* @sa getRichBody(), getPlainBody()
|
||||||
*/
|
*/
|
||||||
static QString genericBody(const Quotient::RoomEvent *event);
|
QString getGenericBody() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Output a string for the event to be used as a RoomList subtitle.
|
* @brief Output a string for the event to be used as a RoomList subtitle.
|
||||||
@@ -200,7 +192,7 @@ public:
|
|||||||
* The output includes the username followed by the plain message, all with no
|
* The output includes the username followed by the plain message, all with no
|
||||||
* line breaks.
|
* line breaks.
|
||||||
*/
|
*/
|
||||||
static QString subtitleText(const NeoChatRoom *room, const Quotient::RoomEvent *event);
|
QString subtitleText() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the media info for the event.
|
* @brief Return the media info for the event.
|
||||||
@@ -218,21 +210,22 @@ public:
|
|||||||
* - tempInfo - mediaInfo (with the same properties as this except no tempInfo) for a temporary image while the file downloads.
|
* - tempInfo - mediaInfo (with the same properties as this except no tempInfo) for a temporary image while the file downloads.
|
||||||
* - isSticker - Whether the image is a sticker or not
|
* - isSticker - Whether the image is a sticker or not
|
||||||
*/
|
*/
|
||||||
static QVariantMap mediaInfo(const NeoChatRoom *room, const Quotient::RoomEvent *event);
|
QVariantMap getMediaInfo() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether the event is a reply to another in the timeline.
|
* @brief Whether the event is a reply to another in the timeline.
|
||||||
*
|
|
||||||
* @param showFallbacks whether message that have is_falling_back set true should
|
|
||||||
* show the fallback reply. Leave true for non-threaded
|
|
||||||
* timelines.
|
|
||||||
*/
|
*/
|
||||||
static bool hasReply(const Quotient::RoomEvent *event, bool showFallbacks = true);
|
bool hasReply() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the Matrix ID of the event replied to.
|
* @brief Return the Matrix ID of the event replied to.
|
||||||
*/
|
*/
|
||||||
static QString replyId(const Quotient::RoomEvent *event);
|
QString getReplyId() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The MessageComponentType to use to visualise the reply content.
|
||||||
|
*/
|
||||||
|
MessageComponentType::Type replyMessageComponentType() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the author of the event replied to in context of the room.
|
* @brief Get the author of the event replied to in context of the room.
|
||||||
@@ -247,21 +240,73 @@ public:
|
|||||||
*
|
*
|
||||||
* @sa Quotient::RoomMember
|
* @sa Quotient::RoomMember
|
||||||
*/
|
*/
|
||||||
static Quotient::RoomMember replyAuthor(const NeoChatRoom *room, const Quotient::RoomEvent *event);
|
Quotient::RoomMember getReplyAuthor() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Output a string for the message content of the event replied to ready
|
||||||
|
* for display in a rich text field.
|
||||||
|
*
|
||||||
|
* The output string is dependant upon the event type and the desired output format.
|
||||||
|
*
|
||||||
|
* For most messages this is the body content of the message. For media messages
|
||||||
|
* this will be the caption and for state events it will be a string specific
|
||||||
|
* to that event with some dynamic details about the event added.
|
||||||
|
*
|
||||||
|
* E.g. For a room topic state event the text will be:
|
||||||
|
* "set the topic to: <new topic text>"
|
||||||
|
*
|
||||||
|
* @param stripNewlines whether the output should have new lines in it.
|
||||||
|
*/
|
||||||
|
QString getReplyRichBody(bool stripNewlines = false) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Output a string for the message content of the event replied to ready
|
||||||
|
* for display in a plain text field.
|
||||||
|
*
|
||||||
|
* The output string is dependant upon the event type and the desired output format.
|
||||||
|
*
|
||||||
|
* For most messages this is the body content of the message. For media messages
|
||||||
|
* this will be the caption and for state events it will be a string specific
|
||||||
|
* to that event with some dynamic details about the event added.
|
||||||
|
*
|
||||||
|
* E.g. For a room topic state event the text will be:
|
||||||
|
* "set the topic to: <new topic text>"
|
||||||
|
*
|
||||||
|
* @param stripNewlines whether the output should have new lines in it.
|
||||||
|
*/
|
||||||
|
QString getReplyPlainBody(bool stripNewlines = false) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the media info for the event replied to.
|
||||||
|
*
|
||||||
|
* An empty QVariantMap will be returned for any event that doesn't have any
|
||||||
|
* media info.
|
||||||
|
*
|
||||||
|
* @return This should consist of the following:
|
||||||
|
* - source - The mxc URL for the media.
|
||||||
|
* - mimeType - The MIME type of the media (should be image/xxx for this delegate).
|
||||||
|
* - mimeIcon - The MIME icon name (should be image-xxx).
|
||||||
|
* - size - The file size in bytes.
|
||||||
|
* - width - The width in pixels of the audio media.
|
||||||
|
* - height - The height in pixels of the audio media.
|
||||||
|
* - tempInfo - mediaInfo (with the same properties as this except no tempInfo) for a temporary image while the file downloads.
|
||||||
|
* - isSticker - Whether the image is a sticker or not
|
||||||
|
*/
|
||||||
|
QVariantMap getReplyMediaInfo() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether the message is part of a thread.
|
* @brief Whether the message is part of a thread.
|
||||||
*
|
*
|
||||||
* i.e. There is a rel_type of m.thread.
|
* i.e. There is a rel_type of m.thread.
|
||||||
*/
|
*/
|
||||||
static bool isThreaded(const Quotient::RoomEvent *event);
|
bool isThreaded() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the Matrix ID of the thread's root message.
|
* @brief Return the Matrix ID of the thread's root message.
|
||||||
*
|
*
|
||||||
* Empty if this not part of a thread.
|
* Empty if this not part of a thread.
|
||||||
*/
|
*/
|
||||||
static QString threadRoot(const Quotient::RoomEvent *event);
|
QString threadRoot() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the latitude for the event.
|
* @brief Return the latitude for the event.
|
||||||
@@ -269,7 +314,7 @@ public:
|
|||||||
* Returns -100.0 if the event doesn't have a location (latitudes are in the
|
* Returns -100.0 if the event doesn't have a location (latitudes are in the
|
||||||
* range -90deg to +90deg so -100 is out of range).
|
* range -90deg to +90deg so -100 is out of range).
|
||||||
*/
|
*/
|
||||||
static float latitude(const Quotient::RoomEvent *event);
|
float getLatitude() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the longitude for the event.
|
* @brief Return the longitude for the event.
|
||||||
@@ -277,21 +322,31 @@ public:
|
|||||||
* Returns -200.0 if the event doesn't have a location (latitudes are in the
|
* Returns -200.0 if the event doesn't have a location (latitudes are in the
|
||||||
* range -180deg to +180deg so -200 is out of range).
|
* range -180deg to +180deg so -200 is out of range).
|
||||||
*/
|
*/
|
||||||
static float longitude(const Quotient::RoomEvent *event);
|
float getLongitude() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the type of location marker for the event.
|
* @brief Return the type of location marker for the event.
|
||||||
*/
|
*/
|
||||||
static QString locationAssetType(const Quotient::RoomEvent *event);
|
QString getLocationAssetType() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static QString getBody(const NeoChatRoom *room, const Quotient::RoomEvent *event, Qt::TextFormat format, bool stripNewlines);
|
const NeoChatRoom *m_room = nullptr;
|
||||||
static QString getMessageBody(const NeoChatRoom *room, const Quotient::RoomMessageEvent &event, Qt::TextFormat format, bool stripNewlines);
|
const Quotient::RoomEvent *m_event = nullptr;
|
||||||
|
|
||||||
static QVariantMap getMediaInfoForEvent(const NeoChatRoom *room, const Quotient::RoomEvent *event);
|
KFormat m_format;
|
||||||
QVariantMap static getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
|
||||||
const Quotient::EventContent::FileInfo *fileInfo,
|
QString getBody(const Quotient::RoomEvent *event, Qt::TextFormat format, bool stripNewlines) const;
|
||||||
const QString &eventId,
|
QString getMessageBody(const Quotient::RoomMessageEvent &event, Qt::TextFormat format, bool stripNewlines) const;
|
||||||
bool isThumbnail = false,
|
|
||||||
bool isSticker = false);
|
QVariantMap getMediaInfoForEvent(const Quotient::RoomEvent *event) const;
|
||||||
|
QVariantMap getMediaInfoFromFileInfo(
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
const Quotient::EventContent::FileContentBase *fileContent,
|
||||||
|
#else
|
||||||
|
const Quotient::EventContent::TypedBase *fileContent,
|
||||||
|
#endif
|
||||||
|
const QString &eventId,
|
||||||
|
bool isThumbnail = false,
|
||||||
|
bool isSticker = false) const;
|
||||||
|
QVariantMap getMediaInfoFromTumbnail(const Quotient::EventContent::Thumbnail &thumbnail, const QString &eventId) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,13 +10,22 @@
|
|||||||
#include <Quotient/keyverificationsession.h>
|
#include <Quotient/keyverificationsession.h>
|
||||||
#include <Quotient/roommember.h>
|
#include <Quotient/roommember.h>
|
||||||
|
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
#include <Quotient/keyimport.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
|
|
||||||
|
struct ForeignConfig {
|
||||||
|
Q_GADGET
|
||||||
|
QML_FOREIGN(NeoChatConfig)
|
||||||
|
QML_NAMED_ELEMENT(Config)
|
||||||
|
QML_SINGLETON
|
||||||
|
public:
|
||||||
|
static NeoChatConfig *create(QQmlEngine *, QJSEngine *)
|
||||||
|
{
|
||||||
|
QQmlEngine::setObjectOwnership(NeoChatConfig::self(), QQmlEngine::CppOwnership);
|
||||||
|
return NeoChatConfig::self();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct ForeignAccountRegistry {
|
struct ForeignAccountRegistry {
|
||||||
Q_GADGET
|
Q_GADGET
|
||||||
QML_FOREIGN(Quotient::AccountRegistry)
|
QML_FOREIGN(Quotient::AccountRegistry)
|
||||||
@@ -42,12 +51,3 @@ struct ForeignSSSSHandler {
|
|||||||
QML_FOREIGN(Quotient::SSSSHandler)
|
QML_FOREIGN(Quotient::SSSSHandler)
|
||||||
QML_NAMED_ELEMENT(SSSSHandler)
|
QML_NAMED_ELEMENT(SSSSHandler)
|
||||||
};
|
};
|
||||||
|
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
struct ForeignKeyImport {
|
|
||||||
Q_GADGET
|
|
||||||
QML_SINGLETON
|
|
||||||
QML_FOREIGN(Quotient::KeyImport)
|
|
||||||
QML_NAMED_ELEMENT(KeyImport)
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import org.kde.kirigamiaddons.formcard as FormCard
|
|||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
import org.kde.neochat.settings
|
import org.kde.neochat.settings
|
||||||
|
|
||||||
Kirigami.Page {
|
FormCard.FormCardPage {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property bool showExisting: false
|
property bool showExisting: false
|
||||||
@@ -23,237 +23,202 @@ Kirigami.Page {
|
|||||||
signal connectionChosen
|
signal connectionChosen
|
||||||
|
|
||||||
title: i18n("Welcome")
|
title: i18n("Welcome")
|
||||||
globalToolBarStyle: Kirigami.ApplicationHeaderStyle.None
|
|
||||||
|
|
||||||
header: QQC2.Control {
|
header: QQC2.Control {
|
||||||
topPadding: 0
|
contentItem: Kirigami.InlineMessage {
|
||||||
bottomPadding: 0
|
id: headerMessage
|
||||||
leftPadding: 0
|
type: Kirigami.MessageType.Error
|
||||||
rightPadding: 0
|
showCloseButton: true
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
Kirigami.Icon {
|
||||||
spacing: 0
|
source: "org.kde.neochat"
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
implicitWidth: Math.round(Kirigami.Units.iconSizes.huge * 1.5)
|
||||||
|
implicitHeight: Math.round(Kirigami.Units.iconSizes.huge * 1.5)
|
||||||
|
}
|
||||||
|
|
||||||
Kirigami.Separator {
|
Kirigami.Heading {
|
||||||
Layout.fillWidth: true
|
id: welcomeMessage
|
||||||
|
|
||||||
|
text: i18n("Welcome to NeoChat")
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormHeader {
|
||||||
|
id: existingAccountsHeader
|
||||||
|
title: i18nc("@title", "Continue with an existing account")
|
||||||
|
visible: (loadedAccounts.count > 0 || loadingAccounts.count > 0) && root._showExisting
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormCard {
|
||||||
|
visible: existingAccountsHeader.visible
|
||||||
|
Repeater {
|
||||||
|
id: loadedAccounts
|
||||||
|
model: AccountRegistry
|
||||||
|
delegate: FormCard.FormButtonDelegate {
|
||||||
|
text: model.userId
|
||||||
|
onClicked: {
|
||||||
|
Controller.activeConnection = model.connection;
|
||||||
|
root.connectionChosen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Repeater {
|
||||||
|
id: loadingAccounts
|
||||||
|
model: Controller.accountsLoading
|
||||||
|
delegate: FormCard.AbstractFormDelegate {
|
||||||
|
id: loadingDelegate
|
||||||
|
|
||||||
Kirigami.InlineMessage {
|
topPadding: Kirigami.Units.smallSpacing
|
||||||
id: headerMessage
|
bottomPadding: Kirigami.Units.smallSpacing
|
||||||
type: Kirigami.MessageType.Error
|
|
||||||
showCloseButton: true
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
background: null
|
||||||
Layout.margins: Kirigami.Units.largeSpacing
|
contentItem: RowLayout {
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
QQC2.Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: i18nc("As in 'this account is still loading'", "%1 (loading)", modelData)
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
maximumLineCount: 2
|
||||||
|
color: Kirigami.Theme.disabledTextColor
|
||||||
|
Accessible.ignored: true // base class sets this text on root already
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.ToolButton {
|
||||||
|
text: i18nc("@action:button", "Log out of this account")
|
||||||
|
icon.name: "edit-delete-remove"
|
||||||
|
onClicked: Controller.removeConnection(modelData)
|
||||||
|
display: QQC2.Button.IconOnly
|
||||||
|
QQC2.ToolTip.text: text
|
||||||
|
QQC2.ToolTip.visible: hovered
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
enabled: true
|
||||||
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormArrow {
|
||||||
|
Layout.leftMargin: Kirigami.Units.smallSpacing
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
direction: Qt.RightArrow
|
||||||
|
visible: root.background.visible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCountChanged: {
|
||||||
|
if (loadingAccounts.count === 0 && loadedAccounts.count === 1 && showExisting) {
|
||||||
|
Controller.activeConnection = AccountRegistry.data(AccountRegistry.index(0, 0), 257);
|
||||||
|
root.connectionChosen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
FormCard.FormHeader {
|
||||||
ColumnLayout {
|
title: i18nc("@title", "Log in or Create a New Account")
|
||||||
anchors {
|
}
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
spacing: 0
|
FormCard.FormCard {
|
||||||
|
Loader {
|
||||||
|
id: module
|
||||||
|
Layout.fillWidth: true
|
||||||
|
sourceComponent: Qt.createComponent('org.kde.neochat.login', root.initialStep)
|
||||||
|
|
||||||
Kirigami.Icon {
|
Connections {
|
||||||
source: "org.kde.neochat"
|
id: stepConnections
|
||||||
Layout.alignment: Qt.AlignHCenter
|
target: currentStep
|
||||||
implicitWidth: Math.round(Kirigami.Units.iconSizes.huge * 1.5)
|
|
||||||
implicitHeight: Math.round(Kirigami.Units.iconSizes.huge * 1.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
Kirigami.Heading {
|
function onProcessed(nextStep: string): void {
|
||||||
id: welcomeMessage
|
module.source = nextStep + ".qml";
|
||||||
|
root.currentStepString = nextStep;
|
||||||
text: i18n("NeoChat")
|
headerMessage.text = "";
|
||||||
|
headerMessage.visible = false;
|
||||||
Layout.alignment: Qt.AlignHCenter
|
if (!module.item.noControls) {
|
||||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
module.item.forceActiveFocus();
|
||||||
}
|
} else {
|
||||||
|
continueButton.forceActiveFocus();
|
||||||
FormCard.FormHeader {
|
|
||||||
id: existingAccountsHeader
|
|
||||||
title: i18nc("@title", "Continue with an existing account")
|
|
||||||
visible: (loadedAccounts.count > 0 || loadingAccounts.count > 0) && root._showExisting
|
|
||||||
maximumWidth: Kirigami.Units.gridUnit * 20
|
|
||||||
}
|
|
||||||
|
|
||||||
FormCard.FormCard {
|
|
||||||
visible: existingAccountsHeader.visible
|
|
||||||
maximumWidth: Kirigami.Units.gridUnit * 20
|
|
||||||
Repeater {
|
|
||||||
id: loadedAccounts
|
|
||||||
model: AccountRegistry
|
|
||||||
delegate: FormCard.FormButtonDelegate {
|
|
||||||
text: model.userId
|
|
||||||
onClicked: {
|
|
||||||
Controller.activeConnection = model.connection;
|
|
||||||
root.connectionChosen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Repeater {
|
|
||||||
id: loadingAccounts
|
|
||||||
model: Controller.accountsLoading
|
|
||||||
delegate: FormCard.AbstractFormDelegate {
|
|
||||||
id: loadingDelegate
|
|
||||||
|
|
||||||
topPadding: Kirigami.Units.smallSpacing
|
|
||||||
bottomPadding: Kirigami.Units.smallSpacing
|
|
||||||
|
|
||||||
background: null
|
|
||||||
contentItem: RowLayout {
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
QQC2.Label {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: i18nc("As in 'this account is still loading'", "%1 (loading)", modelData)
|
|
||||||
elide: Text.ElideRight
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
maximumLineCount: 2
|
|
||||||
color: Kirigami.Theme.disabledTextColor
|
|
||||||
Accessible.ignored: true // base class sets this text on root already
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.ToolButton {
|
|
||||||
text: i18nc("@action:button", "Log out of this account")
|
|
||||||
icon.name: "im-kick-user"
|
|
||||||
onClicked: Controller.removeConnection(modelData)
|
|
||||||
display: QQC2.Button.IconOnly
|
|
||||||
QQC2.ToolTip.text: text
|
|
||||||
QQC2.ToolTip.visible: hovered
|
|
||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
||||||
enabled: true
|
|
||||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
|
||||||
}
|
|
||||||
|
|
||||||
FormCard.FormArrow {
|
|
||||||
Layout.leftMargin: Kirigami.Units.smallSpacing
|
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
|
||||||
direction: Qt.RightArrow
|
|
||||||
visible: root.background.visible
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onCountChanged: {
|
|
||||||
if (loadingAccounts.count === 0 && loadedAccounts.count === 1 && showExisting) {
|
|
||||||
Controller.activeConnection = AccountRegistry.data(AccountRegistry.index(0, 0), 257);
|
|
||||||
root.connectionChosen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FormCard.FormHeader {
|
|
||||||
title: i18nc("@title", "Log in or Create a New Account")
|
|
||||||
maximumWidth: Kirigami.Units.gridUnit * 20
|
|
||||||
}
|
|
||||||
|
|
||||||
FormCard.FormCard {
|
|
||||||
maximumWidth: Kirigami.Units.gridUnit * 20
|
|
||||||
Loader {
|
|
||||||
id: module
|
|
||||||
Layout.fillWidth: true
|
|
||||||
sourceComponent: Qt.createComponent('org.kde.neochat.login', root.initialStep)
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
id: stepConnections
|
|
||||||
target: currentStep
|
|
||||||
|
|
||||||
function onProcessed(nextStep: string): void {
|
|
||||||
module.source = nextStep + ".qml";
|
|
||||||
root.currentStepString = nextStep;
|
|
||||||
headerMessage.text = "";
|
|
||||||
headerMessage.visible = false;
|
|
||||||
if (!module.item.noControls) {
|
|
||||||
module.item.forceActiveFocus();
|
|
||||||
} else {
|
|
||||||
continueButton.forceActiveFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onShowMessage(message: string): void {
|
|
||||||
headerMessage.text = message;
|
|
||||||
headerMessage.visible = true;
|
|
||||||
headerMessage.type = Kirigami.MessageType.Information;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onClearError(): void {
|
|
||||||
headerMessage.text = "";
|
|
||||||
headerMessage.visible = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onCloseDialog(): void {
|
|
||||||
root.closeDialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: Registration
|
|
||||||
|
|
||||||
function onNextStepChanged() {
|
|
||||||
if (Registration.nextStep === "m.login.recaptcha") {
|
|
||||||
stepConnections.onProcessed("Captcha");
|
|
||||||
}
|
|
||||||
if (Registration.nextStep === "m.login.terms") {
|
|
||||||
stepConnections.onProcessed("Terms");
|
|
||||||
}
|
|
||||||
if (Registration.nextStep === "m.login.email.identity") {
|
|
||||||
stepConnections.onProcessed("Email");
|
|
||||||
}
|
|
||||||
if (Registration.nextStep === "loading") {
|
|
||||||
stepConnections.onProcessed("Loading");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Connections {
|
|
||||||
target: LoginHelper
|
|
||||||
|
|
||||||
function onErrorOccured(message) {
|
|
||||||
headerMessage.text = message;
|
|
||||||
headerMessage.visible = message.length > 0;
|
|
||||||
headerMessage.type = Kirigami.MessageType.Error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormDelegateSeparator {
|
function onShowMessage(message: string): void {
|
||||||
below: continueButton
|
headerMessage.text = message;
|
||||||
visible: root.currentStep.nextAction
|
headerMessage.visible = true;
|
||||||
|
headerMessage.type = Kirigami.MessageType.Information;
|
||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormButtonDelegate {
|
function onClearError(): void {
|
||||||
id: continueButton
|
headerMessage.text = "";
|
||||||
text: root.currentStep.nextAction && root.currentStep.nextAction.text ? root.currentStep.nextAction.text : i18nc("@action:button", "Continue")
|
headerMessage.visible = false;
|
||||||
visible: root.currentStep.nextAction
|
|
||||||
onClicked: root.currentStep.nextAction.trigger()
|
|
||||||
icon.name: "arrow-right"
|
|
||||||
enabled: root.currentStep.nextAction ? root.currentStep.nextAction.enabled : false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormButtonDelegate {
|
function onCloseDialog(): void {
|
||||||
text: i18nc("@action:button", "Go back")
|
root.closeDialog();
|
||||||
visible: root.currentStep.previousAction
|
|
||||||
onClicked: root.currentStep.previousAction.trigger()
|
|
||||||
icon.name: "arrow-left"
|
|
||||||
enabled: root.currentStep.previousAction ? root.currentStep.previousAction.enabled : false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormCard {
|
Connections {
|
||||||
Layout.topMargin: Kirigami.Units.largeSpacing * 2
|
target: Registration
|
||||||
maximumWidth: Kirigami.Units.gridUnit * 20
|
function onNextStepChanged() {
|
||||||
FormCard.FormButtonDelegate {
|
if (Registration.nextStep === "m.login.recaptcha") {
|
||||||
text: i18nc("@action:button", "Settings")
|
stepConnections.onProcessed("Captcha");
|
||||||
icon.name: "settings-configure"
|
}
|
||||||
onClicked: NeoChatSettingsView.open()
|
if (Registration.nextStep === "m.login.terms") {
|
||||||
|
stepConnections.onProcessed("Terms");
|
||||||
|
}
|
||||||
|
if (Registration.nextStep === "m.login.email.identity") {
|
||||||
|
stepConnections.onProcessed("Email");
|
||||||
|
}
|
||||||
|
if (Registration.nextStep === "loading") {
|
||||||
|
stepConnections.onProcessed("Loading");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Connections {
|
||||||
|
target: LoginHelper
|
||||||
|
function onErrorOccured(message) {
|
||||||
|
headerMessage.text = message;
|
||||||
|
headerMessage.visible = message.length > 0;
|
||||||
|
headerMessage.type = Kirigami.MessageType.Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormDelegateSeparator {
|
||||||
|
below: continueButton
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormButtonDelegate {
|
||||||
|
id: continueButton
|
||||||
|
text: root.currentStep.nextAction && root.currentStep.nextAction.text ? root.currentStep.nextAction.text : i18nc("@action:button", "Continue")
|
||||||
|
visible: root.currentStep.nextAction
|
||||||
|
onClicked: root.currentStep.nextAction.trigger()
|
||||||
|
icon.name: "arrow-right"
|
||||||
|
enabled: root.currentStep.nextAction ? root.currentStep.nextAction.enabled : false
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormButtonDelegate {
|
||||||
|
text: i18nc("@action:button", "Go back")
|
||||||
|
visible: root.currentStep.previousAction
|
||||||
|
onClicked: root.currentStep.previousAction.trigger()
|
||||||
|
icon.name: "arrow-left"
|
||||||
|
enabled: root.currentStep.previousAction ? root.currentStep.previousAction.enabled : false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormCard {
|
||||||
|
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||||
|
FormCard.FormButtonDelegate {
|
||||||
|
text: i18nc("@action:button", "Open proxy settings")
|
||||||
|
icon.name: "settings-configure"
|
||||||
|
onClicked: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat.settings", "NetworkProxyPage"), {}, {
|
||||||
|
title: i18nc("@title:window", "Proxy Settings")
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ int main(int argc, char *argv[])
|
|||||||
i18n("Maintainer"),
|
i18n("Maintainer"),
|
||||||
QStringLiteral("carl@carlschwan.eu"),
|
QStringLiteral("carl@carlschwan.eu"),
|
||||||
QStringLiteral("https://carlschwan.eu"),
|
QStringLiteral("https://carlschwan.eu"),
|
||||||
QUrl(QStringLiteral("https://carlschwan.eu/avatar.png")));
|
QStringLiteral("https://carlschwan.eu/avatar.png"));
|
||||||
about.addAuthor(i18n("Tobias Fella"), i18n("Maintainer"), QStringLiteral("tobias.fella@kde.org"), QStringLiteral("https://tobiasfella.de"));
|
about.addAuthor(i18n("Tobias Fella"), i18n("Maintainer"), QStringLiteral("tobias.fella@kde.org"), QStringLiteral("https://tobiasfella.de"));
|
||||||
about.addAuthor(i18n("James Graham"), i18n("Maintainer"), QStringLiteral("james.h.graham@protonmail.com"));
|
about.addAuthor(i18n("James Graham"), i18n("Maintainer"), QStringLiteral("james.h.graham@protonmail.com"));
|
||||||
about.addCredit(i18n("Black Hat"), i18n("Original author of Spectral"), QStringLiteral("bhat@encom.eu.org"));
|
about.addCredit(i18n("Black Hat"), i18n("Original author of Spectral"), QStringLiteral("bhat@encom.eu.org"));
|
||||||
@@ -185,6 +185,9 @@ int main(int argc, char *argv[])
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
ColorSchemer colorScheme;
|
ColorSchemer colorScheme;
|
||||||
|
if (!NeoChatConfig::self()->colorScheme().isEmpty()) {
|
||||||
|
colorScheme.apply(NeoChatConfig::self()->colorScheme());
|
||||||
|
}
|
||||||
|
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.setApplicationDescription(i18n("Client for the matrix communication protocol"));
|
parser.setApplicationDescription(i18n("Client for the matrix communication protocol"));
|
||||||
@@ -304,6 +307,7 @@ int main(int argc, char *argv[])
|
|||||||
QWindow *window = windowFromEngine(&engine);
|
QWindow *window = windowFromEngine(&engine);
|
||||||
|
|
||||||
WindowController::instance().setWindow(window);
|
WindowController::instance().setWindow(window);
|
||||||
|
WindowController::instance().restoreGeometry();
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|||||||
124
src/matriximageprovider.cpp
Normal file
124
src/matriximageprovider.cpp
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
|
||||||
|
// SPDX-FileCopyrightText: 2019 Kitsune Ral <kitsune-ral@users.sf.net>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
#include "matriximageprovider.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
#include <KLocalizedString>
|
||||||
|
|
||||||
|
#include "neochatconnection.h"
|
||||||
|
|
||||||
|
using namespace Quotient;
|
||||||
|
|
||||||
|
ThumbnailResponse::ThumbnailResponse(QString id, QSize size, NeoChatConnection *connection)
|
||||||
|
: mediaId(std::move(id))
|
||||||
|
, requestedSize(size)
|
||||||
|
, localFile(QStringLiteral("%1/image_provider/%2-%3x%4.png")
|
||||||
|
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation),
|
||||||
|
mediaId,
|
||||||
|
QString::number(requestedSize.width()),
|
||||||
|
QString::number(requestedSize.height())))
|
||||||
|
, m_connection(connection)
|
||||||
|
, errorStr("Image request hasn't started"_ls)
|
||||||
|
{
|
||||||
|
if (requestedSize.isEmpty()) {
|
||||||
|
requestedSize.setHeight(100);
|
||||||
|
requestedSize.setWidth(100);
|
||||||
|
}
|
||||||
|
if (mediaId.count(QLatin1Char('/')) != 1) {
|
||||||
|
if (mediaId.startsWith(QLatin1Char('/'))) {
|
||||||
|
mediaId = mediaId.mid(1);
|
||||||
|
} else {
|
||||||
|
errorStr = i18n("Media id '%1' doesn't follow server/mediaId pattern", mediaId);
|
||||||
|
Q_EMIT finished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaId = mediaId.split(QLatin1Char('?'))[0];
|
||||||
|
|
||||||
|
QImage cachedImage;
|
||||||
|
if (cachedImage.load(localFile)) {
|
||||||
|
image = cachedImage;
|
||||||
|
errorStr.clear();
|
||||||
|
Q_EMIT finished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_connection) {
|
||||||
|
qWarning() << "Current connection is null";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute a request on the main thread asynchronously
|
||||||
|
moveToThread(m_connection->thread());
|
||||||
|
QMetaObject::invokeMethod(this, &ThumbnailResponse::startRequest, Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThumbnailResponse::startRequest()
|
||||||
|
{
|
||||||
|
if (!m_connection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Runs in the main thread, not QML thread
|
||||||
|
Q_ASSERT(QThread::currentThread() == m_connection->thread());
|
||||||
|
job = m_connection->getThumbnail(mediaId, requestedSize);
|
||||||
|
// Connect to any possible outcome including abandonment
|
||||||
|
// to make sure the QML thread is not left stuck forever.
|
||||||
|
connect(job, &BaseJob::finished, this, &ThumbnailResponse::prepareResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThumbnailResponse::prepareResult()
|
||||||
|
{
|
||||||
|
Q_ASSERT(QThread::currentThread() == job->thread());
|
||||||
|
Q_ASSERT(job->error() != BaseJob::Pending);
|
||||||
|
{
|
||||||
|
QWriteLocker _(&lock);
|
||||||
|
if (job->error() == BaseJob::Success) {
|
||||||
|
image = job->thumbnail();
|
||||||
|
|
||||||
|
QString localPath = QFileInfo(localFile).absolutePath();
|
||||||
|
QDir dir;
|
||||||
|
if (!dir.exists(localPath)) {
|
||||||
|
dir.mkpath(localPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
image.save(localFile);
|
||||||
|
|
||||||
|
errorStr.clear();
|
||||||
|
} else if (job->error() == BaseJob::Abandoned) {
|
||||||
|
errorStr = i18n("Image request has been cancelled");
|
||||||
|
// qDebug() << "ThumbnailResponse: cancelled for" << mediaId;
|
||||||
|
} else {
|
||||||
|
errorStr = job->errorString();
|
||||||
|
qWarning() << "ThumbnailResponse: no valid image for" << mediaId << "-" << errorStr;
|
||||||
|
}
|
||||||
|
job = nullptr;
|
||||||
|
}
|
||||||
|
Q_EMIT finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickTextureFactory *ThumbnailResponse::textureFactory() const
|
||||||
|
{
|
||||||
|
QReadLocker _(&lock);
|
||||||
|
return QQuickTextureFactory::textureFactoryForImage(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ThumbnailResponse::errorString() const
|
||||||
|
{
|
||||||
|
QReadLocker _(&lock);
|
||||||
|
return errorStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickImageResponse *MatrixImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
|
||||||
|
{
|
||||||
|
return new ThumbnailResponse(id, requestedSize, m_connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "moc_matriximageprovider.cpp"
|
||||||
80
src/matriximageprovider.h
Normal file
80
src/matriximageprovider.h
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
|
||||||
|
// SPDX-FileCopyrightText: 2019 Kitsune Ral <kitsune-ral@users.sf.net>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QQuickAsyncImageProvider>
|
||||||
|
|
||||||
|
#include <Quotient/jobs/mediathumbnailjob.h>
|
||||||
|
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
|
class NeoChatConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class ThumbnailResponse
|
||||||
|
*
|
||||||
|
* A QQuickImageResponse for an mxc image.
|
||||||
|
*
|
||||||
|
* @sa QQuickImageResponse
|
||||||
|
*/
|
||||||
|
class ThumbnailResponse : public QQuickImageResponse
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ThumbnailResponse(QString mediaId, QSize requestedSize, NeoChatConnection *m_connection);
|
||||||
|
~ThumbnailResponse() override = default;
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void startRequest();
|
||||||
|
void prepareResult();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString mediaId;
|
||||||
|
QSize requestedSize;
|
||||||
|
const QString localFile;
|
||||||
|
Quotient::MediaThumbnailJob *job = nullptr;
|
||||||
|
QPointer<NeoChatConnection> m_connection;
|
||||||
|
|
||||||
|
QImage image;
|
||||||
|
QString errorStr;
|
||||||
|
mutable QReadWriteLock lock; // Guards ONLY these two members above
|
||||||
|
|
||||||
|
QQuickTextureFactory *textureFactory() const override;
|
||||||
|
QString errorString() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class MatrixImageProvider
|
||||||
|
*
|
||||||
|
* A QQuickAsyncImageProvider for mxc images.
|
||||||
|
*
|
||||||
|
* @sa QQuickAsyncImageProvider
|
||||||
|
*/
|
||||||
|
class MatrixImageProvider : public QQuickAsyncImageProvider
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
QML_SINGLETON
|
||||||
|
|
||||||
|
Q_PROPERTY(NeoChatConnection *connection MEMBER m_connection)
|
||||||
|
public:
|
||||||
|
static MatrixImageProvider *create(QQmlEngine *engine, QJSEngine *)
|
||||||
|
{
|
||||||
|
static MatrixImageProvider *instance = new MatrixImageProvider;
|
||||||
|
engine->setObjectOwnership(instance, QQmlEngine::CppOwnership);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return a job to provide the image with the given ID.
|
||||||
|
*
|
||||||
|
* @sa QQuickAsyncImageProvider::requestImageResponse
|
||||||
|
*/
|
||||||
|
QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QPointer<NeoChatConnection> m_connection;
|
||||||
|
MatrixImageProvider() = default;
|
||||||
|
};
|
||||||
@@ -211,7 +211,7 @@ QList<ActionsModel::Action> actions{
|
|||||||
Q_EMIT Controller::instance().showMessage(Controller::Positive, i18n("You are already in this room."));
|
Q_EMIT Controller::instance().showMessage(Controller::Positive, i18n("You are already in this room."));
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
if (room->joinedMemberIds().contains(text)) {
|
if (room->members().contains(room->member(text))) {
|
||||||
Q_EMIT Controller::instance().showMessage(Controller::Info, i18nc("<user> is already in this room.", "%1 is already in this room.", text));
|
Q_EMIT Controller::instance().showMessage(Controller::Info, i18nc("<user> is already in this room.", "%1 is already in this room.", text));
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,18 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
#include "messagecontentmodel.h"
|
#include "messagecontentmodel.h"
|
||||||
#include "eventhandler.h"
|
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
|
#include "neochatroommember.h"
|
||||||
|
|
||||||
#include <QImageReader>
|
#include <QImageReader>
|
||||||
|
|
||||||
|
#include <Quotient/events/eventcontent.h>
|
||||||
#include <Quotient/events/redactionevent.h>
|
#include <Quotient/events/redactionevent.h>
|
||||||
#include <Quotient/events/roommessageevent.h>
|
#include <Quotient/events/roommessageevent.h>
|
||||||
#include <Quotient/events/stickerevent.h>
|
#include <Quotient/events/stickerevent.h>
|
||||||
#include <Quotient/qt_connection_util.h>
|
|
||||||
|
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
#include <Kirigami/Platform/PlatformTheme>
|
#include <Quotient/qt_connection_util.h>
|
||||||
|
|
||||||
#ifndef Q_OS_ANDROID
|
#ifndef Q_OS_ANDROID
|
||||||
#include <KSyntaxHighlighting/Definition>
|
#include <KSyntaxHighlighting/Definition>
|
||||||
@@ -21,7 +21,10 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "chatbarcache.h"
|
#include "chatbarcache.h"
|
||||||
|
#include "enums/messagecomponenttype.h"
|
||||||
|
#include "eventhandler.h"
|
||||||
#include "filetype.h"
|
#include "filetype.h"
|
||||||
|
#include "itinerarymodel.h"
|
||||||
#include "linkpreviewer.h"
|
#include "linkpreviewer.h"
|
||||||
#include "neochatconnection.h"
|
#include "neochatconnection.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
@@ -29,8 +32,8 @@
|
|||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
MessageContentModel::MessageContentModel(NeoChatRoom *room, const Quotient::RoomEvent *event, bool isReply, bool isPending, MessageContentModel *parent)
|
MessageContentModel::MessageContentModel(NeoChatRoom *room, const Quotient::RoomEvent *event, bool isReply, bool isPending)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(nullptr)
|
||||||
, m_room(room)
|
, m_room(room)
|
||||||
, m_eventId(event != nullptr ? event->id() : QString())
|
, m_eventId(event != nullptr ? event->id() : QString())
|
||||||
, m_eventSenderId(event != nullptr ? event->senderId() : QString())
|
, m_eventSenderId(event != nullptr ? event->senderId() : QString())
|
||||||
@@ -41,8 +44,8 @@ MessageContentModel::MessageContentModel(NeoChatRoom *room, const Quotient::Room
|
|||||||
initializeModel();
|
initializeModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageContentModel::MessageContentModel(NeoChatRoom *room, const QString &eventId, bool isReply, bool isPending, MessageContentModel *parent)
|
MessageContentModel::MessageContentModel(NeoChatRoom *room, const QString &eventId, bool isReply, bool isPending)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(nullptr)
|
||||||
, m_room(room)
|
, m_room(room)
|
||||||
, m_eventId(eventId)
|
, m_eventId(eventId)
|
||||||
, m_isPending(isPending)
|
, m_isPending(isPending)
|
||||||
@@ -56,27 +59,15 @@ void MessageContentModel::initializeModel()
|
|||||||
Q_ASSERT(m_room != nullptr);
|
Q_ASSERT(m_room != nullptr);
|
||||||
// Allow making a model for an event that is being downloaded but will appear later
|
// Allow making a model for an event that is being downloaded but will appear later
|
||||||
// e.g. a reply, but we need an ID to know when it has arrived.
|
// e.g. a reply, but we need an ID to know when it has arrived.
|
||||||
// Also note that a pending event may not have an event ID yet but as long as we have an event
|
Q_ASSERT(!m_eventId.isEmpty());
|
||||||
// pointer we can pass out the transaction ID until it is set.
|
|
||||||
Q_ASSERT(!m_eventId.isEmpty() || m_event != nullptr);
|
|
||||||
|
|
||||||
Quotient::connectUntil(m_room.get(), &NeoChatRoom::extraEventLoaded, this, [this](const QString &eventId) {
|
Quotient::connectUntil(m_room.get(), &NeoChatRoom::extraEventLoaded, this, [this](const QString &eventId) {
|
||||||
if (m_room != nullptr) {
|
if (m_room != nullptr) {
|
||||||
if (eventId == m_eventId) {
|
if (eventId == m_eventId) {
|
||||||
m_notFound = false;
|
m_event = loadEvent<RoomEvent>(m_room->getEvent(eventId)->fullJson());
|
||||||
intiializeEvent(m_room->getEvent(eventId));
|
Q_EMIT eventUpdated();
|
||||||
updateReplyModel();
|
updateReplyModel();
|
||||||
resetModel();
|
resetContent();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
Quotient::connectUntil(m_room.get(), &NeoChatRoom::extraEventNotFound, this, [this](const QString &eventId) {
|
|
||||||
if (m_room != nullptr) {
|
|
||||||
if (eventId == m_eventId) {
|
|
||||||
m_notFound = true;
|
|
||||||
resetModel();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,15 +75,12 @@ void MessageContentModel::initializeModel()
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (m_event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
intiializeEvent(m_room->getEvent(m_eventId));
|
m_room->downloadEventFromServer(m_eventId);
|
||||||
if (m_event == nullptr) {
|
|
||||||
m_room->downloadEventFromServer(m_eventId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(m_room, &NeoChatRoom::pendingEventAboutToMerge, this, [this](Quotient::RoomEvent *serverEvent) {
|
connect(m_room, &NeoChatRoom::pendingEventAboutToMerge, this, [this](Quotient::RoomEvent *serverEvent) {
|
||||||
if (m_room != nullptr && m_event != nullptr) {
|
if (m_room != nullptr && m_event != nullptr) {
|
||||||
if (m_eventId == serverEvent->id() || m_eventId == serverEvent->transactionId()) {
|
if (m_eventId == serverEvent->id()) {
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_isPending = false;
|
m_isPending = false;
|
||||||
intiializeEvent(serverEvent);
|
intiializeEvent(serverEvent);
|
||||||
@@ -105,7 +93,6 @@ void MessageContentModel::initializeModel()
|
|||||||
if (m_eventId == newEvent->id()) {
|
if (m_eventId == newEvent->id()) {
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
intiializeEvent(newEvent);
|
intiializeEvent(newEvent);
|
||||||
resetContent();
|
|
||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,13 +127,6 @@ void MessageContentModel::initializeModel()
|
|||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect(m_room->threadCache(), &ChatBarCache::threadIdChanged, this, [this](const QString &oldThreadId, const QString &newThreadId) {
|
|
||||||
if (m_event != nullptr && (oldThreadId == m_eventId || newThreadId == m_eventId)) {
|
|
||||||
beginResetModel();
|
|
||||||
resetContent(false, newThreadId == m_eventId);
|
|
||||||
endResetModel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
connect(m_room, &NeoChatRoom::urlPreviewEnabledChanged, this, [this]() {
|
connect(m_room, &NeoChatRoom::urlPreviewEnabledChanged, this, [this]() {
|
||||||
resetContent();
|
resetContent();
|
||||||
});
|
});
|
||||||
@@ -168,11 +148,6 @@ void MessageContentModel::initializeModel()
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(NeoChatConfig::self(), &NeoChatConfig::ThreadsChanged, this, [this]() {
|
|
||||||
updateReplyModel();
|
|
||||||
resetModel();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (m_event != nullptr) {
|
if (m_event != nullptr) {
|
||||||
updateReplyModel();
|
updateReplyModel();
|
||||||
}
|
}
|
||||||
@@ -189,15 +164,8 @@ void MessageContentModel::intiializeEvent(const QString &eventId)
|
|||||||
|
|
||||||
void MessageContentModel::intiializeEvent(const Quotient::RoomEvent *event)
|
void MessageContentModel::intiializeEvent(const Quotient::RoomEvent *event)
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_event = loadEvent<RoomEvent>(event->fullJson());
|
m_event = loadEvent<RoomEvent>(event->fullJson());
|
||||||
// a pending event may not previously have had an event ID so update.
|
auto senderId = event->senderId();
|
||||||
m_eventId = EventHandler::id(m_event.get());
|
|
||||||
|
|
||||||
auto senderId = m_event->senderId();
|
|
||||||
// A pending event might not have a sender ID set yet but in that case it must
|
// A pending event might not have a sender ID set yet but in that case it must
|
||||||
// be the local member.
|
// be the local member.
|
||||||
if (senderId.isEmpty()) {
|
if (senderId.isEmpty()) {
|
||||||
@@ -221,7 +189,7 @@ void MessageContentModel::setShowAuthor(bool showAuthor)
|
|||||||
}
|
}
|
||||||
m_showAuthor = showAuthor;
|
m_showAuthor = showAuthor;
|
||||||
|
|
||||||
if (m_event != nullptr && m_room->connection()->isIgnored(m_event->senderId())) {
|
if (m_event != nullptr) {
|
||||||
if (showAuthor) {
|
if (showAuthor) {
|
||||||
beginInsertRows({}, 0, 0);
|
beginInsertRows({}, 0, 0);
|
||||||
m_components.prepend(MessageComponent{MessageComponentType::Author, QString(), {}});
|
m_components.prepend(MessageComponent{MessageComponentType::Author, QString(), {}});
|
||||||
@@ -248,38 +216,20 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EventHandler eventHandler(m_room, m_event.get());
|
||||||
const auto component = m_components[index.row()];
|
const auto component = m_components[index.row()];
|
||||||
|
|
||||||
if (role == DisplayRole) {
|
if (role == DisplayRole) {
|
||||||
if (m_notFound || (m_event && m_room->connection()->isIgnored(m_event->senderId()))) {
|
|
||||||
Kirigami::Platform::PlatformTheme *theme =
|
|
||||||
static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
|
|
||||||
|
|
||||||
QString disabledTextColor;
|
|
||||||
if (theme != nullptr) {
|
|
||||||
disabledTextColor = theme->disabledTextColor().name();
|
|
||||||
} else {
|
|
||||||
disabledTextColor = QStringLiteral("#000000");
|
|
||||||
}
|
|
||||||
return QString(QStringLiteral("<span style=\"color:%1\">").arg(disabledTextColor)
|
|
||||||
+ i18nc("@info", "This message was either not found, you do not have permission to view it, or it was sent by an ignored user")
|
|
||||||
+ QStringLiteral("</span>"));
|
|
||||||
}
|
|
||||||
if (component.type == MessageComponentType::Loading && m_isReply) {
|
if (component.type == MessageComponentType::Loading && m_isReply) {
|
||||||
return i18n("Loading reply");
|
return i18n("Loading reply");
|
||||||
}
|
}
|
||||||
if (m_event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
if (m_event->isRedacted()) {
|
|
||||||
auto reason = m_event->redactedBecause()->reason();
|
|
||||||
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>")
|
|
||||||
: i18n("<i>[This message was deleted: %1]</i>", m_event->redactedBecause()->reason());
|
|
||||||
}
|
|
||||||
if (!component.content.isEmpty()) {
|
if (!component.content.isEmpty()) {
|
||||||
return component.content;
|
return component.content;
|
||||||
}
|
}
|
||||||
return EventHandler::richBody(m_room, m_event.get());
|
return eventHandler.getRichBody();
|
||||||
}
|
}
|
||||||
if (role == ComponentTypeRole) {
|
if (role == ComponentTypeRole) {
|
||||||
return component.type;
|
return component.type;
|
||||||
@@ -288,7 +238,7 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
|||||||
return component.attributes;
|
return component.attributes;
|
||||||
}
|
}
|
||||||
if (role == EventIdRole) {
|
if (role == EventIdRole) {
|
||||||
return EventHandler::id(m_event.get());
|
return eventHandler.getId();
|
||||||
}
|
}
|
||||||
if (role == TimeRole) {
|
if (role == TimeRole) {
|
||||||
const auto pendingIt = std::find_if(m_room->pendingEvents().cbegin(), m_room->pendingEvents().cend(), [this](const PendingEventItem &pendingEvent) {
|
const auto pendingIt = std::find_if(m_room->pendingEvents().cbegin(), m_room->pendingEvents().cend(), [this](const PendingEventItem &pendingEvent) {
|
||||||
@@ -296,7 +246,7 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
|||||||
});
|
});
|
||||||
|
|
||||||
auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated();
|
auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated();
|
||||||
return EventHandler::time(m_event.get(), m_isPending, lastUpdated);
|
return eventHandler.getTime(m_isPending, lastUpdated);
|
||||||
}
|
}
|
||||||
if (role == TimeStringRole) {
|
if (role == TimeStringRole) {
|
||||||
const auto pendingIt = std::find_if(m_room->pendingEvents().cbegin(), m_room->pendingEvents().cend(), [this](const PendingEventItem &pendingEvent) {
|
const auto pendingIt = std::find_if(m_room->pendingEvents().cbegin(), m_room->pendingEvents().cend(), [this](const PendingEventItem &pendingEvent) {
|
||||||
@@ -304,13 +254,13 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
|||||||
});
|
});
|
||||||
|
|
||||||
auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated();
|
auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated();
|
||||||
return EventHandler::timeString(m_event.get(), QStringLiteral("hh:mm"), m_isPending, lastUpdated);
|
return eventHandler.getTimeString(QStringLiteral("hh:mm"), m_isPending, lastUpdated);
|
||||||
}
|
}
|
||||||
if (role == AuthorRole) {
|
if (role == AuthorRole) {
|
||||||
return QVariant::fromValue<NeochatRoomMember *>(m_eventSenderObject.get());
|
return QVariant::fromValue<NeochatRoomMember *>(m_eventSenderObject.get());
|
||||||
}
|
}
|
||||||
if (role == MediaInfoRole) {
|
if (role == MediaInfoRole) {
|
||||||
return EventHandler::mediaInfo(m_room, m_event.get());
|
return eventHandler.getMediaInfo();
|
||||||
}
|
}
|
||||||
if (role == FileTransferInfoRole) {
|
if (role == FileTransferInfoRole) {
|
||||||
return QVariant::fromValue(m_room->cachedFileTransferInfo(m_event.get()));
|
return QVariant::fromValue(m_room->cachedFileTransferInfo(m_event.get()));
|
||||||
@@ -319,22 +269,25 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
|||||||
return QVariant::fromValue<ItineraryModel *>(m_itineraryModel);
|
return QVariant::fromValue<ItineraryModel *>(m_itineraryModel);
|
||||||
}
|
}
|
||||||
if (role == LatitudeRole) {
|
if (role == LatitudeRole) {
|
||||||
return EventHandler::latitude(m_event.get());
|
return eventHandler.getLatitude();
|
||||||
}
|
}
|
||||||
if (role == LongitudeRole) {
|
if (role == LongitudeRole) {
|
||||||
return EventHandler::longitude(m_event.get());
|
return eventHandler.getLongitude();
|
||||||
}
|
}
|
||||||
if (role == AssetRole) {
|
if (role == AssetRole) {
|
||||||
return EventHandler::locationAssetType(m_event.get());
|
return eventHandler.getLocationAssetType();
|
||||||
}
|
}
|
||||||
if (role == PollHandlerRole) {
|
if (role == PollHandlerRole) {
|
||||||
return QVariant::fromValue<PollHandler *>(m_room->poll(m_eventId));
|
return QVariant::fromValue<PollHandler *>(m_room->poll(m_eventId));
|
||||||
}
|
}
|
||||||
|
if (role == IsReplyRole) {
|
||||||
|
return eventHandler.hasReply();
|
||||||
|
}
|
||||||
if (role == ReplyEventIdRole) {
|
if (role == ReplyEventIdRole) {
|
||||||
return EventHandler::replyId(m_event.get());
|
return eventHandler.getReplyId();
|
||||||
}
|
}
|
||||||
if (role == ReplyAuthorRole) {
|
if (role == ReplyAuthorRole) {
|
||||||
return QVariant::fromValue(EventHandler::replyAuthor(m_room, m_event.get()));
|
return QVariant::fromValue(eventHandler.getReplyAuthor());
|
||||||
}
|
}
|
||||||
if (role == ReplyContentModelRole) {
|
if (role == ReplyContentModelRole) {
|
||||||
return QVariant::fromValue<MessageContentModel *>(m_replyModel);
|
return QVariant::fromValue<MessageContentModel *>(m_replyModel);
|
||||||
@@ -347,12 +300,6 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
|||||||
return QVariant::fromValue<LinkPreviewer *>(emptyLinkPreview);
|
return QVariant::fromValue<LinkPreviewer *>(emptyLinkPreview);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (role == ChatBarCacheRole) {
|
|
||||||
if (m_room->threadCache()->threadId() == m_eventId) {
|
|
||||||
return QVariant::fromValue<ChatBarCache *>(m_room->threadCache());
|
|
||||||
}
|
|
||||||
return QVariant::fromValue<ChatBarCache *>(m_room->editCache());
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -380,11 +327,11 @@ QHash<int, QByteArray> MessageContentModel::roleNames() const
|
|||||||
roles[LongitudeRole] = "longitude";
|
roles[LongitudeRole] = "longitude";
|
||||||
roles[AssetRole] = "asset";
|
roles[AssetRole] = "asset";
|
||||||
roles[PollHandlerRole] = "pollHandler";
|
roles[PollHandlerRole] = "pollHandler";
|
||||||
|
roles[IsReplyRole] = "isReply";
|
||||||
roles[ReplyEventIdRole] = "replyEventId";
|
roles[ReplyEventIdRole] = "replyEventId";
|
||||||
roles[ReplyAuthorRole] = "replyAuthor";
|
roles[ReplyAuthorRole] = "replyAuthor";
|
||||||
roles[ReplyContentModelRole] = "replyContentModel";
|
roles[ReplyContentModelRole] = "replyContentModel";
|
||||||
roles[LinkPreviewerRole] = "linkPreviewer";
|
roles[LinkPreviewerRole] = "linkPreviewer";
|
||||||
roles[ChatBarCacheRole] = "chatBarCache";
|
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,12 +340,6 @@ void MessageContentModel::resetModel()
|
|||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_components.clear();
|
m_components.clear();
|
||||||
|
|
||||||
if ((m_event && m_room->connection()->isIgnored(m_event->senderId())) || m_notFound) {
|
|
||||||
m_components += MessageComponent{MessageComponentType::Text, QString(), {}};
|
|
||||||
endResetModel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
m_components += MessageComponent{MessageComponentType::Loading, QString(), {}};
|
m_components += MessageComponent{MessageComponentType::Loading, QString(), {}};
|
||||||
endResetModel();
|
endResetModel();
|
||||||
@@ -413,7 +354,7 @@ void MessageContentModel::resetModel()
|
|||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageContentModel::resetContent(bool isEditing, bool isThreading)
|
void MessageContentModel::resetContent(bool isEditing)
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_event != nullptr);
|
Q_ASSERT(m_event != nullptr);
|
||||||
|
|
||||||
@@ -422,7 +363,7 @@ void MessageContentModel::resetContent(bool isEditing, bool isThreading)
|
|||||||
m_components.remove(startRow, rowCount() - startRow);
|
m_components.remove(startRow, rowCount() - startRow);
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
|
|
||||||
const auto newComponents = messageContentComponents(isEditing, isThreading);
|
const auto newComponents = messageContentComponents(isEditing);
|
||||||
if (newComponents.size() == 0) {
|
if (newComponents.size() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -431,7 +372,7 @@ void MessageContentModel::resetContent(bool isEditing, bool isThreading)
|
|||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEditing, bool isThreading)
|
QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEditing)
|
||||||
{
|
{
|
||||||
QList<MessageComponent> newComponents;
|
QList<MessageComponent> newComponents;
|
||||||
|
|
||||||
@@ -451,45 +392,35 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
newComponents += MessageComponent{MessageComponentType::ChatBar, QString(), {}};
|
newComponents += MessageComponent{MessageComponentType::Edit, QString(), {}};
|
||||||
} else {
|
} else {
|
||||||
newComponents.append(componentsForType(MessageComponentType::typeForEvent(*m_event.get())));
|
EventHandler eventHandler(m_room, m_event.get());
|
||||||
|
newComponents.append(componentsForType(eventHandler.messageComponentType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_room->urlPreviewEnabled()) {
|
if (m_room->urlPreviewEnabled()) {
|
||||||
newComponents = addLinkPreviews(newComponents);
|
newComponents = addLinkPreviews(newComponents);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the event is already threaded the ThreadModel will handle displaying a chat bar.
|
|
||||||
if (isThreading && !EventHandler::isThreaded(m_event.get())) {
|
|
||||||
newComponents += MessageComponent{MessageComponentType::ChatBar, QString(), {}};
|
|
||||||
}
|
|
||||||
|
|
||||||
return newComponents;
|
return newComponents;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageContentModel::updateReplyModel()
|
void MessageContentModel::updateReplyModel()
|
||||||
{
|
{
|
||||||
if (m_event == nullptr || m_isReply) {
|
if (m_event == nullptr || m_replyModel != nullptr || m_isReply) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EventHandler::hasReply(m_event.get()) || (EventHandler::isThreaded(m_event.get()) && NeoChatConfig::self()->threads())) {
|
EventHandler eventHandler(m_room, m_event.get());
|
||||||
if (m_replyModel) {
|
if (!eventHandler.hasReply()) {
|
||||||
delete m_replyModel;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_replyModel != nullptr) {
|
const auto replyEvent = m_room->findInTimeline(eventHandler.getReplyId());
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto replyEvent = m_room->findInTimeline(EventHandler::replyId(m_event.get()));
|
|
||||||
if (replyEvent == m_room->historyEdge()) {
|
if (replyEvent == m_room->historyEdge()) {
|
||||||
m_replyModel = new MessageContentModel(m_room, EventHandler::replyId(m_event.get()), true, false, this);
|
m_replyModel = new MessageContentModel(m_room, eventHandler.getReplyId(), true);
|
||||||
} else {
|
} else {
|
||||||
m_replyModel = new MessageContentModel(m_room, replyEvent->get(), true, false, this);
|
m_replyModel = new MessageContentModel(m_room, replyEvent->get(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(m_replyModel, &MessageContentModel::eventUpdated, this, [this]() {
|
connect(m_replyModel, &MessageContentModel::eventUpdated, this, [this]() {
|
||||||
@@ -515,10 +446,19 @@ QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentT
|
|||||||
auto fileTransferInfo = m_room->cachedFileTransferInfo(m_event.get());
|
auto fileTransferInfo = m_room->cachedFileTransferInfo(m_event.get());
|
||||||
|
|
||||||
#ifndef Q_OS_ANDROID
|
#ifndef Q_OS_ANDROID
|
||||||
Q_ASSERT(event->content() != nullptr && event->content()->fileInfo() != nullptr);
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
Q_ASSERT(event->content() != nullptr && event->has<EventContent::FileContent>());
|
||||||
|
const QMimeType mimeType = event->get<EventContent::FileContent>()->mimeType;
|
||||||
|
#else
|
||||||
|
Q_ASSERT(event->content() != nullptr && event->hasFileContent());
|
||||||
const QMimeType mimeType = event->content()->fileInfo()->mimeType;
|
const QMimeType mimeType = event->content()->fileInfo()->mimeType;
|
||||||
|
#endif
|
||||||
if (mimeType.name() == QStringLiteral("text/plain") || mimeType.parentMimeTypes().contains(QStringLiteral("text/plain"))) {
|
if (mimeType.name() == QStringLiteral("text/plain") || mimeType.parentMimeTypes().contains(QStringLiteral("text/plain"))) {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
QString originalName = event->get<EventContent::FileContent>()->originalName;
|
||||||
|
#else
|
||||||
QString originalName = event->content()->fileInfo()->originalName;
|
QString originalName = event->content()->fileInfo()->originalName;
|
||||||
|
#endif
|
||||||
if (originalName.isEmpty()) {
|
if (originalName.isEmpty()) {
|
||||||
originalName = event->plainBody();
|
originalName = event->plainBody();
|
||||||
}
|
}
|
||||||
@@ -579,7 +519,7 @@ MessageComponent MessageContentModel::linkPreviewComponent(const QUrl &link)
|
|||||||
if (linkPreviewer->loaded()) {
|
if (linkPreviewer->loaded()) {
|
||||||
return MessageComponent{MessageComponentType::LinkPreview, QString(), {{"link"_ls, link}}};
|
return MessageComponent{MessageComponentType::LinkPreview, QString(), {{"link"_ls, link}}};
|
||||||
} else {
|
} else {
|
||||||
connect(linkPreviewer, &LinkPreviewer::loadedChanged, [this, link]() {
|
connect(linkPreviewer, &LinkPreviewer::loadedChanged, this, [this, link]() {
|
||||||
const auto linkPreviewer = dynamic_cast<NeoChatConnection *>(m_room->connection())->previewerForLink(link);
|
const auto linkPreviewer = dynamic_cast<NeoChatConnection *>(m_room->connection())->previewerForLink(link);
|
||||||
if (linkPreviewer != nullptr && linkPreviewer->loaded()) {
|
if (linkPreviewer != nullptr && linkPreviewer->loaded()) {
|
||||||
for (auto &component : m_components) {
|
for (auto &component : m_components) {
|
||||||
@@ -620,6 +560,11 @@ QList<MessageComponent> MessageContentModel::addLinkPreviews(QList<MessageCompon
|
|||||||
|
|
||||||
void MessageContentModel::closeLinkPreview(int row)
|
void MessageContentModel::closeLinkPreview(int row)
|
||||||
{
|
{
|
||||||
|
if (row < 0 || row > m_components.size()) {
|
||||||
|
qWarning() << "closeLinkPreview() called with row" << row << "which does not exist. m_components.size() =" << m_components.size();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_components[row].type == MessageComponentType::LinkPreview || m_components[row].type == MessageComponentType::LinkPreviewLoad) {
|
if (m_components[row].type == MessageComponentType::LinkPreview || m_components[row].type == MessageComponentType::LinkPreviewLoad) {
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_removedLinkPreviews += m_components[row].attributes["link"_ls].toUrl();
|
m_removedLinkPreviews += m_components[row].attributes["link"_ls].toUrl();
|
||||||
@@ -636,8 +581,12 @@ void MessageContentModel::updateItineraryModel()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
if (event->has<EventContent::FileContent>()) {
|
||||||
|
#else
|
||||||
if (event->hasFileContent()) {
|
if (event->hasFileContent()) {
|
||||||
auto filePath = m_room->cachedFileTransferInfo(m_event.get()).localPath;
|
#endif
|
||||||
|
auto filePath = m_room->cachedFileTransferInfo(event).localPath;
|
||||||
if (filePath.isEmpty() && m_itineraryModel != nullptr) {
|
if (filePath.isEmpty() && m_itineraryModel != nullptr) {
|
||||||
delete m_itineraryModel;
|
delete m_itineraryModel;
|
||||||
m_itineraryModel = nullptr;
|
m_itineraryModel = nullptr;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <Quotient/room.h>
|
#include <Quotient/room.h>
|
||||||
|
|
||||||
#include "enums/messagecomponenttype.h"
|
#include "enums/messagecomponenttype.h"
|
||||||
|
#include "eventhandler.h"
|
||||||
#include "itinerarymodel.h"
|
#include "itinerarymodel.h"
|
||||||
#include "neochatroommember.h"
|
#include "neochatroommember.h"
|
||||||
|
|
||||||
@@ -65,21 +66,17 @@ public:
|
|||||||
AssetRole, /**< Type of location event, e.g. self pin of the user location. */
|
AssetRole, /**< Type of location event, e.g. self pin of the user location. */
|
||||||
PollHandlerRole, /**< The PollHandler for the event, if any. */
|
PollHandlerRole, /**< The PollHandler for the event, if any. */
|
||||||
|
|
||||||
|
IsReplyRole, /**< Is the message a reply to another event. */
|
||||||
ReplyEventIdRole, /**< The matrix ID of the message that was replied to. */
|
ReplyEventIdRole, /**< The matrix ID of the message that was replied to. */
|
||||||
ReplyAuthorRole, /**< The author of the event that was replied to. */
|
ReplyAuthorRole, /**< The author of the event that was replied to. */
|
||||||
ReplyContentModelRole, /**< The MessageContentModel for the reply event. */
|
ReplyContentModelRole, /**< The MessageContentModel for the reply event. */
|
||||||
|
|
||||||
LinkPreviewerRole, /**< The link preview details. */
|
LinkPreviewerRole, /**< The link preview details. */
|
||||||
ChatBarCacheRole, /**< The ChatBarCache to use. */
|
|
||||||
};
|
};
|
||||||
Q_ENUM(Roles)
|
Q_ENUM(Roles)
|
||||||
|
|
||||||
explicit MessageContentModel(NeoChatRoom *room,
|
explicit MessageContentModel(NeoChatRoom *room, const Quotient::RoomEvent *event, bool isReply = false, bool isPending = false);
|
||||||
const Quotient::RoomEvent *event,
|
MessageContentModel(NeoChatRoom *room, const QString &eventId, bool isReply = false, bool isPending = false);
|
||||||
bool isReply = false,
|
|
||||||
bool isPending = false,
|
|
||||||
MessageContentModel *parent = nullptr);
|
|
||||||
MessageContentModel(NeoChatRoom *room, const QString &eventId, bool isReply = false, bool isPending = false, MessageContentModel *parent = nullptr);
|
|
||||||
|
|
||||||
bool showAuthor() const;
|
bool showAuthor() const;
|
||||||
void setShowAuthor(bool showAuthor);
|
void setShowAuthor(bool showAuthor);
|
||||||
@@ -126,7 +123,6 @@ private:
|
|||||||
bool m_isPending;
|
bool m_isPending;
|
||||||
bool m_showAuthor = true;
|
bool m_showAuthor = true;
|
||||||
bool m_isReply;
|
bool m_isReply;
|
||||||
bool m_notFound = false;
|
|
||||||
|
|
||||||
void initializeModel();
|
void initializeModel();
|
||||||
void intiializeEvent(const QString &eventId);
|
void intiializeEvent(const QString &eventId);
|
||||||
@@ -134,8 +130,8 @@ private:
|
|||||||
|
|
||||||
QList<MessageComponent> m_components;
|
QList<MessageComponent> m_components;
|
||||||
void resetModel();
|
void resetModel();
|
||||||
void resetContent(bool isEditing = false, bool isThreading = false);
|
void resetContent(bool isEditing = false);
|
||||||
QList<MessageComponent> messageContentComponents(bool isEditing = false, bool isThreading = false);
|
QList<MessageComponent> messageContentComponents(bool isEditing = false);
|
||||||
|
|
||||||
QPointer<MessageContentModel> m_replyModel;
|
QPointer<MessageContentModel> m_replyModel;
|
||||||
void updateReplyModel();
|
void updateReplyModel();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
|
|
||||||
#include <Quotient/csapi/rooms.h>
|
#include <Quotient/csapi/rooms.h>
|
||||||
|
#include <Quotient/events/eventcontent.h>
|
||||||
#include <Quotient/events/redactionevent.h>
|
#include <Quotient/events/redactionevent.h>
|
||||||
#include <Quotient/events/roommessageevent.h>
|
#include <Quotient/events/roommessageevent.h>
|
||||||
#include <Quotient/events/stickerevent.h>
|
#include <Quotient/events/stickerevent.h>
|
||||||
@@ -22,8 +23,13 @@
|
|||||||
#include "enums/delegatetype.h"
|
#include "enums/delegatetype.h"
|
||||||
#include "eventhandler.h"
|
#include "eventhandler.h"
|
||||||
#include "events/pollevent.h"
|
#include "events/pollevent.h"
|
||||||
|
#include "linkpreviewer.h"
|
||||||
|
#include "messagecontentmodel.h"
|
||||||
#include "models/messagefiltermodel.h"
|
#include "models/messagefiltermodel.h"
|
||||||
#include "models/reactionmodel.h"
|
#include "models/reactionmodel.h"
|
||||||
|
#include "neochatroom.h"
|
||||||
|
#include "neochatroommember.h"
|
||||||
|
#include "readmarkermodel.h"
|
||||||
#include "texthandler.h"
|
#include "texthandler.h"
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
@@ -66,11 +72,6 @@ MessageEventModel::MessageEventModel(QObject *parent)
|
|||||||
connect(this, &MessageEventModel::modelReset, this, [this]() {
|
connect(this, &MessageEventModel::modelReset, this, [this]() {
|
||||||
resetting = false;
|
resetting = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(NeoChatConfig::self(), &NeoChatConfig::ThreadsChanged, this, [this]() {
|
|
||||||
beginResetModel();
|
|
||||||
endResetModel();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NeoChatRoom *MessageEventModel::room() const
|
NeoChatRoom *MessageEventModel::room() const
|
||||||
@@ -88,6 +89,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
|||||||
// HACK: Reset the model to a null room first to make sure QML dismantles
|
// HACK: Reset the model to a null room first to make sure QML dismantles
|
||||||
// last room's objects before the room is actually changed
|
// last room's objects before the room is actually changed
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
|
m_readMarkerModels.clear();
|
||||||
m_currentRoom->disconnect(this);
|
m_currentRoom->disconnect(this);
|
||||||
m_currentRoom = nullptr;
|
m_currentRoom = nullptr;
|
||||||
endResetModel();
|
endResetModel();
|
||||||
@@ -95,9 +97,6 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
|||||||
// Don't clear the member objects until the model has been fully reset and all
|
// Don't clear the member objects until the model has been fully reset and all
|
||||||
// refs cleared.
|
// refs cleared.
|
||||||
m_memberObjects.clear();
|
m_memberObjects.clear();
|
||||||
m_contentModels.clear();
|
|
||||||
m_reactionModels.clear();
|
|
||||||
m_readMarkerModels.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
@@ -432,30 +431,26 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
const auto pendingIt = m_currentRoom->pendingEvents().crbegin() + std::min(row, timelineBaseIndex());
|
const auto pendingIt = m_currentRoom->pendingEvents().crbegin() + std::min(row, timelineBaseIndex());
|
||||||
const auto &evt = isPending ? **pendingIt : **timelineIt;
|
const auto &evt = isPending ? **pendingIt : **timelineIt;
|
||||||
|
|
||||||
|
EventHandler eventHandler(m_currentRoom, &evt);
|
||||||
|
|
||||||
if (role == Qt::DisplayRole) {
|
if (role == Qt::DisplayRole) {
|
||||||
if (evt.isRedacted()) {
|
return eventHandler.getRichBody();
|
||||||
auto reason = evt.redactedBecause()->reason();
|
|
||||||
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>")
|
|
||||||
: i18n("<i>[This message was deleted: %1]</i>", evt.redactedBecause()->reason());
|
|
||||||
}
|
|
||||||
return EventHandler::richBody(m_currentRoom, &evt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == ContentModelRole) {
|
if (role == ContentModelRole) {
|
||||||
QString modelId;
|
if (!evt.isStateEvent() && !evt.id().isEmpty()) {
|
||||||
if (!evt.id().isEmpty() && m_contentModels.contains(evt.id())) {
|
return QVariant::fromValue<MessageContentModel *>(new MessageContentModel(m_currentRoom, &evt));
|
||||||
modelId = evt.id();
|
|
||||||
} else if (!evt.transactionId().isEmpty() && m_contentModels.contains(evt.transactionId())) {
|
|
||||||
modelId = evt.transactionId();
|
|
||||||
}
|
}
|
||||||
if (!modelId.isEmpty()) {
|
if (evt.isStateEvent()) {
|
||||||
return QVariant::fromValue<MessageContentModel *>(m_contentModels.at(modelId).get());
|
if (evt.matrixType() == QStringLiteral("org.matrix.msc3672.beacon_info")) {
|
||||||
|
return QVariant::fromValue<MessageContentModel *>(new MessageContentModel(m_currentRoom, &evt));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == GenericDisplayRole) {
|
if (role == GenericDisplayRole) {
|
||||||
return EventHandler::genericBody(&evt);
|
return eventHandler.getGenericBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == DelegateTypeRole) {
|
if (role == DelegateTypeRole) {
|
||||||
@@ -478,7 +473,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (role == HighlightRole) {
|
if (role == HighlightRole) {
|
||||||
return EventHandler::isHighlighted(m_currentRoom, &evt);
|
return eventHandler.isHighlighted();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == SpecialMarksRole) {
|
if (role == SpecialMarksRole) {
|
||||||
@@ -491,11 +486,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
return pendingIt->deliveryStatus();
|
return pendingIt->deliveryStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EventHandler::isHidden(m_currentRoom, &evt)) {
|
if (eventHandler.isHidden()) {
|
||||||
return EventStatus::Hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EventHandler::isThreaded(&evt) && EventHandler::threadRoot(&evt) != EventHandler::id(&evt) && NeoChatConfig::threads()) {
|
|
||||||
return EventStatus::Hidden;
|
return EventStatus::Hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,12 +494,16 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (role == EventIdRole) {
|
if (role == EventIdRole) {
|
||||||
return EventHandler::id(&evt);
|
return eventHandler.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == ProgressInfoRole) {
|
if (role == ProgressInfoRole) {
|
||||||
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
if (e->has<EventContent::FileContent>()) {
|
||||||
|
#else
|
||||||
if (e->hasFileContent()) {
|
if (e->hasFileContent()) {
|
||||||
|
#endif
|
||||||
return QVariant::fromValue(m_currentRoom->cachedFileTransferInfo(&evt));
|
return QVariant::fromValue(m_currentRoom->cachedFileTransferInfo(&evt));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -519,20 +514,20 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
|
|
||||||
if (role == TimeRole) {
|
if (role == TimeRole) {
|
||||||
auto lastUpdated = isPending ? pendingIt->lastUpdated() : QDateTime();
|
auto lastUpdated = isPending ? pendingIt->lastUpdated() : QDateTime();
|
||||||
return EventHandler::time(&evt, isPending, lastUpdated);
|
return eventHandler.getTime(isPending, lastUpdated);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == SectionRole) {
|
if (role == SectionRole) {
|
||||||
auto lastUpdated = isPending ? pendingIt->lastUpdated() : QDateTime();
|
auto lastUpdated = isPending ? pendingIt->lastUpdated() : QDateTime();
|
||||||
return EventHandler::timeString(&evt, true, QLocale::ShortFormat, isPending, lastUpdated);
|
return eventHandler.getTimeString(true, QLocale::ShortFormat, isPending, lastUpdated);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == IsThreadedRole) {
|
if (role == IsThreadedRole) {
|
||||||
return EventHandler::isThreaded(&evt);
|
return eventHandler.isThreaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == ThreadRootRole) {
|
if (role == ThreadRootRole) {
|
||||||
return EventHandler::threadRoot(&evt);
|
return eventHandler.threadRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == ShowSectionRole) {
|
if (role == ShowSectionRole) {
|
||||||
@@ -585,7 +580,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (role == AuthorDisplayNameRole) {
|
if (role == AuthorDisplayNameRole) {
|
||||||
return EventHandler::authorDisplayName(m_currentRoom, &evt, isPending);
|
return eventHandler.getAuthorDisplayName(isPending);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == IsRedactedRole) {
|
if (role == IsRedactedRole) {
|
||||||
@@ -597,11 +592,11 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (role == MediaInfoRole) {
|
if (role == MediaInfoRole) {
|
||||||
return EventHandler::mediaInfo(m_currentRoom, &evt);
|
return eventHandler.getMediaInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == IsEditableRole) {
|
if (role == IsEditableRole) {
|
||||||
return MessageComponentType::typeForEvent(evt) == MessageComponentType::Text && evt.senderId() == m_currentRoom->localMember().id();
|
return eventHandler.messageComponentType() == MessageComponentType::Text && evt.senderId() == m_currentRoom->localMember().id();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@@ -629,9 +624,6 @@ void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event)
|
|||||||
|
|
||||||
auto eventId = event->id();
|
auto eventId = event->id();
|
||||||
auto senderId = event->senderId();
|
auto senderId = event->senderId();
|
||||||
if (eventId.isEmpty()) {
|
|
||||||
eventId = event->transactionId();
|
|
||||||
}
|
|
||||||
// A pending event might not have a sender ID set yet but in that case it must
|
// A pending event might not have a sender ID set yet but in that case it must
|
||||||
// be the local member.
|
// be the local member.
|
||||||
if (senderId.isEmpty()) {
|
if (senderId.isEmpty()) {
|
||||||
@@ -642,16 +634,6 @@ void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event)
|
|||||||
m_memberObjects[senderId] = std::unique_ptr<NeochatRoomMember>(new NeochatRoomMember(m_currentRoom, senderId));
|
m_memberObjects[senderId] = std::unique_ptr<NeochatRoomMember>(new NeochatRoomMember(m_currentRoom, senderId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_contentModels.contains(eventId) && !m_contentModels.contains(event->transactionId())) {
|
|
||||||
if (!event->isStateEvent() || event->matrixType() == QStringLiteral("org.matrix.msc3672.beacon_info")) {
|
|
||||||
m_contentModels[eventId] = std::unique_ptr<MessageContentModel>(new MessageContentModel(m_currentRoom, event));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EventHandler::isThreaded(event) && !m_threadModels.contains(EventHandler::threadRoot(event))) {
|
|
||||||
m_threadModels[EventHandler::threadRoot(event)] = QSharedPointer<ThreadModel>(new ThreadModel(EventHandler::threadRoot(event), m_currentRoom));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadMarkerModel handles updates to add and remove markers, we only need to
|
// ReadMarkerModel handles updates to add and remove markers, we only need to
|
||||||
// handle adding and removing whole models here.
|
// handle adding and removing whole models here.
|
||||||
if (m_readMarkerModels.contains(eventId)) {
|
if (m_readMarkerModels.contains(eventId)) {
|
||||||
@@ -711,9 +693,4 @@ bool MessageEventModel::event(QEvent *event)
|
|||||||
return QObject::event(event);
|
return QObject::event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadModel *MessageEventModel::threadModelForRootId(const QString &threadRootId) const
|
|
||||||
{
|
|
||||||
return m_threadModels[threadRootId].data();
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "moc_messageeventmodel.cpp"
|
#include "moc_messageeventmodel.cpp"
|
||||||
|
|||||||
@@ -8,12 +8,10 @@
|
|||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
|
|
||||||
#include "linkpreviewer.h"
|
#include "linkpreviewer.h"
|
||||||
#include "messagecontentmodel.h"
|
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
#include "neochatroommember.h"
|
#include "neochatroommember.h"
|
||||||
#include "pollhandler.h"
|
#include "pollhandler.h"
|
||||||
#include "readmarkermodel.h"
|
#include "readmarkermodel.h"
|
||||||
#include "threadmodel.h"
|
|
||||||
|
|
||||||
class ReactionModel;
|
class ReactionModel;
|
||||||
|
|
||||||
@@ -56,8 +54,8 @@ public:
|
|||||||
|
|
||||||
ContentModelRole, /**< The MessageContentModel for the event. */
|
ContentModelRole, /**< The MessageContentModel for the event. */
|
||||||
|
|
||||||
IsThreadedRole, /**< Whether the message is in a thread. */
|
IsThreadedRole,
|
||||||
ThreadRootRole, /**< The Matrix ID of the thread root message, if any . */
|
ThreadRootRole,
|
||||||
|
|
||||||
ShowSectionRole, /**< Whether the section header should be shown. */
|
ShowSectionRole, /**< Whether the section header should be shown. */
|
||||||
|
|
||||||
@@ -106,8 +104,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_INVOKABLE [[nodiscard]] int eventIdToRow(const QString &eventID) const;
|
Q_INVOKABLE [[nodiscard]] int eventIdToRow(const QString &eventID) const;
|
||||||
|
|
||||||
Q_INVOKABLE ThreadModel *threadModelForRootId(const QString &threadRootId) const;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool event(QEvent *event) override;
|
bool event(QEvent *event) override;
|
||||||
|
|
||||||
@@ -121,9 +117,7 @@ private:
|
|||||||
KFormat m_format;
|
KFormat m_format;
|
||||||
|
|
||||||
std::map<QString, std::unique_ptr<NeochatRoomMember>> m_memberObjects;
|
std::map<QString, std::unique_ptr<NeochatRoomMember>> m_memberObjects;
|
||||||
std::map<QString, std::unique_ptr<MessageContentModel>> m_contentModels;
|
|
||||||
QMap<QString, QSharedPointer<ReadMarkerModel>> m_readMarkerModels;
|
QMap<QString, QSharedPointer<ReadMarkerModel>> m_readMarkerModels;
|
||||||
QMap<QString, QSharedPointer<ThreadModel>> m_threadModels;
|
|
||||||
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
||||||
|
|
||||||
[[nodiscard]] int timelineBaseIndex() const;
|
[[nodiscard]] int timelineBaseIndex() const;
|
||||||
|
|||||||
@@ -8,8 +8,10 @@
|
|||||||
|
|
||||||
#include "enums/delegatetype.h"
|
#include "enums/delegatetype.h"
|
||||||
#include "messagecontentmodel.h"
|
#include "messagecontentmodel.h"
|
||||||
|
#include "messageeventmodel.h"
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
#include "neochatroommember.h"
|
#include "neochatroommember.h"
|
||||||
|
#include "timelinemodel.h"
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
|
|||||||
@@ -121,11 +121,12 @@ void NotificationsModel::loadData()
|
|||||||
const auto &authorAvatar = avatar.isValid() && avatar.scheme() == QStringLiteral("mxc") ? avatar : QUrl();
|
const auto &authorAvatar = avatar.isValid() && avatar.scheme() == QStringLiteral("mxc") ? avatar : QUrl();
|
||||||
|
|
||||||
const auto &roomEvent = eventCast<const RoomEvent>(notification.event.get());
|
const auto &roomEvent = eventCast<const RoomEvent>(notification.event.get());
|
||||||
|
EventHandler eventHandler(dynamic_cast<NeoChatRoom *>(room), roomEvent);
|
||||||
beginInsertRows({}, m_notifications.length(), m_notifications.length());
|
beginInsertRows({}, m_notifications.length(), m_notifications.length());
|
||||||
m_notifications += Notification{
|
m_notifications += Notification{
|
||||||
.roomId = notification.roomId,
|
.roomId = notification.roomId,
|
||||||
.text = room->member(authorId).htmlSafeDisplayName() + (roomEvent->is<StateEvent>() ? QStringLiteral(" ") : QStringLiteral(": "))
|
.text = room->member(authorId).htmlSafeDisplayName() + (roomEvent->is<StateEvent>() ? QStringLiteral(" ") : QStringLiteral(": "))
|
||||||
+ EventHandler::plainBody(dynamic_cast<NeoChatRoom *>(room), roomEvent, true),
|
+ eventHandler.getPlainBody(true),
|
||||||
.authorName = room->member(authorId).htmlSafeDisplayName(),
|
.authorName = room->member(authorId).htmlSafeDisplayName(),
|
||||||
.authorAvatar = authorAvatar,
|
.authorAvatar = authorAvatar,
|
||||||
.eventId = roomEvent->id(),
|
.eventId = roomEvent->id(),
|
||||||
|
|||||||
@@ -2,40 +2,20 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
#include "permissionsmodel.h"
|
#include "permissionsmodel.h"
|
||||||
|
#include "powerlevel.h"
|
||||||
|
|
||||||
#include <Quotient/events/roompowerlevelsevent.h>
|
#include <Quotient/events/roompowerlevelsevent.h>
|
||||||
|
|
||||||
#include <KLazyLocalizedString>
|
#include <KLazyLocalizedString>
|
||||||
|
|
||||||
#include "powerlevel.h"
|
|
||||||
|
|
||||||
using namespace Qt::Literals::StringLiterals;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
constexpr auto UsersDefaultKey = "users_default"_L1;
|
|
||||||
constexpr auto StateDefaultKey = "state_default"_L1;
|
|
||||||
constexpr auto EventsDefaultKey = "events_default"_L1;
|
|
||||||
|
|
||||||
constexpr auto InviteKey = "invite"_L1;
|
|
||||||
constexpr auto KickKey = "kick"_L1;
|
|
||||||
constexpr auto BanKey = "ban"_L1;
|
|
||||||
constexpr auto RedactKey = "redact"_L1;
|
|
||||||
|
|
||||||
static const QStringList defaultPermissions = {
|
static const QStringList defaultPermissions = {
|
||||||
UsersDefaultKey,
|
QStringLiteral("users_default"),
|
||||||
StateDefaultKey,
|
QStringLiteral("state_default"),
|
||||||
EventsDefaultKey,
|
QStringLiteral("events_default"),
|
||||||
};
|
QStringLiteral("invite"),
|
||||||
|
QStringLiteral("kick"),
|
||||||
static const QStringList basicPermissions = {
|
QStringLiteral("ban"),
|
||||||
InviteKey,
|
QStringLiteral("redact"),
|
||||||
KickKey,
|
|
||||||
BanKey,
|
|
||||||
RedactKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const QStringList knownPermissions = {
|
|
||||||
QStringLiteral("m.reaction"),
|
QStringLiteral("m.reaction"),
|
||||||
QStringLiteral("m.room.redaction"),
|
QStringLiteral("m.room.redaction"),
|
||||||
QStringLiteral("m.room.power_levels"),
|
QStringLiteral("m.room.power_levels"),
|
||||||
@@ -53,14 +33,14 @@ static const QStringList knownPermissions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Alternate name text for default permissions.
|
// Alternate name text for default permissions.
|
||||||
static const QHash<QString, KLazyLocalizedString> permissionNames = {
|
static const QHash<QString, KLazyLocalizedString> defaultPermissionNames = {
|
||||||
{UsersDefaultKey, kli18nc("Room permission type", "Default user power level")},
|
{QStringLiteral("users_default"), kli18nc("Room permission type", "Default user power level")},
|
||||||
{StateDefaultKey, kli18nc("Room permission type", "Default power level to set the room state")},
|
{QStringLiteral("state_default"), kli18nc("Room permission type", "Default power level to set the room state")},
|
||||||
{EventsDefaultKey, kli18nc("Room permission type", "Default power level to send messages")},
|
{QStringLiteral("events_default"), kli18nc("Room permission type", "Default power level to send messages")},
|
||||||
{InviteKey, kli18nc("Room permission type", "Invite users")},
|
{QStringLiteral("invite"), kli18nc("Room permission type", "Invite users")},
|
||||||
{KickKey, kli18nc("Room permission type", "Kick users")},
|
{QStringLiteral("kick"), kli18nc("Room permission type", "Kick users")},
|
||||||
{BanKey, kli18nc("Room permission type", "Ban users")},
|
{QStringLiteral("ban"), kli18nc("Room permission type", "Ban users")},
|
||||||
{RedactKey, kli18nc("Room permission type", "Remove messages sent by other users")},
|
{QStringLiteral("redact"), kli18nc("Room permission type", "Remove messages sent by other users")},
|
||||||
{QStringLiteral("m.reaction"), kli18nc("Room permission type", "Send reactions")},
|
{QStringLiteral("m.reaction"), kli18nc("Room permission type", "Send reactions")},
|
||||||
{QStringLiteral("m.room.redaction"), kli18nc("Room permission type", "Remove their own messages")},
|
{QStringLiteral("m.room.redaction"), kli18nc("Room permission type", "Remove their own messages")},
|
||||||
{QStringLiteral("m.room.power_levels"), kli18nc("Room permission type", "Change user permissions")},
|
{QStringLiteral("m.room.power_levels"), kli18nc("Room permission type", "Change user permissions")},
|
||||||
@@ -78,10 +58,10 @@ static const QHash<QString, KLazyLocalizedString> permissionNames = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Subtitles for the default values.
|
// Subtitles for the default values.
|
||||||
static const QHash<QString, KLazyLocalizedString> permissionSubtitles = {
|
static const QHash<QString, KLazyLocalizedString> defaultSubtitles = {
|
||||||
{UsersDefaultKey, kli18nc("Room permission type", "This is the power level for all new users when joining the room")},
|
{QStringLiteral("users_default"), kli18nc("Room permission type", "This is the power level for all new users when joining the room")},
|
||||||
{StateDefaultKey, kli18nc("Room permission type", "This is used for all state events that do not have their own entry here")},
|
{QStringLiteral("state_default"), kli18nc("Room permission type", "This is used for all state events that do not have their own entry here")},
|
||||||
{EventsDefaultKey, kli18nc("Room permission type", "This is used for all message events that do not have their own entry here")},
|
{QStringLiteral("events_default"), kli18nc("Room permission type", "This is used for all message events that do not have their own entry here")},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Permissions that should use the event default.
|
// Permissions that should use the event default.
|
||||||
@@ -90,7 +70,6 @@ static const QStringList eventPermissions = {
|
|||||||
QStringLiteral("m.reaction"),
|
QStringLiteral("m.reaction"),
|
||||||
QStringLiteral("m.room.redaction"),
|
QStringLiteral("m.room.redaction"),
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
PermissionsModel::PermissionsModel(QObject *parent)
|
PermissionsModel::PermissionsModel(QObject *parent)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
@@ -130,8 +109,6 @@ void PermissionsModel::initializeModel()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_permissions.append(defaultPermissions);
|
m_permissions.append(defaultPermissions);
|
||||||
m_permissions.append(basicPermissions);
|
|
||||||
m_permissions.append(knownPermissions);
|
|
||||||
|
|
||||||
for (const auto &event : currentPowerLevelEvent->events().keys()) {
|
for (const auto &event : currentPowerLevelEvent->events().keys()) {
|
||||||
if (!m_permissions.contains(event)) {
|
if (!m_permissions.contains(event)) {
|
||||||
@@ -154,17 +131,17 @@ QVariant PermissionsModel::data(const QModelIndex &index, int role) const
|
|||||||
|
|
||||||
const auto permission = m_permissions.value(index.row());
|
const auto permission = m_permissions.value(index.row());
|
||||||
if (role == NameRole) {
|
if (role == NameRole) {
|
||||||
if (permissionNames.keys().contains(permission)) {
|
if (defaultPermissionNames.keys().contains(permission)) {
|
||||||
return permissionNames.value(permission).toString();
|
return defaultPermissionNames.value(permission).toString();
|
||||||
}
|
}
|
||||||
return permission;
|
return permission;
|
||||||
}
|
}
|
||||||
if (role == SubtitleRole) {
|
if (role == SubtitleRole) {
|
||||||
if (knownPermissions.contains(permission) && permissionNames.keys().contains(permission)) {
|
if (permission.startsWith(QLatin1String("m.")) && defaultPermissionNames.keys().contains(permission)) {
|
||||||
return permission;
|
return permission;
|
||||||
}
|
}
|
||||||
if (permissionSubtitles.contains(permission)) {
|
if (defaultSubtitles.contains(permission)) {
|
||||||
return permissionSubtitles.value(permission).toString();
|
return defaultSubtitles.value(permission).toString();
|
||||||
}
|
}
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
@@ -189,10 +166,11 @@ QVariant PermissionsModel::data(const QModelIndex &index, int role) const
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
if (role == IsDefaultValueRole) {
|
if (role == IsDefaultValueRole) {
|
||||||
return defaultPermissions.contains(permission);
|
return permission.contains(QLatin1String("default"));
|
||||||
}
|
}
|
||||||
if (role == IsBasicPermissionRole) {
|
if (role == IsBasicPermissionRole) {
|
||||||
return basicPermissions.contains(permission);
|
return permission == QStringLiteral("invite") || permission == QStringLiteral("kick") || permission == QStringLiteral("ban")
|
||||||
|
|| permission == QStringLiteral("redact");
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -223,19 +201,19 @@ std::optional<int> PermissionsModel::powerLevel(const QString &permission) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (const auto currentPowerLevelEvent = m_room->currentState().get<Quotient::RoomPowerLevelsEvent>()) {
|
if (const auto currentPowerLevelEvent = m_room->currentState().get<Quotient::RoomPowerLevelsEvent>()) {
|
||||||
if (permission == BanKey) {
|
if (permission == QStringLiteral("ban")) {
|
||||||
return currentPowerLevelEvent->ban();
|
return currentPowerLevelEvent->ban();
|
||||||
} else if (permission == KickKey) {
|
} else if (permission == QStringLiteral("kick")) {
|
||||||
return currentPowerLevelEvent->kick();
|
return currentPowerLevelEvent->kick();
|
||||||
} else if (permission == InviteKey) {
|
} else if (permission == QStringLiteral("invite")) {
|
||||||
return currentPowerLevelEvent->invite();
|
return currentPowerLevelEvent->invite();
|
||||||
} else if (permission == RedactKey) {
|
} else if (permission == QStringLiteral("redact")) {
|
||||||
return currentPowerLevelEvent->redact();
|
return currentPowerLevelEvent->redact();
|
||||||
} else if (permission == UsersDefaultKey) {
|
} else if (permission == QStringLiteral("users_default")) {
|
||||||
return currentPowerLevelEvent->usersDefault();
|
return currentPowerLevelEvent->usersDefault();
|
||||||
} else if (permission == StateDefaultKey) {
|
} else if (permission == QStringLiteral("state_default")) {
|
||||||
return currentPowerLevelEvent->stateDefault();
|
return currentPowerLevelEvent->stateDefault();
|
||||||
} else if (permission == EventsDefaultKey) {
|
} else if (permission == QStringLiteral("events_default")) {
|
||||||
return currentPowerLevelEvent->eventsDefault();
|
return currentPowerLevelEvent->eventsDefault();
|
||||||
} else if (eventPermissions.contains(permission)) {
|
} else if (eventPermissions.contains(permission)) {
|
||||||
return currentPowerLevelEvent->powerLevelForEvent(permission);
|
return currentPowerLevelEvent->powerLevelForEvent(permission);
|
||||||
@@ -263,16 +241,13 @@ void PermissionsModel::setPowerLevel(const QString &permission, const int &newPo
|
|||||||
auto powerLevelContent = currentPowerLevelEvent->contentJson();
|
auto powerLevelContent = currentPowerLevelEvent->contentJson();
|
||||||
if (powerLevelContent.contains(permission)) {
|
if (powerLevelContent.contains(permission)) {
|
||||||
powerLevelContent[permission] = clampPowerLevel;
|
powerLevelContent[permission] = clampPowerLevel;
|
||||||
// Deal with the case where a default or basic permission is missing from the event content erroneously.
|
|
||||||
} else if (defaultPermissions.contains(permission) || basicPermissions.contains(permission)) {
|
|
||||||
powerLevelContent[permission] = clampPowerLevel;
|
|
||||||
} else {
|
} else {
|
||||||
auto eventPowerLevels = powerLevelContent[QLatin1String("events")].toObject();
|
auto eventPowerLevels = powerLevelContent[QLatin1String("events")].toObject();
|
||||||
eventPowerLevels[permission] = clampPowerLevel;
|
eventPowerLevels[permission] = clampPowerLevel;
|
||||||
powerLevelContent[QLatin1String("events")] = eventPowerLevels;
|
powerLevelContent[QLatin1String("events")] = eventPowerLevels;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_room->setState<Quotient::RoomPowerLevelsEvent>(powerLevelContent);
|
m_room->setState<Quotient::RoomPowerLevelsEvent>(Quotient::fromJson<Quotient::PowerLevelsEventContent>(powerLevelContent));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <Quotient/jobs/basejob.h>
|
#include <Quotient/jobs/basejob.h>
|
||||||
|
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
|
#include "neochatconnection.h"
|
||||||
|
|
||||||
#include <KLazyLocalizedString>
|
#include <KLazyLocalizedString>
|
||||||
|
|
||||||
@@ -311,8 +312,12 @@ void PushRuleModel::addKeyword(const QString &keyword, const QString &roomId)
|
|||||||
pushConditions.append(keywordCondition);
|
pushConditions.append(keywordCondition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
auto job = m_connection->callApi<Quotient::SetPushRuleJob>(PushRuleKind::kindString(kind),
|
||||||
|
#else
|
||||||
auto job = m_connection->callApi<Quotient::SetPushRuleJob>(QLatin1String("global"),
|
auto job = m_connection->callApi<Quotient::SetPushRuleJob>(QLatin1String("global"),
|
||||||
PushRuleKind::kindString(kind),
|
PushRuleKind::kindString(kind),
|
||||||
|
#endif
|
||||||
keyword,
|
keyword,
|
||||||
actions,
|
actions,
|
||||||
QString(),
|
QString(),
|
||||||
@@ -337,7 +342,11 @@ void PushRuleModel::removeKeyword(const QString &keyword)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto kind = PushRuleKind::kindString(m_rules[index].kind);
|
auto kind = PushRuleKind::kindString(m_rules[index].kind);
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(kind, m_rules[index].id);
|
||||||
|
#else
|
||||||
auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(QStringLiteral("global"), kind, m_rules[index].id);
|
auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(QStringLiteral("global"), kind, m_rules[index].id);
|
||||||
|
#endif
|
||||||
connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
|
connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
|
||||||
qWarning() << QLatin1String("Unable to remove push rule for keyword %1: ").arg(m_rules[index].id) << job->errorString();
|
qWarning() << QLatin1String("Unable to remove push rule for keyword %1: ").arg(m_rules[index].id) << job->errorString();
|
||||||
});
|
});
|
||||||
@@ -345,10 +354,18 @@ void PushRuleModel::removeKeyword(const QString &keyword)
|
|||||||
|
|
||||||
void PushRuleModel::setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled)
|
void PushRuleModel::setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled)
|
||||||
{
|
{
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(kind, ruleId);
|
||||||
|
#else
|
||||||
auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(QStringLiteral("global"), kind, ruleId);
|
auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(QStringLiteral("global"), kind, ruleId);
|
||||||
|
#endif
|
||||||
connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled, this]() {
|
connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled, this]() {
|
||||||
if (job->enabled() != enabled) {
|
if (job->enabled() != enabled) {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
m_connection->callApi<Quotient::SetPushRuleEnabledJob>(kind, ruleId, enabled);
|
||||||
|
#else
|
||||||
m_connection->callApi<Quotient::SetPushRuleEnabledJob>(QStringLiteral("global"), kind, ruleId, enabled);
|
m_connection->callApi<Quotient::SetPushRuleEnabledJob>(QStringLiteral("global"), kind, ruleId, enabled);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -362,7 +379,11 @@ void PushRuleModel::setNotificationRuleActions(const QString &kind, const QStrin
|
|||||||
actions = actionToVariant(action);
|
actions = actionToVariant(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
m_connection->callApi<Quotient::SetPushRuleActionsJob>(kind, ruleId, actions);
|
||||||
|
#else
|
||||||
m_connection->callApi<Quotient::SetPushRuleActionsJob>(QStringLiteral("global"), kind, ruleId, actions);
|
m_connection->callApi<Quotient::SetPushRuleActionsJob>(QStringLiteral("global"), kind, ruleId, actions);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
PushRuleAction::Action PushRuleModel::variantToAction(const QList<QVariant> &actions, bool enabled)
|
PushRuleAction::Action PushRuleModel::variantToAction(const QList<QVariant> &actions, bool enabled)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
#include "reactionmodel.h"
|
#include "reactionmodel.h"
|
||||||
|
#include "neochatroom.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
@@ -9,6 +10,8 @@
|
|||||||
|
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
|
|
||||||
|
#include <Quotient/roommember.h>
|
||||||
|
|
||||||
ReactionModel::ReactionModel(const Quotient::RoomMessageEvent *event, NeoChatRoom *room)
|
ReactionModel::ReactionModel(const Quotient::RoomMessageEvent *event, NeoChatRoom *room)
|
||||||
: QAbstractListModel(nullptr)
|
: QAbstractListModel(nullptr)
|
||||||
, m_room(room)
|
, m_room(room)
|
||||||
|
|||||||
@@ -245,7 +245,8 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
|
|||||||
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
|
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
return EventHandler::subtitleText(room, room->lastEvent());
|
EventHandler eventHandler(room, room->lastEvent());
|
||||||
|
return eventHandler.subtitleText();
|
||||||
}
|
}
|
||||||
if (role == AvatarImageRole) {
|
if (role == AvatarImageRole) {
|
||||||
return room->avatar(128);
|
return room->avatar(128);
|
||||||
|
|||||||
@@ -357,7 +357,8 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
|||||||
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
|
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
return EventHandler::subtitleText(room, room->lastEvent());
|
EventHandler eventHandler(room, room->lastEvent());
|
||||||
|
return eventHandler.subtitleText();
|
||||||
}
|
}
|
||||||
if (role == AvatarImageRole) {
|
if (role == AvatarImageRole) {
|
||||||
return room->avatar(128);
|
return room->avatar(128);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "eventhandler.h"
|
#include "eventhandler.h"
|
||||||
#include "models/messagecontentmodel.h"
|
#include "models/messagecontentmodel.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
|
#include "neochatroommember.h"
|
||||||
|
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
|
|
||||||
@@ -89,6 +90,8 @@ QVariant SearchModel::data(const QModelIndex &index, int role) const
|
|||||||
auto row = index.row();
|
auto row = index.row();
|
||||||
const auto &event = *m_result->results[row].result;
|
const auto &event = *m_result->results[row].result;
|
||||||
|
|
||||||
|
EventHandler eventHandler(m_room, &event);
|
||||||
|
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case AuthorRole:
|
case AuthorRole:
|
||||||
return QVariant::fromValue<NeochatRoomMember *>(m_memberObjects.at(event.senderId()).get());
|
return QVariant::fromValue<NeochatRoomMember *>(m_memberObjects.at(event.senderId()).get());
|
||||||
@@ -98,7 +101,7 @@ QVariant SearchModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
return event.originTimestamp().date() != m_result->results[row - 1].result->originTimestamp().date();
|
return event.originTimestamp().date() != m_result->results[row - 1].result->originTimestamp().date();
|
||||||
case SectionRole:
|
case SectionRole:
|
||||||
return EventHandler::timeString(&event, true);
|
return eventHandler.getTimeString(true);
|
||||||
case ShowReactionsRole:
|
case ShowReactionsRole:
|
||||||
return false;
|
return false;
|
||||||
case ShowReadMarkersRole:
|
case ShowReadMarkersRole:
|
||||||
@@ -106,13 +109,13 @@ QVariant SearchModel::data(const QModelIndex &index, int role) const
|
|||||||
case IsPendingRole:
|
case IsPendingRole:
|
||||||
return false;
|
return false;
|
||||||
case HighlightRole:
|
case HighlightRole:
|
||||||
return EventHandler::isHighlighted(m_room, &event);
|
return eventHandler.isHighlighted();
|
||||||
case EventIdRole:
|
case EventIdRole:
|
||||||
return EventHandler::id(&event);
|
return eventHandler.getId();
|
||||||
case IsThreadedRole:
|
case IsThreadedRole:
|
||||||
return EventHandler::isThreaded(&event);
|
return eventHandler.isThreaded();
|
||||||
case ThreadRootRole:
|
case ThreadRootRole:
|
||||||
return EventHandler::threadRoot(&event);
|
return eventHandler.threadRoot();
|
||||||
case ContentModelRole: {
|
case ContentModelRole: {
|
||||||
if (!event.isStateEvent()) {
|
if (!event.isStateEvent()) {
|
||||||
return QVariant::fromValue<MessageContentModel *>(new MessageContentModel(m_room, &event));
|
return QVariant::fromValue<MessageContentModel *>(new MessageContentModel(m_room, &event));
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <Quotient/room.h>
|
#include <Quotient/room.h>
|
||||||
|
|
||||||
#include "neochatconnection.h"
|
#include "neochatconnection.h"
|
||||||
|
#include "neochatroom.h"
|
||||||
|
|
||||||
SpaceChildrenModel::SpaceChildrenModel(QObject *parent)
|
SpaceChildrenModel::SpaceChildrenModel(QObject *parent)
|
||||||
: QAbstractItemModel(parent)
|
: QAbstractItemModel(parent)
|
||||||
|
|||||||
@@ -1,190 +0,0 @@
|
|||||||
// 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 "threadmodel.h"
|
|
||||||
|
|
||||||
#include <Quotient/events/event.h>
|
|
||||||
#include <Quotient/events/stickerevent.h>
|
|
||||||
#include <Quotient/jobs/basejob.h>
|
|
||||||
#include <Quotient/omittable.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "chatbarcache.h"
|
|
||||||
#include "eventhandler.h"
|
|
||||||
#include "messagecomponenttype.h"
|
|
||||||
#include "neochatroom.h"
|
|
||||||
|
|
||||||
ThreadModel::ThreadModel(const QString &threadRootId, NeoChatRoom *room)
|
|
||||||
: QConcatenateTablesProxyModel(room)
|
|
||||||
, m_threadRootId(threadRootId)
|
|
||||||
, m_threadChatBarModel(new ThreadChatBarModel(this, room))
|
|
||||||
{
|
|
||||||
Q_ASSERT(!m_threadRootId.isEmpty());
|
|
||||||
Q_ASSERT(room);
|
|
||||||
|
|
||||||
m_threadRootContentModel = std::unique_ptr<MessageContentModel>(new MessageContentModel(room, threadRootId));
|
|
||||||
|
|
||||||
connect(room, &Quotient::Room::pendingEventAboutToAdd, this, [this](Quotient::RoomEvent *event) {
|
|
||||||
if (auto roomEvent = eventCast<const Quotient::RoomMessageEvent>(event)) {
|
|
||||||
if (EventHandler::isThreaded(roomEvent) && EventHandler::threadRoot(roomEvent) == m_threadRootId) {
|
|
||||||
addNewEvent(event);
|
|
||||||
addModels();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
connect(room, &Quotient::Room::aboutToAddNewMessages, this, [this](Quotient::RoomEventsRange events) {
|
|
||||||
for (const auto &event : events) {
|
|
||||||
if (auto roomEvent = eventCast<const Quotient::RoomMessageEvent>(event)) {
|
|
||||||
if (EventHandler::isThreaded(roomEvent) && EventHandler::threadRoot(roomEvent) == m_threadRootId) {
|
|
||||||
addNewEvent(roomEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addModels();
|
|
||||||
});
|
|
||||||
|
|
||||||
fetchMore({});
|
|
||||||
addModels();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString ThreadModel::threadRootId() const
|
|
||||||
{
|
|
||||||
return m_threadRootId;
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageContentModel *ThreadModel::threadRootContentModel() const
|
|
||||||
{
|
|
||||||
return m_threadRootContentModel.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> ThreadModel::roleNames() const
|
|
||||||
{
|
|
||||||
return m_threadRootContentModel->roleNames();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ThreadModel::canFetchMore(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(parent);
|
|
||||||
return !m_currentJob && m_nextBatch.has_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadModel::fetchMore(const QModelIndex &parent)
|
|
||||||
{
|
|
||||||
Q_UNUSED(parent);
|
|
||||||
if (!m_currentJob && m_nextBatch.has_value()) {
|
|
||||||
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent());
|
|
||||||
const auto connection = room->connection();
|
|
||||||
m_currentJob =
|
|
||||||
connection->callApi<Quotient::GetRelatingEventsWithRelTypeJob>(room->id(), m_threadRootId, QLatin1String("m.thread"), *m_nextBatch, QString(), 5);
|
|
||||||
connect(m_currentJob, &Quotient::BaseJob::success, this, [this]() {
|
|
||||||
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent());
|
|
||||||
auto newEvents = m_currentJob->chunk();
|
|
||||||
for (auto &event : newEvents) {
|
|
||||||
m_contentModels.push_back(new MessageContentModel(room, event.get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
addModels();
|
|
||||||
|
|
||||||
const auto newNextBatch = m_currentJob->nextBatch();
|
|
||||||
if (!newNextBatch.isEmpty() && *m_nextBatch != newNextBatch) {
|
|
||||||
*m_nextBatch = newNextBatch;
|
|
||||||
} else {
|
|
||||||
// Insert the thread root at the end.
|
|
||||||
beginInsertRows({}, rowCount(), rowCount());
|
|
||||||
endInsertRows();
|
|
||||||
m_nextBatch.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_currentJob.clear();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadModel::addNewEvent(const Quotient::RoomEvent *event)
|
|
||||||
{
|
|
||||||
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent());
|
|
||||||
m_contentModels.push_front(new MessageContentModel(room, event));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadModel::addModels()
|
|
||||||
{
|
|
||||||
if (!sourceModels().isEmpty()) {
|
|
||||||
clearModels();
|
|
||||||
}
|
|
||||||
|
|
||||||
addSourceModel(m_threadRootContentModel.get());
|
|
||||||
for (auto it = m_contentModels.crbegin(); it != m_contentModels.crend(); ++it) {
|
|
||||||
addSourceModel(*it);
|
|
||||||
}
|
|
||||||
addSourceModel(m_threadChatBarModel);
|
|
||||||
|
|
||||||
beginResetModel();
|
|
||||||
endResetModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadModel::clearModels()
|
|
||||||
{
|
|
||||||
removeSourceModel(m_threadRootContentModel.get());
|
|
||||||
for (const auto &model : m_contentModels) {
|
|
||||||
if (sourceModels().contains(model)) {
|
|
||||||
removeSourceModel(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
removeSourceModel(m_threadChatBarModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadChatBarModel::ThreadChatBarModel(QObject *parent, NeoChatRoom *room)
|
|
||||||
: QAbstractListModel(parent)
|
|
||||||
, m_room(room)
|
|
||||||
{
|
|
||||||
if (m_room != nullptr) {
|
|
||||||
connect(m_room->threadCache(), &ChatBarCache::threadIdChanged, this, [this](const QString &oldThreadId, const QString &newThreadId) {
|
|
||||||
const auto threadModel = dynamic_cast<ThreadModel *>(this->parent());
|
|
||||||
if (threadModel != nullptr && (oldThreadId == threadModel->threadRootId() || newThreadId == threadModel->threadRootId())) {
|
|
||||||
beginResetModel();
|
|
||||||
endResetModel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ThreadChatBarModel::data(const QModelIndex &idx, int role) const
|
|
||||||
{
|
|
||||||
if (idx.row() > 1) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (role == ComponentTypeRole) {
|
|
||||||
return MessageComponentType::ChatBar;
|
|
||||||
}
|
|
||||||
if (role == ChatBarCacheRole) {
|
|
||||||
if (m_room == nullptr) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return QVariant::fromValue<ChatBarCache *>(m_room->threadCache());
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
int ThreadChatBarModel::rowCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(parent)
|
|
||||||
if (m_room == nullptr) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const auto threadModel = dynamic_cast<ThreadModel *>(this->parent());
|
|
||||||
if (threadModel != nullptr) {
|
|
||||||
return m_room->threadCache()->threadId() == threadModel->threadRootId() ? 1 : 0;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> ThreadChatBarModel::roleNames() const
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
{ComponentTypeRole, "componentType"},
|
|
||||||
{ChatBarCacheRole, "chatBarCache"},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "moc_threadmodel.cpp"
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
// 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 <Quotient/util.h>
|
|
||||||
|
|
||||||
#include <QConcatenateTablesProxyModel>
|
|
||||||
#include <QQmlEngine>
|
|
||||||
|
|
||||||
#include <QPointer>
|
|
||||||
#include <Quotient/csapi/relations.h>
|
|
||||||
#include <Quotient/events/roomevent.h>
|
|
||||||
#include <Quotient/events/roommessageevent.h>
|
|
||||||
#include <deque>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include "linkpreviewer.h"
|
|
||||||
#include "messagecontentmodel.h"
|
|
||||||
|
|
||||||
class NeoChatRoom;
|
|
||||||
class ReactionModel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class ThreadChatBarModel
|
|
||||||
*
|
|
||||||
* A model to provide a chat bar component to send new messages in a thread.
|
|
||||||
*/
|
|
||||||
class ThreadChatBarModel : public QAbstractListModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Defines the model roles.
|
|
||||||
*
|
|
||||||
* The role values need to match MessageContentModel not to blow up.
|
|
||||||
*
|
|
||||||
* @sa MessageContentModel
|
|
||||||
*/
|
|
||||||
enum Roles {
|
|
||||||
ComponentTypeRole = MessageContentModel::ComponentTypeRole, /**< The type of component to visualise the message. */
|
|
||||||
ChatBarCacheRole = MessageContentModel::ChatBarCacheRole, /**< The ChatBarCache to use. */
|
|
||||||
};
|
|
||||||
Q_ENUM(Roles)
|
|
||||||
|
|
||||||
explicit ThreadChatBarModel(QObject *parent, NeoChatRoom *room);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the given role value at the given index.
|
|
||||||
*
|
|
||||||
* @sa QAbstractItemModel::data
|
|
||||||
*/
|
|
||||||
[[nodiscard]] QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 1 or 0, depending on whether a chat bar should be shown.
|
|
||||||
*
|
|
||||||
* @sa QAbstractItemModel::rowCount
|
|
||||||
*/
|
|
||||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns a map with ComponentTypeRole it's the only one.
|
|
||||||
*
|
|
||||||
* @sa Roles, QAbstractItemModel::roleNames()
|
|
||||||
*/
|
|
||||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QPointer<NeoChatRoom> m_room;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class ThreadModel
|
|
||||||
*
|
|
||||||
* This class defines the model for visualising a thread.
|
|
||||||
*
|
|
||||||
* The class also provides functions to access the data of the root event, typically
|
|
||||||
* used to visualise the thread in a list of room threads.
|
|
||||||
*/
|
|
||||||
class ThreadModel : public QConcatenateTablesProxyModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
QML_ELEMENT
|
|
||||||
QML_UNCREATABLE("")
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ThreadModel(const QString &threadRootId, NeoChatRoom *room);
|
|
||||||
|
|
||||||
QString threadRootId() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The content model for the thread root event.
|
|
||||||
*/
|
|
||||||
MessageContentModel *threadRootContentModel() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns a mapping from Role enum values to role names.
|
|
||||||
*
|
|
||||||
* @sa Roles, QAbstractItemModel::roleNames()
|
|
||||||
*/
|
|
||||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Whether there is more data available for the model to fetch.
|
|
||||||
*
|
|
||||||
* @sa QAbstractItemModel::canFetchMore()
|
|
||||||
*/
|
|
||||||
bool canFetchMore(const QModelIndex &parent) const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Fetches the next batch of model data if any is available.
|
|
||||||
*
|
|
||||||
* @sa QAbstractItemModel::fetchMore()
|
|
||||||
*/
|
|
||||||
void fetchMore(const QModelIndex &parent) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString m_threadRootId;
|
|
||||||
|
|
||||||
std::unique_ptr<MessageContentModel> m_threadRootContentModel;
|
|
||||||
|
|
||||||
std::deque<MessageContentModel *> m_contentModels;
|
|
||||||
ThreadChatBarModel *m_threadChatBarModel;
|
|
||||||
|
|
||||||
QList<QString> m_events;
|
|
||||||
QList<QString> m_pendingEvents;
|
|
||||||
|
|
||||||
std::unordered_map<QString, std::unique_ptr<Quotient::RoomEvent>> m_unloadedEvents;
|
|
||||||
|
|
||||||
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
|
||||||
|
|
||||||
QPointer<Quotient::GetRelatingEventsWithRelTypeJob> m_currentJob = nullptr;
|
|
||||||
std::optional<QString> m_nextBatch = QString();
|
|
||||||
bool m_addingPending = false;
|
|
||||||
|
|
||||||
void addNewEvent(const Quotient::RoomEvent *event);
|
|
||||||
void addModels();
|
|
||||||
void clearModels();
|
|
||||||
};
|
|
||||||
@@ -3,15 +3,11 @@
|
|||||||
|
|
||||||
#include "timelinemodel.h"
|
#include "timelinemodel.h"
|
||||||
|
|
||||||
#include <Quotient/qt_connection_util.h>
|
|
||||||
|
|
||||||
#include "delegatetype.h"
|
#include "delegatetype.h"
|
||||||
|
|
||||||
TimelineModel::TimelineModel(QObject *parent)
|
TimelineModel::TimelineModel(QObject *parent)
|
||||||
: QConcatenateTablesProxyModel(parent)
|
: QConcatenateTablesProxyModel(parent)
|
||||||
{
|
{
|
||||||
m_timelineBeginningModel = new TimelineBeginningModel(this);
|
|
||||||
addSourceModel(m_timelineBeginningModel);
|
|
||||||
m_messageEventModel = new MessageEventModel(this);
|
m_messageEventModel = new MessageEventModel(this);
|
||||||
addSourceModel(m_messageEventModel);
|
addSourceModel(m_messageEventModel);
|
||||||
m_timelineEndModel = new TimelineEndModel(this);
|
m_timelineEndModel = new TimelineEndModel(this);
|
||||||
@@ -27,7 +23,6 @@ void TimelineModel::setRoom(NeoChatRoom *room)
|
|||||||
{
|
{
|
||||||
// Both models do their own null checking so just pass along.
|
// Both models do their own null checking so just pass along.
|
||||||
m_messageEventModel->setRoom(room);
|
m_messageEventModel->setRoom(room);
|
||||||
m_timelineBeginningModel->setRoom(room);
|
|
||||||
m_timelineEndModel->setRoom(room);
|
m_timelineEndModel->setRoom(room);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,69 +36,6 @@ QHash<int, QByteArray> TimelineModel::roleNames() const
|
|||||||
return m_messageEventModel->roleNames();
|
return m_messageEventModel->roleNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineBeginningModel::TimelineBeginningModel(QObject *parent)
|
|
||||||
: QAbstractListModel(parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void TimelineBeginningModel::setRoom(NeoChatRoom *room)
|
|
||||||
{
|
|
||||||
if (room == m_room) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
beginResetModel();
|
|
||||||
|
|
||||||
if (m_room != nullptr) {
|
|
||||||
m_room->disconnect(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_room = room;
|
|
||||||
|
|
||||||
if (m_room != nullptr) {
|
|
||||||
Quotient::connectUntil(m_room.get(), &Quotient::Room::eventsHistoryJobChanged, this, [this]() {
|
|
||||||
if (m_room && m_room->allHistoryLoaded()) {
|
|
||||||
// HACK: We have to do it this way because DelegateChooser doesn't update dynamically.
|
|
||||||
beginRemoveRows({}, 0, 0);
|
|
||||||
endRemoveRows();
|
|
||||||
beginInsertRows({}, 0, 0);
|
|
||||||
endInsertRows();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
endResetModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant TimelineBeginningModel::data(const QModelIndex &idx, int role) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(idx)
|
|
||||||
if (m_room == nullptr) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (role == DelegateTypeRole) {
|
|
||||||
return DelegateType::Successor;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
int TimelineBeginningModel::rowCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(parent)
|
|
||||||
if (m_room == nullptr) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return m_room->successorId().isEmpty() ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> TimelineBeginningModel::roleNames() const
|
|
||||||
{
|
|
||||||
return {{DelegateTypeRole, "delegateType"}};
|
|
||||||
}
|
|
||||||
|
|
||||||
TimelineEndModel::TimelineEndModel(QObject *parent)
|
TimelineEndModel::TimelineEndModel(QObject *parent)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
{
|
{
|
||||||
@@ -146,11 +78,7 @@ QVariant TimelineEndModel::data(const QModelIndex &idx, int role) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (role == DelegateTypeRole) {
|
if (role == DelegateTypeRole) {
|
||||||
if (idx.row() == 1 || rowCount() == 1) {
|
return m_room->allHistoryLoaded() ? DelegateType::TimelineEnd : DelegateType::Loading;
|
||||||
return m_room->allHistoryLoaded() ? DelegateType::TimelineEnd : DelegateType::Loading;
|
|
||||||
} else {
|
|
||||||
return DelegateType::Predecessor;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -158,10 +86,7 @@ QVariant TimelineEndModel::data(const QModelIndex &idx, int role) const
|
|||||||
int TimelineEndModel::rowCount(const QModelIndex &parent) const
|
int TimelineEndModel::rowCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(parent)
|
Q_UNUSED(parent)
|
||||||
if (m_room == nullptr) {
|
return 1;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return m_room->predecessorId().isEmpty() ? 1 : (m_room->allHistoryLoaded() ? 2 : 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> TimelineEndModel::roleNames() const
|
QHash<int, QByteArray> TimelineEndModel::roleNames() const
|
||||||
|
|||||||
@@ -10,57 +10,6 @@
|
|||||||
#include "messageeventmodel.h"
|
#include "messageeventmodel.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* @class TimelineBeginningModel
|
|
||||||
*
|
|
||||||
* A model to provide a delegate at the start of the timeline to show upgrades.
|
|
||||||
*/
|
|
||||||
class TimelineBeginningModel : public QAbstractListModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
QML_ELEMENT
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Defines the model roles.
|
|
||||||
*/
|
|
||||||
enum Roles {
|
|
||||||
DelegateTypeRole = MessageEventModel::DelegateTypeRole, /**< The delegate type of the message. */
|
|
||||||
};
|
|
||||||
Q_ENUM(Roles)
|
|
||||||
|
|
||||||
explicit TimelineBeginningModel(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the room for the timeline.
|
|
||||||
*/
|
|
||||||
void setRoom(NeoChatRoom *room);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the given role value at the given index.
|
|
||||||
*
|
|
||||||
* @sa QAbstractItemModel::data
|
|
||||||
*/
|
|
||||||
[[nodiscard]] QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 1, the answer is always 1.
|
|
||||||
*
|
|
||||||
* @sa QAbstractItemModel::rowCount
|
|
||||||
*/
|
|
||||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns a map with DelegateTypeRole it's the only one.
|
|
||||||
*
|
|
||||||
* @sa Roles, QAbstractItemModel::roleNames()
|
|
||||||
*/
|
|
||||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QPointer<NeoChatRoom> m_room = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class TimelineEndModel
|
* @class TimelineEndModel
|
||||||
*
|
*
|
||||||
@@ -159,6 +108,5 @@ Q_SIGNALS:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
MessageEventModel *m_messageEventModel = nullptr;
|
MessageEventModel *m_messageEventModel = nullptr;
|
||||||
TimelineBeginningModel *m_timelineBeginningModel = nullptr;
|
|
||||||
TimelineEndModel *m_timelineEndModel = nullptr;
|
TimelineEndModel *m_timelineEndModel = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
bool UserFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
bool UserFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(sourceParent);
|
Q_UNUSED(sourceParent);
|
||||||
if (m_filterText.length() < 1) {
|
if (!m_allowEmpty && m_filterText.length() < 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return sourceModel()->data(sourceModel()->index(sourceRow, 0), UserListModel::DisplayNameRole).toString().contains(m_filterText, Qt::CaseInsensitive)
|
return sourceModel()->data(sourceModel()->index(sourceRow, 0), UserListModel::DisplayNameRole).toString().contains(m_filterText, Qt::CaseInsensitive)
|
||||||
@@ -27,4 +27,15 @@ void UserFilterModel::setFilterText(const QString &filterText)
|
|||||||
invalidateFilter();
|
invalidateFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UserFilterModel::allowEmpty() const
|
||||||
|
{
|
||||||
|
return m_allowEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserFilterModel::setAllowEmpty(bool allowEmpty)
|
||||||
|
{
|
||||||
|
m_allowEmpty = allowEmpty;
|
||||||
|
Q_EMIT allowEmptyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_userfiltermodel.cpp"
|
#include "moc_userfiltermodel.cpp"
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class UserFilterModel : public QSortFilterProxyModel
|
|||||||
* The text is either a desired display name or matrix id.
|
* The text is either a desired display name or matrix id.
|
||||||
*/
|
*/
|
||||||
Q_PROPERTY(QString filterText READ filterText WRITE setFilterText NOTIFY filterTextChanged)
|
Q_PROPERTY(QString filterText READ filterText WRITE setFilterText NOTIFY filterTextChanged)
|
||||||
|
Q_PROPERTY(bool allowEmpty READ allowEmpty WRITE setAllowEmpty NOTIFY allowEmptyChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@@ -36,9 +37,14 @@ public:
|
|||||||
QString filterText() const;
|
QString filterText() const;
|
||||||
void setFilterText(const QString &filterText);
|
void setFilterText(const QString &filterText);
|
||||||
|
|
||||||
|
bool allowEmpty() const;
|
||||||
|
void setAllowEmpty(bool allowEmpty);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void filterTextChanged();
|
void filterTextChanged();
|
||||||
|
void allowEmptyChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_filterText;
|
QString m_filterText;
|
||||||
|
bool m_allowEmpty = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <Quotient/avatar.h>
|
#include <Quotient/avatar.h>
|
||||||
#include <Quotient/events/roompowerlevelsevent.h>
|
#include <Quotient/events/roompowerlevelsevent.h>
|
||||||
|
#include <Quotient/room.h>
|
||||||
|
|
||||||
#include "enums/powerlevel.h"
|
#include "enums/powerlevel.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
@@ -87,7 +88,11 @@ QVariant UserListModel::data(const QModelIndex &index, int role) const
|
|||||||
return memberId;
|
return memberId;
|
||||||
}
|
}
|
||||||
if (role == AvatarRole) {
|
if (role == AvatarRole) {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
return m_currentRoom->member(memberId).avatarUrl();
|
||||||
|
#else
|
||||||
return m_currentRoom->memberAvatar(memberId).url();
|
return m_currentRoom->memberAvatar(memberId).url();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
if (role == ObjectRole) {
|
if (role == ObjectRole) {
|
||||||
return QVariant::fromValue(memberId);
|
return QVariant::fromValue(memberId);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ Name[en_GB]=NeoChat
|
|||||||
Name[eo]=NeoChat
|
Name[eo]=NeoChat
|
||||||
Name[es]=NeoChat
|
Name[es]=NeoChat
|
||||||
Name[eu]=NeoChat
|
Name[eu]=NeoChat
|
||||||
|
Name[fa]=نئوچت
|
||||||
Name[fi]=NeoChat
|
Name[fi]=NeoChat
|
||||||
Name[fr]=NeoChat
|
Name[fr]=NeoChat
|
||||||
Name[gl]=NeoChat
|
Name[gl]=NeoChat
|
||||||
@@ -261,8 +262,9 @@ Action=Popup
|
|||||||
Name=Share
|
Name=Share
|
||||||
Name[ar]=شارك
|
Name[ar]=شارك
|
||||||
Name[ca]=Compartició
|
Name[ca]=Compartició
|
||||||
Name[ca@valencia]=Compartició
|
Name[ca@valencia]=Compartiu
|
||||||
Name[cs]=Sdílet
|
Name[cs]=Sdílet
|
||||||
|
Name[de]=Teilen
|
||||||
Name[en_GB]=Share
|
Name[en_GB]=Share
|
||||||
Name[eo]=Kundividi
|
Name[eo]=Kundividi
|
||||||
Name[es]=Compartir
|
Name[es]=Compartir
|
||||||
@@ -280,6 +282,7 @@ Name[nl]=Gedeelde
|
|||||||
Name[nn]=Del
|
Name[nn]=Del
|
||||||
Name[pl]=Udostępnij
|
Name[pl]=Udostępnij
|
||||||
Name[pt_BR]=Compartilhar
|
Name[pt_BR]=Compartilhar
|
||||||
|
Name[ru]=Публикация
|
||||||
Name[sl]=Deli
|
Name[sl]=Deli
|
||||||
Name[sv]=Dela
|
Name[sv]=Dela
|
||||||
Name[ta]=பகிர்
|
Name[ta]=பகிர்
|
||||||
@@ -291,6 +294,7 @@ Comment=The result of sharing a piece of content
|
|||||||
Comment[ar]=نتيجة مشاركة محتوى
|
Comment[ar]=نتيجة مشاركة محتوى
|
||||||
Comment[ca]=El resultat de compartir una peça de contingut
|
Comment[ca]=El resultat de compartir una peça de contingut
|
||||||
Comment[ca@valencia]=El resultat de compartir una peça de contingut
|
Comment[ca@valencia]=El resultat de compartir una peça de contingut
|
||||||
|
Comment[de]=Das Ergebnis nach dem Teilen eines Teils des Inhalts
|
||||||
Comment[en_GB]=The result of sharing a piece of content
|
Comment[en_GB]=The result of sharing a piece of content
|
||||||
Comment[eo]=La rezulto el kundividado de enhavero
|
Comment[eo]=La rezulto el kundividado de enhavero
|
||||||
Comment[es]=El resultado de compartir una parte de contenido
|
Comment[es]=El resultado de compartir una parte de contenido
|
||||||
@@ -308,6 +312,7 @@ Comment[nl]=Het resultaat van het delen van een stukje inhoud
|
|||||||
Comment[nn]=Resultatet av deling av innhald
|
Comment[nn]=Resultatet av deling av innhald
|
||||||
Comment[pl]=Wynik udostępniania kawałka treści
|
Comment[pl]=Wynik udostępniania kawałka treści
|
||||||
Comment[pt_BR]=O resultado de compartilhar um conteúdo
|
Comment[pt_BR]=O resultado de compartilhar um conteúdo
|
||||||
|
Comment[ru]=Результат публикации данных
|
||||||
Comment[sl]=Rezultat deljenega kosa vsebine
|
Comment[sl]=Rezultat deljenega kosa vsebine
|
||||||
Comment[sv]=Resultatet av att dela innehåll
|
Comment[sv]=Resultatet av att dela innehåll
|
||||||
Comment[ta]=எதையோ பகிர்ந்ததன் விளைவு
|
Comment[ta]=எதையோ பகிர்ந்ததன் விளைவு
|
||||||
|
|||||||
@@ -15,6 +15,9 @@
|
|||||||
<entry name="OpenRoom" type="String">
|
<entry name="OpenRoom" type="String">
|
||||||
<label>Latest opened room</label>
|
<label>Latest opened room</label>
|
||||||
</entry>
|
</entry>
|
||||||
|
<entry name="ColorScheme" type="String">
|
||||||
|
<label>Color scheme</label>
|
||||||
|
</entry>
|
||||||
<entry name="Blur" type="bool">
|
<entry name="Blur" type="bool">
|
||||||
<label>Make NeoChat blurry</label>
|
<label>Make NeoChat blurry</label>
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
@@ -86,10 +89,6 @@
|
|||||||
<label>Show deleted messages in the timeline</label>
|
<label>Show deleted messages in the timeline</label>
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
</entry>
|
</entry>
|
||||||
<entry name="HideImages" type="bool">
|
|
||||||
<label>Hide images in the timeline</label>
|
|
||||||
<default>false</default>
|
|
||||||
</entry>
|
|
||||||
<entry name="ShowLinkPreview" type="bool">
|
<entry name="ShowLinkPreview" type="bool">
|
||||||
<label>Show preview of the links in the chat messages</label>
|
<label>Show preview of the links in the chat messages</label>
|
||||||
</entry>
|
</entry>
|
||||||
|
|||||||
9
src/neochatconfig.kcfgc
Normal file
9
src/neochatconfig.kcfgc
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
File=neochatconfig.kcfg
|
||||||
|
ClassName=NeoChatConfig
|
||||||
|
Mutators=true
|
||||||
|
DefaultValueGetters=true
|
||||||
|
GenerateProperties=true
|
||||||
|
ParentInConstructor=true
|
||||||
|
Singleton=true
|
||||||
@@ -9,12 +9,14 @@
|
|||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "jobs/neochatchangepasswordjob.h"
|
#include "jobs/neochatchangepasswordjob.h"
|
||||||
#include "jobs/neochatdeactivateaccountjob.h"
|
#include "jobs/neochatdeactivateaccountjob.h"
|
||||||
|
#include "linkpreviewer.h"
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
#include "notificationsmanager.h"
|
#include "notificationsmanager.h"
|
||||||
#include "roommanager.h"
|
#include "roommanager.h"
|
||||||
#include "spacehierarchycache.h"
|
#include "spacehierarchycache.h"
|
||||||
|
|
||||||
|
#include <Quotient/connection.h>
|
||||||
#include <Quotient/jobs/basejob.h>
|
#include <Quotient/jobs/basejob.h>
|
||||||
#include <Quotient/quotient_common.h>
|
#include <Quotient/quotient_common.h>
|
||||||
#include <qt6keychain/keychain.h>
|
#include <qt6keychain/keychain.h>
|
||||||
@@ -44,7 +46,6 @@ NeoChatConnection::NeoChatConnection(QObject *parent)
|
|||||||
: Connection(parent)
|
: Connection(parent)
|
||||||
, m_threePIdModel(new ThreePIdModel(this))
|
, m_threePIdModel(new ThreePIdModel(this))
|
||||||
{
|
{
|
||||||
m_linkPreviewers.setMaxCost(20);
|
|
||||||
connectSignals();
|
connectSignals();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +53,6 @@ NeoChatConnection::NeoChatConnection(const QUrl &server, QObject *parent)
|
|||||||
: Connection(server, parent)
|
: Connection(server, parent)
|
||||||
, m_threePIdModel(new ThreePIdModel(this))
|
, m_threePIdModel(new ThreePIdModel(this))
|
||||||
{
|
{
|
||||||
m_linkPreviewers.setMaxCost(20);
|
|
||||||
connectSignals();
|
connectSignals();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -564,31 +564,14 @@ LinkPreviewer *NeoChatConnection::previewerForLink(const QUrl &link)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto previewer = m_linkPreviewers.object(link);
|
auto previewer = m_linkPreviewers.value(link, nullptr);
|
||||||
if (previewer != nullptr) {
|
if (previewer != nullptr) {
|
||||||
return previewer;
|
return previewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
previewer = new LinkPreviewer(link, this);
|
previewer = new LinkPreviewer(link, this);
|
||||||
m_linkPreviewers.insert(link, previewer);
|
m_linkPreviewers[link] = previewer;
|
||||||
return previewer;
|
return previewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
KeyImport::Error NeoChatConnection::exportMegolmSessions(const QString &passphrase, const QString &path)
|
|
||||||
{
|
|
||||||
KeyImport keyImport;
|
|
||||||
auto result = keyImport.exportKeys(passphrase, this);
|
|
||||||
if (!result.has_value()) {
|
|
||||||
return result.error();
|
|
||||||
}
|
|
||||||
QUrl url(path);
|
|
||||||
QFile file(url.toLocalFile());
|
|
||||||
file.open(QFile::WriteOnly);
|
|
||||||
file.write(result.value());
|
|
||||||
file.close();
|
|
||||||
return KeyImport::Success;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "moc_neochatconnection.cpp"
|
#include "moc_neochatconnection.cpp"
|
||||||
|
|||||||
@@ -3,20 +3,16 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QCache>
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
|
|
||||||
#include <QCoroTask>
|
#include <QCoroTask>
|
||||||
#include <Quotient/connection.h>
|
#include <Quotient/connection.h>
|
||||||
|
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
#include <Quotient/keyimport.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "linkpreviewer.h"
|
|
||||||
#include "models/threepidmodel.h"
|
#include "models/threepidmodel.h"
|
||||||
|
|
||||||
|
class LinkPreviewer;
|
||||||
|
|
||||||
class NeoChatConnection : public Quotient::Connection
|
class NeoChatConnection : public Quotient::Connection
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -173,10 +169,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_INVOKABLE QString accountDataJsonString(const QString &type) const;
|
Q_INVOKABLE QString accountDataJsonString(const QString &type) const;
|
||||||
|
|
||||||
#if Quotient_VERSION_MINOR > 8
|
|
||||||
Q_INVOKABLE Quotient::KeyImport::Error exportMegolmSessions(const QString &passphrase, const QString &path);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
qsizetype directChatNotifications() const;
|
qsizetype directChatNotifications() const;
|
||||||
bool directChatsHaveHighlightNotifications() const;
|
bool directChatsHaveHighlightNotifications() const;
|
||||||
qsizetype homeNotifications() const;
|
qsizetype homeNotifications() const;
|
||||||
@@ -222,7 +214,7 @@ private:
|
|||||||
|
|
||||||
int m_badgeNotificationCount = 0;
|
int m_badgeNotificationCount = 0;
|
||||||
|
|
||||||
QCache<QUrl, LinkPreviewer> m_linkPreviewers;
|
QHash<QUrl, LinkPreviewer *> m_linkPreviewers;
|
||||||
|
|
||||||
bool m_canCheckMutualRooms = false;
|
bool m_canCheckMutualRooms = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <QMimeDatabase>
|
#include <QMimeDatabase>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
|
|
||||||
|
#include <Quotient/events/eventcontent.h>
|
||||||
#include <Quotient/jobs/basejob.h>
|
#include <Quotient/jobs/basejob.h>
|
||||||
#include <Quotient/quotient_common.h>
|
#include <Quotient/quotient_common.h>
|
||||||
#include <qcoro/qcorosignal.h>
|
#include <qcoro/qcorosignal.h>
|
||||||
@@ -64,7 +65,6 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
|||||||
{
|
{
|
||||||
m_mainCache = new ChatBarCache(this);
|
m_mainCache = new ChatBarCache(this);
|
||||||
m_editCache = new ChatBarCache(this);
|
m_editCache = new ChatBarCache(this);
|
||||||
m_threadCache = new ChatBarCache(this);
|
|
||||||
|
|
||||||
connect(connection, &Connection::accountDataChanged, this, &NeoChatRoom::updatePushNotificationState);
|
connect(connection, &Connection::accountDataChanged, this, &NeoChatRoom::updatePushNotificationState);
|
||||||
connect(this, &Room::fileTransferCompleted, this, [this] {
|
connect(this, &Room::fileTransferCompleted, this, [this] {
|
||||||
@@ -77,11 +77,16 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
|||||||
const auto m_event = evtIt->viewAs<RoomEvent>();
|
const auto m_event = evtIt->viewAs<RoomEvent>();
|
||||||
QString mxcUrl;
|
QString mxcUrl;
|
||||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
if (event->has<EventContent::FileContentBase>()) {
|
||||||
|
mxcUrl = event->get<EventContent::FileContentBase>()->url().toString();
|
||||||
|
#else
|
||||||
if (event->hasFileContent()) {
|
if (event->hasFileContent()) {
|
||||||
mxcUrl = event->content()->fileInfo()->url().toString();
|
mxcUrl = event->content()->fileInfo()->url().toString();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
} else if (auto event = eventCast<const Quotient::StickerEvent>(m_event)) {
|
} else if (auto event = eventCast<const Quotient::StickerEvent>(m_event)) {
|
||||||
mxcUrl = event->image().fileInfo()->url().toString();
|
mxcUrl = event->image().url().toString();
|
||||||
}
|
}
|
||||||
if (mxcUrl.isEmpty()) {
|
if (mxcUrl.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@@ -135,7 +140,11 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
|||||||
auto showNotification = [this, roomMemberEvent] {
|
auto showNotification = [this, roomMemberEvent] {
|
||||||
QImage avatar_image;
|
QImage avatar_image;
|
||||||
if (roomMemberEvent && !member(roomMemberEvent->senderId()).avatarUrl().isEmpty()) {
|
if (roomMemberEvent && !member(roomMemberEvent->senderId()).avatarUrl().isEmpty()) {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
avatar_image = member(roomMemberEvent->senderId()).avatar(128, 128, {});
|
||||||
|
#else
|
||||||
avatar_image = memberAvatar(roomMemberEvent->senderId()).get(this->connection(), 128, [] {});
|
avatar_image = memberAvatar(roomMemberEvent->senderId()).get(this->connection(), 128, [] {});
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "using this room's avatar";
|
qWarning() << "using this room's avatar";
|
||||||
avatar_image = avatar(128);
|
avatar_image = avatar(128);
|
||||||
@@ -149,7 +158,7 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
|||||||
|
|
||||||
if (NeoChatConfig::rejectUnknownInvites()) {
|
if (NeoChatConfig::rejectUnknownInvites()) {
|
||||||
auto job = this->connection()->callApi<NeochatGetCommonRoomsJob>(roomMemberEvent->senderId());
|
auto job = this->connection()->callApi<NeochatGetCommonRoomsJob>(roomMemberEvent->senderId());
|
||||||
connect(job, &BaseJob::result, this, [this, job, showNotification] {
|
connect(job, &BaseJob::result, this, [this, job, roomMemberEvent, showNotification] {
|
||||||
QJsonObject replyData = job->jsonData();
|
QJsonObject replyData = job->jsonData();
|
||||||
if (replyData.contains(QStringLiteral("joined"))) {
|
if (replyData.contains(QStringLiteral("joined"))) {
|
||||||
const bool inAnyOfOurRooms = !replyData[QStringLiteral("joined")].toArray().isEmpty();
|
const bool inAnyOfOurRooms = !replyData[QStringLiteral("joined")].toArray().isEmpty();
|
||||||
@@ -252,7 +261,11 @@ QCoro::Task<void> NeoChatRoom::doUploadFile(QUrl url, QString body)
|
|||||||
auto mime = QMimeDatabase().mimeTypeForUrl(url);
|
auto mime = QMimeDatabase().mimeTypeForUrl(url);
|
||||||
url.setScheme("file"_ls);
|
url.setScheme("file"_ls);
|
||||||
QFileInfo fileInfo(url.isLocalFile() ? url.toLocalFile() : url.toString());
|
QFileInfo fileInfo(url.isLocalFile() ? url.toLocalFile() : url.toString());
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
EventContent::FileContentBase *content;
|
||||||
|
#else
|
||||||
EventContent::TypedBase *content;
|
EventContent::TypedBase *content;
|
||||||
|
#endif
|
||||||
if (mime.name().startsWith("image/"_ls)) {
|
if (mime.name().startsWith("image/"_ls)) {
|
||||||
QImage image(url.toLocalFile());
|
QImage image(url.toLocalFile());
|
||||||
content = new EventContent::ImageContent(url, fileInfo.size(), mime, image.size(), fileInfo.fileName());
|
content = new EventContent::ImageContent(url, fileInfo.size(), mime, image.size(), fileInfo.fileName());
|
||||||
@@ -267,7 +280,11 @@ QCoro::Task<void> NeoChatRoom::doUploadFile(QUrl url, QString body)
|
|||||||
} else {
|
} else {
|
||||||
content = new EventContent::FileContent(url, fileInfo.size(), mime, fileInfo.fileName());
|
content = new EventContent::FileContent(url, fileInfo.size(), mime, fileInfo.fileName());
|
||||||
}
|
}
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
QString txnId = postFile(body.isEmpty() ? url.fileName() : body, std::unique_ptr<EventContent::FileContentBase>(content));
|
||||||
|
#else
|
||||||
QString txnId = postFile(body.isEmpty() ? url.fileName() : body, content);
|
QString txnId = postFile(body.isEmpty() ? url.fileName() : body, content);
|
||||||
|
#endif
|
||||||
setHasFileUploading(true);
|
setHasFileUploading(true);
|
||||||
connect(this, &Room::fileTransferCompleted, [this, txnId](const QString &id, FileSourceInfo) {
|
connect(this, &Room::fileTransferCompleted, [this, txnId](const QString &id, FileSourceInfo) {
|
||||||
if (id == txnId) {
|
if (id == txnId) {
|
||||||
@@ -303,16 +320,10 @@ void NeoChatRoom::acceptInvitation()
|
|||||||
|
|
||||||
void NeoChatRoom::forget()
|
void NeoChatRoom::forget()
|
||||||
{
|
{
|
||||||
QStringList roomIds{id()};
|
if (const auto &predecessor = dynamic_cast<NeoChatRoom *>(this->predecessor(JoinState::Join))) {
|
||||||
|
predecessor->forget();
|
||||||
NeoChatRoom *predecessor = this;
|
|
||||||
while (predecessor = dynamic_cast<NeoChatRoom *>(predecessor->predecessor(JoinState::Join)), predecessor && !roomIds.contains(predecessor->id())) {
|
|
||||||
roomIds += predecessor->id();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &id : roomIds) {
|
|
||||||
connection()->forgetRoom(id);
|
|
||||||
}
|
}
|
||||||
|
connection()->forgetRoom(id());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeoChatRoom::sendTypingNotification(bool isTyping)
|
void NeoChatRoom::sendTypingNotification(bool isTyping)
|
||||||
@@ -399,8 +410,13 @@ bool NeoChatRoom::lastEventIsSpoiler() const
|
|||||||
{
|
{
|
||||||
if (auto event = lastEvent()) {
|
if (auto event = lastEvent()) {
|
||||||
if (auto e = eventCast<const RoomMessageEvent>(event)) {
|
if (auto e = eventCast<const RoomMessageEvent>(event)) {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
if (e->has<EventContent::TextContent>() && e->content() && e->mimeType().name() == "text/html"_ls) {
|
||||||
|
auto htmlBody = e->get<EventContent::TextContent>()->body;
|
||||||
|
#else
|
||||||
if (e->hasTextContent() && e->content() && e->mimeType().name() == "text/html"_ls) {
|
if (e->hasTextContent() && e->content() && e->mimeType().name() == "text/html"_ls) {
|
||||||
auto htmlBody = static_cast<const Quotient::EventContent::TextContent *>(e->content())->body;
|
auto htmlBody = static_cast<const Quotient::EventContent::TextContent *>(e->content())->body;
|
||||||
|
#endif
|
||||||
return htmlBody.contains("data-mx-spoiler"_ls);
|
return htmlBody.contains("data-mx-spoiler"_ls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -523,10 +539,9 @@ void NeoChatRoom::postMessage(const QString &rawText,
|
|||||||
MessageEventType type,
|
MessageEventType type,
|
||||||
const QString &replyEventId,
|
const QString &replyEventId,
|
||||||
const QString &relateToEventId,
|
const QString &relateToEventId,
|
||||||
const QString &threadRootId,
|
const QString &threadRootId)
|
||||||
const QString &fallbackId)
|
|
||||||
{
|
{
|
||||||
postHtmlMessage(rawText, text, type, replyEventId, relateToEventId, threadRootId, fallbackId);
|
postHtmlMessage(rawText, text, type, replyEventId, relateToEventId, threadRootId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeoChatRoom::postHtmlMessage(const QString &text,
|
void NeoChatRoom::postHtmlMessage(const QString &text,
|
||||||
@@ -534,8 +549,7 @@ void NeoChatRoom::postHtmlMessage(const QString &text,
|
|||||||
MessageEventType type,
|
MessageEventType type,
|
||||||
const QString &replyEventId,
|
const QString &replyEventId,
|
||||||
const QString &relateToEventId,
|
const QString &relateToEventId,
|
||||||
const QString &threadRootId,
|
const QString &threadRootId)
|
||||||
const QString &fallbackId)
|
|
||||||
{
|
{
|
||||||
bool isReply = !replyEventId.isEmpty();
|
bool isReply = !replyEventId.isEmpty();
|
||||||
bool isEdit = !relateToEventId.isEmpty();
|
bool isEdit = !relateToEventId.isEmpty();
|
||||||
@@ -546,19 +560,9 @@ void NeoChatRoom::postHtmlMessage(const QString &text,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isThread) {
|
if (isThread) {
|
||||||
bool isFallingBack = !fallbackId.isEmpty();
|
EventHandler eventHandler(this, &**replyIt);
|
||||||
QString replyEventId = isFallingBack ? fallbackId : QString();
|
|
||||||
if (isReply) {
|
|
||||||
isFallingBack = false;
|
|
||||||
replyEventId = EventHandler::id(replyIt->get());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are not replying and there is no fallback ID it means a new thread
|
const bool isFallingBack = !eventHandler.isThreaded();
|
||||||
// is being created.
|
|
||||||
if (!isFallingBack && !isReply) {
|
|
||||||
isFallingBack = true;
|
|
||||||
replyEventId = threadRootId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
QJsonObject json{
|
QJsonObject json{
|
||||||
@@ -603,10 +607,12 @@ void NeoChatRoom::postHtmlMessage(const QString &text,
|
|||||||
if (isReply) {
|
if (isReply) {
|
||||||
const auto &replyEvt = **replyIt;
|
const auto &replyEvt = **replyIt;
|
||||||
|
|
||||||
|
EventHandler eventHandler(this, &**replyIt);
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
QJsonObject json{
|
QJsonObject json{
|
||||||
{"msgtype"_ls, msgTypeToString(type)},
|
{"msgtype"_ls, msgTypeToString(type)},
|
||||||
{"body"_ls, "> <%1> %2\n\n%3"_ls.arg(replyEvt.senderId(), EventHandler::plainBody(this, &replyEvt), text)},
|
{"body"_ls, "> <%1> %2\n\n%3"_ls.arg(replyEvt.senderId(), eventHandler.getPlainBody(), text)},
|
||||||
{"format"_ls, "org.matrix.custom.html"_ls},
|
{"format"_ls, "org.matrix.custom.html"_ls},
|
||||||
{"m.relates_to"_ls,
|
{"m.relates_to"_ls,
|
||||||
QJsonObject {
|
QJsonObject {
|
||||||
@@ -618,7 +624,7 @@ void NeoChatRoom::postHtmlMessage(const QString &text,
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{"formatted_body"_ls,
|
{"formatted_body"_ls,
|
||||||
"<mx-reply><blockquote><a href=\"https://matrix.to/#/%1/%2\">In reply to</a> <a href=\"https://matrix.to/#/%3\">%4</a><br>%5</blockquote></mx-reply>%6"_ls.arg(id(), replyEventId, replyEvt.senderId(), replyEvt.senderId(), EventHandler::richBody(this, &replyEvt), html)
|
"<mx-reply><blockquote><a href=\"https://matrix.to/#/%1/%2\">In reply to</a> <a href=\"https://matrix.to/#/%3\">%4</a><br>%5</blockquote></mx-reply>%6"_ls.arg(id(), replyEventId, replyEvt.senderId(), replyEvt.senderId(), eventHandler.getRichBody(), html)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
@@ -1240,7 +1246,11 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
|||||||
for (const auto &i : roomRuleArray) {
|
for (const auto &i : roomRuleArray) {
|
||||||
QJsonObject roomRule = i.toObject();
|
QJsonObject roomRule = i.toObject();
|
||||||
if (roomRule["rule_id"_ls] == id()) {
|
if (roomRule["rule_id"_ls] == id()) {
|
||||||
connection()->callApi<DeletePushRuleJob>("global"_ls, "room"_ls, id());
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
connection()->callApi<DeletePushRuleJob>("room"_ls, id());
|
||||||
|
#else
|
||||||
|
connection()->callApi<DeletePushRuleJob>(QLatin1String("global"), "room"_ls, id());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1251,7 +1261,11 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
|||||||
for (const auto &i : overrideRuleArray) {
|
for (const auto &i : overrideRuleArray) {
|
||||||
QJsonObject overrideRule = i.toObject();
|
QJsonObject overrideRule = i.toObject();
|
||||||
if (overrideRule["rule_id"_ls] == id()) {
|
if (overrideRule["rule_id"_ls] == id()) {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
connection()->callApi<DeletePushRuleJob>("override"_ls, id());
|
||||||
|
#else
|
||||||
connection()->callApi<DeletePushRuleJob>("global"_ls, "override"_ls, id());
|
connection()->callApi<DeletePushRuleJob>("global"_ls, "override"_ls, id());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1287,9 +1301,17 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
|||||||
const QList<PushCondition> conditions = {pushCondition};
|
const QList<PushCondition> conditions = {pushCondition};
|
||||||
|
|
||||||
// Add new override rule and make sure it's enabled
|
// Add new override rule and make sure it's enabled
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
auto job = connection()->callApi<SetPushRuleJob>("override"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||||
|
#else
|
||||||
auto job = connection()->callApi<SetPushRuleJob>("global"_ls, "override"_ls, id(), actions, QString(), QString(), conditions, QString());
|
auto job = connection()->callApi<SetPushRuleJob>("global"_ls, "override"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||||
|
#endif
|
||||||
connect(job, &BaseJob::success, this, [this]() {
|
connect(job, &BaseJob::success, this, [this]() {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("override"_ls, id(), true);
|
||||||
|
#else
|
||||||
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("global"_ls, "override"_ls, id(), true);
|
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("global"_ls, "override"_ls, id(), true);
|
||||||
|
#endif
|
||||||
connect(enableJob, &BaseJob::success, this, [this]() {
|
connect(enableJob, &BaseJob::success, this, [this]() {
|
||||||
m_pushNotificationStateUpdating = false;
|
m_pushNotificationStateUpdating = false;
|
||||||
});
|
});
|
||||||
@@ -1313,9 +1335,17 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
|||||||
// No conditions for a room rule
|
// No conditions for a room rule
|
||||||
const QList<PushCondition> conditions;
|
const QList<PushCondition> conditions;
|
||||||
|
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
auto setJob = connection()->callApi<SetPushRuleJob>("room"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||||
|
#else
|
||||||
auto setJob = connection()->callApi<SetPushRuleJob>("global"_ls, "room"_ls, id(), actions, QString(), QString(), conditions, QString());
|
auto setJob = connection()->callApi<SetPushRuleJob>("global"_ls, "room"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||||
|
#endif
|
||||||
connect(setJob, &BaseJob::success, this, [this]() {
|
connect(setJob, &BaseJob::success, this, [this]() {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("room"_ls, id(), true);
|
||||||
|
#else
|
||||||
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("global"_ls, "room"_ls, id(), true);
|
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("global"_ls, "room"_ls, id(), true);
|
||||||
|
#endif
|
||||||
connect(enableJob, &BaseJob::success, this, [this]() {
|
connect(enableJob, &BaseJob::success, this, [this]() {
|
||||||
m_pushNotificationStateUpdating = false;
|
m_pushNotificationStateUpdating = false;
|
||||||
});
|
});
|
||||||
@@ -1344,9 +1374,17 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
|||||||
const QList<PushCondition> conditions;
|
const QList<PushCondition> conditions;
|
||||||
|
|
||||||
// Add new room rule and make sure enabled
|
// Add new room rule and make sure enabled
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
auto setJob = connection()->callApi<SetPushRuleJob>("room"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||||
|
#else
|
||||||
auto setJob = connection()->callApi<SetPushRuleJob>("global"_ls, "room"_ls, id(), actions, QString(), QString(), conditions, QString());
|
auto setJob = connection()->callApi<SetPushRuleJob>("global"_ls, "room"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||||
|
#endif
|
||||||
connect(setJob, &BaseJob::success, this, [this]() {
|
connect(setJob, &BaseJob::success, this, [this]() {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("room"_ls, id(), true);
|
||||||
|
#else
|
||||||
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("global"_ls, "room"_ls, id(), true);
|
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("global"_ls, "room"_ls, id(), true);
|
||||||
|
#endif
|
||||||
connect(enableJob, &BaseJob::success, this, [this]() {
|
connect(enableJob, &BaseJob::success, this, [this]() {
|
||||||
m_pushNotificationStateUpdating = false;
|
m_pushNotificationStateUpdating = false;
|
||||||
});
|
});
|
||||||
@@ -1437,7 +1475,11 @@ void NeoChatRoom::openEventMediaExternally(const QString &eventId)
|
|||||||
const auto evtIt = findInTimeline(eventId);
|
const auto evtIt = findInTimeline(eventId);
|
||||||
if (evtIt != messageEvents().rend() && is<RoomMessageEvent>(**evtIt)) {
|
if (evtIt != messageEvents().rend() && is<RoomMessageEvent>(**evtIt)) {
|
||||||
const auto event = evtIt->viewAs<RoomMessageEvent>();
|
const auto event = evtIt->viewAs<RoomMessageEvent>();
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
if (event->has<EventContent::FileContent>()) {
|
||||||
|
#else
|
||||||
if (event->hasFileContent()) {
|
if (event->hasFileContent()) {
|
||||||
|
#endif
|
||||||
const auto transferInfo = cachedFileTransferInfo(event);
|
const auto transferInfo = cachedFileTransferInfo(event);
|
||||||
if (transferInfo.completed()) {
|
if (transferInfo.completed()) {
|
||||||
UrlHelper helper;
|
UrlHelper helper;
|
||||||
@@ -1470,7 +1512,11 @@ void NeoChatRoom::copyEventMedia(const QString &eventId)
|
|||||||
const auto evtIt = findInTimeline(eventId);
|
const auto evtIt = findInTimeline(eventId);
|
||||||
if (evtIt != messageEvents().rend() && is<RoomMessageEvent>(**evtIt)) {
|
if (evtIt != messageEvents().rend() && is<RoomMessageEvent>(**evtIt)) {
|
||||||
const auto event = evtIt->viewAs<RoomMessageEvent>();
|
const auto event = evtIt->viewAs<RoomMessageEvent>();
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
if (event->has<EventContent::FileContent>()) {
|
||||||
|
#else
|
||||||
if (event->hasFileContent()) {
|
if (event->hasFileContent()) {
|
||||||
|
#endif
|
||||||
const auto transferInfo = fileTransferInfo(eventId);
|
const auto transferInfo = fileTransferInfo(eventId);
|
||||||
if (transferInfo.completed()) {
|
if (transferInfo.completed()) {
|
||||||
Clipboard clipboard;
|
Clipboard clipboard;
|
||||||
@@ -1503,13 +1549,20 @@ FileTransferInfo NeoChatRoom::cachedFileTransferInfo(const Quotient::RoomEvent *
|
|||||||
QString mxcUrl;
|
QString mxcUrl;
|
||||||
int total = 0;
|
int total = 0;
|
||||||
if (auto evt = eventCast<const Quotient::RoomMessageEvent>(event)) {
|
if (auto evt = eventCast<const Quotient::RoomMessageEvent>(event)) {
|
||||||
|
#if Quotient_VERSION_MINOR > 8
|
||||||
|
if (evt->has<EventContent::FileContent>()) {
|
||||||
|
const auto fileContent = evt->get<EventContent::FileContent>();
|
||||||
|
#else
|
||||||
if (evt->hasFileContent()) {
|
if (evt->hasFileContent()) {
|
||||||
mxcUrl = evt->content()->fileInfo()->url().toString();
|
const auto fileContent = evt->content()->fileInfo();
|
||||||
total = evt->content()->fileInfo()->payloadSize;
|
#endif
|
||||||
|
|
||||||
|
mxcUrl = fileContent->url().toString();
|
||||||
|
total = fileContent->payloadSize;
|
||||||
}
|
}
|
||||||
} else if (auto evt = eventCast<const Quotient::StickerEvent>(event)) {
|
} else if (auto evt = eventCast<const Quotient::StickerEvent>(event)) {
|
||||||
mxcUrl = evt->image().fileInfo()->url().toString();
|
mxcUrl = evt->image().url().toString();
|
||||||
total = evt->image().fileInfo()->payloadSize;
|
total = evt->image().payloadSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileTransferInfo transferInfo = fileTransferInfo(event->id());
|
FileTransferInfo transferInfo = fileTransferInfo(event->id());
|
||||||
@@ -1549,11 +1602,6 @@ ChatBarCache *NeoChatRoom::editCache() const
|
|||||||
return m_editCache;
|
return m_editCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatBarCache *NeoChatRoom::threadCache() const
|
|
||||||
{
|
|
||||||
return m_threadCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NeoChatRoom::replyLastMessage()
|
void NeoChatRoom::replyLastMessage()
|
||||||
{
|
{
|
||||||
const auto &timelineBottom = messageEvents().rbegin();
|
const auto &timelineBottom = messageEvents().rbegin();
|
||||||
@@ -1727,9 +1775,13 @@ int NeoChatRoom::maxRoomVersion() const
|
|||||||
return maxVersion;
|
return maxVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
Quotient::RoomMember NeoChatRoom::directChatRemoteMember() const
|
NeochatRoomMember *NeoChatRoom::directChatRemoteMember()
|
||||||
{
|
{
|
||||||
return directChatMembers()[0];
|
if (directChatMembers().size() == 0) {
|
||||||
|
qWarning() << "No other member available in this room";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return new NeochatRoomMember(this, directChatMembers()[0].id());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeoChatRoom::sendLocation(float lat, float lon, const QString &description)
|
void NeoChatRoom::sendLocation(float lat, float lon, const QString &description)
|
||||||
@@ -1770,7 +1822,6 @@ void NeoChatRoom::downloadEventFromServer(const QString &eventId)
|
|||||||
connect(job, &BaseJob::success, this, [this, job, eventId] {
|
connect(job, &BaseJob::success, this, [this, job, eventId] {
|
||||||
// The event may have arrived in the meantime so check it's not in the timeline.
|
// The event may have arrived in the meantime so check it's not in the timeline.
|
||||||
if (findInTimeline(eventId) != historyEdge()) {
|
if (findInTimeline(eventId) != historyEdge()) {
|
||||||
Q_EMIT extraEventLoaded(eventId);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1778,11 +1829,6 @@ void NeoChatRoom::downloadEventFromServer(const QString &eventId)
|
|||||||
m_extraEvents.push_back(std::move(event));
|
m_extraEvents.push_back(std::move(event));
|
||||||
Q_EMIT extraEventLoaded(eventId);
|
Q_EMIT extraEventLoaded(eventId);
|
||||||
});
|
});
|
||||||
connect(job, &BaseJob::failure, this, [this, job, eventId] {
|
|
||||||
if (job->error() == BaseJob::NotFound) {
|
|
||||||
Q_EMIT extraEventNotFound(eventId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const RoomEvent *NeoChatRoom::getEvent(const QString &eventId) const
|
const RoomEvent *NeoChatRoom::getEvent(const QString &eventId) const
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user