Compare commits
90 Commits
work/fix_c
...
work/tobia
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
036a60a095 | ||
|
|
b3315e1ed4 | ||
|
|
d300e9cf52 | ||
|
|
1f4bcd150f | ||
|
|
9ad8894983 | ||
|
|
9e63ca5eb7 | ||
|
|
ade66242bb | ||
|
|
2c6932b4cb | ||
|
|
5cce9e7205 | ||
|
|
7dd3ad9548 | ||
|
|
f690b76efa | ||
|
|
87b8d6710e | ||
|
|
965b890346 | ||
|
|
02b4e5cc70 | ||
|
|
410add04fb | ||
|
|
b575b1e700 | ||
|
|
5828ee1ada | ||
|
|
a64c80109e | ||
|
|
de47f7f2fa | ||
|
|
19adc7b9e5 | ||
|
|
23f60a59fe | ||
|
|
946ba2e56d | ||
|
|
9dcb7b49fa | ||
|
|
52f5901642 | ||
|
|
d4b4a7e1ff | ||
|
|
4449678b74 | ||
|
|
19e197e0ec | ||
|
|
cfc5202645 | ||
|
|
9b80d9e7aa | ||
|
|
dc409387bd | ||
|
|
1e73a7bda4 | ||
|
|
ef1d62d45c | ||
|
|
c2d82750b1 | ||
|
|
c97d276b36 | ||
|
|
681a0b1e93 | ||
|
|
39556f45ab | ||
|
|
3c7774800a | ||
|
|
bc7530eaa1 | ||
|
|
82d11f79d6 | ||
|
|
25d0368d41 | ||
|
|
83b7e7d121 | ||
|
|
26fd26f9fd | ||
|
|
0029567c3a | ||
|
|
c7614caf41 | ||
|
|
6571dbe554 | ||
|
|
b3a29068cc | ||
|
|
5adda55a85 | ||
|
|
d56f0d6086 | ||
|
|
60772be391 | ||
|
|
27f1679741 | ||
|
|
838596c3ae | ||
|
|
a57744891a | ||
|
|
f5417a6227 | ||
|
|
ac6f9ea219 | ||
|
|
4b49559d39 | ||
|
|
baa33f1843 | ||
|
|
dae5718c6c | ||
|
|
60260cff3b | ||
|
|
2df9a26cdc | ||
|
|
b16cd12b33 | ||
|
|
6a3b22ef2d | ||
|
|
b28a85ff05 | ||
|
|
e480299563 | ||
|
|
fe70e2773f | ||
|
|
e78ea4721a | ||
|
|
1699dcf0c4 | ||
|
|
9d6aef6c2b | ||
|
|
a9c2428498 | ||
|
|
0730f15e2b | ||
|
|
136856f3c3 | ||
|
|
763b6af076 | ||
|
|
ac231320a3 | ||
|
|
87aee162f1 | ||
|
|
0899db31af | ||
|
|
b4198bc13b | ||
|
|
c6bfe73d26 | ||
|
|
d490dffa36 | ||
|
|
43b2b71b73 | ||
|
|
39a51d1f35 | ||
|
|
2eb26ffbb3 | ||
|
|
87ef55215f | ||
|
|
f6186aad2e | ||
|
|
2251edbf86 | ||
|
|
1c55649740 | ||
|
|
aa0b6613de | ||
|
|
f948e813b6 | ||
|
|
b5c6411aad | ||
|
|
b1daa76d9f | ||
|
|
7180fa022b | ||
|
|
17bc08270d |
@@ -19,6 +19,7 @@
|
||||
"--talk-name=org.freedesktop.Notifications",
|
||||
"--talk-name=org.kde.kwalletd5",
|
||||
"--talk-name=org.kde.StatusNotifierWatcher",
|
||||
"--talk-name=org.freedesktop.secrets",
|
||||
"--own-name=org.kde.StatusNotifierItem-2-2"
|
||||
],
|
||||
"modules": [
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
include:
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/reuse-lint.yml
|
||||
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android-qt6.yml
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android-qt6.yml
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml
|
||||
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows-qt6.yml
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd-qt6.yml
|
||||
|
||||
@@ -13,6 +13,7 @@ Dependencies:
|
||||
'frameworks/kitemmodels': '@latest-kf6'
|
||||
'frameworks/kquickcharts': '@latest-kf6'
|
||||
'frameworks/knotifications': '@latest-kf6'
|
||||
'frameworks/kcolorscheme': '@latest-kf6'
|
||||
'libraries/kquickimageeditor': '@latest-kf6'
|
||||
'frameworks/sonnet': '@latest-kf6'
|
||||
'libraries/kirigami-addons': '@latest-kf6'
|
||||
@@ -25,7 +26,6 @@ Dependencies:
|
||||
'frameworks/qqc2-desktop-style': '@latest-kf6'
|
||||
'frameworks/kio': '@latest-kf6'
|
||||
'frameworks/kwindowsystem': '@latest-kf6'
|
||||
'frameworks/kconfigwidgets': '@latest-kf6'
|
||||
- 'on': ['Linux', 'FreeBSD']
|
||||
'require':
|
||||
'frameworks/kdbusaddons': '@latest-kf6'
|
||||
|
||||
@@ -41,3 +41,7 @@ License: CC0-1.0
|
||||
Files: .flatpak-manifest.json
|
||||
Copyright: 2020-2022 Tobias Fella <tobias.fella@kde.org>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: autotests/data/*
|
||||
Copyright: none
|
||||
License: CC0-1.0
|
||||
|
||||
@@ -57,7 +57,7 @@ set_package_properties(Qt6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
)
|
||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami2 I18n Notifications Config CoreAddons Sonnet ItemModels)
|
||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami2 I18n Notifications Config CoreAddons Sonnet ItemModels ColorScheme)
|
||||
set_package_properties(KF6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
@@ -76,7 +76,7 @@ if(ANDROID)
|
||||
)
|
||||
else()
|
||||
find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Widgets)
|
||||
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle ConfigWidgets KIO WindowSystem StatusNotifierItem)
|
||||
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem)
|
||||
set_package_properties(KF6QQC2DesktopStyle PROPERTIES
|
||||
TYPE RUNTIME
|
||||
)
|
||||
@@ -101,7 +101,8 @@ set_package_properties(QuotientQt6 PROPERTIES
|
||||
PURPOSE "Talk with matrix server"
|
||||
)
|
||||
|
||||
if (NOT TARGET Olm::Olm)
|
||||
# The android part is just for CI. We do NOT support any builds without E2EE
|
||||
if (NOT TARGET Olm::Olm AND NOT ANDROID)
|
||||
message(FATAL_ERROR "NeoChat requires Quotient with the E2EE feature enabled")
|
||||
endif()
|
||||
|
||||
@@ -117,7 +118,7 @@ set_package_properties(cmark PROPERTIES
|
||||
ecm_find_qmlmodule(org.kde.kquickimageeditor 1.0)
|
||||
ecm_find_qmlmodule(org.kde.kitemmodels 1.0)
|
||||
ecm_find_qmlmodule(org.kde.quickcharts 1.0)
|
||||
ecm_find_qmlmodule(QtLocation ${QTLOCATION_MODULE_QML_VERSION})
|
||||
ecm_find_qmlmodule(QtLocation)
|
||||
|
||||
find_package(KQuickImageEditor COMPONENTS)
|
||||
set_package_properties(KQuickImageEditor PROPERTIES
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_definitions(-DDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" )
|
||||
|
||||
ecm_add_test(
|
||||
neochatroomtest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
@@ -32,3 +34,9 @@ ecm_add_test(
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME eventhandlertest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
chatbarcachetest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME chatbarcachetest
|
||||
)
|
||||
|
||||
157
autotests/chatbarcachetest.cpp
Normal file
157
autotests/chatbarcachetest.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
|
||||
#include <Quotient/syncdata.h>
|
||||
#include <qtestcase.h>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class TestRoom : public NeoChatRoom
|
||||
{
|
||||
public:
|
||||
using NeoChatRoom::NeoChatRoom;
|
||||
|
||||
void update(SyncRoomData &&data, bool fromCache = false)
|
||||
{
|
||||
Room::updateData(std::move(data), fromCache);
|
||||
}
|
||||
};
|
||||
|
||||
class ChatBarCacheTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
TestRoom *room = nullptr;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void empty();
|
||||
void noRoom();
|
||||
void badParent();
|
||||
void reply();
|
||||
void edit();
|
||||
void attachment();
|
||||
};
|
||||
|
||||
void ChatBarCacheTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||
|
||||
QFile testMinSyncFile;
|
||||
testMinSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-min-sync.json"));
|
||||
testMinSyncFile.open(QIODevice::ReadOnly);
|
||||
const auto testMinSyncJson = QJsonDocument::fromJson(testMinSyncFile.readAll());
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testMinSyncJson.object());
|
||||
room->update(std::move(roomData));
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::empty()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||
|
||||
QCOMPARE(chatBarCache->text(), QString());
|
||||
QCOMPARE(chatBarCache->isReplying(), false);
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(nullptr));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::noRoom()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache());
|
||||
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
|
||||
|
||||
// These should return empty even though a reply ID has been set because the
|
||||
// ChatBarCache has no parent.
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationUser(), QVariantMap());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::badParent()
|
||||
{
|
||||
QScopedPointer<QObject> badParent(new QObject());
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(badParent.get()));
|
||||
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
|
||||
|
||||
// These should return empty even though a reply ID has been set because the
|
||||
// ChatBarCache has no parent.
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationUser(), QVariantMap());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::reply()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||
chatBarCache->setText(QLatin1String("some text"));
|
||||
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
|
||||
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
|
||||
|
||||
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
|
||||
QCOMPARE(chatBarCache->isReplying(), true);
|
||||
QCOMPARE(chatBarCache->replyId(), QLatin1String("$153456789:example.org"));
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(room->user(QLatin1String("@example:example.org"))));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QLatin1String("This is an example\ntext message"));
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::edit()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||
chatBarCache->setText(QLatin1String("some text"));
|
||||
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
|
||||
chatBarCache->setEditId(QLatin1String("$153456789:example.org"));
|
||||
|
||||
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
|
||||
QCOMPARE(chatBarCache->isReplying(), false);
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), true);
|
||||
QCOMPARE(chatBarCache->editId(), QLatin1String("$153456789:example.org"));
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(room->user(QLatin1String("@example:example.org"))));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QLatin1String("This is an example\ntext message"));
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::attachment()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||
chatBarCache->setText(QLatin1String("some text"));
|
||||
chatBarCache->setEditId(QLatin1String("$153456789:example.org"));
|
||||
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
|
||||
|
||||
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
|
||||
QCOMPARE(chatBarCache->isReplying(), false);
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(nullptr));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QLatin1String("some/path"));
|
||||
}
|
||||
|
||||
QTEST_MAIN(ChatBarCacheTest)
|
||||
#include "chatbarcachetest.moc"
|
||||
381
autotests/data/test-eventhandler-sync.json
Normal file
381
autotests/data/test-eventhandler-sync.json
Normal file
@@ -0,0 +1,381 @@
|
||||
{
|
||||
"account_data": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"tags": {
|
||||
"u.work": {
|
||||
"order": 0.9
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.tag"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"custom_config_key": "custom_config_value"
|
||||
},
|
||||
"type": "org.example.custom.room.config"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ephemeral": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"user_ids": [
|
||||
"@alice:matrix.org",
|
||||
"@bob:example.com"
|
||||
]
|
||||
},
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"type": "m.typing"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$153456789:example.org": {
|
||||
"m.read": {
|
||||
"@alice:matrix.org": {
|
||||
"ts": 1436451550453
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@bob:example.com": {
|
||||
"ts": 1436451550453
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@tim:example.com": {
|
||||
"ts": 1436451550454
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@jeff:example.com": {
|
||||
"ts": 1436451550455
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@tina:example.com": {
|
||||
"ts": 1436451550456
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@sally:example.com": {
|
||||
"ts": 1436451550457
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@fred:example.com": {
|
||||
"ts": 1436451550458
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Alice Margatroid",
|
||||
"membership": "join",
|
||||
"reason": "Looking for support"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"m.heroes": [
|
||||
"@alice:example.com",
|
||||
"@bob:example.com"
|
||||
],
|
||||
"m.invited_member_count": 0,
|
||||
"m.joined_member_count": 2
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an example\ntext message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example<br>text message</b>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$153456789:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://kde.org/123456",
|
||||
"displayname": "after",
|
||||
"membership": "join"
|
||||
},
|
||||
"origin_server_ts": 1690651134736,
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@example:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"replaces_state": "$1234567890:example.org",
|
||||
"prev_content": {
|
||||
"avatar_url": "mxc://kde.org/12345",
|
||||
"displayname": "before",
|
||||
"membership": "join"
|
||||
},
|
||||
"prev_sender": "@example:example.orgg",
|
||||
"age": 1234
|
||||
},
|
||||
"event_id": "$143273583553PhrSn:example.org",
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "This is a highlight @bob:kde.org and this is a link https://kde.org",
|
||||
"format": "org.matrix.custom.html",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$1532735824654:example.org",
|
||||
"origin_server_ts": 1532735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1233
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"event_id": "$153456789:example.org",
|
||||
"key": "👍",
|
||||
"rel_type": "m.annotation"
|
||||
}
|
||||
},
|
||||
"origin_server_ts": 1690322545182,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@alice:matrix.org",
|
||||
"type": "m.reaction",
|
||||
"unsigned": {
|
||||
"age": 390159120
|
||||
},
|
||||
"event_id": "$163456789:example.org",
|
||||
"age": 390159120
|
||||
},
|
||||
{
|
||||
"age": 4926305285,
|
||||
"content": {
|
||||
"body": "video caption",
|
||||
"filename": "video.mp4",
|
||||
"info": {
|
||||
"duration": 10,
|
||||
"h": 1080,
|
||||
"mimetype": "video/mp4",
|
||||
"size": 62650636,
|
||||
"w": 1920,
|
||||
"thumbnail_info": {
|
||||
"h": 450,
|
||||
"mimetype": "image/jpeg",
|
||||
"size": 382249,
|
||||
"w": 800
|
||||
},
|
||||
"thumbnail_url": "mxc://kde.org/2234567"
|
||||
},
|
||||
"msgtype": "m.video",
|
||||
"url": "mxc://kde.org/1234567"
|
||||
},
|
||||
"event_id": "$263456789:example.org",
|
||||
"origin_server_ts": 1685793783330,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 4926305285
|
||||
},
|
||||
"user_id": "@example:example.org"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "> <@example:example.org> This is an example\ntext message\n\nreply",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<mx-reply><blockquote><a href=\"https://matrix.to/#/!jEsUZKDJdhlrceRyVU:example.org/$153456789:example.org?via=kde.org&via=matrix.org\">In reply to</a> <a href=\"https://matrix.to/#/@example:example.org\">@example:example.org</a><br><b>This is an example<br>text message</b></blockquote></mx-reply>reply",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$153456789:example.org"
|
||||
}
|
||||
},
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"origin_server_ts": 1690725965572,
|
||||
"sender": "@alice:matrix.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 98
|
||||
},
|
||||
"event_id": "$154456789:example.org",
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "> <@example:example.org> video caption\n\nreply",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$263456789:example.org"
|
||||
}
|
||||
},
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"origin_server_ts": 1690725965573,
|
||||
"sender": "@alice:matrix.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 98
|
||||
},
|
||||
"event_id": "$154456799:example.org",
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||
},
|
||||
{
|
||||
"age": 96845207,
|
||||
"content": {
|
||||
"body": "Lat: 51.7035, Lon: -1.14394",
|
||||
"geo_uri": "geo:51.7035,-1.14394",
|
||||
"msgtype": "m.location",
|
||||
"org.matrix.msc1767.text": "Lat: 51.7035, Lon: -1.14394",
|
||||
"org.matrix.msc3488.asset": {
|
||||
"type": "m.pin"
|
||||
},
|
||||
"org.matrix.msc3488.location": {
|
||||
"uri": "geo:51.7035,-1.14394"
|
||||
}
|
||||
},
|
||||
"event_id": "$1544567999:example.org",
|
||||
"origin_server_ts": 1690821582876,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 96845207
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "Thread root",
|
||||
"format": "org.matrix.custom.html",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$threadroot:example.org",
|
||||
"origin_server_ts": 1690821582879,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "Thread message 1",
|
||||
"msgtype": "m.text",
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.thread",
|
||||
"event_id": "$threadroot:example.org",
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$threadroot:example.org"
|
||||
},
|
||||
"is_falling_back": true
|
||||
}
|
||||
},
|
||||
"event_id": "$threadmessage1:example.org",
|
||||
"origin_server_ts": 1690821582890,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1238
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "Thread message 2",
|
||||
"msgtype": "m.text",
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.thread",
|
||||
"event_id": "$threadroot:example.org",
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$threadmessage1:example.org"
|
||||
},
|
||||
"is_falling_back": true
|
||||
}
|
||||
},
|
||||
"event_id": "$threadmessage2:example.org",
|
||||
"origin_server_ts": 1690821582890,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1238
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
}
|
||||
87
autotests/data/test-min-sync.json
Normal file
87
autotests/data/test-min-sync.json
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"account_data": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"tags": {
|
||||
"u.work": {
|
||||
"order": 0.9
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.tag"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"custom_config_key": "custom_config_value"
|
||||
},
|
||||
"type": "org.example.custom.room.config"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ephemeral": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"user_ids": [
|
||||
"@alice:matrix.org",
|
||||
"@bob:example.com"
|
||||
]
|
||||
},
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"type": "m.typing"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Alice Margatroid",
|
||||
"membership": "join",
|
||||
"reason": "Looking for support"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"m.heroes": [
|
||||
"@alice:example.com",
|
||||
"@bob:example.com"
|
||||
],
|
||||
"m.invited_member_count": 0,
|
||||
"m.joined_member_count": 2
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an example\ntext message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example<br>text message</b>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$153456789:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,7 @@ private Q_SLOTS:
|
||||
void replyAuthor();
|
||||
void replyBody();
|
||||
void replyMediaInfo();
|
||||
void thread();
|
||||
void location();
|
||||
void readMarkers();
|
||||
};
|
||||
@@ -73,329 +74,11 @@ void EventHandlerTest::initTestCase()
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||
|
||||
const auto json = QJsonDocument::fromJson(R"EVENT({
|
||||
"account_data": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"tags": {
|
||||
"u.work": {
|
||||
"order": 0.9
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.tag"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"custom_config_key": "custom_config_value"
|
||||
},
|
||||
"type": "org.example.custom.room.config"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ephemeral": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"user_ids": [
|
||||
"@alice:matrix.org",
|
||||
"@bob:example.com"
|
||||
]
|
||||
},
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"type": "m.typing"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$153456789:example.org": {
|
||||
"m.read": {
|
||||
"@alice:matrix.org": {
|
||||
"ts": 1436451550453
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@bob:example.com": {
|
||||
"ts": 1436451550453
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@tim:example.com": {
|
||||
"ts": 1436451550454
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@jeff:example.com": {
|
||||
"ts": 1436451550455
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@tina:example.com": {
|
||||
"ts": 1436451550456
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@sally:example.com": {
|
||||
"ts": 1436451550457
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@fred:example.com": {
|
||||
"ts": 1436451550458
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Alice Margatroid",
|
||||
"membership": "join",
|
||||
"reason": "Looking for support"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"m.heroes": [
|
||||
"@alice:example.com",
|
||||
"@bob:example.com"
|
||||
],
|
||||
"m.invited_member_count": 0,
|
||||
"m.joined_member_count": 2
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an example\ntext message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example<br>text message</b>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$153456789:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://kde.org/123456",
|
||||
"displayname": "after",
|
||||
"membership": "join"
|
||||
},
|
||||
"origin_server_ts": 1690651134736,
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@example:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"replaces_state": "$1234567890:example.org",
|
||||
"prev_content": {
|
||||
"avatar_url": "mxc://kde.org/12345",
|
||||
"displayname": "before",
|
||||
"membership": "join"
|
||||
},
|
||||
"prev_sender": "@example:example.orgg",
|
||||
"age": 1234
|
||||
},
|
||||
"event_id": "$143273583553PhrSn:example.org",
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "This is a highlight @bob:kde.org and this is a link https://kde.org",
|
||||
"format": "org.matrix.custom.html",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$1532735824654:example.org",
|
||||
"origin_server_ts": 1532735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1233
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"event_id": "$153456789:example.org",
|
||||
"key": "👍",
|
||||
"rel_type": "m.annotation"
|
||||
}
|
||||
},
|
||||
"origin_server_ts": 1690322545182,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@alice:matrix.org",
|
||||
"type": "m.reaction",
|
||||
"unsigned": {
|
||||
"age": 390159120
|
||||
},
|
||||
"event_id": "$163456789:example.org",
|
||||
"age": 390159120
|
||||
},
|
||||
{
|
||||
"age": 4926305285,
|
||||
"content": {
|
||||
"body": "video caption",
|
||||
"filename": "video.mp4",
|
||||
"info": {
|
||||
"duration": 10,
|
||||
"h": 1080,
|
||||
"mimetype": "video/mp4",
|
||||
"size": 62650636,
|
||||
"w": 1920,
|
||||
"thumbnail_info": {
|
||||
"h": 450,
|
||||
"mimetype": "image/jpeg",
|
||||
"size": 382249,
|
||||
"w": 800
|
||||
},
|
||||
"thumbnail_url": "mxc://kde.org/2234567"
|
||||
},
|
||||
"msgtype": "m.video",
|
||||
"url": "mxc://kde.org/1234567"
|
||||
},
|
||||
"event_id": "$263456789:example.org",
|
||||
"origin_server_ts": 1685793783330,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 4926305285
|
||||
},
|
||||
"user_id": "@example:example.org"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "> <@example:example.org> This is an example\ntext message\n\nreply",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<mx-reply><blockquote><a href=\"https://matrix.to/#/!jEsUZKDJdhlrceRyVU:example.org/$153456789:example.org?via=kde.org&via=matrix.org\">In reply to</a> <a href=\"https://matrix.to/#/@example:example.org\">@example:example.org</a><br><b>This is an example<br>text message</b></blockquote></mx-reply>reply",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$153456789:example.org"
|
||||
}
|
||||
},
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"origin_server_ts": 1690725965572,
|
||||
"sender": "@alice:matrix.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 98
|
||||
},
|
||||
"event_id": "$154456789:example.org",
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "> <@example:example.org> video caption\n\nreply",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$263456789:example.org"
|
||||
}
|
||||
},
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"origin_server_ts": 1690725965573,
|
||||
"sender": "@alice:matrix.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 98
|
||||
},
|
||||
"event_id": "$154456799:example.org",
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||
},
|
||||
{
|
||||
"age": 96845207,
|
||||
"content": {
|
||||
"body": "Lat: 51.7035, Lon: -1.14394",
|
||||
"geo_uri": "geo:51.7035,-1.14394",
|
||||
"msgtype": "m.location",
|
||||
"org.matrix.msc1767.text": "Lat: 51.7035, Lon: -1.14394",
|
||||
"org.matrix.msc3488.asset": {
|
||||
"type": "m.pin"
|
||||
},
|
||||
"org.matrix.msc3488.location": {
|
||||
"uri": "geo:51.7035,-1.14394"
|
||||
}
|
||||
},
|
||||
"event_id": "$1544567999:example.org",
|
||||
"origin_server_ts": 1690821582876,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 96845207
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
})EVENT");
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object());
|
||||
QFile testEventHandlerSyncFile;
|
||||
testEventHandlerSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-eventhandler-sync.json"));
|
||||
testEventHandlerSyncFile.open(QIODevice::ReadOnly);
|
||||
const auto testEventHandlerSyncJson = QJsonDocument::fromJson(testEventHandlerSyncFile.readAll());
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testEventHandlerSyncJson.object());
|
||||
room->update(std::move(roomData));
|
||||
|
||||
eventHandler.setRoom(room);
|
||||
@@ -686,6 +369,29 @@ void EventHandlerTest::replyMediaInfo()
|
||||
QCOMPARE(thumbnailInfo["height"_ls], 450);
|
||||
}
|
||||
|
||||
void EventHandlerTest::thread()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.isThreaded(), false);
|
||||
QCOMPARE(eventHandler.threadRoot(), QString());
|
||||
|
||||
event = room->messageEvents().at(9).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.isThreaded(), true);
|
||||
QCOMPARE(eventHandler.threadRoot(), QStringLiteral("$threadroot:example.org"));
|
||||
QCOMPARE(eventHandler.getReplyId(), QStringLiteral("$threadroot:example.org"));
|
||||
|
||||
event = room->messageEvents().at(10).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.isThreaded(), true);
|
||||
QCOMPARE(eventHandler.threadRoot(), QStringLiteral("$threadroot:example.org"));
|
||||
QCOMPARE(eventHandler.getReplyId(), QStringLiteral("$threadmessage1:example.org"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::location()
|
||||
{
|
||||
auto event = room->messageEvents().at(7).get();
|
||||
|
||||
@@ -66,6 +66,7 @@ private Q_SLOTS:
|
||||
void receiveRichEdited_data();
|
||||
void receiveRichEdited();
|
||||
void receiveLineSeparator();
|
||||
void receiveRichCodeUrl();
|
||||
|
||||
void linkPreviewsMatch_data();
|
||||
void linkPreviewsMatch();
|
||||
@@ -647,5 +648,13 @@ void TextHandlerTest::linkPreviewsReject()
|
||||
QCOMPARE(testTextHandler.getLinkPreviews(), testOutputLinks);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveRichCodeUrl()
|
||||
{
|
||||
auto input = QStringLiteral("<code>https://kde.org</code>");
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(input);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), input);
|
||||
}
|
||||
|
||||
QTEST_MAIN(TextHandlerTest)
|
||||
#include "texthandlertest.moc"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
</provides>
|
||||
<name>NeoChat</name>
|
||||
<name xml:lang="ar">نيوتشات</name>
|
||||
<name xml:lang="ast">NeoChat</name>
|
||||
<name xml:lang="az">NeoChat</name>
|
||||
<name xml:lang="ca">NeoChat</name>
|
||||
<name xml:lang="ca-valencia">NeoChat</name>
|
||||
@@ -98,7 +99,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<p>NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
|
||||
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. على هذا النحو يتم دعم كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP والخيوط وبعض جوانب التشفير من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار ، ولكن يبقى الهدف توفير الدعم النهائي للمواصفات بأكملها.</p>
|
||||
<p xml:lang="ca">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptatge d'extrem a extrem. Hi ha algunes altres omissions més petites a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu segueix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="en-GB">NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
|
||||
<p xml:lang="eo">NeoChat celas esti plene kapabla aplikaĵo por la Matrix-specifo. Kiel tia, ĉio en la nuna stabila specifo kun la rimarkindaj esceptoj de VoIP, fadenoj kaj kelkaj aspektoj de Fin-al-Fina Ĉifrado estas subtenataj. Estas kelkaj aliaj pli malgrandaj preterlasoj pro la fakto, ke la Matrix-speco konstante evoluas, sed la celo restas provizi finfine subtenon por la tuta specifaĵo.</p>
|
||||
<p xml:lang="es">NeoChat pretende ser una aplicación con todas las funciones para la especificación de Matrix. Como tal, admite todo en la especificación estable actual, con las notables excepciones de VoIP, subprocesos y algunas funciones de cifrado de extremo a extremo. Existen algunas omisiones menos importantes debido al hecho de que la especificación de Matrix está en constante evolución, pero el objetivo sigue siendo brindar compatibilidad final con toda la especificación.</p>
|
||||
@@ -283,7 +284,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<screenshot type="default">
|
||||
<image>https://cdn.kde.org/screenshots/neochat/application.png</image>
|
||||
</screenshot>
|
||||
<screenshot x-kde-os="windows">
|
||||
<screenshot environment="windows">
|
||||
<image>https://cdn.kde.org/screenshots/neochat/NeoChat-Windows-Timeline.png</image>
|
||||
<caption>Main view with room list, chat, and room information</caption>
|
||||
<caption xml:lang="ar">العرض الرئيسة مع قائمة الغرف والدردشات و معلومات الغرفة</caption>
|
||||
@@ -310,7 +311,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<caption xml:lang="uk">Головна панель із списком кімнат, спілкуванням та даними щодо кімнати</caption>
|
||||
<caption xml:lang="x-test">xxMain view with room list, chat, and room informationxx</caption>
|
||||
</screenshot>
|
||||
<screenshot x-kde-os="windows">
|
||||
<screenshot environment="windows">
|
||||
<image>https://cdn.kde.org/screenshots/neochat/NeoChat-Windows-Login.png</image>
|
||||
<caption>Login screen</caption>
|
||||
<caption xml:lang="ar">شاشة الدخول</caption>
|
||||
@@ -342,6 +343,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release version="23.08.2" date="2023-10-12"/>
|
||||
<release version="23.08.0" date="2023-08-24">
|
||||
<url>https://kde.org/announcements/gear/23.08.0/#neochathttpsappskdeorgneochat</url>
|
||||
<description>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
Version=1.5
|
||||
Name=NeoChat
|
||||
Name[ar]=نيوتشات
|
||||
Name[ast]=NeoChat
|
||||
Name[az]=NeoChat
|
||||
Name[ca]=NeoChat
|
||||
Name[ca@valencia]=NeoChat
|
||||
|
||||
518
po/ar/neochat.po
518
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
4353
po/ast/neochat.po
Normal file
4353
po/ast/neochat.po
Normal file
File diff suppressed because it is too large
Load Diff
520
po/az/neochat.po
520
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
523
po/ca/neochat.po
523
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
580
po/cs/neochat.po
580
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
505
po/da/neochat.po
505
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
517
po/de/neochat.po
517
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
518
po/el/neochat.po
518
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
500
po/eo/neochat.po
500
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
518
po/es/neochat.po
518
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
517
po/eu/neochat.po
517
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
524
po/fi/neochat.po
524
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
634
po/fr/neochat.po
634
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
524
po/hu/neochat.po
524
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
515
po/ia/neochat.po
515
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
518
po/id/neochat.po
518
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
510
po/ie/neochat.po
510
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
532
po/it/neochat.po
532
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
494
po/ja/neochat.po
494
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
513
po/ka/neochat.po
513
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
849
po/ko/neochat.po
849
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
494
po/lt/neochat.po
494
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
518
po/nl/neochat.po
518
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
512
po/nn/neochat.po
512
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
514
po/pa/neochat.po
514
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
516
po/pl/neochat.po
516
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
518
po/pt/neochat.po
518
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
518
po/ru/neochat.po
518
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
520
po/sk/neochat.po
520
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
538
po/sl/neochat.po
538
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
528
po/sv/neochat.po
528
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
621
po/ta/neochat.po
621
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
515
po/tr/neochat.po
515
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
518
po/uk/neochat.po
518
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
@@ -135,6 +135,10 @@ add_library(neochat STATIC
|
||||
enums/delegatetype.h
|
||||
roomlastmessageprovider.cpp
|
||||
roomlastmessageprovider.h
|
||||
chatbarcache.cpp
|
||||
chatbarcache.h
|
||||
colorschemer.cpp
|
||||
colorschemer.h
|
||||
)
|
||||
|
||||
qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
@@ -152,6 +156,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/RoomPage.qml
|
||||
qml/RoomWindow.qml
|
||||
qml/JoinRoomPage.qml
|
||||
qml/ManualRoomDialog.qml
|
||||
qml/ExplorerDelegate.qml
|
||||
qml/InviteUserPage.qml
|
||||
qml/StartChatPage.qml
|
||||
@@ -165,7 +170,6 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/NeochatMaximizeComponent.qml
|
||||
qml/FancyEffectsContainer.qml
|
||||
qml/TypingPane.qml
|
||||
qml/ShimmerGradient.qml
|
||||
qml/QuickSwitcher.qml
|
||||
qml/HoverActions.qml
|
||||
qml/ChatBox.qml
|
||||
@@ -281,6 +285,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/SpaceHomePage.qml
|
||||
qml/SpaceHierarchyDelegate.qml
|
||||
qml/RemoveChildDialog.qml
|
||||
qml/SelectParentDialog.qml
|
||||
RESOURCES
|
||||
qml/confetti.png
|
||||
qml/glowdot.png
|
||||
@@ -322,15 +327,13 @@ ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
|
||||
target_sources(neochat-app PRIVATE ${NEOCHAT_ICON})
|
||||
|
||||
if(NOT ANDROID)
|
||||
target_sources(neochat PRIVATE colorschemer.cpp colorschemer.h)
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
target_sources(neochat PRIVATE trayicon_sni.cpp trayicon_sni.h)
|
||||
target_link_libraries(neochat PRIVATE KF6::StatusNotifierItem)
|
||||
else()
|
||||
target_sources(neochat PRIVATE trayicon.cpp trayicon.h)
|
||||
endif()
|
||||
target_link_libraries(neochat PUBLIC KF6::ConfigWidgets KF6::WindowSystem ICU::uc)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_COLORSCHEME)
|
||||
target_link_libraries(neochat PUBLIC KF6::WindowSystem ICU::uc)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_WINDOWSYSTEM)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_ICU)
|
||||
endif()
|
||||
@@ -342,7 +345,27 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
endif()
|
||||
|
||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
|
||||
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF6::I18n KF6::Kirigami2 KF6::Notifications KF6::ConfigCore KF6::ConfigGui KF6::CoreAddons KF6::SonnetCore KF6::ItemModels QuotientQt6 cmark::cmark QCoro::Core)
|
||||
target_link_libraries(neochat PUBLIC
|
||||
Qt::Core
|
||||
Qt::Quick
|
||||
Qt::Qml
|
||||
Qt::Gui
|
||||
Qt::Multimedia
|
||||
Qt::Network
|
||||
Qt::QuickControls2
|
||||
KF6::I18n
|
||||
KF6::Kirigami2
|
||||
KF6::Notifications
|
||||
KF6::ConfigCore
|
||||
KF6::ConfigGui
|
||||
KF6::CoreAddons
|
||||
KF6::SonnetCore
|
||||
KF6::ColorScheme
|
||||
KF6::ItemModels
|
||||
QuotientQt6
|
||||
cmark::cmark
|
||||
QCoro::Core
|
||||
)
|
||||
|
||||
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||
|
||||
@@ -428,6 +451,7 @@ if(ANDROID)
|
||||
"preferences-desktop-notification"
|
||||
"computer-symbolic"
|
||||
"gps"
|
||||
"system-users-symbolic"
|
||||
)
|
||||
else()
|
||||
target_link_libraries(neochat PUBLIC Qt::Widgets KF6::KIOWidgets)
|
||||
|
||||
@@ -38,45 +38,33 @@ void ActionsHandler::setRoom(NeoChatRoom *room)
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
void ActionsHandler::handleNewMessage()
|
||||
void ActionsHandler::handleMessageEvent(ChatBarCache *chatBarCache)
|
||||
{
|
||||
checkEffects(m_room->chatBoxText());
|
||||
if (!m_room->chatBoxAttachmentPath().isEmpty()) {
|
||||
QUrl url(m_room->chatBoxAttachmentPath());
|
||||
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
|
||||
m_room->uploadFile(QUrl(path), m_room->chatBoxText().isEmpty() ? path.mid(path.lastIndexOf(u'/') + 1) : m_room->chatBoxText());
|
||||
m_room->setChatBoxAttachmentPath({});
|
||||
m_room->setChatBoxText({});
|
||||
if (!chatBarCache) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString handledText = m_room->chatBoxText();
|
||||
handledText = handleMentions(handledText);
|
||||
handleMessage(m_room->chatBoxText(), handledText);
|
||||
checkEffects(chatBarCache->text());
|
||||
if (!chatBarCache->attachmentPath().isEmpty()) {
|
||||
QUrl url(chatBarCache->attachmentPath());
|
||||
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
|
||||
m_room->uploadFile(QUrl(path), chatBarCache->text().isEmpty() ? path.mid(path.lastIndexOf(u'/') + 1) : chatBarCache->text());
|
||||
chatBarCache->setAttachmentPath({});
|
||||
chatBarCache->setText({});
|
||||
return;
|
||||
}
|
||||
|
||||
QString handledText = chatBarCache->text();
|
||||
handledText = handleMentions(handledText, chatBarCache->mentions());
|
||||
handleMessage(m_room->mainCache()->text(), handledText, chatBarCache);
|
||||
}
|
||||
|
||||
void ActionsHandler::handleEdit()
|
||||
{
|
||||
checkEffects(m_room->editText());
|
||||
|
||||
QString handledText = m_room->editText();
|
||||
handledText = handleMentions(handledText, true);
|
||||
handleMessage(m_room->editText(), handledText, true);
|
||||
}
|
||||
|
||||
QString ActionsHandler::handleMentions(QString handledText, const bool &isEdit)
|
||||
QString ActionsHandler::handleMentions(QString handledText, QList<Mention> *mentions)
|
||||
{
|
||||
if (!m_room) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
QVector<Mention> *mentions;
|
||||
if (isEdit) {
|
||||
mentions = m_room->editMentions();
|
||||
} else {
|
||||
mentions = m_room->mentions();
|
||||
}
|
||||
|
||||
std::sort(mentions->begin(), mentions->end(), [](const auto &a, const auto &b) -> bool {
|
||||
return a.cursor.anchor() > b.cursor.anchor();
|
||||
});
|
||||
@@ -94,7 +82,7 @@ QString ActionsHandler::handleMentions(QString handledText, const bool &isEdit)
|
||||
return handledText;
|
||||
}
|
||||
|
||||
void ActionsHandler::handleMessage(const QString &text, QString handledText, const bool &isEdit)
|
||||
void ActionsHandler::handleMessage(const QString &text, QString handledText, ChatBarCache *chatBarCache)
|
||||
{
|
||||
if (NeoChatConfig::allowQuickEdit()) {
|
||||
QRegularExpression sed(QStringLiteral("^s/([^/]*)/([^/]*)(/g)?$"));
|
||||
@@ -134,7 +122,7 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, con
|
||||
for (const auto &action : ActionsModel::instance().allActions()) {
|
||||
if (handledText.indexOf(action.prefix) == 1
|
||||
&& (handledText.indexOf(" "_ls) == action.prefix.length() + 1 || handledText.length() == action.prefix.length() + 1)) {
|
||||
handledText = action.handle(handledText.mid(action.prefix.length() + 1).trimmed(), m_room);
|
||||
handledText = action.handle(handledText.mid(action.prefix.length() + 1).trimmed(), m_room, chatBarCache);
|
||||
if (action.messageType.has_value()) {
|
||||
messageType = *action.messageType;
|
||||
}
|
||||
@@ -161,7 +149,7 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, con
|
||||
return;
|
||||
}
|
||||
|
||||
m_room->postMessage(text, handledText, messageType, m_room->chatBoxReplyId(), isEdit ? m_room->chatBoxEditId() : QString());
|
||||
m_room->postMessage(text, handledText, messageType, chatBarCache->replyId(), chatBarCache->editId(), chatBarCache->threadId());
|
||||
}
|
||||
|
||||
void ActionsHandler::checkEffects(const QString &text)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
@@ -51,21 +52,15 @@ Q_SIGNALS:
|
||||
void showEffect(const QString &effect);
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
/**
|
||||
* @brief Pre-process text and send message.
|
||||
* @brief Pre-process text and send message event.
|
||||
*/
|
||||
void handleNewMessage();
|
||||
|
||||
/**
|
||||
* @brief Pre-process text and send edit.
|
||||
*/
|
||||
void handleEdit();
|
||||
void handleMessageEvent(ChatBarCache *chatBarCache);
|
||||
|
||||
private:
|
||||
NeoChatRoom *m_room = nullptr;
|
||||
void checkEffects(const QString &text);
|
||||
|
||||
QString handleMentions(QString handledText, const bool &isEdit = false);
|
||||
void handleMessage(const QString &text, QString handledText, const bool &isEdit = false);
|
||||
QString handleMentions(QString handledText, QList<Mention> *mentions);
|
||||
void handleMessage(const QString &text, QString handledText, ChatBarCache *chatBarCache);
|
||||
};
|
||||
|
||||
175
src/chatbarcache.cpp
Normal file
175
src/chatbarcache.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "chatbarcache.h"
|
||||
|
||||
#include "eventhandler.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
ChatBarCache::ChatBarCache(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QString ChatBarCache::text() const
|
||||
{
|
||||
return m_text;
|
||||
}
|
||||
|
||||
void ChatBarCache::setText(const QString &text)
|
||||
{
|
||||
if (text == m_text) {
|
||||
return;
|
||||
}
|
||||
m_text = text;
|
||||
Q_EMIT textChanged();
|
||||
}
|
||||
|
||||
bool ChatBarCache::isReplying() const
|
||||
{
|
||||
return m_relationType == Reply && !m_relationId.isEmpty();
|
||||
}
|
||||
|
||||
QString ChatBarCache::replyId() const
|
||||
{
|
||||
if (m_relationType != Reply) {
|
||||
return {};
|
||||
}
|
||||
return m_relationId;
|
||||
}
|
||||
|
||||
void ChatBarCache::setReplyId(const QString &replyId)
|
||||
{
|
||||
if (m_relationType == Reply && m_relationId == replyId) {
|
||||
return;
|
||||
}
|
||||
m_relationId = replyId;
|
||||
if (m_relationId.isEmpty()) {
|
||||
m_relationType = None;
|
||||
} else {
|
||||
m_relationType = Reply;
|
||||
}
|
||||
m_attachmentPath = QString();
|
||||
Q_EMIT relationIdChanged();
|
||||
Q_EMIT attachmentPathChanged();
|
||||
}
|
||||
|
||||
bool ChatBarCache::isEditing() const
|
||||
{
|
||||
return m_relationType == Edit && !m_relationId.isEmpty();
|
||||
}
|
||||
|
||||
QString ChatBarCache::editId() const
|
||||
{
|
||||
if (m_relationType != Edit) {
|
||||
return {};
|
||||
}
|
||||
return m_relationId;
|
||||
}
|
||||
|
||||
void ChatBarCache::setEditId(const QString &editId)
|
||||
{
|
||||
if (m_relationType == Edit && m_relationId == editId) {
|
||||
return;
|
||||
}
|
||||
m_relationId = editId;
|
||||
if (m_relationId.isEmpty()) {
|
||||
m_relationType = None;
|
||||
} else {
|
||||
m_relationType = Edit;
|
||||
}
|
||||
m_attachmentPath = QString();
|
||||
Q_EMIT relationIdChanged();
|
||||
Q_EMIT attachmentPathChanged();
|
||||
}
|
||||
|
||||
QVariantMap ChatBarCache::relationUser() const
|
||||
{
|
||||
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 {};
|
||||
}
|
||||
if (m_relationId.isEmpty()) {
|
||||
return room->getUser(nullptr);
|
||||
}
|
||||
return room->getUser(room->user((*room->findInTimeline(m_relationId))->senderId()));
|
||||
}
|
||||
|
||||
QString ChatBarCache::relationMessage() const
|
||||
{
|
||||
if (parent() == nullptr) {
|
||||
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return {};
|
||||
}
|
||||
if (m_relationId.isEmpty()) {
|
||||
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 {};
|
||||
}
|
||||
EventHandler eventhandler;
|
||||
eventhandler.setRoom(room);
|
||||
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
|
||||
eventhandler.setEvent(&**event);
|
||||
return eventhandler.getPlainBody();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool ChatBarCache::isThreaded() const
|
||||
{
|
||||
return !m_threadId.isEmpty();
|
||||
}
|
||||
|
||||
QString ChatBarCache::threadId() const
|
||||
{
|
||||
return m_threadId;
|
||||
}
|
||||
|
||||
void ChatBarCache::setThreadId(const QString &threadId)
|
||||
{
|
||||
if (m_threadId == threadId) {
|
||||
return;
|
||||
}
|
||||
m_threadId = threadId;
|
||||
Q_EMIT threadIdChanged();
|
||||
}
|
||||
|
||||
QString ChatBarCache::attachmentPath() const
|
||||
{
|
||||
return m_attachmentPath;
|
||||
}
|
||||
|
||||
void ChatBarCache::setAttachmentPath(const QString &attachmentPath)
|
||||
{
|
||||
if (attachmentPath == m_attachmentPath) {
|
||||
return;
|
||||
}
|
||||
m_attachmentPath = attachmentPath;
|
||||
m_relationType = None;
|
||||
m_relationId = QString();
|
||||
Q_EMIT attachmentPathChanged();
|
||||
Q_EMIT relationIdChanged();
|
||||
}
|
||||
|
||||
QList<Mention> *ChatBarCache::mentions()
|
||||
{
|
||||
return &m_mentions;
|
||||
}
|
||||
|
||||
QString ChatBarCache::savedText() const
|
||||
{
|
||||
return m_savedText;
|
||||
}
|
||||
|
||||
void ChatBarCache::setSavedText(const QString &savedText)
|
||||
{
|
||||
m_savedText = savedText;
|
||||
}
|
||||
201
src/chatbarcache.h
Normal file
201
src/chatbarcache.h
Normal file
@@ -0,0 +1,201 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QTextCursor>
|
||||
|
||||
/**
|
||||
* @brief Defines a user mention in the current chat or edit text.
|
||||
*/
|
||||
struct Mention {
|
||||
QTextCursor cursor; /**< Contains the mention's text and position in the text. */
|
||||
QString text; /**< The inserted text of the mention. */
|
||||
int start = 0; /**< Start position of the mention. */
|
||||
int position = 0; /**< End position of the mention. */
|
||||
QString id; /**< The id the mention (used to create link when sending the message). */
|
||||
};
|
||||
|
||||
/**
|
||||
* @class ChatBarCache
|
||||
*
|
||||
* A class to cache data from a chat bar.
|
||||
*
|
||||
* A chat bar can be anything that allows users to compose or edit message, it doesn't
|
||||
* necessarily have to use the ChatBar component, e.g. MessageEditComponent.
|
||||
*
|
||||
* This object is intended to allow the current contents of a chat bar to be cached
|
||||
* between different rooms, i.e. there is an expectation that each NeoChatRoom could
|
||||
* have a separate cache for each chat bar.
|
||||
*
|
||||
* @note The NeoChatRoom which this component is created in is expected to be set
|
||||
* as it's parent. This is necessary for certain functions which need to get
|
||||
* relevant room information.
|
||||
*
|
||||
* @sa ChatBar, MessageEditComponent, NeoChatRoom
|
||||
*/
|
||||
class ChatBarCache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
/**
|
||||
* @brief The text in the chat bar.
|
||||
*
|
||||
* Due to problems with QTextDocument, unlike the other properties here,
|
||||
* text is *not* used to store the text when switching rooms.
|
||||
*/
|
||||
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the chat bar is currently replying to a message.
|
||||
*/
|
||||
Q_PROPERTY(bool isReplying READ isReplying NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The Matrix message ID of an event being replied to, if any.
|
||||
*
|
||||
* Will return empty if the RelationType is currently set to None or Edit.
|
||||
*
|
||||
* @note Replying, editing and attachments are exclusive so setting this will
|
||||
* clear an edit or attachment.
|
||||
*
|
||||
* @sa RelationType
|
||||
*/
|
||||
Q_PROPERTY(QString replyId READ replyId WRITE setReplyId NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the chat bar is currently editing a message.
|
||||
*/
|
||||
Q_PROPERTY(bool isEditing READ isEditing NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The Matrix message ID of an event being edited, if any.
|
||||
*
|
||||
* Will return empty if the RelationType is currently set to None or Reply.
|
||||
*
|
||||
* @note Replying, editing and attachments are exclusive so setting this will
|
||||
* clear an reply or attachment.
|
||||
*
|
||||
* @sa RelationType
|
||||
*/
|
||||
Q_PROPERTY(QString editId READ editId WRITE setEditId NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Get the user for the message being replied to.
|
||||
*
|
||||
* This is different to getting a Quotient::User object
|
||||
* as neither of those can provide details like the displayName or avatarMediaId
|
||||
* without the room context as these can vary from room to room.
|
||||
*
|
||||
* Returns an empty user if not replying to a message.
|
||||
*
|
||||
* The user QVariantMap has the following properties:
|
||||
* - isLocalUser - Whether the user is the local user.
|
||||
* - id - The matrix ID of the user.
|
||||
* - displayName - Display name in the context of this room.
|
||||
* - avatarSource - The mxc URL for the user's avatar in the current room.
|
||||
* - avatarMediaId - Avatar id in the context of this room.
|
||||
* - color - Color for the user.
|
||||
* - object - The Quotient::User object for the user.
|
||||
*
|
||||
* @sa getUser, Quotient::User
|
||||
*/
|
||||
Q_PROPERTY(QVariantMap relationUser READ relationUser NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The content of the related message.
|
||||
*
|
||||
* Will be QString() if no related message.
|
||||
*/
|
||||
Q_PROPERTY(QString relationMessage READ relationMessage NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the chat bar is replying in a thread.
|
||||
*/
|
||||
Q_PROPERTY(bool isThreaded READ isThreaded NOTIFY threadIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The Matrix message ID of thread root event, if any.
|
||||
*/
|
||||
Q_PROPERTY(QString threadId READ threadId WRITE setThreadId NOTIFY threadIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The local path for a file to send, if any.
|
||||
*
|
||||
* @note Replying, editing and attachments are exclusive so setting this will
|
||||
* clear an edit or reply.
|
||||
*/
|
||||
Q_PROPERTY(QString attachmentPath READ attachmentPath WRITE setAttachmentPath NOTIFY attachmentPathChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Describes the type of relation which relationId can refer to.
|
||||
*
|
||||
* A chat bar can only be relating to a single message at a time making these
|
||||
* exclusive.
|
||||
*/
|
||||
enum RelationType {
|
||||
Reply, /**< The current relation is a message being replied to. */
|
||||
Edit, /**< The current relation is a message being edited. */
|
||||
None, /**< There is currently no relation event */
|
||||
};
|
||||
Q_ENUM(RelationType)
|
||||
|
||||
explicit ChatBarCache(QObject *parent = nullptr);
|
||||
|
||||
QString text() const;
|
||||
void setText(const QString &text);
|
||||
|
||||
bool isReplying() const;
|
||||
QString replyId() const;
|
||||
void setReplyId(const QString &replyId);
|
||||
|
||||
bool isEditing() const;
|
||||
QString editId() const;
|
||||
void setEditId(const QString &editId);
|
||||
|
||||
QVariantMap relationUser() const;
|
||||
|
||||
QString relationMessage() const;
|
||||
|
||||
bool isThreaded() const;
|
||||
QString threadId() const;
|
||||
void setThreadId(const QString &threadId);
|
||||
|
||||
QString attachmentPath() const;
|
||||
void setAttachmentPath(const QString &attachmentPath);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the mentions for the current chat bar text.
|
||||
*/
|
||||
QList<Mention> *mentions();
|
||||
|
||||
/**
|
||||
* @brief Get the saved chat bar text.
|
||||
*/
|
||||
QString savedText() const;
|
||||
|
||||
/**
|
||||
* @brief Save the chat bar text.
|
||||
*/
|
||||
void setSavedText(const QString &savedText);
|
||||
|
||||
Q_SIGNALS:
|
||||
void textChanged();
|
||||
void relationIdChanged();
|
||||
void threadIdChanged();
|
||||
void attachmentPathChanged();
|
||||
|
||||
private:
|
||||
QString m_text = QString();
|
||||
QString m_relationId = QString();
|
||||
RelationType m_relationType = RelationType::None;
|
||||
QString m_threadId = QString();
|
||||
QString m_attachmentPath = QString();
|
||||
QList<Mention> m_mentions;
|
||||
QString m_savedText;
|
||||
};
|
||||
@@ -57,11 +57,12 @@ public:
|
||||
setFormat(error.first, error.second.size(), errorFormat);
|
||||
}
|
||||
}
|
||||
auto room = dynamic_cast<ChatDocumentHandler *>(parent())->room();
|
||||
auto handler = dynamic_cast<ChatDocumentHandler *>(parent());
|
||||
auto room = handler->room();
|
||||
if (!room) {
|
||||
return;
|
||||
}
|
||||
auto mentions = room->mentions();
|
||||
auto mentions = handler->chatBarCache()->mentions();
|
||||
mentions->erase(std::remove_if(mentions->begin(),
|
||||
mentions->end(),
|
||||
[this](auto &mention) {
|
||||
@@ -103,15 +104,10 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
||||
m_completionModel->setRoom(m_room);
|
||||
static QPointer<NeoChatRoom> previousRoom = nullptr;
|
||||
if (previousRoom) {
|
||||
disconnect(previousRoom, &NeoChatRoom::chatBoxTextChanged, this, nullptr);
|
||||
disconnect(previousRoom, &NeoChatRoom::editTextChanged, this, nullptr);
|
||||
disconnect(m_chatBarCache, &ChatBarCache::textChanged, this, nullptr);
|
||||
}
|
||||
previousRoom = m_room;
|
||||
connect(m_room, &NeoChatRoom::chatBoxTextChanged, this, [this]() {
|
||||
int start = completionStartIndex();
|
||||
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::editTextChanged, this, [this]() {
|
||||
connect(m_chatBarCache, &ChatBarCache::textChanged, this, [this]() {
|
||||
int start = completionStartIndex();
|
||||
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||
});
|
||||
@@ -215,6 +211,20 @@ void ChatDocumentHandler::setRoom(NeoChatRoom *room)
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
ChatBarCache *ChatDocumentHandler::chatBarCache() const
|
||||
{
|
||||
return m_chatBarCache;
|
||||
}
|
||||
|
||||
void ChatDocumentHandler::setChatBarCache(ChatBarCache *chatBarCache)
|
||||
{
|
||||
if (m_chatBarCache == chatBarCache) {
|
||||
return;
|
||||
}
|
||||
m_chatBarCache = chatBarCache;
|
||||
Q_EMIT chatBarCacheChanged();
|
||||
}
|
||||
|
||||
void ChatDocumentHandler::complete(int index)
|
||||
{
|
||||
if (m_completionModel->autoCompletionType() == CompletionModel::User) {
|
||||
@@ -303,11 +313,7 @@ QString ChatDocumentHandler::getText() const
|
||||
if (!m_room) {
|
||||
return QString();
|
||||
}
|
||||
if (m_isEdit) {
|
||||
return m_room->editText();
|
||||
} else {
|
||||
return m_room->chatBoxText();
|
||||
}
|
||||
return m_chatBarCache->text();
|
||||
}
|
||||
|
||||
void ChatDocumentHandler::pushMention(const Mention mention) const
|
||||
@@ -315,11 +321,7 @@ void ChatDocumentHandler::pushMention(const Mention mention) const
|
||||
if (!m_room) {
|
||||
return;
|
||||
}
|
||||
if (m_isEdit) {
|
||||
m_room->editMentions()->push_back(mention);
|
||||
} else {
|
||||
m_room->mentions()->push_back(mention);
|
||||
}
|
||||
m_chatBarCache->mentions()->push_back(mention);
|
||||
}
|
||||
|
||||
QColor ChatDocumentHandler::mentionColor() const
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <QQuickTextDocument>
|
||||
#include <QTextCursor>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "models/completionmodel.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
@@ -102,6 +103,11 @@ class ChatDocumentHandler : public QObject
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@@ -133,6 +139,9 @@ public:
|
||||
[[nodiscard]] NeoChatRoom *room() const;
|
||||
void setRoom(NeoChatRoom *room);
|
||||
|
||||
[[nodiscard]] ChatBarCache *chatBarCache() const;
|
||||
void setChatBarCache(ChatBarCache *chatBarCache);
|
||||
|
||||
Q_INVOKABLE void complete(int index);
|
||||
|
||||
void updateCompletions();
|
||||
@@ -149,6 +158,7 @@ Q_SIGNALS:
|
||||
void documentChanged();
|
||||
void cursorPositionChanged();
|
||||
void roomChanged();
|
||||
void chatBarCacheChanged();
|
||||
void completionModelChanged();
|
||||
void selectionStartChanged();
|
||||
void selectionEndChanged();
|
||||
@@ -163,6 +173,7 @@ private:
|
||||
QPointer<QQuickTextDocument> m_document;
|
||||
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
QPointer<ChatBarCache> m_chatBarCache;
|
||||
bool completionVisible = false;
|
||||
|
||||
QColor m_mentionColor;
|
||||
|
||||
@@ -231,11 +231,8 @@ bool EventHandler::isHidden()
|
||||
}
|
||||
}
|
||||
|
||||
if (m_event->isStateEvent()) {
|
||||
auto *stateEvent = eventCast<const StateEvent>(m_event);
|
||||
if (stateEvent && stateEvent->repeatsState()) {
|
||||
return true;
|
||||
}
|
||||
if (m_event->isStateEvent() && eventCast<const StateEvent>(m_event)->repeatsState()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// isReplacement?
|
||||
@@ -904,6 +901,28 @@ QVariantMap EventHandler::getReplyMediaInfo() const
|
||||
return getMediaInfoForEvent(replyPtr);
|
||||
}
|
||||
|
||||
bool EventHandler::isThreaded() const
|
||||
{
|
||||
return (m_event->contentPart<QJsonObject>("m.relates_to"_ls).contains("rel_type"_ls)
|
||||
&& m_event->contentPart<QJsonObject>("m.relates_to"_ls)["rel_type"_ls].toString() == "m.thread"_ls)
|
||||
|| (!m_event->unsignedPart<QJsonObject>("m.relations"_ls).isEmpty() && m_event->unsignedPart<QJsonObject>("m.relations"_ls).contains("m.thread"_ls));
|
||||
}
|
||||
|
||||
QString EventHandler::threadRoot() const
|
||||
{
|
||||
// Get the thread root ID from m.relates_to if it exists.
|
||||
if (m_event->contentPart<QJsonObject>("m.relates_to"_ls).contains("rel_type"_ls)
|
||||
&& m_event->contentPart<QJsonObject>("m.relates_to"_ls)["rel_type"_ls].toString() == "m.thread"_ls) {
|
||||
return m_event->contentPart<QJsonObject>("m.relates_to"_ls)["event_id"_ls].toString();
|
||||
}
|
||||
// For thread root events they have an m.relations in the unsigned part with a m.thread object.
|
||||
// If so return the event ID as it is the root.
|
||||
if (!m_event->unsignedPart<QJsonObject>("m.relations"_ls).isEmpty() && m_event->unsignedPart<QJsonObject>("m.relations"_ls).contains("m.thread"_ls)) {
|
||||
return getId();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
float EventHandler::getLatitude() const
|
||||
{
|
||||
const auto geoUri = m_event->contentJson()["geo_uri"_ls].toString();
|
||||
|
||||
@@ -326,6 +326,20 @@ public:
|
||||
*/
|
||||
QVariantMap getReplyMediaInfo() const;
|
||||
|
||||
/**
|
||||
* @brief Whether the message is part of a thread.
|
||||
*
|
||||
* i.e. There is a rel_type of m.thread.
|
||||
*/
|
||||
bool isThreaded() const;
|
||||
|
||||
/**
|
||||
* @brief Return the Matrix ID of the thread's root message.
|
||||
*
|
||||
* Empty if this not part of a thread.
|
||||
*/
|
||||
QString threadRoot() const;
|
||||
|
||||
/**
|
||||
* @brief Return the latitude for the event.
|
||||
*
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QVector>
|
||||
#include <QList>
|
||||
#include <Quotient/events/eventcontent.h>
|
||||
#include <Quotient/events/stateevent.h>
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
*
|
||||
* @sa ImagePackImage
|
||||
*/
|
||||
QVector<ImagePackEventContent::ImagePackImage> images;
|
||||
QList<ImagePackEventContent::ImagePackImage> images;
|
||||
|
||||
explicit ImagePackEventContent(const QJsonObject &o);
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ class LocationHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
QML_SINGLETON
|
||||
|
||||
public:
|
||||
/** Unite two rectanlges. */
|
||||
Q_INVOKABLE static QRectF unite(const QRectF &r1, const QRectF &r2);
|
||||
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
file.write(buf.constData(), buf.size());
|
||||
file.flush();
|
||||
|
||||
if (oldHandler && (strcmp(context.category, "quotient.e2ee") != 0 || e2eeDebugEnabled)) {
|
||||
if (oldHandler && (!context.category || (strcmp(context.category, "quotient.e2ee") != 0 || e2eeDebugEnabled))) {
|
||||
oldHandler(type, context, message);
|
||||
}
|
||||
}
|
||||
@@ -197,7 +197,6 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
sInstance()->log(QtInfoMsg, context, message);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +211,7 @@ void filter(QLoggingCategory *category)
|
||||
|
||||
void initLogging()
|
||||
{
|
||||
e2eeDebugEnabled = QLoggingCategory("quotient.e2ee", QtDebugMsg).isEnabled(QtDebugMsg);
|
||||
e2eeDebugEnabled = QLoggingCategory("quotient.e2ee", QtInfoMsg).isEnabled(QtDebugMsg);
|
||||
oldCategoryFilter = QLoggingCategory::installFilter(filter);
|
||||
oldHandler = qInstallMessageHandler(messageHandler);
|
||||
sInstance->setOrigHandler(oldHandler);
|
||||
|
||||
11
src/main.cpp
11
src/main.cpp
@@ -38,6 +38,7 @@
|
||||
#include <Quotient/util.h>
|
||||
|
||||
#include "blurhashimageprovider.h"
|
||||
#include "colorschemer.h"
|
||||
#include "controller.h"
|
||||
#include "logger.h"
|
||||
#include "matriximageprovider.h"
|
||||
@@ -45,10 +46,6 @@
|
||||
#include "roommanager.h"
|
||||
#include "windowcontroller.h"
|
||||
|
||||
#ifdef HAVE_COLORSCHEME
|
||||
#include "colorschemer.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_RUNNER
|
||||
#include "runner.h"
|
||||
#include <QDBusConnection>
|
||||
@@ -125,7 +122,7 @@ int main(int argc, char *argv[])
|
||||
font.setHintingPreference(QFont::PreferNoHinting);
|
||||
app.setFont(font);
|
||||
#endif
|
||||
KLocalizedString::setApplicationDomain("neochat");
|
||||
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
|
||||
|
||||
QGuiApplication::setOrganizationName("KDE"_ls);
|
||||
|
||||
@@ -157,9 +154,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
initLogging();
|
||||
|
||||
#if Quotient_VERSION_MINOR == 8
|
||||
Connection::setEncryptionDefault(true);
|
||||
#endif
|
||||
|
||||
#ifdef NEOCHAT_FLATPAK
|
||||
// Copy over the included FontConfig configuration to the
|
||||
@@ -168,12 +163,10 @@ int main(int argc, char *argv[])
|
||||
QStringLiteral("/var/config/fontconfig/conf.d/99-noto-mono-color-emoji.conf"));
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_COLORSCHEME
|
||||
ColorSchemer colorScheme;
|
||||
if (!NeoChatConfig::self()->colorScheme().isEmpty()) {
|
||||
colorScheme.apply(NeoChatConfig::self()->colorScheme());
|
||||
}
|
||||
#endif
|
||||
|
||||
qml_register_types_org_kde_neochat();
|
||||
qmlRegisterSingletonInstance("org.kde.neochat.config", 1, 0, "Config", NeoChatConfig::self());
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QCoroTask>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QQmlEngine>
|
||||
#include <QVector>
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "actionsmodel.h"
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "controller.h"
|
||||
#include "neochatroom.h"
|
||||
#include "roommanager.h"
|
||||
@@ -19,7 +20,7 @@ QStringList rainbowColors{"#ff2b00"_ls, "#ff5500"_ls, "#ff8000"_ls, "#ffaa00"_ls
|
||||
"#00d4ff"_ls, "#00aaff"_ls, "#007fff"_ls, "#0055ff"_ls, "#002bff"_ls, "#0000ff"_ls, "#2a00ff"_ls, "#5500ff"_ls, "#7f00ff"_ls,
|
||||
"#aa00ff"_ls, "#d400ff"_ls, "#ff00ff"_ls, "#ff00d4"_ls, "#ff00aa"_ls, "#ff0080"_ls, "#ff0055"_ls, "#ff002b"_ls, "#ff0000"_ls};
|
||||
|
||||
auto leaveRoomLambda = [](const QString &text, NeoChatRoom *room) {
|
||||
auto leaveRoomLambda = [](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
if (text.isEmpty()) {
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Info, i18n("Leaving this room."));
|
||||
room->connection()->leaveRoom(room);
|
||||
@@ -45,7 +46,7 @@ auto leaveRoomLambda = [](const QString &text, NeoChatRoom *room) {
|
||||
return QString();
|
||||
};
|
||||
|
||||
auto roomNickLambda = [](const QString &text, NeoChatRoom *room) {
|
||||
auto roomNickLambda = [](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
if (text.isEmpty()) {
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("No new nickname provided, no changes will happen."));
|
||||
} else {
|
||||
@@ -54,10 +55,10 @@ auto roomNickLambda = [](const QString &text, NeoChatRoom *room) {
|
||||
return QString();
|
||||
};
|
||||
|
||||
QVector<ActionsModel::Action> actions{
|
||||
QList<ActionsModel::Action> actions{
|
||||
Action{
|
||||
QStringLiteral("shrug"),
|
||||
[](const QString &message, NeoChatRoom *) {
|
||||
[](const QString &message, NeoChatRoom *, ChatBarCache *) {
|
||||
return QStringLiteral("¯\\\\_(ツ)_/¯ %1").arg(message);
|
||||
},
|
||||
true,
|
||||
@@ -67,7 +68,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("lenny"),
|
||||
[](const QString &message, NeoChatRoom *) {
|
||||
[](const QString &message, NeoChatRoom *, ChatBarCache *) {
|
||||
return QStringLiteral("( ͡° ͜ʖ ͡°) %1").arg(message);
|
||||
},
|
||||
true,
|
||||
@@ -77,7 +78,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("tableflip"),
|
||||
[](const QString &message, NeoChatRoom *) {
|
||||
[](const QString &message, NeoChatRoom *, ChatBarCache *) {
|
||||
return QStringLiteral("(╯°□°)╯︵ ┻━┻ %1").arg(message);
|
||||
},
|
||||
true,
|
||||
@@ -87,7 +88,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("unflip"),
|
||||
[](const QString &message, NeoChatRoom *) {
|
||||
[](const QString &message, NeoChatRoom *, ChatBarCache *) {
|
||||
return QStringLiteral("┬──┬ ノ( ゜-゜ノ) %1").arg(message);
|
||||
},
|
||||
true,
|
||||
@@ -97,7 +98,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("rainbow"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *chatBarCache) {
|
||||
QString rainbowText;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
rainbowText += QStringLiteral("<font color='%2'>%3</font>").arg(rainbowColors[i % rainbowColors.length()], text.at(i));
|
||||
@@ -106,8 +107,8 @@ QVector<ActionsModel::Action> actions{
|
||||
room->postMessage(QStringLiteral("/rainbow %1").arg(text),
|
||||
rainbowText,
|
||||
RoomMessageEvent::MsgType::Text,
|
||||
room->chatBoxReplyId(),
|
||||
room->chatBoxEditId());
|
||||
chatBarCache->replyId(),
|
||||
chatBarCache->editId());
|
||||
return QString();
|
||||
},
|
||||
false,
|
||||
@@ -117,7 +118,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("rainbowme"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *chatBarCache) {
|
||||
QString rainbowText;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
rainbowText += QStringLiteral("<font color='%2'>%3</font>").arg(rainbowColors[i % rainbowColors.length()], text.at(i));
|
||||
@@ -126,8 +127,8 @@ QVector<ActionsModel::Action> actions{
|
||||
room->postMessage(QStringLiteral("/rainbow %1").arg(text),
|
||||
rainbowText,
|
||||
RoomMessageEvent::MsgType::Emote,
|
||||
room->chatBoxReplyId(),
|
||||
room->chatBoxEditId());
|
||||
chatBarCache->replyId(),
|
||||
chatBarCache->editId());
|
||||
return QString();
|
||||
},
|
||||
false,
|
||||
@@ -137,7 +138,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("plain"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
room->postMessage(text, text.toHtmlEscaped(), RoomMessageEvent::MsgType::Text, {}, {});
|
||||
return QString();
|
||||
},
|
||||
@@ -148,13 +149,13 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("spoiler"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *chatBarCache) {
|
||||
// Ideally, we would just return rainbowText and let that do the rest, but the colors don't survive markdownToHTML.
|
||||
room->postMessage(QStringLiteral("/spoiler %1").arg(text),
|
||||
QStringLiteral("<span data-mx-spoiler>%1</span>").arg(text),
|
||||
RoomMessageEvent::MsgType::Text,
|
||||
room->chatBoxReplyId(),
|
||||
room->chatBoxEditId());
|
||||
chatBarCache->replyId(),
|
||||
chatBarCache->editId());
|
||||
return QString();
|
||||
},
|
||||
false,
|
||||
@@ -164,7 +165,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("me"),
|
||||
[](const QString &text, NeoChatRoom *) {
|
||||
[](const QString &text, NeoChatRoom *, ChatBarCache *) {
|
||||
return text;
|
||||
},
|
||||
true,
|
||||
@@ -174,7 +175,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("notice"),
|
||||
[](const QString &text, NeoChatRoom *) {
|
||||
[](const QString &text, NeoChatRoom *, ChatBarCache *) {
|
||||
return text;
|
||||
},
|
||||
true,
|
||||
@@ -184,7 +185,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("invite"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
static const QRegularExpression mxidRegex(
|
||||
QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
|
||||
auto regexMatch = mxidRegex.match(text);
|
||||
@@ -220,7 +221,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("join"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"));
|
||||
auto regexMatch = roomRegex.match(text);
|
||||
if (!regexMatch.hasMatch()) {
|
||||
@@ -244,7 +245,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("knock"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
auto parts = text.split(QLatin1String(" "));
|
||||
QString roomName = parts[0];
|
||||
QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"));
|
||||
@@ -276,7 +277,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("j"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"));
|
||||
auto regexMatch = roomRegex.match(text);
|
||||
if (!regexMatch.hasMatch()) {
|
||||
@@ -315,7 +316,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("nick"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
if (text.isEmpty()) {
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("No new nickname provided, no changes will happen."));
|
||||
} else {
|
||||
@@ -346,7 +347,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("ignore"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
static const QRegularExpression mxidRegex(
|
||||
QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
|
||||
auto regexMatch = mxidRegex.match(text);
|
||||
@@ -374,7 +375,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("unignore"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
static const QRegularExpression mxidRegex(
|
||||
QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
|
||||
auto regexMatch = mxidRegex.match(text);
|
||||
@@ -402,9 +403,8 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("react"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
QString replyEventId = room->chatBoxReplyId();
|
||||
if (replyEventId.isEmpty()) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *chatBarCache) {
|
||||
if (chatBarCache->replyId().isEmpty()) {
|
||||
for (auto it = room->messageEvents().crbegin(); it != room->messageEvents().crend(); it++) {
|
||||
const auto &evt = **it;
|
||||
if (const auto event = eventCast<const RoomMessageEvent>(&evt)) {
|
||||
@@ -413,7 +413,7 @@ QVector<ActionsModel::Action> actions{
|
||||
}
|
||||
}
|
||||
}
|
||||
room->toggleReaction(replyEventId, text);
|
||||
room->toggleReaction(chatBarCache->replyId(), text);
|
||||
return QString();
|
||||
},
|
||||
false,
|
||||
@@ -423,7 +423,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("ban"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
auto parts = text.split(QLatin1String(" "));
|
||||
static const QRegularExpression mxidRegex(
|
||||
QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
|
||||
@@ -462,7 +462,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("unban"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
static const QRegularExpression mxidRegex(
|
||||
QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
|
||||
auto regexMatch = mxidRegex.match(text);
|
||||
@@ -495,7 +495,7 @@ QVector<ActionsModel::Action> actions{
|
||||
},
|
||||
Action{
|
||||
QStringLiteral("kick"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
auto parts = text.split(QLatin1String(" "));
|
||||
static const QRegularExpression mxidRegex(
|
||||
QStringLiteral(R"((^|[][[:space:](){}`'";])([!#@][-a-z0-9_=#/.]{1,252}:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?))"));
|
||||
@@ -574,7 +574,7 @@ QHash<int, QByteArray> ActionsModel::roleNames() const
|
||||
};
|
||||
}
|
||||
|
||||
QVector<Action> &ActionsModel::allActions() const
|
||||
QList<Action> &ActionsModel::allActions() const
|
||||
{
|
||||
return actions;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <QAbstractListModel>
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
|
||||
class ChatBarCache;
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
@@ -28,7 +29,7 @@ public:
|
||||
/**
|
||||
* @brief The function to execute when the action is triggered.
|
||||
*/
|
||||
std::function<QString(const QString &, NeoChatRoom *)> handle;
|
||||
std::function<QString(const QString &, NeoChatRoom *, ChatBarCache *)> handle;
|
||||
/**
|
||||
* @brief Whether the action is a message type action.
|
||||
*
|
||||
@@ -87,7 +88,7 @@ public:
|
||||
/**
|
||||
* @brief Return a vector with all supported actions.
|
||||
*/
|
||||
QVector<Action> &allActions() const;
|
||||
QList<Action> &allActions() const;
|
||||
|
||||
private:
|
||||
ActionsModel() = default;
|
||||
|
||||
@@ -98,6 +98,6 @@ Q_SIGNALS:
|
||||
|
||||
private:
|
||||
void fetchDevices();
|
||||
QVector<Quotient::Device> m_devices;
|
||||
QList<Quotient::Device> m_devices;
|
||||
QPointer<Quotient::Connection> m_connection;
|
||||
};
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
EmojiModel::EmojiModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, m_config(KSharedConfig::openStateConfig())
|
||||
, m_configGroup(KConfigGroup(m_config, QStringLiteral("Editor")))
|
||||
{
|
||||
if (_emojis.isEmpty()) {
|
||||
#include "emojis.h"
|
||||
@@ -61,9 +63,9 @@ QHash<int, QByteArray> EmojiModel::roleNames() const
|
||||
return {{ShortNameRole, "shortName"}, {UnicodeRole, "unicode"}};
|
||||
}
|
||||
|
||||
QVariantList EmojiModel::history() const
|
||||
QStringList EmojiModel::lastUsedEmojis() const
|
||||
{
|
||||
return m_settings.value(QStringLiteral("Editor/emojis"), QVariantList()).toList();
|
||||
return m_configGroup.readEntry(QStringLiteral("lastUsedEmojis"), QStringList());
|
||||
}
|
||||
|
||||
QVariantList EmojiModel::filterModel(const QString &filter, bool limit)
|
||||
@@ -93,19 +95,21 @@ QVariantList EmojiModel::filterModelNoCustom(const QString &filter, bool limit)
|
||||
|
||||
void EmojiModel::emojiUsed(const QVariant &modelData)
|
||||
{
|
||||
QVariantList list = history();
|
||||
auto list = lastUsedEmojis();
|
||||
const auto emoji = modelData.value<Emoji>();
|
||||
|
||||
auto it = list.begin();
|
||||
while (it != list.end()) {
|
||||
if ((*it).value<Emoji>().unicode == modelData.value<Emoji>().unicode) {
|
||||
if (*it == emoji.shortName) {
|
||||
it = list.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
list.push_front(modelData);
|
||||
m_settings.setValue(QStringLiteral("Editor/emojis"), list);
|
||||
list.push_front(emoji.shortName);
|
||||
|
||||
m_configGroup.writeEntry(QStringLiteral("lastUsedEmojis"), list);
|
||||
|
||||
Q_EMIT historyChanged();
|
||||
}
|
||||
@@ -113,11 +117,11 @@ void EmojiModel::emojiUsed(const QVariant &modelData)
|
||||
QVariantList EmojiModel::emojis(Category category) const
|
||||
{
|
||||
if (category == History) {
|
||||
return history();
|
||||
return emojiHistory();
|
||||
}
|
||||
if (category == HistoryNoCustom) {
|
||||
QVariantList list;
|
||||
for (const auto &e : history()) {
|
||||
for (const auto &e : emojiHistory()) {
|
||||
auto emoji = qvariant_cast<Emoji>(e);
|
||||
if (!emoji.isCustom) {
|
||||
list.append(e);
|
||||
@@ -217,4 +221,19 @@ QVariantList EmojiModel::categoriesWithCustom() const
|
||||
return cats;
|
||||
}
|
||||
|
||||
QVariantList EmojiModel::emojiHistory() const
|
||||
{
|
||||
QVariantList list;
|
||||
for (const auto &historicEmoji : lastUsedEmojis()) {
|
||||
for (const auto &emojiCategory : _emojis) {
|
||||
for (const auto &emoji : emojiCategory) {
|
||||
if (qvariant_cast<Emoji>(emoji).shortName == historicEmoji) {
|
||||
list.append(emoji);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
#include "moc_emojimodel.cpp"
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <KConfigGroup>
|
||||
#include <KSharedConfig>
|
||||
#include <QAbstractListModel>
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QSettings>
|
||||
|
||||
struct Emoji {
|
||||
Emoji(QString unicode, QString shortname, bool isCustom = false)
|
||||
@@ -23,21 +24,6 @@ struct Emoji {
|
||||
}
|
||||
Emoji() = default;
|
||||
|
||||
friend QDataStream &operator<<(QDataStream &arch, const Emoji &object)
|
||||
{
|
||||
arch << object.unicode;
|
||||
arch << object.shortName;
|
||||
return arch;
|
||||
}
|
||||
|
||||
friend QDataStream &operator>>(QDataStream &arch, Emoji &object)
|
||||
{
|
||||
arch >> object.unicode;
|
||||
arch >> object.shortName;
|
||||
object.isCustom = object.unicode.startsWith(QStringLiteral("image://"));
|
||||
return arch;
|
||||
}
|
||||
|
||||
QString unicode;
|
||||
QString shortName;
|
||||
QString description;
|
||||
@@ -63,11 +49,6 @@ class EmojiModel : public QAbstractListModel
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
/**
|
||||
* @brief Return a list of recently used emojis.
|
||||
*/
|
||||
Q_PROPERTY(QVariantList history READ history NOTIFY historyChanged)
|
||||
|
||||
/**
|
||||
* @brief Return a list of emoji categories.
|
||||
*
|
||||
@@ -177,7 +158,11 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE QVariantList tones(const QString &baseEmoji) const;
|
||||
|
||||
Q_INVOKABLE QVariantList history() const;
|
||||
/**
|
||||
* @brief Return a list of the last used emoji shortnames
|
||||
*/
|
||||
QStringList lastUsedEmojis() const;
|
||||
|
||||
QVariantList categories() const;
|
||||
QVariantList categoriesWithCustom() const;
|
||||
|
||||
@@ -190,7 +175,10 @@ public Q_SLOTS:
|
||||
private:
|
||||
static QHash<Category, QVariantList> _emojis;
|
||||
|
||||
// TODO: Port away from QSettings
|
||||
QSettings m_settings;
|
||||
/// Returns QVariants containing the last used Emojis
|
||||
QVariantList emojiHistory() const;
|
||||
|
||||
KSharedConfig::Ptr m_config;
|
||||
KConfigGroup m_configGroup;
|
||||
EmojiModel(QObject *parent = nullptr);
|
||||
};
|
||||
|
||||
@@ -151,7 +151,7 @@ void ImagePacksModel::setShowEmoticons(bool showEmoticons)
|
||||
m_showEmoticons = showEmoticons;
|
||||
Q_EMIT showEmoticonsChanged();
|
||||
}
|
||||
QVector<Quotient::ImagePackEventContent::ImagePackImage> ImagePacksModel::images(int index)
|
||||
QList<Quotient::ImagePackEventContent::ImagePackImage> ImagePacksModel::images(int index)
|
||||
{
|
||||
if (index < 0 || index >= m_events.size()) {
|
||||
return {};
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
#include "events/imagepackevent.h"
|
||||
#include <QAbstractListModel>
|
||||
#include <QList>
|
||||
#include <QPointer>
|
||||
#include <QQmlEngine>
|
||||
#include <QVector>
|
||||
|
||||
class NeoChatRoom;
|
||||
|
||||
@@ -86,7 +86,7 @@ public:
|
||||
/**
|
||||
* @brief Return a vector of the images in the pack at the given index.
|
||||
*/
|
||||
[[nodiscard]] QVector<Quotient::ImagePackEventContent::ImagePackImage> images(int index);
|
||||
[[nodiscard]] QList<Quotient::ImagePackEventContent::ImagePackImage> images(int index);
|
||||
|
||||
Q_SIGNALS:
|
||||
void roomChanged();
|
||||
@@ -96,7 +96,7 @@ Q_SIGNALS:
|
||||
|
||||
private:
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
QVector<Quotient::ImagePackEventContent> m_events;
|
||||
QList<Quotient::ImagePackEventContent> m_events;
|
||||
bool m_showStickers = true;
|
||||
bool m_showEmoticons = true;
|
||||
void reloadImages();
|
||||
|
||||
@@ -35,7 +35,7 @@ void KeywordNotificationRuleModel::updateNotificationRules(const QString &type)
|
||||
|
||||
const QJsonObject ruleDataJson = Controller::instance().activeConnection()->accountDataJson("m.push_rules");
|
||||
const Quotient::PushRuleset ruleData = Quotient::fromJson<Quotient::PushRuleset>(ruleDataJson["global"].toObject());
|
||||
const QVector<Quotient::PushRule> contentRules = ruleData.content;
|
||||
const QList<Quotient::PushRule> contentRules = ruleData.content;
|
||||
|
||||
beginResetModel();
|
||||
m_notificationRules.clear();
|
||||
@@ -78,11 +78,11 @@ void KeywordNotificationRuleModel::addKeyword(const QString &keyword)
|
||||
NotificationsManager::instance().initializeKeywordNotificationAction();
|
||||
}
|
||||
|
||||
const QVector<QVariant> actions = NotificationsManager::instance().getKeywordNotificationActions();
|
||||
const QList<QVariant> actions = NotificationsManager::instance().getKeywordNotificationActions();
|
||||
|
||||
auto job = Controller::instance()
|
||||
.activeConnection()
|
||||
->callApi<Quotient::SetPushRuleJob>("global", "content", keyword, actions, "", "", QVector<Quotient::PushCondition>(), keyword);
|
||||
->callApi<Quotient::SetPushRuleJob>("global", "content", keyword, actions, "", "", QList<Quotient::PushCondition>(), keyword);
|
||||
connect(job, &Quotient::BaseJob::success, this, [this, keyword]() {
|
||||
beginInsertRows(QModelIndex(), m_notificationRules.count(), m_notificationRules.count());
|
||||
m_notificationRules.append(keyword);
|
||||
|
||||
@@ -47,6 +47,8 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
|
||||
roles[ReplyDelegateTypeRole] = "replyDelegateType";
|
||||
roles[ReplyDisplayRole] = "replyDisplay";
|
||||
roles[ReplyMediaInfoRole] = "replyMediaInfo";
|
||||
roles[IsThreadedRole] = "isThreaded";
|
||||
roles[ThreadRootRole] = "threadRoot";
|
||||
roles[ShowAuthorRole] = "showAuthor";
|
||||
roles[ShowSectionRole] = "showSection";
|
||||
roles[ReadMarkersRole] = "readMarkers";
|
||||
@@ -98,7 +100,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
room->setDisplayed();
|
||||
|
||||
for (auto event = m_currentRoom->messageEvents().begin(); event != m_currentRoom->messageEvents().end(); ++event) {
|
||||
createEventObjects(&*event->viewAs<RoomMessageEvent>());
|
||||
if (const auto &roomMessageEvent = &*event->viewAs<RoomMessageEvent>()) {
|
||||
createEventObjects(roomMessageEvent);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_currentRoom->timelineSize() < 10 && !room->allHistoryLoaded()) {
|
||||
@@ -118,9 +122,8 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
for (auto &&event : events) {
|
||||
const RoomMessageEvent *message = dynamic_cast<RoomMessageEvent *>(event.get());
|
||||
|
||||
createEventObjects(message);
|
||||
|
||||
if (message != nullptr) {
|
||||
createEventObjects(message);
|
||||
if (NeoChatConfig::self()->showFancyEffects()) {
|
||||
QString planBody = message->plainBody();
|
||||
// snowflake
|
||||
@@ -156,8 +159,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
});
|
||||
connect(m_currentRoom, &Room::aboutToAddHistoricalMessages, this, [this](RoomEventsRange events) {
|
||||
for (auto &event : events) {
|
||||
RoomMessageEvent *message = dynamic_cast<RoomMessageEvent *>(event.get());
|
||||
createEventObjects(message);
|
||||
if (const auto &roomMessageEvent = dynamic_cast<RoomMessageEvent *>(event.get())) {
|
||||
createEventObjects(roomMessageEvent);
|
||||
}
|
||||
}
|
||||
if (rowCount() > 0) {
|
||||
rowBelowInserted = rowCount() - 1; // See #312
|
||||
@@ -226,7 +230,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
}
|
||||
const auto eventIt = m_currentRoom->findInTimeline(eventId);
|
||||
if (eventIt != m_currentRoom->historyEdge()) {
|
||||
createEventObjects(static_cast<const RoomMessageEvent *>(&**eventIt));
|
||||
if (const auto &event = dynamic_cast<const RoomMessageEvent *>(&**eventIt)) {
|
||||
createEventObjects(event);
|
||||
}
|
||||
}
|
||||
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole, Qt::DisplayRole});
|
||||
});
|
||||
@@ -266,7 +272,7 @@ int MessageEventModel::timelineBaseIndex() const
|
||||
return m_currentRoom ? int(m_currentRoom->pendingEvents().size()) : 0;
|
||||
}
|
||||
|
||||
void MessageEventModel::refreshEventRoles(int row, const QVector<int> &roles)
|
||||
void MessageEventModel::refreshEventRoles(int row, const QList<int> &roles)
|
||||
{
|
||||
const auto idx = index(row);
|
||||
Q_EMIT dataChanged(idx, idx, roles);
|
||||
@@ -310,7 +316,7 @@ void MessageEventModel::moveReadMarker(const QString &toEventId)
|
||||
endMoveRows();
|
||||
}
|
||||
|
||||
int MessageEventModel::refreshEventRoles(const QString &id, const QVector<int> &roles)
|
||||
int MessageEventModel::refreshEventRoles(const QString &id, const QList<int> &roles)
|
||||
{
|
||||
// On 64-bit platforms, difference_type for std containers is long long
|
||||
// but Qt uses int throughout its interfaces; hence casting to int below.
|
||||
@@ -586,6 +592,14 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
return eventHandler.getReplyMediaInfo();
|
||||
}
|
||||
|
||||
if (role == IsThreadedRole) {
|
||||
return eventHandler.isThreaded();
|
||||
}
|
||||
|
||||
if (role == ThreadRootRole) {
|
||||
return eventHandler.threadRoot();
|
||||
}
|
||||
|
||||
if (role == ShowAuthorRole) {
|
||||
for (auto r = row + 1; r < rowCount(); ++r) {
|
||||
auto i = index(r);
|
||||
@@ -694,10 +708,6 @@ int MessageEventModel::eventIdToRow(const QString &eventID) const
|
||||
|
||||
void MessageEventModel::createEventObjects(const Quotient::RoomMessageEvent *event)
|
||||
{
|
||||
if (event == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto eventId = event->id();
|
||||
|
||||
EventHandler eventHandler;
|
||||
|
||||
@@ -63,6 +63,9 @@ public:
|
||||
ReplyDisplayRole, /**< The body of the message that was replied to. */
|
||||
ReplyMediaInfoRole, /**< The media info of the message that was replied to. */
|
||||
|
||||
IsThreadedRole,
|
||||
ThreadRootRole,
|
||||
|
||||
ShowAuthorRole, /**< Whether the author's name should be shown. */
|
||||
ShowSectionRole, /**< Whether the section header should be shown. */
|
||||
|
||||
@@ -137,8 +140,8 @@ private:
|
||||
void fetchMore(const QModelIndex &parent) override;
|
||||
|
||||
void refreshLastUserEvents(int baseTimelineRow);
|
||||
void refreshEventRoles(int row, const QVector<int> &roles = {});
|
||||
int refreshEventRoles(const QString &eventId, const QVector<int> &roles = {});
|
||||
void refreshEventRoles(int row, const QList<int> &roles = {});
|
||||
int refreshEventRoles(const QString &eventId, const QList<int> &roles = {});
|
||||
void moveReadMarker(const QString &toEventId);
|
||||
|
||||
void createEventObjects(const Quotient::RoomMessageEvent *event);
|
||||
|
||||
@@ -124,6 +124,20 @@ void PublicRoomListModel::setKeyword(const QString &value)
|
||||
Q_EMIT hasMoreChanged();
|
||||
}
|
||||
|
||||
bool PublicRoomListModel::showOnlySpaces() const
|
||||
{
|
||||
return m_showOnlySpaces;
|
||||
}
|
||||
|
||||
void PublicRoomListModel::setShowOnlySpaces(bool showOnlySpaces)
|
||||
{
|
||||
if (showOnlySpaces == m_showOnlySpaces) {
|
||||
return;
|
||||
}
|
||||
m_showOnlySpaces = showOnlySpaces;
|
||||
Q_EMIT showOnlySpacesChanged();
|
||||
}
|
||||
|
||||
void PublicRoomListModel::next(int count)
|
||||
{
|
||||
if (count < 1) {
|
||||
@@ -136,7 +150,11 @@ void PublicRoomListModel::next(int count)
|
||||
return;
|
||||
}
|
||||
|
||||
job = m_connection->callApi<QueryPublicRoomsJob>(m_server, count, nextBatch, QueryPublicRoomsJob::Filter{m_keyword, {}});
|
||||
QStringList roomTypes;
|
||||
if (m_showOnlySpaces) {
|
||||
roomTypes += QLatin1String("m.space");
|
||||
}
|
||||
job = m_connection->callApi<QueryPublicRoomsJob>(m_server, count, nextBatch, QueryPublicRoomsJob::Filter{m_keyword, roomTypes});
|
||||
Q_EMIT loadingChanged();
|
||||
|
||||
connect(job, &BaseJob::finished, this, [this] {
|
||||
|
||||
@@ -45,6 +45,11 @@ class PublicRoomListModel : public QAbstractListModel
|
||||
*/
|
||||
Q_PROPERTY(QString keyword READ keyword WRITE setKeyword NOTIFY keywordChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether only space rooms should be shown.
|
||||
*/
|
||||
Q_PROPERTY(bool showOnlySpaces READ showOnlySpaces WRITE setShowOnlySpaces NOTIFY showOnlySpacesChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the model has more items to load.
|
||||
*/
|
||||
@@ -103,6 +108,9 @@ public:
|
||||
[[nodiscard]] QString keyword() const;
|
||||
void setKeyword(const QString &value);
|
||||
|
||||
[[nodiscard]] bool showOnlySpaces() const;
|
||||
void setShowOnlySpaces(bool showOnlySpaces);
|
||||
|
||||
[[nodiscard]] bool hasMore() const;
|
||||
|
||||
[[nodiscard]] bool loading() const;
|
||||
@@ -118,12 +126,13 @@ private:
|
||||
Quotient::Connection *m_connection = nullptr;
|
||||
QString m_server;
|
||||
QString m_keyword;
|
||||
bool m_showOnlySpaces = false;
|
||||
|
||||
bool attempted = false;
|
||||
bool m_loading = false;
|
||||
QString nextBatch;
|
||||
|
||||
QVector<Quotient::PublicRoomsChunk> rooms;
|
||||
QList<Quotient::PublicRoomsChunk> rooms;
|
||||
|
||||
Quotient::QueryPublicRoomsJob *job = nullptr;
|
||||
|
||||
@@ -131,6 +140,7 @@ Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
void serverChanged();
|
||||
void keywordChanged();
|
||||
void showOnlySpacesChanged();
|
||||
void hasMoreChanged();
|
||||
void loadingChanged();
|
||||
};
|
||||
|
||||
@@ -106,7 +106,7 @@ void PushRuleModel::updateNotificationRules(const QString &type)
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void PushRuleModel::setRules(QVector<Quotient::PushRule> rules, PushNotificationKind::Kind kind)
|
||||
void PushRuleModel::setRules(QList<Quotient::PushRule> rules, PushNotificationKind::Kind kind)
|
||||
{
|
||||
for (const auto &rule : rules) {
|
||||
QString roomId;
|
||||
@@ -307,8 +307,8 @@ void PushRuleModel::setPushRuleAction(const QString &id, PushNotificationAction:
|
||||
void PushRuleModel::addKeyword(const QString &keyword, const QString &roomId)
|
||||
{
|
||||
PushNotificationKind::Kind kind = PushNotificationKind::Content;
|
||||
const QVector<QVariant> actions = actionToVariant(m_defaultKeywordAction);
|
||||
QVector<Quotient::PushCondition> pushConditions;
|
||||
const QList<QVariant> actions = actionToVariant(m_defaultKeywordAction);
|
||||
QList<Quotient::PushCondition> pushConditions;
|
||||
if (!roomId.isEmpty()) {
|
||||
kind = PushNotificationKind::Override;
|
||||
|
||||
@@ -369,7 +369,7 @@ void PushRuleModel::setNotificationRuleEnabled(const QString &kind, const QStrin
|
||||
|
||||
void PushRuleModel::setNotificationRuleActions(const QString &kind, const QString &ruleId, PushNotificationAction::Action action)
|
||||
{
|
||||
QVector<QVariant> actions;
|
||||
QList<QVariant> actions;
|
||||
if (ruleId == QStringLiteral(".m.rule.call")) {
|
||||
actions = actionToVariant(action, QStringLiteral("ring"));
|
||||
} else {
|
||||
@@ -379,7 +379,7 @@ void PushRuleModel::setNotificationRuleActions(const QString &kind, const QStrin
|
||||
Controller::instance().activeConnection()->callApi<Quotient::SetPushRuleActionsJob>(QStringLiteral("global"), kind, ruleId, actions);
|
||||
}
|
||||
|
||||
PushNotificationAction::Action PushRuleModel::variantToAction(const QVector<QVariant> &actions, bool enabled)
|
||||
PushNotificationAction::Action PushRuleModel::variantToAction(const QList<QVariant> &actions, bool enabled)
|
||||
{
|
||||
bool notify = false;
|
||||
bool isNoisy = false;
|
||||
@@ -422,16 +422,16 @@ PushNotificationAction::Action PushRuleModel::variantToAction(const QVector<QVar
|
||||
}
|
||||
}
|
||||
|
||||
QVector<QVariant> PushRuleModel::actionToVariant(PushNotificationAction::Action action, const QString &sound)
|
||||
QList<QVariant> PushRuleModel::actionToVariant(PushNotificationAction::Action action, const QString &sound)
|
||||
{
|
||||
// The caller should never try to set the state to unknown.
|
||||
// It exists only as a default state to diable the settings options until the actual state is retrieved from the server.
|
||||
if (action == PushNotificationAction::Unknown) {
|
||||
Q_ASSERT(false);
|
||||
return QVector<QVariant>();
|
||||
return QList<QVariant>();
|
||||
}
|
||||
|
||||
QVector<QVariant> actions;
|
||||
QList<QVariant> actions;
|
||||
|
||||
if (action != PushNotificationAction::Off) {
|
||||
actions.append(QStringLiteral("notify"));
|
||||
|
||||
@@ -238,14 +238,14 @@ private:
|
||||
PushNotificationAction::Action m_defaultKeywordAction;
|
||||
QList<Rule> m_rules;
|
||||
|
||||
void setRules(QVector<Quotient::PushRule> rules, PushNotificationKind::Kind kind);
|
||||
void setRules(QList<Quotient::PushRule> rules, PushNotificationKind::Kind kind);
|
||||
|
||||
int getRuleIndex(const QString &ruleId) const;
|
||||
PushNotificationSection::Section getSection(Quotient::PushRule rule);
|
||||
|
||||
void setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled);
|
||||
void setNotificationRuleActions(const QString &kind, const QString &ruleId, PushNotificationAction::Action action);
|
||||
PushNotificationAction::Action variantToAction(const QVector<QVariant> &actions, bool enabled);
|
||||
QVector<QVariant> actionToVariant(PushNotificationAction::Action action, const QString &sound = QStringLiteral("default"));
|
||||
PushNotificationAction::Action variantToAction(const QList<QVariant> &actions, bool enabled);
|
||||
QList<QVariant> actionToVariant(PushNotificationAction::Action action, const QString &sound = QStringLiteral("default"));
|
||||
};
|
||||
Q_DECLARE_METATYPE(PushRuleModel *)
|
||||
|
||||
@@ -368,11 +368,14 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
|
||||
if (role == IsChildSpaceRole) {
|
||||
return SpaceHierarchyCache::instance().isChildSpace(room->id());
|
||||
}
|
||||
if (role == ReplacementIdRole) {
|
||||
return room->successorId();
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void RoomListModel::refresh(NeoChatRoom *room, const QVector<int> &roles)
|
||||
void RoomListModel::refresh(NeoChatRoom *room, const QList<int> &roles)
|
||||
{
|
||||
const auto it = std::find(m_rooms.begin(), m_rooms.end(), room);
|
||||
if (it == m_rooms.end()) {
|
||||
|
||||
@@ -79,6 +79,7 @@ public:
|
||||
RoomIdRole, /**< The room matrix ID. */
|
||||
IsSpaceRole, /**< Whether the room is a space. */
|
||||
IsChildSpaceRole, /**< Whether this space is a child of a different space. */
|
||||
ReplacementIdRole, /**< The room id of the room replacing this one, if any. */
|
||||
};
|
||||
Q_ENUM(EventRoles)
|
||||
|
||||
@@ -158,7 +159,7 @@ private Q_SLOTS:
|
||||
void doAddRoom(Quotient::Room *room);
|
||||
void updateRoom(Quotient::Room *room, Quotient::Room *prev);
|
||||
void deleteRoom(Quotient::Room *room);
|
||||
void refresh(NeoChatRoom *room, const QVector<int> &roles = {});
|
||||
void refresh(NeoChatRoom *room, const QList<int> &roles = {});
|
||||
void refreshNotificationCount();
|
||||
void refreshHighlightCount();
|
||||
|
||||
|
||||
@@ -11,12 +11,13 @@
|
||||
|
||||
#include <KConfig>
|
||||
#include <KConfigGroup>
|
||||
#include <KSharedConfig>
|
||||
|
||||
ServerListModel::ServerListModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
KConfig dataResource(QStringLiteral("data"), KConfig::SimpleConfig, QStandardPaths::AppDataLocation);
|
||||
KConfigGroup serverGroup(&dataResource, QStringLiteral("Servers"));
|
||||
const auto stateConfig = KSharedConfig::openStateConfig();
|
||||
const KConfigGroup serverGroup = stateConfig->group(QStringLiteral("Servers"));
|
||||
|
||||
QString domain = Controller::instance().activeConnection()->domain();
|
||||
|
||||
@@ -91,8 +92,8 @@ int ServerListModel::rowCount(const QModelIndex &parent) const
|
||||
|
||||
void ServerListModel::checkServer(const QString &url)
|
||||
{
|
||||
KConfig dataResource(QStringLiteral("data"), KConfig::SimpleConfig, QStandardPaths::AppDataLocation);
|
||||
KConfigGroup serverGroup(&dataResource, QStringLiteral("Servers"));
|
||||
const auto stateConfig = KSharedConfig::openStateConfig();
|
||||
const KConfigGroup serverGroup = stateConfig->group(QStringLiteral("Servers"));
|
||||
|
||||
if (!serverGroup.hasKey(url)) {
|
||||
if (Quotient::isJobPending(m_checkServerJob)) {
|
||||
@@ -108,8 +109,8 @@ void ServerListModel::checkServer(const QString &url)
|
||||
|
||||
void ServerListModel::addServer(const QString &url)
|
||||
{
|
||||
KConfig dataResource(QStringLiteral("data"), KConfig::SimpleConfig, QStandardPaths::AppDataLocation);
|
||||
KConfigGroup serverGroup(&dataResource, QStringLiteral("Servers"));
|
||||
const auto stateConfig = KSharedConfig::openStateConfig();
|
||||
KConfigGroup serverGroup = stateConfig->group(QStringLiteral("Servers"));
|
||||
|
||||
if (!serverGroup.hasKey(url)) {
|
||||
Server newServer = Server{
|
||||
@@ -125,17 +126,21 @@ void ServerListModel::addServer(const QString &url)
|
||||
}
|
||||
|
||||
serverGroup.writeEntry(url, url);
|
||||
stateConfig->sync();
|
||||
}
|
||||
|
||||
void ServerListModel::removeServerAtIndex(int row)
|
||||
{
|
||||
KConfig dataResource(QStringLiteral("data"), KConfig::SimpleConfig, QStandardPaths::AppDataLocation);
|
||||
KConfigGroup serverGroup(&dataResource, QStringLiteral("Servers"));
|
||||
const auto stateConfig = KSharedConfig::openStateConfig();
|
||||
KConfigGroup serverGroup = stateConfig->group(QStringLiteral("Servers"));
|
||||
|
||||
serverGroup.deleteEntry(data(index(row), UrlRole).toString());
|
||||
|
||||
beginRemoveRows(QModelIndex(), row, row);
|
||||
m_servers.removeAt(row);
|
||||
endRemoveRows();
|
||||
|
||||
stateConfig->sync();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ServerListModel::roleNames() const
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "sortfilterroomlistmodel.h"
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include "roomlistmodel.h"
|
||||
#include "spacehierarchycache.h"
|
||||
|
||||
@@ -14,6 +15,10 @@ SortFilterRoomListModel::SortFilterRoomListModel(QObject *parent)
|
||||
connect(this, &SortFilterRoomListModel::filterTextChanged, this, [this]() {
|
||||
invalidateFilter();
|
||||
});
|
||||
connect(this, &SortFilterRoomListModel::sourceModelChanged, this, [this]() {
|
||||
connect(sourceModel(), &QAbstractListModel::rowsInserted, this, &SortFilterRoomListModel::invalidateRowsFilter);
|
||||
connect(sourceModel(), &QAbstractListModel::rowsRemoved, this, &SortFilterRoomListModel::invalidateRowsFilter);
|
||||
});
|
||||
}
|
||||
|
||||
void SortFilterRoomListModel::setRoomSortOrder(SortFilterRoomListModel::RoomSortOrder sortOrder)
|
||||
@@ -78,9 +83,15 @@ bool SortFilterRoomListModel::filterAcceptsRow(int source_row, const QModelIndex
|
||||
{
|
||||
Q_UNUSED(source_parent);
|
||||
|
||||
if (sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::JoinStateRole).toString() == QStringLiteral("upgraded")
|
||||
&& dynamic_cast<RoomListModel *>(sourceModel())
|
||||
->connection()
|
||||
->room(sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::ReplacementIdRole).toString())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool acceptRoom =
|
||||
sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::DisplayNameRole).toString().contains(m_filterText, Qt::CaseInsensitive)
|
||||
&& sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::JoinStateRole).toString() != QStringLiteral("upgraded")
|
||||
&& sourceModel()->data(sourceModel()->index(source_row, 0), RoomListModel::IsSpaceRole).toBool() == false;
|
||||
|
||||
if (m_activeSpaceId.isEmpty()) {
|
||||
|
||||
@@ -12,7 +12,7 @@ SortFilterSpaceListModel::SortFilterSpaceListModel(QObject *parent)
|
||||
sort(0);
|
||||
invalidateFilter();
|
||||
connect(this, &QAbstractProxyModel::sourceModelChanged, this, [this]() {
|
||||
connect(sourceModel(), &QAbstractListModel::dataChanged, this, [this](const QModelIndex &, const QModelIndex &, QVector<int> roles) {
|
||||
connect(sourceModel(), &QAbstractListModel::dataChanged, this, [this](const QModelIndex &, const QModelIndex &, QList<int> roles) {
|
||||
if (roles.contains(RoomListModel::IsChildSpaceRole)) {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
#include "events/imagepackevent.h"
|
||||
#include "neochatroom.h"
|
||||
#include <QAbstractListModel>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QVector>
|
||||
|
||||
class ImagePacksModel;
|
||||
|
||||
@@ -100,7 +100,7 @@ Q_SIGNALS:
|
||||
private:
|
||||
ImagePacksModel *m_model = nullptr;
|
||||
int m_index = 0;
|
||||
QVector<Quotient::ImagePackEventContent::ImagePackImage> m_images;
|
||||
QList<Quotient::ImagePackEventContent::ImagePackImage> m_images;
|
||||
NeoChatRoom *m_room;
|
||||
void reloadImages();
|
||||
};
|
||||
|
||||
@@ -103,7 +103,7 @@ private:
|
||||
|
||||
bool attempted = false;
|
||||
|
||||
QVector<Quotient::SearchUserDirectoryJob::User> users;
|
||||
QList<Quotient::SearchUserDirectoryJob::User> users;
|
||||
|
||||
Quotient::SearchUserDirectoryJob *job = nullptr;
|
||||
};
|
||||
|
||||
@@ -145,7 +145,7 @@ void UserListModel::userRemoved(Quotient::User *user)
|
||||
}
|
||||
}
|
||||
|
||||
void UserListModel::refreshUser(Quotient::User *user, const QVector<int> &roles)
|
||||
void UserListModel::refreshUser(Quotient::User *user, const QList<int> &roles)
|
||||
{
|
||||
auto pos = findUserPos(user);
|
||||
if (pos != m_users.size()) {
|
||||
|
||||
@@ -89,7 +89,7 @@ Q_SIGNALS:
|
||||
private Q_SLOTS:
|
||||
void userAdded(Quotient::User *user);
|
||||
void userRemoved(Quotient::User *user);
|
||||
void refreshUser(Quotient::User *user, const QVector<int> &roles = {});
|
||||
void refreshUser(Quotient::User *user, const QList<int> &roles = {});
|
||||
void refreshAllUsers();
|
||||
|
||||
private:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
IconName=org.kde.neochat
|
||||
Name=NeoChat
|
||||
Name[ar]=نيوتشات
|
||||
Name[ast]=NeoChat
|
||||
Name[az]=NeoChat
|
||||
Name[ca]=NeoChat
|
||||
Name[ca@valencia]=NeoChat
|
||||
|
||||
@@ -158,7 +158,7 @@ void NeoChatConnection::deactivateAccount(const QString &password)
|
||||
|
||||
void NeoChatConnection::createRoom(const QString &name, const QString &topic, const QString &parent, bool setChildParent)
|
||||
{
|
||||
QVector<CreateRoomJob::StateEvent> initialStateEvents;
|
||||
QList<CreateRoomJob::StateEvent> initialStateEvents;
|
||||
if (!parent.isEmpty()) {
|
||||
initialStateEvents.append(CreateRoomJob::StateEvent{
|
||||
"m.space.parent"_ls,
|
||||
@@ -190,7 +190,7 @@ void NeoChatConnection::createRoom(const QString &name, const QString &topic, co
|
||||
|
||||
void NeoChatConnection::createSpace(const QString &name, const QString &topic, const QString &parent, bool setChildParent)
|
||||
{
|
||||
QVector<CreateRoomJob::StateEvent> initialStateEvents;
|
||||
QList<CreateRoomJob::StateEvent> initialStateEvents;
|
||||
if (!parent.isEmpty()) {
|
||||
initialStateEvents.append(CreateRoomJob::StateEvent{
|
||||
"m.space.parent"_ls,
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <Quotient/jobs/downloadfilejob.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "clipboard.h"
|
||||
#include "controller.h"
|
||||
#include "eventhandler.h"
|
||||
@@ -65,6 +66,9 @@ using namespace Quotient;
|
||||
NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinState)
|
||||
: Room(connection, std::move(roomId), joinState)
|
||||
{
|
||||
m_mainCache = new ChatBarCache(this);
|
||||
m_editCache = new ChatBarCache(this);
|
||||
|
||||
connect(connection, &Connection::accountDataChanged, this, &NeoChatRoom::updatePushNotificationState);
|
||||
connect(this, &Room::fileTransferCompleted, this, [this] {
|
||||
setFileUploadingProgress(0);
|
||||
@@ -117,6 +121,8 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
||||
});
|
||||
connect(this, &Room::changed, this, [this] {
|
||||
Q_EMIT canEncryptRoomChanged();
|
||||
Q_EMIT parentIdsChanged();
|
||||
Q_EMIT canonicalParentChanged();
|
||||
});
|
||||
connect(connection, &Connection::capabilitiesLoaded, this, &NeoChatRoom::maxRoomVersionChanged);
|
||||
connect(this, &Room::changed, this, [this]() {
|
||||
@@ -310,7 +316,7 @@ void NeoChatRoom::cacheLastEvent()
|
||||
if (event != nullptr) {
|
||||
auto &roomLastMessageProvider = RoomLastMessageProvider::self();
|
||||
|
||||
auto eventJson = QJsonDocument(event->fullJson()).toJson();
|
||||
auto eventJson = QJsonDocument(event->fullJson()).toJson(QJsonDocument::Compact);
|
||||
roomLastMessageProvider.write(id(), eventJson);
|
||||
|
||||
auto uniqueEvent = loadEvent<RoomEvent>(event->fullJson());
|
||||
@@ -520,20 +526,63 @@ QString msgTypeToString(MessageEventType msgType)
|
||||
}
|
||||
}
|
||||
|
||||
void NeoChatRoom::postMessage(const QString &rawText, const QString &text, MessageEventType type, const QString &replyEventId, const QString &relateToEventId)
|
||||
void NeoChatRoom::postMessage(const QString &rawText,
|
||||
const QString &text,
|
||||
MessageEventType type,
|
||||
const QString &replyEventId,
|
||||
const QString &relateToEventId,
|
||||
const QString &threadRootId)
|
||||
{
|
||||
postHtmlMessage(rawText, text, type, replyEventId, relateToEventId);
|
||||
postHtmlMessage(rawText, text, type, replyEventId, relateToEventId, threadRootId);
|
||||
}
|
||||
|
||||
void NeoChatRoom::postHtmlMessage(const QString &text, const QString &html, MessageEventType type, const QString &replyEventId, const QString &relateToEventId)
|
||||
void NeoChatRoom::postHtmlMessage(const QString &text,
|
||||
const QString &html,
|
||||
MessageEventType type,
|
||||
const QString &replyEventId,
|
||||
const QString &relateToEventId,
|
||||
const QString &threadRootId)
|
||||
{
|
||||
bool isReply = !replyEventId.isEmpty();
|
||||
bool isEdit = !relateToEventId.isEmpty();
|
||||
bool isThread = !threadRootId.isEmpty();
|
||||
const auto replyIt = findInTimeline(replyEventId);
|
||||
if (replyIt == historyEdge()) {
|
||||
isReply = false;
|
||||
}
|
||||
|
||||
if (isThread) {
|
||||
EventHandler eventHandler;
|
||||
eventHandler.setRoom(this);
|
||||
eventHandler.setEvent(&**replyIt);
|
||||
|
||||
const bool isFallingBack = !eventHandler.isThreaded();
|
||||
|
||||
// clang-format off
|
||||
QJsonObject json{
|
||||
{"msgtype"_ls, msgTypeToString(type)},
|
||||
{"body"_ls, text},
|
||||
{"format"_ls, "org.matrix.custom.html"_ls},
|
||||
{"m.relates_to"_ls,
|
||||
QJsonObject {
|
||||
{"rel_type"_ls, "m.thread"_ls},
|
||||
{"event_id"_ls, threadRootId},
|
||||
{"is_falling_back"_ls, isFallingBack},
|
||||
{"m.in_reply_to"_ls,
|
||||
QJsonObject {
|
||||
{"event_id"_ls, replyEventId}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{"formatted_body"_ls, html}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
postJson("m.room.message"_ls, json);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEdit) {
|
||||
QJsonObject json{
|
||||
{"type"_ls, "m.room.message"_ls},
|
||||
@@ -1099,6 +1148,125 @@ void NeoChatRoom::clearInvitationNotification()
|
||||
NotificationsManager::instance().clearInvitationNotification(id());
|
||||
}
|
||||
|
||||
bool NeoChatRoom::hasParent() const
|
||||
{
|
||||
return currentState().eventsOfType("m.space.parent"_ls).size() > 0;
|
||||
}
|
||||
|
||||
QList<QString> NeoChatRoom::parentIds() const
|
||||
{
|
||||
auto parentEvents = currentState().eventsOfType("m.space.parent"_ls);
|
||||
QList<QString> parentIds;
|
||||
for (const auto &parentEvent : parentEvents) {
|
||||
if (parentEvent->contentJson().contains("via"_ls) && !parentEvent->contentPart<QJsonArray>("via"_ls).isEmpty()) {
|
||||
parentIds += parentEvent->stateKey();
|
||||
}
|
||||
}
|
||||
return parentIds;
|
||||
}
|
||||
|
||||
QString NeoChatRoom::canonicalParent() const
|
||||
{
|
||||
auto parentEvents = currentState().eventsOfType("m.space.parent"_ls);
|
||||
for (const auto &parentEvent : parentEvents) {
|
||||
if (parentEvent->contentJson().contains("via"_ls) && !parentEvent->contentPart<QJsonArray>("via"_ls).isEmpty()) {
|
||||
if (parentEvent->contentPart<bool>("canonical"_ls)) {
|
||||
return parentEvent->stateKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void NeoChatRoom::setCanonicalParent(const QString &parentId)
|
||||
{
|
||||
if (!canModifyParent(parentId)) {
|
||||
return;
|
||||
}
|
||||
if (const auto &parent = currentState().get("m.space.parent"_ls, parentId)) {
|
||||
auto content = parent->contentJson();
|
||||
content.insert("canonical"_ls, true);
|
||||
setState("m.space.parent"_ls, parentId, content);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only one canonical parent can exist so make sure others are set false.
|
||||
auto parentEvents = currentState().eventsOfType("m.space.parent"_ls);
|
||||
for (const auto &parentEvent : parentEvents) {
|
||||
if (parentEvent->contentPart<bool>("canonical"_ls) && parentEvent->stateKey() != parentId) {
|
||||
auto content = parentEvent->contentJson();
|
||||
content.insert("canonical"_ls, false);
|
||||
setState("m.space.parent"_ls, parentEvent->stateKey(), content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NeoChatRoom::canModifyParent(const QString &parentId) const
|
||||
{
|
||||
if (!canSendState("m.space.parent"_ls)) {
|
||||
return false;
|
||||
}
|
||||
// If we can't peek the parent we assume that we neither have permission nor is
|
||||
// there an existing space child event for this room.
|
||||
if (auto parent = static_cast<NeoChatRoom *>(connection()->room(parentId))) {
|
||||
if (!parent->isSpace()) {
|
||||
return false;
|
||||
}
|
||||
// If the user is allowed to set space child events in the parent they are
|
||||
// allowed to set the space as a parent (even if a space child event doesn't
|
||||
// exist).
|
||||
if (parent->canSendState("m.space.child"_ls)) {
|
||||
return true;
|
||||
}
|
||||
// If the parent has a space child event the user can set as a parent (even
|
||||
// if they don't have permission to set space child events in that parent).
|
||||
if (parent->currentState().contains("m.space.child"_ls, id())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NeoChatRoom::addParent(const QString &parentId, bool canonical, bool setParentChild)
|
||||
{
|
||||
if (!canModifyParent(parentId)) {
|
||||
return;
|
||||
}
|
||||
if (canonical) {
|
||||
// Only one canonical parent can exist so make sure others are set false.
|
||||
auto parentEvents = currentState().eventsOfType("m.space.parent"_ls);
|
||||
for (const auto &parentEvent : parentEvents) {
|
||||
if (parentEvent->contentPart<bool>("canonical"_ls)) {
|
||||
auto content = parentEvent->contentJson();
|
||||
content.insert("canonical"_ls, false);
|
||||
setState("m.space.parent"_ls, parentEvent->stateKey(), content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setState("m.space.parent"_ls, parentId, QJsonObject{{"canonical"_ls, canonical}, {"via"_ls, QJsonArray{connection()->domain()}}});
|
||||
|
||||
if (setParentChild) {
|
||||
if (auto parent = static_cast<NeoChatRoom *>(connection()->room(parentId))) {
|
||||
parent->setState("m.space.child"_ls, id(), QJsonObject{{QLatin1String("via"), QJsonArray{connection()->domain()}}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NeoChatRoom::removeParent(const QString &parentId)
|
||||
{
|
||||
if (!canModifyParent(parentId)) {
|
||||
return;
|
||||
}
|
||||
if (!currentState().contains("m.space.parent"_ls, parentId)) {
|
||||
return;
|
||||
}
|
||||
if (auto parent = static_cast<NeoChatRoom *>(connection()->room(parentId))) {
|
||||
setState("m.space.parent"_ls, parentId, {});
|
||||
}
|
||||
}
|
||||
|
||||
bool NeoChatRoom::isSpace()
|
||||
{
|
||||
const auto creationEvent = this->creation();
|
||||
@@ -1109,7 +1277,7 @@ bool NeoChatRoom::isSpace()
|
||||
return creationEvent->roomType() == RoomType::Space;
|
||||
}
|
||||
|
||||
void NeoChatRoom::addChild(const QString &childId, bool setChildParent)
|
||||
void NeoChatRoom::addChild(const QString &childId, bool setChildParent, bool canonical)
|
||||
{
|
||||
if (!isSpace()) {
|
||||
return;
|
||||
@@ -1122,7 +1290,19 @@ void NeoChatRoom::addChild(const QString &childId, bool setChildParent)
|
||||
if (setChildParent) {
|
||||
if (auto child = static_cast<NeoChatRoom *>(connection()->room(childId))) {
|
||||
if (child->canSendState("m.space.parent"_ls)) {
|
||||
child->setState("m.space.parent"_ls, id(), QJsonObject{{"canonical"_ls, true}, {"via"_ls, QJsonArray{connection()->domain()}}});
|
||||
child->setState("m.space.parent"_ls, id(), QJsonObject{{"canonical"_ls, canonical}, {"via"_ls, QJsonArray{connection()->domain()}}});
|
||||
|
||||
if (canonical) {
|
||||
// Only one canonical parent can exist so make sure others are set to false.
|
||||
auto parentEvents = child->currentState().eventsOfType("m.space.parent"_ls);
|
||||
for (const auto &parentEvent : parentEvents) {
|
||||
if (parentEvent->contentPart<bool>("canonical"_ls)) {
|
||||
auto content = parentEvent->contentJson();
|
||||
content.insert("canonical"_ls, false);
|
||||
setState("m.space.parent"_ls, parentEvent->stateKey(), content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1209,7 +1389,7 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
||||
* "don't_notify"
|
||||
* ]
|
||||
*/
|
||||
const QVector<QVariant> actions = {"dont_notify"_ls};
|
||||
const QList<QVariant> actions = {"dont_notify"_ls};
|
||||
/**
|
||||
* Setup the push condition to get all events for the current room
|
||||
* see https://spec.matrix.org/v1.3/client-server-api/#conditions-1
|
||||
@@ -1226,7 +1406,7 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
||||
pushCondition.kind = "event_match"_ls;
|
||||
pushCondition.key = "room_id"_ls;
|
||||
pushCondition.pattern = id();
|
||||
const QVector<PushCondition> conditions = {pushCondition};
|
||||
const QList<PushCondition> conditions = {pushCondition};
|
||||
|
||||
// Add new override rule and make sure it's enabled
|
||||
auto job = Controller::instance()
|
||||
@@ -1253,9 +1433,9 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
||||
* "don't_notify"
|
||||
* ]
|
||||
*/
|
||||
const QVector<QVariant> actions = {"dont_notify"_ls};
|
||||
const QList<QVariant> actions = {"dont_notify"_ls};
|
||||
// No conditions for a room rule
|
||||
const QVector<PushCondition> conditions;
|
||||
const QList<PushCondition> conditions;
|
||||
|
||||
auto setJob = Controller::instance()
|
||||
.activeConnection()
|
||||
@@ -1285,9 +1465,9 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
||||
QJsonObject tweaks;
|
||||
tweaks.insert("set_tweak"_ls, "sound"_ls);
|
||||
tweaks.insert("value"_ls, "default"_ls);
|
||||
const QVector<QVariant> actions = {"notify"_ls, tweaks};
|
||||
const QList<QVariant> actions = {"notify"_ls, tweaks};
|
||||
// No conditions for a room rule
|
||||
const QVector<PushCondition> conditions;
|
||||
const QList<PushCondition> conditions;
|
||||
|
||||
// Add new room rule and make sure enabled
|
||||
auto setJob = Controller::instance()
|
||||
@@ -1438,128 +1618,14 @@ void NeoChatRoom::copyEventMedia(const QString &eventId)
|
||||
}
|
||||
}
|
||||
|
||||
QString NeoChatRoom::chatBoxText() const
|
||||
ChatBarCache *NeoChatRoom::mainCache() const
|
||||
{
|
||||
return m_chatBoxText;
|
||||
return m_mainCache;
|
||||
}
|
||||
|
||||
void NeoChatRoom::setChatBoxText(const QString &text)
|
||||
ChatBarCache *NeoChatRoom::editCache() const
|
||||
{
|
||||
m_chatBoxText = text;
|
||||
Q_EMIT chatBoxTextChanged();
|
||||
}
|
||||
|
||||
QString NeoChatRoom::editText() const
|
||||
{
|
||||
return m_editText;
|
||||
}
|
||||
|
||||
void NeoChatRoom::setEditText(const QString &text)
|
||||
{
|
||||
m_editText = text;
|
||||
Q_EMIT editTextChanged();
|
||||
}
|
||||
|
||||
QString NeoChatRoom::chatBoxReplyId() const
|
||||
{
|
||||
return m_chatBoxReplyId;
|
||||
}
|
||||
|
||||
void NeoChatRoom::setChatBoxReplyId(const QString &replyId)
|
||||
{
|
||||
if (replyId == m_chatBoxReplyId) {
|
||||
return;
|
||||
}
|
||||
m_chatBoxReplyId = replyId;
|
||||
Q_EMIT chatBoxReplyIdChanged();
|
||||
}
|
||||
|
||||
QString NeoChatRoom::chatBoxEditId() const
|
||||
{
|
||||
return m_chatBoxEditId;
|
||||
}
|
||||
|
||||
void NeoChatRoom::setChatBoxEditId(const QString &editId)
|
||||
{
|
||||
if (editId == m_chatBoxEditId) {
|
||||
return;
|
||||
}
|
||||
m_chatBoxEditId = editId;
|
||||
Q_EMIT chatBoxEditIdChanged();
|
||||
}
|
||||
|
||||
QVariantMap NeoChatRoom::chatBoxReplyUser() const
|
||||
{
|
||||
if (m_chatBoxReplyId.isEmpty()) {
|
||||
return emptyUser;
|
||||
}
|
||||
return getUser(user((*findInTimeline(m_chatBoxReplyId))->senderId()));
|
||||
}
|
||||
|
||||
QString NeoChatRoom::chatBoxReplyMessage() const
|
||||
{
|
||||
if (m_chatBoxReplyId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
EventHandler eventhandler;
|
||||
eventhandler.setRoom(this);
|
||||
eventhandler.setEvent(&**findInTimeline(m_chatBoxReplyId));
|
||||
return eventhandler.getPlainBody();
|
||||
}
|
||||
|
||||
QVariantMap NeoChatRoom::chatBoxEditUser() const
|
||||
{
|
||||
if (m_chatBoxEditId.isEmpty()) {
|
||||
return emptyUser;
|
||||
}
|
||||
return getUser(user((*findInTimeline(m_chatBoxEditId))->senderId()));
|
||||
}
|
||||
|
||||
QString NeoChatRoom::chatBoxEditMessage() const
|
||||
{
|
||||
if (m_chatBoxEditId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
EventHandler eventhandler;
|
||||
eventhandler.setRoom(this);
|
||||
if (auto event = findInTimeline(m_chatBoxEditId); event != historyEdge()) {
|
||||
eventhandler.setEvent(&**event);
|
||||
return eventhandler.getPlainBody();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString NeoChatRoom::chatBoxAttachmentPath() const
|
||||
{
|
||||
return m_chatBoxAttachmentPath;
|
||||
}
|
||||
|
||||
void NeoChatRoom::setChatBoxAttachmentPath(const QString &attachmentPath)
|
||||
{
|
||||
m_chatBoxAttachmentPath = attachmentPath;
|
||||
Q_EMIT chatBoxAttachmentPathChanged();
|
||||
}
|
||||
|
||||
QVector<Mention> *NeoChatRoom::mentions()
|
||||
{
|
||||
return &m_mentions;
|
||||
}
|
||||
|
||||
QVector<Mention> *NeoChatRoom::editMentions()
|
||||
{
|
||||
return &m_editMentions;
|
||||
}
|
||||
|
||||
QString NeoChatRoom::savedText() const
|
||||
{
|
||||
return m_savedText;
|
||||
}
|
||||
|
||||
void NeoChatRoom::setSavedText(const QString &savedText)
|
||||
{
|
||||
m_savedText = savedText;
|
||||
return m_editCache;
|
||||
}
|
||||
|
||||
void NeoChatRoom::replyLastMessage()
|
||||
@@ -1588,7 +1654,7 @@ void NeoChatRoom::replyLastMessage()
|
||||
// For any message that isn't an edit return the id of the current message
|
||||
eventId = (*it)->id();
|
||||
}
|
||||
setChatBoxReplyId(eventId);
|
||||
mainCache()->setReplyId(eventId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1622,7 +1688,7 @@ void NeoChatRoom::editLastMessage()
|
||||
// For any message that isn't an edit return the id of the current message
|
||||
eventId = (*it)->id();
|
||||
}
|
||||
setChatBoxEditId(eventId);
|
||||
editCache()->setEditId(eventId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace Quotient
|
||||
class User;
|
||||
}
|
||||
|
||||
class ChatBarCache;
|
||||
|
||||
class PushNotificationState : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -40,17 +42,6 @@ public:
|
||||
Q_ENUM(State)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Defines a user mention in the current chat or edit text.
|
||||
*/
|
||||
struct Mention {
|
||||
QTextCursor cursor; /**< Contains the mention's text and position in the text. */
|
||||
QString text; /**< The inserted text of the mention. */
|
||||
int start = 0; /**< Start position of the mention. */
|
||||
int position = 0; /**< End position of the mention. */
|
||||
QString id; /**< The id the mention (used to create link when sending the message). */
|
||||
};
|
||||
|
||||
/**
|
||||
* @class NeoChatRoom
|
||||
*
|
||||
@@ -126,6 +117,28 @@ class NeoChatRoom : public Quotient::Room
|
||||
*/
|
||||
Q_PROPERTY(Quotient::User *directChatRemoteUser READ directChatRemoteUser CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The Matrix IDs of this room's parents.
|
||||
*
|
||||
* Empty if no parent space is set.
|
||||
*/
|
||||
Q_PROPERTY(QList<QString> parentIds READ parentIds NOTIFY parentIdsChanged)
|
||||
|
||||
/**
|
||||
* @brief The current canonical parent for the room.
|
||||
*
|
||||
* Empty if no canonical parent is set. The write method can only be used to
|
||||
* set an existing parent as canonical; If you wish to add a new parent and set
|
||||
* it as canonical use the addParent method and pass true to the canonical
|
||||
* parameter.
|
||||
*
|
||||
* Setting will fail if the user doesn't have the required privileges (see
|
||||
* canModifyParent) or if the given room ID is not a parent room.
|
||||
*
|
||||
* @sa canModifyParent, addParent
|
||||
*/
|
||||
Q_PROPERTY(QString canonicalParent READ canonicalParent WRITE setCanonicalParent NOTIFY canonicalParentChanged)
|
||||
|
||||
/**
|
||||
* @brief If the room is a space.
|
||||
*/
|
||||
@@ -289,94 +302,14 @@ class NeoChatRoom : public Quotient::Room
|
||||
Q_PROPERTY(int spaceParentPowerLevel READ spaceParentPowerLevel WRITE setSpaceParentPowerLevel NOTIFY spaceParentPowerLevelChanged)
|
||||
|
||||
/**
|
||||
* @brief The current text in the chatbox for the room.
|
||||
*
|
||||
* Due to problems with QTextDocument, unlike the other properties here,
|
||||
* chatBoxText is *not* used to store the text when switching rooms.
|
||||
* @brief The cache for the main chat bar in the room.
|
||||
*/
|
||||
Q_PROPERTY(QString chatBoxText READ chatBoxText WRITE setChatBoxText NOTIFY chatBoxTextChanged)
|
||||
Q_PROPERTY(ChatBarCache *mainCache READ mainCache CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The text for any message currently being edited in the room.
|
||||
* @brief The cache for the edit chat bar in the room.
|
||||
*/
|
||||
Q_PROPERTY(QString editText READ editText WRITE setEditText NOTIFY editTextChanged)
|
||||
|
||||
/**
|
||||
* @brief The event id of a message being replied to.
|
||||
*
|
||||
* Will be QString() if not replying to a message.
|
||||
*/
|
||||
Q_PROPERTY(QString chatBoxReplyId READ chatBoxReplyId WRITE setChatBoxReplyId NOTIFY chatBoxReplyIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The event id of a message being edited.
|
||||
*
|
||||
* Will be QString() if not editing to a message.
|
||||
*/
|
||||
Q_PROPERTY(QString chatBoxEditId READ chatBoxEditId WRITE setChatBoxEditId NOTIFY chatBoxEditIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Get the user for the message being replied to.
|
||||
*
|
||||
* This is different to getting a Quotient::User object
|
||||
* as neither of those can provide details like the displayName or avatarMediaId
|
||||
* without the room context as these can vary from room to room.
|
||||
*
|
||||
* Returns an empty user if not replying to a message.
|
||||
*
|
||||
* The user QVariantMap has the following properties:
|
||||
* - isLocalUser - Whether the user is the local user.
|
||||
* - id - The matrix ID of the user.
|
||||
* - displayName - Display name in the context of this room.
|
||||
* - avatarSource - The mxc URL for the user's avatar in the current room.
|
||||
* - avatarMediaId - Avatar id in the context of this room.
|
||||
* - color - Color for the user.
|
||||
* - object - The Quotient::User object for the user.
|
||||
*
|
||||
* @sa getUser, Quotient::User
|
||||
*/
|
||||
Q_PROPERTY(QVariantMap chatBoxReplyUser READ chatBoxReplyUser NOTIFY chatBoxReplyIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The content of the message being replied to.
|
||||
*
|
||||
* Will be QString() if not replying to a message.
|
||||
*/
|
||||
Q_PROPERTY(QString chatBoxReplyMessage READ chatBoxReplyMessage NOTIFY chatBoxReplyIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Get the user for the message being edited.
|
||||
*
|
||||
* This is different to getting a Quotient::User object
|
||||
* as neither of those can provide details like the displayName or avatarMediaId
|
||||
* without the room context as these can vary from room to room.
|
||||
*
|
||||
* Returns an empty user if not replying to a message.
|
||||
*
|
||||
* The user QVariantMap has the following properties:
|
||||
* - isLocalUser - Whether the user is the local user.
|
||||
* - id - The matrix ID of the user.
|
||||
* - displayName - Display name in the context of this room.
|
||||
* - avatarSource - The mxc URL for the user's avatar in the current room.
|
||||
* - avatarMediaId - Avatar id in the context of this room.
|
||||
* - color - Color for the user.
|
||||
* - object - The Quotient::User object for the user.
|
||||
*
|
||||
* @sa getUser, Quotient::User
|
||||
*/
|
||||
Q_PROPERTY(QVariantMap chatBoxEditUser READ chatBoxEditUser NOTIFY chatBoxEditIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The content of the message being edited.
|
||||
*
|
||||
* Will be QString() if not editing a message.
|
||||
*/
|
||||
Q_PROPERTY(QString chatBoxEditMessage READ chatBoxEditMessage NOTIFY chatBoxEditIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The file path of the attachment to be sent.
|
||||
*/
|
||||
Q_PROPERTY(QString chatBoxAttachmentPath READ chatBoxAttachmentPath WRITE setChatBoxAttachmentPath NOTIFY chatBoxAttachmentPathChanged)
|
||||
Q_PROPERTY(ChatBarCache *editCache READ editCache CONSTANT)
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -587,10 +520,60 @@ public:
|
||||
|
||||
Quotient::User *directChatRemoteUser() const;
|
||||
|
||||
/**
|
||||
* @brief Whether this room has one or more parent spaces set.
|
||||
*/
|
||||
Q_INVOKABLE bool hasParent() const;
|
||||
|
||||
QList<QString> parentIds() const;
|
||||
|
||||
QString canonicalParent() const;
|
||||
void setCanonicalParent(const QString &parentId);
|
||||
|
||||
/**
|
||||
* @brief Whether the local user has permission to set the given space as a parent.
|
||||
*
|
||||
* @note This follows the rules determined in the Matrix spec
|
||||
* https://spec.matrix.org/v1.7/client-server-api/#mspaceparent-relationships
|
||||
*/
|
||||
Q_INVOKABLE bool canModifyParent(const QString &parentId) const;
|
||||
|
||||
/**
|
||||
* @brief Add the given room as a parent.
|
||||
*
|
||||
* Will fail if the user doesn't have the required privileges (see
|
||||
* canModifyParent()).
|
||||
*
|
||||
* @sa canModifyParent()
|
||||
*/
|
||||
Q_INVOKABLE void addParent(const QString &parentId, bool canonical = false, bool setParentChild = false);
|
||||
|
||||
/**
|
||||
* @brief Remove the given room as a parent.
|
||||
*
|
||||
* Will fail if the user doesn't have the required privileges (see
|
||||
* canModifyParent()).
|
||||
*
|
||||
* @sa canModifyParent()
|
||||
*/
|
||||
Q_INVOKABLE void removeParent(const QString &parentId);
|
||||
|
||||
[[nodiscard]] bool isSpace();
|
||||
|
||||
Q_INVOKABLE void addChild(const QString &childId, bool setChildParent = false);
|
||||
/**
|
||||
* @brief Add the given room as a child.
|
||||
*
|
||||
* Will fail if the user doesn't have the required privileges or this room is
|
||||
* not a space.
|
||||
*/
|
||||
Q_INVOKABLE void addChild(const QString &childId, bool setChildParent = false, bool canonical = false);
|
||||
|
||||
/**
|
||||
* @brief Remove the given room as a child.
|
||||
*
|
||||
* Will fail if the user doesn't have the required privileges or this room is
|
||||
* not a space.
|
||||
*/
|
||||
Q_INVOKABLE void removeChild(const QString &childId, bool unsetChildParent = false);
|
||||
|
||||
bool isInvite() const;
|
||||
@@ -733,46 +716,9 @@ public:
|
||||
[[nodiscard]] int spaceParentPowerLevel() const;
|
||||
void setSpaceParentPowerLevel(const int &newPowerLevel);
|
||||
|
||||
QString chatBoxText() const;
|
||||
void setChatBoxText(const QString &text);
|
||||
ChatBarCache *mainCache() const;
|
||||
|
||||
QString editText() const;
|
||||
void setEditText(const QString &text);
|
||||
|
||||
QString chatBoxReplyId() const;
|
||||
void setChatBoxReplyId(const QString &replyId);
|
||||
|
||||
QVariantMap chatBoxReplyUser() const;
|
||||
QString chatBoxReplyMessage() const;
|
||||
|
||||
QString chatBoxEditId() const;
|
||||
void setChatBoxEditId(const QString &editId);
|
||||
|
||||
QVariantMap chatBoxEditUser() const;
|
||||
QString chatBoxEditMessage() const;
|
||||
|
||||
QString chatBoxAttachmentPath() const;
|
||||
void setChatBoxAttachmentPath(const QString &attachmentPath);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the mentions for the current chatbox text.
|
||||
*/
|
||||
QVector<Mention> *mentions();
|
||||
|
||||
/**
|
||||
* @brief Retrieve the mentions for the current edit text.
|
||||
*/
|
||||
QVector<Mention> *editMentions();
|
||||
|
||||
/**
|
||||
* @brief Get the saved chatbox text for the room.
|
||||
*/
|
||||
QString savedText() const;
|
||||
|
||||
/**
|
||||
* @brief Save the chatbox text for the room.
|
||||
*/
|
||||
void setSavedText(const QString &savedText);
|
||||
ChatBarCache *editCache() const;
|
||||
|
||||
/**
|
||||
* @brief Reply to the last message sent in the timeline.
|
||||
@@ -844,14 +790,9 @@ private:
|
||||
|
||||
std::unique_ptr<Quotient::RoomEvent> m_cachedEvent;
|
||||
|
||||
QString m_chatBoxText;
|
||||
QString m_editText;
|
||||
QString m_chatBoxReplyId;
|
||||
QString m_chatBoxEditId;
|
||||
QString m_chatBoxAttachmentPath;
|
||||
QVector<Mention> m_mentions;
|
||||
QVector<Mention> m_editMentions;
|
||||
QString m_savedText;
|
||||
ChatBarCache *m_mainCache;
|
||||
ChatBarCache *m_editCache;
|
||||
|
||||
QCache<QString, PollHandler> m_polls;
|
||||
std::vector<Quotient::event_ptr_tt<Quotient::RoomEvent>> m_extraEvents;
|
||||
|
||||
@@ -867,16 +808,13 @@ Q_SIGNALS:
|
||||
void fileUploadingProgressChanged();
|
||||
void backgroundChanged();
|
||||
void readMarkerLoadedChanged();
|
||||
void parentIdsChanged();
|
||||
void canonicalParentChanged();
|
||||
void lastActiveTimeChanged();
|
||||
void isInviteChanged();
|
||||
void displayNameChanged();
|
||||
void pushNotificationStateChanged(PushNotificationState::State state);
|
||||
void showMessage(MessageType messageType, const QString &message);
|
||||
void chatBoxTextChanged();
|
||||
void editTextChanged();
|
||||
void chatBoxReplyIdChanged();
|
||||
void chatBoxEditIdChanged();
|
||||
void chatBoxAttachmentPathChanged();
|
||||
void canEncryptRoomChanged();
|
||||
void joinRuleChanged();
|
||||
void historyVisibilityChanged();
|
||||
@@ -946,7 +884,8 @@ public Q_SLOTS:
|
||||
const QString &cleanedText,
|
||||
Quotient::MessageEventType type = Quotient::MessageEventType::Text,
|
||||
const QString &replyEventId = QString(),
|
||||
const QString &relateToEventId = QString());
|
||||
const QString &relateToEventId = QString(),
|
||||
const QString &threadRootId = QString());
|
||||
|
||||
/**
|
||||
* @brief Send an html message to the room.
|
||||
@@ -961,7 +900,8 @@ public Q_SLOTS:
|
||||
const QString &html,
|
||||
Quotient::MessageEventType type = Quotient::MessageEventType::Text,
|
||||
const QString &replyEventId = QString(),
|
||||
const QString &relateToEventId = QString());
|
||||
const QString &relateToEventId = QString(),
|
||||
const QString &threadRootId = QString());
|
||||
|
||||
/**
|
||||
* @brief Set the room avatar.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
[Desktop Entry]
|
||||
Name=NeoChat
|
||||
Name[ar]=نيوتشات
|
||||
Name[ast]=NeoChat
|
||||
Name[az]=NeoChat
|
||||
Name[ca]=NeoChat
|
||||
Name[ca@valencia]=NeoChat
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.neochat
|
||||
import org.kde.coreaddons
|
||||
|
||||
FormCard.AboutPage {
|
||||
title: i18nc("@title:window", "About NeoChat")
|
||||
aboutData: About
|
||||
aboutData: AboutData
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ QQC2.Menu {
|
||||
QQC2.MenuItem {
|
||||
text: i18n("Logout")
|
||||
icon.name: "list-remove-user"
|
||||
onTriggered: confirmLogoutDialogComponent.createObject(QQC2.ApplicationWindow.overlay).open()
|
||||
onTriggered: confirmLogoutDialogComponent.createObject(applicationWindow().overlay).open()
|
||||
}
|
||||
|
||||
Component {
|
||||
|
||||
@@ -77,7 +77,7 @@ FormCard.FormCardPage {
|
||||
Component {
|
||||
id: confirmLogoutDialogComponent
|
||||
ConfirmLogoutDialog {
|
||||
connection: model.connection
|
||||
connection: accountDelegate.connection
|
||||
onAccepted: {
|
||||
if (AccountRegistry.accountCount === 1) {
|
||||
root.Window.window.close()
|
||||
|
||||
@@ -39,6 +39,7 @@ QQC2.Control {
|
||||
* @brief The current room that user is viewing.
|
||||
*/
|
||||
required property NeoChatRoom currentRoom
|
||||
onCurrentRoomChanged: _private.chatBarCache = currentRoom.mainCache
|
||||
|
||||
/**
|
||||
* @brief The QQC2.TextArea object.
|
||||
@@ -49,6 +50,14 @@ QQC2.Control {
|
||||
|
||||
property NeoChatConnection connection
|
||||
|
||||
/**
|
||||
* @brief The ActionsHandler object to use.
|
||||
*
|
||||
* This is expected to have the correct room set otherwise messages will be sent
|
||||
* to the wrong room.
|
||||
*/
|
||||
required property ActionsHandler actionsHandler
|
||||
|
||||
/**
|
||||
* @brief The list of actions in the ChatBar.
|
||||
*
|
||||
@@ -62,7 +71,7 @@ QQC2.Control {
|
||||
property bool isBusy: root.currentRoom && root.currentRoom.hasFileUploading
|
||||
|
||||
// Matrix does not allow sending attachments in replies
|
||||
visible: root.currentRoom.chatBoxReplyId.length === 0 && root.currentRoom.chatBoxAttachmentPath.length === 0
|
||||
visible: _private.chatBarCache.isReplying && _private.chatBarCache.attachmentPath.length === 0
|
||||
icon.name: "mail-attachment"
|
||||
text: i18n("Attach an image or file")
|
||||
displayHint: Kirigami.DisplayHint.IconOnly
|
||||
@@ -76,7 +85,7 @@ QQC2.Control {
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
root.currentRoom.chatBoxAttachmentPath = path;
|
||||
_private.chatBarCache.attachmentPath = path;
|
||||
})
|
||||
fileDialog.open()
|
||||
}
|
||||
@@ -169,7 +178,7 @@ QQC2.Control {
|
||||
leftPadding: LayoutMirroring.enabled ? actionsRow.width : Kirigami.Units.largeSpacing
|
||||
rightPadding: LayoutMirroring.enabled ? Kirigami.Units.largeSpacing : actionsRow.width + x * 2 + Kirigami.Units.largeSpacing * 2
|
||||
|
||||
placeholderText: root.currentRoom.usesEncryption ? i18n("Send an encrypted message…") : root.currentRoom.chatBoxAttachmentPath.length > 0 ? i18n("Set an attachment caption...") : i18n("Send a message…")
|
||||
placeholderText: root.currentRoom.usesEncryption ? i18n("Send an encrypted message…") : _private.chatBarCache.attachmentPath.length > 0 ? i18n("Set an attachment caption...") : i18n("Send a message…")
|
||||
verticalAlignment: TextEdit.AlignVCenter
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
@@ -189,7 +198,7 @@ QQC2.Control {
|
||||
root.currentRoom.sendTypingNotification(textExists)
|
||||
textExists ? repeatTimer.start() : repeatTimer.stop()
|
||||
}
|
||||
root.currentRoom.chatBoxText = text
|
||||
_private.chatBarCache.text = text
|
||||
}
|
||||
onCursorRectangleChanged: chatBarScrollView.ensureVisible(cursorRectangle)
|
||||
onSelectedTextChanged: {
|
||||
@@ -285,25 +294,25 @@ QQC2.Control {
|
||||
anchors.rightMargin: root.width > chatBarSizeHelper.currentWidth ? 0 : (chatBarScrollView.QQC2.ScrollBar.vertical.visible ? Kirigami.Units.largeSpacing * 3.5 : Kirigami.Units.largeSpacing)
|
||||
|
||||
active: visible
|
||||
visible: root.currentRoom.chatBoxReplyId.length > 0 || root.currentRoom.chatBoxAttachmentPath.length > 0
|
||||
sourceComponent: root.currentRoom.chatBoxReplyId.length > 0 ? replyPane : attachmentPane
|
||||
visible: _private.chatBarCache.isReplying || _private.chatBarCache.attachmentPath.length > 0
|
||||
sourceComponent: _private.chatBarCache.isReplying ? replyPane : attachmentPane
|
||||
}
|
||||
Component {
|
||||
id: replyPane
|
||||
ReplyPane {
|
||||
userName: root.currentRoom.chatBoxReplyUser.displayName
|
||||
userColor: root.currentRoom.chatBoxReplyUser.color
|
||||
userAvatar: root.currentRoom.chatBoxReplyUser.avatarSource
|
||||
text: root.currentRoom.chatBoxReplyMessage
|
||||
userName: _private.chatBarCache.relationUser.displayName
|
||||
userColor: _private.chatBarCache.relationUser.color
|
||||
userAvatar: _private.chatBarCache.relationUser.avatarSource
|
||||
text: _private.chatBarCache.relationMessage
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: attachmentPane
|
||||
AttachmentPane {
|
||||
attachmentPath: root.currentRoom.chatBoxAttachmentPath
|
||||
attachmentPath: _private.chatBarCache.attachmentPath
|
||||
|
||||
onAttachmentCancelled: {
|
||||
root.currentRoom.chatBoxAttachmentPath = "";
|
||||
_private.chatBarCache.attachmentPath = "";
|
||||
root.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
@@ -349,14 +358,14 @@ QQC2.Control {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: (root.width - chatBarSizeHelper.currentWidth) / 2 + Kirigami.Units.largeSpacing + (chatBarScrollView.QQC2.ScrollBar.vertical.visible && !(root.width > chatBarSizeHelper.currentWidth) ? Kirigami.Units.largeSpacing * 2.5 : 0)
|
||||
|
||||
visible: root.currentRoom.chatBoxReplyId.length > 0
|
||||
visible: _private.chatBarCache.isReplying
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
action: Kirigami.Action {
|
||||
text: i18nc("@action:button", "Cancel reply")
|
||||
icon.name: "dialog-close"
|
||||
onTriggered: {
|
||||
root.currentRoom.chatBoxReplyId = "";
|
||||
root.currentRoom.chatBoxAttachmentPath = "";
|
||||
_private.chatBarCache.replyId = "";
|
||||
_private.chatBarCache.attachmentPath = "";
|
||||
root.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
@@ -472,16 +481,16 @@ QQC2.Control {
|
||||
if (localPath.length === 0) {
|
||||
return false;
|
||||
}
|
||||
root.currentRoom.chatBoxAttachmentPath = localPath;
|
||||
_private.chatBarCache.attachmentPath = localPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
function postMessage() {
|
||||
actionsHandler.handleNewMessage();
|
||||
root.actionsHandler.handleMessageEvent(_private.chatBarCache);
|
||||
repeatTimer.stop()
|
||||
root.currentRoom.markAllMessagesAsRead();
|
||||
textField.clear();
|
||||
root.currentRoom.chatBoxReplyId = "";
|
||||
_private.chatBarCache.replyId = "";
|
||||
messageSent()
|
||||
}
|
||||
|
||||
@@ -558,7 +567,7 @@ QQC2.Control {
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
root.currentRoom.chatBoxAttachmentPath = path;
|
||||
_private.chatBarCache.attachmentPath = path;
|
||||
})
|
||||
|
||||
fileDialog.open()
|
||||
@@ -581,7 +590,7 @@ QQC2.Control {
|
||||
if (!Clipboard.saveImage(localPath)) {
|
||||
return;
|
||||
}
|
||||
root.currentRoom.chatBoxAttachmentPath = localPath;
|
||||
_private.chatBarCache.attachmentPath = localPath;
|
||||
attachDialog.close();
|
||||
}
|
||||
}
|
||||
@@ -595,4 +604,10 @@ QQC2.Control {
|
||||
parentWindow: Window.window
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: _private
|
||||
property ChatBarCache chatBarCache
|
||||
onChatBarCacheChanged: documentHandler.chatBarCache = chatBarCache
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,14 @@ ColumnLayout {
|
||||
|
||||
required property NeoChatConnection connection
|
||||
|
||||
/**
|
||||
* @brief The ActionsHandler object to use.
|
||||
*
|
||||
* This is expected to have the correct room set otherwise messages will be sent
|
||||
* to the wrong room.
|
||||
*/
|
||||
required property ActionsHandler actionsHandler
|
||||
|
||||
/**
|
||||
* @brief A message has been sent from the chat bar.
|
||||
*/
|
||||
@@ -75,6 +83,7 @@ ColumnLayout {
|
||||
Layout.preferredHeight: Math.round(implicitHeight)
|
||||
|
||||
currentRoom: root.currentRoom
|
||||
actionsHandler: root.actionsHandler
|
||||
|
||||
FontMetrics {
|
||||
id: chatBarFontMetrics
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user