Compare commits
125 Commits
work/tobia
...
release/24
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e75739cda | ||
|
|
8935e339c3 | ||
|
|
1967247333 | ||
|
|
c68ce91b87 | ||
|
|
1890dfbdac | ||
|
|
1d23264b1f | ||
|
|
2915339b41 | ||
|
|
767cda8c77 | ||
|
|
a0fb6e2e42 | ||
|
|
23b5fb554a | ||
|
|
c00debf1c3 | ||
|
|
8446bd22f0 | ||
|
|
f1e7fa74c2 | ||
|
|
913f8ae91a | ||
|
|
5b250c73ec | ||
|
|
4991c5f771 | ||
|
|
95cb745536 | ||
|
|
d09f597886 | ||
|
|
1efebfb6a4 | ||
|
|
7c65d2653a | ||
|
|
8be3921ba6 | ||
|
|
7b3fa17020 | ||
|
|
8e2d0523c3 | ||
|
|
098966f2f6 | ||
|
|
e0496e73d9 | ||
|
|
a58236647b | ||
|
|
03db319a24 | ||
|
|
cd9ce10fb0 | ||
|
|
c5c47d7b67 | ||
|
|
2af04d5b00 | ||
|
|
45743bc9bc | ||
|
|
af83db6503 | ||
|
|
06b2861e21 | ||
|
|
e54fef7476 | ||
|
|
a96e3d00fc | ||
|
|
67f5c0b1ed | ||
|
|
b097f1c0ec | ||
|
|
1467e43956 | ||
|
|
c8c26a0b23 | ||
|
|
f344a8d690 | ||
|
|
696ce3af5e | ||
|
|
5c220f3c53 | ||
|
|
e7fe65bf57 | ||
|
|
321bc293f3 | ||
|
|
bd984f84ea | ||
|
|
f36bc88745 | ||
|
|
5f9402c1be | ||
|
|
155ca582f8 | ||
|
|
d9eadca3a2 | ||
|
|
da186fd57f | ||
|
|
d04946b471 | ||
|
|
cc823eff64 | ||
|
|
6f36aa3929 | ||
|
|
196344ad49 | ||
|
|
fbe5f1ff6d | ||
|
|
09c016fa8a | ||
|
|
e1448a5478 | ||
|
|
8c8140533b | ||
|
|
409579b287 | ||
|
|
a6a4008944 | ||
|
|
6bc3d55144 | ||
|
|
32400358f6 | ||
|
|
13846d485b | ||
|
|
c8d53b9e79 | ||
|
|
f8ff0dd3ce | ||
|
|
e577af65d3 | ||
|
|
6057e9a34f | ||
|
|
99fdbc1882 | ||
|
|
925d522da7 | ||
|
|
283aa1dc72 | ||
|
|
c66c035dbb | ||
|
|
cce9b967b9 | ||
|
|
7b751a4e6e | ||
|
|
3b0cc5f4fc | ||
|
|
5a15c013ff | ||
|
|
2351d76466 | ||
|
|
f477ae0d5e | ||
|
|
f8d7a969ac | ||
|
|
c4f79ea73a | ||
|
|
9c132f2a7a | ||
|
|
ba2698585f | ||
|
|
c31b716846 | ||
|
|
49df3b2b2f | ||
|
|
8ef41b9c90 | ||
|
|
2398d917f0 | ||
|
|
3aefdb4aed | ||
|
|
9a2c3e1deb | ||
|
|
110d90bb51 | ||
|
|
0a0cde77e6 | ||
|
|
18d92ec475 | ||
|
|
fd69439927 | ||
|
|
ad97af20b7 | ||
|
|
ca85b99fe9 | ||
|
|
0cde5d6168 | ||
|
|
eeddf99ca5 | ||
|
|
09a35b1a7e | ||
|
|
533182ec55 | ||
|
|
70a8842f00 | ||
|
|
ab33d1ca88 | ||
|
|
9e45f22e09 | ||
|
|
6a627dfff0 | ||
|
|
a9f05a7f63 | ||
|
|
4dfd4b68eb | ||
|
|
3786710d81 | ||
|
|
3967b27352 | ||
|
|
714ea8413c | ||
|
|
4097addae9 | ||
|
|
e9ac9deb40 | ||
|
|
3b858ab7d5 | ||
|
|
08807797a5 | ||
|
|
923839d6c7 | ||
|
|
3d4a1d22b0 | ||
|
|
5aa7f499c0 | ||
|
|
40c3519737 | ||
|
|
6ec9cc2475 | ||
|
|
eba34b19ad | ||
|
|
8517636485 | ||
|
|
4a96dae57d | ||
|
|
09f433be45 | ||
|
|
b9901a9167 | ||
|
|
8b27d99d82 | ||
|
|
6b53c4d7b1 | ||
|
|
bd28a7f66d | ||
|
|
0d1c09696d | ||
|
|
aeb4013d26 |
@@ -2,7 +2,7 @@
|
||||
"id": "org.kde.neochat",
|
||||
"branch": "master",
|
||||
"runtime": "org.kde.Platform",
|
||||
"runtime-version": "6.7",
|
||||
"runtime-version": "6.6",
|
||||
"sdk": "org.kde.Sdk",
|
||||
"command": "neochat",
|
||||
"tags": [
|
||||
|
||||
@@ -8,7 +8,7 @@ include:
|
||||
- /gitlab-templates/android-qt6.yml
|
||||
- /gitlab-templates/linux-qt6.yml
|
||||
- /gitlab-templates/windows-qt6.yml
|
||||
# - /gitlab-templates/freebsd-qt6.yml
|
||||
- /gitlab-templates/freebsd-qt6.yml
|
||||
- /gitlab-templates/flatpak.yml
|
||||
- /gitlab-templates/craft-android-qt6-apks.yml
|
||||
- /gitlab-templates/craft-appimage-qt6.yml
|
||||
|
||||
@@ -8,13 +8,13 @@ cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# KDE Applications version, managed by release script.
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "24")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "11")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "05")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "2")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||
|
||||
set(KF_MIN_VERSION "6.4")
|
||||
set(KF_MIN_VERSION "6.0")
|
||||
set(QT_MIN_VERSION "6.5")
|
||||
|
||||
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
|
||||
@@ -60,9 +60,6 @@ set_package_properties(Qt6 PROPERTIES
|
||||
)
|
||||
|
||||
qt_policy(SET QTP0001 NEW)
|
||||
if (QT_KNOWN_POLICY_QTP0004)
|
||||
qt_policy(SET QTP0004 NEW)
|
||||
endif ()
|
||||
|
||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels ColorScheme)
|
||||
set_package_properties(KF6 PROPERTIES
|
||||
@@ -105,7 +102,7 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(QuotientQt6 0.8.2)
|
||||
find_package(QuotientQt6 0.7)
|
||||
set_package_properties(QuotientQt6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
DESCRIPTION "Qt wrapper around Matrix API"
|
||||
|
||||
@@ -38,8 +38,8 @@ Due to the nature of the Matrix specification development NeoChat also supports
|
||||
|
||||
Details where to find stable releases for NeoChat can be found on its [homepage](https://apps.kde.org/neochat).
|
||||
|
||||
Nightly builds for Linux and Windows can be downloaded from [cdn.kde.org](https://cdn.kde.org/ci-builds/network/neochat/).
|
||||
Nightly builds for Android are available from [KDE's nightly F-Droid repository](https://community.kde.org/Android/F-Droid).
|
||||
Nightly builds for linux and windows can be downloaded from [cdn.kde.org](https://cdn.kde.org/ci-builds/network/neochat/).
|
||||
Nightly builds for android are available from [KDE's nightly F-Droid repository](https://community.kde.org/Android/F-Droid).
|
||||
Nightly Flatpaks are available from [KDE's nightly Flatpak repository](https://userbase.kde.org/Tutorials/Flatpak).
|
||||
|
||||
## Building NeoChat
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# SPDX-License-Identifier: MIT
|
||||
# SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
import time
|
||||
|
||||
from appium import webdriver
|
||||
from appium.options.common.base import AppiumOptions
|
||||
from appium.webdriver.common.appiumby import AppiumBy
|
||||
|
||||
|
||||
class CreateRoomTest(unittest.TestCase):
|
||||
|
||||
mockServerProcess: subprocess.Popen
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.mockServerProcess = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), "login-server.py")])
|
||||
options = AppiumOptions()
|
||||
options.set_capability("app", "neochat --ignore-ssl-errors --test")
|
||||
cls.driver = webdriver.Remote(command_executor='http://127.0.0.1:4723', options=options)
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
if not self._outcome.result.wasSuccessful():
|
||||
self.driver.get_screenshot_as_file("failed_test_shot_{}.png".format(self.id()))
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(self):
|
||||
self.mockServerProcess.terminate()
|
||||
self.driver.quit()
|
||||
|
||||
def test_create_room(self):
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="@user:localhost:1234").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Show Menu").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Create a Room").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Name:").send_keys("Super awesome room name")#
|
||||
time.sleep(0.1) # without this, the second half of the text is sent to the topic field?!
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Topic:").send_keys("There are not enough raccoons here")
|
||||
time.sleep(0.1)
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Create Room").click()
|
||||
time.sleep(0.1)
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Super awesome room name").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Show Room Information").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="There are not enough raccoons here")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,78 +0,0 @@
|
||||
{
|
||||
"next_batch": "batch1234",
|
||||
"rooms": {
|
||||
"join": {
|
||||
"!newroom123321:localhost:1234": {
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"type": "m.room.member",
|
||||
"state_key": "@user:localhost:1234",
|
||||
"sender": "@user:localhost:1234",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"event_id": "$event_id_1234_0:localhost:1234",
|
||||
"room_id": "!newroom123321:localhost:1234",
|
||||
"content": {
|
||||
"avatar_url": "",
|
||||
"displayname": "A Display Name",
|
||||
"membership": "join",
|
||||
"reason": "Nothing"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "m.room.name",
|
||||
"state_key": "",
|
||||
"sender": "@user:localhost:1234",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"event_id": "$event_id_1234_1:localhost:1234",
|
||||
"room_id": "!newroom123321:localhost:1234",
|
||||
"content": {
|
||||
"name": "Super awesome room name"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "m.room.topic",
|
||||
"state_key": "",
|
||||
"sender": "@user:localhost:1234",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"event_id": "$event_id_1234_2:localhost:1234",
|
||||
"room_id": "!newroom123321:localhost:1234",
|
||||
"content": {
|
||||
"topic": "There are not enough raccoons here"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"type": "m.room.message",
|
||||
"sender": "@user:localhost:1234",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"event_id": "$event_id_1234_1:localhost:1234",
|
||||
"room_id": "!newroom123321:localhost:1234",
|
||||
"content": {
|
||||
"body": "This is a message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<a href=\"https://matrix.to/#/@user:localhost:1234\">User</a>:",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,6 @@ from flask import Flask, request, abort
|
||||
import os
|
||||
app = Flask(__name__)
|
||||
|
||||
next_sync_payload = ""
|
||||
|
||||
|
||||
@app.route("/_matrix/client/v3/login", methods=["GET"])
|
||||
def login_get():
|
||||
@@ -44,13 +42,8 @@ def load_json(name):
|
||||
|
||||
@app.route("/_matrix/client/r0/sync")
|
||||
def sync():
|
||||
global next_sync_payload
|
||||
result = dict()
|
||||
if len(next_sync_payload) > 0:
|
||||
result = load_json(next_sync_payload)
|
||||
next_sync_payload = ""
|
||||
else:
|
||||
result = load_json("sync_response_no_rooms") if ("login" in request.headers.get("Authorization")) else load_json("sync_response_rooms")
|
||||
|
||||
result = load_json("sync_response_no_rooms") if ("login" in request.headers.get("Authorization")) else load_json("sync_response_rooms")
|
||||
return result
|
||||
|
||||
@app.route("/.well-known/matrix/client")
|
||||
@@ -72,18 +65,6 @@ def upload_keys():
|
||||
reply = dict()
|
||||
return reply
|
||||
|
||||
@app.route("/_matrix/client/v3/createRoom", methods=["POST"])
|
||||
def create_room():
|
||||
global next_sync_payload
|
||||
data = request.get_json()
|
||||
if data["name"] != "Super awesome room name" or data["topic"] != "There are not enough raccoons here":
|
||||
return dict(), 400
|
||||
response = dict()
|
||||
response["room_id"] = "!newroom123321:localhost:1234"
|
||||
next_sync_payload = "sync_response_new_room"
|
||||
return response
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(ssl_context='adhoc', port=1234)
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
|
||||
#include <Quotient/roommember.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
#include <qtestcase.h>
|
||||
|
||||
@@ -51,7 +50,7 @@ void ChatBarCacheTest::empty()
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->member(QString()));
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(nullptr));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
}
|
||||
@@ -65,7 +64,7 @@ void ChatBarCacheTest::noRoom()
|
||||
// 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(), Quotient::RoomMember());
|
||||
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());
|
||||
@@ -81,7 +80,7 @@ void ChatBarCacheTest::badParent()
|
||||
// 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(), Quotient::RoomMember());
|
||||
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());
|
||||
@@ -99,7 +98,7 @@ void ChatBarCacheTest::reply()
|
||||
QCOMPARE(chatBarCache->replyId(), QLatin1String("$153456789:example.org"));
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->member(QLatin1String("@example: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());
|
||||
}
|
||||
@@ -116,7 +115,7 @@ void ChatBarCacheTest::edit()
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), true);
|
||||
QCOMPARE(chatBarCache->editId(), QLatin1String("$153456789:example.org"));
|
||||
QCOMPARE(chatBarCache->relationUser(), room->member(QLatin1String("@example: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());
|
||||
}
|
||||
@@ -133,7 +132,7 @@ void ChatBarCacheTest::attachment()
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->member(QString()));
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(nullptr));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QLatin1String("some/path"));
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"content": {
|
||||
"user_ids": [
|
||||
"@alice:matrix.org",
|
||||
"@bob:kde.org"
|
||||
"@bob:example.com"
|
||||
]
|
||||
},
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
@@ -35,7 +35,7 @@
|
||||
"content": {
|
||||
"$153456789:example.org": {
|
||||
"m.read": {
|
||||
"@alice:example.org": {
|
||||
"@alice:matrix.org": {
|
||||
"ts": 1436451550453
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@bob:kde.org": {
|
||||
"@bob:example.com": {
|
||||
"ts": 1436451550453
|
||||
}
|
||||
}
|
||||
@@ -67,18 +67,6 @@
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@tim2:example.com": {
|
||||
"ts": 1436451550454
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
@@ -148,22 +136,6 @@
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Bob",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "bob:kde.org",
|
||||
"state_key": "@bob:kde.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"displayname": "Look\nat\nme\nI\nput\nnewlines\nin\nmy\ndisplay name",
|
||||
@@ -184,7 +156,7 @@
|
||||
"summary": {
|
||||
"m.heroes": [
|
||||
"@alice:example.com",
|
||||
"@bob:kde.org"
|
||||
"@bob:example.com"
|
||||
],
|
||||
"m.invited_member_count": 0,
|
||||
"m.joined_member_count": 2
|
||||
|
||||
14
autotests/data/test-invalidmatrixtolink-event.json
Normal file
14
autotests/data/test-invalidmatrixtolink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "https://matrix.to/#/@alice:example.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
14
autotests/data/test-invalidmxclink-event.json
Normal file
14
autotests/data/test-invalidmxclink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
14
autotests/data/test-invalidnospacelink-event.json
Normal file
14
autotests/data/test-invalidnospacelink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "testhttps://kde.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
@@ -37,14 +37,16 @@
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"displayname": "Example",
|
||||
"membership": "join"
|
||||
"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": "@example:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
|
||||
@@ -130,23 +130,7 @@
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@alice:matrix.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Bob",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "bob:example.org",
|
||||
"state_key": "@bob:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
|
||||
@@ -51,21 +51,6 @@
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"displayname": "Example",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@example:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
14
autotests/data/test-validplainlink-event.json
Normal file
14
autotests/data/test-validplainlink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "https://kde.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
14
autotests/data/test-validplainwwwlink-event.json
Normal file
14
autotests/data/test-validplainwwwlink-event.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "www.example.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
16
autotests/data/test-validrichlink-event.json
Normal file
16
autotests/data/test-validrichlink-event.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "[Rich Link](https://kde.org)",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<a href=\"https://kde.org\">Rich Link</a>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,8 @@ private Q_SLOTS:
|
||||
|
||||
void eventId();
|
||||
void nullEventId();
|
||||
void author();
|
||||
void nullAuthor();
|
||||
void authorDisplayName();
|
||||
void nullAuthorDisplayName();
|
||||
void singleLineSidplayName();
|
||||
@@ -54,7 +56,6 @@ private Q_SLOTS:
|
||||
void genericBody();
|
||||
void nullGenericBody();
|
||||
void markdownBody();
|
||||
void markdownBodyReply();
|
||||
void subtitle();
|
||||
void nullSubtitle();
|
||||
void mediaInfo();
|
||||
@@ -73,6 +74,8 @@ private Q_SLOTS:
|
||||
void nullThread();
|
||||
void location();
|
||||
void nullLocation();
|
||||
void readMarkers();
|
||||
void nullReadMarkers();
|
||||
};
|
||||
|
||||
void EventHandlerTest::initTestCase()
|
||||
@@ -94,6 +97,33 @@ void EventHandlerTest::nullEventId()
|
||||
QCOMPARE(noEventHandler.getId(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::author()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
auto author = room->user(event->senderId());
|
||||
EventHandler eventHandler(room, event);
|
||||
|
||||
auto eventHandlerAuthor = eventHandler.getAuthor();
|
||||
|
||||
QCOMPARE(eventHandlerAuthor["isLocalUser"_ls], author->id() == room->localUser()->id());
|
||||
QCOMPARE(eventHandlerAuthor["id"_ls], author->id());
|
||||
QCOMPARE(eventHandlerAuthor["displayName"_ls], author->displayname(room));
|
||||
QCOMPARE(eventHandlerAuthor["avatarSource"_ls], room->avatarForMember(author));
|
||||
QCOMPARE(eventHandlerAuthor["avatarMediaId"_ls], author->avatarMediaId(room));
|
||||
QCOMPARE(eventHandlerAuthor["color"_ls], Utils::getUserColor(author->hueF()));
|
||||
QCOMPARE(eventHandlerAuthor["object"_ls], QVariant::fromValue(author));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullAuthor()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getAuthor(), QVariantMap());
|
||||
|
||||
EventHandler noEventHandler(room, nullptr);
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_event set to nullptr. Returning empty user.");
|
||||
QCOMPARE(noEventHandler.getAuthor(), room->getUser(nullptr));
|
||||
}
|
||||
|
||||
void EventHandlerTest::authorDisplayName()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(1).get());
|
||||
@@ -163,7 +193,6 @@ void EventHandlerTest::timeString()
|
||||
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().time(), QLocale::LongFormat));
|
||||
QCOMPARE(eventHandler.getTimeString(true, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
||||
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::LongFormat));
|
||||
QCOMPARE(eventHandler.getTimeString(QStringLiteral("hh:mm")), QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toString(QStringLiteral("hh:mm")));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullTimeString()
|
||||
@@ -272,13 +301,6 @@ void EventHandlerTest::markdownBody()
|
||||
QCOMPARE(eventHandler.getMarkdownBody(), QStringLiteral("This is an example\ntext message"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::markdownBodyReply()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(5).get());
|
||||
|
||||
QCOMPARE(eventHandler.getMarkdownBody(), QStringLiteral("reply"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::subtitle()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||
@@ -363,30 +385,31 @@ void EventHandlerTest::nullReplyId()
|
||||
void EventHandlerTest::replyAuthor()
|
||||
{
|
||||
auto replyEvent = room->messageEvents().at(0).get();
|
||||
auto replyAuthor = room->member(replyEvent->senderId());
|
||||
auto replyAuthor = room->user(replyEvent->senderId());
|
||||
EventHandler eventHandler(room, room->messageEvents().at(5).get());
|
||||
|
||||
auto eventHandlerReplyAuthor = eventHandler.getReplyAuthor();
|
||||
|
||||
QCOMPARE(eventHandlerReplyAuthor.isLocalMember(), replyAuthor.id() == room->localMember().id());
|
||||
QCOMPARE(eventHandlerReplyAuthor.id(), replyAuthor.id());
|
||||
QCOMPARE(eventHandlerReplyAuthor.displayName(), replyAuthor.displayName());
|
||||
QCOMPARE(eventHandlerReplyAuthor.avatarUrl(), replyAuthor.avatarUrl());
|
||||
QCOMPARE(eventHandlerReplyAuthor.avatarMediaId(), replyAuthor.avatarMediaId());
|
||||
QCOMPARE(eventHandlerReplyAuthor.color(), replyAuthor.color());
|
||||
QCOMPARE(eventHandlerReplyAuthor["isLocalUser"_ls], replyAuthor->id() == room->localUser()->id());
|
||||
QCOMPARE(eventHandlerReplyAuthor["id"_ls], replyAuthor->id());
|
||||
QCOMPARE(eventHandlerReplyAuthor["displayName"_ls], replyAuthor->displayname(room));
|
||||
QCOMPARE(eventHandlerReplyAuthor["avatarSource"_ls], room->avatarForMember(replyAuthor));
|
||||
QCOMPARE(eventHandlerReplyAuthor["avatarMediaId"_ls], replyAuthor->avatarMediaId(room));
|
||||
QCOMPARE(eventHandlerReplyAuthor["color"_ls], Utils::getUserColor(replyAuthor->hueF()));
|
||||
QCOMPARE(eventHandlerReplyAuthor["object"_ls], QVariant::fromValue(replyAuthor));
|
||||
|
||||
EventHandler eventHandlerNoAuthor(room, room->messageEvents().at(0).get());
|
||||
QCOMPARE(eventHandlerNoAuthor.getReplyAuthor(), RoomMember());
|
||||
QCOMPARE(eventHandlerNoAuthor.getReplyAuthor(), room->getUser(nullptr));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReplyAuthor()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyAuthor called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReplyAuthor(), RoomMember());
|
||||
QCOMPARE(emptyHandler.getReplyAuthor(), QVariantMap());
|
||||
|
||||
EventHandler noEventHandler(room, nullptr);
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyAuthor called with m_event set to nullptr. Returning empty user.");
|
||||
QCOMPARE(noEventHandler.getReplyAuthor(), RoomMember());
|
||||
QCOMPARE(noEventHandler.getReplyAuthor(), room->getUser(nullptr));
|
||||
}
|
||||
|
||||
void EventHandlerTest::replyBody()
|
||||
@@ -492,5 +515,59 @@ void EventHandlerTest::nullLocation()
|
||||
QCOMPARE(emptyHandler.getLocationAssetType(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::readMarkers()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||
QCOMPARE(eventHandler.hasReadMarkers(), true);
|
||||
|
||||
auto readMarkers = eventHandler.getReadMarkers();
|
||||
|
||||
QCOMPARE(readMarkers.size(), 1);
|
||||
QCOMPARE(readMarkers[0].toMap()["id"_ls], QStringLiteral("@alice:matrix.org"));
|
||||
|
||||
QCOMPARE(eventHandler.getNumberExcessReadMarkers(), QString());
|
||||
QCOMPARE(eventHandler.getReadMarkersString(), QStringLiteral("1 user: @alice:matrix.org"));
|
||||
|
||||
EventHandler eventHandler2(room, room->messageEvents().at(2).get());
|
||||
QCOMPARE(eventHandler2.hasReadMarkers(), true);
|
||||
|
||||
readMarkers = eventHandler2.getReadMarkers();
|
||||
|
||||
QCOMPARE(readMarkers.size(), 5);
|
||||
|
||||
QCOMPARE(eventHandler2.getNumberExcessReadMarkers(), QStringLiteral("+ 1"));
|
||||
// There are no guarantees on the order of the users it will be different every time so don't match the whole string.
|
||||
QCOMPARE(eventHandler2.getReadMarkersString().startsWith(QStringLiteral("6 users:")), true);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReadMarkers()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "hasReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.hasReadMarkers(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReadMarkers(), QVariantList());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getNumberExcessReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getNumberExcessReadMarkers(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkersString called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReadMarkersString(), QString());
|
||||
|
||||
EventHandler noEventHandler(room, nullptr);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "hasReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.hasReadMarkers(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReadMarkers(), QVariantList());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getNumberExcessReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getNumberExcessReadMarkers(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkersString called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReadMarkersString(), QString());
|
||||
}
|
||||
|
||||
QTEST_MAIN(EventHandlerTest)
|
||||
#include "eventhandlertest.moc"
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
|
||||
#include "linkpreviewer.h"
|
||||
|
||||
#include "utils.h"
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
@@ -29,9 +30,6 @@ private Q_SLOTS:
|
||||
void linkPreviewsMatch_data();
|
||||
void linkPreviewsMatch();
|
||||
|
||||
void multipleLinkPreviewsMatch_data();
|
||||
void multipleLinkPreviewsMatch();
|
||||
|
||||
void linkPreviewsReject_data();
|
||||
void linkPreviewsReject();
|
||||
};
|
||||
@@ -44,59 +42,45 @@ void LinkPreviewerTest::initTestCase()
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsMatch_data()
|
||||
{
|
||||
QTest::addColumn<QString>("inputString");
|
||||
QTest::addColumn<QString>("eventSource");
|
||||
QTest::addColumn<QUrl>("testOutputLink");
|
||||
|
||||
QTest::newRow("plainHttps") << QStringLiteral("https://kde.org") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("richHttps") << QStringLiteral("<a href=\"https://kde.org\">Rich Link</a>") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("richHttpsLinkDescription") << QStringLiteral("<a href=\"https://kde.org\">https://kde.org</a>") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("plainHttps") << QStringLiteral("test-validplainlink-event.json") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("richHttps") << QStringLiteral("test-validrichlink-event.json") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("plainWww") << QStringLiteral("test-validplainwwwlink-event.json") << QUrl("www.example.org"_ls);
|
||||
QTest::newRow("multipleHttps") << QStringLiteral("test-multiplelink-event.json") << QUrl("www.example.org"_ls);
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsMatch()
|
||||
{
|
||||
QFETCH(QString, inputString);
|
||||
QFETCH(QString, eventSource);
|
||||
QFETCH(QUrl, testOutputLink);
|
||||
|
||||
auto link = LinkPreviewer::linkPreviews(inputString)[0];
|
||||
auto event = TestUtils::loadEventFromFile<RoomMessageEvent>(eventSource);
|
||||
auto linkPreviewer = LinkPreviewer(LinkPreviewer::linkPreview(event.get()), connection);
|
||||
|
||||
QCOMPARE(link, testOutputLink);
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::multipleLinkPreviewsMatch_data()
|
||||
{
|
||||
QTest::addColumn<QString>("inputString");
|
||||
QTest::addColumn<QList<QUrl>>("testOutputLinks");
|
||||
|
||||
QTest::newRow("multipleHttps") << QStringLiteral("www.example.org https://kde.org") << QList{QUrl("www.example.org"_ls), QUrl("https://kde.org"_ls)};
|
||||
QTest::newRow("multipleHttps1Invalid") << QStringLiteral("www.example.org mxc://example.org/SEsfnsuifSDFSSEF") << QList{QUrl("www.example.org"_ls)};
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::multipleLinkPreviewsMatch()
|
||||
{
|
||||
QFETCH(QString, inputString);
|
||||
QFETCH(QList<QUrl>, testOutputLinks);
|
||||
|
||||
auto links = LinkPreviewer::linkPreviews(inputString);
|
||||
|
||||
QCOMPARE(links, testOutputLinks);
|
||||
QCOMPARE(linkPreviewer.empty(), false);
|
||||
QCOMPARE(linkPreviewer.url(), testOutputLink);
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsReject_data()
|
||||
{
|
||||
QTest::addColumn<QString>("inputString");
|
||||
QTest::addColumn<QString>("eventSource");
|
||||
|
||||
QTest::newRow("mxc") << QStringLiteral("mxc://example.org/SEsfnsuifSDFSSEF");
|
||||
QTest::newRow("matrixTo") << QStringLiteral("https://matrix.to/#/@alice:example.org");
|
||||
QTest::newRow("noSpace") << QStringLiteral("testhttps://kde.org");
|
||||
QTest::newRow("mxc") << QStringLiteral("test-invalidmxclink-event.json");
|
||||
QTest::newRow("matrixTo") << QStringLiteral("test-invalidmatrixtolink-event.json");
|
||||
QTest::newRow("noSpace") << QStringLiteral("test-invalidnospacelink-event.json");
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsReject()
|
||||
{
|
||||
QFETCH(QString, inputString);
|
||||
QFETCH(QString, eventSource);
|
||||
|
||||
auto links = LinkPreviewer::linkPreviews(inputString);
|
||||
auto event = TestUtils::loadEventFromFile<RoomMessageEvent>(eventSource);
|
||||
auto linkPreviewer = LinkPreviewer(LinkPreviewer::linkPreview(event.get()), connection);
|
||||
|
||||
QCOMPARE(links.empty(), true);
|
||||
QCOMPARE(linkPreviewer.empty(), true);
|
||||
QCOMPARE(linkPreviewer.url(), QUrl());
|
||||
}
|
||||
|
||||
QTEST_MAIN(LinkPreviewerTest)
|
||||
|
||||
@@ -52,8 +52,10 @@ void ReactionModelTest::basicReaction()
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::TextContentRole), QStringLiteral("<span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::ReactionRole), QStringLiteral("👍"));
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::ToolTipRole),
|
||||
QStringLiteral("Alice Margatroid reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::HasLocalMember), false);
|
||||
QStringLiteral("@alice:matrix.org reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
auto authorList = QVariantList{room->getUser(room->user(QStringLiteral("@alice:matrix.org")))};
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::AuthorsRole), authorList);
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::HasLocalUser), false);
|
||||
}
|
||||
|
||||
void ReactionModelTest::newReaction()
|
||||
@@ -63,7 +65,7 @@ void ReactionModelTest::newReaction()
|
||||
|
||||
QCOMPARE(model->rowCount(), 1);
|
||||
QCOMPARE(model->data(model->index(0), ReactionModel::ToolTipRole),
|
||||
QStringLiteral("Alice Margatroid reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
QStringLiteral("@alice:matrix.org reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
|
||||
QSignalSpy spy(model, SIGNAL(modelReset()));
|
||||
|
||||
@@ -72,7 +74,7 @@ void ReactionModelTest::newReaction()
|
||||
QCOMPARE(spy.count(), 2); // Once for each of the 2 new reactions.
|
||||
QCOMPARE(model->data(model->index(1), ReactionModel::ReactionRole), QStringLiteral("😆"));
|
||||
QCOMPARE(model->data(model->index(0), ReactionModel::ToolTipRole),
|
||||
QStringLiteral("Alice Margatroid and Bob reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
QStringLiteral("@alice:matrix.org and @bob:example.org reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
|
||||
delete model;
|
||||
}
|
||||
|
||||
@@ -274,7 +274,6 @@ void TextHandlerTest::receiveRichInPlainOut_data()
|
||||
QTest::newRow("ampersand") << QStringLiteral("a & b") << QStringLiteral("a & b");
|
||||
QTest::newRow("quote") << QStringLiteral(""a and b"") << QStringLiteral("\"a and b\"");
|
||||
QTest::newRow("new line") << QStringLiteral("new<br>line") << QStringLiteral("new\nline");
|
||||
QTest::newRow("unescape") << QStringLiteral("can't") << QStringLiteral("can't");
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveRichInPlainOut()
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
<name xml:lang="fi">NeoChat</name>
|
||||
<name xml:lang="fr">NeoChat</name>
|
||||
<name xml:lang="gl">NeoChat</name>
|
||||
<name xml:lang="he">NeoChat</name>
|
||||
<name xml:lang="hu">NeoChat</name>
|
||||
<name xml:lang="ia">Neochat</name>
|
||||
<name xml:lang="id">NeoChat</name>
|
||||
@@ -62,7 +61,6 @@
|
||||
<summary xml:lang="fi">Keskustelu ystäviesi kanssa Matrixissa</summary>
|
||||
<summary xml:lang="fr">Discuter avec vos ami(e)s sur le réseau Matrix</summary>
|
||||
<summary xml:lang="gl">Charle coas súas amizades en Matrix.</summary>
|
||||
<summary xml:lang="he">התכתבות עם החברים שלך ב־matrix</summary>
|
||||
<summary xml:lang="hu">Csevegjen barátaival a matrixon</summary>
|
||||
<summary xml:lang="ia">Starta Conversation con tu amicos sur matrix</summary>
|
||||
<summary xml:lang="it">Conversa con i tuoi contatti su matrix</summary>
|
||||
@@ -91,7 +89,6 @@
|
||||
<p xml:lang="eu">NeoChat, Matrix sarearen abantaila guztiei probetsua ateratzeko aukera ematen dizun berriketa aplikaizo bat da. Zure familiari, kideei eta lagunei testu mezuak, bideoak eta audio fitxategiak era seguruan bidaltzeko aukera ematen dizu.</p>
|
||||
<p xml:lang="fi">NeoChat on keskustelusovellus, jolla Matrix-verkosta saa täyden hyödyn. Se tarjoaa salatun kanavan lähettää perheelle, työkavereille ja ystäville tekstiviestejä sekä video- ja äänitiedostoja.</p>
|
||||
<p xml:lang="fr">NeoChat est une application de discussions vous permettant de profiter pleinement du réseau Matrix. Elle vous offre un moyen sécurisé d'envoyer des messages de texte, des vidéos et des fichiers audio à votre famille, vos collègues et vos ami(e)s.</p>
|
||||
<p xml:lang="he">NeoChat הוא יישום התכתבות שמאפשר לך לנצל את רשת Matrix במלואה. הוא מספק דרך מאובטחת לשליחת הודעות כתובות, סרטונים וקובצי שמע למשפחה, לעמיתים לעבודה ולחברים.</p>
|
||||
<p xml:lang="hu">A NeoChat egy olyan csevegőalkalmazás, amellyel teljes mértékben kihasználhatja a Matrix hálózatot. Biztonságos módot biztosít szöveges üzenetek, videók és hangfájlok küldéséhez családtagjainak, kollégáinak és barátainak.</p>
|
||||
<p xml:lang="ia">NeoChat es un app de conversation que te permitte prender avantage plen del rete Matrix. Il te forni un modo secur de inviar messages de texto, videos e files audio a tui familia, collegas e amicos.</p>
|
||||
<p xml:lang="it">NeoChat è un'applicazione di chat che ti consente di sfruttare appieno la rete Matrix. Ti fornisce un modo sicuro per inviare messaggi di testo, video e file audio a familiari, colleghi e amici.</p>
|
||||
@@ -117,7 +114,6 @@
|
||||
<p xml:lang="fi">NeoChat pyrkii olemaan Matrix-määritelmän täysominaisuuksinen sovellus, joten se tukee kaikkea nykyisessä vakaassa määritelmässä muutamaa huomattavaa poikkeusta lukuun ottamatta (VoIP, säikeet ja jotkin piirteet päästä päähän -salauksessa). Joitakin pienempiäkin puutteita on Matrix-määritelmän jatkuvan kehityksen vuoksi, mutta lopputavoitteena on tarjota määritelmän täysi tuki.</p>
|
||||
<p xml:lang="fr">L'objectif de NeoChat est d'être une application complète pour le protocole Matrix. En tant que tel, tout dans la spécification stable actuelle avec les exceptions notables de VoIP, les processus et certains aspects du chiffrement de bout en bout sont pris en charge. Il y a quelques autres petites omissions en raison du fait que la spécification du protocole Matrix est en constante évolution. Cependant, l'objectif reste de fournir un soutien éventuel pour l'ensemble de la spécification.</p>
|
||||
<p xml:lang="gl">NeoChat pretende ser unha aplicación completa para a especificación de Matrix. Coas excepcións de VoIP, conversas fiadas e algúns aspectos da cifraxe de extremo a extremo, a versión estábel segue as especificacións. Existen algunhas outras pequenas omisións debido ao feito de que Matrix está en continua evolución pero a intención é implementar a especificación completa.</p>
|
||||
<p xml:lang="he">NeoChat מתיימר להיות יישום עתיר יכולות לפי מפרט Matrix. כיוון שזה ייעודו, כל מה שבמפרט היציב עם חריגות משמעותיות כגון VoIP, שרשורים ועוד מגוון היבטים של הצפנה מקצה לקצה נתמכים גם הם. יש מספר השמטות קטן עקב העובדה שהמפרט של Matrix ממשיך להתפתח אך המטרה היא להמשיך לספק תמיכה בסופו של דבר לכל המפרט.</p>
|
||||
<p xml:lang="hu">A NeoChat célja, hogy a Matrix specifikációnak megfelelő teljes funkcionalitású alkalmazás legyen. Mint ilyen, a jelenlegi stabil specifikáció támogatott a VoIP, a szálak és a végpontok közötti titkosítás egyes elemeinek kivételével. Van még néhány kisebb hiányosság annak köszönhetően, hogy a Matrix specifikáció folyamatosan fejlődik, de végső cél a teljes specifikáció megvalósítása.</p>
|
||||
<p xml:lang="ia">NeoChat aspira a esser un application plenmente eminente per le specification de Matrix. Tal como omne cosas in le specification currentemente stabile con le exceptiones notabile de VOIP, threads e alcun aspectos del cryptation End-to-End es supportate. Il ha ltere pauc omissiones, debite al facto que le specification de Matrix es in evolution constante ma le aspiration remane a fornir supporto eventual per le integre specification.</p>
|
||||
<p xml:lang="it">NeoChat mira ad essere un'applicazione completa per le specifiche Matrix. Pertanto, sono supportati tutti gli elementi dell'attuale specifica stabile con le notevoli eccezioni di VoIP, conversazioni e alcuni aspetti della cifratura end-to-end. Ci sono alcune altre piccole omissioni dovute al fatto che le specifiche Matrix sono in continua evoluzione, ma l'obiettivo rimane quello di fornire un eventuale supporto per l'intera specifica.</p>
|
||||
@@ -146,7 +142,6 @@
|
||||
<p xml:lang="fi">Matrix-määritelmän kehittyessä NeoChat tukee myös monia epävakaita ominaisuuksia. Tällä hetkellä näitä ovat:</p>
|
||||
<p xml:lang="fr">En raison de la nature du développement des spécifications du protocole Matrix, NeoChat prend également en charge de nombreuses fonctionnalités instables. Actuellement, ce sont :</p>
|
||||
<p xml:lang="gl">Debido á natureza do desenvolvemento da especificación de Matrix, NeoChat tamén inclúe varias funcionalidades non estábeis:</p>
|
||||
<p xml:lang="he">מטבע הדברים, הפיתוח של NeoChat תומך במגוון יכולות מפוקפקות כתלות בהתפתחות המפרט הטכני של Matrix. היכולות האלה הן:</p>
|
||||
<p xml:lang="hu">A Matrix specifikáció fejlesztésének jellegéből adódóan a NeoChat számos instabil funkciót is támogat. Jelenleg a következőket:</p>
|
||||
<p xml:lang="ia">Debite al natura del disveloppamento de specification de Matrix NeoChat tamben supporta numerose characteristicas instabile. Currentemente istes es:</p>
|
||||
<p xml:lang="it">A causa della natura dello sviluppo delle specifiche Matrix, NeoChat supporta anche numerose funzionalità instabili. Attualmente queste sono:</p>
|
||||
@@ -177,7 +172,6 @@
|
||||
<li xml:lang="fi">Kyselyt – MSC3381</li>
|
||||
<li xml:lang="fr">Sondages - MSC3381</li>
|
||||
<li xml:lang="gl">Enquisas — MSC3381</li>
|
||||
<li xml:lang="he">סקרים - MSC3381</li>
|
||||
<li xml:lang="hu">Szavazások - MSC3381</li>
|
||||
<li xml:lang="ia">Inquestas - MSC3381</li>
|
||||
<li xml:lang="it">Sondaggi - MSC3381</li>
|
||||
@@ -206,7 +200,6 @@
|
||||
<li xml:lang="fi">Tarrapakkaukset – MSC2545</li>
|
||||
<li xml:lang="fr">Paquets d'auto-collants - MSC2545</li>
|
||||
<li xml:lang="gl">Paquetes de adhesivos — MSC2545</li>
|
||||
<li xml:lang="he">חבילות מדבקות - MSC2545</li>
|
||||
<li xml:lang="hu">Matricacsomagok - MSC2545</li>
|
||||
<li xml:lang="ia">Etiquetta gummate (sticker) -MSC2545</li>
|
||||
<li xml:lang="it">Pacchetti di adesivi - MSC2545</li>
|
||||
@@ -236,7 +229,6 @@
|
||||
<li xml:lang="fi">Sijaintitapahtumat – MSC3488</li>
|
||||
<li xml:lang="fr">Événements de lieu - MSC3488</li>
|
||||
<li xml:lang="gl">Localización de eventos — MSC3488</li>
|
||||
<li xml:lang="he">אירועי מקום - MSC3488</li>
|
||||
<li xml:lang="hu">Események helyadatai - MSC3488</li>
|
||||
<li xml:lang="ia">Eventos de Location - MSC3488</li>
|
||||
<li xml:lang="it">Località eventi - MSC3488</li>
|
||||
@@ -300,7 +292,6 @@
|
||||
<caption xml:lang="fi">Päänäkymä, jossa huoneluettelo, keskustelu ja huoneen tiedot</caption>
|
||||
<caption xml:lang="fr">Vue principale avec la liste des salons ainsi que des informations sur les salons et forums de discussions</caption>
|
||||
<caption xml:lang="gl">Vista principal coa lista de salas, a charla, e información da sala.</caption>
|
||||
<caption xml:lang="he">תצוגה ראשית עם רשימת חדרים, צ׳אט ופרטי חדר</caption>
|
||||
<caption xml:lang="hu">A fő nézet a szobalistával, csevegéssel és szobainformációkkal</caption>
|
||||
<caption xml:lang="ia">Vista principal con lista de sala, chat e information de sala</caption>
|
||||
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
|
||||
@@ -332,7 +323,6 @@
|
||||
<caption xml:lang="eu">Ezagutu komunitate berriak Matrixeko Tokiak erabiliz</caption>
|
||||
<caption xml:lang="fi">Löydä uusia yhteisöjä Matrix Spacesillä</caption>
|
||||
<caption xml:lang="fr">Découvrez de nouvelles communautés avec les espaces sous Matrix</caption>
|
||||
<caption xml:lang="he">אפשר להיחשף לקהילות חדשות דרך Matrix Spaces</caption>
|
||||
<caption xml:lang="hu">Fedezzen fel új közösségeket a Matrix Terek segítségével</caption>
|
||||
<caption xml:lang="ia">Discoperi nove communitate con Matrix Spaces (Spatios de Matrix)</caption>
|
||||
<caption xml:lang="it">Scopri nuove comunità con Matrix Spaces</caption>
|
||||
@@ -368,7 +358,6 @@
|
||||
<caption xml:lang="fi">Päänäkymä, jossa huoneluettelo, keskustelu ja huoneen tiedot</caption>
|
||||
<caption xml:lang="fr">Vue principale avec la liste des salons ainsi que des informations sur les salons et forums de discussions</caption>
|
||||
<caption xml:lang="gl">Vista principal coa lista de salas, a charla, e información da sala.</caption>
|
||||
<caption xml:lang="he">תצוגה ראשית עם רשימת חדרים, צ׳אט ופרטי חדר</caption>
|
||||
<caption xml:lang="hu">A fő nézet a szobalistával, csevegéssel és szobainformációkkal</caption>
|
||||
<caption xml:lang="ia">Vista principal con lista de sala, chat e information de sala</caption>
|
||||
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
|
||||
@@ -402,7 +391,6 @@
|
||||
<caption xml:lang="fi">Kirjautumisnäkymä</caption>
|
||||
<caption xml:lang="fr">Écran de connexion</caption>
|
||||
<caption xml:lang="gl">Pantalla de identificación.</caption>
|
||||
<caption xml:lang="he">מסך כניסה</caption>
|
||||
<caption xml:lang="hu">Bejelentkező képernyő</caption>
|
||||
<caption xml:lang="ia">Schermo de accesso</caption>
|
||||
<caption xml:lang="it">Schermata di accesso</caption>
|
||||
|
||||
@@ -18,7 +18,6 @@ Name[eu]=NeoChat
|
||||
Name[fi]=NeoChat
|
||||
Name[fr]=NeoChat
|
||||
Name[gl]=NeoChat
|
||||
Name[he]=NeoChat
|
||||
Name[hu]=NeoChat
|
||||
Name[ia]=Neochat
|
||||
Name[id]=NeoChat
|
||||
@@ -60,7 +59,6 @@ GenericName[eu]=Matrix bezeroa
|
||||
GenericName[fi]=Matrix-asiakas
|
||||
GenericName[fr]=Client « Matrix »
|
||||
GenericName[gl]=Cliente de Matrix
|
||||
GenericName[he]=לקוח Matrix
|
||||
GenericName[hu]=Matrix kliens
|
||||
GenericName[ia]=Cliente de Matrice
|
||||
GenericName[id]=Klien Matrix
|
||||
@@ -101,7 +99,6 @@ Comment[eu]=Matrix protokolorako bezeroa
|
||||
Comment[fi]=Asiakas Matrix-yhteyskäytännölle
|
||||
Comment[fr]=Client pour le protocole « Matrix »
|
||||
Comment[gl]=Cliente para o protocolo Matrix.
|
||||
Comment[he]=לקוח לפרוטוקול Matrix
|
||||
Comment[hu]=Kliens a Matrix protokollhoz
|
||||
Comment[ia]=Cliente per le protocollo de Matrix
|
||||
Comment[id]=Klien untuk protokol Matrix
|
||||
|
||||
2315
po/ar/neochat.po
2315
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
2140
po/ast/neochat.po
2140
po/ast/neochat.po
File diff suppressed because it is too large
Load Diff
2548
po/az/neochat.po
2548
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
2346
po/ca/neochat.po
2346
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2239
po/cs/neochat.po
2239
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
2348
po/da/neochat.po
2348
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
2518
po/de/neochat.po
2518
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
2541
po/el/neochat.po
2541
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
2367
po/en_GB/neochat.po
2367
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
2178
po/eo/neochat.po
2178
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
2307
po/es/neochat.po
2307
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
2359
po/eu/neochat.po
2359
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
2709
po/fi/neochat.po
2709
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
2401
po/fr/neochat.po
2401
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
5593
po/gl/neochat.po
5593
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
2396
po/hu/neochat.po
2396
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
2369
po/ia/neochat.po
2369
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
2525
po/id/neochat.po
2525
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
2432
po/ie/neochat.po
2432
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
2309
po/it/neochat.po
2309
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
2138
po/ja/neochat.po
2138
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
2307
po/ka/neochat.po
2307
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
2507
po/ko/neochat.po
2507
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
2144
po/lt/neochat.po
2144
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
2499
po/lv/neochat.po
2499
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
2340
po/nl/neochat.po
2340
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
2168
po/nn/neochat.po
2168
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
2464
po/pa/neochat.po
2464
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
2366
po/pl/neochat.po
2366
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
2513
po/pt/neochat.po
2513
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
2567
po/pt_BR/neochat.po
2567
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
2521
po/ru/neochat.po
2521
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
2488
po/sk/neochat.po
2488
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
2290
po/sl/neochat.po
2290
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
2495
po/sv/neochat.po
2495
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
2325
po/ta/neochat.po
2325
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
2311
po/tok/neochat.po
2311
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
2279
po/tr/neochat.po
2279
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
2379
po/uk/neochat.po
2379
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
2186
po/zh_CN/neochat.po
2186
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
2310
po/zh_TW/neochat.po
2310
po/zh_TW/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,8 @@ add_library(neochat STATIC
|
||||
models/customemojimodel.h
|
||||
clipboard.cpp
|
||||
clipboard.h
|
||||
matriximageprovider.cpp
|
||||
matriximageprovider.h
|
||||
models/messageeventmodel.cpp
|
||||
models/messageeventmodel.h
|
||||
models/messagefiltermodel.cpp
|
||||
@@ -134,8 +136,6 @@ add_library(neochat STATIC
|
||||
jobs/neochatdeletedevicejob.h
|
||||
jobs/neochatchangepasswordjob.cpp
|
||||
jobs/neochatchangepasswordjob.h
|
||||
jobs/neochatgetcommonroomsjob.cpp
|
||||
jobs/neochatgetcommonroomsjob.h
|
||||
mediasizehelper.cpp
|
||||
mediasizehelper.h
|
||||
eventhandler.cpp
|
||||
@@ -158,6 +158,7 @@ add_library(neochat STATIC
|
||||
models/linemodel.cpp
|
||||
models/linemodel.h
|
||||
events/locationbeaconevent.h
|
||||
events/serveraclevent.h
|
||||
events/widgetevent.h
|
||||
enums/messagecomponenttype.h
|
||||
models/messagecontentmodel.cpp
|
||||
@@ -176,29 +177,13 @@ add_library(neochat STATIC
|
||||
foreigntypes.h
|
||||
models/threepidmodel.cpp
|
||||
models/threepidmodel.h
|
||||
threepidaddhelper.cpp
|
||||
threepidaddhelper.h
|
||||
jobs/neochatadd3pidjob.cpp
|
||||
jobs/neochatadd3pidjob.h
|
||||
identityserverhelper.cpp
|
||||
identityserverhelper.h
|
||||
enums/powerlevel.cpp
|
||||
enums/powerlevel.h
|
||||
models/permissionsmodel.cpp
|
||||
models/permissionsmodel.h
|
||||
threepidbindhelper.cpp
|
||||
threepidbindhelper.h
|
||||
models/readmarkermodel.cpp
|
||||
models/readmarkermodel.h
|
||||
neochatroommember.cpp
|
||||
neochatroommember.h
|
||||
)
|
||||
|
||||
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||
QT_QML_SINGLETON_TYPE TRUE
|
||||
)
|
||||
|
||||
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||
qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat
|
||||
QML_FILES
|
||||
qml/Main.qml
|
||||
@@ -222,10 +207,16 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||
qml/TypingPane.qml
|
||||
qml/QuickSwitcher.qml
|
||||
qml/HoverActions.qml
|
||||
qml/ChatBar.qml
|
||||
qml/AttachmentPane.qml
|
||||
qml/ReplyPane.qml
|
||||
qml/CompletionMenu.qml
|
||||
qml/PieProgressBar.qml
|
||||
qml/QuickFormatBar.qml
|
||||
qml/EmojiPicker.qml
|
||||
qml/UserDetailDialog.qml
|
||||
qml/CreateRoomDialog.qml
|
||||
qml/EmojiDialog.qml
|
||||
qml/OpenFileDialog.qml
|
||||
qml/KeyVerificationDialog.qml
|
||||
qml/ConfirmLogoutDialog.qml
|
||||
@@ -245,6 +236,9 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||
qml/ConfirmEncryptionDialog.qml
|
||||
qml/RemoveSheet.qml
|
||||
qml/BanSheet.qml
|
||||
qml/EmojiTonesPicker.qml
|
||||
qml/EmojiDelegate.qml
|
||||
qml/EmojiGrid.qml
|
||||
qml/RoomSearchPage.qml
|
||||
qml/LocationChooser.qml
|
||||
qml/TimelineView.qml
|
||||
@@ -268,6 +262,7 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||
qml/SelectParentDialog.qml
|
||||
qml/QrCodeMaximizeComponent.qml
|
||||
qml/SelectSpacesDialog.qml
|
||||
qml/AttachDialog.qml
|
||||
qml/NotificationsView.qml
|
||||
qml/SearchPage.qml
|
||||
qml/ServerComboBox.qml
|
||||
@@ -285,26 +280,12 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||
qml/ConfirmLeaveDialog.qml
|
||||
qml/CodeMaximizeComponent.qml
|
||||
qml/EditStateDialog.qml
|
||||
qml/ConsentDialog.qml
|
||||
qml/AskDirectChatConfirmation.qml
|
||||
qml/HoverLinkIndicator.qml
|
||||
qml/AvatarNotification.qml
|
||||
DEPENDENCIES
|
||||
QtCore
|
||||
QtQuick
|
||||
IMPORTS
|
||||
org.kde.neochat.timeline
|
||||
org.kde.neochat.settings
|
||||
org.kde.neochat.devtools
|
||||
org.kde.neochat.login
|
||||
org.kde.neochat.chatbar
|
||||
)
|
||||
|
||||
add_subdirectory(settings)
|
||||
add_subdirectory(timeline)
|
||||
add_subdirectory(devtools)
|
||||
add_subdirectory(login)
|
||||
add_subdirectory(chatbar)
|
||||
|
||||
if(UNIX)
|
||||
qt_target_qml_sources(neochat QML_FILES qml/ShareAction.qml)
|
||||
@@ -398,7 +379,7 @@ else()
|
||||
endif()
|
||||
|
||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
|
||||
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin devtoolsplugin loginplugin chatbarplugin)
|
||||
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin devtoolsplugin loginplugin)
|
||||
target_link_libraries(neochat PUBLIC
|
||||
Qt::Core
|
||||
Qt::Quick
|
||||
@@ -426,7 +407,7 @@ if (TARGET KF6::Crash)
|
||||
target_link_libraries(neochat PUBLIC KF6::Crash)
|
||||
endif()
|
||||
|
||||
kconfig_target_kcfg_file(neochat FILE neochatconfig.kcfg CLASS_NAME NeoChatConfig MUTATORS GENERATE_PROPERTIES DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR SINGLETON GENERATE_MOC QML_REGISTRATION)
|
||||
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||
|
||||
if(NEOCHAT_FLATPAK)
|
||||
target_compile_definitions(neochat PUBLIC NEOCHAT_FLATPAK)
|
||||
|
||||
@@ -91,7 +91,7 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, Cha
|
||||
|
||||
for (auto it = m_room->messageEvents().crbegin(); it != m_room->messageEvents().crend(); it++) {
|
||||
if (const auto event = eventCast<const RoomMessageEvent>(&**it)) {
|
||||
if (event->senderId() == m_room->localMember().id() && event->hasTextContent()) {
|
||||
if (event->senderId() == m_room->localUser()->id() && event->hasTextContent()) {
|
||||
QString originalString;
|
||||
if (event->content()) {
|
||||
originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content())->body;
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
qt_add_library(chatbar STATIC)
|
||||
ecm_add_qml_module(chatbar GENERATE_PLUGIN_SOURCE
|
||||
URI org.kde.neochat.chatbar
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/chatbar
|
||||
QML_FILES
|
||||
AttachDialog.qml
|
||||
ChatBar.qml
|
||||
CompletionMenu.qml
|
||||
EmojiDelegate.qml
|
||||
EmojiGrid.qml
|
||||
ReplyPane.qml
|
||||
PieProgressBar.qml
|
||||
EmojiPicker.qml
|
||||
EmojiDialog.qml
|
||||
EmojiTonesPicker.qml
|
||||
)
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include "chatbarcache.h"
|
||||
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
#include "chatdocumenthandler.h"
|
||||
#include "eventhandler.h"
|
||||
#include "neochatroom.h"
|
||||
@@ -86,7 +84,7 @@ void ChatBarCache::setEditId(const QString &editId)
|
||||
Q_EMIT attachmentPathChanged();
|
||||
}
|
||||
|
||||
Quotient::RoomMember ChatBarCache::relationUser() const
|
||||
QVariantMap ChatBarCache::relationUser() const
|
||||
{
|
||||
if (parent() == nullptr) {
|
||||
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
@@ -98,9 +96,9 @@ Quotient::RoomMember ChatBarCache::relationUser() const
|
||||
return {};
|
||||
}
|
||||
if (m_relationId.isEmpty()) {
|
||||
return room->member(QString());
|
||||
return room->getUser(nullptr);
|
||||
}
|
||||
return room->member((*room->findInTimeline(m_relationId))->senderId());
|
||||
return room->getUser(room->user((*room->findInTimeline(m_relationId))->senderId()));
|
||||
}
|
||||
|
||||
QString ChatBarCache::relationMessage() const
|
||||
|
||||
@@ -10,12 +10,6 @@
|
||||
|
||||
class ChatDocumentHandler;
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class RoomMember;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines a user mention in the current chat or edit text.
|
||||
*/
|
||||
@@ -94,13 +88,26 @@ class ChatBarCache : public QObject
|
||||
Q_PROPERTY(QString editId READ editId WRITE setEditId NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Get the RoomMember object for the message being replied to.
|
||||
* @brief Get the user for the message being replied to.
|
||||
*
|
||||
* Returns an empty RoomMember if not replying to a message.
|
||||
* 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.
|
||||
*
|
||||
* @sa Quotient::RoomMember
|
||||
* 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(Quotient::RoomMember relationUser READ relationUser NOTIFY relationIdChanged)
|
||||
Q_PROPERTY(QVariantMap relationUser READ relationUser NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The content of the related message.
|
||||
@@ -154,7 +161,7 @@ public:
|
||||
QString editId() const;
|
||||
void setEditId(const QString &editId);
|
||||
|
||||
Quotient::RoomMember relationUser() const;
|
||||
QVariantMap relationUser() const;
|
||||
|
||||
QString relationMessage() const;
|
||||
|
||||
|
||||
@@ -9,26 +9,27 @@
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QNetworkReply>
|
||||
#include <QRandomGenerator>
|
||||
#include <QTcpServer>
|
||||
#include <QNetworkProxy>
|
||||
#include <QQuickTextDocument>
|
||||
#include <QQuickWindow>
|
||||
#include <QStandardPaths>
|
||||
#include <QStringBuilder>
|
||||
#include <QTimer>
|
||||
|
||||
#include <QCoroNetworkReply>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/csapi/logout.h>
|
||||
#include <Quotient/csapi/notifications.h>
|
||||
#include <Quotient/csapi/wellknown.h>
|
||||
#include <Quotient/eventstats.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
#include <Quotient/settings.h>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "notificationsmanager.h"
|
||||
#include "proxycontroller.h"
|
||||
#include "roommanager.h"
|
||||
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
#include "trayicon.h"
|
||||
@@ -44,14 +45,9 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
#include <kunifiedpush/connector.h>
|
||||
#endif
|
||||
|
||||
bool testMode = false;
|
||||
|
||||
using namespace Quotient;
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
Controller::Controller(QObject *parent)
|
||||
: QObject(parent)
|
||||
@@ -110,16 +106,14 @@ Controller::Controller(QObject *parent)
|
||||
connect(&m_accountRegistry, &AccountRegistry::accountCountChanged, this, [this]() {
|
||||
if (m_accountRegistry.size() > oldAccountCount) {
|
||||
auto connection = dynamic_cast<NeoChatConnection *>(m_accountRegistry.accounts()[m_accountRegistry.size() - 1]);
|
||||
connect(
|
||||
connection,
|
||||
&NeoChatConnection::syncDone,
|
||||
this,
|
||||
[this, connection] {
|
||||
if (!m_endpoint.isEmpty()) {
|
||||
connection->setupPushNotifications(m_endpoint);
|
||||
}
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
connect(connection, &NeoChatConnection::syncDone, this, [connection]() {
|
||||
NotificationsManager::instance().handleNotifications(connection);
|
||||
});
|
||||
connectSingleShot(connection, &NeoChatConnection::syncDone, this, [this, connection] {
|
||||
if (!m_endpoint.isEmpty()) {
|
||||
connection->setupPushNotifications(m_endpoint);
|
||||
}
|
||||
});
|
||||
}
|
||||
oldAccountCount = m_accountRegistry.size();
|
||||
});
|
||||
@@ -195,7 +189,7 @@ void Controller::invokeLogin()
|
||||
m_accountsLoading += accountId;
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
if (!account.homeserver().isEmpty()) {
|
||||
auto accessTokenLoadingJob = loadAccessTokenFromKeyChain(account.userId());
|
||||
auto accessTokenLoadingJob = loadAccessTokenFromKeyChain(account);
|
||||
connect(accessTokenLoadingJob, &QKeychain::Job::finished, this, [accountId, this, accessTokenLoadingJob](QKeychain::Job *) {
|
||||
AccountSettings account{accountId};
|
||||
QString accessToken;
|
||||
@@ -217,22 +211,17 @@ void Controller::invokeLogin()
|
||||
connect(connection, &NeoChatConnection::networkError, this, [this](const QString &error, const QString &, int, int) {
|
||||
Q_EMIT errorOccured(i18n("Network Error: %1", error), {});
|
||||
});
|
||||
|
||||
if (!account.clientId().isEmpty()) {
|
||||
connection->assumeOidcIdentity(account.userId(), accessToken, account.clientId(), account.tokenEndpoint());
|
||||
} else {
|
||||
connection->assumeIdentity(account.userId(), accessToken);
|
||||
}
|
||||
connection->assumeIdentity(account.userId(), accessToken);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const QString &userId)
|
||||
QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const AccountSettings &account)
|
||||
{
|
||||
qDebug() << "Reading access token from the keychain for" << userId;
|
||||
qDebug() << "Reading access token from the keychain for" << account.userId();
|
||||
auto job = new QKeychain::ReadPasswordJob(qAppName(), this);
|
||||
job->setKey(userId);
|
||||
job->setKey(account.userId());
|
||||
|
||||
// Handling of errors
|
||||
connect(job, &QKeychain::Job::finished, this, [this, job]() {
|
||||
@@ -263,12 +252,12 @@ QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const QStrin
|
||||
return job;
|
||||
}
|
||||
|
||||
bool Controller::saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken)
|
||||
bool Controller::saveAccessTokenToKeyChain(const AccountSettings &account, const QByteArray &accessToken)
|
||||
{
|
||||
qDebug() << "Save the access token to the keychain for " << userId;
|
||||
qDebug() << "Save the access token to the keychain for " << account.userId();
|
||||
QKeychain::WritePasswordJob job(qAppName());
|
||||
job.setAutoDelete(false);
|
||||
job.setKey(userId);
|
||||
job.setKey(account.userId());
|
||||
job.setBinaryData(accessToken);
|
||||
QEventLoop loop;
|
||||
QKeychain::WritePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
|
||||
@@ -404,6 +393,8 @@ QString Controller::loadFileContent(const QString &path) const
|
||||
return QString::fromLatin1(file.readAll());
|
||||
}
|
||||
|
||||
#include "moc_controller.cpp"
|
||||
|
||||
void Controller::setTestMode(bool test)
|
||||
{
|
||||
testMode = test;
|
||||
@@ -419,31 +410,11 @@ void Controller::removeConnection(const QString &userId)
|
||||
}
|
||||
}
|
||||
|
||||
bool Controller::csSupported() const
|
||||
bool Controller::ssssSupported() const
|
||||
{
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
#if __has_include("Quotient/e2ee/sssshandler.h")
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
NeoChatConnection *Controller::pendingOidcConnection() const
|
||||
{
|
||||
return m_pendingOidcConnection.get();
|
||||
}
|
||||
|
||||
void Controller::startOidcLogin(const QString &homeserver)
|
||||
{
|
||||
auto url = QUrl::fromUserInput(homeserver.startsWith(u"https:"_s) ? homeserver : u"https://%1"_s.arg(homeserver));
|
||||
m_pendingOidcConnection = new NeoChatConnection(url, this);
|
||||
Q_EMIT pendingOidcConnectionChanged();
|
||||
m_pendingOidcConnection->startOidcLogin();
|
||||
connect(m_pendingOidcConnection, &Connection::connected, this, [this]() {
|
||||
m_pendingOidcConnection->loadState();
|
||||
addConnection(m_pendingOidcConnection);
|
||||
setActiveConnection(m_pendingOidcConnection);
|
||||
});
|
||||
}
|
||||
|
||||
#include "moc_controller.cpp"
|
||||
|
||||
@@ -5,9 +5,15 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickItem>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/settings.h>
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
#include <kunifiedpush/connector.h>
|
||||
#endif
|
||||
|
||||
class TrayIcon;
|
||||
class QQuickTextDocument;
|
||||
@@ -50,21 +56,9 @@ class Controller : public QObject
|
||||
|
||||
Q_PROPERTY(QStringList accountsLoading MEMBER m_accountsLoading NOTIFY accountsLoadingChanged)
|
||||
|
||||
Q_PROPERTY(bool csSupported READ csSupported CONSTANT)
|
||||
|
||||
Q_PROPERTY(NeoChatConnection *pendingOidcConnection READ pendingOidcConnection NOTIFY pendingOidcConnectionChanged)
|
||||
Q_PROPERTY(bool ssssSupported READ ssssSupported CONSTANT)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Define the types on inline messages that can be shown.
|
||||
*/
|
||||
enum MessageType {
|
||||
Positive, /**< Positive message, typically green. */
|
||||
Info, /**< Info message, typically highlight color. */
|
||||
Error, /**< Error message, typically red. */
|
||||
};
|
||||
Q_ENUM(MessageType)
|
||||
|
||||
static Controller &instance();
|
||||
static Controller *create(QQmlEngine *engine, QJSEngine *)
|
||||
{
|
||||
@@ -88,7 +82,7 @@ public:
|
||||
/**
|
||||
* @brief Save an access token to the keychain for the given account.
|
||||
*/
|
||||
bool saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken);
|
||||
bool saveAccessTokenToKeyChain(const Quotient::AccountSettings &account, const QByteArray &accessToken);
|
||||
|
||||
[[nodiscard]] bool supportSystemTray() const;
|
||||
|
||||
@@ -108,11 +102,7 @@ public:
|
||||
|
||||
Q_INVOKABLE void removeConnection(const QString &userId);
|
||||
|
||||
Q_INVOKABLE void startOidcLogin(const QString &homeserver);
|
||||
|
||||
bool csSupported() const;
|
||||
|
||||
NeoChatConnection *pendingOidcConnection() const;
|
||||
bool ssssSupported() const;
|
||||
|
||||
private:
|
||||
explicit Controller(QObject *parent = nullptr);
|
||||
@@ -120,18 +110,15 @@ private:
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
TrayIcon *m_trayIcon = nullptr;
|
||||
|
||||
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const QString &account);
|
||||
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const Quotient::AccountSettings &account);
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings() const;
|
||||
|
||||
QCoro::Task<void> doStartOidcLogin(QString homeserver);
|
||||
|
||||
Quotient::AccountRegistry m_accountRegistry;
|
||||
QStringList m_accountsLoading;
|
||||
QMap<QString, QPointer<NeoChatConnection>> m_connectionsLoading;
|
||||
QString m_endpoint;
|
||||
QPointer<NeoChatConnection> m_pendingOidcConnection;
|
||||
|
||||
private Q_SLOTS:
|
||||
void invokeLogin();
|
||||
@@ -144,6 +131,4 @@ Q_SIGNALS:
|
||||
void connectionDropped(NeoChatConnection *connection);
|
||||
void activeConnectionChanged(NeoChatConnection *connection);
|
||||
void accountsLoadingChanged();
|
||||
void showMessage(MessageType messageType, const QString &message);
|
||||
void pendingOidcConnectionChanged();
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
qt_add_library(devtools STATIC)
|
||||
ecm_add_qml_module(devtools GENERATE_PLUGIN_SOURCE
|
||||
qt_add_qml_module(devtools
|
||||
URI org.kde.neochat.devtools
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/devtools
|
||||
QML_FILES
|
||||
|
||||
@@ -17,25 +17,25 @@ FormCard.FormCardPage {
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check", "Show hidden events in the timeline")
|
||||
checked: NeoChatConfig.showAllEvents
|
||||
checked: Config.showAllEvents
|
||||
|
||||
onToggled: NeoChatConfig.showAllEvents = checked
|
||||
onToggled: Config.showAllEvents = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
id: roomAccountDataVisibleCheck
|
||||
text: i18nc("@option:check Enable the matrix 'threads' feature", "Always allow device verification")
|
||||
description: i18n("Allow the user to start a verification session with devices that were already verified")
|
||||
checked: NeoChatConfig.alwaysVerifyDevice
|
||||
checked: Config.alwaysVerifyDevice
|
||||
|
||||
onToggled: NeoChatConfig.alwaysVerifyDevice = checked
|
||||
onToggled: Config.alwaysVerifyDevice = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check", "Show focus in window header")
|
||||
checked: NeoChatConfig.windowTitleFocus
|
||||
checked: Config.windowTitleFocus
|
||||
|
||||
onToggled: {
|
||||
NeoChatConfig.windowTitleFocus = checked;
|
||||
NeoChatConfig.save();
|
||||
Config.windowTitleFocus = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,12 +31,12 @@ FormCard.FormCardPage {
|
||||
implicitWidth: tabBar.tabWidth
|
||||
}
|
||||
QQC2.TabButton {
|
||||
text: i18nc("@title:tab", "Room Data")
|
||||
text: qsTr("Room Data")
|
||||
|
||||
implicitWidth: tabBar.tabWidth
|
||||
}
|
||||
QQC2.TabButton {
|
||||
text: i18nc("@title:tab", "Server Info")
|
||||
text: qsTr("Server Info")
|
||||
|
||||
implicitWidth: tabBar.tabWidth
|
||||
}
|
||||
|
||||
@@ -18,24 +18,15 @@ FormCard.FormCardPage {
|
||||
FormCard.FormCheckDelegate {
|
||||
id: roomAccountDataVisibleCheck
|
||||
text: i18nc("@option:check Enable the matrix 'threads' feature", "Threads")
|
||||
checked: NeoChatConfig.threads
|
||||
checked: Config.threads
|
||||
|
||||
onToggled: NeoChatConfig.threads = checked
|
||||
onToggled: Config.threads = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check Enable the matrix 'secret backup' feature", "Secret Backup")
|
||||
checked: NeoChatConfig.secretBackup
|
||||
checked: Config.secretBackup
|
||||
|
||||
onToggled: NeoChatConfig.secretBackup = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check Enable the matrix feature to add a phone number as a third party ID", "Add phone numbers as 3PIDs")
|
||||
checked: NeoChatConfig.phone3PId
|
||||
|
||||
onToggled: {
|
||||
NeoChatConfig.phone3PId = checked
|
||||
NeoChatConfig.save();
|
||||
}
|
||||
onToggled: Config.secretBackup = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ public:
|
||||
* a room message.
|
||||
*/
|
||||
enum Type {
|
||||
Author, /**< The message sender and time. */
|
||||
Text, /**< A text message. */
|
||||
Image, /**< A message that is an image. */
|
||||
Audio, /**< A message that is an audio recording. */
|
||||
@@ -48,11 +47,11 @@ public:
|
||||
LiveLocation, /**< The initial event of a shared live location (i.e., the place where this is supposed to be shown in the timeline). */
|
||||
Encrypted, /**< An encrypted message that cannot be decrypted. */
|
||||
Reply, /**< A component to show a replied-to message. */
|
||||
ReplyLoad, /**< A loading dialog for a reply. */
|
||||
LinkPreview, /**< A preview of a URL in the message. */
|
||||
LinkPreviewLoad, /**< A loading dialog for a link preview. */
|
||||
Edit, /**< A text edit for editing a message. */
|
||||
Verification, /**< A user verification session start message. */
|
||||
Loading, /**< The component is loading. */
|
||||
Other, /**< Anything that cannot be classified as another type. */
|
||||
};
|
||||
Q_ENUM(Type);
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "powerlevel.h"
|
||||
|
||||
QString PowerLevel::nameForLevel(Level level)
|
||||
{
|
||||
switch (level) {
|
||||
case PowerLevel::Member:
|
||||
return i18n("Member");
|
||||
case PowerLevel::Moderator:
|
||||
return i18n("Moderator");
|
||||
case PowerLevel::Admin:
|
||||
return i18n("Admin");
|
||||
case PowerLevel::Mute:
|
||||
return i18n("Mute");
|
||||
case PowerLevel::Custom:
|
||||
return i18n("Custom");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
int PowerLevel::valueForLevel(Level level)
|
||||
{
|
||||
switch (level) {
|
||||
case PowerLevel::Member:
|
||||
return 0;
|
||||
case PowerLevel::Moderator:
|
||||
return 50;
|
||||
case PowerLevel::Admin:
|
||||
return 100;
|
||||
case PowerLevel::Mute:
|
||||
return -1;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
PowerLevel::Level PowerLevel::levelForValue(int value)
|
||||
{
|
||||
switch (value) {
|
||||
case 0:
|
||||
return PowerLevel::Member;
|
||||
case 50:
|
||||
return PowerLevel::Moderator;
|
||||
case 100:
|
||||
return PowerLevel::Admin;
|
||||
case -1:
|
||||
return PowerLevel::Mute;
|
||||
default:
|
||||
return PowerLevel::Custom;
|
||||
}
|
||||
}
|
||||
|
||||
PowerLevelModel::PowerLevelModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool PowerLevelModel::showMute() const
|
||||
{
|
||||
return m_showMute;
|
||||
}
|
||||
|
||||
void PowerLevelModel::setShowMute(bool showMute)
|
||||
{
|
||||
if (showMute == m_showMute) {
|
||||
return;
|
||||
}
|
||||
m_showMute = showMute;
|
||||
Q_EMIT showMuteChanged();
|
||||
}
|
||||
|
||||
QVariant PowerLevelModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return {};
|
||||
}
|
||||
if (index.row() >= rowCount()) {
|
||||
qDebug() << "PowerLevelModel, something's wrong: index.row() >= m_rules.count()";
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto level = static_cast<PowerLevel::Level>(index.row());
|
||||
if (role == NameRole) {
|
||||
return i18nc("%1 is the name of the power level, e.g. admin and %2 is the value that represents.",
|
||||
"%1 (%2)",
|
||||
PowerLevel::nameForLevel(level),
|
||||
PowerLevel::valueForLevel(level));
|
||||
}
|
||||
if (role == ValueRole) {
|
||||
return PowerLevel::valueForLevel(level);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
int PowerLevelModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return PowerLevel::NUMLevels - (m_showMute ? 0 : 1);
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> PowerLevelModel::roleNames() const
|
||||
{
|
||||
return {{NameRole, "name"}, {ValueRole, "value"}};
|
||||
}
|
||||
|
||||
#include "moc_powerlevel.cpp"
|
||||
@@ -1,110 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
/**
|
||||
* @class PowerLevel
|
||||
*
|
||||
* This class is designed to define the PowerLevel enumeration.
|
||||
*/
|
||||
class PowerLevel : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief The type of delegate that is needed for the event.
|
||||
*
|
||||
* @note While similar this is not the matrix event or message type. This is
|
||||
* to tell a QML ListView what delegate to show for each event. So while
|
||||
* similar to the spec it is not the same.
|
||||
*/
|
||||
enum Level {
|
||||
Member, /**< A basic member. */
|
||||
Moderator, /**< A moderator with enhanced powers. */
|
||||
Admin, /**< The highest power level in the room. */
|
||||
Mute, /**< The level to remove posting privileges. */
|
||||
NUMLevels,
|
||||
Custom, /**< A non-standard value. Intentionally after NUMLevels so it doesn't appear in the model. */
|
||||
};
|
||||
Q_ENUM(Level);
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of the enum value.
|
||||
*/
|
||||
static QString nameForLevel(Level level);
|
||||
|
||||
/**
|
||||
* @brief Return the integer representation of the enum value.
|
||||
*/
|
||||
static int valueForLevel(Level level);
|
||||
|
||||
/**
|
||||
* @brief Return the enum value for the given integer power level.
|
||||
*/
|
||||
static Level levelForValue(int value);
|
||||
};
|
||||
|
||||
/**
|
||||
* @class PowerLevelModel
|
||||
*
|
||||
* A model visualize the allowed power levels.
|
||||
*/
|
||||
class PowerLevelModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
Q_PROPERTY(bool showMute READ showMute WRITE setShowMute NOTIFY showMuteChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*/
|
||||
enum Roles {
|
||||
NameRole = Qt::DisplayRole, /**< The power level name. */
|
||||
ValueRole, /**< The power level value. */
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
explicit PowerLevelModel(QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] bool showMute() const;
|
||||
void setShowMute(bool showMute);
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
*
|
||||
* @sa QAbstractItemModel::data
|
||||
*/
|
||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
/**
|
||||
* @brief Number of rows in the model.
|
||||
*
|
||||
* @sa QAbstractItemModel::rowCount
|
||||
*/
|
||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
/**
|
||||
* @brief Returns a mapping from Role enum values to role names.
|
||||
*
|
||||
* @sa Roles, QAbstractItemModel::roleNames()
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void showMuteChanged();
|
||||
|
||||
private:
|
||||
bool m_showMute = true;
|
||||
};
|
||||
@@ -19,11 +19,11 @@
|
||||
#include <Quotient/events/simplestateevents.h>
|
||||
#include <Quotient/events/stickerevent.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
#include "eventhandler_logging.h"
|
||||
#include "events/locationbeaconevent.h"
|
||||
#include "events/pollevent.h"
|
||||
#include "events/serveraclevent.h"
|
||||
#include "events/widgetevent.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "messagecomponenttype.h"
|
||||
@@ -61,6 +61,22 @@ MessageComponentType::Type EventHandler::messageComponentType() const
|
||||
return MessageComponentType::typeForEvent(*m_event);
|
||||
}
|
||||
|
||||
QVariantMap EventHandler::getAuthor(bool isPending) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getAuthor called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
// If we have a room we can return an empty user by handing nullptr to m_room->getUser.
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getAuthor called with m_event set to nullptr. Returning empty user.";
|
||||
return m_room->getUser(nullptr);
|
||||
}
|
||||
|
||||
const auto author = isPending ? m_room->localUser() : m_room->user(m_event->senderId());
|
||||
return m_room->getUser(author);
|
||||
}
|
||||
|
||||
QString EventHandler::getAuthorDisplayName(bool isPending) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
@@ -80,8 +96,8 @@ QString EventHandler::getAuthorDisplayName(bool isPending) const
|
||||
}
|
||||
return previousDisplayName;
|
||||
} else {
|
||||
const auto author = isPending ? m_room->localMember() : m_room->member(m_event->senderId());
|
||||
return author.htmlSafeDisplayName();
|
||||
const auto author = isPending ? m_room->localUser() : m_room->user(m_event->senderId());
|
||||
return m_room->htmlSafeMemberName(author->id());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +112,8 @@ QString EventHandler::singleLineAuthorDisplayname(bool isPending) const
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto author = isPending ? m_room->localMember() : m_room->member(m_event->senderId());
|
||||
auto displayName = author.displayName();
|
||||
const auto author = isPending ? m_room->localUser() : m_room->user(m_event->senderId());
|
||||
auto displayName = m_room->safeMemberName(author->id());
|
||||
displayName.replace(QStringLiteral("<br>\n"), QStringLiteral(" "));
|
||||
displayName.replace(QStringLiteral("<br>"), QStringLiteral(" "));
|
||||
displayName.replace(QStringLiteral("<br />\n"), QStringLiteral(" "));
|
||||
@@ -143,11 +159,6 @@ QString EventHandler::getTimeString(bool relative, QLocale::FormatType format, b
|
||||
return {};
|
||||
}
|
||||
|
||||
QString EventHandler::getTimeString(const QString &format, bool isPending, const QDateTime &lastUpdated)
|
||||
{
|
||||
return getTime(isPending, lastUpdated).toLocalTime().toString(format);
|
||||
}
|
||||
|
||||
bool EventHandler::isHighlighted()
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
@@ -209,7 +220,7 @@ bool EventHandler::isHidden()
|
||||
}
|
||||
}
|
||||
|
||||
if (m_room->connection()->isIgnored(m_event->senderId())) {
|
||||
if (m_room->connection()->isIgnored(m_room->user(m_event->senderId()))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -232,21 +243,19 @@ Qt::TextFormat EventHandler::messageBodyInputFormat(const Quotient::RoomMessageE
|
||||
|
||||
QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
|
||||
{
|
||||
QString body;
|
||||
|
||||
if (event.hasFileContent()) {
|
||||
// if filename is given or body is equal to filename,
|
||||
// then body is a caption
|
||||
QString filename = event.content()->fileInfo()->originalName;
|
||||
QString body = event.plainBody();
|
||||
if (filename.isEmpty() || filename == body) {
|
||||
return QString();
|
||||
auto fileCaption = event.content()->fileInfo()->originalName;
|
||||
if (fileCaption.isEmpty()) {
|
||||
fileCaption = event.plainBody();
|
||||
} else if (event.content()->fileInfo()->originalName != event.plainBody()) {
|
||||
fileCaption = event.plainBody() + " | "_ls + fileCaption;
|
||||
}
|
||||
return body;
|
||||
return fileCaption;
|
||||
}
|
||||
|
||||
QString body;
|
||||
if (event.hasTextContent() && event.content()) {
|
||||
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
||||
body = static_cast<const MessageEventContent::TextContent *>(event.content())->body;
|
||||
} else {
|
||||
body = event.plainBody();
|
||||
}
|
||||
@@ -284,10 +293,7 @@ QString EventHandler::getMarkdownBody() const
|
||||
}
|
||||
|
||||
const auto roomMessageEvent = eventCast<const RoomMessageEvent>(m_event);
|
||||
|
||||
QString plainBody = roomMessageEvent->plainBody();
|
||||
plainBody.remove(TextRegex::removeReply);
|
||||
return plainBody;
|
||||
return roomMessageEvent->plainBody();
|
||||
}
|
||||
|
||||
QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat format, bool stripNewlines) const
|
||||
@@ -309,19 +315,15 @@ QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat f
|
||||
},
|
||||
[this, prettyPrint](const RoomMemberEvent &e) {
|
||||
// FIXME: Rewind to the name that was at the time of this event
|
||||
auto subjectName = m_room->member(e.userId()).htmlSafeDisplayName();
|
||||
auto subjectName = m_room->htmlSafeMemberName(e.userId());
|
||||
if (e.membership() == Membership::Leave) {
|
||||
if (e.prevContent() && e.prevContent()->displayName) {
|
||||
subjectName = sanitized(*e.prevContent()->displayName);
|
||||
if (prettyPrint) {
|
||||
subjectName = subjectName.toHtmlEscaped();
|
||||
}
|
||||
subjectName = sanitized(*e.prevContent()->displayName).toHtmlEscaped();
|
||||
}
|
||||
}
|
||||
|
||||
if (prettyPrint) {
|
||||
subjectName = QStringLiteral("<a href=\"https://matrix.to/#/%1\" style=\"color: %2\">%3</a>")
|
||||
.arg(e.userId(), m_room->member(e.userId()).color().name(), subjectName);
|
||||
subjectName = QStringLiteral("<a href=\"https://matrix.to/#/%1\">%2</a>").arg(e.userId(), subjectName);
|
||||
}
|
||||
|
||||
// The below code assumes senderName output in AuthorRole
|
||||
@@ -435,7 +437,7 @@ QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat f
|
||||
[](const LocationBeaconEvent &e) {
|
||||
return e.contentJson()["description"_ls].toString();
|
||||
},
|
||||
[](const RoomServerAclEvent &) {
|
||||
[](const ServerAclEvent &) {
|
||||
return i18n("changed the server access control lists for this room");
|
||||
},
|
||||
[](const WidgetEvent &e) {
|
||||
@@ -474,7 +476,7 @@ QString EventHandler::getMessageBody(const RoomMessageEvent &event, Qt::TextForm
|
||||
|
||||
QString body;
|
||||
if (event.hasTextContent() && event.content()) {
|
||||
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
||||
body = static_cast<const MessageEventContent::TextContent *>(event.content())->body;
|
||||
} else {
|
||||
body = event.plainBody();
|
||||
}
|
||||
@@ -604,7 +606,7 @@ QString EventHandler::getGenericBody() const
|
||||
[](const LocationBeaconEvent &) {
|
||||
return i18n("sent a live location beacon");
|
||||
},
|
||||
[](const RoomServerAclEvent &) {
|
||||
[](const ServerAclEvent &) {
|
||||
return i18n("changed the server access control lists for this room");
|
||||
},
|
||||
[](const WidgetEvent &e) {
|
||||
@@ -653,30 +655,23 @@ QVariantMap EventHandler::getMediaInfoForEvent(const Quotient::RoomEvent *event)
|
||||
QString eventId = event->id();
|
||||
|
||||
// Get the file info for the event.
|
||||
const EventContent::FileInfo *fileInfo;
|
||||
bool isSticker = false;
|
||||
if (event->is<RoomMessageEvent>()) {
|
||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event);
|
||||
if (!roomMessageEvent->hasFileContent()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const EventContent::FileInfo *fileInfo;
|
||||
fileInfo = roomMessageEvent->content()->fileInfo();
|
||||
QVariantMap mediaInfo = getMediaInfoFromFileInfo(fileInfo, eventId, false, false);
|
||||
// if filename isn't specifically given, it is in body
|
||||
// https://spec.matrix.org/latest/client-server-api/#mfile
|
||||
mediaInfo["filename"_ls] = (fileInfo->originalName.isEmpty()) ? roomMessageEvent->plainBody() : fileInfo->originalName;
|
||||
|
||||
return mediaInfo;
|
||||
} else if (event->is<StickerEvent>()) {
|
||||
const EventContent::FileInfo *fileInfo;
|
||||
|
||||
auto stickerEvent = eventCast<const StickerEvent>(event);
|
||||
fileInfo = &stickerEvent->image();
|
||||
|
||||
return getMediaInfoFromFileInfo(fileInfo, eventId, false, true);
|
||||
isSticker = true;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
return getMediaInfoFromFileInfo(fileInfo, eventId, false, isSticker);
|
||||
}
|
||||
|
||||
QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail, bool isSticker) const
|
||||
@@ -802,21 +797,25 @@ MessageComponentType::Type EventHandler::replyMessageComponentType() const
|
||||
return MessageComponentType::typeForEvent(*replyEvent);
|
||||
}
|
||||
|
||||
Quotient::RoomMember EventHandler::getReplyAuthor() const
|
||||
QVariantMap EventHandler::getReplyAuthor() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReplyAuthor called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
// If we have a room we can return an empty user by handing nullptr to m_room->getUser.
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReplyAuthor called with m_event set to nullptr. Returning empty user.";
|
||||
return {};
|
||||
return m_room->getUser(nullptr);
|
||||
}
|
||||
|
||||
if (auto replyPtr = m_room->getReplyForEvent(*m_event)) {
|
||||
return m_room->member(replyPtr->senderId());
|
||||
auto replyPtr = m_room->getReplyForEvent(*m_event);
|
||||
|
||||
if (replyPtr) {
|
||||
auto replyUser = m_room->user(replyPtr->senderId());
|
||||
return m_room->getUser(replyUser);
|
||||
} else {
|
||||
return m_room->member(QString());
|
||||
return m_room->getUser(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -952,4 +951,101 @@ QString EventHandler::getLocationAssetType() const
|
||||
return assetType;
|
||||
}
|
||||
|
||||
bool EventHandler::hasReadMarkers() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "hasReadMarkers called with m_room set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "hasReadMarkers called with m_event set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
return userIds.size() > 0;
|
||||
}
|
||||
|
||||
QVariantList EventHandler::getReadMarkers(int maxMarkers) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkers called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkers called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds_temp = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds_temp.remove(m_room->localUser()->id());
|
||||
|
||||
auto userIds = userIds_temp.values();
|
||||
if (userIds.count() > maxMarkers) {
|
||||
userIds = userIds.mid(0, maxMarkers);
|
||||
}
|
||||
|
||||
QVariantList users;
|
||||
users.reserve(userIds.size());
|
||||
for (const auto &userId : userIds) {
|
||||
auto user = m_room->user(userId);
|
||||
users += m_room->getUser(user);
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
QString EventHandler::getNumberExcessReadMarkers(int maxMarkers) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getNumberExcessReadMarkers called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getNumberExcessReadMarkers called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
|
||||
if (userIds.count() > maxMarkers) {
|
||||
return QStringLiteral("+ ") + QString::number(userIds.count() - maxMarkers);
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
QString EventHandler::getReadMarkersString() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkersString called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkersString called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
|
||||
/**
|
||||
* The string ends up in the form
|
||||
* "x users: user1DisplayName, user2DisplayName, etc."
|
||||
*/
|
||||
QString readMarkersString = i18np("1 user: ", "%1 users: ", userIds.size());
|
||||
for (const auto &userId : userIds) {
|
||||
auto user = m_room->user(userId);
|
||||
auto displayName = user->displayname(m_room);
|
||||
if (displayName.isEmpty()) {
|
||||
displayName = userId;
|
||||
}
|
||||
readMarkersString += displayName + i18nc("list separator", ", ");
|
||||
}
|
||||
readMarkersString.chop(2);
|
||||
return readMarkersString;
|
||||
}
|
||||
|
||||
#include "moc_eventhandler.cpp"
|
||||
|
||||
@@ -13,11 +13,6 @@
|
||||
|
||||
#include "enums/messagecomponenttype.h"
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class RoomMember;
|
||||
}
|
||||
|
||||
class LinkPreviewer;
|
||||
class NeoChatRoom;
|
||||
class ReactionModel;
|
||||
@@ -53,10 +48,38 @@ public:
|
||||
*/
|
||||
MessageComponentType::Type messageComponentType() const;
|
||||
|
||||
/**
|
||||
* @brief Get the author of the event in context of the room.
|
||||
*
|
||||
* 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. This function
|
||||
* uses the room context and outputs the result as QVariantMap.
|
||||
*
|
||||
* An empty QVariantMap will be returned if the EventHandler hasn't had the room
|
||||
* intialised. An empty user (i.e. a QVariantMap with all the correct keys
|
||||
* but empty values) will be returned if the room has been set but not an event.
|
||||
*
|
||||
* @param isPending if the event is pending, i.e. has not been confirmed by
|
||||
* the server.
|
||||
*
|
||||
* @return a QVariantMap for the user with 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 Quotient::User
|
||||
*/
|
||||
QVariantMap getAuthor(bool isPending = false) const;
|
||||
|
||||
/**
|
||||
* @brief Get the display name of the event author.
|
||||
*
|
||||
* This method is special in that it will return
|
||||
* This method is separate from getAuthor() and special in that it will return
|
||||
* the old display name of the author if the current event is one that caused it
|
||||
* to change. This allows for scenarios where the UI wishes to notify that a
|
||||
* user's display name has changed and what it changed from.
|
||||
@@ -98,8 +121,6 @@ public:
|
||||
*/
|
||||
QString getTimeString(bool relative, QLocale::FormatType format = QLocale::ShortFormat, bool isPending = false, QDateTime lastUpdated = {}) const;
|
||||
|
||||
QString getTimeString(const QString &format, bool isPending = false, const QDateTime &lastUpdated = {});
|
||||
|
||||
/**
|
||||
* @brief Whether the event should be highlighted in the timeline.
|
||||
*
|
||||
@@ -230,17 +251,27 @@ public:
|
||||
/**
|
||||
* @brief Get the author of the event replied to in context of the room.
|
||||
*
|
||||
* An empty Quotient::RoomMember will be returned if the EventHandler hasn't had
|
||||
* the room or event initialised.
|
||||
* 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. This function
|
||||
* uses the room context and outputs the result as QVariantMap.
|
||||
*
|
||||
* @param isPending if the event is pending, i.e. has not been confirmed by
|
||||
* the server.
|
||||
* An empty QVariantMap will be returned if the EventHandler hasn't had the room
|
||||
* intialised. An empty user (i.e. a QVariantMap with all the correct keys
|
||||
* but empty values) will be returned if the room has been set but not an event.
|
||||
*
|
||||
* @return a Quotient::RoomMember object for the user.
|
||||
* @return a QVariantMap for the user with 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 Quotient::RoomMember
|
||||
* @sa Quotient::User
|
||||
*/
|
||||
Quotient::RoomMember getReplyAuthor() const;
|
||||
QVariantMap getReplyAuthor() const;
|
||||
|
||||
/**
|
||||
* @brief Output a string for the message content of the event replied to ready
|
||||
@@ -329,6 +360,43 @@ public:
|
||||
*/
|
||||
QString getLocationAssetType() const;
|
||||
|
||||
/**
|
||||
* @brief Whether the event has any read marker for other users.
|
||||
*/
|
||||
bool hasReadMarkers() const;
|
||||
|
||||
/**
|
||||
* @brief Returns a list of user read marker for the event.
|
||||
*
|
||||
* @param maxMarkers the maximum number of users to return. Usually the number
|
||||
* of user read makers shown is limited to not clutter the UI.
|
||||
* This needs to be the same as used in getNumberExcessReadMarkers
|
||||
* so that the markers line up with the number displayed, i.e.
|
||||
* the number of users shown plus the excess number will be
|
||||
* the total number of other user read markers at an event.
|
||||
*/
|
||||
QVariantList getReadMarkers(int maxMarkers = 5) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the number of excess user read markers for the event.
|
||||
*
|
||||
* This returns a string in the form "+ x" ready for use in the UI.
|
||||
*
|
||||
* @param maxMarkers the maximum number of markers shown in the UI. This needs to
|
||||
* be the same as used in getReadMarkers so that the value lines
|
||||
* up with the number displayed, i.e. the number of users shown
|
||||
* plus the excess number will be the total number of other user
|
||||
* read markers at an event.
|
||||
*/
|
||||
QString getNumberExcessReadMarkers(int maxMarkers = 5) const;
|
||||
|
||||
/**
|
||||
* @brief Returns a string with the names of the read markers at the event.
|
||||
*
|
||||
* This is in the form "x users: name 1, name 2, ...".
|
||||
*/
|
||||
QString getReadMarkersString() const;
|
||||
|
||||
private:
|
||||
const NeoChatRoom *m_room = nullptr;
|
||||
const Quotient::RoomEvent *m_event = nullptr;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "imagepackevent.h"
|
||||
#include <QJsonObject>
|
||||
#include <Quotient/omittable.h>
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
@@ -17,16 +16,16 @@ ImagePackEventContent::ImagePackEventContent(const QJsonObject &json)
|
||||
fromJson<Omittable<QString>>(json["pack"_ls].toObject()["attribution"_ls]),
|
||||
};
|
||||
} else {
|
||||
pack = std::nullopt;
|
||||
pack = none;
|
||||
}
|
||||
|
||||
const auto &keys = json["images"_ls].toObject().keys();
|
||||
for (const auto &k : keys) {
|
||||
std::optional<EventContent::ImageInfo> info;
|
||||
Omittable<EventContent::ImageInfo> info;
|
||||
if (json["images"_ls][k].toObject().contains(QStringLiteral("info"))) {
|
||||
info = EventContent::ImageInfo(QUrl(json["images"_ls][k]["url"_ls].toString()), json["images"_ls][k]["info"_ls].toObject(), k);
|
||||
} else {
|
||||
info = std::nullopt;
|
||||
info = none;
|
||||
}
|
||||
images += ImagePackImage{
|
||||
k,
|
||||
|
||||
@@ -26,10 +26,10 @@ public:
|
||||
* @brief Defines the properties of an image pack.
|
||||
*/
|
||||
struct Pack {
|
||||
std::optional<QString> displayName; /**< The display name of the pack. */
|
||||
std::optional<QUrl> avatarUrl; /**< The source mxc URL for the pack avatar. */
|
||||
std::optional<QStringList> usage; /**< An array of the usages for this pack. Possible usages are "emoticon" and "sticker". */
|
||||
std::optional<QString> attribution; /**< The attribution for the pack author(s). */
|
||||
Quotient::Omittable<QString> displayName; /**< The display name of the pack. */
|
||||
Quotient::Omittable<QUrl> avatarUrl; /**< The source mxc URL for the pack avatar. */
|
||||
Quotient::Omittable<QStringList> usage; /**< An array of the usages for this pack. Possible usages are "emoticon" and "sticker". */
|
||||
Quotient::Omittable<QString> attribution; /**< The attribution for the pack author(s). */
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -38,14 +38,14 @@ public:
|
||||
struct ImagePackImage {
|
||||
QString shortcode; /**< The shortcode for the image. */
|
||||
QUrl url; /**< The mxc URL for this image. */
|
||||
std::optional<QString> body; /**< An optional text body for this image. */
|
||||
std::optional<Quotient::EventContent::ImageInfo> info; /**< The ImageInfo object used for the info block of m.sticker events. */
|
||||
Quotient::Omittable<QString> body; /**< An optional text body for this image. */
|
||||
Quotient::Omittable<Quotient::EventContent::ImageInfo> info; /**< The ImageInfo object used for the info block of m.sticker events. */
|
||||
/**
|
||||
* @brief An array of the usages for this image.
|
||||
*
|
||||
* The possible values match those of the usage key of a pack object.
|
||||
*/
|
||||
std::optional<QStringList> usage;
|
||||
Quotient::Omittable<QStringList> usage;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
*
|
||||
* @sa Pack
|
||||
*/
|
||||
std::optional<Pack> pack;
|
||||
Quotient::Omittable<Pack> pack;
|
||||
|
||||
/**
|
||||
* @brief Return a vector of images in the pack.
|
||||
|
||||
14
src/events/serveraclevent.h
Normal file
14
src/events/serveraclevent.h
Normal file
@@ -0,0 +1,14 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/events/simplestateevents.h>
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
|
||||
// Defined so we can directly switch on type.
|
||||
DEFINE_SIMPLE_STATE_EVENT(ServerAclEvent, "m.room.server_acl", bool, allow_ip_literals, "allow_ip_literals")
|
||||
|
||||
} // namespace Quotient
|
||||
@@ -14,15 +14,12 @@ Q_SCRIPTABLE RemoteActions FakeRunner::Actions()
|
||||
|
||||
Q_SCRIPTABLE RemoteMatches FakeRunner::Match(const QString &searchTerm)
|
||||
{
|
||||
Q_UNUSED(searchTerm);
|
||||
QCoreApplication::quit();
|
||||
return {};
|
||||
}
|
||||
|
||||
Q_SCRIPTABLE void FakeRunner::Run(const QString &id, const QString &actionId)
|
||||
{
|
||||
Q_UNUSED(id);
|
||||
Q_UNUSED(actionId);
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
|
||||
@@ -36,4 +33,4 @@ FakeRunner::FakeRunner()
|
||||
qDBusRegisterMetaType<RemoteImage>();
|
||||
}
|
||||
|
||||
#include "moc_fakerunner.cpp"
|
||||
#include "moc_fakerunner.cpp"
|
||||
@@ -6,17 +6,27 @@
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/e2ee/sssshandler.h>
|
||||
#include <Quotient/keyverificationsession.h>
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
#include <Quotient/keyimport.h>
|
||||
#if __has_include("Quotient/e2ee/sssshandler.h")
|
||||
#include <Quotient/e2ee/sssshandler.h>
|
||||
#endif
|
||||
|
||||
#include "controller.h"
|
||||
#include "neochatconfig.h"
|
||||
|
||||
struct ForeignConfig {
|
||||
Q_GADGET
|
||||
QML_FOREIGN(NeoChatConfig)
|
||||
QML_NAMED_ELEMENT(Config)
|
||||
QML_SINGLETON
|
||||
public:
|
||||
static NeoChatConfig *create(QQmlEngine *, QJSEngine *)
|
||||
{
|
||||
QQmlEngine::setObjectOwnership(NeoChatConfig::self(), QQmlEngine::CppOwnership);
|
||||
return NeoChatConfig::self();
|
||||
}
|
||||
};
|
||||
|
||||
struct ForeignAccountRegistry {
|
||||
Q_GADGET
|
||||
QML_FOREIGN(Quotient::AccountRegistry)
|
||||
@@ -37,17 +47,10 @@ struct ForeignKeyVerificationSession {
|
||||
QML_UNCREATABLE("")
|
||||
};
|
||||
|
||||
#if __has_include("Quotient/e2ee/sssshandler.h")
|
||||
struct ForeignSSSSHandler {
|
||||
Q_GADGET
|
||||
QML_FOREIGN(Quotient::SSSSHandler)
|
||||
QML_NAMED_ELEMENT(SSSSHandler)
|
||||
};
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
struct ForeignKeyImport {
|
||||
Q_GADGET
|
||||
QML_SINGLETON
|
||||
QML_FOREIGN(Quotient::KeyImport)
|
||||
QML_NAMED_ELEMENT(KeyImport)
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "identityserverhelper.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <Quotient/networkaccessmanager.h>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
|
||||
IdentityServerHelper::IdentityServerHelper(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
NeoChatConnection *IdentityServerHelper::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
void IdentityServerHelper::setConnection(NeoChatConnection *connection)
|
||||
{
|
||||
if (m_connection == connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_connection != nullptr) {
|
||||
m_connection->disconnect(this);
|
||||
}
|
||||
|
||||
m_connection = connection;
|
||||
Q_EMIT connectionChanged();
|
||||
}
|
||||
|
||||
QString IdentityServerHelper::url() const
|
||||
{
|
||||
return m_url;
|
||||
}
|
||||
|
||||
void IdentityServerHelper::setUrl(const QString &url)
|
||||
{
|
||||
if (url == m_url) {
|
||||
return;
|
||||
}
|
||||
m_url = url;
|
||||
Q_EMIT urlChanged();
|
||||
|
||||
checkUrl();
|
||||
}
|
||||
|
||||
IdentityServerHelper::IdServerStatus IdentityServerHelper::status() const
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
void IdentityServerHelper::checkUrl()
|
||||
{
|
||||
if (m_idServerCheckRequest != nullptr) {
|
||||
m_idServerCheckRequest->abort();
|
||||
m_idServerCheckRequest.clear();
|
||||
}
|
||||
|
||||
if (m_url == m_connection->identityServer().toString()) {
|
||||
m_status = Match;
|
||||
Q_EMIT statusChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_url.isEmpty()) {
|
||||
m_status = Valid;
|
||||
Q_EMIT statusChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto requestUrl = QUrl(m_url + QStringLiteral("/_matrix/identity/v2"));
|
||||
if (!(requestUrl.scheme() == QStringLiteral("https") || requestUrl.scheme() == QStringLiteral("http"))) {
|
||||
m_status = Invalid;
|
||||
Q_EMIT statusChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
QNetworkRequest request(requestUrl);
|
||||
m_idServerCheckRequest = Quotient::NetworkAccessManager::instance()->get(request);
|
||||
connect(m_idServerCheckRequest, &QNetworkReply::finished, this, [this]() {
|
||||
if (m_idServerCheckRequest->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200) {
|
||||
m_status = Valid;
|
||||
Q_EMIT statusChanged();
|
||||
} else {
|
||||
m_status = Invalid;
|
||||
Q_EMIT statusChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void IdentityServerHelper::setIdentityServer()
|
||||
{
|
||||
if (m_url == m_connection->identityServer().toString()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_connection->setAccountData(QLatin1String("m.identity_server"), {{QLatin1String("base_url"), m_url}});
|
||||
m_status = Ready;
|
||||
Q_EMIT statusChanged();
|
||||
}
|
||||
|
||||
void IdentityServerHelper::clearIdentityServer()
|
||||
{
|
||||
if (m_connection->identityServer().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
m_connection->setAccountData(QLatin1String("m.identity_server"), {{QLatin1String("base_url"), QString()}});
|
||||
m_status = Ready;
|
||||
Q_EMIT statusChanged();
|
||||
}
|
||||
|
||||
#include "moc_identityserverhelper.cpp"
|
||||
@@ -1,88 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
|
||||
class NeoChatConnection;
|
||||
|
||||
/**
|
||||
* @class IdentityServerHelper
|
||||
*
|
||||
* This class is designed to help the process of setting an identity server for the account.
|
||||
* It will manage the various stages of verification and authentication.
|
||||
*/
|
||||
class IdentityServerHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
/**
|
||||
* @brief The connection to add a 3PID to.
|
||||
*/
|
||||
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
|
||||
/**
|
||||
* @brief The URL for the desired server.
|
||||
*/
|
||||
Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged)
|
||||
|
||||
/**
|
||||
* @brief The current status.
|
||||
*/
|
||||
Q_PROPERTY(IdServerStatus status READ status NOTIFY statusChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief The current status for adding an identity server
|
||||
*/
|
||||
enum IdServerStatus {
|
||||
Ready, /**< The process is ready to start. I.e. there is no ongoing attempt to set a new server. */
|
||||
Valid, /**< The server URL is valid. */
|
||||
Invalid, /**< The server URL is invalid. */
|
||||
Match, /**< The server URL is the one that is already configured. */
|
||||
Other, /**< An unknown problem occurred. */
|
||||
};
|
||||
Q_ENUM(IdServerStatus)
|
||||
|
||||
explicit IdentityServerHelper(QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] NeoChatConnection *connection() const;
|
||||
void setConnection(NeoChatConnection *connection);
|
||||
|
||||
[[nodiscard]] QString url() const;
|
||||
void setUrl(const QString &url);
|
||||
|
||||
[[nodiscard]] IdServerStatus status() const;
|
||||
|
||||
/**
|
||||
* @brief Set the current URL as the user's identity server.
|
||||
*
|
||||
* Will do nothing if the URL isn't a valid identity server.
|
||||
*/
|
||||
Q_INVOKABLE void setIdentityServer();
|
||||
|
||||
/**
|
||||
* @brief Clear the user's identity server.
|
||||
*/
|
||||
Q_INVOKABLE void clearIdentityServer();
|
||||
|
||||
Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
void urlChanged();
|
||||
void statusChanged();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
|
||||
IdServerStatus m_status = Ready;
|
||||
QString m_url;
|
||||
|
||||
QPointer<QNetworkReply> m_idServerCheckRequest;
|
||||
|
||||
void checkUrl();
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "neochatadd3pidjob.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
NeochatAdd3PIdJob::NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const Omittable<QJsonObject> &auth)
|
||||
: BaseJob(HttpVerb::Post, QStringLiteral("Add3PIDJob"), makePath("/_matrix/client/v3", "/account/3pid/add"))
|
||||
{
|
||||
QJsonObject _dataJson;
|
||||
addParam<IfNotEmpty>(_dataJson, QStringLiteral("auth"), auth);
|
||||
addParam<>(_dataJson, QStringLiteral("client_secret"), clientSecret);
|
||||
addParam<>(_dataJson, QStringLiteral("sid"), sid);
|
||||
setRequestData({_dataJson});
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/omittable.h>
|
||||
|
||||
class NeochatAdd3PIdJob : public Quotient::BaseJob
|
||||
{
|
||||
public:
|
||||
explicit NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const Quotient::Omittable<QJsonObject> &auth = {});
|
||||
};
|
||||
@@ -4,11 +4,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
|
||||
#include <Quotient/omittable.h>
|
||||
|
||||
class NeochatChangePasswordJob : public Quotient::BaseJob
|
||||
{
|
||||
public:
|
||||
explicit NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const Quotient::Omittable<QJsonObject> &auth = {});
|
||||
explicit NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const Quotient::Omittable<QJsonObject> &auth = Quotient::none);
|
||||
};
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
class NeoChatDeactivateAccountJob : public Quotient::BaseJob
|
||||
{
|
||||
public:
|
||||
explicit NeoChatDeactivateAccountJob(const Quotient::Omittable<QJsonObject> &auth = {});
|
||||
explicit NeoChatDeactivateAccountJob(const Quotient::Omittable<QJsonObject> &auth = Quotient::none);
|
||||
};
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
class NeochatDeleteDeviceJob : public Quotient::BaseJob
|
||||
{
|
||||
public:
|
||||
explicit NeochatDeleteDeviceJob(const QString &deviceId, const Quotient::Omittable<QJsonObject> &auth = {});
|
||||
explicit NeochatDeleteDeviceJob(const QString &deviceId, const Quotient::Omittable<QJsonObject> &auth = Quotient::none);
|
||||
};
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "neochatgetcommonroomsjob.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
NeochatGetCommonRoomsJob::NeochatGetCommonRoomsJob(const QString &userId)
|
||||
: BaseJob(HttpVerb::Get,
|
||||
QStringLiteral("GetCommonRoomsJob"),
|
||||
QStringLiteral("/_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms").toLatin1(),
|
||||
QUrlQuery({{QStringLiteral("user_id"), userId}}))
|
||||
{
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/omittable.h>
|
||||
|
||||
// TODO: Upstream to libQuotient
|
||||
class NeochatGetCommonRoomsJob : public Quotient::BaseJob
|
||||
{
|
||||
public:
|
||||
explicit NeochatGetCommonRoomsJob(const QString &userId);
|
||||
};
|
||||
@@ -89,23 +89,38 @@ bool LinkPreviewer::empty() const
|
||||
return m_url.isEmpty();
|
||||
}
|
||||
|
||||
QList<QUrl> LinkPreviewer::linkPreviews(QString string)
|
||||
QUrl LinkPreviewer::linkPreview(const Quotient::RoomMessageEvent *event)
|
||||
{
|
||||
auto data = string.remove(TextRegex::removeRichReply);
|
||||
if (event == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QString text;
|
||||
if (event->hasTextContent()) {
|
||||
auto textContent = static_cast<const Quotient::EventContent::TextContent *>(event->content());
|
||||
if (textContent) {
|
||||
text = textContent->body;
|
||||
} else {
|
||||
text = event->plainBody();
|
||||
}
|
||||
} else {
|
||||
text = event->plainBody();
|
||||
}
|
||||
|
||||
auto data = text.remove(TextRegex::removeRichReply);
|
||||
auto linksMatch = TextRegex::url.globalMatch(data);
|
||||
QList<QUrl> links;
|
||||
while (linksMatch.hasNext()) {
|
||||
auto link = linksMatch.next().captured();
|
||||
if (!link.contains(QStringLiteral("matrix.to")) && !links.contains(QUrl(link))) {
|
||||
links += QUrl(link);
|
||||
if (!link.contains(QStringLiteral("matrix.to"))) {
|
||||
return QUrl(link);
|
||||
}
|
||||
}
|
||||
return links;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool LinkPreviewer::hasPreviewableLinks(const QString &string)
|
||||
bool LinkPreviewer::hasPreviewableLinks(const Quotient::RoomMessageEvent *event)
|
||||
{
|
||||
return !linkPreviews(string).isEmpty();
|
||||
return !linkPreview(event).isEmpty();
|
||||
}
|
||||
|
||||
#include "moc_linkpreviewer.cpp"
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
#include <QQmlEngine>
|
||||
#include <QUrl>
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class RoomMessageEvent;
|
||||
}
|
||||
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
@@ -66,19 +71,19 @@ public:
|
||||
[[nodiscard]] bool empty() const;
|
||||
|
||||
/**
|
||||
* @brief Whether the given string has at least 1 pre-viewable link.
|
||||
* @brief Whether the given event has at least 1 pre-viewable link.
|
||||
*
|
||||
* A link is only pre-viewable if it is http, https or something starting with www.
|
||||
*/
|
||||
static bool hasPreviewableLinks(const QString &string);
|
||||
static bool hasPreviewableLinks(const Quotient::RoomMessageEvent *event);
|
||||
|
||||
/**
|
||||
* @brief Return previewable links from the given string.
|
||||
* @brief Return the link to be previewed from the given event.
|
||||
*
|
||||
* This function is designed to give only links that should be previewed so
|
||||
* http, https or something starting with www. The first valid link is returned.
|
||||
*/
|
||||
static QList<QUrl> linkPreviews(QString string);
|
||||
static QUrl linkPreview(const Quotient::RoomMessageEvent *event);
|
||||
|
||||
private:
|
||||
bool m_loaded;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
#include <Quotient/settings.h>
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
@@ -54,19 +53,14 @@ void LoginHelper::init()
|
||||
m_connection = new NeoChatConnection();
|
||||
}
|
||||
m_connection->resolveServer(m_matrixId);
|
||||
connect(
|
||||
m_connection.get(),
|
||||
&Connection::loginFlowsChanged,
|
||||
this,
|
||||
[this]() {
|
||||
setHomeserverReachable(true);
|
||||
m_testing = false;
|
||||
Q_EMIT testingChanged();
|
||||
m_supportsSso = m_connection->supportsSso();
|
||||
m_supportsPassword = m_connection->supportsPasswordAuth();
|
||||
Q_EMIT loginFlowsChanged();
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
connectSingleShot(m_connection.get(), &Connection::loginFlowsChanged, this, [this]() {
|
||||
setHomeserverReachable(true);
|
||||
m_testing = false;
|
||||
Q_EMIT testingChanged();
|
||||
m_supportsSso = m_connection->supportsSso();
|
||||
m_supportsPassword = m_connection->supportsPasswordAuth();
|
||||
Q_EMIT loginFlowsChanged();
|
||||
});
|
||||
});
|
||||
connect(m_connection, &Connection::connected, this, [this] {
|
||||
Q_EMIT connected();
|
||||
@@ -78,7 +72,7 @@ void LoginHelper::init()
|
||||
account.setHomeserver(m_connection->homeserver());
|
||||
account.setDeviceId(m_connection->deviceId());
|
||||
account.setDeviceName(m_deviceName);
|
||||
if (!Controller::instance().saveAccessTokenToKeyChain(account.userId(), m_connection->accessToken())) {
|
||||
if (!Controller::instance().saveAccessTokenToKeyChain(account, m_connection->accessToken())) {
|
||||
qWarning() << "Couldn't save access token";
|
||||
}
|
||||
account.sync();
|
||||
@@ -105,14 +99,9 @@ void LoginHelper::init()
|
||||
Q_EMIT Controller::instance().errorOccured(i18n("Network Error"), std::move(error));
|
||||
});
|
||||
|
||||
connect(
|
||||
m_connection.get(),
|
||||
&Connection::syncDone,
|
||||
this,
|
||||
[this]() {
|
||||
Q_EMIT loaded();
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
connectSingleShot(m_connection.get(), &Connection::syncDone, this, [this]() {
|
||||
Q_EMIT loaded();
|
||||
});
|
||||
}
|
||||
|
||||
void LoginHelper::setHomeserverReachable(bool reachable)
|
||||
@@ -192,16 +181,11 @@ QUrl LoginHelper::ssoUrl() const
|
||||
void LoginHelper::loginWithSso()
|
||||
{
|
||||
m_connection->resolveServer(m_matrixId);
|
||||
connect(
|
||||
m_connection.get(),
|
||||
&Connection::loginFlowsChanged,
|
||||
this,
|
||||
[this]() {
|
||||
SsoSession *session = m_connection->prepareForSso(m_deviceName);
|
||||
m_ssoUrl = session->ssoUrl();
|
||||
Q_EMIT ssoUrlChanged();
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
connectSingleShot(m_connection.get(), &Connection::loginFlowsChanged, this, [this]() {
|
||||
SsoSession *session = m_connection->prepareForSso(m_deviceName);
|
||||
m_ssoUrl = session->ssoUrl();
|
||||
Q_EMIT ssoUrlChanged();
|
||||
});
|
||||
}
|
||||
|
||||
bool LoginHelper::testing() const
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
qt_add_library(login STATIC)
|
||||
ecm_add_qml_module(login GENERATE_PLUGIN_SOURCE
|
||||
qt_add_qml_module(login
|
||||
URI org.kde.neochat.login
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/login
|
||||
QML_FILES
|
||||
@@ -20,5 +20,4 @@ ecm_add_qml_module(login GENERATE_PLUGIN_SOURCE
|
||||
Sso.qml
|
||||
Terms.qml
|
||||
Username.qml
|
||||
Oidc.qml
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user