Compare commits
101 Commits
v25.08.0
...
work/tobia
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd70e2b47f | ||
|
|
279b611754 | ||
|
|
f7c74a60cd | ||
|
|
3465fc7d39 | ||
|
|
6381f06acb | ||
|
|
a7c7a5c72d | ||
|
|
c8ded65e46 | ||
|
|
729b46fc71 | ||
|
|
e63e04aa57 | ||
|
|
ae88879651 | ||
|
|
dfd106258b | ||
|
|
4d29b9fd57 | ||
|
|
3de7ad237a | ||
|
|
7d4e589894 | ||
|
|
4bbd127fe8 | ||
|
|
f2a0a66b01 | ||
|
|
143c685045 | ||
|
|
b8fa6f0690 | ||
|
|
93b6c53c82 | ||
|
|
6822a1ef08 | ||
|
|
12b7c25395 | ||
|
|
42c0060122 | ||
|
|
b30ee55a81 | ||
|
|
9d2ef838bb | ||
|
|
b5351e48dd | ||
|
|
7b437d91e1 | ||
|
|
a9d39353ab | ||
|
|
5f778dbd81 | ||
|
|
b01286eae3 | ||
|
|
4688802628 | ||
|
|
0282f2c7aa | ||
|
|
53d0fd1663 | ||
|
|
24bdb7d651 | ||
|
|
dc32f2f947 | ||
|
|
401cf29ca8 | ||
|
|
f16dea85ed | ||
|
|
b4e1740cad | ||
|
|
501f14fead | ||
|
|
d14466451d | ||
|
|
f7cd4bd2fb | ||
|
|
7742c6d4b0 | ||
|
|
4f6dd50320 | ||
|
|
92f77860dd | ||
|
|
03035b735d | ||
|
|
4e0b295f66 | ||
|
|
8cbd3f5e0f | ||
|
|
a0b3e484f5 | ||
|
|
8ff83ca6df | ||
|
|
5fe28cb183 | ||
|
|
17af4dfddb | ||
|
|
43c6349359 | ||
|
|
265494ee44 | ||
|
|
a9e4996191 | ||
|
|
6e3276826d | ||
|
|
bfe976c438 | ||
|
|
f288367653 | ||
|
|
6082bc89b0 | ||
|
|
4d3791250b | ||
|
|
04472dae4f | ||
|
|
aa40fc84ea | ||
|
|
24e43d063a | ||
|
|
c5caffcdf9 | ||
|
|
95d334ad86 | ||
|
|
602ac5c55f | ||
|
|
247423bf83 | ||
|
|
24d35b3eae | ||
|
|
8bcd9f7469 | ||
|
|
edf5d55da4 | ||
|
|
976af783e2 | ||
|
|
d87954838e | ||
|
|
e757331dce | ||
|
|
bf4f6f5728 | ||
|
|
c73bc8fc29 | ||
|
|
211a08db68 | ||
|
|
38987e6d4c | ||
|
|
9d76e7e30b | ||
|
|
4c1a8d3657 | ||
|
|
7a5de25885 | ||
|
|
a17aa2c6fa | ||
|
|
207a7876b6 | ||
|
|
4c638a740e | ||
|
|
0ee89e1b2b | ||
|
|
4af42a57f4 | ||
|
|
34f2c2dabc | ||
|
|
9ff942915a | ||
|
|
10123abc5b | ||
|
|
ad993d4340 | ||
|
|
ddc0a66d5b | ||
|
|
e8981bdc0f | ||
|
|
c42486a061 | ||
|
|
64d82b8d2a | ||
|
|
677abee890 | ||
|
|
3a25a62350 | ||
|
|
bc7b480c41 | ||
|
|
d9b495356d | ||
|
|
ce82606e6e | ||
|
|
07837c2e64 | ||
|
|
1738253e6f | ||
|
|
17fa2246da | ||
|
|
4f5e096e7e | ||
|
|
b125c284bd |
@@ -20,8 +20,16 @@
|
|||||||
"--talk-name=org.kde.kwalletd5",
|
"--talk-name=org.kde.kwalletd5",
|
||||||
"--talk-name=org.kde.StatusNotifierWatcher",
|
"--talk-name=org.kde.StatusNotifierWatcher",
|
||||||
"--talk-name=org.freedesktop.secrets",
|
"--talk-name=org.freedesktop.secrets",
|
||||||
|
"--talk-name=org.kde.kuiserver",
|
||||||
"--own-name=org.kde.StatusNotifierItem-2-2"
|
"--own-name=org.kde.StatusNotifierItem-2-2"
|
||||||
],
|
],
|
||||||
|
"cleanup": [
|
||||||
|
"/include",
|
||||||
|
"/lib/*.a",
|
||||||
|
"/lib/cmake",
|
||||||
|
"/lib/pkgconfig",
|
||||||
|
"/share/ndk-modules"
|
||||||
|
],
|
||||||
"modules": [
|
"modules": [
|
||||||
{
|
{
|
||||||
"name": "kirigamiaddons",
|
"name": "kirigamiaddons",
|
||||||
@@ -82,8 +90,8 @@
|
|||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "archive",
|
"type": "archive",
|
||||||
"url": "https://download.gnome.org/sources/libsecret/0.21/libsecret-0.21.6.tar.xz",
|
"url": "https://download.gnome.org/sources/libsecret/0.21/libsecret-0.21.7.tar.xz",
|
||||||
"sha256": "747b8c175be108c880d3adfb9c3537ea66e520e4ad2dccf5dce58003aeeca090",
|
"sha256": "6b452e4750590a2b5617adc40026f28d2f4903de15f1250e1d1c40bfd68ed55e",
|
||||||
"x-checker-data": {
|
"x-checker-data": {
|
||||||
"type": "gnome",
|
"type": "gnome",
|
||||||
"name": "libsecret",
|
"name": "libsecret",
|
||||||
@@ -156,13 +164,13 @@
|
|||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "archive",
|
"type": "archive",
|
||||||
"url": "https://download.kde.org/stable/kunifiedpush/kunifiedpush-1.0.0.tar.xz",
|
"url": "https://download.kde.org/stable/release-service/25.04.3/src/kunifiedpush-25.04.3.tar.xz",
|
||||||
"sha256": "2ddeba21306d0307114ec50a2c38159ec62359f9fc6cdd58da30a369fbd550cf",
|
"sha256": "a16ffe4117b14baa02f3b8ae7de9e509a17359c1b67dcd851aef4f3c3661a1df",
|
||||||
"x-checker-data": {
|
"x-checker-data": {
|
||||||
"type": "anitya",
|
"type": "anitya",
|
||||||
"project-id": 375055,
|
"project-id": 8763,
|
||||||
"stable-only": true,
|
"stable-only": true,
|
||||||
"url-template": "https://download.kde.org/stable/kunifiedpush/kunifiedpush-$version.tar.xz"
|
"url-template": "https://download.kde.org/stable/release-service/$version/src/kunifiedpush-$version.tar.xz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ cmake_minimum_required(VERSION 3.16)
|
|||||||
|
|
||||||
# KDE Applications version, managed by release script.
|
# KDE Applications version, managed by release script.
|
||||||
set(RELEASE_SERVICE_VERSION_MAJOR "25")
|
set(RELEASE_SERVICE_VERSION_MAJOR "25")
|
||||||
set(RELEASE_SERVICE_VERSION_MINOR "08")
|
set(RELEASE_SERVICE_VERSION_MINOR "11")
|
||||||
set(RELEASE_SERVICE_VERSION_MICRO "0")
|
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
||||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||||
|
|
||||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTest>
|
#include <QTest>
|
||||||
|
|
||||||
|
#include <QSignalSpy>
|
||||||
#include <Quotient/roommember.h>
|
#include <Quotient/roommember.h>
|
||||||
#include <Quotient/syncdata.h>
|
#include <Quotient/syncdata.h>
|
||||||
#include <qtestcase.h>
|
#include <qtestcase.h>
|
||||||
@@ -32,6 +33,7 @@ private Q_SLOTS:
|
|||||||
void noRoom();
|
void noRoom();
|
||||||
void badParent();
|
void badParent();
|
||||||
void reply();
|
void reply();
|
||||||
|
void replyMissingUser();
|
||||||
void edit();
|
void edit();
|
||||||
void attachment();
|
void attachment();
|
||||||
};
|
};
|
||||||
@@ -102,6 +104,33 @@ void ChatBarCacheTest::reply()
|
|||||||
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@example:example.org"_s));
|
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@example:example.org"_s));
|
||||||
QCOMPARE(chatBarCache->relationMessage(), u"This is an example\ntext message"_s);
|
QCOMPARE(chatBarCache->relationMessage(), u"This is an example\ntext message"_s);
|
||||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||||
|
QCOMPARE(chatBarCache->relationAuthorIsPresent(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatBarCacheTest::replyMissingUser()
|
||||||
|
{
|
||||||
|
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||||
|
chatBarCache->setText(u"some text"_s);
|
||||||
|
chatBarCache->setAttachmentPath(u"some/path"_s);
|
||||||
|
chatBarCache->setReplyId(u"$153456789:example.org"_s);
|
||||||
|
|
||||||
|
QCOMPARE(chatBarCache->text(), u"some text"_s);
|
||||||
|
QCOMPARE(chatBarCache->isReplying(), true);
|
||||||
|
QCOMPARE(chatBarCache->replyId(), u"$153456789:example.org"_s);
|
||||||
|
QCOMPARE(chatBarCache->isEditing(), false);
|
||||||
|
QCOMPARE(chatBarCache->editId(), QString());
|
||||||
|
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@example:example.org"_s));
|
||||||
|
QCOMPARE(chatBarCache->relationMessage(), u"This is an example\ntext message"_s);
|
||||||
|
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||||
|
QCOMPARE(chatBarCache->relationAuthorIsPresent(), true);
|
||||||
|
|
||||||
|
QSignalSpy relationAuthorIsPresentSpy(chatBarCache.get(), &ChatBarCache::relationAuthorIsPresentChanged);
|
||||||
|
|
||||||
|
// sync again, which will simulate the reply user leaving the room
|
||||||
|
room->syncNewEvents(u"test-min-sync-extra-sync.json"_s);
|
||||||
|
|
||||||
|
QTRY_COMPARE(relationAuthorIsPresentSpy.count(), 1);
|
||||||
|
QCOMPARE(chatBarCache->relationAuthorIsPresent(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatBarCacheTest::edit()
|
void ChatBarCacheTest::edit()
|
||||||
|
|||||||
20
autotests/data/test-min-sync-extra-sync.json
Normal file
20
autotests/data/test-min-sync-extra-sync.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"state": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"membership": "leave"
|
||||||
|
},
|
||||||
|
"event_id": "$1432735824666PhrSA:example.org",
|
||||||
|
"origin_server_ts": 1432735824666,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"sender": "@example:example.org",
|
||||||
|
"state_key": "@example:example.org",
|
||||||
|
"type": "m.room.member",
|
||||||
|
"unsigned": {
|
||||||
|
"replaces_state": "$143273582443PhrSn:example.org"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
#include <Quotient/roommember.h>
|
#include <Quotient/roommember.h>
|
||||||
#include <Quotient/syncdata.h>
|
#include <Quotient/syncdata.h>
|
||||||
|
|
||||||
#include "models/messagecontentmodel.h"
|
#include "models/eventmessagecontentmodel.h"
|
||||||
|
|
||||||
#include "neochatconnection.h"
|
#include "neochatconnection.h"
|
||||||
#include "testutils.h"
|
#include "testutils.h"
|
||||||
@@ -39,13 +39,13 @@ void MessageContentModelTest::initTestCase()
|
|||||||
void MessageContentModelTest::missingEvent()
|
void MessageContentModelTest::missingEvent()
|
||||||
{
|
{
|
||||||
auto room = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s);
|
auto room = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s);
|
||||||
auto model1 = MessageContentModel(room, u"$153456789:example.org"_s);
|
auto model1 = EventMessageContentModel(room, u"$153456789:example.org"_s);
|
||||||
|
|
||||||
QCOMPARE(model1.rowCount(), 1);
|
QCOMPARE(model1.rowCount(), 1);
|
||||||
QCOMPARE(model1.data(model1.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
|
QCOMPARE(model1.data(model1.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
|
||||||
QCOMPARE(model1.data(model1.index(0), MessageContentModel::DisplayRole), u"Loading"_s);
|
QCOMPARE(model1.data(model1.index(0), MessageContentModel::DisplayRole), u"Loading"_s);
|
||||||
|
|
||||||
auto model2 = MessageContentModel(room, u"$153456789:example.org"_s, true);
|
auto model2 = EventMessageContentModel(room, u"$153456789:example.org"_s, true);
|
||||||
|
|
||||||
QCOMPARE(model2.rowCount(), 1);
|
QCOMPARE(model2.rowCount(), 1);
|
||||||
QCOMPARE(model2.data(model2.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
|
QCOMPARE(model2.data(model2.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#include <Quotient/events/roommessageevent.h>
|
#include <Quotient/events/roommessageevent.h>
|
||||||
|
|
||||||
#include "models/messagecontentmodel.h"
|
#include "models/eventmessagecontentmodel.h"
|
||||||
#include "testutils.h"
|
#include "testutils.h"
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
@@ -21,7 +21,7 @@ class ReactionModelTest : public QObject
|
|||||||
private:
|
private:
|
||||||
Connection *connection = nullptr;
|
Connection *connection = nullptr;
|
||||||
TestUtils::TestRoom *room = nullptr;
|
TestUtils::TestRoom *room = nullptr;
|
||||||
MessageContentModel *parentModel;
|
EventMessageContentModel *parentModel;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
@@ -34,7 +34,7 @@ void ReactionModelTest::initTestCase()
|
|||||||
{
|
{
|
||||||
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
|
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
|
||||||
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-reactionmodel-sync.json"_s);
|
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-reactionmodel-sync.json"_s);
|
||||||
parentModel = new MessageContentModel(room, "123456"_L1);
|
parentModel = new EventMessageContentModel(room, "123456"_L1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReactionModelTest::basicReaction()
|
void ReactionModelTest::basicReaction()
|
||||||
|
|||||||
909
po/ar/neochat.po
909
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
892
po/ca/neochat.po
892
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
990
po/cs/neochat.po
990
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
927
po/da/neochat.po
927
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
959
po/de/neochat.po
959
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
938
po/el/neochat.po
938
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
951
po/eo/neochat.po
951
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
895
po/es/neochat.po
895
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
909
po/eu/neochat.po
909
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
953
po/fi/neochat.po
953
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
933
po/fr/neochat.po
933
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
965
po/gl/neochat.po
965
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
901
po/he/neochat.po
901
po/he/neochat.po
File diff suppressed because it is too large
Load Diff
953
po/hi/neochat.po
953
po/hi/neochat.po
File diff suppressed because it is too large
Load Diff
959
po/hu/neochat.po
959
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
983
po/ia/neochat.po
983
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
916
po/id/neochat.po
916
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
883
po/ie/neochat.po
883
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
911
po/it/neochat.po
911
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
898
po/ja/neochat.po
898
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
901
po/ka/neochat.po
901
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
932
po/ko/neochat.po
932
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
898
po/lt/neochat.po
898
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
965
po/lv/neochat.po
965
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
903
po/nl/neochat.po
903
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
922
po/nn/neochat.po
922
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
891
po/pa/neochat.po
891
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
957
po/pl/neochat.po
957
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
916
po/pt/neochat.po
916
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
927
po/ru/neochat.po
927
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
953
po/sa/neochat.po
953
po/sa/neochat.po
File diff suppressed because it is too large
Load Diff
896
po/sk/neochat.po
896
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
911
po/sl/neochat.po
911
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
902
po/sv/neochat.po
902
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
957
po/ta/neochat.po
957
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
903
po/tr/neochat.po
903
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
898
po/uk/neochat.po
898
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
@@ -5,7 +5,6 @@
|
|||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
|
|
||||||
#include <Quotient/connection.h>
|
#include <Quotient/connection.h>
|
||||||
#include <qt6keychain/keychain.h>
|
|
||||||
|
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
|
|
||||||
@@ -14,7 +13,6 @@
|
|||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
#include <Quotient/csapi/notifications.h>
|
|
||||||
#include <Quotient/events/roommemberevent.h>
|
#include <Quotient/events/roommemberevent.h>
|
||||||
#include <Quotient/qt_connection_util.h>
|
#include <Quotient/qt_connection_util.h>
|
||||||
#include <Quotient/settings.h>
|
#include <Quotient/settings.h>
|
||||||
@@ -24,7 +22,6 @@
|
|||||||
#include "mediasizehelper.h"
|
#include "mediasizehelper.h"
|
||||||
#include "models/actionsmodel.h"
|
#include "models/actionsmodel.h"
|
||||||
#include "models/messagemodel.h"
|
#include "models/messagemodel.h"
|
||||||
#include "models/pushrulemodel.h"
|
|
||||||
#include "models/roomlistmodel.h"
|
#include "models/roomlistmodel.h"
|
||||||
#include "models/roomtreemodel.h"
|
#include "models/roomtreemodel.h"
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
|
|
||||||
#include "neochatconnection.h"
|
#include "neochatconnection.h"
|
||||||
#include "neochatroom.h"
|
|
||||||
|
|
||||||
#include <Quotient/events/roommessageevent.h>
|
#include <Quotient/events/roommessageevent.h>
|
||||||
#include <Quotient/roommember.h>
|
#include <Quotient/roommember.h>
|
||||||
@@ -25,11 +24,7 @@ class CommonRoomsModel : public QAbstractListModel
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
enum Roles {
|
enum Roles {
|
||||||
TextRole = Qt::DisplayRole,
|
RoomIdRole = Qt::DisplayRole,
|
||||||
LongitudeRole,
|
|
||||||
LatitudeRole,
|
|
||||||
AssetRole,
|
|
||||||
AuthorRole,
|
|
||||||
};
|
};
|
||||||
Q_ENUM(Roles)
|
Q_ENUM(Roles)
|
||||||
|
|
||||||
|
|||||||
@@ -189,6 +189,10 @@
|
|||||||
<label>Don't hide any events in the timeline</label>
|
<label>Don't hide any events in the timeline</label>
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
</entry>
|
</entry>
|
||||||
|
<entry name="RelateAnyEvent" type="bool">
|
||||||
|
<label>Send relations to any event, including state events and events normally hidden.</label>
|
||||||
|
<default>false</default>
|
||||||
|
</entry>
|
||||||
<entry name="AlwaysVerifyDevice" type="bool">
|
<entry name="AlwaysVerifyDevice" type="bool">
|
||||||
<label>Always allow device verification</label>
|
<label>Always allow device verification</label>
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
|
|||||||
@@ -93,4 +93,3 @@ X-Plasma-API=DBus
|
|||||||
X-Plasma-DBusRunner-Service=org.kde.neochat
|
X-Plasma-DBusRunner-Service=org.kde.neochat
|
||||||
X-Plasma-DBusRunner-Path=/RoomRunner
|
X-Plasma-DBusRunner-Path=/RoomRunner
|
||||||
X-Plasma-Request-Actions-Once=true
|
X-Plasma-Request-Actions-Once=true
|
||||||
X-Plasma-Runner-Min-Letter-Count=3
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls as QQC2
|
import QtQuick.Controls as QQC2
|
||||||
import QtQuick.Layouts
|
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.components as KirigamiComponents
|
import org.kde.kirigamiaddons.components as KirigamiComponents
|
||||||
@@ -18,21 +19,17 @@ KirigamiComponents.ConvergentContextMenu {
|
|||||||
required property NeoChatConnection connection
|
required property NeoChatConnection connection
|
||||||
required property Kirigami.ApplicationWindow window
|
required property Kirigami.ApplicationWindow window
|
||||||
|
|
||||||
QQC2.Action {
|
Kirigami.Action {
|
||||||
text: i18nc("@action:button", "Show QR Code")
|
text: i18nc("@action:button", "Show QR Code")
|
||||||
icon.name: "view-barcode-qr-symbolic"
|
icon.name: "view-barcode-qr-symbolic"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
let qrMax = Qt.createComponent('org.kde.neochat', 'QrCodeMaximizeComponent').createObject(QQC2.Overlay.overlay, {
|
(Qt.createComponent('org.kde.neochat', 'QrCodeMaximizeComponent').createObject(QQC2.Overlay.overlay, {
|
||||||
text: "https://matrix.to/#/" + root.connection.localUser.id,
|
text: "https://matrix.to/#/" + root.connection.localUser.id,
|
||||||
title: root.connection.localUser.displayName,
|
title: root.connection.localUser.displayName,
|
||||||
subtitle: root.connection.localUser.id,
|
subtitle: root.connection.localUser.id,
|
||||||
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
|
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
|
||||||
avatarSource: root.connection.localUser.avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(root.connection.localUser.avatarUrl) : ""
|
avatarSource: root.connection.localUser.avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(root.connection.localUser.avatarUrl) : ""
|
||||||
});
|
}) as QrCodeMaximizeComponent).open();
|
||||||
if (typeof root.closeDialog === "function") {
|
|
||||||
root.closeDialog();
|
|
||||||
}
|
|
||||||
qrMax.open();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,26 +37,27 @@ KirigamiComponents.ConvergentContextMenu {
|
|||||||
text: i18nc("@action:inmenu", "Switch Account")
|
text: i18nc("@action:inmenu", "Switch Account")
|
||||||
icon.name: "system-switch-user"
|
icon.name: "system-switch-user"
|
||||||
shortcut: "Ctrl+U"
|
shortcut: "Ctrl+U"
|
||||||
onTriggered: accountSwitchDialog.createObject(QQC2.Overlay.overlay, {
|
onTriggered: (Qt.createComponent("org.kde.neochat", "AccountSwitchDialog").createObject(QQC2.Overlay.overlay, {
|
||||||
connection: root.connection
|
connection: root.connection
|
||||||
}).open();
|
}) as Kirigami.Dialog).open();
|
||||||
}
|
}
|
||||||
QQC2.Action {
|
|
||||||
text: i18n("Edit This Account")
|
Kirigami.Action {
|
||||||
|
text: i18nc("@action:inmenu", "Edit This Account")
|
||||||
icon.name: "document-edit"
|
icon.name: "document-edit"
|
||||||
onTriggered: NeoChatSettingsView.openWithInitialProperties("accounts", {initialAccount: root.connection});
|
onTriggered: NeoChatSettingsView.openWithInitialProperties("accounts", {initialAccount: root.connection});
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Notification Settings")
|
text: i18nc("@action:inmenu", "Notification Settings")
|
||||||
icon.name: "notifications"
|
icon.name: "notifications"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
NeoChatSettingsView.open('notifications');
|
NeoChatSettingsView.open('notifications');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Devices")
|
text: i18nc("@action:inmenu", "Devices")
|
||||||
icon.name: "computer-symbolic"
|
icon.name: "computer-symbolic"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
NeoChatSettingsView.open('devices');
|
NeoChatSettingsView.open('devices');
|
||||||
@@ -67,10 +65,10 @@ KirigamiComponents.ConvergentContextMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Open Developer Tools")
|
text: i18nc("@action:inmenu", "Open Developer Tools")
|
||||||
icon.name: "tools"
|
icon.name: "tools"
|
||||||
visible: NeoChatConfig.developerTools
|
visible: NeoChatConfig.developerTools
|
||||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'DevtoolsPage'), {
|
onTriggered: root.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'DevtoolsPage'), {
|
||||||
connection: root.connection
|
connection: root.connection
|
||||||
}, {
|
}, {
|
||||||
title: i18nc("@title:window", "Developer Tools"),
|
title: i18nc("@title:window", "Developer Tools"),
|
||||||
@@ -88,9 +86,10 @@ KirigamiComponents.ConvergentContextMenu {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.Action {
|
Kirigami.Action {
|
||||||
text: i18nc("@action:inmenu", "Verify This Device")
|
text: i18nc("@action:inmenu", "Verify This Device")
|
||||||
icon.name: "security-low"
|
icon.name: "security-low"
|
||||||
|
visible: !root.connection.isVerifiedSession()
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
root.connection.startSelfVerification();
|
root.connection.startSelfVerification();
|
||||||
const dialog = Qt.createComponent("org.kde.kirigami", "PromptDialog").createObject(QQC2.Overlay.overlay, {
|
const dialog = Qt.createComponent("org.kde.kirigami", "PromptDialog").createObject(QQC2.Overlay.overlay, {
|
||||||
@@ -99,19 +98,17 @@ KirigamiComponents.ConvergentContextMenu {
|
|||||||
standardButtons: Kirigami.Dialog.Ok
|
standardButtons: Kirigami.Dialog.Ok
|
||||||
})
|
})
|
||||||
dialog.open();
|
dialog.open();
|
||||||
root.connection.onNewKeyVerificationSession.connect(() => {
|
root.connection.newKeyVerificationSession.connect(() => {
|
||||||
dialog.close();
|
dialog.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Logout")
|
text: i18nc("@action:inmenu", "Logout…")
|
||||||
icon.name: "im-kick-user"
|
icon.name: "im-kick-user"
|
||||||
onTriggered: confirmLogoutDialogComponent.createObject(root).open()
|
onTriggered: (Qt.createComponent("org.kde.neochat", "ConfirmLogoutDialog").createObject(QQC2.Overlay.overlay, {
|
||||||
}
|
connection: root.connection
|
||||||
|
}) as Kirigami.Dialog).open()
|
||||||
readonly property Component confirmLogoutDialogComponent: ConfirmLogoutDialog {
|
|
||||||
connection: root.connection
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Controls as QQC2
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
@@ -16,8 +17,6 @@ Kirigami.Dialog {
|
|||||||
|
|
||||||
required property NeoChatConnection connection
|
required property NeoChatConnection connection
|
||||||
|
|
||||||
parent: applicationWindow().overlay
|
|
||||||
|
|
||||||
leftPadding: 0
|
leftPadding: 0
|
||||||
rightPadding: 0
|
rightPadding: 0
|
||||||
topPadding: 0
|
topPadding: 0
|
||||||
@@ -25,7 +24,7 @@ Kirigami.Dialog {
|
|||||||
|
|
||||||
standardButtons: Kirigami.Dialog.NoButton
|
standardButtons: Kirigami.Dialog.NoButton
|
||||||
|
|
||||||
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
|
width: Math.min(QQC2.ApplicationWindow.window.width, Kirigami.Units.gridUnit * 24)
|
||||||
title: i18nc("@title: dialog to switch between logged in accounts", "Switch Account")
|
title: i18nc("@title: dialog to switch between logged in accounts", "Switch Account")
|
||||||
|
|
||||||
onVisibleChanged: if (visible) {
|
onVisibleChanged: if (visible) {
|
||||||
@@ -53,7 +52,7 @@ Kirigami.Dialog {
|
|||||||
}
|
}
|
||||||
text: i18nc("@button: login to or register a new account.", "Add Account")
|
text: i18nc("@button: login to or register a new account.", "Add Account")
|
||||||
contentItem: Delegates.SubtitleContentItem {
|
contentItem: Delegates.SubtitleContentItem {
|
||||||
itemDelegate: parent
|
itemDelegate: addDelegate
|
||||||
subtitle: i18n("Log in or create a new account")
|
subtitle: i18n("Log in or create a new account")
|
||||||
labelItem.textFormat: Text.PlainText
|
labelItem.textFormat: Text.PlainText
|
||||||
subtitleItem.textFormat: Text.PlainText
|
subtitleItem.textFormat: Text.PlainText
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
QQC2.ToolButton {
|
QQC2.ToolButton {
|
||||||
id: editImageButton
|
id: editImageButton
|
||||||
visible: hasImage
|
visible: root.hasImage
|
||||||
icon.name: "document-edit"
|
icon.name: "document-edit"
|
||||||
text: i18n("Edit")
|
text: i18n("Edit")
|
||||||
display: QQC2.AbstractButton.IconOnly
|
display: QQC2.AbstractButton.IconOnly
|
||||||
@@ -46,9 +46,9 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let imageEditor = applicationWindow().pageStack.pushDialogLayer(imageEditorPage);
|
let imageEditor = (Kirigami.PageStack.pageStack as Kirigami.PageRow).pushDialogLayer(imageEditorPage);
|
||||||
imageEditor.newPathChanged.connect(function (newPath) {
|
imageEditor.newPathChanged.connect(function (newPath) {
|
||||||
applicationWindow().pageStack.layers.pop();
|
imageEditor.closeDialog();
|
||||||
root.attachmentPath = newPath;
|
root.attachmentPath = newPath;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ ColumnLayout {
|
|||||||
action: Kirigami.Action {
|
action: Kirigami.Action {
|
||||||
text: i18n("Cancel sending attachment")
|
text: i18n("Cancel sending attachment")
|
||||||
icon.name: "dialog-close"
|
icon.name: "dialog-close"
|
||||||
onTriggered: attachmentCancelled()
|
onTriggered: root.attachmentCancelled()
|
||||||
shortcut: "Escape"
|
shortcut: "Escape"
|
||||||
}
|
}
|
||||||
QQC2.ToolTip.text: text
|
QQC2.ToolTip.text: text
|
||||||
@@ -75,8 +75,8 @@ ColumnLayout {
|
|||||||
|
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
cache: false // Cache is not needed. Images will rarely be shown repeatedly.
|
cache: false // Cache is not needed. Images will rarely be shown repeatedly.
|
||||||
source: hasImage ? root.attachmentPath : ""
|
source: root.hasImage ? root.attachmentPath : ""
|
||||||
visible: hasImage
|
visible: root.hasImage
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
onSourceChanged: {
|
onSourceChanged: {
|
||||||
@@ -114,11 +114,11 @@ ColumnLayout {
|
|||||||
id: mimetypeIcon
|
id: mimetypeIcon
|
||||||
implicitWidth: Kirigami.Units.iconSizes.smallMedium
|
implicitWidth: Kirigami.Units.iconSizes.smallMedium
|
||||||
implicitHeight: Kirigami.Units.iconSizes.smallMedium
|
implicitHeight: Kirigami.Units.iconSizes.smallMedium
|
||||||
source: attachmentMimetype.iconName
|
source: root.attachmentMimetype.iconName
|
||||||
}
|
}
|
||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
id: fileLabel
|
id: fileLabel
|
||||||
text: baseFileName
|
text: root.baseFileName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,9 @@
|
|||||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
|
||||||
import QtQuick.Controls as QQC2
|
import QtQuick.Controls as QQC2
|
||||||
import QtQuick.Templates as T
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.delegates as Delegates
|
import org.kde.kirigamiaddons.delegates as Delegates
|
||||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
|
||||||
|
|
||||||
Delegates.RoundedItemDelegate {
|
Delegates.RoundedItemDelegate {
|
||||||
id: root
|
id: root
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import QtQml.Models
|
|||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||||
import org.kde.kitemmodels
|
|
||||||
|
|
||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ ApplicationWindow {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: mapView.map
|
target: mapView.map
|
||||||
function onCopyrightLinkActivated() {
|
function onCopyrightLinkActivated(link: string): void {
|
||||||
Qt.openUrlExternally(link);
|
Qt.openUrlExternally(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,23 +17,6 @@ Labs.MenuBar {
|
|||||||
|
|
||||||
required property NeoChatConnection connection
|
required property NeoChatConnection connection
|
||||||
|
|
||||||
Labs.Menu {
|
|
||||||
title: i18nc("menu", "NeoChat")
|
|
||||||
|
|
||||||
Labs.MenuItem {
|
|
||||||
enabled: pageStack.layers.currentItem.title !== i18n("Configure NeoChat…")
|
|
||||||
text: i18nc("menu", "Configure NeoChat…")
|
|
||||||
|
|
||||||
shortcut: StandardKey.Preferences
|
|
||||||
onTriggered: NeoChatSettingsView.open()
|
|
||||||
}
|
|
||||||
Labs.MenuItem {
|
|
||||||
text: i18nc("menu", "Quit NeoChat")
|
|
||||||
|
|
||||||
shortcut: StandardKey.Quit
|
|
||||||
onTriggered: Qt.quit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Labs.Menu {
|
Labs.Menu {
|
||||||
title: i18nc("menu", "File")
|
title: i18nc("menu", "File")
|
||||||
|
|
||||||
@@ -74,6 +57,19 @@ Labs.MenuBar {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Labs.MenuItem {
|
||||||
|
enabled: pageStack.layers.currentItem.title !== i18n("Configure NeoChat…")
|
||||||
|
text: i18nc("menu", "Configure NeoChat…")
|
||||||
|
|
||||||
|
shortcut: StandardKey.Preferences
|
||||||
|
onTriggered: NeoChatSettingsView.open()
|
||||||
|
}
|
||||||
|
Labs.MenuItem {
|
||||||
|
text: i18nc("menu", "Quit NeoChat")
|
||||||
|
|
||||||
|
shortcut: StandardKey.Quit
|
||||||
|
onTriggered: Qt.quit()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EditMenu {
|
EditMenu {
|
||||||
title: i18nc("menu", "Edit")
|
title: i18nc("menu", "Edit")
|
||||||
|
|||||||
@@ -52,6 +52,15 @@ ColumnLayout {
|
|||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Kirigami.SelectableLabel {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font: Kirigami.Theme.smallFont
|
||||||
|
textFormat: TextEdit.PlainText
|
||||||
|
visible: root.currentRoom && root.currentRoom.canonicalAlias
|
||||||
|
text: root.currentRoom && root.currentRoom.canonicalAlias ? root.currentRoom.canonicalAlias : ""
|
||||||
|
color: Kirigami.Theme.disabledTextColor
|
||||||
|
}
|
||||||
|
|
||||||
Kirigami.Heading {
|
Kirigami.Heading {
|
||||||
text: root.currentRoom.displayName
|
text: root.currentRoom.displayName
|
||||||
|
|
||||||
@@ -70,7 +79,14 @@ ColumnLayout {
|
|||||||
spacing: Kirigami.Units.smallSpacing
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
Kirigami.Heading {
|
Kirigami.Heading {
|
||||||
text: root.currentRoom.displayName
|
text: root.invitingMember.displayName
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.Label {
|
||||||
|
text: root.invitingMember.id
|
||||||
|
color: Kirigami.Theme.disabledTextColor
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
}
|
}
|
||||||
@@ -159,7 +175,7 @@ ColumnLayout {
|
|||||||
|
|
||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
color: Kirigami.Theme.disabledTextColor
|
color: Kirigami.Theme.disabledTextColor
|
||||||
text: i18nc("@info:label", "You can reject invitations from unknown users under Security settings.")
|
text: xi18nc("@info:label Ensure you are referring to the same translation used for that settings page", "You can reject invitations from unknown users under the <interface>Security & Safety</interface> settings.")
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
|
|
||||||
// + 5 to prevent it from wrapping unnecessarily
|
// + 5 to prevent it from wrapping unnecessarily
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import QtQuick.Layouts
|
|||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.components as KirigamiComponents
|
import org.kde.kirigamiaddons.components as KirigamiComponents
|
||||||
import org.kde.kirigamiaddons.formcard as FormCard
|
import org.kde.kirigamiaddons.formcard as FormCard
|
||||||
import org.kde.prison
|
|
||||||
|
|
||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
|
|
||||||
@@ -25,7 +24,7 @@ Kirigami.Dialog {
|
|||||||
|
|
||||||
standardButtons: Kirigami.Dialog.NoButton
|
standardButtons: Kirigami.Dialog.NoButton
|
||||||
|
|
||||||
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
|
width: Math.min(QQC2.ApplicationWindow.window.width, Kirigami.Units.gridUnit * 24)
|
||||||
title: i18nc("@title:dialog", "Join Room")
|
title: i18nc("@title:dialog", "Join Room")
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
|
|||||||
@@ -60,21 +60,21 @@ Kirigami.ApplicationWindow {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: LoginHelper
|
target: LoginHelper
|
||||||
function onLoaded() {
|
function onLoaded(): void {
|
||||||
root.load();
|
root.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Registration
|
target: Registration
|
||||||
function onLoaded() {
|
function onLoaded(): void {
|
||||||
root.load();
|
root.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root.quitAction
|
target: root.quitAction
|
||||||
function onTriggered() {
|
function onTriggered(): void {
|
||||||
Qt.quit();
|
Qt.quit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,42 +98,40 @@ Kirigami.ApplicationWindow {
|
|||||||
Connections {
|
Connections {
|
||||||
target: RoomManager
|
target: RoomManager
|
||||||
|
|
||||||
function onCurrentRoomChanged() {
|
function onCurrentRoomChanged(): void {
|
||||||
if (RoomManager.currentRoom && pageStack.depth <= 1 && root.initialized && Kirigami.Settings.isMobile) {
|
if (RoomManager.currentRoom && pageStack.depth <= 1 && root.initialized && Kirigami.Settings.isMobile) {
|
||||||
let roomPage = pageStack.layers.push(Qt.createComponent('org.kde.neochat', 'RoomPage'), {
|
let roomPage = pageStack.layers.push(Qt.createComponent('org.kde.neochat', 'RoomPage'));
|
||||||
connection: root.connection
|
|
||||||
});
|
|
||||||
roomPage.backRequested.connect(event => {
|
roomPage.backRequested.connect(event => {
|
||||||
RoomManager.clearCurrentRoom();
|
RoomManager.clearCurrentRoom();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAskJoinRoom(room) {
|
function onAskJoinRoom(room: NeoChatRoom): void {
|
||||||
Qt.createComponent("org.kde.neochat", "JoinRoomDialog").createObject(root, {
|
Qt.createComponent("org.kde.neochat", "JoinRoomDialog").createObject(root, {
|
||||||
room: room,
|
room: room,
|
||||||
connection: root.connection
|
connection: root.connection
|
||||||
}).open();
|
}).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onShowUserDetail(user, room) {
|
function onShowUserDetail(user, room: NeoChatRoom): void {
|
||||||
root.showUserDetail(user, room);
|
root.showUserDetail(user, room);
|
||||||
}
|
}
|
||||||
|
|
||||||
function goToEvent(event) {
|
function goToEvent(event: string): void {
|
||||||
if (event.length > 0) {
|
if (event.length > 0) {
|
||||||
roomItem.goToEvent(event);
|
roomItem.goToEvent(event);
|
||||||
}
|
}
|
||||||
roomItem.forceActiveFocus();
|
roomItem.forceActiveFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAskDirectChatConfirmation(user) {
|
function onAskDirectChatConfirmation(user): void {
|
||||||
Qt.createComponent("org.kde.neochat", "AskDirectChatConfirmation").createObject(this, {
|
Qt.createComponent("org.kde.neochat", "AskDirectChatConfirmation").createObject(this, {
|
||||||
user: user
|
user: user
|
||||||
}).open();
|
}).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onExternalUrl(url) {
|
function onExternalUrl(url): void {
|
||||||
let dialog = Qt.createComponent("org.kde.neochat", "ConfirmUrlDialog").createObject(this);
|
let dialog = Qt.createComponent("org.kde.neochat", "ConfirmUrlDialog").createObject(this);
|
||||||
dialog.link = url;
|
dialog.link = url;
|
||||||
dialog.open();
|
dialog.open();
|
||||||
@@ -338,7 +336,7 @@ Kirigami.ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function handleShare(): void {
|
function handleShare(): void {
|
||||||
const dialog = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), {
|
const dialog = root.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), {
|
||||||
connection: root.connection
|
connection: root.connection
|
||||||
}, {
|
}, {
|
||||||
title: i18nc("@title", "Share"),
|
title: i18nc("@title", "Share"),
|
||||||
@@ -365,9 +363,7 @@ Kirigami.ApplicationWindow {
|
|||||||
RoomManager.loadInitialRoom();
|
RoomManager.loadInitialRoom();
|
||||||
|
|
||||||
if (!Kirigami.Settings.isMobile) {
|
if (!Kirigami.Settings.isMobile) {
|
||||||
let roomPage = pageStack.push(Qt.createComponent('org.kde.neochat', 'RoomPage'), {
|
let roomPage = pageStack.push(Qt.createComponent('org.kde.neochat', 'RoomPage'));
|
||||||
connection: root.connection
|
|
||||||
});
|
|
||||||
roomPage.forceActiveFocus();
|
roomPage.forceActiveFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ Kirigami.Page {
|
|||||||
icon.name: "document-edit"
|
icon.name: "document-edit"
|
||||||
visible: root.allowEdit
|
visible: root.allowEdit
|
||||||
enabled: room.canSendState(root.type) && (!root.stateKey.startsWith("@") || root.stateKey === root.room.connection.localUserId) && root.type !== "m.room.create"
|
enabled: room.canSendState(root.type) && (!root.stateKey.startsWith("@") || root.stateKey === root.room.connection.localUserId) && root.type !== "m.room.create"
|
||||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "EditStateDialog.qml"), {
|
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "EditStateDialog"), {
|
||||||
room: root.room,
|
room: root.room,
|
||||||
type: root.type,
|
type: root.type,
|
||||||
stateKey: root.stateKey,
|
stateKey: root.stateKey,
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ Components.AlbumMaximizeComponent {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: MediaManager
|
target: MediaManager
|
||||||
function onPlaybackStarted() {
|
function onPlaybackStarted(): void {
|
||||||
if (currentItem.playbackState === MediaPlayer.PlayingState) {
|
if (currentItem.playbackState === MediaPlayer.PlayingState) {
|
||||||
currentItem.pause();
|
currentItem.pause();
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ Components.AlbumMaximizeComponent {
|
|||||||
Connections {
|
Connections {
|
||||||
target: currentRoom
|
target: currentRoom
|
||||||
|
|
||||||
function onFileTransferProgress(id, progress, total) {
|
function onFileTransferProgress(id: string, progress: int, total: int): void {
|
||||||
if (id == root.currentEventId) {
|
if (id == root.currentEventId) {
|
||||||
root.downloadAction.progress = progress / total * 100.0;
|
root.downloadAction.progress = progress / total * 100.0;
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ Components.AlbumMaximizeComponent {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: RoomManager
|
target: RoomManager
|
||||||
function onCloseFullScreen() {
|
function onCloseFullScreen(): void {
|
||||||
root.close();
|
root.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ Kirigami.Dialog {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
|
width: Math.min(QQC2.ApplicationWindow.window.width, Kirigami.Units.gridUnit * 24)
|
||||||
title: i18nc("@title: create new poll in the room", "Create Poll")
|
title: i18nc("@title: create new poll in the room", "Create Poll")
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Kirigami.Page {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root.QQC2.ApplicationWindow.window
|
target: root.QQC2.ApplicationWindow.window
|
||||||
function onClosing() {
|
function onClosing(): void {
|
||||||
root.destroy();
|
root.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import QtQuick.Controls as QQC2
|
|||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kitemmodels
|
|
||||||
|
|
||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ Kirigami.Dialog {
|
|||||||
|
|
||||||
property var connection
|
property var connection
|
||||||
|
|
||||||
parent: applicationWindow().overlay
|
|
||||||
|
|
||||||
leftPadding: 0
|
leftPadding: 0
|
||||||
rightPadding: 0
|
rightPadding: 0
|
||||||
topPadding: 0
|
topPadding: 0
|
||||||
@@ -24,7 +22,7 @@ Kirigami.Dialog {
|
|||||||
|
|
||||||
title: i18nc("@title Join <name of a space>", "Join %1", SpaceHierarchyCache.recommendedSpaceDisplayName)
|
title: i18nc("@title Join <name of a space>", "Join %1", SpaceHierarchyCache.recommendedSpaceDisplayName)
|
||||||
|
|
||||||
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
|
width: Math.min(QQC2.ApplicationWindow.window.width, Kirigami.Units.gridUnit * 24)
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
FormCard.AbstractFormDelegate {
|
FormCard.AbstractFormDelegate {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import QtQuick.Layouts
|
|||||||
import QtQuick.Window
|
import QtQuick.Window
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kitemmodels
|
|
||||||
|
|
||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
|
|
||||||
@@ -64,9 +63,9 @@ Kirigami.Page {
|
|||||||
|
|
||||||
actions: [
|
actions: [
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
visible: Kirigami.Settings.isMobile || !applicationWindow().pageStack.wideMode
|
visible: Kirigami.Settings.isMobile || !root.Kirigami.PageStack.pageStack.wideMode
|
||||||
icon.name: "view-right-new"
|
icon.name: "view-right-new"
|
||||||
onTriggered: applicationWindow().openRoomDrawer()
|
onTriggered: (root.QQC2.ApplicationWindow.window as Main).openRoomDrawer()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -81,9 +80,9 @@ Kirigami.Page {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root.currentRoom.connection
|
target: root.currentRoom.connection
|
||||||
function onIsOnlineChanged() {
|
function onIsOnlineChanged(): void {
|
||||||
if (!root.currentRoom.connection.isOnline) {
|
if (!root.currentRoom.connection.isOnline) {
|
||||||
banner.text = i18n("NeoChat is offline. Please check your network connection.");
|
banner.text = i18nc("@info:status", "NeoChat is offline. Please check your network connection.");
|
||||||
banner.visible = true;
|
banner.visible = true;
|
||||||
banner.type = Kirigami.MessageType.Error;
|
banner.type = Kirigami.MessageType.Error;
|
||||||
} else {
|
} else {
|
||||||
@@ -138,8 +137,8 @@ Kirigami.Page {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
sourceComponent: Kirigami.PlaceholderMessage {
|
sourceComponent: Kirigami.PlaceholderMessage {
|
||||||
icon.name: "org.kde.neochat"
|
icon.name: "org.kde.neochat"
|
||||||
text: i18n("Welcome to NeoChat")
|
text: i18nc("@title", "Welcome to NeoChat")
|
||||||
explanation: i18n("Select or join a room to get started")
|
explanation: i18nc("@info:usagetip", "Select or join a room to get started")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,20 +162,20 @@ Kirigami.Page {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: RoomManager
|
target: RoomManager
|
||||||
function onCurrentRoomChanged() {
|
function onCurrentRoomChanged(): void {
|
||||||
if (root.currentRoom && root.currentRoom.isInvite) {
|
if (root.currentRoom && root.currentRoom.isInvite) {
|
||||||
Controller.clearInvitationNotification(root.currentRoom.id);
|
Controller.clearInvitationNotification(root.currentRoom.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onGoToEvent(eventId) {
|
function onGoToEvent(eventId: string): void {
|
||||||
(timelineViewLoader.item as TimelineView).goToEvent(eventId);
|
(timelineViewLoader.item as TimelineView).goToEvent(eventId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root.currentRoom.connection
|
target: root.currentRoom.connection
|
||||||
function onJoinedRoom(room, invited) {
|
function onJoinedRoom(room: NeoChatRoom, invited: NeoChatRoom): void {
|
||||||
if (root.currentRoom.id === invited.id) {
|
if (root.currentRoom.id === invited.id) {
|
||||||
RoomManager.resolveResource(room.id);
|
RoomManager.resolveResource(room.id);
|
||||||
}
|
}
|
||||||
@@ -196,22 +195,22 @@ Kirigami.Page {
|
|||||||
Connections {
|
Connections {
|
||||||
target: RoomManager
|
target: RoomManager
|
||||||
|
|
||||||
function onShowMessage(messageType, message) {
|
function onShowMessage(messageType: Kirigami.MessageType, message: string): void {
|
||||||
banner.text = message;
|
banner.text = message;
|
||||||
banner.type = messageType;
|
banner.type = messageType;
|
||||||
banner.visible = true;
|
banner.visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onShowEventSource(eventId) {
|
function onShowEventSource(eventId: string): void {
|
||||||
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
(root.Kirigami.PageStack.pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||||
sourceText: root.currentRoom.getEventJsonSource(eventId)
|
sourceText: root.currentRoom.getEventJsonSource(eventId)
|
||||||
}, {
|
}, {
|
||||||
title: i18n("Message Source"),
|
title: i18nc("@title:dialog", "Message Source"),
|
||||||
width: Kirigami.Units.gridUnit * 25
|
width: Kirigami.Units.gridUnit * 25
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onShowMessageMenu(eventId, author, messageComponentType, plainText, htmlText, selectedText, hoveredLink, isThread) {
|
function onShowMessageMenu(eventId: string, author, messageComponentType, plainText: string, htmlText: string, selectedText: string, hoveredLink: string, isThread: bool): void {
|
||||||
const contextMenu = messageDelegateContextMenu.createObject(root, {
|
const contextMenu = messageDelegateContextMenu.createObject(root, {
|
||||||
selectedText: selectedText,
|
selectedText: selectedText,
|
||||||
hoveredLink: hoveredLink,
|
hoveredLink: hoveredLink,
|
||||||
|
|||||||
@@ -67,10 +67,14 @@ QQC2.ComboBox {
|
|||||||
QQC2.ToolButton {
|
QQC2.ToolButton {
|
||||||
visible: serverItem.isAddServerDelegate || serverItem.isDeletable
|
visible: serverItem.isAddServerDelegate || serverItem.isDeletable
|
||||||
icon.name: serverItem.isAddServerDelegate ? "list-add" : "dialog-close"
|
icon.name: serverItem.isAddServerDelegate ? "list-add" : "dialog-close"
|
||||||
text: i18nc("@action:button", "Add new server")
|
text: serverItem.isAddServerDelegate ? i18nc("@action:button", "Add new server") : i18nc("@action:button", "Remove server")
|
||||||
Accessible.name: text
|
Accessible.name: text
|
||||||
display: QQC2.AbstractButton.IconOnly
|
display: QQC2.AbstractButton.IconOnly
|
||||||
|
|
||||||
|
QQC2.ToolTip.text: text
|
||||||
|
QQC2.ToolTip.visible: hovered
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (root.currentIndex === serverItem.index && serverItem.isDeletable) {
|
if (root.currentIndex === serverItem.index && serverItem.isDeletable) {
|
||||||
root.currentIndex = 0;
|
root.currentIndex = 0;
|
||||||
@@ -173,7 +177,7 @@ QQC2.ComboBox {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: serverListModel
|
target: serverListModel
|
||||||
function onServerCheckComplete(url, valid) {
|
function onServerCheckComplete(url: string, valid: bool): void {
|
||||||
if (url == serverUrlField.text && valid) {
|
if (url == serverUrlField.text && valid) {
|
||||||
serverUrlField.isValidServer = true;
|
serverUrlField.isValidServer = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ Kirigami.Action {
|
|||||||
text: i18n("Share")
|
text: i18n("Share")
|
||||||
tooltip: i18n("Share the selected media")
|
tooltip: i18n("Share the selected media")
|
||||||
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This property holds the input data for purpose.
|
* This property holds the input data for purpose.
|
||||||
*
|
*
|
||||||
@@ -60,7 +58,7 @@ Kirigami.Action {
|
|||||||
if (id != root.eventId) {
|
if (id != root.eventId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "ShareDialog"), {
|
pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "ShareDialog"), {
|
||||||
title: root.text,
|
title: root.text,
|
||||||
index: index,
|
index: index,
|
||||||
model: root._instantiator.model
|
model: root._instantiator.model
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ Kirigami.Dialog {
|
|||||||
icon.name: "delete"
|
icon.name: "delete"
|
||||||
icon.color: Kirigami.Theme.negativeTextColor
|
icon.color: Kirigami.Theme.negativeTextColor
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
let dialog = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ReasonDialog'), {
|
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ReasonDialog'), {
|
||||||
title: i18nc("@title:dialog", "Remove Messages"),
|
title: i18nc("@title:dialog", "Remove Messages"),
|
||||||
placeholder: i18nc("@info:placeholder", "Reason for removing this user's recent messages"),
|
placeholder: i18nc("@info:placeholder", "Reason for removing this user's recent messages"),
|
||||||
actionText: i18nc("@action:button 'Remove' as in 'Remove these messages'", "Remove"),
|
actionText: i18nc("@action:button 'Remove' as in 'Remove these messages'", "Remove"),
|
||||||
|
|||||||
@@ -236,11 +236,18 @@ void RoomManager::resolveResource(Uri uri, const QString &action)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoomManager::maximizeMedia(int index)
|
void RoomManager::maximizeMedia(const QString &eventId)
|
||||||
{
|
{
|
||||||
if (index < -1 || index > m_mediaMessageFilterModel->rowCount()) {
|
if (eventId.isEmpty()) {
|
||||||
|
qWarning() << "Tried to open media for empty event id";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto index = m_mediaMessageFilterModel->getRowForEventId(eventId);
|
||||||
|
if (index == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Q_EMIT showMaximizedMedia(index);
|
Q_EMIT showMaximizedMedia(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,8 +271,17 @@ void RoomManager::viewEventSource(const QString &eventId)
|
|||||||
|
|
||||||
void RoomManager::viewEventMenu(const QString &eventId, NeoChatRoom *room, NeochatRoomMember *sender, const QString &selectedText, const QString &hoveredLink)
|
void RoomManager::viewEventMenu(const QString &eventId, NeoChatRoom *room, NeochatRoomMember *sender, const QString &selectedText, const QString &hoveredLink)
|
||||||
{
|
{
|
||||||
const auto &event = **room->findInTimeline(eventId);
|
if (eventId.isEmpty()) {
|
||||||
|
qWarning() << "Tried to open event menu with empty event id";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto it = room->findInTimeline(eventId);
|
||||||
|
if (it == room->historyEdge()) {
|
||||||
|
// This is probably a pending event
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto &event = **it;
|
||||||
if (EventHandler::mediaInfo(room, &event).contains("mimeType"_L1)) {
|
if (EventHandler::mediaInfo(room, &event).contains("mimeType"_L1)) {
|
||||||
Q_EMIT showFileMenu(eventId,
|
Q_EMIT showFileMenu(eventId,
|
||||||
sender,
|
sender,
|
||||||
@@ -358,19 +374,6 @@ void RoomManager::visitRoom(Room *r, const QString &eventId)
|
|||||||
if (m_currentRoom && !m_currentRoom->editCache()->editId().isEmpty()) {
|
if (m_currentRoom && !m_currentRoom->editCache()->editId().isEmpty()) {
|
||||||
m_currentRoom->editCache()->setEditId({});
|
m_currentRoom->editCache()->setEditId({});
|
||||||
}
|
}
|
||||||
if (m_currentRoom && !m_currentRoom->isSpace() && m_chatDocumentHandler) {
|
|
||||||
// We're doing these things here because it is critical that they are switched at the same time
|
|
||||||
if (m_chatDocumentHandler->document()) {
|
|
||||||
m_currentRoom->mainCache()->setSavedText(m_chatDocumentHandler->document()->textDocument()->toPlainText());
|
|
||||||
m_chatDocumentHandler->setRoom(room);
|
|
||||||
if (room) {
|
|
||||||
m_chatDocumentHandler->document()->textDocument()->setPlainText(room->mainCache()->savedText());
|
|
||||||
room->mainCache()->setText(room->mainCache()->savedText());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_chatDocumentHandler->setRoom(room);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!room) {
|
if (!room) {
|
||||||
setCurrentRoom({});
|
setCurrentRoom({});
|
||||||
@@ -470,18 +473,6 @@ bool RoomManager::visitNonMatrix(const QUrl &url)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatDocumentHandler *RoomManager::chatDocumentHandler() const
|
|
||||||
{
|
|
||||||
return m_chatDocumentHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RoomManager::setChatDocumentHandler(ChatDocumentHandler *handler)
|
|
||||||
{
|
|
||||||
m_chatDocumentHandler = handler;
|
|
||||||
m_chatDocumentHandler->setRoom(m_currentRoom);
|
|
||||||
Q_EMIT chatDocumentHandlerChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RoomManager::setConnection(NeoChatConnection *connection)
|
void RoomManager::setConnection(NeoChatConnection *connection)
|
||||||
{
|
{
|
||||||
if (m_connection == connection) {
|
if (m_connection == connection) {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#include <Quotient/roommember.h>
|
#include <Quotient/roommember.h>
|
||||||
#include <Quotient/uriresolver.h>
|
#include <Quotient/uriresolver.h>
|
||||||
|
|
||||||
#include "chatdocumenthandler.h"
|
|
||||||
#include "enums/messagecomponenttype.h"
|
#include "enums/messagecomponenttype.h"
|
||||||
#include "enums/messagetype.h"
|
#include "enums/messagetype.h"
|
||||||
#include "models/mediamessagefiltermodel.h"
|
#include "models/mediamessagefiltermodel.h"
|
||||||
@@ -137,13 +136,6 @@ class RoomManager : public QObject, public UriResolverBase
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(bool hasOpenRoom READ hasOpenRoom NOTIFY currentRoomChanged)
|
Q_PROPERTY(bool hasOpenRoom READ hasOpenRoom NOTIFY currentRoomChanged)
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The ChatDocumentHandler for the open room.
|
|
||||||
*
|
|
||||||
* @sa ChatDocumentHandler
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(ChatDocumentHandler *chatDocumentHandler READ chatDocumentHandler WRITE setChatDocumentHandler NOTIFY chatDocumentHandlerChanged)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~RoomManager();
|
virtual ~RoomManager();
|
||||||
static RoomManager &instance();
|
static RoomManager &instance();
|
||||||
@@ -212,12 +204,8 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Show a media item maximized.
|
* @brief Show a media item maximized.
|
||||||
*
|
|
||||||
* @param index the index to open the maximize delegate model at. This is the
|
|
||||||
* index in the MediaMessageFilterModel owned by this RoomManager. A value
|
|
||||||
* of -1 opens a the default item.
|
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void maximizeMedia(int index);
|
Q_INVOKABLE void maximizeMedia(const QString &eventId);
|
||||||
|
|
||||||
Q_INVOKABLE void maximizeCode(NeochatRoomMember *author, const QDateTime &time, const QString &codeText, const QString &language);
|
Q_INVOKABLE void maximizeCode(NeochatRoomMember *author, const QDateTime &time, const QString &codeText, const QString &language);
|
||||||
|
|
||||||
@@ -237,9 +225,6 @@ public:
|
|||||||
Q_INVOKABLE void
|
Q_INVOKABLE void
|
||||||
viewEventMenu(const QString &eventId, NeoChatRoom *room, NeochatRoomMember *sender, const QString &selectedText = {}, const QString &hoveredLink = {});
|
viewEventMenu(const QString &eventId, NeoChatRoom *room, NeochatRoomMember *sender, const QString &selectedText = {}, const QString &hoveredLink = {});
|
||||||
|
|
||||||
ChatDocumentHandler *chatDocumentHandler() const;
|
|
||||||
void setChatDocumentHandler(ChatDocumentHandler *handler);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set a URL to be loaded as the initial room.
|
* @brief Set a URL to be loaded as the initial room.
|
||||||
*/
|
*/
|
||||||
@@ -342,8 +327,6 @@ Q_SIGNALS:
|
|||||||
*/
|
*/
|
||||||
void showMessage(MessageType::Type messageType, const QString &message);
|
void showMessage(MessageType::Type messageType, const QString &message);
|
||||||
|
|
||||||
void chatDocumentHandlerChanged();
|
|
||||||
|
|
||||||
void connectionChanged();
|
void connectionChanged();
|
||||||
|
|
||||||
void directChatsActiveChanged();
|
void directChatsActiveChanged();
|
||||||
@@ -372,7 +355,6 @@ private:
|
|||||||
KConfigGroup m_lastRoomConfig;
|
KConfigGroup m_lastRoomConfig;
|
||||||
KConfigGroup m_lastSpaceConfig;
|
KConfigGroup m_lastSpaceConfig;
|
||||||
KConfigGroup m_directChatsConfig;
|
KConfigGroup m_directChatsConfig;
|
||||||
QPointer<ChatDocumentHandler> m_chatDocumentHandler;
|
|
||||||
|
|
||||||
RoomListModel *m_roomListModel;
|
RoomListModel *m_roomListModel;
|
||||||
SortFilterRoomListModel *m_sortFilterRoomListModel;
|
SortFilterRoomListModel *m_sortFilterRoomListModel;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#include <QDBusMetaType>
|
#include <QDBusMetaType>
|
||||||
|
|
||||||
|
#include <KWindowSystem>
|
||||||
|
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "models/roomlistmodel.h"
|
#include "models/roomlistmodel.h"
|
||||||
#include "models/sortfilterroomlistmodel.h"
|
#include "models/sortfilterroomlistmodel.h"
|
||||||
@@ -86,4 +88,9 @@ void Runner::Run(const QString &id, const QString &actionId)
|
|||||||
WindowController::instance().showAndRaiseWindow(QString());
|
WindowController::instance().showAndRaiseWindow(QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Runner::SetActivationToken(const QString &token)
|
||||||
|
{
|
||||||
|
KWindowSystem::setCurrentXdgActivationToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_runner.cpp"
|
#include "moc_runner.cpp"
|
||||||
|
|||||||
@@ -190,6 +190,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
Q_SCRIPTABLE void Run(const QString &id, const QString &actionId);
|
Q_SCRIPTABLE void Run(const QString &id, const QString &actionId);
|
||||||
|
|
||||||
|
Q_SCRIPTABLE void SetActivationToken(const QString &token);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void roomListModelChanged();
|
void roomListModelChanged();
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ QQC2.Control {
|
|||||||
displayHint: Kirigami.DisplayHint.IconOnly
|
displayHint: Kirigami.DisplayHint.IconOnly
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
let dialog = (Clipboard.hasImage ? attachDialog : openFileDialog).createObject(applicationWindow().overlay);
|
let dialog = (Clipboard.hasImage ? attachDialog : openFileDialog).createObject(root.QQC2.Overlay.overlay);
|
||||||
dialog.chosen.connect(path => _private.chatBarCache.attachmentPath = path);
|
dialog.chosen.connect(path => _private.chatBarCache.attachmentPath = path);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
}
|
}
|
||||||
@@ -197,6 +197,22 @@ QQC2.Control {
|
|||||||
visible: root.currentRoom.mainCache.replyId.length > 0
|
visible: root.currentRoom.mainCache.replyId.length > 0
|
||||||
sourceComponent: replyPane
|
sourceComponent: replyPane
|
||||||
}
|
}
|
||||||
|
RowLayout {
|
||||||
|
visible: replyLoader.visible && !root.currentRoom.mainCache.relationAuthorIsPresent
|
||||||
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
|
Kirigami.Icon {
|
||||||
|
source: "help-hint-symbolic"
|
||||||
|
color: Kirigami.Theme.disabledTextColor
|
||||||
|
|
||||||
|
Layout.preferredWidth: Kirigami.Units.iconSizes.small
|
||||||
|
Layout.preferredHeight: Kirigami.Units.iconSizes.small
|
||||||
|
}
|
||||||
|
QQC2.Label {
|
||||||
|
text: i18nc("@info", "The user you're replying to has left the room, and can't be notified.")
|
||||||
|
color: Kirigami.Theme.disabledTextColor
|
||||||
|
}
|
||||||
|
}
|
||||||
Loader {
|
Loader {
|
||||||
id: attachLoader
|
id: attachLoader
|
||||||
|
|
||||||
@@ -382,8 +398,6 @@ QQC2.Control {
|
|||||||
implicitHeight: replyComponent.implicitHeight
|
implicitHeight: replyComponent.implicitHeight
|
||||||
ReplyComponent {
|
ReplyComponent {
|
||||||
id: replyComponent
|
id: replyComponent
|
||||||
replyEventId: _private.chatBarCache.replyId
|
|
||||||
replyAuthor: _private.chatBarCache.relationAuthor
|
|
||||||
replyContentModel: ContentProvider.contentModelForEvent(root.currentRoom, _private.chatBarCache.replyId, true)
|
replyContentModel: ContentProvider.contentModelForEvent(root.currentRoom, _private.chatBarCache.replyId, true)
|
||||||
Message.maxContentWidth: replyLoader.item.width
|
Message.maxContentWidth: replyLoader.item.width
|
||||||
|
|
||||||
@@ -424,7 +438,6 @@ QQC2.Control {
|
|||||||
QtObject {
|
QtObject {
|
||||||
id: _private
|
id: _private
|
||||||
property ChatBarCache chatBarCache
|
property ChatBarCache chatBarCache
|
||||||
onChatBarCacheChanged: documentHandler.chatBarCache = chatBarCache
|
|
||||||
|
|
||||||
function postMessage() {
|
function postMessage() {
|
||||||
_private.chatBarCache.postMessage();
|
_private.chatBarCache.postMessage();
|
||||||
@@ -486,15 +499,14 @@ QQC2.Control {
|
|||||||
|
|
||||||
ChatDocumentHandler {
|
ChatDocumentHandler {
|
||||||
id: documentHandler
|
id: documentHandler
|
||||||
|
type: ChatBarType.Room
|
||||||
|
room: root.currentRoom
|
||||||
document: textField.textDocument
|
document: textField.textDocument
|
||||||
cursorPosition: textField.cursorPosition
|
cursorPosition: textField.cursorPosition
|
||||||
selectionStart: textField.selectionStart
|
selectionStart: textField.selectionStart
|
||||||
selectionEnd: textField.selectionEnd
|
selectionEnd: textField.selectionEnd
|
||||||
mentionColor: Kirigami.Theme.linkColor
|
mentionColor: Kirigami.Theme.linkColor
|
||||||
errorColor: Kirigami.Theme.negativeTextColor
|
errorColor: Kirigami.Theme.negativeTextColor
|
||||||
Component.onCompleted: {
|
|
||||||
RoomManager.chatDocumentHandler = documentHandler;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ QQC2.Popup {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: RoomManager
|
target: RoomManager
|
||||||
function onCurrentRoomChanged() {
|
function onCurrentRoomChanged(): void {
|
||||||
root.close();
|
root.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ QQC2.Popup {
|
|||||||
padding: 2
|
padding: 2
|
||||||
|
|
||||||
implicitHeight: Kirigami.Units.gridUnit * 20 + 2 * padding
|
implicitHeight: Kirigami.Units.gridUnit * 20 + 2 * padding
|
||||||
width: Math.min(contentItem.categoryIconSize * 11 + 2 * padding, applicationWindow().width)
|
width: Math.min(contentItem.categoryIconSize * 11 + 2 * padding, QQC2.ApplicationWindow.window.width)
|
||||||
contentItem: EmojiPicker {
|
contentItem: EmojiPicker {
|
||||||
id: emojiPicker
|
id: emojiPicker
|
||||||
height: 400
|
height: 400
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ Kirigami.Page {
|
|||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: selectionTool.selectionArea
|
target: selectionTool.selectionArea
|
||||||
function onDoubleClicked() {
|
function onDoubleClicked(): void {
|
||||||
rootEditorView.crop();
|
rootEditorView.crop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,13 @@ FormCard.FormCard {
|
|||||||
|
|
||||||
onToggled: NeoChatConfig.showAllEvents = checked
|
onToggled: NeoChatConfig.showAllEvents = checked
|
||||||
}
|
}
|
||||||
|
FormCard.FormCheckDelegate {
|
||||||
|
text: i18nc("@option:check", "Allow sending relations to any event in the timeline")
|
||||||
|
description: i18nc("@info", "This includes state events")
|
||||||
|
checked: NeoChatConfig.relateAnyEvent
|
||||||
|
|
||||||
|
onToggled: NeoChatConfig.relateAnyEvent = checked
|
||||||
|
}
|
||||||
FormCard.FormCheckDelegate {
|
FormCard.FormCheckDelegate {
|
||||||
id: roomAccountDataVisibleCheck
|
id: roomAccountDataVisibleCheck
|
||||||
text: i18nc("@option:check Enable the matrix 'threads' feature", "Always allow device verification")
|
text: i18nc("@option:check Enable the matrix 'threads' feature", "Always allow device verification")
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import QtQuick.Window
|
|||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.formcard as FormCard
|
import org.kde.kirigamiaddons.formcard as FormCard
|
||||||
import org.kde.kitemmodels
|
|
||||||
|
|
||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import QtQuick.Layouts
|
|||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.formcard as FormCard
|
import org.kde.kirigamiaddons.formcard as FormCard
|
||||||
import org.kde.kitemmodels
|
|
||||||
|
|
||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import QtQuick.Window
|
|||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.formcard as FormCard
|
import org.kde.kirigamiaddons.formcard as FormCard
|
||||||
import org.kde.kitemmodels
|
|
||||||
|
|
||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ target_sources(LibNeoChat PRIVATE
|
|||||||
texthandler.cpp
|
texthandler.cpp
|
||||||
urlhelper.cpp
|
urlhelper.cpp
|
||||||
utils.cpp
|
utils.cpp
|
||||||
|
enums/chatbartype.h
|
||||||
enums/messagecomponenttype.h
|
enums/messagecomponenttype.h
|
||||||
enums/messagetype.h
|
enums/messagetype.h
|
||||||
enums/powerlevel.cpp
|
enums/powerlevel.cpp
|
||||||
|
|||||||
@@ -15,6 +15,18 @@ using namespace Qt::StringLiterals;
|
|||||||
ChatBarCache::ChatBarCache(QObject *parent)
|
ChatBarCache::ChatBarCache(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
|
if (parent == nullptr) {
|
||||||
|
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto room = dynamic_cast<NeoChatRoom *>(parent);
|
||||||
|
if (room == nullptr) {
|
||||||
|
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connect(room, &NeoChatRoom::memberLeft, this, &ChatBarCache::relationAuthorIsPresentChanged);
|
||||||
|
connect(room, &NeoChatRoom::memberJoined, this, &ChatBarCache::relationAuthorIsPresentChanged);
|
||||||
|
connect(this, &ChatBarCache::relationIdChanged, this, &ChatBarCache::relationAuthorIsPresentChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ChatBarCache::text() const
|
QString ChatBarCache::text() const
|
||||||
@@ -137,6 +149,11 @@ Quotient::RoomMember ChatBarCache::relationAuthor() const
|
|||||||
return room->member((*room->findInTimeline(m_relationId))->senderId());
|
return room->member((*room->findInTimeline(m_relationId))->senderId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChatBarCache::relationAuthorIsPresent() const
|
||||||
|
{
|
||||||
|
return relationAuthor().membershipState() == Quotient::Membership::Join;
|
||||||
|
}
|
||||||
|
|
||||||
QString ChatBarCache::relationMessage() const
|
QString ChatBarCache::relationMessage() const
|
||||||
{
|
{
|
||||||
if (parent() == nullptr) {
|
if (parent() == nullptr) {
|
||||||
|
|||||||
@@ -99,6 +99,13 @@ class ChatBarCache : public QObject
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(Quotient::RoomMember relationAuthor READ relationAuthor NOTIFY relationIdChanged)
|
Q_PROPERTY(Quotient::RoomMember relationAuthor READ relationAuthor NOTIFY relationIdChanged)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief If the author for the message being replied to is still present in the room.
|
||||||
|
*
|
||||||
|
* @sa Quotient::RoomMember
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool relationAuthorIsPresent READ relationAuthorIsPresent NOTIFY relationAuthorIsPresentChanged)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The content of the related message.
|
* @brief The content of the related message.
|
||||||
*
|
*
|
||||||
@@ -153,6 +160,7 @@ public:
|
|||||||
void setEditId(const QString &editId);
|
void setEditId(const QString &editId);
|
||||||
|
|
||||||
Quotient::RoomMember relationAuthor() const;
|
Quotient::RoomMember relationAuthor() const;
|
||||||
|
bool relationAuthorIsPresent() const;
|
||||||
|
|
||||||
QString relationMessage() const;
|
QString relationMessage() const;
|
||||||
|
|
||||||
@@ -196,6 +204,7 @@ Q_SIGNALS:
|
|||||||
void threadIdChanged(const QString &oldThreadId, const QString &newThreadId);
|
void threadIdChanged(const QString &oldThreadId, const QString &newThreadId);
|
||||||
void attachmentPathChanged();
|
void attachmentPathChanged();
|
||||||
void mentionAdded(const QString &mention);
|
void mentionAdded(const QString &mention);
|
||||||
|
void relationAuthorIsPresentChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_text = QString();
|
QString m_text = QString();
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <Sonnet/BackgroundChecker>
|
#include <Sonnet/BackgroundChecker>
|
||||||
#include <Sonnet/Settings>
|
#include <Sonnet/Settings>
|
||||||
|
|
||||||
|
#include "chatbartype.h"
|
||||||
#include "chatdocumenthandler_logging.h"
|
#include "chatdocumenthandler_logging.h"
|
||||||
#include "eventhandler.h"
|
#include "eventhandler.h"
|
||||||
|
|
||||||
@@ -67,7 +68,11 @@ public:
|
|||||||
if (!room) {
|
if (!room) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto mentions = handler->chatBarCache()->mentions();
|
const auto chatchache = handler->chatBarCache();
|
||||||
|
if (!chatchache) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto mentions = chatchache->mentions();
|
||||||
mentions->erase(std::remove_if(mentions->begin(),
|
mentions->erase(std::remove_if(mentions->begin(),
|
||||||
mentions->end(),
|
mentions->end(),
|
||||||
[this](auto &mention) {
|
[this](auto &mention) {
|
||||||
@@ -105,18 +110,6 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
|||||||
, m_highlighter(new SyntaxHighlighter(this))
|
, m_highlighter(new SyntaxHighlighter(this))
|
||||||
, m_completionModel(new CompletionModel(this))
|
, m_completionModel(new CompletionModel(this))
|
||||||
{
|
{
|
||||||
connect(this, &ChatDocumentHandler::roomChanged, this, [this]() {
|
|
||||||
m_completionModel->setRoom(m_room);
|
|
||||||
static QPointer<NeoChatRoom> previousRoom = nullptr;
|
|
||||||
if (previousRoom) {
|
|
||||||
disconnect(m_chatBarCache, &ChatBarCache::textChanged, this, nullptr);
|
|
||||||
}
|
|
||||||
previousRoom = m_room;
|
|
||||||
connect(m_chatBarCache, &ChatBarCache::textChanged, this, [this]() {
|
|
||||||
int start = completionStartIndex();
|
|
||||||
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
connect(this, &ChatDocumentHandler::documentChanged, this, [this]() {
|
connect(this, &ChatDocumentHandler::documentChanged, this, [this]() {
|
||||||
if (!m_document) {
|
if (!m_document) {
|
||||||
m_highlighter->setDocument(nullptr);
|
m_highlighter->setDocument(nullptr);
|
||||||
@@ -153,6 +146,20 @@ int ChatDocumentHandler::completionStartIndex() const
|
|||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChatBarType::Type ChatDocumentHandler::type() const
|
||||||
|
{
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatDocumentHandler::setType(ChatBarType::Type type)
|
||||||
|
{
|
||||||
|
if (type == m_type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_type = type;
|
||||||
|
Q_EMIT typeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
QQuickTextDocument *ChatDocumentHandler::document() const
|
QQuickTextDocument *ChatDocumentHandler::document() const
|
||||||
{
|
{
|
||||||
return m_document;
|
return m_document;
|
||||||
@@ -198,22 +205,36 @@ void ChatDocumentHandler::setRoom(NeoChatRoom *room)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_room && m_type != ChatBarType::None) {
|
||||||
|
m_room->cacheForType(m_type)->disconnect(this);
|
||||||
|
if (!m_room->isSpace() && m_document && m_type == ChatBarType::Room) {
|
||||||
|
m_room->mainCache()->setSavedText(document()->textDocument()->toPlainText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_room = room;
|
m_room = room;
|
||||||
|
|
||||||
|
m_completionModel->setRoom(m_room);
|
||||||
|
if (m_room && m_type != ChatBarType::None) {
|
||||||
|
connect(m_room->cacheForType(m_type), &ChatBarCache::textChanged, this, [this]() {
|
||||||
|
int start = completionStartIndex();
|
||||||
|
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||||
|
});
|
||||||
|
if (!m_room->isSpace() && m_document && m_type == ChatBarType::Room) {
|
||||||
|
document()->textDocument()->setPlainText(room->mainCache()->savedText());
|
||||||
|
m_room->mainCache()->setText(room->mainCache()->savedText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Q_EMIT roomChanged();
|
Q_EMIT roomChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatBarCache *ChatDocumentHandler::chatBarCache() const
|
ChatBarCache *ChatDocumentHandler::chatBarCache() const
|
||||||
{
|
{
|
||||||
return m_chatBarCache;
|
if (!m_room || m_type == ChatBarType::None) {
|
||||||
}
|
return nullptr;
|
||||||
|
|
||||||
void ChatDocumentHandler::setChatBarCache(ChatBarCache *chatBarCache)
|
|
||||||
{
|
|
||||||
if (m_chatBarCache == chatBarCache) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
m_chatBarCache = chatBarCache;
|
return m_room->cacheForType(m_type);
|
||||||
Q_EMIT chatBarCacheChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatDocumentHandler::complete(int index)
|
void ChatDocumentHandler::complete(int index)
|
||||||
@@ -313,20 +334,20 @@ void ChatDocumentHandler::setSelectionEnd(int position)
|
|||||||
|
|
||||||
QString ChatDocumentHandler::getText() const
|
QString ChatDocumentHandler::getText() const
|
||||||
{
|
{
|
||||||
if (!m_chatBarCache) {
|
if (!m_room || m_type == ChatBarType::None) {
|
||||||
qCWarning(ChatDocumentHandling) << "getText called with m_chatBarCache set to nullptr.";
|
qCWarning(ChatDocumentHandling) << "getText called with no ChatBarCache available. ChatBarType: " << m_type << " Room: " << m_room;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return m_chatBarCache->text();
|
return m_room->cacheForType(m_type)->text();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatDocumentHandler::pushMention(const Mention mention) const
|
void ChatDocumentHandler::pushMention(const Mention mention) const
|
||||||
{
|
{
|
||||||
if (!m_chatBarCache) {
|
if (!m_room || m_type == ChatBarType::None) {
|
||||||
qCWarning(ChatDocumentHandling) << "pushMention called with m_chatBarCache set to nullptr.";
|
qCWarning(ChatDocumentHandling) << "pushMention called with no ChatBarCache available. ChatBarType: " << m_type << " Room: " << m_room;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_chatBarCache->mentions()->push_back(mention);
|
m_room->cacheForType(m_type)->mentions()->push_back(mention);
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor ChatDocumentHandler::mentionColor() const
|
QColor ChatDocumentHandler::mentionColor() const
|
||||||
@@ -365,7 +386,7 @@ void ChatDocumentHandler::updateMentions(QQuickTextDocument *document, const QSt
|
|||||||
{
|
{
|
||||||
setDocument(document);
|
setDocument(document);
|
||||||
|
|
||||||
if (editId.isEmpty() || !m_chatBarCache || !m_room) {
|
if (editId.isEmpty() || m_type == ChatBarType::None || !m_room) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,7 +395,7 @@ void ChatDocumentHandler::updateMentions(QQuickTextDocument *document, const QSt
|
|||||||
// Replaces the mentions that are baked into the HTML but plaintext in the original markdown
|
// Replaces the mentions that are baked into the HTML but plaintext in the original markdown
|
||||||
const QRegularExpression re(uR"lit(<a\shref="https:\/\/matrix.to\/#\/([\S]*)"\s?>([\S]*)<\/a>)lit"_s);
|
const QRegularExpression re(uR"lit(<a\shref="https:\/\/matrix.to\/#\/([\S]*)"\s?>([\S]*)<\/a>)lit"_s);
|
||||||
|
|
||||||
m_chatBarCache->mentions()->clear();
|
m_room->cacheForType(m_type)->mentions()->clear();
|
||||||
|
|
||||||
int linkSize = 0;
|
int linkSize = 0;
|
||||||
auto matches = re.globalMatch(EventHandler::rawMessageBody(*roomMessageEvent));
|
auto matches = re.globalMatch(EventHandler::rawMessageBody(*roomMessageEvent));
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
|
|
||||||
#include "chatbarcache.h"
|
#include "chatbarcache.h"
|
||||||
|
#include "enums/chatbartype.h"
|
||||||
#include "models/completionmodel.h"
|
#include "models/completionmodel.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
|
|
||||||
@@ -62,6 +63,11 @@ class ChatDocumentHandler : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The QQuickTextDocument that is being handled.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(ChatBarType::Type type READ type WRITE setType NOTIFY typeChanged)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The QQuickTextDocument that is being handled.
|
* @brief The QQuickTextDocument that is being handled.
|
||||||
*/
|
*/
|
||||||
@@ -95,11 +101,6 @@ class ChatDocumentHandler : public QObject
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The cache for the chat bar the text document is being handled for.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(ChatBarCache *chatBarCache READ chatBarCache WRITE setChatBarCache NOTIFY chatBarCacheChanged)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The color to highlight user mentions.
|
* @brief The color to highlight user mentions.
|
||||||
*/
|
*/
|
||||||
@@ -113,6 +114,9 @@ class ChatDocumentHandler : public QObject
|
|||||||
public:
|
public:
|
||||||
explicit ChatDocumentHandler(QObject *parent = nullptr);
|
explicit ChatDocumentHandler(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
ChatBarType::Type type() const;
|
||||||
|
void setType(ChatBarType::Type type);
|
||||||
|
|
||||||
[[nodiscard]] QQuickTextDocument *document() const;
|
[[nodiscard]] QQuickTextDocument *document() const;
|
||||||
void setDocument(QQuickTextDocument *document);
|
void setDocument(QQuickTextDocument *document);
|
||||||
|
|
||||||
@@ -128,8 +132,7 @@ public:
|
|||||||
[[nodiscard]] NeoChatRoom *room() const;
|
[[nodiscard]] NeoChatRoom *room() const;
|
||||||
void setRoom(NeoChatRoom *room);
|
void setRoom(NeoChatRoom *room);
|
||||||
|
|
||||||
[[nodiscard]] ChatBarCache *chatBarCache() const;
|
ChatBarCache *chatBarCache() const;
|
||||||
void setChatBarCache(ChatBarCache *chatBarCache);
|
|
||||||
|
|
||||||
Q_INVOKABLE void complete(int index);
|
Q_INVOKABLE void complete(int index);
|
||||||
|
|
||||||
@@ -147,10 +150,10 @@ public:
|
|||||||
Q_INVOKABLE void updateMentions(QQuickTextDocument *document, const QString &editId);
|
Q_INVOKABLE void updateMentions(QQuickTextDocument *document, const QString &editId);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
void typeChanged();
|
||||||
void documentChanged();
|
void documentChanged();
|
||||||
void cursorPositionChanged();
|
void cursorPositionChanged();
|
||||||
void roomChanged();
|
void roomChanged();
|
||||||
void chatBarCacheChanged();
|
|
||||||
void selectionStartChanged();
|
void selectionStartChanged();
|
||||||
void selectionEndChanged();
|
void selectionEndChanged();
|
||||||
void errorColorChanged();
|
void errorColorChanged();
|
||||||
@@ -159,10 +162,10 @@ Q_SIGNALS:
|
|||||||
private:
|
private:
|
||||||
int completionStartIndex() const;
|
int completionStartIndex() const;
|
||||||
|
|
||||||
|
ChatBarType::Type m_type = ChatBarType::None;
|
||||||
QPointer<QQuickTextDocument> m_document;
|
QPointer<QQuickTextDocument> m_document;
|
||||||
|
|
||||||
QPointer<NeoChatRoom> m_room;
|
QPointer<NeoChatRoom> m_room;
|
||||||
QPointer<ChatBarCache> m_chatBarCache;
|
|
||||||
|
|
||||||
QColor m_mentionColor;
|
QColor m_mentionColor;
|
||||||
QColor m_errorColor;
|
QColor m_errorColor;
|
||||||
|
|||||||
31
src/libneochat/enums/chatbartype.h
Normal file
31
src/libneochat/enums/chatbartype.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class ChatBarType
|
||||||
|
*
|
||||||
|
* This class is designed to define the ChatBarType enumeration.
|
||||||
|
*/
|
||||||
|
class ChatBarType : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
QML_UNCREATABLE("")
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief The type of chatbar.
|
||||||
|
*/
|
||||||
|
enum Type {
|
||||||
|
Room = 0, /**< A standard room chatbar for creating new messages. */
|
||||||
|
Edit, /**< A chatbar for editing an existing message. */
|
||||||
|
Thread, /**< A chatbar for creating a new threaded message. */
|
||||||
|
None, /**< Undefined. */
|
||||||
|
};
|
||||||
|
Q_ENUM(Type);
|
||||||
|
};
|
||||||
@@ -70,13 +70,23 @@ public:
|
|||||||
*
|
*
|
||||||
* @param event the event to return a type for.
|
* @param event the event to return a type for.
|
||||||
*
|
*
|
||||||
|
* @param isInReply whether this event is to be treated like a replied-to event (i.e., a basic text fallback should be shown if no other type is used)
|
||||||
|
*
|
||||||
* @sa Type
|
* @sa Type
|
||||||
*/
|
*/
|
||||||
static Type typeForEvent(const Quotient::RoomEvent &event)
|
static Type typeForEvent(const Quotient::RoomEvent &event, bool isInReply = false)
|
||||||
{
|
{
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
|
if (event.isRedacted()) {
|
||||||
|
return MessageComponentType::Text;
|
||||||
|
}
|
||||||
|
|
||||||
if (const auto e = eventCast<const RoomMessageEvent>(&event)) {
|
if (const auto e = eventCast<const RoomMessageEvent>(&event)) {
|
||||||
|
if (e->rawMsgtype() == u"m.key.verification.request"_s) {
|
||||||
|
return MessageComponentType::Verification;
|
||||||
|
}
|
||||||
|
|
||||||
switch (e->msgtype()) {
|
switch (e->msgtype()) {
|
||||||
case MessageEventType::Emote:
|
case MessageEventType::Emote:
|
||||||
return MessageComponentType::Text;
|
return MessageComponentType::Text;
|
||||||
@@ -103,7 +113,8 @@ public:
|
|||||||
if (event.matrixType() == u"org.matrix.msc3672.beacon_info"_s) {
|
if (event.matrixType() == u"org.matrix.msc3672.beacon_info"_s) {
|
||||||
return MessageComponentType::LiveLocation;
|
return MessageComponentType::LiveLocation;
|
||||||
}
|
}
|
||||||
return MessageComponentType::Other;
|
// In the (unlikely) case that this is a reply to a state event, we do want to show something
|
||||||
|
return isInReply ? MessageComponentType::Text : MessageComponentType::Other;
|
||||||
}
|
}
|
||||||
if (is<const EncryptedEvent>(event)) {
|
if (is<const EncryptedEvent>(event)) {
|
||||||
return MessageComponentType::Encrypted;
|
return MessageComponentType::Encrypted;
|
||||||
@@ -116,7 +127,8 @@ public:
|
|||||||
return MessageComponentType::Poll;
|
return MessageComponentType::Poll;
|
||||||
}
|
}
|
||||||
|
|
||||||
return MessageComponentType::Other;
|
// In the (unlikely) case that this is a reply to an unusual event, we do want to show something
|
||||||
|
return isInReply ? MessageComponentType::Text : MessageComponentType::Other;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -198,6 +198,10 @@ bool EventHandler::isHidden(const NeoChatRoom *room, const Quotient::RoomEvent *
|
|||||||
|
|
||||||
Qt::TextFormat EventHandler::messageBodyInputFormat(const Quotient::RoomMessageEvent &event)
|
Qt::TextFormat EventHandler::messageBodyInputFormat(const Quotient::RoomMessageEvent &event)
|
||||||
{
|
{
|
||||||
|
if (event.isRedacted() && !event.isStateEvent()) {
|
||||||
|
return Qt::RichText;
|
||||||
|
}
|
||||||
|
|
||||||
if (event.mimeType().name() == "text/plain"_L1) {
|
if (event.mimeType().name() == "text/plain"_L1) {
|
||||||
return Qt::PlainText;
|
return Qt::PlainText;
|
||||||
} else {
|
} else {
|
||||||
@@ -207,6 +211,11 @@ Qt::TextFormat EventHandler::messageBodyInputFormat(const Quotient::RoomMessageE
|
|||||||
|
|
||||||
QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
|
QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
|
||||||
{
|
{
|
||||||
|
if (event.isRedacted() && !event.isStateEvent()) {
|
||||||
|
auto reason = event.redactedBecause()->reason();
|
||||||
|
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>") : i18n("<i>[This message was deleted: %1]</i>", reason.toHtmlEscaped());
|
||||||
|
}
|
||||||
|
|
||||||
QString body;
|
QString body;
|
||||||
|
|
||||||
if (event.has<EventContent::FileContent>()) {
|
if (event.has<EventContent::FileContent>()) {
|
||||||
@@ -448,6 +457,12 @@ QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent
|
|||||||
[](const PollStartEvent &e) {
|
[](const PollStartEvent &e) {
|
||||||
return e.question();
|
return e.question();
|
||||||
},
|
},
|
||||||
|
[](const EncryptedEvent &) {
|
||||||
|
return i18nc("@info In room list", "Encrypted event");
|
||||||
|
},
|
||||||
|
[](const ReactionEvent &e) {
|
||||||
|
return i18nc("[user] reacted with <emoji>", "reacted with %1", e.key());
|
||||||
|
},
|
||||||
i18n("Unknown event"));
|
i18n("Unknown event"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,12 @@
|
|||||||
|
|
||||||
struct MessageComponent {
|
struct MessageComponent {
|
||||||
MessageComponentType::Type type = MessageComponentType::Other;
|
MessageComponentType::Type type = MessageComponentType::Other;
|
||||||
QString content;
|
QString display;
|
||||||
QVariantMap attributes;
|
QVariantMap attributes;
|
||||||
|
|
||||||
bool operator==(const MessageComponent &right) const
|
bool operator==(const MessageComponent &right) const
|
||||||
{
|
{
|
||||||
return type == right.type && content == right.content && attributes == right.attributes;
|
return type == right.type && display == right.display && attributes == right.attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isEmpty() const
|
bool isEmpty() const
|
||||||
|
|||||||
@@ -100,6 +100,10 @@ QVariant UserListModel::data(const QModelIndex &index, int role) const
|
|||||||
return plEvent->powerLevelForUser(memberId);
|
return plEvent->powerLevelForUser(memberId);
|
||||||
}
|
}
|
||||||
if (role == PowerLevelStringRole) {
|
if (role == PowerLevelStringRole) {
|
||||||
|
if (m_currentRoom->roomCreatorHasUltimatePowerLevel() && m_currentRoom->isCreator(memberId)) {
|
||||||
|
return i18nc("@info the person that created this room", "Creator");
|
||||||
|
}
|
||||||
|
|
||||||
auto pl = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
|
auto pl = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
|
||||||
// User might not in the room yet, in this case pl can be nullptr.
|
// User might not in the room yet, in this case pl can be nullptr.
|
||||||
// e.g. When invited but user not accepted or denied the invitation.
|
// e.g. When invited but user not accepted or denied the invitation.
|
||||||
|
|||||||
@@ -558,4 +558,9 @@ bool NeoChatConnection::enablePushNotifications() const
|
|||||||
return m_pushNotificationsEnabled;
|
return m_pushNotificationsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NeoChatConnection::isVerifiedSession() const
|
||||||
|
{
|
||||||
|
return isVerifiedDevice(userId(), deviceId());
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_neochatconnection.cpp"
|
#include "moc_neochatconnection.cpp"
|
||||||
|
|||||||
@@ -206,6 +206,11 @@ public:
|
|||||||
|
|
||||||
LinkPreviewer *previewerForLink(const QUrl &link);
|
LinkPreviewer *previewerForLink(const QUrl &link);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True if this connection is a verified session.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE bool isVerifiedSession() const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void globalUrlPreviewEnabledChanged();
|
void globalUrlPreviewEnabledChanged();
|
||||||
void labelChanged();
|
void labelChanged();
|
||||||
|
|||||||
@@ -2,12 +2,15 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
|
#include "chatbartype.h"
|
||||||
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QMediaMetaData>
|
#include <QMediaMetaData>
|
||||||
#include <QMediaPlayer>
|
#include <QMediaPlayer>
|
||||||
#include <QMimeDatabase>
|
#include <QMimeDatabase>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
|
#include <QVideoFrame>
|
||||||
|
#include <QVideoSink>
|
||||||
|
|
||||||
#include <Quotient/events/eventcontent.h>
|
#include <Quotient/events/eventcontent.h>
|
||||||
#include <Quotient/events/eventrelation.h>
|
#include <Quotient/events/eventrelation.h>
|
||||||
@@ -245,11 +248,37 @@ QCoro::Task<void> NeoChatRoom::doUploadFile(QUrl url, QString body, std::optiona
|
|||||||
} else if (mime.name().startsWith("audio/"_L1)) {
|
} else if (mime.name().startsWith("audio/"_L1)) {
|
||||||
content = new EventContent::AudioContent(url, fileInfo.size(), mime, fileInfo.fileName());
|
content = new EventContent::AudioContent(url, fileInfo.size(), mime, fileInfo.fileName());
|
||||||
} else if (mime.name().startsWith("video/"_L1)) {
|
} else if (mime.name().startsWith("video/"_L1)) {
|
||||||
|
QVideoSink sink;
|
||||||
|
|
||||||
QMediaPlayer player;
|
QMediaPlayer player;
|
||||||
player.setSource(url);
|
player.setSource(url);
|
||||||
|
player.setVideoSink(&sink);
|
||||||
co_await qCoro(&player, &QMediaPlayer::mediaStatusChanged);
|
co_await qCoro(&player, &QMediaPlayer::mediaStatusChanged);
|
||||||
auto resolution = player.metaData().value(QMediaMetaData::Resolution).toSize();
|
|
||||||
|
// Get the first video frame to use as a thumbnail.
|
||||||
|
player.play();
|
||||||
|
co_await qCoro(&player, &QMediaPlayer::positionChanged);
|
||||||
|
|
||||||
|
QTemporaryFile file;
|
||||||
|
file.setFileTemplate(QStringLiteral("XXXXXX.jpg"));
|
||||||
|
file.open();
|
||||||
|
|
||||||
|
const auto thumbnailImage = sink.videoFrame().toImage();
|
||||||
|
Q_UNUSED(thumbnailImage.save(file.fileName()))
|
||||||
|
player.stop(); // We have to delay the stop() because it will invalidate our image
|
||||||
|
|
||||||
|
const auto thumbnailFileInfo = QFileInfo(file.fileName());
|
||||||
|
|
||||||
|
// Upload the thumbnail
|
||||||
|
const auto job = connection()->uploadFile(thumbnailFileInfo.absoluteFilePath());
|
||||||
|
co_await qCoro(job.get(), &BaseJob::finished);
|
||||||
|
|
||||||
|
const auto resolution = player.metaData().value(QMediaMetaData::Resolution).toSize();
|
||||||
content = new EventContent::VideoContent(url, fileInfo.size(), mime, resolution, fileInfo.fileName());
|
content = new EventContent::VideoContent(url, fileInfo.size(), mime, resolution, fileInfo.fileName());
|
||||||
|
content->thumbnail = EventContent::Thumbnail(job->contentUri(),
|
||||||
|
thumbnailFileInfo.size(),
|
||||||
|
QMimeDatabase().mimeTypeForName(QStringLiteral("image/jpeg")),
|
||||||
|
thumbnailImage.size());
|
||||||
} else {
|
} else {
|
||||||
content = new EventContent::FileContent(url, fileInfo.size(), mime, fileInfo.fileName());
|
content = new EventContent::FileContent(url, fileInfo.size(), mime, fileInfo.fileName());
|
||||||
}
|
}
|
||||||
@@ -359,9 +388,14 @@ const RoomEvent *NeoChatRoom::lastEvent(std::function<bool(const RoomEvent *)> f
|
|||||||
if (auto lastEvent = eventCast<const RoomMessageEvent>(event)) {
|
if (auto lastEvent = eventCast<const RoomMessageEvent>(event)) {
|
||||||
return lastEvent;
|
return lastEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto lastEvent = eventCast<const PollStartEvent>(event)) {
|
if (auto lastEvent = eventCast<const PollStartEvent>(event)) {
|
||||||
return lastEvent;
|
return lastEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto lastEvent = eventCast<const EncryptedEvent>(event)) {
|
||||||
|
return lastEvent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_cachedEvent != nullptr) {
|
if (m_cachedEvent != nullptr) {
|
||||||
@@ -441,20 +475,19 @@ void NeoChatRoom::onRedaction(const RoomEvent &prevEvent, const RoomEvent & /*af
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QDateTime NeoChatRoom::lastActiveTime()
|
QDateTime NeoChatRoom::lastActiveTime() const
|
||||||
{
|
{
|
||||||
if (timelineSize() == 0) {
|
// Find the last relevant event:
|
||||||
if (m_cachedEvent != nullptr) {
|
if (const auto event = lastEvent(m_hiddenFilter)) {
|
||||||
return m_cachedEvent->originTimestamp();
|
|
||||||
}
|
|
||||||
return QDateTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto event = lastEvent()) {
|
|
||||||
return event->originTimestamp();
|
return event->originTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
// no message found, take last event
|
// If nothing is loaded yet, and there is no cached event:
|
||||||
|
if (timelineSize() == 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// No message found, take last event:
|
||||||
return messageEvents().rbegin()->get()->originTimestamp();
|
return messageEvents().rbegin()->get()->originTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,6 +565,9 @@ bool NeoChatRoom::containsUser(const QString &userID) const
|
|||||||
|
|
||||||
bool NeoChatRoom::canSendEvent(const QString &eventType) const
|
bool NeoChatRoom::canSendEvent(const QString &eventType) const
|
||||||
{
|
{
|
||||||
|
if (roomCreatorHasUltimatePowerLevel() && isCreator(localMember().id())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
auto plEvent = currentState().get<RoomPowerLevelsEvent>();
|
auto plEvent = currentState().get<RoomPowerLevelsEvent>();
|
||||||
if (!plEvent) {
|
if (!plEvent) {
|
||||||
return false;
|
return false;
|
||||||
@@ -544,6 +580,9 @@ bool NeoChatRoom::canSendEvent(const QString &eventType) const
|
|||||||
|
|
||||||
bool NeoChatRoom::canSendState(const QString &eventType) const
|
bool NeoChatRoom::canSendState(const QString &eventType) const
|
||||||
{
|
{
|
||||||
|
if (roomCreatorHasUltimatePowerLevel() && isCreator(localMember().id())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
auto plEvent = currentState().get<RoomPowerLevelsEvent>();
|
auto plEvent = currentState().get<RoomPowerLevelsEvent>();
|
||||||
if (!plEvent) {
|
if (!plEvent) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1281,6 +1320,19 @@ void NeoChatRoom::copyEventMedia(const QString &eventId)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileTransferInfo NeoChatRoom::cachedFileTransferInfo(const QString &eventId) const
|
||||||
|
{
|
||||||
|
if (eventId.isEmpty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto eventResult = getEvent(eventId);
|
||||||
|
if (!eventResult.first) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return cachedFileTransferInfo(eventResult.first);
|
||||||
|
}
|
||||||
|
|
||||||
FileTransferInfo NeoChatRoom::cachedFileTransferInfo(const Quotient::RoomEvent *event) const
|
FileTransferInfo NeoChatRoom::cachedFileTransferInfo(const Quotient::RoomEvent *event) const
|
||||||
{
|
{
|
||||||
QString mxcUrl;
|
QString mxcUrl;
|
||||||
@@ -1339,6 +1391,20 @@ ChatBarCache *NeoChatRoom::threadCache() const
|
|||||||
return m_threadCache;
|
return m_threadCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChatBarCache *NeoChatRoom::cacheForType(ChatBarType::Type type) const
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case ChatBarType::Room:
|
||||||
|
return m_mainCache;
|
||||||
|
case ChatBarType::Edit:
|
||||||
|
return m_editCache;
|
||||||
|
case ChatBarType::Thread:
|
||||||
|
return m_threadCache;
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NeoChatRoom::replyLastMessage()
|
void NeoChatRoom::replyLastMessage()
|
||||||
{
|
{
|
||||||
const auto &timelineBottom = messageEvents().rbegin();
|
const auto &timelineBottom = messageEvents().rbegin();
|
||||||
@@ -1670,8 +1736,14 @@ void NeoChatRoom::setRoomState(const QString &type, const QString &stateKey, con
|
|||||||
|
|
||||||
NeochatRoomMember *NeoChatRoom::qmlSafeMember(const QString &memberId)
|
NeochatRoomMember *NeoChatRoom::qmlSafeMember(const QString &memberId)
|
||||||
{
|
{
|
||||||
|
if (memberId.isEmpty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_memberObjects.contains(memberId)) {
|
if (!m_memberObjects.contains(memberId)) {
|
||||||
return m_memberObjects.emplace(memberId, std::make_unique<NeochatRoomMember>(this, memberId)).first->second.get();
|
auto member = m_memberObjects.emplace(memberId, std::make_unique<NeochatRoomMember>(this, memberId)).first->second.get();
|
||||||
|
QQmlEngine::setObjectOwnership(member, QQmlEngine::CppOwnership);
|
||||||
|
return member;
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_memberObjects[memberId].get();
|
return m_memberObjects[memberId].get();
|
||||||
@@ -1731,4 +1803,20 @@ void NeoChatRoom::setHiddenFilter(std::function<bool(const Quotient::RoomEvent *
|
|||||||
NeoChatRoom::m_hiddenFilter = hiddenFilter;
|
NeoChatRoom::m_hiddenFilter = hiddenFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NeoChatRoom::roomCreatorHasUltimatePowerLevel() const
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
auto version = this->version().toInt(&ok);
|
||||||
|
// This is terrible. For non-numeric room versions, I don't think there's a way of knowing whether they're pre- or post hydra.
|
||||||
|
// We just assume they are. Shouldn't matter for normal users anyway.
|
||||||
|
return !ok || version > 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NeoChatRoom::isCreator(const QString &userId) const
|
||||||
|
{
|
||||||
|
auto createEvent = currentState().get<RoomCreateEvent>();
|
||||||
|
return roomCreatorHasUltimatePowerLevel() && createEvent
|
||||||
|
&& (createEvent->senderId() == userId || createEvent->contentPart<QStringList>(u"additional_creators"_s).contains(userId));
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_neochatroom.cpp"
|
#include "moc_neochatroom.cpp"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <QCoroTask>
|
#include <QCoroTask>
|
||||||
|
|
||||||
|
#include "enums/chatbartype.h"
|
||||||
#include "enums/messagetype.h"
|
#include "enums/messagetype.h"
|
||||||
#include "enums/pushrule.h"
|
#include "enums/pushrule.h"
|
||||||
#include "events/pollevent.h"
|
#include "events/pollevent.h"
|
||||||
@@ -208,7 +209,7 @@ public:
|
|||||||
bool visible() const;
|
bool visible() const;
|
||||||
void setVisible(bool visible);
|
void setVisible(bool visible);
|
||||||
|
|
||||||
[[nodiscard]] QDateTime lastActiveTime();
|
[[nodiscard]] QDateTime lastActiveTime() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the last interesting event.
|
* @brief Get the last interesting event.
|
||||||
@@ -484,6 +485,8 @@ public:
|
|||||||
|
|
||||||
ChatBarCache *threadCache() const;
|
ChatBarCache *threadCache() const;
|
||||||
|
|
||||||
|
ChatBarCache *cacheForType(ChatBarType::Type type) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reply to the last message sent in the timeline.
|
* @brief Reply to the last message sent in the timeline.
|
||||||
*
|
*
|
||||||
@@ -544,7 +547,15 @@ public:
|
|||||||
* @brief Return the cached file transfer information for the event.
|
* @brief Return the cached file transfer information for the event.
|
||||||
*
|
*
|
||||||
* If we downloaded the file previously, return a struct with Completed status
|
* If we downloaded the file previously, return a struct with Completed status
|
||||||
* and the local file path stored in KSharedCOnfig
|
* and the local file path stored in KSharedConfig
|
||||||
|
*/
|
||||||
|
Quotient::FileTransferInfo cachedFileTransferInfo(const QString &eventId) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the cached file transfer information for the event.
|
||||||
|
*
|
||||||
|
* If we downloaded the file previously, return a struct with Completed status
|
||||||
|
* and the local file path stored in KSharedConfig
|
||||||
*/
|
*/
|
||||||
Quotient::FileTransferInfo cachedFileTransferInfo(const Quotient::RoomEvent *event) const;
|
Quotient::FileTransferInfo cachedFileTransferInfo(const Quotient::RoomEvent *event) const;
|
||||||
|
|
||||||
@@ -589,6 +600,18 @@ public:
|
|||||||
|
|
||||||
static void setHiddenFilter(std::function<bool(const Quotient::RoomEvent *)> hiddenFilter);
|
static void setHiddenFilter(std::function<bool(const Quotient::RoomEvent *)> hiddenFilter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether this room has a room version where the creator is treated as having an ultimate power level
|
||||||
|
*
|
||||||
|
* For unusual room versions, this information might be wrong.
|
||||||
|
*/
|
||||||
|
bool roomCreatorHasUltimatePowerLevel() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether this user is considered a creator of this room. Only applies to post-v12 rooms.
|
||||||
|
*/
|
||||||
|
bool isCreator(const QString &userId) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_visible = false;
|
bool m_visible = false;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user