Compare commits
57 Commits
work/redst
...
work/nvrwh
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8b269869c | ||
|
|
7fd8394253 | ||
|
|
c54a447caf | ||
|
|
61b009422d | ||
|
|
5a8b0184ea | ||
|
|
f48c2a21d9 | ||
|
|
538cfbee8d | ||
|
|
7666f1c362 | ||
|
|
4b5d828bf8 | ||
|
|
4bd160cceb | ||
|
|
5f56fc1156 | ||
|
|
72a2a74395 | ||
|
|
f6a5cc7c25 | ||
|
|
80f3bd64b6 | ||
|
|
1f69a96766 | ||
|
|
f963e06983 | ||
|
|
e6980e2370 | ||
|
|
8e8105d04d | ||
|
|
21d9e69712 | ||
|
|
e0783a3c6e | ||
|
|
3b9337d2a8 | ||
|
|
85cda8ffa7 | ||
|
|
f1efc1f17d | ||
|
|
0486fa61cd | ||
|
|
2247a2a7af | ||
|
|
08a0fbfd6b | ||
|
|
898b993b94 | ||
|
|
77e366b179 | ||
|
|
981edc9cf7 | ||
|
|
d45aa14348 | ||
|
|
4926488d49 | ||
|
|
dcc1935150 | ||
|
|
55364a8eb8 | ||
|
|
70bb06715f | ||
|
|
ec4aa73e37 | ||
|
|
c1d122a717 | ||
|
|
f75c194e7c | ||
|
|
ecf93de006 | ||
|
|
7feb02c7d8 | ||
|
|
0764fe0dd9 | ||
|
|
50a7138633 | ||
|
|
96a477f91c | ||
|
|
5a6b0f756d | ||
|
|
dbbf975f7a | ||
|
|
e06c2d2f93 | ||
|
|
5dea17b616 | ||
|
|
63b6d7ebe0 | ||
|
|
652106e6a1 | ||
|
|
3e158b3e60 | ||
|
|
b9ec33dd94 | ||
|
|
69bd1202ba | ||
|
|
f75c09e130 | ||
|
|
683d216f44 | ||
|
|
c10bcf1764 | ||
|
|
9e2bf0da26 | ||
|
|
51f7de117d | ||
|
|
f361f4e2d8 |
@@ -2,9 +2,6 @@
|
||||
; SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
[BlueprintSettings]
|
||||
kde/frameworks/extra-cmake-modules.version=master
|
||||
kde/unreleased/kirigami-addons.version=master
|
||||
kde/frameworks.version=master
|
||||
kde/libs.version=master
|
||||
kde/plasma.version=master
|
||||
kde/unreleased.version=master
|
||||
libs/qt.qtMajorVersion=6
|
||||
|
||||
@@ -11,3 +11,5 @@ include:
|
||||
- /gitlab-templates/freebsd-qt6.yml
|
||||
- /gitlab-templates/flatpak.yml
|
||||
- /gitlab-templates/craft-android-qt6-apks.yml
|
||||
- /gitlab-templates/craft-appimage-qt6.yml
|
||||
- /gitlab-templates/craft-windows-x86-64-qt6.yml
|
||||
|
||||
@@ -8,8 +8,8 @@ cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# KDE Applications version, managed by release script.
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "24")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "01")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "85")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "04")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||
|
||||
@@ -76,3 +76,9 @@ ecm_add_test(
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME reactionmodeltest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
linkpreviewertest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME linkpreviewertest
|
||||
)
|
||||
|
||||
14
autotests/data/test-invalidmatrixtolink-event.json
Normal file
14
autotests/data/test-invalidmatrixtolink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "https://matrix.to/#/@alice:example.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
14
autotests/data/test-invalidmxclink-event.json
Normal file
14
autotests/data/test-invalidmxclink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
14
autotests/data/test-invalidnospacelink-event.json
Normal file
14
autotests/data/test-invalidnospacelink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "testhttps://kde.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
24
autotests/data/test-linkpreviewerintial-sync.json
Normal file
24
autotests/data/test-linkpreviewerintial-sync.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "https://kde.org",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "https://kde.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"origin_server_ts": 1704648567967,
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 112
|
||||
},
|
||||
"event_id": "$validlink:example.org",
|
||||
"room_id": "!test:example.org"
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
}
|
||||
35
autotests/data/test-linkpreviewerreplace-sync.json
Normal file
35
autotests/data/test-linkpreviewerreplace-sync.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "* ",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "no link",
|
||||
"m.new_content": {
|
||||
"body": "",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "no link",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"m.relates_to": {
|
||||
"event_id": "$validlink:example.org",
|
||||
"rel_type": "m.replace"
|
||||
},
|
||||
"msgtype": "m.text",
|
||||
"type": "m.room.message"
|
||||
},
|
||||
"origin_server_ts": 1704648614969,
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 65
|
||||
},
|
||||
"event_id": "$nolink:example.org",
|
||||
"room_id": "!test:example.org"
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
}
|
||||
14
autotests/data/test-multiplelink-event.json
Normal file
14
autotests/data/test-multiplelink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "www.example.org https://kde.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
14
autotests/data/test-validplainlink-event.json
Normal file
14
autotests/data/test-validplainlink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "https://kde.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
14
autotests/data/test-validplainwwwlink-event.json
Normal file
14
autotests/data/test-validplainwwwlink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "www.example.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
16
autotests/data/test-validrichlink-event.json
Normal file
16
autotests/data/test-validrichlink-event.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "[Rich Link](https://kde.org)",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<a href=\"https://kde.org\">Rich Link</a>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
@@ -65,8 +65,6 @@ private Q_SLOTS:
|
||||
void nullSubtitle();
|
||||
void mediaInfo();
|
||||
void nullMediaInfo();
|
||||
void linkPreviewer();
|
||||
void nullLinkPreviewer();
|
||||
void hasReply();
|
||||
void nullHasReply();
|
||||
void replyId();
|
||||
@@ -399,28 +397,6 @@ void EventHandlerTest::nullMediaInfo()
|
||||
QCOMPARE(noEventHandler.getMediaInfo(), QVariantMap());
|
||||
}
|
||||
|
||||
void EventHandlerTest::linkPreviewer()
|
||||
{
|
||||
auto event = room->messageEvents().at(2).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.getLinkPreviewer()->url(), QUrl("https://kde.org"_ls));
|
||||
|
||||
event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.getLinkPreviewer(), nullptr);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullLinkPreviewer()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getLinkPreviewer called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getLinkPreviewer(), nullptr);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getLinkPreviewer called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getLinkPreviewer(), nullptr);
|
||||
}
|
||||
|
||||
void EventHandlerTest::hasReply()
|
||||
{
|
||||
auto event = room->messageEvents().at(5).get();
|
||||
|
||||
104
autotests/linkpreviewertest.cpp
Normal file
104
autotests/linkpreviewertest.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
// 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 <QObject>
|
||||
#include <QTest>
|
||||
|
||||
#include "linkpreviewer.h"
|
||||
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class LinkPreviewerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
TestUtils::TestRoom *room = nullptr;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void linkPreviewsMatch_data();
|
||||
void linkPreviewsMatch();
|
||||
|
||||
void linkPreviewsReject_data();
|
||||
void linkPreviewsReject();
|
||||
|
||||
void editedLink();
|
||||
};
|
||||
|
||||
void LinkPreviewerTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:example.org"));
|
||||
room = new TestUtils::TestRoom(connection, QStringLiteral("!test:example.org"));
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsMatch_data()
|
||||
{
|
||||
QTest::addColumn<QString>("eventSource");
|
||||
QTest::addColumn<QUrl>("testOutputLink");
|
||||
|
||||
QTest::newRow("plainHttps") << QStringLiteral("test-validplainlink-event.json") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("richHttps") << QStringLiteral("test-validrichlink-event.json") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("plainWww") << QStringLiteral("test-validplainwwwlink-event.json") << QUrl("www.example.org"_ls);
|
||||
QTest::newRow("multipleHttps") << QStringLiteral("test-multiplelink-event.json") << QUrl("www.example.org"_ls);
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsMatch()
|
||||
{
|
||||
QFETCH(QString, eventSource);
|
||||
QFETCH(QUrl, testOutputLink);
|
||||
|
||||
auto event = TestUtils::loadEventFromFile<RoomMessageEvent>(eventSource);
|
||||
auto linkPreviewer = LinkPreviewer(room, event.get());
|
||||
|
||||
QCOMPARE(linkPreviewer.empty(), false);
|
||||
QCOMPARE(linkPreviewer.url(), testOutputLink);
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsReject_data()
|
||||
{
|
||||
QTest::addColumn<QString>("eventSource");
|
||||
|
||||
QTest::newRow("mxc") << QStringLiteral("test-invalidmxclink-event.json");
|
||||
QTest::newRow("matrixTo") << QStringLiteral("test-invalidmatrixtolink-event.json");
|
||||
QTest::newRow("noSpace") << QStringLiteral("test-invalidnospacelink-event.json");
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsReject()
|
||||
{
|
||||
QFETCH(QString, eventSource);
|
||||
|
||||
auto event = TestUtils::loadEventFromFile<RoomMessageEvent>(eventSource);
|
||||
auto linkPreviewer = LinkPreviewer(room, event.get());
|
||||
|
||||
QCOMPARE(linkPreviewer.empty(), true);
|
||||
QCOMPARE(linkPreviewer.url(), QUrl());
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::editedLink()
|
||||
{
|
||||
room->syncNewEvents(QStringLiteral("test-linkpreviewerintial-sync.json"));
|
||||
auto event = eventCast<const RoomMessageEvent>(room->messageEvents().at(0).get());
|
||||
auto linkPreviewer = LinkPreviewer(room, event);
|
||||
|
||||
QCOMPARE(linkPreviewer.empty(), false);
|
||||
QCOMPARE(linkPreviewer.url(), QUrl("https://kde.org"_ls));
|
||||
|
||||
room->syncNewEvents(QStringLiteral("test-linkpreviewerreplace-sync.json"));
|
||||
|
||||
QCOMPARE(linkPreviewer.empty(), true);
|
||||
QCOMPARE(linkPreviewer.url(), QUrl());
|
||||
}
|
||||
|
||||
QTEST_MAIN(LinkPreviewerTest)
|
||||
#include "linkpreviewertest.moc"
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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 <Quotient/events/event.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
|
||||
#include "neochatroom.h"
|
||||
@@ -38,4 +39,17 @@ public:
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<Quotient::EventClass EventT>
|
||||
inline Quotient::event_ptr_tt<EventT> loadEventFromFile(const QString &eventFileName)
|
||||
{
|
||||
if (!eventFileName.isEmpty()) {
|
||||
QFile testEventFile;
|
||||
testEventFile.setFileName(QLatin1String(DATA_DIR) + u'/' + eventFileName);
|
||||
testEventFile.open(QIODevice::ReadOnly);
|
||||
auto testSyncJson = QJsonDocument::fromJson(testEventFile.readAll()).object();
|
||||
return Quotient::loadEvent<EventT>(testSyncJson);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,11 +64,6 @@ private Q_SLOTS:
|
||||
void receiveRichEdited();
|
||||
void receiveLineSeparator();
|
||||
void receiveRichCodeUrl();
|
||||
|
||||
void linkPreviewsMatch_data();
|
||||
void linkPreviewsMatch();
|
||||
void linkPreviewsReject_data();
|
||||
void linkPreviewsReject();
|
||||
};
|
||||
|
||||
void TextHandlerTest::initTestCase()
|
||||
@@ -523,53 +518,6 @@ void TextHandlerTest::receiveLineSeparator()
|
||||
QCOMPARE(textHandler.handleRecievePlainText(Qt::PlainText, true), QStringLiteral("foo bar"));
|
||||
}
|
||||
|
||||
void TextHandlerTest::linkPreviewsMatch_data()
|
||||
{
|
||||
QTest::addColumn<QString>("testInputString");
|
||||
QTest::addColumn<QList<QUrl>>("testOutputLinks");
|
||||
|
||||
QTest::newRow("plainHttps") << QStringLiteral("https://kde.org") << QList<QUrl>({QUrl("https://kde.org"_ls)});
|
||||
QTest::newRow("richHttps") << QStringLiteral("<a href=\"https://kde.org\">Rich Link</a>") << QList<QUrl>({QUrl("https://kde.org"_ls)});
|
||||
QTest::newRow("plainWww") << QStringLiteral("www.example.org") << QList<QUrl>({QUrl("www.example.org"_ls)});
|
||||
QTest::newRow("multipleHttps") << QStringLiteral("https://kde.org www.example.org")
|
||||
<< QList<QUrl>({
|
||||
QUrl("https://kde.org"_ls),
|
||||
QUrl("www.example.org"_ls),
|
||||
});
|
||||
}
|
||||
|
||||
void TextHandlerTest::linkPreviewsMatch()
|
||||
{
|
||||
QFETCH(QString, testInputString);
|
||||
QFETCH(QList<QUrl>, testOutputLinks);
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.getLinkPreviews(), testOutputLinks);
|
||||
}
|
||||
|
||||
void TextHandlerTest::linkPreviewsReject_data()
|
||||
{
|
||||
QTest::addColumn<QString>("testInputString");
|
||||
QTest::addColumn<QList<QUrl>>("testOutputLinks");
|
||||
|
||||
QTest::newRow("mxc") << QStringLiteral("mxc://example.org/SEsfnsuifSDFSSEF") << QList<QUrl>();
|
||||
QTest::newRow("matrixTo") << QStringLiteral("https://matrix.to/#/@alice:example.org") << QList<QUrl>();
|
||||
QTest::newRow("noSpace") << QStringLiteral("testhttps://kde.org") << QList<QUrl>();
|
||||
}
|
||||
|
||||
void TextHandlerTest::linkPreviewsReject()
|
||||
{
|
||||
QFETCH(QString, testInputString);
|
||||
QFETCH(QList<QUrl>, testOutputLinks);
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.getLinkPreviews(), testOutputLinks);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveRichCodeUrl()
|
||||
{
|
||||
auto input = QStringLiteral("<code>https://kde.org</code>");
|
||||
|
||||
@@ -238,44 +238,11 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<categories>
|
||||
<category>Network</category>
|
||||
</categories>
|
||||
<developer_name>The KDE Community</developer_name>
|
||||
<developer_name xml:lang="ar">مجتمع كِيدِي</developer_name>
|
||||
<developer_name xml:lang="az">KDE Cəmiyyəti</developer_name>
|
||||
<developer_name xml:lang="ca">La comunitat KDE</developer_name>
|
||||
<developer_name xml:lang="ca-valencia">La comunitat KDE</developer_name>
|
||||
<developer_name xml:lang="cs">Komunita KDE</developer_name>
|
||||
<developer_name xml:lang="de">Die KDE-Gemeinschaft</developer_name>
|
||||
<developer_name xml:lang="el">Η Κοινότητα του KDE</developer_name>
|
||||
<developer_name xml:lang="en-GB">The KDE Community</developer_name>
|
||||
<developer_name xml:lang="eo">La KDE-Komunumo</developer_name>
|
||||
<developer_name xml:lang="es">La comunidad KDE</developer_name>
|
||||
<developer_name xml:lang="eu">KDE komunitatea</developer_name>
|
||||
<developer_name xml:lang="fi">KDE-yhteisö</developer_name>
|
||||
<developer_name xml:lang="fr">La communauté de KDE</developer_name>
|
||||
<developer_name xml:lang="gl">A comunidade KDE</developer_name>
|
||||
<developer_name xml:lang="hu">A KDE Közösség</developer_name>
|
||||
<developer_name xml:lang="ia">Le communitate de KDE</developer_name>
|
||||
<developer_name xml:lang="id">Komunitas KDE</developer_name>
|
||||
<developer_name xml:lang="ie">Li comunité de KDE</developer_name>
|
||||
<developer_name xml:lang="it">La comunità KDE</developer_name>
|
||||
<developer_name xml:lang="ka">KDE-ის საზოგადოება</developer_name>
|
||||
<developer_name xml:lang="ko">KDE 커뮤니티</developer_name>
|
||||
<developer_name xml:lang="nl">De KDE gemeenschap</developer_name>
|
||||
<developer_name xml:lang="nn">KDE-fellesskapet</developer_name>
|
||||
<developer_name xml:lang="pa">ਕੇਡੀਈ ਕਮਿਊਨਟੀ</developer_name>
|
||||
<developer_name xml:lang="pl">Społeczność KDE</developer_name>
|
||||
<developer_name xml:lang="pt">A Comunidade do KDE</developer_name>
|
||||
<developer_name xml:lang="pt-BR">A comunidade KDE</developer_name>
|
||||
<developer_name xml:lang="ru">Сообщество KDE</developer_name>
|
||||
<developer_name xml:lang="sk">KDE Komunita</developer_name>
|
||||
<developer_name xml:lang="sl">Skupnost KDE</developer_name>
|
||||
<developer_name xml:lang="sv">KDE-gemenskapen</developer_name>
|
||||
<developer_name xml:lang="ta">கே.டீ.யீ. சமூகம்</developer_name>
|
||||
<developer_name xml:lang="tr">KDE Topluluğu</developer_name>
|
||||
<developer_name xml:lang="uk">Спільнота KDE</developer_name>
|
||||
<developer_name xml:lang="x-test">xxThe KDE Communityxx</developer_name>
|
||||
<developer_name xml:lang="zh-CN">KDE 社区</developer_name>
|
||||
<developer_name xml:lang="zh-TW">KDE 社群</developer_name>
|
||||
<developer>
|
||||
<id>kde.org</id>
|
||||
<name>The KDE Community</name>
|
||||
<url>https://kde.org</url>
|
||||
</developer>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-3.0</project_license>
|
||||
<custom>
|
||||
|
||||
@@ -65,7 +65,7 @@ GenericName[ie]=Cliente de Matrix
|
||||
GenericName[it]=Client Matrix
|
||||
GenericName[ka]=Matrix -ის კლიენტი
|
||||
GenericName[ko]=Matrix 클라이언트
|
||||
GenericName[lt]=Matrix kliento programą
|
||||
GenericName[lt]=Matrix kliento programa
|
||||
GenericName[nl]=Matrix-client
|
||||
GenericName[nn]=Matrix-klient
|
||||
GenericName[pa]=ਮੈਟਰਿਕਸ ਕਲਾਈਂਟ
|
||||
|
||||
913
po/ar/neochat.po
913
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
894
po/az/neochat.po
894
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
879
po/ca/neochat.po
879
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
861
po/cs/neochat.po
861
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
862
po/da/neochat.po
862
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
901
po/de/neochat.po
901
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
893
po/el/neochat.po
893
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
845
po/eo/neochat.po
845
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
878
po/es/neochat.po
878
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
885
po/eu/neochat.po
885
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
888
po/fi/neochat.po
888
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
884
po/fr/neochat.po
884
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
1097
po/hu/neochat.po
1097
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
873
po/ia/neochat.po
873
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
897
po/id/neochat.po
897
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
879
po/ie/neochat.po
879
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
885
po/it/neochat.po
885
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
819
po/ja/neochat.po
819
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
872
po/ka/neochat.po
872
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
886
po/ko/neochat.po
886
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
819
po/lt/neochat.po
819
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
878
po/nl/neochat.po
878
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
858
po/nn/neochat.po
858
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
894
po/pa/neochat.po
894
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
882
po/pl/neochat.po
882
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
899
po/pt/neochat.po
899
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1296
po/ru/neochat.po
1296
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
1103
po/sk/neochat.po
1103
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
878
po/sl/neochat.po
878
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
892
po/sv/neochat.po
892
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
884
po/ta/neochat.po
884
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
876
po/tr/neochat.po
876
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
878
po/uk/neochat.po
878
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -144,6 +144,10 @@ add_library(neochat STATIC
|
||||
models/timelinemodel.cpp
|
||||
models/timelinemodel.h
|
||||
enums/pushrule.h
|
||||
models/itinerarymodel.cpp
|
||||
models/itinerarymodel.h
|
||||
proxycontroller.cpp
|
||||
proxycontroller.h
|
||||
)
|
||||
|
||||
qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
@@ -161,11 +165,10 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/UserInfoDesktop.qml
|
||||
qml/RoomPage.qml
|
||||
qml/RoomWindow.qml
|
||||
qml/JoinRoomPage.qml
|
||||
qml/ExploreRoomsPage.qml
|
||||
qml/ManualRoomDialog.qml
|
||||
qml/ExplorerDelegate.qml
|
||||
qml/InviteUserPage.qml
|
||||
qml/StartChatPage.qml
|
||||
qml/ImageEditorPage.qml
|
||||
qml/WelcomePage.qml
|
||||
qml/General.qml
|
||||
@@ -267,7 +270,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/EmojiTonesPicker.qml
|
||||
qml/EmojiDelegate.qml
|
||||
qml/EmojiGrid.qml
|
||||
qml/SearchPage.qml
|
||||
qml/RoomSearchPage.qml
|
||||
qml/LocationDelegate.qml
|
||||
qml/LocationChooser.qml
|
||||
qml/TimelineView.qml
|
||||
@@ -298,11 +301,16 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/NotificationsView.qml
|
||||
qml/LoadingDelegate.qml
|
||||
qml/TimelineEndDelegate.qml
|
||||
qml/SearchPage.qml
|
||||
qml/ServerComboBox.qml
|
||||
qml/UserSearchPage.qml
|
||||
RESOURCES
|
||||
qml/confetti.png
|
||||
qml/glowdot.png
|
||||
)
|
||||
|
||||
configure_file(config-neochat.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-neochat.h)
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(neochat PROPERTIES OUTPUT_NAME "neochatlib")
|
||||
endif()
|
||||
@@ -316,6 +324,15 @@ ecm_qt_declare_logging_category(neochat
|
||||
EXPORT NEOCHAT
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(neochat
|
||||
HEADER "publicroomlist_logging.h"
|
||||
IDENTIFIER "PublicRoomList"
|
||||
CATEGORY_NAME "org.kde.neochat.publicroomlistmodel"
|
||||
DESCRIPTION "Neochat: publicroomlistmodel"
|
||||
DEFAULT_SEVERITY Info
|
||||
EXPORT NEOCHAT
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(neochat
|
||||
HEADER "eventhandler_logging.h"
|
||||
IDENTIFIER "EventHandling"
|
||||
|
||||
8
src/config-neochat.h.in
Normal file
8
src/config-neochat.h.in
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define CMAKE_INSTALL_FULL_LIBEXECDIR_KF6 "${KDE_INSTALL_FULL_LIBEXECDIR_KF}"
|
||||
@@ -7,10 +7,7 @@
|
||||
#include <qt6keychain/keychain.h>
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <KWindowConfig>
|
||||
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QGuiApplication>
|
||||
#include <QNetworkProxy>
|
||||
#include <QQuickTextDocument>
|
||||
@@ -26,15 +23,13 @@
|
||||
#include <Quotient/csapi/logout.h>
|
||||
#include <Quotient/csapi/notifications.h>
|
||||
#include <Quotient/eventstats.h>
|
||||
#include <Quotient/jobs/downloadfilejob.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
#include <Quotient/user.h>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroom.h"
|
||||
#include "notificationsmanager.h"
|
||||
#include "proxycontroller.h"
|
||||
#include "roommanager.h"
|
||||
#include "windowcontroller.h"
|
||||
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
#include "trayicon.h"
|
||||
@@ -51,7 +46,7 @@ Controller::Controller(QObject *parent)
|
||||
{
|
||||
Connection::setRoomType<NeoChatRoom>();
|
||||
|
||||
setApplicationProxy();
|
||||
ProxyController::instance().setApplicationProxy();
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
setQuitOnLastWindowClosed();
|
||||
@@ -77,8 +72,7 @@ Controller::Controller(QObject *parent)
|
||||
});
|
||||
|
||||
#ifndef Q_OS_WINDOWS
|
||||
// Setup Unix signal handlers
|
||||
const auto unixExitHandler = [](int /*sig*/) -> void {
|
||||
const auto unixExitHandler = [](int) -> void {
|
||||
QCoreApplication::quit();
|
||||
};
|
||||
|
||||
@@ -104,7 +98,7 @@ Controller::Controller(QObject *parent)
|
||||
connect(&m_accountRegistry, &AccountRegistry::accountCountChanged, this, [this]() {
|
||||
if (m_accountRegistry.size() > oldAccountCount) {
|
||||
auto connection = dynamic_cast<NeoChatConnection *>(m_accountRegistry.accounts()[m_accountRegistry.size() - 1]);
|
||||
connect(connection, &NeoChatConnection::syncDone, this, [this, connection]() {
|
||||
connect(connection, &NeoChatConnection::syncDone, this, [connection]() {
|
||||
NotificationsManager::instance().handleNotifications(connection);
|
||||
});
|
||||
connectSingleShot(connection, &NeoChatConnection::syncDone, this, [this, connection] {
|
||||
@@ -144,7 +138,7 @@ void Controller::addConnection(NeoChatConnection *c)
|
||||
|
||||
c->setLazyLoading(true);
|
||||
|
||||
connect(c, &NeoChatConnection::syncDone, this, [this, c] {
|
||||
connect(c, &NeoChatConnection::syncDone, this, [c] {
|
||||
c->sync(30000);
|
||||
c->saveState();
|
||||
});
|
||||
@@ -152,12 +146,6 @@ void Controller::addConnection(NeoChatConnection *c)
|
||||
dropConnection(c);
|
||||
});
|
||||
|
||||
connect(c, &NeoChatConnection::requestFailed, this, [this](BaseJob *job) {
|
||||
if (job->error() == BaseJob::UserConsentRequired) {
|
||||
Q_EMIT userConsentRequired(job->errorUrl());
|
||||
}
|
||||
});
|
||||
|
||||
c->sync();
|
||||
|
||||
Q_EMIT connectionAdded(c);
|
||||
@@ -174,18 +162,13 @@ void Controller::dropConnection(NeoChatConnection *c)
|
||||
void Controller::invokeLogin()
|
||||
{
|
||||
const auto accounts = SettingsGroup("Accounts"_ls).childGroups();
|
||||
QString id = NeoChatConfig::self()->activeConnection();
|
||||
for (const auto &accountId : accounts) {
|
||||
AccountSettings account{accountId};
|
||||
m_accountsLoading += accountId;
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
if (id.isEmpty()) {
|
||||
// handle case where the account config is empty
|
||||
id = accountId;
|
||||
}
|
||||
if (!account.homeserver().isEmpty()) {
|
||||
auto accessTokenLoadingJob = loadAccessTokenFromKeyChain(account);
|
||||
connect(accessTokenLoadingJob, &QKeychain::Job::finished, this, [accountId, id, this, accessTokenLoadingJob](QKeychain::Job *) {
|
||||
connect(accessTokenLoadingJob, &QKeychain::Job::finished, this, [accountId, this, accessTokenLoadingJob](QKeychain::Job *) {
|
||||
AccountSettings account{accountId};
|
||||
QString accessToken;
|
||||
if (accessTokenLoadingJob->error() == QKeychain::Error::NoError) {
|
||||
@@ -195,28 +178,11 @@ void Controller::invokeLogin()
|
||||
}
|
||||
|
||||
auto connection = new NeoChatConnection(account.homeserver());
|
||||
connect(connection, &NeoChatConnection::connected, this, [this, connection, id] {
|
||||
connect(connection, &NeoChatConnection::connected, this, [this, connection] {
|
||||
connection->loadState();
|
||||
addConnection(connection);
|
||||
m_accountsLoading.removeAll(connection->userId());
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
if (connection->userId() == id) {
|
||||
setActiveConnection(connection);
|
||||
}
|
||||
});
|
||||
connect(connection, &NeoChatConnection::loginError, this, [this, connection](const QString &error, const QString &) {
|
||||
if (error == "Unrecognised access token"_ls) {
|
||||
Q_EMIT errorOccured(i18n("Login Failed: Access Token invalid or revoked"), {});
|
||||
connection->logout(false);
|
||||
} else if (error == "Connection closed"_ls) {
|
||||
Q_EMIT errorOccured(i18n("Login Failed: %1", error), {});
|
||||
// Failed due to network connection issue. This might happen when the homeserver is
|
||||
// temporary down, or the user trying to re-launch NeoChat in a network that cannot
|
||||
// connect to the homeserver. In this case, we don't want to do logout().
|
||||
} else {
|
||||
Q_EMIT errorOccured(i18n("Login Failed: %1", error), {});
|
||||
connection->logout(true);
|
||||
}
|
||||
});
|
||||
connect(connection, &NeoChatConnection::networkError, this, [this](const QString &error, const QString &, int, int) {
|
||||
Q_EMIT errorOccured(i18n("Network Error: %1", error), {});
|
||||
@@ -319,36 +285,10 @@ void Controller::setActiveConnection(NeoChatConnection *connection)
|
||||
if (connection == m_connection) {
|
||||
return;
|
||||
}
|
||||
if (m_connection != nullptr) {
|
||||
disconnect(m_connection, &NeoChatConnection::syncError, this, nullptr);
|
||||
disconnect(m_connection, &NeoChatConnection::accountDataChanged, this, nullptr);
|
||||
}
|
||||
m_connection = connection;
|
||||
if (connection != nullptr) {
|
||||
NeoChatConfig::self()->setActiveConnection(connection->userId());
|
||||
connect(connection, &NeoChatConnection::requestFailed, this, [](BaseJob *job) {
|
||||
if (dynamic_cast<DownloadFileJob *>(job) && job->jsonData()["errcode"_ls].toString() == "M_TOO_LARGE"_ls) {
|
||||
RoomManager::instance().warning(i18n("File too large to download."), i18n("Contact your matrix server administrator for support."));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
NeoChatConfig::self()->setActiveConnection(QString());
|
||||
}
|
||||
NeoChatConfig::self()->save();
|
||||
Q_EMIT activeConnectionChanged();
|
||||
}
|
||||
|
||||
void Controller::saveWindowGeometry()
|
||||
{
|
||||
WindowController::instance().saveGeometry();
|
||||
}
|
||||
|
||||
void Controller::forceRefreshTextDocument(QQuickTextDocument *textDocument, QQuickItem *item)
|
||||
{
|
||||
// HACK: Workaround bug QTBUG 93281
|
||||
connect(textDocument->textDocument(), SIGNAL(imagesLoaded()), item, SLOT(updateWholeDocument()));
|
||||
}
|
||||
|
||||
void Controller::listenForNotifications()
|
||||
{
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
@@ -370,36 +310,6 @@ void Controller::listenForNotifications()
|
||||
#endif
|
||||
}
|
||||
|
||||
void Controller::setApplicationProxy()
|
||||
{
|
||||
NeoChatConfig *cfg = NeoChatConfig::self();
|
||||
QNetworkProxy proxy;
|
||||
|
||||
// type match to ProxyType from neochatconfig.kcfg
|
||||
switch (cfg->proxyType()) {
|
||||
case 1: // HTTP
|
||||
proxy.setType(QNetworkProxy::HttpProxy);
|
||||
proxy.setHostName(cfg->proxyHost());
|
||||
proxy.setPort(cfg->proxyPort());
|
||||
proxy.setUser(cfg->proxyUser());
|
||||
proxy.setPassword(cfg->proxyPassword());
|
||||
break;
|
||||
case 2: // SOCKS 5
|
||||
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||||
proxy.setHostName(cfg->proxyHost());
|
||||
proxy.setPort(cfg->proxyPort());
|
||||
proxy.setUser(cfg->proxyUser());
|
||||
proxy.setPassword(cfg->proxyPassword());
|
||||
break;
|
||||
case 0: // System Default
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
QNetworkProxy::setApplicationProxy(proxy);
|
||||
}
|
||||
|
||||
bool Controller::isFlatpak() const
|
||||
{
|
||||
#ifdef NEOCHAT_FLATPAK
|
||||
|
||||
@@ -84,22 +84,8 @@ public:
|
||||
|
||||
[[nodiscard]] bool supportSystemTray() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the QNetworkProxy for the application.
|
||||
*
|
||||
* @sa QNetworkProxy::setApplicationProxy
|
||||
*/
|
||||
Q_INVOKABLE void setApplicationProxy();
|
||||
|
||||
bool isFlatpak() const;
|
||||
|
||||
/**
|
||||
* @brief Force a QQuickTextDocument to refresh when images are loaded.
|
||||
*
|
||||
* HACK: This is a workaround for QTBUG 93281.
|
||||
*/
|
||||
Q_INVOKABLE void forceRefreshTextDocument(QQuickTextDocument *textDocument, QQuickItem *item);
|
||||
|
||||
/**
|
||||
* @brief Start listening for notifications in dbus-activated mode.
|
||||
* These notifications will quit the application when closed.
|
||||
@@ -134,9 +120,5 @@ Q_SIGNALS:
|
||||
void connectionAdded(NeoChatConnection *connection);
|
||||
void connectionDropped(NeoChatConnection *connection);
|
||||
void activeConnectionChanged();
|
||||
void userConsentRequired(QUrl url);
|
||||
void accountsLoadingChanged();
|
||||
|
||||
public Q_SLOTS:
|
||||
void saveWindowGeometry();
|
||||
};
|
||||
|
||||
@@ -355,7 +355,7 @@ QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat f
|
||||
if (e.repeatsState()) {
|
||||
auto text = i18n("reinvited %1 to the room", subjectName);
|
||||
if (!e.reason().isEmpty()) {
|
||||
text += i18nc("Optional reason for an invitation", ": %1") + e.reason().toHtmlEscaped();
|
||||
text += i18nc("Optional reason for an invitation", ": %1") + (prettyPrint ? e.reason().toHtmlEscaped() : e.reason());
|
||||
}
|
||||
return text;
|
||||
}
|
||||
@@ -379,7 +379,9 @@ QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat f
|
||||
if (!e.newDisplayName()) {
|
||||
text = i18nc("their refers to a singular user", "cleared their display name");
|
||||
} else {
|
||||
text = i18nc("their refers to a singular user", "changed their display name to %1", e.newDisplayName()->toHtmlEscaped());
|
||||
text = i18nc("their refers to a singular user",
|
||||
"changed their display name to %1",
|
||||
prettyPrint ? e.newDisplayName()->toHtmlEscaped() : *e.newDisplayName());
|
||||
}
|
||||
}
|
||||
if (e.isAvatarUpdate()) {
|
||||
@@ -415,7 +417,7 @@ QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat f
|
||||
if (e.reason().isEmpty()) {
|
||||
return i18n("banned %1 from the room", subjectName);
|
||||
} else {
|
||||
return i18n("banned %1 from the room: %2", subjectName, e.reason().toHtmlEscaped());
|
||||
return i18n("banned %1 from the room: %2", subjectName, prettyPrint ? e.reason().toHtmlEscaped() : e.reason());
|
||||
}
|
||||
} else {
|
||||
return i18n("self-banned from the room");
|
||||
@@ -431,8 +433,8 @@ QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat f
|
||||
[](const RoomCanonicalAliasEvent &e) {
|
||||
return (e.alias().isEmpty()) ? i18n("cleared the room main alias") : i18n("set the room main alias to: %1", e.alias());
|
||||
},
|
||||
[](const RoomNameEvent &e) {
|
||||
return (e.name().isEmpty()) ? i18n("cleared the room name") : i18n("set the room name to: %1", e.name().toHtmlEscaped());
|
||||
[prettyPrint](const RoomNameEvent &e) {
|
||||
return (e.name().isEmpty()) ? i18n("cleared the room name") : i18n("set the room name to: %1", prettyPrint ? e.name().toHtmlEscaped() : e.name());
|
||||
},
|
||||
[prettyPrint, stripNewlines](const RoomTopicEvent &e) {
|
||||
return (e.topic().isEmpty()) ? i18n("cleared the topic")
|
||||
@@ -447,14 +449,15 @@ QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat f
|
||||
[](const EncryptionEvent &) {
|
||||
return i18n("activated End-to-End Encryption");
|
||||
},
|
||||
[](const RoomCreateEvent &e) {
|
||||
return e.isUpgrade() ? i18n("upgraded the room to version %1", e.version().isEmpty() ? "1"_ls : e.version().toHtmlEscaped())
|
||||
: i18n("created the room, version %1", e.version().isEmpty() ? "1"_ls : e.version().toHtmlEscaped());
|
||||
[prettyPrint](const RoomCreateEvent &e) {
|
||||
return e.isUpgrade()
|
||||
? i18n("upgraded the room to version %1", e.version().isEmpty() ? "1"_ls : (prettyPrint ? e.version().toHtmlEscaped() : e.version()))
|
||||
: i18n("created the room, version %1", e.version().isEmpty() ? "1"_ls : (prettyPrint ? e.version().toHtmlEscaped() : e.version()));
|
||||
},
|
||||
[](const RoomPowerLevelsEvent &) {
|
||||
return i18nc("'power level' means permission level", "changed the power levels for this room");
|
||||
},
|
||||
[](const StateEvent &e) {
|
||||
[prettyPrint](const StateEvent &e) {
|
||||
if (e.matrixType() == QLatin1String("m.room.server_acl")) {
|
||||
return i18n("changed the server access control lists for this room");
|
||||
}
|
||||
@@ -471,7 +474,7 @@ QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat f
|
||||
return e.contentJson()["description"_ls].toString();
|
||||
}
|
||||
return e.stateKey().isEmpty() ? i18n("updated %1 state", e.matrixType())
|
||||
: i18n("updated %1 state for %2", e.matrixType(), e.stateKey().toHtmlEscaped());
|
||||
: i18n("updated %1 state for %2", e.matrixType(), prettyPrint ? e.stateKey().toHtmlEscaped() : e.stateKey());
|
||||
},
|
||||
[](const PollStartEvent &e) {
|
||||
return e.question();
|
||||
@@ -774,43 +777,6 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo
|
||||
return mediaInfo;
|
||||
}
|
||||
|
||||
QSharedPointer<LinkPreviewer> EventHandler::getLinkPreviewer() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getLinkPreviewer called with m_room set to nullptr.";
|
||||
return nullptr;
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getLinkPreviewer called with m_event set to nullptr.";
|
||||
return nullptr;
|
||||
}
|
||||
if (!m_event->is<RoomMessageEvent>()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString text;
|
||||
auto event = eventCast<const RoomMessageEvent>(m_event);
|
||||
if (event->hasTextContent()) {
|
||||
auto textContent = static_cast<const EventContent::TextContent *>(event->content());
|
||||
if (textContent) {
|
||||
text = textContent->body;
|
||||
} else {
|
||||
text = event->plainBody();
|
||||
}
|
||||
} else {
|
||||
text = event->plainBody();
|
||||
}
|
||||
TextHandler textHandler;
|
||||
textHandler.setData(text);
|
||||
|
||||
QList<QUrl> links = textHandler.getLinkPreviews();
|
||||
if (links.size() > 0) {
|
||||
return QSharedPointer<LinkPreviewer>(new LinkPreviewer(nullptr, m_room, links.size() > 0 ? links[0] : QUrl()));
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool EventHandler::hasReply() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
|
||||
@@ -231,16 +231,6 @@ public:
|
||||
*/
|
||||
QVariantMap getMediaInfo() const;
|
||||
|
||||
/**
|
||||
* @brief Return a LinkPreviewer object for the event.
|
||||
*
|
||||
* A nullptr will be returned for any event that doesn't have any links so the
|
||||
* return should be null checked and an empty LinkPreviewer provided if null.
|
||||
*
|
||||
* @sa LinkPreviewer
|
||||
*/
|
||||
QSharedPointer<LinkPreviewer> getLinkPreviewer() const;
|
||||
|
||||
/**
|
||||
* @brief Whether the event is a reply to another in the timeline.
|
||||
*/
|
||||
|
||||
@@ -3,25 +3,37 @@
|
||||
|
||||
#include "linkpreviewer.h"
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/csapi/content-repo.h>
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroom.h"
|
||||
#include "utils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
LinkPreviewer::LinkPreviewer(QObject *parent, const NeoChatRoom *room, const QUrl &url)
|
||||
: QObject(parent)
|
||||
LinkPreviewer::LinkPreviewer(const NeoChatRoom *room, const Quotient::RoomMessageEvent *event)
|
||||
: QObject(nullptr)
|
||||
, m_currentRoom(room)
|
||||
, m_event(event)
|
||||
, m_loaded(false)
|
||||
, m_url(url)
|
||||
, m_url(linkPreview(event))
|
||||
{
|
||||
loadUrlPreview();
|
||||
if (m_currentRoom) {
|
||||
connect(this, &LinkPreviewer::urlChanged, this, &LinkPreviewer::emptyChanged);
|
||||
|
||||
if (m_event != nullptr && m_currentRoom != nullptr) {
|
||||
loadUrlPreview();
|
||||
connect(m_currentRoom, &NeoChatRoom::urlPreviewEnabledChanged, this, &LinkPreviewer::loadUrlPreview);
|
||||
// Make sure that we react to edits
|
||||
connect(m_currentRoom, &NeoChatRoom::replacedEvent, this, [this](const Quotient::RoomEvent *newEvent) {
|
||||
if (m_event->id() == newEvent->id()) {
|
||||
m_event = eventCast<const Quotient::RoomMessageEvent>(newEvent);
|
||||
m_url = linkPreview(m_event);
|
||||
Q_EMIT urlChanged();
|
||||
loadUrlPreview();
|
||||
}
|
||||
});
|
||||
}
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, &LinkPreviewer::loadUrlPreview);
|
||||
}
|
||||
@@ -51,15 +63,6 @@ QUrl LinkPreviewer::url() const
|
||||
return m_url;
|
||||
}
|
||||
|
||||
void LinkPreviewer::setUrl(QUrl url)
|
||||
{
|
||||
if (url != m_url) {
|
||||
m_url = url;
|
||||
urlChanged();
|
||||
loadUrlPreview();
|
||||
}
|
||||
}
|
||||
|
||||
void LinkPreviewer::loadUrlPreview()
|
||||
{
|
||||
if (!m_currentRoom || !NeoChatConfig::showLinkPreview() || !m_currentRoom->urlPreviewEnabled()) {
|
||||
@@ -98,4 +101,38 @@ bool LinkPreviewer::empty() const
|
||||
return m_url.isEmpty();
|
||||
}
|
||||
|
||||
QUrl LinkPreviewer::linkPreview(const Quotient::RoomMessageEvent *event)
|
||||
{
|
||||
if (event == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QString text;
|
||||
if (event->hasTextContent()) {
|
||||
auto textContent = static_cast<const Quotient::EventContent::TextContent *>(event->content());
|
||||
if (textContent) {
|
||||
text = textContent->body;
|
||||
} else {
|
||||
text = event->plainBody();
|
||||
}
|
||||
} else {
|
||||
text = event->plainBody();
|
||||
}
|
||||
|
||||
auto data = text.remove(TextRegex::removeRichReply);
|
||||
auto linksMatch = TextRegex::url.globalMatch(data);
|
||||
while (linksMatch.hasNext()) {
|
||||
auto link = linksMatch.next().captured();
|
||||
if (!link.contains(QStringLiteral("matrix.to"))) {
|
||||
return QUrl(link);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool LinkPreviewer::hasPreviewableLinks(const Quotient::RoomMessageEvent *event)
|
||||
{
|
||||
return !linkPreview(event).isEmpty();
|
||||
}
|
||||
|
||||
#include "moc_linkpreviewer.cpp"
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
#include <QQmlEngine>
|
||||
#include <QUrl>
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class RoomMessageEvent;
|
||||
}
|
||||
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
@@ -25,7 +30,7 @@ class LinkPreviewer : public QObject
|
||||
/**
|
||||
* @brief The URL to get the preview for.
|
||||
*/
|
||||
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
|
||||
Q_PROPERTY(QUrl url READ url NOTIFY urlChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the preview information has been loaded.
|
||||
@@ -55,18 +60,25 @@ class LinkPreviewer : public QObject
|
||||
Q_PROPERTY(bool empty READ empty NOTIFY emptyChanged)
|
||||
|
||||
public:
|
||||
explicit LinkPreviewer(QObject *parent = nullptr, const NeoChatRoom *room = nullptr, const QUrl &url = {});
|
||||
explicit LinkPreviewer(const NeoChatRoom *room = nullptr, const Quotient::RoomMessageEvent *event = nullptr);
|
||||
|
||||
[[nodiscard]] QUrl url() const;
|
||||
void setUrl(QUrl);
|
||||
[[nodiscard]] bool loaded() const;
|
||||
[[nodiscard]] QString title() const;
|
||||
[[nodiscard]] QString description() const;
|
||||
[[nodiscard]] QUrl imageSource() const;
|
||||
[[nodiscard]] bool empty() const;
|
||||
|
||||
/**
|
||||
* @brief Whether the given event has at least 1 pre-viewable link.
|
||||
*
|
||||
* A link is only pre-viewable if it is http, https or something starting with www.
|
||||
*/
|
||||
static bool hasPreviewableLinks(const Quotient::RoomMessageEvent *event);
|
||||
|
||||
private:
|
||||
const NeoChatRoom *m_currentRoom = nullptr;
|
||||
const NeoChatRoom *m_currentRoom;
|
||||
const Quotient::RoomMessageEvent *m_event;
|
||||
|
||||
bool m_loaded;
|
||||
QString m_title = QString();
|
||||
@@ -76,6 +88,14 @@ private:
|
||||
|
||||
void loadUrlPreview();
|
||||
|
||||
/**
|
||||
* @brief Return the link to be previewed from the given event.
|
||||
*
|
||||
* This function is designed to give only links that should be previewed so
|
||||
* http, https or something starting with www. The first valid link is returned.
|
||||
*/
|
||||
static QUrl linkPreview(const Quotient::RoomMessageEvent *event);
|
||||
|
||||
Q_SIGNALS:
|
||||
void loadedChanged();
|
||||
void titleChanged();
|
||||
|
||||
@@ -130,7 +130,7 @@ int main(int argc, char *argv[])
|
||||
QStringLiteral(NEOCHAT_VERSION_STRING),
|
||||
i18n("Matrix client"),
|
||||
KAboutLicense::GPL_V3,
|
||||
i18n("© 2018-2020 Black Hat, 2020-2023 KDE Community"));
|
||||
i18n("© 2018-2020 Black Hat, 2020-2024 KDE Community"));
|
||||
about.addAuthor(i18n("Carl Schwan"),
|
||||
i18n("Maintainer"),
|
||||
QStringLiteral("carl@carlschwan.eu"),
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include "controller.h"
|
||||
#include "neochatconnection.h"
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "actionsmodel.h"
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "controller.h"
|
||||
#include "neochatroom.h"
|
||||
#include "roommanager.h"
|
||||
#include <Quotient/events/roommemberevent.h>
|
||||
|
||||
@@ -6,10 +6,8 @@
|
||||
#include <QImage>
|
||||
#include <QMimeDatabase>
|
||||
|
||||
#include "controller.h"
|
||||
#include "emojimodel.h"
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/csapi/account-data.h>
|
||||
#include <Quotient/csapi/content-repo.h>
|
||||
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
#include <QAbstractListModel>
|
||||
#include <QQmlEngine>
|
||||
#include <QRegularExpression>
|
||||
#include <memory>
|
||||
|
||||
class NeoChatConnection;
|
||||
#include "neochatconnection.h"
|
||||
|
||||
struct CustomEmoji {
|
||||
QString name; // with :semicolons:
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "devicesmodel.h"
|
||||
|
||||
#include "controller.h"
|
||||
#include "jobs/neochatdeletedevicejob.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
163
src/models/itinerarymodel.cpp
Normal file
163
src/models/itinerarymodel.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "itinerarymodel.h"
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
#include "config-neochat.h"
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
#include <KIO/ApplicationLauncherJob>
|
||||
#endif
|
||||
|
||||
ItineraryModel::ItineraryModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void ItineraryModel::setConnection(NeoChatConnection *connection)
|
||||
{
|
||||
if (m_connection == connection) {
|
||||
return;
|
||||
}
|
||||
m_connection = connection;
|
||||
Q_EMIT connectionChanged();
|
||||
}
|
||||
|
||||
NeoChatConnection *ItineraryModel::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
QVariant ItineraryModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return {};
|
||||
}
|
||||
auto row = index.row();
|
||||
auto data = m_data[row];
|
||||
if (role == NameRole) {
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("TrainReservation")) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("trainNumber")];
|
||||
}
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("LodgingReservation")) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("name")];
|
||||
}
|
||||
}
|
||||
if (role == TypeRole) {
|
||||
return data[QStringLiteral("@type")];
|
||||
}
|
||||
if (role == DepartureStationRole) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("departureStation")][QStringLiteral("name")];
|
||||
}
|
||||
if (role == ArrivalStationRole) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("arrivalStation")][QStringLiteral("name")];
|
||||
}
|
||||
if (role == DepartureTimeRole) {
|
||||
const auto &time = data[QStringLiteral("reservationFor")][QStringLiteral("departureTime")];
|
||||
auto dateTime = (time.isString() ? time : time[QStringLiteral("@value")]).toVariant().toDateTime();
|
||||
if (const auto &timeZone = time[QStringLiteral("timezone")].toString(); timeZone.length() > 0) {
|
||||
dateTime.setTimeZone(QTimeZone(timeZone.toLatin1().data()));
|
||||
}
|
||||
return dateTime.toString(QLocale::system().dateTimeFormat(QLocale::ShortFormat));
|
||||
}
|
||||
if (role == ArrivalTimeRole) {
|
||||
const auto &time = data[QStringLiteral("reservationFor")][QStringLiteral("arrivalTime")];
|
||||
auto dateTime = (time.isString() ? time : time[QStringLiteral("@value")]).toVariant().toDateTime();
|
||||
if (const auto &timeZone = time[QStringLiteral("timezone")].toString(); timeZone.length() > 0) {
|
||||
dateTime.setTimeZone(QTimeZone(timeZone.toLatin1().data()));
|
||||
}
|
||||
return dateTime.toString(QLocale::system().dateTimeFormat(QLocale::ShortFormat));
|
||||
}
|
||||
if (role == AddressRole) {
|
||||
const auto &addressData = data[QStringLiteral("reservationFor")][QStringLiteral("address")];
|
||||
return QStringLiteral("%1 - %2 %3 %4")
|
||||
.arg(addressData[QStringLiteral("streetAddress")].toString(),
|
||||
addressData[QStringLiteral("postalCode")].toString(),
|
||||
addressData[QStringLiteral("addressLocality")].toString(),
|
||||
addressData[QStringLiteral("addressCountry")].toString());
|
||||
}
|
||||
if (role == StartTimeRole) {
|
||||
auto dateTime = data[QStringLiteral("checkinTime")][QStringLiteral("@value")].toVariant().toDateTime();
|
||||
return dateTime.toString(QLocale::system().dateTimeFormat(QLocale::ShortFormat));
|
||||
}
|
||||
if (role == EndTimeRole) {
|
||||
auto dateTime = data[QStringLiteral("checkoutTime")][QStringLiteral("@value")].toVariant().toDateTime();
|
||||
return dateTime.toString(QLocale::system().dateTimeFormat(QLocale::ShortFormat));
|
||||
}
|
||||
if (role == DeparturePlatformRole) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("departurePlatform")];
|
||||
}
|
||||
if (role == ArrivalPlatformRole) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("arrivalPlatform")];
|
||||
}
|
||||
if (role == CoachRole) {
|
||||
return data[QStringLiteral("reservedTicket")][QStringLiteral("ticketedSeat")][QStringLiteral("seatSection")];
|
||||
}
|
||||
if (role == SeatRole) {
|
||||
return data[QStringLiteral("reservedTicket")][QStringLiteral("ticketedSeat")][QStringLiteral("seatNumber")];
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
int ItineraryModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ItineraryModel::roleNames() const
|
||||
{
|
||||
return {
|
||||
{NameRole, "name"},
|
||||
{TypeRole, "type"},
|
||||
{DepartureStationRole, "departureStation"},
|
||||
{ArrivalStationRole, "arrivalStation"},
|
||||
{DepartureTimeRole, "departureTime"},
|
||||
{ArrivalTimeRole, "arrivalTime"},
|
||||
{AddressRole, "address"},
|
||||
{StartTimeRole, "startTime"},
|
||||
{EndTimeRole, "endTime"},
|
||||
{DeparturePlatformRole, "departurePlatform"},
|
||||
{ArrivalPlatformRole, "arrivalPlatform"},
|
||||
{CoachRole, "coach"},
|
||||
{SeatRole, "seat"},
|
||||
};
|
||||
}
|
||||
|
||||
QString ItineraryModel::path() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
void ItineraryModel::setPath(const QString &path)
|
||||
{
|
||||
if (path == m_path) {
|
||||
return;
|
||||
}
|
||||
m_path = path;
|
||||
Q_EMIT pathChanged();
|
||||
loadData();
|
||||
}
|
||||
|
||||
void ItineraryModel::loadData()
|
||||
{
|
||||
auto process = new QProcess(this);
|
||||
process->start(QLatin1String(CMAKE_INSTALL_FULL_LIBEXECDIR_KF6) + QLatin1String("/kitinerary-extractor"), {m_path.mid(7)});
|
||||
connect(process, &QProcess::finished, this, [this, process]() {
|
||||
auto data = process->readAllStandardOutput();
|
||||
beginResetModel();
|
||||
m_data = QJsonDocument::fromJson(data).array();
|
||||
endResetModel();
|
||||
});
|
||||
}
|
||||
|
||||
void ItineraryModel::sendToItinerary()
|
||||
{
|
||||
#ifndef Q_OS_ANDROID
|
||||
auto job = new KIO::ApplicationLauncherJob(KService::serviceByDesktopName(QStringLiteral("org.kde.itinerary")));
|
||||
job->setUrls({QUrl::fromLocalFile(m_path.mid(7))});
|
||||
job->start();
|
||||
#endif
|
||||
}
|
||||
62
src/models/itinerarymodel.h
Normal file
62
src/models/itinerarymodel.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QPointer>
|
||||
#include <QQmlEngine>
|
||||
#include <QString>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
|
||||
class ItineraryModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
NameRole = Qt::DisplayRole,
|
||||
TypeRole,
|
||||
DepartureStationRole,
|
||||
ArrivalStationRole,
|
||||
DepartureTimeRole,
|
||||
ArrivalTimeRole,
|
||||
AddressRole,
|
||||
StartTimeRole,
|
||||
EndTimeRole,
|
||||
DeparturePlatformRole,
|
||||
ArrivalPlatformRole,
|
||||
CoachRole,
|
||||
SeatRole,
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
explicit ItineraryModel(QObject *parent = nullptr);
|
||||
|
||||
void setConnection(NeoChatConnection *connection);
|
||||
NeoChatConnection *connection() const;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
int rowCount(const QModelIndex &parent = {}) const override;
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
QString path() const;
|
||||
void setPath(const QString &path);
|
||||
|
||||
Q_INVOKABLE void sendToItinerary();
|
||||
|
||||
Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
void pathChanged();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
QJsonArray m_data;
|
||||
QString m_path;
|
||||
void loadData();
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#include "messageeventmodel.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "messageeventmodel_logging.h"
|
||||
|
||||
#include "neochatconfig.h"
|
||||
@@ -22,6 +23,7 @@
|
||||
#include "eventhandler.h"
|
||||
#include "events/pollevent.h"
|
||||
#include "models/reactionmodel.h"
|
||||
#include "texthandler.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
@@ -237,6 +239,10 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
});
|
||||
connect(m_currentRoom, &Room::replacedEvent, this, [this](const RoomEvent *newEvent) {
|
||||
refreshLastUserEvents(refreshEvent(newEvent->id()) - timelineBaseIndex());
|
||||
const RoomMessageEvent *message = eventCast<const RoomMessageEvent>(newEvent);
|
||||
if (message != nullptr) {
|
||||
createEventObjects(message);
|
||||
}
|
||||
});
|
||||
connect(m_currentRoom, &Room::updatedEvent, this, [this](const QString &eventId) {
|
||||
if (eventId.isEmpty()) { // How did we get here?
|
||||
@@ -720,14 +726,14 @@ void MessageEventModel::createEventObjects(const Quotient::RoomMessageEvent *eve
|
||||
{
|
||||
auto eventId = event->id();
|
||||
|
||||
EventHandler eventHandler;
|
||||
eventHandler.setRoom(m_currentRoom);
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
if (auto linkPreviewer = eventHandler.getLinkPreviewer()) {
|
||||
m_linkPreviewers[eventId] = linkPreviewer;
|
||||
if (m_linkPreviewers.contains(eventId)) {
|
||||
if (!LinkPreviewer::hasPreviewableLinks(event)) {
|
||||
m_linkPreviewers.remove(eventId);
|
||||
}
|
||||
} else {
|
||||
m_linkPreviewers.remove(eventId);
|
||||
if (LinkPreviewer::hasPreviewableLinks(event)) {
|
||||
m_linkPreviewers[eventId] = QSharedPointer<LinkPreviewer>(new LinkPreviewer(m_currentRoom, event));
|
||||
}
|
||||
}
|
||||
|
||||
// ReactionModel handles updates to add and remove reactions, we only need to
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
|
||||
#include "publicroomlist_logging.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
PublicRoomListModel::PublicRoomListModel(QObject *parent)
|
||||
@@ -41,7 +43,7 @@ void PublicRoomListModel::setConnection(Connection *conn)
|
||||
if (job) {
|
||||
job->abandon();
|
||||
job = nullptr;
|
||||
Q_EMIT loadingChanged();
|
||||
Q_EMIT searchingChanged();
|
||||
}
|
||||
|
||||
if (m_connection) {
|
||||
@@ -50,7 +52,6 @@ void PublicRoomListModel::setConnection(Connection *conn)
|
||||
|
||||
Q_EMIT connectionChanged();
|
||||
Q_EMIT serverChanged();
|
||||
Q_EMIT hasMoreChanged();
|
||||
}
|
||||
|
||||
QString PublicRoomListModel::server() const
|
||||
@@ -71,14 +72,13 @@ void PublicRoomListModel::setServer(const QString &value)
|
||||
nextBatch = QString();
|
||||
attempted = false;
|
||||
rooms.clear();
|
||||
Q_EMIT loadingChanged();
|
||||
|
||||
endResetModel();
|
||||
|
||||
if (job) {
|
||||
job->abandon();
|
||||
job = nullptr;
|
||||
Q_EMIT loadingChanged();
|
||||
Q_EMIT searchingChanged();
|
||||
}
|
||||
|
||||
if (m_connection) {
|
||||
@@ -86,42 +86,30 @@ void PublicRoomListModel::setServer(const QString &value)
|
||||
}
|
||||
|
||||
Q_EMIT serverChanged();
|
||||
Q_EMIT hasMoreChanged();
|
||||
}
|
||||
|
||||
QString PublicRoomListModel::keyword() const
|
||||
QString PublicRoomListModel::searchText() const
|
||||
{
|
||||
return m_keyword;
|
||||
return m_searchText;
|
||||
}
|
||||
|
||||
void PublicRoomListModel::setKeyword(const QString &value)
|
||||
void PublicRoomListModel::setSearchText(const QString &value)
|
||||
{
|
||||
if (m_keyword == value) {
|
||||
if (m_searchText == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_keyword = value;
|
||||
|
||||
beginResetModel();
|
||||
m_searchText = value;
|
||||
Q_EMIT searchTextChanged();
|
||||
|
||||
nextBatch = QString();
|
||||
attempted = false;
|
||||
rooms.clear();
|
||||
|
||||
endResetModel();
|
||||
|
||||
if (job) {
|
||||
job->abandon();
|
||||
job = nullptr;
|
||||
Q_EMIT loadingChanged();
|
||||
Q_EMIT searchingChanged();
|
||||
}
|
||||
|
||||
if (m_connection) {
|
||||
next();
|
||||
}
|
||||
|
||||
Q_EMIT keywordChanged();
|
||||
Q_EMIT hasMoreChanged();
|
||||
}
|
||||
|
||||
bool PublicRoomListModel::showOnlySpaces() const
|
||||
@@ -138,15 +126,28 @@ void PublicRoomListModel::setShowOnlySpaces(bool showOnlySpaces)
|
||||
Q_EMIT showOnlySpacesChanged();
|
||||
}
|
||||
|
||||
void PublicRoomListModel::next(int count)
|
||||
void PublicRoomListModel::search(int limit)
|
||||
{
|
||||
if (count < 1) {
|
||||
if (limit < 1 || attempted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (job) {
|
||||
qDebug() << "PublicRoomListModel: Other jobs running, ignore";
|
||||
qCDebug(PublicRoomList) << "Other job running, ignore";
|
||||
return;
|
||||
}
|
||||
|
||||
next(limit);
|
||||
}
|
||||
|
||||
void PublicRoomListModel::next(int limit)
|
||||
{
|
||||
if (m_connection == nullptr || limit < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (job) {
|
||||
qCDebug(PublicRoomList) << "Other job running, ignore";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -154,11 +155,17 @@ void PublicRoomListModel::next(int count)
|
||||
if (m_showOnlySpaces) {
|
||||
roomTypes += QLatin1String("m.space");
|
||||
}
|
||||
job = m_connection->callApi<QueryPublicRoomsJob>(m_server, count, nextBatch, QueryPublicRoomsJob::Filter{m_keyword, roomTypes});
|
||||
Q_EMIT loadingChanged();
|
||||
job = m_connection->callApi<QueryPublicRoomsJob>(m_server, limit, nextBatch, QueryPublicRoomsJob::Filter{m_searchText, roomTypes});
|
||||
Q_EMIT searchingChanged();
|
||||
|
||||
connect(job, &BaseJob::finished, this, [this] {
|
||||
attempted = true;
|
||||
if (!attempted) {
|
||||
beginResetModel();
|
||||
rooms.clear();
|
||||
endResetModel();
|
||||
|
||||
attempted = true;
|
||||
}
|
||||
|
||||
if (job->status() == BaseJob::Success) {
|
||||
nextBatch = job->nextBatch();
|
||||
@@ -166,14 +173,10 @@ void PublicRoomListModel::next(int count)
|
||||
this->beginInsertRows({}, rooms.count(), rooms.count() + job->chunk().count() - 1);
|
||||
rooms.append(job->chunk());
|
||||
this->endInsertRows();
|
||||
|
||||
if (job->nextBatch().isEmpty()) {
|
||||
Q_EMIT hasMoreChanged();
|
||||
}
|
||||
}
|
||||
|
||||
this->job = nullptr;
|
||||
Q_EMIT loadingChanged();
|
||||
Q_EMIT searchingChanged();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -184,8 +187,7 @@ QVariant PublicRoomListModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
|
||||
if (index.row() >= rooms.count()) {
|
||||
qDebug() << "PublicRoomListModel, something's wrong: index.row() >= "
|
||||
"rooms.count()";
|
||||
qCDebug(PublicRoomList) << "something's wrong: index.row() >= rooms.count()";
|
||||
return {};
|
||||
}
|
||||
auto room = rooms.at(index.row());
|
||||
@@ -271,12 +273,19 @@ int PublicRoomListModel::rowCount(const QModelIndex &parent) const
|
||||
return rooms.count();
|
||||
}
|
||||
|
||||
bool PublicRoomListModel::hasMore() const
|
||||
bool PublicRoomListModel::canFetchMore(const QModelIndex &parent) const
|
||||
{
|
||||
return !(attempted && nextBatch.isEmpty());
|
||||
Q_UNUSED(parent)
|
||||
return !nextBatch.isEmpty();
|
||||
}
|
||||
|
||||
bool PublicRoomListModel::loading() const
|
||||
void PublicRoomListModel::fetchMore(const QModelIndex &parent)
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
next();
|
||||
}
|
||||
|
||||
bool PublicRoomListModel::searching() const
|
||||
{
|
||||
return job != nullptr;
|
||||
}
|
||||
|
||||
@@ -41,9 +41,9 @@ class PublicRoomListModel : public QAbstractListModel
|
||||
Q_PROPERTY(QString server READ server WRITE setServer NOTIFY serverChanged)
|
||||
|
||||
/**
|
||||
* @brief The filter keyword for the list of public rooms.
|
||||
* @brief The text to search the public room list for.
|
||||
*/
|
||||
Q_PROPERTY(QString keyword READ keyword WRITE setKeyword NOTIFY keywordChanged)
|
||||
Q_PROPERTY(QString searchText READ searchText WRITE setSearchText NOTIFY searchTextChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether only space rooms should be shown.
|
||||
@@ -51,14 +51,9 @@ class PublicRoomListModel : public QAbstractListModel
|
||||
Q_PROPERTY(bool showOnlySpaces READ showOnlySpaces WRITE setShowOnlySpaces NOTIFY showOnlySpacesChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the model has more items to load.
|
||||
* @brief Whether the model is searching.
|
||||
*/
|
||||
Q_PROPERTY(bool hasMore READ hasMore NOTIFY hasMoreChanged)
|
||||
|
||||
/**
|
||||
* @biref Whether the model is still loading.
|
||||
*/
|
||||
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
|
||||
Q_PROPERTY(bool searching READ searching NOTIFY searchingChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -105,31 +100,38 @@ public:
|
||||
[[nodiscard]] QString server() const;
|
||||
void setServer(const QString &value);
|
||||
|
||||
[[nodiscard]] QString keyword() const;
|
||||
void setKeyword(const QString &value);
|
||||
[[nodiscard]] QString searchText() const;
|
||||
void setSearchText(const QString &searchText);
|
||||
|
||||
[[nodiscard]] bool showOnlySpaces() const;
|
||||
void setShowOnlySpaces(bool showOnlySpaces);
|
||||
|
||||
[[nodiscard]] bool hasMore() const;
|
||||
[[nodiscard]] bool searching() const;
|
||||
|
||||
[[nodiscard]] bool loading() const;
|
||||
/**
|
||||
* @brief Search the room directory.
|
||||
*
|
||||
* @param limit the maximum number of rooms to load.
|
||||
*/
|
||||
Q_INVOKABLE void search(int limit = 50);
|
||||
|
||||
private:
|
||||
QPointer<Quotient::Connection> m_connection = nullptr;
|
||||
QString m_server;
|
||||
QString m_searchText;
|
||||
bool m_showOnlySpaces = false;
|
||||
|
||||
/**
|
||||
* @brief Load the next set of rooms.
|
||||
*
|
||||
* @param count the maximum number of rooms to load.
|
||||
* @param limit the maximum number of rooms to load.
|
||||
*/
|
||||
Q_INVOKABLE void next(int count = 50);
|
||||
|
||||
private:
|
||||
Quotient::Connection *m_connection = nullptr;
|
||||
QString m_server;
|
||||
QString m_keyword;
|
||||
bool m_showOnlySpaces = false;
|
||||
void next(int limit = 50);
|
||||
bool canFetchMore(const QModelIndex &parent) const override;
|
||||
void fetchMore(const QModelIndex &parent) override;
|
||||
|
||||
bool attempted = false;
|
||||
bool m_loading = false;
|
||||
bool m_searching = false;
|
||||
QString nextBatch;
|
||||
|
||||
QList<Quotient::PublicRoomsChunk> rooms;
|
||||
@@ -139,8 +141,7 @@ private:
|
||||
Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
void serverChanged();
|
||||
void keywordChanged();
|
||||
void searchTextChanged();
|
||||
void showOnlySpacesChanged();
|
||||
void hasMoreChanged();
|
||||
void loadingChanged();
|
||||
void searchingChanged();
|
||||
};
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <Quotient/csapi/pushrules.h>
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
|
||||
#include "controller.h"
|
||||
#include "neochatconfig.h"
|
||||
|
||||
#include <KLazyLocalizedString>
|
||||
|
||||
@@ -22,6 +22,7 @@ class ReactionModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
public:
|
||||
/**
|
||||
|
||||
@@ -3,15 +3,12 @@
|
||||
|
||||
#include "roomlistmodel.h"
|
||||
|
||||
#include "controller.h"
|
||||
#include "eventhandler.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroom.h"
|
||||
#include "roommanager.h"
|
||||
#include "spacehierarchycache.h"
|
||||
|
||||
#include <Quotient/user.h>
|
||||
|
||||
#include <QDebug>
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
|
||||
#ifndef Q_OS_ANDROID
|
||||
@@ -23,7 +20,6 @@
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <QGuiApplication>
|
||||
#include <utility>
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
@@ -349,7 +345,7 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
|
||||
return m_categoryVisibility.value(data(index, CategoryRole).toInt(), true);
|
||||
}
|
||||
if (role == SubtitleTextRole) {
|
||||
if (room->lastEventIsSpoiler()) {
|
||||
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
|
||||
return QString();
|
||||
}
|
||||
EventHandler eventHandler;
|
||||
@@ -372,6 +368,9 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
|
||||
if (role == ReplacementIdRole) {
|
||||
return room->successorId();
|
||||
}
|
||||
if (role == IsDirectChat) {
|
||||
return room->isDirectChat();
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
@@ -405,6 +404,7 @@ QHash<int, QByteArray> RoomListModel::roleNames() const
|
||||
roles[IsSpaceRole] = "isSpace";
|
||||
roles[RoomIdRole] = "roomId";
|
||||
roles[IsChildSpaceRole] = "isChildSpace";
|
||||
roles[IsDirectChat] = "isDirectChat";
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -416,7 +416,7 @@ QString RoomListModel::categoryName(int category)
|
||||
case NeoChatRoomType::Favorite:
|
||||
return i18n("Favorite");
|
||||
case NeoChatRoomType::Direct:
|
||||
return i18n("Direct Messages");
|
||||
return i18n("Friends");
|
||||
case NeoChatRoomType::Normal:
|
||||
return i18n("Normal");
|
||||
case NeoChatRoomType::Deprioritized:
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/events/roomevent.h>
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QQmlEngine>
|
||||
|
||||
@@ -79,6 +77,7 @@ public:
|
||||
IsSpaceRole, /**< Whether the room is a space. */
|
||||
IsChildSpaceRole, /**< Whether this space is a child of a different space. */
|
||||
ReplacementIdRole, /**< The room id of the room replacing this one, if any. */
|
||||
IsDirectChat, /**< Whether this room is a direct chat. */
|
||||
};
|
||||
Q_ENUM(EventRoles)
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ void SearchModel::setSearchText(const QString &searchText)
|
||||
|
||||
void SearchModel::search()
|
||||
{
|
||||
Q_ASSERT(m_connection);
|
||||
Q_ASSERT(m_room);
|
||||
setSearching(true);
|
||||
if (m_job) {
|
||||
m_job->abandon();
|
||||
@@ -62,7 +62,7 @@ void SearchModel::search()
|
||||
|
||||
};
|
||||
|
||||
auto job = m_connection->callApi<SearchJob>(SearchJob::Categories{criteria});
|
||||
auto job = m_room->connection()->callApi<SearchJob>(SearchJob::Categories{criteria});
|
||||
m_job = job;
|
||||
connect(job, &BaseJob::finished, this, [this, job] {
|
||||
beginResetModel();
|
||||
@@ -74,17 +74,6 @@ void SearchModel::search()
|
||||
});
|
||||
}
|
||||
|
||||
Connection *SearchModel::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
void SearchModel::setConnection(Connection *connection)
|
||||
{
|
||||
m_connection = connection;
|
||||
Q_EMIT connectionChanged();
|
||||
}
|
||||
|
||||
QVariant SearchModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
auto row = index.row();
|
||||
@@ -136,6 +125,10 @@ QVariant SearchModel::data(const QModelIndex &index, int role) const
|
||||
return eventHandler.isHighlighted();
|
||||
case EventIdRole:
|
||||
return eventHandler.getId();
|
||||
case IsThreadedRole:
|
||||
return eventHandler.isThreaded();
|
||||
case ThreadRootRole:
|
||||
return eventHandler.threadRoot();
|
||||
}
|
||||
return DelegateType::Message;
|
||||
}
|
||||
@@ -181,6 +174,8 @@ QHash<int, QByteArray> SearchModel::roleNames() const
|
||||
{MimeTypeRole, "mimeType"},
|
||||
{ShowLinkPreviewRole, "showLinkPreview"},
|
||||
{LinkPreviewRole, "linkPreview"},
|
||||
{IsThreadedRole, "isThreaded"},
|
||||
{ThreadRootRole, "threadRoot"},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,6 @@ class SearchModel : public QAbstractListModel
|
||||
*/
|
||||
Q_PROPERTY(QString searchText READ searchText WRITE setSearchText NOTIFY searchTextChanged)
|
||||
|
||||
/**
|
||||
* @brief The current connection that the model is using to search for messages.
|
||||
*/
|
||||
Q_PROPERTY(Quotient::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
|
||||
/**
|
||||
* @brief The current room that the search is being done from.
|
||||
*/
|
||||
@@ -85,6 +80,8 @@ public:
|
||||
MimeTypeRole,
|
||||
ShowLinkPreviewRole,
|
||||
LinkPreviewRole,
|
||||
IsThreadedRole,
|
||||
ThreadRootRole,
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
explicit SearchModel(QObject *parent = nullptr);
|
||||
@@ -92,9 +89,6 @@ public:
|
||||
QString searchText() const;
|
||||
void setSearchText(const QString &searchText);
|
||||
|
||||
Quotient::Connection *connection() const;
|
||||
void setConnection(Quotient::Connection *connection);
|
||||
|
||||
NeoChatRoom *room() const;
|
||||
void setRoom(NeoChatRoom *room);
|
||||
|
||||
@@ -128,7 +122,6 @@ public:
|
||||
|
||||
Q_SIGNALS:
|
||||
void searchTextChanged();
|
||||
void connectionChanged();
|
||||
void roomChanged();
|
||||
void searchingChanged();
|
||||
|
||||
@@ -139,7 +132,6 @@ private:
|
||||
void setSearching(bool searching);
|
||||
|
||||
QString m_searchText;
|
||||
Quotient::Connection *m_connection = nullptr;
|
||||
NeoChatRoom *m_room = nullptr;
|
||||
Quotient::Omittable<Quotient::SearchJob::ResultRoomEvents> m_result = Quotient::none;
|
||||
Quotient::SearchJob *m_job = nullptr;
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include "serverlistmodel.h"
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
|
||||
#include <QDebug>
|
||||
@@ -13,6 +11,8 @@
|
||||
#include <KConfigGroup>
|
||||
#include <KSharedConfig>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
|
||||
ServerListModel::ServerListModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
|
||||
@@ -83,6 +83,21 @@ bool SortFilterRoomListModel::filterAcceptsRow(int source_row, const QModelIndex
|
||||
{
|
||||
Q_UNUSED(source_parent);
|
||||
|
||||
bool acceptRoom =
|
||||
sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::DisplayNameRole).toString().contains(m_filterText, Qt::CaseInsensitive)
|
||||
&& sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::IsSpaceRole).toBool() == false;
|
||||
|
||||
bool isDirectChat = sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::IsDirectChat).toBool();
|
||||
// In `show direct chats` mode we only care about whether or not it's a direct chat or if the filter string matches.'
|
||||
if (m_mode == DirectChats) {
|
||||
return isDirectChat && acceptRoom;
|
||||
}
|
||||
|
||||
// When not in `show direct chats` mode, filter them out.
|
||||
if (isDirectChat && m_mode == Rooms) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::JoinStateRole).toString() == QStringLiteral("upgraded")
|
||||
&& dynamic_cast<RoomListModel *>(sourceModel())
|
||||
->connection()
|
||||
@@ -90,10 +105,6 @@ bool SortFilterRoomListModel::filterAcceptsRow(int source_row, const QModelIndex
|
||||
return false;
|
||||
}
|
||||
|
||||
bool acceptRoom =
|
||||
sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::DisplayNameRole).toString().contains(m_filterText, Qt::CaseInsensitive)
|
||||
&& sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::IsSpaceRole).toBool() == false;
|
||||
|
||||
if (m_activeSpaceId.isEmpty()) {
|
||||
return acceptRoom;
|
||||
} else {
|
||||
@@ -116,4 +127,20 @@ void SortFilterRoomListModel::setActiveSpaceId(const QString &spaceId)
|
||||
invalidate();
|
||||
}
|
||||
|
||||
SortFilterRoomListModel::Mode SortFilterRoomListModel::mode() const
|
||||
{
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
void SortFilterRoomListModel::setMode(SortFilterRoomListModel::Mode mode)
|
||||
{
|
||||
if (m_mode == mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_mode = mode;
|
||||
Q_EMIT modeChanged();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
#include "moc_sortfilterroomlistmodel.cpp"
|
||||
|
||||
@@ -47,6 +47,11 @@ class SortFilterRoomListModel : public QSortFilterProxyModel
|
||||
*/
|
||||
Q_PROPERTY(QString activeSpaceId READ activeSpaceId WRITE setActiveSpaceId NOTIFY activeSpaceIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether only direct chats should be shown.
|
||||
*/
|
||||
Q_PROPERTY(Mode mode READ mode WRITE setMode NOTIFY modeChanged)
|
||||
|
||||
public:
|
||||
enum RoomSortOrder {
|
||||
Alphabetical,
|
||||
@@ -55,6 +60,13 @@ public:
|
||||
};
|
||||
Q_ENUM(RoomSortOrder)
|
||||
|
||||
enum Mode {
|
||||
Rooms,
|
||||
DirectChats,
|
||||
All,
|
||||
};
|
||||
Q_ENUM(Mode)
|
||||
|
||||
explicit SortFilterRoomListModel(QObject *parent = nullptr);
|
||||
|
||||
void setRoomSortOrder(RoomSortOrder sortOrder);
|
||||
@@ -66,6 +78,9 @@ public:
|
||||
QString activeSpaceId() const;
|
||||
void setActiveSpaceId(const QString &spaceId);
|
||||
|
||||
Mode mode() const;
|
||||
void setMode(Mode mode);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Returns true if the value of source_left is less than source_right.
|
||||
@@ -85,9 +100,11 @@ Q_SIGNALS:
|
||||
void roomSortOrderChanged();
|
||||
void filterTextChanged();
|
||||
void activeSpaceIdChanged();
|
||||
void modeChanged();
|
||||
|
||||
private:
|
||||
RoomSortOrder m_sortOrder = Categories;
|
||||
Mode m_mode = All;
|
||||
QString m_filterText;
|
||||
QString m_activeSpaceId;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/room.h>
|
||||
|
||||
#include "controller.h"
|
||||
#include "neochatconnection.h"
|
||||
|
||||
SpaceChildrenModel::SpaceChildrenModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "spacetreeitem.h"
|
||||
|
||||
#include "controller.h"
|
||||
#include "neochatconnection.h"
|
||||
|
||||
SpaceTreeItem::SpaceTreeItem(NeoChatConnection *connection,
|
||||
SpaceTreeItem *parent,
|
||||
|
||||
@@ -26,7 +26,6 @@ void UserDirectoryListModel::setConnection(Connection *conn)
|
||||
|
||||
beginResetModel();
|
||||
|
||||
m_limited = false;
|
||||
attempted = false;
|
||||
users.clear();
|
||||
|
||||
@@ -37,53 +36,50 @@ void UserDirectoryListModel::setConnection(Connection *conn)
|
||||
endResetModel();
|
||||
|
||||
m_connection = conn;
|
||||
|
||||
if (job) {
|
||||
job->abandon();
|
||||
job = nullptr;
|
||||
}
|
||||
|
||||
Q_EMIT connectionChanged();
|
||||
Q_EMIT limitedChanged();
|
||||
|
||||
if (m_job) {
|
||||
m_job->abandon();
|
||||
m_job = nullptr;
|
||||
Q_EMIT searchingChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString UserDirectoryListModel::keyword() const
|
||||
QString UserDirectoryListModel::searchText() const
|
||||
{
|
||||
return m_keyword;
|
||||
return m_searchText;
|
||||
}
|
||||
|
||||
void UserDirectoryListModel::setKeyword(const QString &value)
|
||||
void UserDirectoryListModel::setSearchText(const QString &value)
|
||||
{
|
||||
if (m_keyword == value) {
|
||||
if (m_searchText == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_keyword = value;
|
||||
m_searchText = value;
|
||||
Q_EMIT searchTextChanged();
|
||||
|
||||
if (m_job) {
|
||||
m_job->abandon();
|
||||
m_job = nullptr;
|
||||
Q_EMIT searchingChanged();
|
||||
}
|
||||
|
||||
m_limited = false;
|
||||
attempted = false;
|
||||
|
||||
if (job) {
|
||||
job->abandon();
|
||||
job = nullptr;
|
||||
}
|
||||
|
||||
Q_EMIT keywordChanged();
|
||||
Q_EMIT limitedChanged();
|
||||
}
|
||||
|
||||
bool UserDirectoryListModel::limited() const
|
||||
bool UserDirectoryListModel::searching() const
|
||||
{
|
||||
return m_limited;
|
||||
return m_job != nullptr;
|
||||
}
|
||||
|
||||
void UserDirectoryListModel::search(int count)
|
||||
void UserDirectoryListModel::search(int limit)
|
||||
{
|
||||
if (count < 1) {
|
||||
if (limit < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (job) {
|
||||
if (m_job) {
|
||||
qDebug() << "UserDirectoryListModel: Other jobs running, ignore";
|
||||
|
||||
return;
|
||||
@@ -93,25 +89,22 @@ void UserDirectoryListModel::search(int count)
|
||||
return;
|
||||
}
|
||||
|
||||
job = m_connection->callApi<SearchUserDirectoryJob>(m_keyword, count);
|
||||
m_job = m_connection->callApi<SearchUserDirectoryJob>(m_searchText, limit);
|
||||
Q_EMIT searchingChanged();
|
||||
|
||||
connect(job, &BaseJob::finished, this, [this] {
|
||||
connect(m_job, &BaseJob::finished, this, [this] {
|
||||
attempted = true;
|
||||
|
||||
if (job->status() == BaseJob::Success) {
|
||||
auto users = job->results();
|
||||
if (m_job->status() == BaseJob::Success) {
|
||||
auto users = m_job->results();
|
||||
|
||||
this->beginResetModel();
|
||||
|
||||
this->users = users;
|
||||
this->m_limited = job->limited();
|
||||
|
||||
this->endResetModel();
|
||||
}
|
||||
|
||||
this->job = nullptr;
|
||||
|
||||
Q_EMIT limitedChanged();
|
||||
this->m_job = nullptr;
|
||||
Q_EMIT searchingChanged();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -127,7 +120,7 @@ QVariant UserDirectoryListModel::data(const QModelIndex &index, int role) const
|
||||
return {};
|
||||
}
|
||||
auto user = users.at(index.row());
|
||||
if (role == NameRole) {
|
||||
if (role == DisplayNameRole) {
|
||||
auto displayName = user.displayName;
|
||||
if (!displayName.isEmpty()) {
|
||||
return displayName;
|
||||
@@ -142,18 +135,17 @@ QVariant UserDirectoryListModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
if (role == AvatarRole) {
|
||||
auto avatarUrl = user.avatarUrl;
|
||||
|
||||
if (avatarUrl.isEmpty()) {
|
||||
return QString();
|
||||
if (avatarUrl.isEmpty() || !m_connection) {
|
||||
return QUrl();
|
||||
}
|
||||
return avatarUrl.url().remove(0, 6);
|
||||
return m_connection->makeMediaUrl(avatarUrl);
|
||||
}
|
||||
if (role == UserIDRole) {
|
||||
return user.userId;
|
||||
}
|
||||
if (role == DirectChatsRole) {
|
||||
if (role == DirectChatExistsRole) {
|
||||
if (!m_connection) {
|
||||
return QStringList();
|
||||
return false;
|
||||
};
|
||||
|
||||
auto userObj = m_connection->user(user.userId);
|
||||
@@ -162,11 +154,11 @@ QVariant UserDirectoryListModel::data(const QModelIndex &index, int role) const
|
||||
if (userObj && directChats.contains(userObj)) {
|
||||
auto directChatsForUser = directChats.values(userObj);
|
||||
if (!directChatsForUser.isEmpty()) {
|
||||
return QVariant::fromValue(directChatsForUser);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return QStringList();
|
||||
return false;
|
||||
}
|
||||
|
||||
return {};
|
||||
@@ -176,10 +168,10 @@ QHash<int, QByteArray> UserDirectoryListModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
|
||||
roles[NameRole] = "name";
|
||||
roles[AvatarRole] = "avatar";
|
||||
roles[UserIDRole] = "userID";
|
||||
roles[DirectChatsRole] = "directChats";
|
||||
roles[DisplayNameRole] = "displayName";
|
||||
roles[AvatarRole] = "avatarUrl";
|
||||
roles[UserIDRole] = "userId";
|
||||
roles[DirectChatExistsRole] = "directChatExists";
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -35,24 +35,24 @@ class UserDirectoryListModel : public QAbstractListModel
|
||||
Q_PROPERTY(Quotient::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
|
||||
/**
|
||||
* @brief The keyword to use in the search.
|
||||
* @brief The text to search the public room list for.
|
||||
*/
|
||||
Q_PROPERTY(QString keyword READ keyword WRITE setKeyword NOTIFY keywordChanged)
|
||||
Q_PROPERTY(QString searchText READ searchText WRITE setSearchText NOTIFY searchTextChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the current results have been truncated.
|
||||
* @brief Whether the model is searching.
|
||||
*/
|
||||
Q_PROPERTY(bool limited READ limited NOTIFY limitedChanged)
|
||||
Q_PROPERTY(bool searching READ searching NOTIFY searchingChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*/
|
||||
enum EventRoles {
|
||||
NameRole = Qt::DisplayRole + 1, /**< The user's display name. */
|
||||
DisplayNameRole = Qt::DisplayRole, /**< The user's display name. */
|
||||
AvatarRole, /**< The source URL for the user's avatar. */
|
||||
UserIDRole, /**< Matrix ID of the user. */
|
||||
DirectChatsRole, /**< A list of direct chat matrix IDs with the user. */
|
||||
DirectChatExistsRole, /**< Whether there is already a direct chat with the user. */
|
||||
};
|
||||
|
||||
explicit UserDirectoryListModel(QObject *parent = nullptr);
|
||||
@@ -60,17 +60,17 @@ public:
|
||||
[[nodiscard]] Quotient::Connection *connection() const;
|
||||
void setConnection(Quotient::Connection *conn);
|
||||
|
||||
[[nodiscard]] QString keyword() const;
|
||||
void setKeyword(const QString &value);
|
||||
[[nodiscard]] QString searchText() const;
|
||||
void setSearchText(const QString &searchText);
|
||||
|
||||
[[nodiscard]] bool limited() const;
|
||||
[[nodiscard]] bool searching() const;
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
*
|
||||
* @sa QAbstractItemModel::data
|
||||
*/
|
||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role = NameRole) const override;
|
||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
/**
|
||||
* @brief Number of rows in the model.
|
||||
@@ -87,23 +87,23 @@ public:
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
/**
|
||||
* @brief Start the user search.
|
||||
* @brief Search the user directory.
|
||||
*
|
||||
* @param limit the maximum number of rooms to load.
|
||||
*/
|
||||
Q_INVOKABLE void search(int count = 50);
|
||||
Q_INVOKABLE void search(int limit = 50);
|
||||
|
||||
Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
void keywordChanged();
|
||||
void limitedChanged();
|
||||
void searchTextChanged();
|
||||
void searchingChanged();
|
||||
|
||||
private:
|
||||
Quotient::Connection *m_connection = nullptr;
|
||||
QString m_keyword;
|
||||
bool m_limited = false;
|
||||
QString m_searchText;
|
||||
|
||||
bool attempted = false;
|
||||
|
||||
QList<Quotient::SearchUserDirectoryJob::User> users;
|
||||
|
||||
Quotient::SearchUserDirectoryJob *job = nullptr;
|
||||
Quotient::SearchUserDirectoryJob *m_job = nullptr;
|
||||
};
|
||||
|
||||
@@ -181,12 +181,14 @@ Name[eu]=Gonbidapen berria
|
||||
Name[fi]=Uusi kutsu
|
||||
Name[fr]=Nouvelle invitation
|
||||
Name[gl]=Novo convite
|
||||
Name[hu]=Új meghívó
|
||||
Name[ia]=Nove invitation
|
||||
Name[id]=Undangan Baru
|
||||
Name[ie]=Nov invitation
|
||||
Name[it]=Nuovo invito
|
||||
Name[ka]=ახალი მოსაწვევი
|
||||
Name[ko]=새 초대장
|
||||
Name[lt]=Naujas pakvietimas
|
||||
Name[nl]=Nieuwe uitnodiging
|
||||
Name[nn]=Ny invitasjon
|
||||
Name[pa]=ਨਵਾਂ ਸੱਦਾ
|
||||
@@ -217,12 +219,14 @@ Comment[eu]=Gela baterako gonbidapen berri bat dago
|
||||
Comment[fi]=Uusi kutsu huoneeseen
|
||||
Comment[fr]=Il y a une nouvelle invitation dans un salon.
|
||||
Comment[gl]=Tes un novo convite para unha sala.
|
||||
Comment[hu]=Új meghívó érkezett egy szobába
|
||||
Comment[ia]=Il ha un nove invitation a un sala
|
||||
Comment[id]=Ada undangan baru ke sebuah ruangan
|
||||
Comment[ie]=Vu have un nov invitation a un chambre
|
||||
Comment[it]=È presente un nuovo invito a una stanza
|
||||
Comment[ka]=გაქვთ ახალი ოთახის მოსაწვევი
|
||||
Comment[ko]=새로운 대화방 초대장을 받음
|
||||
Comment[lt]=Yra naujas pakvietimas į kambarį
|
||||
Comment[nl]=Er is een nieuwe uitnodiging naar een room
|
||||
Comment[nn]=Du har ein ny invitasjon til eit rom
|
||||
Comment[pa]=ਰੂਮ ਲਈ ਨਵਾਂ ਸੱਦਾ ਹੈ
|
||||
|
||||
@@ -11,9 +11,6 @@
|
||||
<entry name="OpenRoom" type="String">
|
||||
<label>Latest opened room</label>
|
||||
</entry>
|
||||
<entry name="ActiveConnection" type="String">
|
||||
<label>Latest active connection</label>
|
||||
</entry>
|
||||
<entry name="ColorScheme" type="String">
|
||||
<label>Color scheme</label>
|
||||
</entry>
|
||||
@@ -25,10 +22,6 @@
|
||||
<label>Background transparency value</label>
|
||||
<default>0.3</default>
|
||||
</entry>
|
||||
<entry name="MergeRoomList" type="bool">
|
||||
<label>Merge Room Lists</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="AllowQuickEdit" type="bool">
|
||||
<label>Use s/text/replacement syntax to edit your last message.</label>
|
||||
<default>false</default>
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "jobs/neochatdeactivateaccountjob.h"
|
||||
#include "roommanager.h"
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <qt6keychain/keychain.h>
|
||||
|
||||
#include <KLocalizedString>
|
||||
@@ -17,7 +19,9 @@
|
||||
#include <Quotient/csapi/content-repo.h>
|
||||
#include <Quotient/csapi/profile.h>
|
||||
#include <Quotient/database.h>
|
||||
#include <Quotient/jobs/downloadfilejob.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
#include <Quotient/room.h>
|
||||
#include <Quotient/settings.h>
|
||||
#include <Quotient/user.h>
|
||||
|
||||
@@ -32,6 +36,17 @@ using namespace Qt::StringLiterals;
|
||||
|
||||
NeoChatConnection::NeoChatConnection(QObject *parent)
|
||||
: Connection(parent)
|
||||
{
|
||||
connectSignals();
|
||||
}
|
||||
|
||||
NeoChatConnection::NeoChatConnection(const QUrl &server, QObject *parent)
|
||||
: Connection(server, parent)
|
||||
{
|
||||
connectSignals();
|
||||
}
|
||||
|
||||
void NeoChatConnection::connectSignals()
|
||||
{
|
||||
connect(this, &NeoChatConnection::accountDataChanged, this, [this](const QString &type) {
|
||||
if (type == QLatin1String("org.kde.neochat.account_label")) {
|
||||
@@ -44,14 +59,42 @@ NeoChatConnection::NeoChatConnection(QObject *parent)
|
||||
connect(this, &NeoChatConnection::networkError, this, [this]() {
|
||||
setIsOnline(false);
|
||||
});
|
||||
}
|
||||
|
||||
NeoChatConnection::NeoChatConnection(const QUrl &server, QObject *parent)
|
||||
: Connection(server, parent)
|
||||
{
|
||||
connect(this, &NeoChatConnection::accountDataChanged, this, [this](const QString &type) {
|
||||
if (type == QLatin1String("org.kde.neochat.account_label")) {
|
||||
Q_EMIT labelChanged();
|
||||
connect(this, &NeoChatConnection::requestFailed, this, [this](BaseJob *job) {
|
||||
if (job->error() == BaseJob::UserConsentRequired) {
|
||||
Q_EMIT userConsentRequired(job->errorUrl());
|
||||
}
|
||||
});
|
||||
connect(this, &NeoChatConnection::requestFailed, this, [](BaseJob *job) {
|
||||
if (dynamic_cast<DownloadFileJob *>(job) && job->jsonData()["errcode"_ls].toString() == "M_TOO_LARGE"_ls) {
|
||||
RoomManager::instance().warning(i18n("File too large to download."), i18n("Contact your matrix server administrator for support."));
|
||||
}
|
||||
});
|
||||
connect(this, &NeoChatConnection::directChatsListChanged, this, [this](DirectChatsMap additions, DirectChatsMap removals) {
|
||||
Q_EMIT directChatInvitesChanged();
|
||||
for (const auto &chatId : additions) {
|
||||
if (const auto chat = room(chatId)) {
|
||||
connect(chat, &Room::unreadStatsChanged, this, [this]() {
|
||||
Q_EMIT directChatNotificationsChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const auto &chatId : removals) {
|
||||
if (const auto chat = room(chatId)) {
|
||||
disconnect(chat, &Room::unreadStatsChanged, this, nullptr);
|
||||
}
|
||||
}
|
||||
});
|
||||
connect(this, &NeoChatConnection::joinedRoom, this, [this](Room *room) {
|
||||
if (room->isDirectChat()) {
|
||||
connect(room, &Room::unreadStatsChanged, this, [this]() {
|
||||
Q_EMIT directChatNotificationsChanged();
|
||||
});
|
||||
}
|
||||
});
|
||||
connect(this, &NeoChatConnection::leftRoom, this, [this](Room *room, Room *prev) {
|
||||
Q_UNUSED(room)
|
||||
if (prev && prev->isDirectChat()) {
|
||||
Q_EMIT directChatInvitesChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -233,6 +276,20 @@ void NeoChatConnection::createSpace(const QString &name, const QString &topic, c
|
||||
});
|
||||
}
|
||||
|
||||
bool NeoChatConnection::directChatExists(Quotient::User *user)
|
||||
{
|
||||
return directChats().contains(user);
|
||||
}
|
||||
|
||||
void NeoChatConnection::openOrCreateDirectChat(const QString &userId)
|
||||
{
|
||||
if (auto user = this->user(userId)) {
|
||||
openOrCreateDirectChat(user);
|
||||
} else {
|
||||
qWarning() << "openOrCreateDirectChat: Couldn't get user object for ID " << userId << ", unable to open/request direct chat.";
|
||||
}
|
||||
}
|
||||
|
||||
void NeoChatConnection::openOrCreateDirectChat(User *user)
|
||||
{
|
||||
const auto existing = directChats();
|
||||
@@ -247,6 +304,32 @@ void NeoChatConnection::openOrCreateDirectChat(User *user)
|
||||
requestDirectChat(user);
|
||||
}
|
||||
|
||||
qsizetype NeoChatConnection::directChatNotifications() const
|
||||
{
|
||||
qsizetype notifications = 0;
|
||||
QStringList added; // The same ID can be in the list multiple times.
|
||||
for (const auto &chatId : directChats()) {
|
||||
if (!added.contains(chatId)) {
|
||||
if (const auto chat = room(chatId)) {
|
||||
notifications += chat->notificationCount();
|
||||
added += chatId;
|
||||
}
|
||||
}
|
||||
}
|
||||
return notifications;
|
||||
}
|
||||
|
||||
bool NeoChatConnection::directChatInvites() const
|
||||
{
|
||||
auto inviteRooms = rooms(JoinState::Invite);
|
||||
for (const auto inviteRoom : inviteRooms) {
|
||||
if (inviteRoom->isDirectChat()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QCoro::Task<void> NeoChatConnection::setupPushNotifications(QString endpoint)
|
||||
{
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
|
||||
@@ -27,6 +27,16 @@ class NeoChatConnection : public Quotient::Connection
|
||||
Q_PROPERTY(QString deviceKey READ deviceKey CONSTANT)
|
||||
Q_PROPERTY(QString encryptionKey READ encryptionKey CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The total number of notifications for all direct chats.
|
||||
*/
|
||||
Q_PROPERTY(qsizetype directChatNotifications READ directChatNotifications NOTIFY directChatNotificationsChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether there is at least one invite to a direct chat.
|
||||
*/
|
||||
Q_PROPERTY(bool directChatInvites READ directChatInvites NOTIFY directChatInvitesChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether NeoChat is currently able to connect to the server.
|
||||
*/
|
||||
@@ -80,12 +90,27 @@ public:
|
||||
Q_INVOKABLE void createSpace(const QString &name, const QString &topic, const QString &parent = {}, bool setChildParent = false);
|
||||
|
||||
/**
|
||||
* @brief Join a direct chat with the given user.
|
||||
* @brief Whether a direct chat with the user exists.
|
||||
*/
|
||||
Q_INVOKABLE bool directChatExists(Quotient::User *user);
|
||||
|
||||
/**
|
||||
* @brief Join a direct chat with the given user ID.
|
||||
*
|
||||
* If a direct chat with the user doesn't exist one is created and then joined.
|
||||
*/
|
||||
Q_INVOKABLE void openOrCreateDirectChat(const QString &userId);
|
||||
|
||||
/**
|
||||
* @brief Join a direct chat with the given user object.
|
||||
*
|
||||
* If a direct chat with the user doesn't exist one is created and then joined.
|
||||
*/
|
||||
Q_INVOKABLE void openOrCreateDirectChat(Quotient::User *user);
|
||||
|
||||
qsizetype directChatNotifications() const;
|
||||
bool directChatInvites() const;
|
||||
|
||||
// note: this is intentionally a copied QString because
|
||||
// the reference could be destroyed before the task is finished
|
||||
QCoro::Task<void> setupPushNotifications(QString endpoint);
|
||||
@@ -97,10 +122,15 @@ public:
|
||||
|
||||
Q_SIGNALS:
|
||||
void labelChanged();
|
||||
void directChatNotificationsChanged();
|
||||
void directChatInvitesChanged();
|
||||
void isOnlineChanged();
|
||||
void passwordStatus(NeoChatConnection::PasswordStatus status);
|
||||
void userConsentRequired(QUrl url);
|
||||
|
||||
private:
|
||||
bool m_isOnline = true;
|
||||
void setIsOnline(bool isOnline);
|
||||
|
||||
void connectSignals();
|
||||
};
|
||||
|
||||
@@ -4,15 +4,10 @@
|
||||
#include "neochatroom.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QGuiApplication>
|
||||
#include <QMetaObject>
|
||||
#include <QMimeDatabase>
|
||||
#include <QPalette>
|
||||
#include <QTemporaryFile>
|
||||
#include <QTextDocument>
|
||||
|
||||
#include <QMediaMetaData>
|
||||
#include <QMediaPlayer>
|
||||
#include <QMimeDatabase>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/user.h>
|
||||
@@ -35,13 +30,11 @@
|
||||
#include <Quotient/events/roommemberevent.h>
|
||||
#include <Quotient/events/roompowerlevelsevent.h>
|
||||
#include <Quotient/events/simplestateevents.h>
|
||||
#include <Quotient/events/stickerevent.h>
|
||||
#include <Quotient/jobs/downloadfilejob.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "clipboard.h"
|
||||
#include "controller.h"
|
||||
#include "eventhandler.h"
|
||||
#include "events/joinrulesevent.h"
|
||||
#include "events/pollevent.h"
|
||||
@@ -53,8 +46,6 @@
|
||||
#include "urlhelper.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <KConfig>
|
||||
#include <KConfigGroup>
|
||||
#ifndef Q_OS_ANDROID
|
||||
#include <KIO/Job>
|
||||
#include <KIO/JobTracker>
|
||||
@@ -123,6 +114,7 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
||||
Q_EMIT parentIdsChanged();
|
||||
Q_EMIT canonicalParentChanged();
|
||||
Q_EMIT joinRuleChanged();
|
||||
Q_EMIT readOnlyChanged();
|
||||
});
|
||||
connect(connection, &Connection::capabilitiesLoaded, this, &NeoChatRoom::maxRoomVersionChanged);
|
||||
connect(this, &Room::changed, this, [this]() {
|
||||
@@ -671,6 +663,11 @@ bool NeoChatRoom::isInvite() const
|
||||
return joinState() == JoinState::Invite;
|
||||
}
|
||||
|
||||
bool NeoChatRoom::readOnly() const
|
||||
{
|
||||
return !canSendEvent("m.room.message"_ls);
|
||||
}
|
||||
|
||||
bool NeoChatRoom::isUserBanned(const QString &user) const
|
||||
{
|
||||
auto roomMemberEvent = currentState().get<RoomMemberEvent>(user);
|
||||
@@ -1829,7 +1826,11 @@ int NeoChatRoom::maxRoomVersion() const
|
||||
|
||||
Quotient::User *NeoChatRoom::directChatRemoteUser() const
|
||||
{
|
||||
return connection()->directChatUsers(this)[0];
|
||||
auto users = connection()->directChatUsers(this);
|
||||
if (users.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return users[0];
|
||||
}
|
||||
|
||||
void NeoChatRoom::sendLocation(float lat, float lon, const QString &description)
|
||||
|
||||
@@ -133,6 +133,11 @@ class NeoChatRoom : public Quotient::Room
|
||||
*/
|
||||
Q_PROPERTY(bool isInvite READ isInvite NOTIFY isInviteChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the local user can send messages in the room.
|
||||
*/
|
||||
Q_PROPERTY(bool readOnly READ readOnly NOTIFY readOnlyChanged)
|
||||
|
||||
/**
|
||||
* @brief The current join rule for the room as a QString.
|
||||
*
|
||||
@@ -552,6 +557,8 @@ public:
|
||||
|
||||
bool isInvite() const;
|
||||
|
||||
bool readOnly() const;
|
||||
|
||||
Q_INVOKABLE void clearInvitationNotification();
|
||||
|
||||
[[nodiscard]] QString joinRule() const;
|
||||
@@ -809,6 +816,7 @@ Q_SIGNALS:
|
||||
void canonicalParentChanged();
|
||||
void lastActiveTimeChanged();
|
||||
void isInviteChanged();
|
||||
void readOnlyChanged();
|
||||
void displayNameChanged();
|
||||
void pushNotificationStateChanged(PushNotificationState::State state);
|
||||
void showMessage(MessageType messageType, const QString &message);
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#endif
|
||||
|
||||
#include "controller.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "roommanager.h"
|
||||
|
||||
@@ -56,12 +56,14 @@ Comment[eu]=Bilatu gelak NeoChat-en
|
||||
Comment[fi]=Etsi huoneita NeoChatissä
|
||||
Comment[fr]=Trouver des salons dans NeoChat
|
||||
Comment[gl]=Atopa salas en NeoChat.
|
||||
Comment[hu]=Szobák keresése a NeoChatben
|
||||
Comment[ia]=Trova salas in NeoChat
|
||||
Comment[id]=Cari ruangan di NeoChat
|
||||
Comment[ie]=Trovar chambres in NeoChat
|
||||
Comment[it]=Trova stanze in NeoChat
|
||||
Comment[ka]=იპოვე ოთახები NeoChat-ში
|
||||
Comment[ko]=NeoChat에서 대화방 찾기
|
||||
Comment[lt]=Rasti kambarius NeoChat
|
||||
Comment[nl]=Rooms zoeken in NeoChat
|
||||
Comment[nn]=Finn rom i NeoChat
|
||||
Comment[pl]=Znajdź pokoje w NeoChat
|
||||
|
||||
35
src/proxycontroller.cpp
Normal file
35
src/proxycontroller.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "proxycontroller.h"
|
||||
|
||||
#include <QNetworkProxy>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
|
||||
void ProxyController::setApplicationProxy()
|
||||
{
|
||||
auto cfg = NeoChatConfig::self();
|
||||
QNetworkProxy proxy;
|
||||
|
||||
switch (cfg->proxyType()) {
|
||||
case 1:
|
||||
proxy.setType(QNetworkProxy::HttpProxy);
|
||||
break;
|
||||
case 2:
|
||||
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
proxy.setHostName(cfg->proxyHost());
|
||||
proxy.setPort(cfg->proxyPort());
|
||||
proxy.setUser(cfg->proxyUser());
|
||||
proxy.setPassword(cfg->proxyPassword());
|
||||
QNetworkProxy::setApplicationProxy(proxy);
|
||||
}
|
||||
|
||||
ProxyController::ProxyController(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user