Compare commits
56 Commits
work/nvrwh
...
v24.08.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 "1")
|
||||||
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();
|
||||||
|
|||||||
1181
po/ar/neochat.po
1181
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
1064
po/ast/neochat.po
1064
po/ast/neochat.po
File diff suppressed because it is too large
Load Diff
1282
po/az/neochat.po
1282
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
1347
po/ca/neochat.po
1347
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1231
po/cs/neochat.po
1231
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
1209
po/da/neochat.po
1209
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
1291
po/de/neochat.po
1291
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
1294
po/el/neochat.po
1294
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
1199
po/en_GB/neochat.po
1199
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
1252
po/eo/neochat.po
1252
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
1182
po/es/neochat.po
1182
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
1175
po/eu/neochat.po
1175
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
1270
po/fi/neochat.po
1270
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
1187
po/fr/neochat.po
1187
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
1168
po/gl/neochat.po
1168
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
1272
po/hu/neochat.po
1272
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
1181
po/ia/neochat.po
1181
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
1292
po/id/neochat.po
1292
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
1249
po/ie/neochat.po
1249
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
1258
po/it/neochat.po
1258
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
1064
po/ja/neochat.po
1064
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
1181
po/ka/neochat.po
1181
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
1279
po/ko/neochat.po
1279
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
1064
po/lt/neochat.po
1064
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
1265
po/lv/neochat.po
1265
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
1205
po/nl/neochat.po
1205
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
1177
po/nn/neochat.po
1177
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
1281
po/pa/neochat.po
1281
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
1178
po/pl/neochat.po
1178
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
1290
po/pt/neochat.po
1290
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
1273
po/pt_BR/neochat.po
1273
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
1265
po/ru/neochat.po
1265
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
1287
po/sk/neochat.po
1287
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
1178
po/sl/neochat.po
1178
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
1173
po/sv/neochat.po
1173
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
1180
po/ta/neochat.po
1180
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
1224
po/tok/neochat.po
1224
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
1204
po/tr/neochat.po
1204
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
1189
po/uk/neochat.po
1189
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
1098
po/zh_CN/neochat.po
1098
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
1175
po/zh_TW/neochat.po
1175
po/zh_TW/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -192,8 +192,6 @@ 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
|
||||||
@@ -242,9 +240,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 +288,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,7 +305,7 @@ 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
|
set_source_files_properties(qml/ShareActionStub.qml PROPERTIES
|
||||||
@@ -427,7 +425,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)
|
||||||
|
|||||||
@@ -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,8 @@ 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);
|
||||||
|
return eventhandler.getMarkdownBody();
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -139,8 +140,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 +161,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,16 @@
|
|||||||
|
|
||||||
#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/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 +25,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 +35,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 +107,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 +118,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 +143,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 +188,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,52 +253,44 @@ QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
|
|||||||
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();
|
||||||
@@ -284,15 +301,15 @@ QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent
|
|||||||
|
|
||||||
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 +321,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 +384,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,7 +457,7 @@ 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;
|
||||||
|
|
||||||
@@ -476,24 +489,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,33 +626,29 @@ 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();
|
||||||
|
|
||||||
@@ -652,7 +661,7 @@ QVariantMap EventHandler::getMediaInfoForEvent(const NeoChatRoom *room, const Qu
|
|||||||
|
|
||||||
const EventContent::FileInfo *fileInfo;
|
const EventContent::FileInfo *fileInfo;
|
||||||
fileInfo = roomMessageEvent->content()->fileInfo();
|
fileInfo = roomMessageEvent->content()->fileInfo();
|
||||||
QVariantMap mediaInfo = getMediaInfoFromFileInfo(room, fileInfo, eventId, false, false);
|
QVariantMap mediaInfo = getMediaInfoFromFileInfo(fileInfo, eventId, false, false);
|
||||||
// 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;
|
mediaInfo["filename"_ls] = (fileInfo->originalName.isEmpty()) ? roomMessageEvent->plainBody() : fileInfo->originalName;
|
||||||
@@ -664,17 +673,13 @@ QVariantMap EventHandler::getMediaInfoForEvent(const NeoChatRoom *room, const Qu
|
|||||||
auto stickerEvent = eventCast<const StickerEvent>(event);
|
auto stickerEvent = eventCast<const StickerEvent>(event);
|
||||||
fileInfo = &stickerEvent->image();
|
fileInfo = &stickerEvent->image();
|
||||||
|
|
||||||
return getMediaInfoFromFileInfo(room, fileInfo, eventId, false, true);
|
return getMediaInfoFromFileInfo(fileInfo, eventId, false, true);
|
||||||
} else {
|
} else {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail, bool isSticker) const
|
||||||
const EventContent::FileInfo *fileInfo,
|
|
||||||
const QString &eventId,
|
|
||||||
bool isThumbnail,
|
|
||||||
bool isSticker)
|
|
||||||
{
|
{
|
||||||
QVariantMap mediaInfo;
|
QVariantMap mediaInfo;
|
||||||
|
|
||||||
@@ -682,7 +687,7 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
|||||||
if (!fileInfo->url().isValid() || fileInfo->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
if (!fileInfo->url().isValid() || fileInfo->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
||||||
mediaInfo["source"_ls] = QUrl();
|
mediaInfo["source"_ls] = QUrl();
|
||||||
} else {
|
} else {
|
||||||
QUrl source = room->makeMediaUrl(eventId, fileInfo->url());
|
QUrl source = m_room->makeMediaUrl(eventId, fileInfo->url());
|
||||||
|
|
||||||
if (source.isValid()) {
|
if (source.isValid()) {
|
||||||
mediaInfo["source"_ls] = source;
|
mediaInfo["source"_ls] = source;
|
||||||
@@ -714,7 +719,7 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
|||||||
|
|
||||||
if (!isThumbnail) {
|
if (!isThumbnail) {
|
||||||
QVariantMap tempInfo;
|
QVariantMap tempInfo;
|
||||||
auto thumbnailInfo = getMediaInfoFromFileInfo(room, castInfo->thumbnailInfo(), eventId, true);
|
auto thumbnailInfo = getMediaInfoFromFileInfo(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 {
|
} else {
|
||||||
@@ -737,7 +742,7 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
|||||||
|
|
||||||
if (!isThumbnail) {
|
if (!isThumbnail) {
|
||||||
QVariantMap tempInfo;
|
QVariantMap tempInfo;
|
||||||
auto thumbnailInfo = getMediaInfoFromFileInfo(room, castInfo->thumbnailInfo(), eventId, true);
|
auto thumbnailInfo = getMediaInfoFromFileInfo(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 {
|
} else {
|
||||||
@@ -761,89 +766,156 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
|||||||
return mediaInfo;
|
return mediaInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventHandler::hasReply(const Quotient::RoomEvent *event, bool showFallbacks)
|
bool EventHandler::hasReply() const
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "hasReply called with event set to nullptr.";
|
qCWarning(EventHandling) << "hasReply called with m_event set to nullptr.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return !m_event->contentJson()["m.relates_to"_ls].toObject()["m.in_reply_to"_ls].toObject()["event_id"_ls].toString().isEmpty();
|
||||||
const auto relations = event->contentPart<QJsonObject>("m.relates_to"_ls);
|
|
||||||
if (!relations.isEmpty()) {
|
|
||||||
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)
|
QString EventHandler::getReplyId() const
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "replyId called with event set to nullptr.";
|
qCWarning(EventHandling) << "getReplyId called with m_event set to nullptr.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return event->contentJson()["m.relates_to"_ls].toObject()["m.in_reply_to"_ls].toObject()["event_id"_ls].toString();
|
return m_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)
|
MessageComponentType::Type EventHandler::replyMessageComponentType() const
|
||||||
{
|
{
|
||||||
if (room == nullptr) {
|
if (m_room == nullptr) {
|
||||||
qCWarning(EventHandling) << "replyAuthor called with room set to 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 {};
|
return {};
|
||||||
}
|
}
|
||||||
if (event == nullptr) {
|
if (m_event == nullptr) {
|
||||||
qCWarning(EventHandling) << "replyAuthor called with event set to nullptr. Returning empty user.";
|
qCWarning(EventHandling) << "getReplyAuthor called with m_event set to nullptr. Returning empty user.";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto replyPtr = room->getReplyForEvent(*event)) {
|
if (auto replyPtr = m_room->getReplyForEvent(*m_event)) {
|
||||||
return room->member(replyPtr->senderId());
|
return m_room->member(replyPtr->senderId());
|
||||||
} else {
|
} else {
|
||||||
return room->member(QString());
|
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 +923,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 +938,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,23 @@ 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(const Quotient::EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail = false, bool isSticker = false) 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,17 @@
|
|||||||
// 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/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 +20,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 +31,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 +43,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 +58,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 +74,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 +92,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 +126,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 +147,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 +163,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 +188,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,23 +215,10 @@ 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");
|
||||||
}
|
}
|
||||||
@@ -279,7 +233,7 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
|||||||
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 +242,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 +250,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 +258,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 +273,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 +304,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 +331,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 +344,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 +358,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 +367,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 +376,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 +396,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]() {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -22,8 +22,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 +71,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 +88,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 +96,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 +430,31 @@ 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()) {
|
if (evt.isRedacted()) {
|
||||||
auto reason = evt.redactedBecause()->reason();
|
auto reason = evt.redactedBecause()->reason();
|
||||||
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>")
|
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>")
|
||||||
: i18n("<i>[This message was deleted: %1]</i>", evt.redactedBecause()->reason());
|
: i18n("<i>[This message was deleted: %1]</i>", evt.redactedBecause()->reason());
|
||||||
}
|
}
|
||||||
return EventHandler::richBody(m_currentRoom, &evt);
|
return eventHandler.getRichBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +477,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 +490,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,7 +498,7 @@ 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) {
|
||||||
@@ -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,9 +241,6 @@ 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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,21 @@
|
|||||||
// 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 "utils.h"
|
#include "neochatroom.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
|
#ifdef HAVE_ICU
|
||||||
|
#include <QTextBoundaryFinder>
|
||||||
|
#include <QTextCharFormat>
|
||||||
|
#include <unicode/uchar.h>
|
||||||
|
#include <unicode/urename.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#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)
|
||||||
@@ -149,6 +157,30 @@ QHash<int, QByteArray> ReactionModel::roleNames() const
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isEmoji(const QString &text)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_ICU
|
||||||
|
QTextBoundaryFinder finder(QTextBoundaryFinder::Grapheme, text);
|
||||||
|
int from = 0;
|
||||||
|
while (finder.toNextBoundary() != -1) {
|
||||||
|
auto to = finder.position();
|
||||||
|
if (text[from].isSpace()) {
|
||||||
|
from = to;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto first = text.mid(from, to - from).toUcs4()[0];
|
||||||
|
if (!u_hasBinaryProperty(first, UCHAR_EMOJI)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
from = to;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
QString ReactionModel::reactionText(QString text) const
|
QString ReactionModel::reactionText(QString text) const
|
||||||
{
|
{
|
||||||
text = text.toHtmlEscaped();
|
text = text.toHtmlEscaped();
|
||||||
@@ -162,7 +194,7 @@ QString ReactionModel::reactionText(QString text) const
|
|||||||
.arg(m_room->connection()->makeMediaUrl(QUrl(text)).toString(), QString::number(size));
|
.arg(m_room->connection()->makeMediaUrl(QUrl(text)).toString(), QString::number(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Utils::isEmoji(text) ? QStringLiteral("<span style=\"font-family: 'emoji';\">") + text + QStringLiteral("</span>") : text;
|
return isEmoji(text) ? QStringLiteral("<span style=\"font-family: 'emoji';\">") + text + QStringLiteral("</span>") : text;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "moc_reactionmodel.cpp"
|
#include "moc_reactionmodel.cpp"
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ 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[en_GB]=Share
|
Name[en_GB]=Share
|
||||||
Name[eo]=Kundividi
|
Name[eo]=Kundividi
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -64,7 +64,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] {
|
||||||
@@ -149,7 +148,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();
|
||||||
@@ -303,16 +302,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)
|
||||||
@@ -523,10 +516,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 +526,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 +537,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 +584,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 +601,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
|
||||||
@@ -1549,11 +1532,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();
|
||||||
@@ -1770,7 +1748,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 +1755,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
|
||||||
|
|||||||
@@ -201,11 +201,6 @@ class NeoChatRoom : public Quotient::Room
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(ChatBarCache *editCache READ editCache CONSTANT)
|
Q_PROPERTY(ChatBarCache *editCache READ editCache CONSTANT)
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The cache for the thread chat bar in the room.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(ChatBarCache *threadCache READ threadCache CONSTANT)
|
|
||||||
|
|
||||||
#if Quotient_VERSION_MINOR == 8
|
#if Quotient_VERSION_MINOR == 8
|
||||||
Q_PROPERTY(QList<Quotient::RoomMember> otherMembersTyping READ otherMembersTyping NOTIFY typingChanged)
|
Q_PROPERTY(QList<Quotient::RoomMember> otherMembersTyping READ otherMembersTyping NOTIFY typingChanged)
|
||||||
#endif
|
#endif
|
||||||
@@ -516,8 +511,6 @@ public:
|
|||||||
|
|
||||||
ChatBarCache *editCache() const;
|
ChatBarCache *editCache() const;
|
||||||
|
|
||||||
ChatBarCache *threadCache() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reply to the last message sent in the timeline.
|
* @brief Reply to the last message sent in the timeline.
|
||||||
*
|
*
|
||||||
@@ -616,7 +609,6 @@ private:
|
|||||||
|
|
||||||
ChatBarCache *m_mainCache;
|
ChatBarCache *m_mainCache;
|
||||||
ChatBarCache *m_editCache;
|
ChatBarCache *m_editCache;
|
||||||
ChatBarCache *m_threadCache;
|
|
||||||
|
|
||||||
QCache<QString, PollHandler> m_polls;
|
QCache<QString, PollHandler> m_polls;
|
||||||
std::vector<Quotient::event_ptr_tt<Quotient::RoomEvent>> m_extraEvents;
|
std::vector<Quotient::event_ptr_tt<Quotient::RoomEvent>> m_extraEvents;
|
||||||
@@ -655,7 +647,6 @@ Q_SIGNALS:
|
|||||||
void urlPreviewEnabledChanged();
|
void urlPreviewEnabledChanged();
|
||||||
void maxRoomVersionChanged();
|
void maxRoomVersionChanged();
|
||||||
void extraEventLoaded(const QString &eventId);
|
void extraEventLoaded(const QString &eventId);
|
||||||
void extraEventNotFound(const QString &eventId);
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
/**
|
/**
|
||||||
@@ -700,8 +691,7 @@ public Q_SLOTS:
|
|||||||
Quotient::MessageEventType type = Quotient::MessageEventType::Text,
|
Quotient::MessageEventType type = Quotient::MessageEventType::Text,
|
||||||
const QString &replyEventId = QString(),
|
const QString &replyEventId = QString(),
|
||||||
const QString &relateToEventId = QString(),
|
const QString &relateToEventId = QString(),
|
||||||
const QString &threadRootId = QString(),
|
const QString &threadRootId = QString());
|
||||||
const QString &fallbackId = QString());
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send an html message to the room.
|
* @brief Send an html message to the room.
|
||||||
@@ -717,8 +707,7 @@ public Q_SLOTS:
|
|||||||
Quotient::MessageEventType type = Quotient::MessageEventType::Text,
|
Quotient::MessageEventType type = Quotient::MessageEventType::Text,
|
||||||
const QString &replyEventId = QString(),
|
const QString &replyEventId = QString(),
|
||||||
const QString &relateToEventId = QString(),
|
const QString &relateToEventId = QString(),
|
||||||
const QString &threadRootId = QString(),
|
const QString &threadRootId = QString());
|
||||||
const QString &fallbackId = QString());
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the room avatar.
|
* @brief Set the room avatar.
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
"Name[fi]": "Tobias Fella",
|
"Name[fi]": "Tobias Fella",
|
||||||
"Name[fr]": "Tobias Fella",
|
"Name[fr]": "Tobias Fella",
|
||||||
"Name[gl]": "Tobias Fella",
|
"Name[gl]": "Tobias Fella",
|
||||||
"Name[he]": "טוביאס פלה",
|
|
||||||
"Name[hu]": "Tobias Fella",
|
"Name[hu]": "Tobias Fella",
|
||||||
"Name[ia]": "Tobias Fella",
|
"Name[ia]": "Tobias Fella",
|
||||||
"Name[it]": "Tobias Fella",
|
"Name[it]": "Tobias Fella",
|
||||||
@@ -24,6 +23,7 @@
|
|||||||
"Name[nl]": "Tobias Fella",
|
"Name[nl]": "Tobias Fella",
|
||||||
"Name[nn]": "Tobias Fella",
|
"Name[nn]": "Tobias Fella",
|
||||||
"Name[pl]": "Tobias Fella",
|
"Name[pl]": "Tobias Fella",
|
||||||
|
"Name[pt_BR]": "Tobias Fella",
|
||||||
"Name[ru]": "Tobias Fella",
|
"Name[ru]": "Tobias Fella",
|
||||||
"Name[sl]": "Tobias Fella",
|
"Name[sl]": "Tobias Fella",
|
||||||
"Name[sv]": "Tobias Fella",
|
"Name[sv]": "Tobias Fella",
|
||||||
@@ -47,7 +47,6 @@
|
|||||||
"Description[fi]": "Jaa NeoChatillä",
|
"Description[fi]": "Jaa NeoChatillä",
|
||||||
"Description[fr]": "Partager grâce à NeoChat",
|
"Description[fr]": "Partager grâce à NeoChat",
|
||||||
"Description[gl]": "Compartir por NeoChat",
|
"Description[gl]": "Compartir por NeoChat",
|
||||||
"Description[he]": "שיתוף דרך NeoChat",
|
|
||||||
"Description[hu]": "Megosztás NeoChatben",
|
"Description[hu]": "Megosztás NeoChatben",
|
||||||
"Description[ia]": "Comparti via NeoChat",
|
"Description[ia]": "Comparti via NeoChat",
|
||||||
"Description[it]": "Condividi tramite NeoChat",
|
"Description[it]": "Condividi tramite NeoChat",
|
||||||
@@ -56,6 +55,7 @@
|
|||||||
"Description[nl]": "Delen via NeoChat",
|
"Description[nl]": "Delen via NeoChat",
|
||||||
"Description[nn]": "Del via NeoChat",
|
"Description[nn]": "Del via NeoChat",
|
||||||
"Description[pl]": "Udostępnij przez NeoChat",
|
"Description[pl]": "Udostępnij przez NeoChat",
|
||||||
|
"Description[pt_BR]": "Compartilhar via NeoChat",
|
||||||
"Description[ru]": "Опубликовать в NeoChat",
|
"Description[ru]": "Опубликовать в NeoChat",
|
||||||
"Description[sl]": "Deli prek NeoChat",
|
"Description[sl]": "Deli prek NeoChat",
|
||||||
"Description[sv]": "Dela via NeoChat",
|
"Description[sv]": "Dela via NeoChat",
|
||||||
@@ -80,7 +80,6 @@
|
|||||||
"Name[fi]": "NeoChat",
|
"Name[fi]": "NeoChat",
|
||||||
"Name[fr]": "NeoChat",
|
"Name[fr]": "NeoChat",
|
||||||
"Name[gl]": "NeoChat",
|
"Name[gl]": "NeoChat",
|
||||||
"Name[he]": "NeoChat",
|
|
||||||
"Name[hu]": "NeoChat",
|
"Name[hu]": "NeoChat",
|
||||||
"Name[ia]": "Neochat",
|
"Name[ia]": "Neochat",
|
||||||
"Name[it]": "NeoChat",
|
"Name[it]": "NeoChat",
|
||||||
@@ -89,6 +88,7 @@
|
|||||||
"Name[nl]": "NeoChat",
|
"Name[nl]": "NeoChat",
|
||||||
"Name[nn]": "NeoChat",
|
"Name[nn]": "NeoChat",
|
||||||
"Name[pl]": "NeoChat",
|
"Name[pl]": "NeoChat",
|
||||||
|
"Name[pt_BR]": "NeoChat",
|
||||||
"Name[ru]": "NeoChat",
|
"Name[ru]": "NeoChat",
|
||||||
"Name[sl]": "NeoChat",
|
"Name[sl]": "NeoChat",
|
||||||
"Name[sv]": "NeoChat",
|
"Name[sv]": "NeoChat",
|
||||||
|
|||||||
@@ -19,22 +19,6 @@ QQC2.Menu {
|
|||||||
|
|
||||||
margins: Kirigami.Units.smallSpacing
|
margins: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
QQC2.MenuItem {
|
|
||||||
text: i18nc("@action:button", "Show QR code")
|
|
||||||
icon.name: "view-barcode-qr-symbolic"
|
|
||||||
onTriggered: {
|
|
||||||
let qrMax = Qt.createComponent('org.kde.neochat', 'QrCodeMaximizeComponent').createObject(QQC2.Overlay.overlay, {
|
|
||||||
text: "https://matrix.to/#/" + root.connection.localUser.id,
|
|
||||||
title: root.connection.localUser.displayName,
|
|
||||||
subtitle: root.connection.localUser.id,
|
|
||||||
avatarSource: root.connection.makeMediaUrl(root.connection.localUser.avatarUrl)
|
|
||||||
});
|
|
||||||
if (typeof root.closeDialog === "function") {
|
|
||||||
root.closeDialog();
|
|
||||||
}
|
|
||||||
qrMax.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QQC2.MenuItem {
|
QQC2.MenuItem {
|
||||||
text: i18n("Edit this account")
|
text: i18n("Edit this account")
|
||||||
icon.name: "document-edit"
|
icon.name: "document-edit"
|
||||||
@@ -61,7 +45,7 @@ QQC2.Menu {
|
|||||||
QQC2.MenuItem {
|
QQC2.MenuItem {
|
||||||
text: i18n("Open developer tools")
|
text: i18n("Open developer tools")
|
||||||
icon.name: "tools"
|
icon.name: "tools"
|
||||||
visible: NeoChatConfig.developerTools
|
visible: Config.developerTools
|
||||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'DevtoolsPage'), {
|
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'DevtoolsPage'), {
|
||||||
connection: root.connection
|
connection: root.connection
|
||||||
}, {
|
}, {
|
||||||
@@ -73,7 +57,7 @@ QQC2.Menu {
|
|||||||
QQC2.MenuItem {
|
QQC2.MenuItem {
|
||||||
text: i18nc("@action:inmenu", "Open Secret Backup")
|
text: i18nc("@action:inmenu", "Open Secret Backup")
|
||||||
icon.name: "unlock"
|
icon.name: "unlock"
|
||||||
visible: NeoChatConfig.secretBackup
|
visible: Config.secretBackup
|
||||||
onTriggered: root.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UnlockSSSSDialog'), {}, {
|
onTriggered: root.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UnlockSSSSDialog'), {}, {
|
||||||
title: i18nc("@title:window", "Open Key Backup")
|
title: i18nc("@title:window", "Open Key Backup")
|
||||||
})
|
})
|
||||||
@@ -86,7 +70,7 @@ QQC2.Menu {
|
|||||||
}
|
}
|
||||||
QQC2.MenuItem {
|
QQC2.MenuItem {
|
||||||
text: i18n("Logout")
|
text: i18n("Logout")
|
||||||
icon.name: "im-kick-user"
|
icon.name: "list-remove-user"
|
||||||
onTriggered: confirmLogoutDialogComponent.createObject(QQC2.ApplicationWindow.window.overlay).open()
|
onTriggered: confirmLogoutDialogComponent.createObject(QQC2.ApplicationWindow.window.overlay).open()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ Kirigami.Dialog {
|
|||||||
if (switchUserButton.checked) {
|
if (switchUserButton.checked) {
|
||||||
switchUserButton.checked = false;
|
switchUserButton.checked = false;
|
||||||
}
|
}
|
||||||
root.close();
|
accountView.currentIndex = Controller.activeConnectionIndex;
|
||||||
}
|
}
|
||||||
Keys.onUpPressed: {
|
Keys.onUpPressed: {
|
||||||
accountView.currentIndex = accountView.count - 1;
|
accountView.currentIndex = accountView.count - 1;
|
||||||
|
|||||||
@@ -38,20 +38,20 @@ ColumnLayout {
|
|||||||
text: i18n("Edit")
|
text: i18n("Edit")
|
||||||
display: QQC2.AbstractButton.IconOnly
|
display: QQC2.AbstractButton.IconOnly
|
||||||
|
|
||||||
// Component {
|
Component {
|
||||||
// id: imageEditorPage
|
id: imageEditorPage
|
||||||
// ImageEditorPage {
|
ImageEditorPage {
|
||||||
// imagePath: root.attachmentPath
|
imagePath: root.attachmentPath
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// onClicked: {
|
onClicked: {
|
||||||
// let imageEditor = applicationWindow().pageStack.pushDialogLayer(imageEditorPage);
|
let imageEditor = applicationWindow().pageStack.pushDialogLayer(imageEditorPage);
|
||||||
// imageEditor.newPathChanged.connect(function (newPath) {
|
imageEditor.newPathChanged.connect(function (newPath) {
|
||||||
// applicationWindow().pageStack.layers.pop();
|
applicationWindow().pageStack.layers.pop();
|
||||||
// root.attachmentPath = newPath;
|
root.attachmentPath = newPath;
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
QQC2.ToolTip.text: text
|
QQC2.ToolTip.text: text
|
||||||
QQC2.ToolTip.visible: hovered
|
QQC2.ToolTip.visible: hovered
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Controls as QQC2
|
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
|
||||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
|
||||||
|
|
||||||
KirigamiComponents.Avatar {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property int notificationCount
|
|
||||||
|
|
||||||
property bool notificationHighlight
|
|
||||||
|
|
||||||
property bool showNotificationLabel
|
|
||||||
|
|
||||||
QQC2.Label {
|
|
||||||
id: notificationCountLabel
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.topMargin: -Kirigami.Units.smallSpacing
|
|
||||||
anchors.rightMargin: -Kirigami.Units.smallSpacing
|
|
||||||
z: 1
|
|
||||||
width: Math.max(notificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
|
|
||||||
height: Kirigami.Units.iconSizes.smallMedium
|
|
||||||
|
|
||||||
text: root.notificationCount > 0 ? root.notificationCount : ""
|
|
||||||
visible: root.showNotificationLabel
|
|
||||||
color: Kirigami.Theme.textColor
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
background: Rectangle {
|
|
||||||
visible: true
|
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
|
||||||
Kirigami.Theme.inherit: false
|
|
||||||
color: root.notificationHighlight ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
|
|
||||||
radius: height / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
TextMetrics {
|
|
||||||
id: notificationCountTextMetrics
|
|
||||||
text: notificationCountLabel.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user