Compare commits
5 Commits
v25.08.0
...
work/tobia
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88e0c38e5f | ||
|
|
20a6b90904 | ||
|
|
fcd7a320e7 | ||
|
|
2f39e70b67 | ||
|
|
ddc16a17d2 |
@@ -2,5 +2,7 @@
|
||||
; SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
[BlueprintSettings]
|
||||
kde/frameworks/extra-cmake-modules.version=master
|
||||
kde/unreleased/kirigami-addons.version=master
|
||||
kde/applications/neochat.packageAppx=True
|
||||
libs/qt.qtMajorVersion=6
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"id": "org.kde.neochat",
|
||||
"branch": "master",
|
||||
"runtime": "org.kde.Platform",
|
||||
"runtime-version": "6.9",
|
||||
"runtime-version": "6.8",
|
||||
"sdk": "org.kde.Sdk",
|
||||
"command": "neochat",
|
||||
"tags": [
|
||||
@@ -150,19 +150,22 @@
|
||||
"builddir": true
|
||||
},
|
||||
{
|
||||
"name": "kunifiedpush",
|
||||
"name": "qcoro",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"builddir": true,
|
||||
"config-opts": [
|
||||
"-DQCORO_BUILD_EXAMPLES=OFF",
|
||||
"-DBUILD_TESTING=OFF"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://download.kde.org/stable/kunifiedpush/kunifiedpush-1.0.0.tar.xz",
|
||||
"sha256": "2ddeba21306d0307114ec50a2c38159ec62359f9fc6cdd58da30a369fbd550cf",
|
||||
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.11.0.tar.gz",
|
||||
"sha256": "9942c5b4c533192f6c5954dc6d10178b3829075e6a621b67df73f0a4b74d8297",
|
||||
"x-checker-data": {
|
||||
"type": "anitya",
|
||||
"project-id": 375055,
|
||||
"project-id": 236236,
|
||||
"stable-only": true,
|
||||
"url-template": "https://download.kde.org/stable/kunifiedpush/kunifiedpush-$version.tar.xz"
|
||||
"url-template": "https://github.com/danvratil/qcoro/archive/refs/tags/v$version.tar.gz"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -12,7 +12,7 @@ include:
|
||||
- /gitlab-templates/linux-qt6.yml
|
||||
- /gitlab-templates/linux-qt6-next.yml
|
||||
- /gitlab-templates/windows-qt6.yml
|
||||
- /gitlab-templates/freebsd-qt6.yml
|
||||
# - /gitlab-templates/freebsd-qt6.yml
|
||||
- /gitlab-templates/flatpak.yml
|
||||
- /gitlab-templates/snap-snapcraft-lxd.yml
|
||||
- /gitlab-templates/craft-android-qt6-apks.yml
|
||||
|
||||
@@ -34,7 +34,6 @@ Dependencies:
|
||||
'require':
|
||||
'frameworks/kdbusaddons': '@latest-kf6'
|
||||
'frameworks/purpose': '@latest-kf6'
|
||||
'libraries/kunifiedpush': '@latest-kf6'
|
||||
|
||||
- 'on': ['Linux']
|
||||
'require':
|
||||
|
||||
@@ -8,13 +8,13 @@ cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# KDE Applications version, managed by release script.
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "25")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "08")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "0")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "07")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||
|
||||
set(KF_MIN_VERSION "6.12")
|
||||
set(KF_MIN_VERSION "6.6")
|
||||
set(QT_MIN_VERSION "6.5")
|
||||
|
||||
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
|
||||
@@ -56,7 +56,7 @@ ecm_setup_version(${PROJECT_VERSION}
|
||||
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
|
||||
)
|
||||
|
||||
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg TextToSpeech WebView)
|
||||
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg Protobuf WebView)
|
||||
set_package_properties(Qt6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
@@ -75,7 +75,7 @@ set_package_properties(KF6Kirigami PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Kirigami application UI framework"
|
||||
)
|
||||
find_package(KF6KirigamiAddons 1.6.0 REQUIRED)
|
||||
find_package(KF6KirigamiAddons 0.7.2 REQUIRED)
|
||||
|
||||
if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT NEOCHAT_FLATPAK AND NOT NEOCHAT_APPIMAGE)
|
||||
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Purpose)
|
||||
@@ -107,7 +107,7 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
|
||||
find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(QuotientQt6 0.9.1)
|
||||
find_package(QuotientQt6 0.9)
|
||||
set_package_properties(QuotientQt6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
DESCRIPTION "Qt wrapper around Matrix API"
|
||||
@@ -115,6 +115,8 @@ set_package_properties(QuotientQt6 PROPERTIES
|
||||
PURPOSE "Talk with matrix server"
|
||||
)
|
||||
|
||||
find_package(LiveKit REQUIRED)
|
||||
|
||||
find_package(cmark)
|
||||
set_package_properties(cmark PROPERTIES
|
||||
TYPE REQUIRED
|
||||
@@ -147,24 +149,16 @@ set_package_properties(KF6DocTools PROPERTIES DESCRIPTION
|
||||
TYPE OPTIONAL
|
||||
)
|
||||
|
||||
option(WITH_UNIFIEDPUSH "Build with KUnifiedPush support" ON)
|
||||
|
||||
if (ANDROID OR APPLE OR WIN32 OR HAIKU)
|
||||
set(WITH_UNIFIEDPUSH OFF)
|
||||
endif()
|
||||
|
||||
if (WITH_UNIFIEDPUSH)
|
||||
find_package(KUnifiedPush)
|
||||
set_package_properties(KUnifiedPush PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Push notification support"
|
||||
URL "https://invent.kde.org/libraries/kunifiedpush"
|
||||
)
|
||||
endif()
|
||||
find_package(KUnifiedPush QUIET)
|
||||
set_package_properties(KUnifiedPush PROPERTIES
|
||||
TYPE OPTIONAL
|
||||
PURPOSE "Push notification support"
|
||||
URL "https://invent.kde.org/libraries/kunifiedpush"
|
||||
)
|
||||
|
||||
if(ANDROID)
|
||||
find_package(Sqlite3)
|
||||
set(BUILD_TESTING FALSE)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/android/version.gradle.in ${CMAKE_BINARY_DIR}/version.gradle)
|
||||
endif()
|
||||
|
||||
ki18n_install(po)
|
||||
@@ -179,9 +173,9 @@ add_definitions(-DQT_NO_FOREACH)
|
||||
add_subdirectory(src)
|
||||
|
||||
if (BUILD_TESTING)
|
||||
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test HttpServer)
|
||||
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test)
|
||||
add_subdirectory(autotests)
|
||||
# add_subdirectory(appiumtests)
|
||||
add_subdirectory(appiumtests)
|
||||
if (NOT ANDROID)
|
||||
add_subdirectory(memorytests)
|
||||
endif()
|
||||
@@ -192,7 +186,7 @@ if(KF6DocTools_FOUND)
|
||||
add_subdirectory(doc)
|
||||
endif()
|
||||
|
||||
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
if (NOT ANDROID)
|
||||
file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES src/*.cpp src/*.h)
|
||||
|
||||
@@ -51,6 +51,10 @@ is primarily aimed at Linux development.
|
||||
For Windows and Android [Craft](https://invent.kde.org/packaging/craft) is the primary choice. There are guides for setting up
|
||||
development environments for [Windows](https://community.kde.org/Get_Involved/development/Windows) and [Android](https://develop.kde.org/docs/packaging/android/building_applications/).
|
||||
|
||||
### Building with support for voice / video calls
|
||||
|
||||
[LiveKit](https://livekit.io) is needed for call support. Build the [Rust SDK](https://github.com/livekit/rust-sdks) and copy `liblivekit_ffi.so` to your usual library folder. Copy `livekit_ffi.h` to somewhere under your usual include folder. NeoChat should then automatically pick it up.
|
||||
|
||||
## Running
|
||||
|
||||
Just start the executable in your preferred way - either from the build directory or from the installed location.
|
||||
|
||||
@@ -42,13 +42,13 @@ SPDX-FileCopyrightText = "2021 Carl Schwan <carlschwan@kde.org>"
|
||||
SPDX-License-Identifier = "BSD-2-Clause"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/app/neochatconfig.kcfg"
|
||||
path = "src/neochatconfig.kcfg"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2020-2021 Carl Schwan <carlschwan@kde.org>, Tobias Fella <tobias.fella@kde.org>"
|
||||
SPDX-License-Identifier = "BSD-2-Clause"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/app/neochat.notifyrc"
|
||||
path = "src/neochat.notifyrc"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2020 Tobias Fella <tobias.fella@kde.org>"
|
||||
SPDX-License-Identifier = "BSD-2-Clause"
|
||||
|
||||
@@ -23,7 +23,8 @@ repositories {
|
||||
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply from: '../ecm-version.gradle'
|
||||
apply from: '../version.gradle'
|
||||
def timestamp = (int)(new Date().getTime()/1000)
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
|
||||
@@ -78,9 +79,9 @@ android {
|
||||
targetSdkVersion qtTargetSdkVersion
|
||||
applicationId "org.kde.neochat"
|
||||
namespace "org.kde.neochat"
|
||||
versionCode ecmVersionCode
|
||||
versionName ecmVersionName
|
||||
manifestPlaceholders = [versionName: ecmVersionName, versionCode: ecmVersionCode]
|
||||
versionCode timestamp
|
||||
versionName projectVersionFull
|
||||
manifestPlaceholders = [versionName: projectVersionFull, versionCode: timestamp]
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
|
||||
6
android/version.gradle.in
Normal file
6
android/version.gradle.in
Normal file
@@ -0,0 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
ext {
|
||||
projectVersionFull = "@NEOCHAT_VERSION@"
|
||||
}
|
||||
@@ -3,10 +3,6 @@
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_library(neochat_server STATIC server.cpp)
|
||||
|
||||
target_link_libraries(neochat_server PUBLIC Qt::HttpServer QuotientQt6)
|
||||
|
||||
add_definitions(-DDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" )
|
||||
|
||||
ecm_add_test(
|
||||
@@ -15,11 +11,13 @@ ecm_add_test(
|
||||
TEST_NAME neochatroomtest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
texthandlertest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME texthandlertest
|
||||
)
|
||||
if (NOT $ENV{KDE_CI})
|
||||
ecm_add_test(
|
||||
texthandlertest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME texthandlertest
|
||||
)
|
||||
endif()
|
||||
|
||||
ecm_add_test(
|
||||
delegatesizehelpertest.cpp
|
||||
@@ -89,6 +87,6 @@ ecm_add_test(
|
||||
|
||||
ecm_add_test(
|
||||
actionstest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test neochat_server
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME actionstest
|
||||
)
|
||||
|
||||
@@ -6,11 +6,9 @@
|
||||
#include <QSignalSpy>
|
||||
#include <QVariantList>
|
||||
|
||||
#include "accountmanager.h"
|
||||
#include "chatbarcache.h"
|
||||
#include "models/actionsmodel.h"
|
||||
|
||||
#include "server.h"
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
@@ -23,12 +21,10 @@ class ActionsTest : public QObject
|
||||
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
NeoChatRoom *room = nullptr;
|
||||
TestUtils::TestRoom *room = nullptr;
|
||||
|
||||
void expectMessage(const QString &actionName, const QString &args, MessageType::Type type, const QString &message);
|
||||
|
||||
Server server;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void testActions();
|
||||
@@ -38,23 +34,8 @@ private Q_SLOTS:
|
||||
|
||||
void ActionsTest::initTestCase()
|
||||
{
|
||||
Connection::setRoomType<NeoChatRoom>();
|
||||
server.start();
|
||||
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
|
||||
auto accountManager = new AccountManager(true);
|
||||
QSignalSpy spy(accountManager, &AccountManager::connectionAdded);
|
||||
connection = accountManager->accounts()->front();
|
||||
auto roomId = server.createRoom(u"@user:localhost:1234"_s);
|
||||
server.inviteUser(roomId, u"@invited:example.com"_s);
|
||||
server.banUser(roomId, u"@banned:example.com"_s);
|
||||
server.joinUser(roomId, u"@example:example.com"_s);
|
||||
|
||||
QSignalSpy syncSpy(connection, &Connection::syncDone);
|
||||
// We need to wait for two syncs, as the next one won't have the changes yet
|
||||
QVERIFY(syncSpy.wait());
|
||||
QVERIFY(syncSpy.wait());
|
||||
room = dynamic_cast<NeoChatRoom *>(connection->room(roomId));
|
||||
QVERIFY(room);
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-min-sync.json"));
|
||||
}
|
||||
|
||||
void ActionsTest::testActions_data()
|
||||
@@ -109,7 +90,7 @@ static ActionsModel::Action findAction(const QString &name)
|
||||
void ActionsTest::expectMessage(const QString &actionName, const QString &args, MessageType::Type type, const QString &message)
|
||||
{
|
||||
auto action = findAction(actionName);
|
||||
QSignalSpy spy(room, &NeoChatRoom::showMessage);
|
||||
QSignalSpy spy(room, &TestUtils::TestRoom::showMessage);
|
||||
auto result = action.handle(args, room, nullptr);
|
||||
auto expected = QVariantList {type, message};
|
||||
auto signal = spy.takeFirst();
|
||||
@@ -125,26 +106,14 @@ void ActionsTest::testInvite()
|
||||
QCOMPARE(room->memberState(u"@banned:example.com"_s), Membership::Ban);
|
||||
expectMessage(u"invite"_s, connection->userId(), MessageType::Positive, u"You are already in this room."_s);
|
||||
QCOMPARE(room->memberState(connection->userId()), Membership::Join);
|
||||
expectMessage(u"invite"_s, u"@example:example.com"_s, MessageType::Information, u"@example:example.com is already in this room."_s);
|
||||
QCOMPARE(room->memberState(u"@example:example.com"_s), Membership::Join);
|
||||
expectMessage(u"invite"_s, u"@example:example.org"_s, MessageType::Information, u"@example:example.org is already in this room."_s);
|
||||
QCOMPARE(room->memberState(u"@example:example.org"_s), Membership::Join);
|
||||
|
||||
QCOMPARE(room->memberState(u"@user:example.com"_s), Membership::Leave);
|
||||
expectMessage(u"invite"_s, u"@user:example.com"_s, MessageType::Positive, u"@user:example.com was invited into this room."_s);
|
||||
|
||||
QSignalSpy spy(room, &NeoChatRoom::changed);
|
||||
QVERIFY(spy.wait());
|
||||
|
||||
auto tries = 0;
|
||||
|
||||
while (room->memberState(u"@user:example.com"_s) != Membership::Invite) {
|
||||
QVERIFY(spy.wait());
|
||||
tries += 1;
|
||||
if (tries > 3) {
|
||||
QVERIFY(false);
|
||||
}
|
||||
}
|
||||
|
||||
QCOMPARE(room->memberState(u"@user:example.com"_s), Membership::Invite);
|
||||
//TODO mock server, wait for invite state to change
|
||||
//TODO QCOMPARE(room->memberState(u"@user:example.com"_s), Membership::Invite);
|
||||
}
|
||||
|
||||
QTEST_MAIN(ActionsTest)
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDNTCCAh2gAwIBAgIUXbyWfTfcvVLrVB1qx36pW/7IkwMwDQYJKoZIhvcNAQEL
|
||||
BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE
|
||||
CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAgFw0yNDEyMjQxNTAxMDNaGA8yNTcyMDcy
|
||||
NDE1MDEwM1owQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEc
|
||||
MBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggEPADCCAQoCggEBAKlxZ540TQ1uUDAR7ZJ9ue0PzcD2dPmblIIddyekvZS59V7X
|
||||
drhamclXpHE2EelR87Sexst0BaHH/jmrHwxCtwbeXHZ8ueJHkGHJ5DLZCCiwfG+Q
|
||||
gml7wlSXxXz37vie2tdlZh2yJSM8yvLAYceHb2zOskaGvul7ZITIS0JrPc3o6VZk
|
||||
+MYGkYtA2JfUsv3jH4oQbxOf7RXqhWNAXbB+3hlwRBwMIdyoBNK6YS9QSrTeS9jj
|
||||
UqgO5QmaQZOVvpaPf1Y/rHHLd2Qa6+a/cCJ1sr2biagb75AihpQFsK/oy6D1PP70
|
||||
zTe7hPWn/efEpmtCV7CQ8ti4cRu0Kjy0T8grtCsCAwEAAaMhMB8wHQYDVR0OBBYE
|
||||
FIFlylzwADNLfgTDNkhFeFelaEDxMA0GCSqGSIb3DQEBCwUAA4IBAQBQ2rw4GLIU
|
||||
v+GY7Qru9LttkrQPd2bZXKxDMd/jT+wjmMVtqS4MAsCuDYwaYLjU1aWyqy0mN+lY
|
||||
A17kD0VjBNBy45sYqkZveY0ks8mCScBemtrIDmjz2tiueecBIEASwEPBOZgv5/MV
|
||||
cz864FiChF+2r8Zl8bhycGy9DEpRjzYKvIQWSDHQ3zpuh3iBnjfoieLHWX2kKCpk
|
||||
ouS3V6485rHNCWsZT5IcCwfBFQkOuWRJpIazpz4AfwZh1TK9+bgiKA5EyZjSNrKw
|
||||
xGQSpMSTRQMB0/FOCL/AixhN9unVFUViqUcdtSfoHE1VyBHv9kDT/cYms/Xl4B0t
|
||||
/ZSQJ/D/Km1+
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,28 +0,0 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCpcWeeNE0NblAw
|
||||
Ee2SfbntD83A9nT5m5SCHXcnpL2UufVe13a4WpnJV6RxNhHpUfO0nsbLdAWhx/45
|
||||
qx8MQrcG3lx2fLniR5BhyeQy2QgosHxvkIJpe8JUl8V89+74ntrXZWYdsiUjPMry
|
||||
wGHHh29szrJGhr7pe2SEyEtCaz3N6OlWZPjGBpGLQNiX1LL94x+KEG8Tn+0V6oVj
|
||||
QF2wft4ZcEQcDCHcqATSumEvUEq03kvY41KoDuUJmkGTlb6Wj39WP6xxy3dkGuvm
|
||||
v3AidbK9m4moG++QIoaUBbCv6Mug9Tz+9M03u4T1p/3nxKZrQlewkPLYuHEbtCo8
|
||||
tE/IK7QrAgMBAAECggEAH9qmeKrra2F4KLlOGNKS//qPGz4Z+ozhi95/NpA1Zb7Z
|
||||
3pUSCBFcROo5i2D3WA4kiymoRLpQjrv60puVcCggoWVvK4VCKsR6Y6/hOx/q9T9M
|
||||
fWrE4ZC3FVEc+uPfZJT0nja9TkrdyXSV0LITD8Ap1eI7yJ9vR5R/bqj64QcpLMrU
|
||||
QeoQIy1oTMR+qdjj33duyRwBZU3Yf8FRB2iW6OILZ8hzFo1jngec7dph9a1RK4e0
|
||||
mEPdc9ywsKlDM7P0Y7zdmjar5XtQn87GiwNhz23f1fzCC2axLtOW0Xm4e4Qumehb
|
||||
WrIi6Vfq8IWMglU7QrBJ7iR0Ls+XoKA5GxomV2IJZQKBgQDoIkOl5YGPQ3iGR+WK
|
||||
e5/2Ml4G/uURzYiOlzSsyfoPXyO4EI2BJd5HkH+EvfgRx4xKkxUZRJdzR7llYPl8
|
||||
BFYcFitvhO8SbD0mNAB5YW7f+3v1pgEN2umzoKd389Zx5WqTZ7YB1VG5RN/Q1JJL
|
||||
2JM0Xgamq2vNtx3roRPxDBeW7QKBgQC63R/bmACJbgIzfaVBX4Zie3NQG0/Hf+gF
|
||||
LnBwUmQDZOR7MY+kSiIUVMn3NuZRiCSCFBVwApruyK8r535JCibTVm5PWjvhFddY
|
||||
LgaPOCKGlm9TLScjoH1pErYgG3uJ4nXeRfXhg4mco6EkrC7RzQywrd0VDoqpuc1Y
|
||||
EKfEsYk8dwKBgE+mSh3nNOBKX1V73+f3aTiZqaeu2DyWkG+UtE9BclrJ40Cp9VPG
|
||||
AZH+o7KRWEgJdzqzYv7riSfWCWgesRv7hOxYMwktzLY+i3DLUQpVAy05ZhwwnJX7
|
||||
ckrfKfc/pGoqNLplUI8qecMfPciy14vMwR2r0Y5orTHFzi9mcqg35PQ1AoGAW2LX
|
||||
OLq+0HdHhk0Va8I+450CSRQCUUvhed87SANTPEG0Z/dWC3/h6NWKrGdh/k+5oxAV
|
||||
Z+EuSkdFPBCLt0bKtCKZ8h7sF+lplotz08kdQXsC2MfFU2wiySdIgK1QHp/tCxZl
|
||||
6LM+sqdnoJrAjwRcB3AQJkMlV1ox7ba/hbdZqYMCgYBS6+JUXSSASpm5ZHd32a8m
|
||||
xwryEZ7H6Hek6lvMHdxmwoKat5dCavxw64nrtyeeGZpg1W3zLLyamF9x/8kMyr6y
|
||||
KKvtBfJ5sCvAbt80o9Pbs6R3yDB3AKiD3s3PQK7lol1nhE/8IbsF2r8JEQVcYd/k
|
||||
oBzkl7MrMyLhhaCqSxwqQQ==
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -8,14 +8,14 @@
|
||||
"answers": [
|
||||
{
|
||||
"id": "option1",
|
||||
"org.matrix.msc1767.text": "option1text"
|
||||
"org.matrix.msc1767.text": "option1"
|
||||
},
|
||||
{
|
||||
"id": "option2",
|
||||
"org.matrix.msc1767.text": "option2text"
|
||||
"org.matrix.msc1767.text": "option2"
|
||||
}
|
||||
],
|
||||
"kind": "org.matrix.msc3381.poll.undisclosed",
|
||||
"kind": "org.matrix.msc3381.poll.disclosed",
|
||||
"max_selections": 1,
|
||||
"question": {
|
||||
"body": "test",
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QObject>
|
||||
#include <QQuickItem>
|
||||
#include <QTest>
|
||||
|
||||
#include "delegatesizehelper.h"
|
||||
@@ -31,7 +30,7 @@ void DelegateSizeHelperTest::risingPercentage_data()
|
||||
QTest::addColumn<int>("currentPercentageWidth");
|
||||
QTest::addColumn<qreal>("currentWidth");
|
||||
|
||||
QTest::newRow("zero") << qreal(0) << int(100) << qreal(0);
|
||||
QTest::newRow("zero") << qreal(0) << int(0) << qreal(0);
|
||||
QTest::newRow("one hundred") << qreal(100) << int(0) << qreal(0);
|
||||
QTest::newRow("one fifty") << qreal(150) << int(50) << qreal(75);
|
||||
QTest::newRow("two hundred") << qreal(200) << int(100) << qreal(200);
|
||||
@@ -44,18 +43,16 @@ void DelegateSizeHelperTest::risingPercentage()
|
||||
QFETCH(int, currentPercentageWidth);
|
||||
QFETCH(qreal, currentWidth);
|
||||
|
||||
auto item = QQuickItem();
|
||||
item.setWidth(parentWidth);
|
||||
|
||||
DelegateSizeHelper delegateSizeHelper;
|
||||
delegateSizeHelper.setParentItem(&item);
|
||||
delegateSizeHelper.setStartBreakpoint(100);
|
||||
delegateSizeHelper.setEndBreakpoint(200);
|
||||
delegateSizeHelper.setStartPercentWidth(0);
|
||||
delegateSizeHelper.setEndPercentWidth(100);
|
||||
|
||||
QCOMPARE(delegateSizeHelper.availablePercentageWidth(), currentPercentageWidth);
|
||||
QCOMPARE(delegateSizeHelper.availableWidth(), currentWidth);
|
||||
delegateSizeHelper.setParentWidth(parentWidth);
|
||||
|
||||
QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth);
|
||||
QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth);
|
||||
}
|
||||
|
||||
void DelegateSizeHelperTest::fallingPercentage_data()
|
||||
@@ -77,18 +74,16 @@ void DelegateSizeHelperTest::fallingPercentage()
|
||||
QFETCH(int, currentPercentageWidth);
|
||||
QFETCH(qreal, currentWidth);
|
||||
|
||||
auto item = QQuickItem();
|
||||
item.setWidth(parentWidth);
|
||||
|
||||
DelegateSizeHelper delegateSizeHelper;
|
||||
delegateSizeHelper.setParentItem(&item);
|
||||
delegateSizeHelper.setStartBreakpoint(100);
|
||||
delegateSizeHelper.setEndBreakpoint(200);
|
||||
delegateSizeHelper.setStartPercentWidth(100);
|
||||
delegateSizeHelper.setEndPercentWidth(0);
|
||||
|
||||
QCOMPARE(delegateSizeHelper.availablePercentageWidth(), currentPercentageWidth);
|
||||
QCOMPARE(delegateSizeHelper.availableWidth(), currentWidth);
|
||||
delegateSizeHelper.setParentWidth(parentWidth);
|
||||
|
||||
QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth);
|
||||
QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth);
|
||||
}
|
||||
|
||||
void DelegateSizeHelperTest::equalPercentage_data()
|
||||
@@ -110,18 +105,16 @@ void DelegateSizeHelperTest::equalPercentage()
|
||||
QFETCH(int, currentPercentageWidth);
|
||||
QFETCH(qreal, currentWidth);
|
||||
|
||||
auto item = QQuickItem();
|
||||
item.setWidth(parentWidth);
|
||||
|
||||
DelegateSizeHelper delegateSizeHelper;
|
||||
delegateSizeHelper.setParentItem(&item);
|
||||
delegateSizeHelper.setStartBreakpoint(100);
|
||||
delegateSizeHelper.setEndBreakpoint(200);
|
||||
delegateSizeHelper.setStartPercentWidth(50);
|
||||
delegateSizeHelper.setEndPercentWidth(50);
|
||||
|
||||
QCOMPARE(delegateSizeHelper.availablePercentageWidth(), currentPercentageWidth);
|
||||
QCOMPARE(delegateSizeHelper.availableWidth(), currentWidth);
|
||||
delegateSizeHelper.setParentWidth(parentWidth);
|
||||
|
||||
QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth);
|
||||
QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth);
|
||||
}
|
||||
|
||||
void DelegateSizeHelperTest::equalBreakpoint_data()
|
||||
@@ -131,9 +124,9 @@ void DelegateSizeHelperTest::equalBreakpoint_data()
|
||||
QTest::addColumn<int>("currentPercentageWidth");
|
||||
QTest::addColumn<qreal>("currentWidth");
|
||||
|
||||
QTest::newRow("start higher") << int(100) << int(0) << int(100) << qreal(1000);
|
||||
QTest::newRow("start higher") << int(100) << int(0) << int(-1) << qreal(0);
|
||||
QTest::newRow("equal") << int(50) << int(50) << int(50) << qreal(500);
|
||||
QTest::newRow("end higher") << int(0) << int(100) << int(100) << qreal(1000);
|
||||
QTest::newRow("end higher") << int(0) << int(100) << int(-1) << qreal(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,18 +140,16 @@ void DelegateSizeHelperTest::equalBreakpoint()
|
||||
QFETCH(int, currentPercentageWidth);
|
||||
QFETCH(qreal, currentWidth);
|
||||
|
||||
auto item = QQuickItem();
|
||||
item.setWidth(1000);
|
||||
|
||||
DelegateSizeHelper delegateSizeHelper;
|
||||
delegateSizeHelper.setParentItem(&item);
|
||||
delegateSizeHelper.setStartBreakpoint(100);
|
||||
delegateSizeHelper.setEndBreakpoint(100);
|
||||
delegateSizeHelper.setStartPercentWidth(startPercentageWidth);
|
||||
delegateSizeHelper.setEndPercentWidth(endPercentageWidth);
|
||||
|
||||
QCOMPARE(delegateSizeHelper.availablePercentageWidth(), currentPercentageWidth);
|
||||
QCOMPARE(delegateSizeHelper.availableWidth(), currentWidth);
|
||||
delegateSizeHelper.setParentWidth(1000);
|
||||
|
||||
QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth);
|
||||
QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(DelegateSizeHelperTest)
|
||||
|
||||
@@ -130,8 +130,7 @@ void EventHandlerTest::timeString()
|
||||
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1432735824654, QTimeZone(QTimeZone::UTC)).toLocalTime().time(), QLocale::ShortFormat));
|
||||
QCOMPARE(EventHandler::timeString(room, event, true),
|
||||
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1432735824654, QTimeZone(QTimeZone::UTC)).toLocalTime().date(), QLocale::ShortFormat));
|
||||
QCOMPARE(EventHandler::timeString(room, event, u"hh:mm"_s),
|
||||
QDateTime::fromMSecsSinceEpoch(1432735824654, QTimeZone(QTimeZone::LocalTime)).toString(u"hh:mm"_s));
|
||||
QCOMPARE(EventHandler::timeString(room, event, u"hh:mm"_s), QDateTime::fromMSecsSinceEpoch(1432735824654, QTimeZone(QTimeZone::UTC)).toString(u"hh:mm"_s));
|
||||
|
||||
const auto txID = room->postJson("m.room.message"_L1, event->fullJson());
|
||||
QCOMPARE(room->pendingEvents().size(), 1);
|
||||
|
||||
@@ -25,7 +25,6 @@ private Q_SLOTS:
|
||||
void MediaSizeHelperTest::uninitialized()
|
||||
{
|
||||
MediaSizeHelper mediasizehelper;
|
||||
mediasizehelper.setMaxSize(540, 540);
|
||||
QCOMPARE(mediasizehelper.currentSize(), QSize(540, qRound(qreal(NeoChatConfig::self()->mediaMaxWidth()) / qreal(16.0) * qreal(9.0))));
|
||||
}
|
||||
|
||||
@@ -61,7 +60,6 @@ void MediaSizeHelperTest::limits()
|
||||
QFETCH(QSize, currentSize);
|
||||
|
||||
MediaSizeHelper mediasizehelper;
|
||||
mediasizehelper.setMaxSize(540, 540);
|
||||
mediasizehelper.setMediaWidth(mediaWidth);
|
||||
mediasizehelper.setMediaHeight(mediaHeight);
|
||||
mediasizehelper.setContentMaxWidth(contentMaxWidth);
|
||||
|
||||
@@ -41,32 +41,29 @@ void PollHandlerTest::nullObject()
|
||||
auto pollHandler = PollHandler();
|
||||
|
||||
QCOMPARE(pollHandler.hasEnded(), false);
|
||||
QCOMPARE(pollHandler.numAnswers(), 0);
|
||||
QCOMPARE(pollHandler.answerCount(), 0);
|
||||
QCOMPARE(pollHandler.question(), QString());
|
||||
QCOMPARE(pollHandler.kind(), PollKind::Disclosed);
|
||||
QCOMPARE(pollHandler.options(), QJsonArray());
|
||||
QCOMPARE(pollHandler.answers(), QJsonObject());
|
||||
QCOMPARE(pollHandler.counts(), QJsonObject());
|
||||
QCOMPARE(pollHandler.kind(), QString());
|
||||
}
|
||||
|
||||
void PollHandlerTest::poll()
|
||||
{
|
||||
auto startEvent = eventCast<const PollStartEvent>(room->messageEvents().at(0).get());
|
||||
auto pollHandler = PollHandler(room, startEvent->id());
|
||||
auto pollHandler = PollHandler(room, startEvent);
|
||||
|
||||
QList<Quotient::EventContent::Answer> options = {EventContent::Answer{"option1"_L1, "option1"_L1}, EventContent::Answer{"option2"_L1, "option2"_L1}};
|
||||
auto options = QJsonArray{QJsonObject{{"id"_L1, "option1"_L1}, {"org.matrix.msc1767.text"_L1, "option1"_L1}},
|
||||
QJsonObject{{"id"_L1, "option2"_L1}, {"org.matrix.msc1767.text"_L1, "option2"_L1}}};
|
||||
|
||||
const auto answer0 = pollHandler.answerAtRow(0);
|
||||
const auto answer1 = pollHandler.answerAtRow(1);
|
||||
QCOMPARE(pollHandler.hasEnded(), false);
|
||||
QCOMPARE(pollHandler.numAnswers(), 2);
|
||||
QCOMPARE(pollHandler.answerCount(), 0);
|
||||
QCOMPARE(pollHandler.question(), u"test"_s);
|
||||
QCOMPARE(answer0.id, "option1"_L1);
|
||||
QCOMPARE(answer1.id, "option2"_L1);
|
||||
QCOMPARE(answer0.text, "option1text"_L1);
|
||||
QCOMPARE(answer1.text, "option2text"_L1);
|
||||
QCOMPARE(pollHandler.answerCountAtId(answer0.id), 0);
|
||||
QCOMPARE(pollHandler.answerCountAtId(answer1.id), 0);
|
||||
QCOMPARE(pollHandler.checkMemberSelectedId(connection->userId(), answer0.id), false);
|
||||
QCOMPARE(pollHandler.checkMemberSelectedId(connection->userId(), answer1.id), false);
|
||||
QCOMPARE(pollHandler.kind(), PollKind::Undisclosed);
|
||||
QCOMPARE(pollHandler.options(), options);
|
||||
QCOMPARE(pollHandler.answers(), QJsonObject());
|
||||
QCOMPARE(pollHandler.counts(), QJsonObject());
|
||||
QCOMPARE(pollHandler.kind(), u"org.matrix.msc3381.poll.disclosed"_s);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(PollHandlerTest)
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
|
||||
#include "models/messagecontentmodel.h"
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "server.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QHttpServer>
|
||||
#include <QHttpServerResponder>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkReply>
|
||||
#include <QSslCertificate>
|
||||
#include <QSslKey>
|
||||
#include <QSslServer>
|
||||
#include <QUuid>
|
||||
|
||||
#include <Quotient/networkaccessmanager.h>
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
QString generateEventId()
|
||||
{
|
||||
return u"$"_s + QString::fromLatin1(QCryptographicHash::hash(QUuid::createUuid().toString().toLatin1(), QCryptographicHash::Sha1).toBase64());
|
||||
}
|
||||
|
||||
QString generateRoomId()
|
||||
{
|
||||
return u"!%1:localhost:1234"_s
|
||||
.arg(QString::fromLatin1(QCryptographicHash::hash(QUuid::createUuid().toString().toLatin1(), QCryptographicHash::Sha1).toBase64()))
|
||||
.replace(u'/', QChar());
|
||||
}
|
||||
|
||||
Server::Server()
|
||||
{
|
||||
}
|
||||
|
||||
void Server::start()
|
||||
{
|
||||
QObject::connect(Quotient::NetworkAccessManager::instance(),
|
||||
&QNetworkAccessManager::sslErrors,
|
||||
Quotient::NetworkAccessManager::instance(),
|
||||
[](QNetworkReply *reply) {
|
||||
reply->ignoreSslErrors();
|
||||
});
|
||||
m_server.route(u"/.well-known/matrix/client"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
|
||||
responder.write(QJsonDocument(QJsonObject{
|
||||
{u"m.homeserver"_s, QJsonObject{{u"base_url"_s, u"https://localhost:1234"_s}}},
|
||||
}),
|
||||
QHttpServerResponder::StatusCode::Ok);
|
||||
});
|
||||
m_server.route(u"/_matrix/client/versions"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
|
||||
responder.write(QJsonDocument(QJsonObject{
|
||||
{u"versions"_s,
|
||||
QJsonArray{
|
||||
u"v1.0"_s,
|
||||
u"v1.1"_s,
|
||||
u"v1.2"_s,
|
||||
u"v1.3"_s,
|
||||
u"v1.4"_s,
|
||||
u"v1.5"_s,
|
||||
u"v1.6"_s,
|
||||
u"v1.7"_s,
|
||||
u"v1.8"_s,
|
||||
u"v1.9"_s,
|
||||
u"v1.10"_s,
|
||||
u"v1.11"_s,
|
||||
u"v1.12"_s,
|
||||
u"v1.13"_s,
|
||||
}},
|
||||
}),
|
||||
QHttpServerResponder::StatusCode::Ok);
|
||||
});
|
||||
m_server.route(u"/_matrix/client/v3/capabilities"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
|
||||
responder.write(
|
||||
QJsonDocument(QJsonObject{{u"capabilities"_s,
|
||||
QJsonObject{
|
||||
{u"m.room_versions"_s, QJsonObject{{u"m.available"_s, QJsonObject{{u"1"_s, u"stable"_s}}}, {u"default"_s, u"1"_s}}},
|
||||
}}}),
|
||||
QHttpServerResponder::StatusCode::Ok);
|
||||
});
|
||||
m_server.route(u"/_matrix/client/v3/account/whoami"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
|
||||
responder.write(QJsonDocument(QJsonObject{
|
||||
{u"device_id"_s, u"device_id_1234"_s},
|
||||
{u"user_id"_s, u"@user:localhost:1234"_s},
|
||||
}),
|
||||
QHttpServerResponder::StatusCode::Ok);
|
||||
});
|
||||
|
||||
m_server.route(u"/_matrix/client/v3/login"_s, QHttpServerRequest::Method::Post, [](QHttpServerResponder &responder) {
|
||||
// TODO
|
||||
// if data["identifier"]["user"] != "user" or data["password"] != "1234":
|
||||
// abort(403)
|
||||
responder.write(QJsonDocument(QJsonObject{
|
||||
{u"access_token"_s, u"token_login"_s},
|
||||
{u"device_id"_s, u"device_1234"_s},
|
||||
{u"user_id"_s, u"@user:localhost:1234"_s},
|
||||
}),
|
||||
QHttpServerResponder::StatusCode::Ok);
|
||||
});
|
||||
|
||||
m_server.route(u"/_matrix/client/v3/login"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
|
||||
responder.write(QJsonDocument(QJsonObject{
|
||||
{u"flows"_s, QJsonArray{QJsonObject{{u"type"_s, u"m.login.password"_s}}}},
|
||||
}),
|
||||
QHttpServerResponder::StatusCode::Ok);
|
||||
});
|
||||
|
||||
m_server.route(u"/_matrix/client/v3/rooms/<arg>/invite"_s,
|
||||
QHttpServerRequest::Method::Post,
|
||||
[this](const QString &roomId, QHttpServerResponder &responder, const QHttpServerRequest &request) {
|
||||
m_invitedUsers[roomId] += QJsonDocument::fromJson(request.body()).object()[u"user_id"_s].toString();
|
||||
responder.write(QJsonDocument(QJsonObject{}), QHttpServerResponder::StatusCode::Ok);
|
||||
});
|
||||
|
||||
m_server.route(u"/_matrix/client/r0/sync"_s, QHttpServerRequest::Method::Get, [this](QHttpServerResponder &responder) {
|
||||
QMap<QString, QJsonArray> stateEvents;
|
||||
|
||||
for (const auto &[roomId, matrixId] : m_roomsToCreate) {
|
||||
stateEvents[roomId] += QJsonObject{
|
||||
{u"content"_s, QJsonObject{{u"room_version"_s, u"11"_s}}},
|
||||
{u"event_id"_s, generateEventId()},
|
||||
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
|
||||
{u"room_id"_s, roomId},
|
||||
{u"sender"_s, matrixId},
|
||||
{u"state_key"_s, QString()},
|
||||
{u"type"_s, u"m.room.create"_s},
|
||||
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||
};
|
||||
stateEvents[roomId] += QJsonObject{
|
||||
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"join"_s}}},
|
||||
{u"event_id"_s, generateEventId()},
|
||||
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
|
||||
{u"room_id"_s, roomId},
|
||||
{u"sender"_s, matrixId},
|
||||
{u"state_key"_s, matrixId},
|
||||
{u"type"_s, u"m.room.member"_s},
|
||||
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||
};
|
||||
}
|
||||
m_roomsToCreate.clear();
|
||||
for (const auto &roomId : m_invitedUsers.keys()) {
|
||||
const auto &values = m_invitedUsers[roomId];
|
||||
for (const auto &value : values) {
|
||||
stateEvents[roomId] += QJsonObject{
|
||||
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"invite"_s}}},
|
||||
{u"event_id"_s, generateEventId()},
|
||||
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
|
||||
{u"room_id"_s, roomId},
|
||||
{u"sender"_s, u"@user:localhost:1234"_s},
|
||||
{u"state_key"_s, value},
|
||||
{u"type"_s, u"m.room.member"_s},
|
||||
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||
};
|
||||
}
|
||||
}
|
||||
m_invitedUsers.clear();
|
||||
|
||||
for (const auto &roomId : m_bannedUsers.keys()) {
|
||||
const auto &values = m_bannedUsers[roomId];
|
||||
for (const auto &value : values) {
|
||||
stateEvents[roomId] += QJsonObject{
|
||||
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"ban"_s}}},
|
||||
{u"event_id"_s, generateEventId()},
|
||||
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
|
||||
{u"room_id"_s, roomId},
|
||||
{u"sender"_s, u"@user:localhost:1234"_s},
|
||||
{u"state_key"_s, value},
|
||||
{u"type"_s, u"m.room.member"_s},
|
||||
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||
};
|
||||
}
|
||||
}
|
||||
m_bannedUsers.clear();
|
||||
|
||||
for (const auto &roomId : m_joinedUsers.keys()) {
|
||||
const auto &values = m_joinedUsers[roomId];
|
||||
for (const auto &value : values) {
|
||||
stateEvents[roomId] += QJsonObject{
|
||||
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"join"_s}}},
|
||||
{u"event_id"_s, generateEventId()},
|
||||
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
|
||||
{u"room_id"_s, roomId},
|
||||
{u"sender"_s, u"@user:localhost:1234"_s},
|
||||
{u"state_key"_s, value},
|
||||
{u"type"_s, u"m.room.member"_s},
|
||||
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||
};
|
||||
}
|
||||
}
|
||||
m_joinedUsers.clear();
|
||||
|
||||
QJsonObject rooms;
|
||||
for (const auto &roomId : stateEvents.keys()) {
|
||||
rooms[roomId] = QJsonObject{{u"state"_s, QJsonObject{{u"events"_s, stateEvents[roomId]}}}};
|
||||
}
|
||||
|
||||
responder.write(QJsonDocument(QJsonObject{{u"rooms"_s, QJsonObject{{u"join"_s, rooms}}}}), QHttpServerResponder::StatusCode::Ok);
|
||||
});
|
||||
|
||||
QSslConfiguration config;
|
||||
QFile key(QStringLiteral(DATA_DIR) + u"/localhost.key"_s);
|
||||
key.open(QFile::ReadOnly);
|
||||
config.setPrivateKey(QSslKey(&key, QSsl::Rsa));
|
||||
config.setLocalCertificate(QSslCertificate::fromPath(QStringLiteral(DATA_DIR) + u"/localhost.crt"_s).front());
|
||||
m_sslServer.setSslConfiguration(config);
|
||||
if (!m_sslServer.listen(QHostAddress::LocalHost, 1234) || !m_server.bind(&m_sslServer)) {
|
||||
qFatal() << "Server failed to listen on a port.";
|
||||
return;
|
||||
} else {
|
||||
qWarning() << "Server listening";
|
||||
}
|
||||
}
|
||||
|
||||
QString Server::createRoom(const QString &matrixId)
|
||||
{
|
||||
auto roomId = generateRoomId();
|
||||
m_roomsToCreate += {roomId, matrixId};
|
||||
return roomId;
|
||||
}
|
||||
|
||||
void Server::inviteUser(const QString &roomId, const QString &matrixId)
|
||||
{
|
||||
m_invitedUsers[roomId] += matrixId;
|
||||
}
|
||||
|
||||
void Server::banUser(const QString &roomId, const QString &matrixId)
|
||||
{
|
||||
m_bannedUsers[roomId] += matrixId;
|
||||
}
|
||||
|
||||
void Server::joinUser(const QString &roomId, const QString &matrixId)
|
||||
{
|
||||
m_joinedUsers[roomId] += matrixId;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include <QHttpServer>
|
||||
#include <QSslServer>
|
||||
|
||||
class Server
|
||||
{
|
||||
public:
|
||||
Server();
|
||||
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Create a room and place the user with id matrixId in it.
|
||||
* Returns the room's id
|
||||
*/
|
||||
QString createRoom(const QString &matrixId);
|
||||
|
||||
void inviteUser(const QString &roomId, const QString &matrixId);
|
||||
void banUser(const QString &roomId, const QString &matrixId);
|
||||
void joinUser(const QString &roomId, const QString &matrixId);
|
||||
|
||||
private:
|
||||
QHttpServer m_server;
|
||||
QSslServer m_sslServer;
|
||||
|
||||
QHash<QString, QList<QString>> m_invitedUsers;
|
||||
QHash<QString, QList<QString>> m_bannedUsers;
|
||||
QHash<QString, QList<QString>> m_joinedUsers;
|
||||
|
||||
QList<std::pair<QString, QString>> m_roomsToCreate;
|
||||
};
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
|
||||
#include <Kirigami/Platform/PlatformTheme>
|
||||
#include <qnamespace.h>
|
||||
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "models/customemojimodel.h"
|
||||
@@ -44,8 +43,6 @@ private Q_SLOTS:
|
||||
void sendCustomEmoji();
|
||||
void sendCustomEmojiCode_data();
|
||||
void sendCustomEmojiCode();
|
||||
void sendCustomTags_data();
|
||||
void sendCustomTags();
|
||||
|
||||
void receiveSpacelessSelfClosingTag();
|
||||
void receiveStripReply();
|
||||
@@ -61,7 +58,6 @@ private Q_SLOTS:
|
||||
void receiveRichStrikethrough();
|
||||
void receiveRichtextIn();
|
||||
void receiveRichMxcUrl();
|
||||
void receiveRichPlainUrl_data();
|
||||
void receiveRichPlainUrl();
|
||||
void receiveRichEdited_data();
|
||||
void receiveRichEdited();
|
||||
@@ -253,40 +249,6 @@ void TextHandlerTest::sendCustomEmojiCode()
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::sendCustomTags_data()
|
||||
{
|
||||
QTest::addColumn<QString>("testInputString");
|
||||
QTest::addColumn<QString>("testOutputString");
|
||||
|
||||
// spoiler
|
||||
QTest::newRow("incomplete spoiler") << u"||test"_s << u"||test"_s;
|
||||
QTest::newRow("complete spoiler") << u"||test||"_s << u"<span data-mx-spoiler>test</span>"_s;
|
||||
QTest::newRow("multiple spoiler") << u"||apple||banana||pear||"_s << u"<span data-mx-spoiler>apple</span>banana<span data-mx-spoiler>pear</span>"_s;
|
||||
QTest::newRow("inside code block spoiler") << u"```||apple||```"_s << u"<code>||apple||</code>"_s;
|
||||
QTest::newRow("outside code block spoiler") << u"||apple|| ```||banana||``` ||pear||"_s
|
||||
<< u"<span data-mx-spoiler>apple</span> <code>||banana||</code> <span data-mx-spoiler>pear</span>"_s;
|
||||
QTest::newRow("complex spoiler") << u"Between `formFactor == Horizontal||Vertical` and `location == top||left||bottom||right`"_s
|
||||
<< u"Between <code>formFactor == Horizontal||Vertical</code> and <code>location == top||left||bottom||right</code>"_s;
|
||||
|
||||
// strikethrough
|
||||
QTest::newRow("incomplete strikethrough") << u"~~test"_s << u"~~test"_s;
|
||||
QTest::newRow("complete strikethrough") << u"~~test~~"_s << u"<del>test</del>"_s;
|
||||
QTest::newRow("inside code block strikethrough") << u"```~~apple~~```"_s << u"<code>~~apple~~</code>"_s;
|
||||
QTest::newRow("outside code block strikethrough") << u"~~apple~~ ```~~banana~~``` ~~pear~~"_s
|
||||
<< u"<del>apple</del> <code>~~banana~~</code> <del>pear</del>"_s;
|
||||
}
|
||||
|
||||
void TextHandlerTest::sendCustomTags()
|
||||
{
|
||||
QFETCH(QString, testInputString);
|
||||
QFETCH(QString, testOutputString);
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveSpacelessSelfClosingTag()
|
||||
{
|
||||
const QString testInputString = u"Test...<br/>...ing"_s;
|
||||
@@ -451,32 +413,6 @@ void TextHandlerTest::receiveRichMxcUrl()
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, room, room->messageEvents().at(0).get()), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveRichPlainUrl_data()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<QString>("output");
|
||||
|
||||
// This is an actual link that caused trouble which is why it's so long. Keeping
|
||||
// so we can confirm consistent behaviour for complex urls.
|
||||
QTest::addRow("link 1")
|
||||
<< u"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im <a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im\">Link already rich</a>"_s
|
||||
<< u"<a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im\">https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im</a> <a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im\">Link already rich</a>"_s;
|
||||
|
||||
// Another real case. The linkification wasn't handling it when a single link
|
||||
// contains what looks like and email. It was broken into 3 but needs to
|
||||
// be just single link.
|
||||
QTest::addRow("link 2")
|
||||
<< u"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/"_s
|
||||
<< u"<a href=\"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/\">https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/</a>"_s;
|
||||
|
||||
QTest::addRow("email") << uR"(email@example.com <a href="mailto:email@example.com">Link already rich</a>)"_s
|
||||
<< uR"(<a href="mailto:email@example.com">email@example.com</a> <a href="mailto:email@example.com">Link already rich</a>)"_s;
|
||||
QTest::addRow("mxid")
|
||||
<< u"@user:kde.org <a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a>"_s
|
||||
<< u"<b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a></b>"_s;
|
||||
QTest::addRow("mxid with prefix") << u"a @user:kde.org b"_s << u"a <b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> b"_s;
|
||||
}
|
||||
|
||||
/**
|
||||
* For when your rich input string has a plain text url left in.
|
||||
*
|
||||
@@ -485,13 +421,46 @@ void TextHandlerTest::receiveRichPlainUrl_data()
|
||||
*/
|
||||
void TextHandlerTest::receiveRichPlainUrl()
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QFETCH(QString, output);
|
||||
// This is an actual link that caused trouble which is why it's so long. Keeping
|
||||
// so we can confirm consistent behaviour for complex urls.
|
||||
const QString testInputStringLink1 =
|
||||
u"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im <a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im\">Link already rich</a>"_s;
|
||||
const QString testOutputStringLink1 =
|
||||
u"<a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im\">https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im</a> <a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im\">Link already rich</a>"_s;
|
||||
|
||||
// Another real case. The linkification wasn't handling it when a single link
|
||||
// contains what looks like and email. It was been broken into 3 but needs to
|
||||
// be just single link.
|
||||
const QString testInputStringLink2 = u"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/"_s;
|
||||
const QString testOutputStringLink2 =
|
||||
u"<a href=\"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/\">https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/</a>"_s;
|
||||
|
||||
QString testInputStringEmail = uR"(email@example.com <a href="mailto:email@example.com">Link already rich</a>)"_s;
|
||||
QString testOutputStringEmail = uR"(<a href="mailto:email@example.com">email@example.com</a> <a href="mailto:email@example.com">Link already rich</a>)"_s;
|
||||
|
||||
QString testInputStringMxId = u"@user:kde.org <a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a>"_s;
|
||||
QString testOutputStringMxId =
|
||||
u"<b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a></b>"_s;
|
||||
|
||||
QString testInputStringMxIdWithPrefix = u"a @user:kde.org b"_s;
|
||||
QString testOutputStringMxIdWithPrefix = u"a <b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> b"_s;
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(input);
|
||||
testTextHandler.setData(testInputStringLink1);
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), output);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringLink1);
|
||||
|
||||
testTextHandler.setData(testInputStringLink2);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringLink2);
|
||||
|
||||
testTextHandler.setData(testInputStringEmail);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringEmail);
|
||||
|
||||
testTextHandler.setData(testInputStringMxId);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringMxId);
|
||||
|
||||
testTextHandler.setData(testInputStringMxIdWithPrefix);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringMxIdWithPrefix);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveRichEdited_data()
|
||||
@@ -499,12 +468,9 @@ void TextHandlerTest::receiveRichEdited_data()
|
||||
QTest::addColumn<QString>("testInputString");
|
||||
QTest::addColumn<QString>("testOutputString");
|
||||
|
||||
auto theme = static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
|
||||
|
||||
QTest::newRow("basic") << u"Edited"_s << u"Edited <span style=\"color:%1\">(edited)</span>"_s.arg(theme ? theme->disabledTextColor().name() : u"#000000"_s);
|
||||
QTest::newRow("basic") << u"Edited"_s << u"Edited <span style=\"color:#000000\">(edited)</span>"_s;
|
||||
QTest::newRow("multiple paragraphs") << u"<p>Edited</p>\n<p>Edited</p>"_s
|
||||
<< u"<p>Edited</p>\n<p>Edited <span style=\"color:%1\">(edited)</span></p>"_s.arg(
|
||||
theme ? theme->disabledTextColor().name() : u"#000000"_s);
|
||||
<< u"<p>Edited</p>\n<p>Edited <span style=\"color:#000000\">(edited)</span></p>"_s;
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveRichEdited()
|
||||
@@ -545,6 +511,8 @@ void TextHandlerTest::receiveRichColor()
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
qInfo() << testTextHandler.handleRecieveRichText();
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||
}
|
||||
|
||||
@@ -562,9 +530,6 @@ void TextHandlerTest::componentOutput_data()
|
||||
QTest::newRow("quote") << u"<p>Text</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>"_s
|
||||
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, u"Text"_s, {}},
|
||||
MessageComponent{MessageComponentType::Quote, u"“blockquote”"_s, {}}};
|
||||
QTest::newRow("multiple paragraph quote") << u"<blockquote>\n<p>blockquote</p>\n<p>next paragraph</p>\n</blockquote>"_s
|
||||
<< QList<MessageComponent>{
|
||||
MessageComponent{MessageComponentType::Quote, u"<p>“blockquote</p>\n<p>next paragraph”</p>"_s, {}}};
|
||||
QTest::newRow("no tag first paragraph") << u"Text\n<p>Text</p>"_s
|
||||
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, u"Text"_s, {}},
|
||||
MessageComponent{MessageComponentType::Text, u"Text"_s, {}}};
|
||||
|
||||
@@ -57,7 +57,7 @@ void TimelineMessageModelTest::switchEmptyRoom()
|
||||
auto firstRoom = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s);
|
||||
auto secondRoom = new TestUtils::TestRoom(connection, u"#secondRoom:kde.org"_s);
|
||||
|
||||
QSignalSpy spy(model, SIGNAL(roomChanged(NeoChatRoom *, NeoChatRoom *)));
|
||||
QSignalSpy spy(model, SIGNAL(roomChanged()));
|
||||
|
||||
QCOMPARE(model->room(), nullptr);
|
||||
model->setRoom(firstRoom);
|
||||
@@ -77,7 +77,7 @@ void TimelineMessageModelTest::switchSyncedRoom()
|
||||
auto firstRoom = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s, u"test-messageventmodel-sync.json"_s);
|
||||
auto secondRoom = new TestUtils::TestRoom(connection, u"#secondRoom:kde.org"_s, u"test-messageventmodel-sync.json"_s);
|
||||
|
||||
QSignalSpy spy(model, SIGNAL(roomChanged(NeoChatRoom *, NeoChatRoom *)));
|
||||
QSignalSpy spy(model, SIGNAL(roomChanged()));
|
||||
|
||||
QCOMPARE(model->room(), nullptr);
|
||||
model->setRoom(firstRoom);
|
||||
@@ -208,7 +208,7 @@ void TimelineMessageModelTest::idToRow()
|
||||
auto room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-min-sync.json"_s);
|
||||
model->setRoom(room);
|
||||
|
||||
QCOMPARE(model->indexforEventId(u"$153456789:example.org"_s).row(), 0);
|
||||
QCOMPARE(model->eventIdToRow(u"$153456789:example.org"_s), 0);
|
||||
}
|
||||
|
||||
void TimelineMessageModelTest::cleanup()
|
||||
|
||||
10
cmake/FindLiveKit.cmake
Normal file
10
cmake/FindLiveKit.cmake
Normal file
@@ -0,0 +1,10 @@
|
||||
# SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
find_library(LIVEKIT_LIB NAMES livekit_ffi)
|
||||
find_path(LIVEKIT_INCLUDE_DIR NAMES livekit_ffi.h)
|
||||
|
||||
add_library(LiveKit UNKNOWN IMPORTED)
|
||||
set_target_properties(LiveKit PROPERTIES IMPORTED_LOCATION ${LIVEKIT_LIB})
|
||||
set_target_properties(LiveKit PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${LIVEKIT_INCLUDE_DIR})
|
||||
set(LiveKit_FOUND True)
|
||||
@@ -7,19 +7,16 @@ qt_add_executable(timeline-memtest
|
||||
main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(timeline-memtest PRIVATE neochatplugin Timelineplugin)
|
||||
target_link_libraries(timeline-memtest PRIVATE neochatplugin timelineplugin)
|
||||
target_link_libraries(timeline-memtest PUBLIC
|
||||
Qt::Core
|
||||
Qt::Quick
|
||||
Qt::Qml
|
||||
Qt::Gui
|
||||
Qt::QuickControls2
|
||||
Qt::Widgets
|
||||
KF6::I18nQml
|
||||
KF6::Kirigami
|
||||
QuotientQt6
|
||||
LibNeoChat
|
||||
Timeline
|
||||
neochat
|
||||
)
|
||||
|
||||
ecm_add_qml_module(timeline-memtest URI org.kde.neochat.timeline-memtest GENERATE_PLUGIN_SOURCE
|
||||
@@ -33,5 +30,5 @@ ecm_add_qml_module(timeline-memtest URI org.kde.neochat.timeline-memtest GENERAT
|
||||
QtCore
|
||||
QtQuick
|
||||
IMPORTS
|
||||
org.kde.neochat.timeline
|
||||
org.kde.neochat
|
||||
)
|
||||
|
||||
@@ -23,7 +23,6 @@ QQC2.ApplicationWindow {
|
||||
height: root.height
|
||||
|
||||
contentItem: ListView {
|
||||
cacheBuffer: 1000000
|
||||
model: messageFilterModel
|
||||
|
||||
delegate: EventDelegate {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
|
||||
#include <KLocalizedQmlContext>
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include "memtesttimelinemodel.h"
|
||||
@@ -20,15 +19,12 @@ int main(int argc, char **argv)
|
||||
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
KLocalization::setupLocalizedContext(&engine);
|
||||
engine.loadFromModule("org.kde.neochat.timeline-memtest", "Main");
|
||||
|
||||
MemTestTimelineModel *memTestTimelineModel = new MemTestTimelineModel;
|
||||
MessageFilterModel *messageFilterModel = new MessageFilterModel(nullptr, memTestTimelineModel);
|
||||
engine.rootContext()->setContextProperty(u"memTestTimelineModel"_s, memTestTimelineModel);
|
||||
engine.rootContext()->setContextProperty(u"messageFilterModel"_s, messageFilterModel);
|
||||
|
||||
engine.loadFromModule("org.kde.neochat.timeline-memtest", "Main");
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@
|
||||
"formatted_body": "This is an example<br>text message",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": 0,
|
||||
"event_id": "$1000000000000:example.org",
|
||||
"origin_server_ts": 1000000000000,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
@@ -255,7 +255,7 @@
|
||||
"body": "This is a highlight @bob:example.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": 1,
|
||||
"event_id": "$1000000000001:example.org",
|
||||
"origin_server_ts": 1000000000001,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
@@ -266,11 +266,11 @@
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"event_id": 1,
|
||||
"m.relates_to": {
|
||||
"event_id": "$1000000000001:example.org",
|
||||
"key": "👍",
|
||||
"rel_type": "m.annotation"
|
||||
}
|
||||
}
|
||||
},
|
||||
"origin_server_ts": 1000000000002,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
@@ -279,7 +279,7 @@
|
||||
"unsigned": {
|
||||
"age": 390159120
|
||||
},
|
||||
"event_id": 2,
|
||||
"event_id": "$1000000000002:example.org",
|
||||
"age": 390159120
|
||||
},
|
||||
{
|
||||
@@ -289,7 +289,7 @@
|
||||
"formatted_body": "reply",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": 0
|
||||
"event_id": "$1000000000000:example.org"
|
||||
}
|
||||
},
|
||||
"msgtype": "m.text"
|
||||
@@ -300,7 +300,7 @@
|
||||
"unsigned": {
|
||||
"age": 98
|
||||
},
|
||||
"event_id": 3,
|
||||
"event_id": "$1000000000003:example.org",
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||
},
|
||||
{
|
||||
@@ -317,7 +317,7 @@
|
||||
"uri": "geo:51.7035,-1.14394"
|
||||
}
|
||||
},
|
||||
"event_id": 4,
|
||||
"event_id": "$1000000000004:example.org",
|
||||
"origin_server_ts": 1000000000004,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
@@ -333,7 +333,7 @@
|
||||
"formatted_body": "<pre><code class=\"language-cpp\">int main(int argc, char **argv)\n{\n QApplication app(argc, argv);\n\n KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));\n\n QQmlApplicationEngine engine;\n engine.loadFromModule("org.kde.neochat.timeline-memtest", "Main");\n\n return app.exec();\n}\n</code></pre>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": 5,
|
||||
"event_id": "$1000000000005:example.org",
|
||||
"origin_server_ts": 1000000000005,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@bob:example.org",
|
||||
@@ -347,7 +347,7 @@
|
||||
"body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed fringilla risus, eget lacinia risus. Suspendisse at magna id justo sagittis suscipit. Maecenas eros quam, pulvinar a consequat sed, varius vitae risus. Cras congue est eget felis porttitor lobortis. Nam cursus, nulla ut finibus suscipit, tellus eros tincidunt ante, a volutpat velit lectus sit amet turpis. Morbi leo justo, fringilla sed rutrum a, suscipit a quam. Proin rhoncus neque eget ligula ullamcorper pellentesque. Mauris volutpat malesuada nunc. Nullam finibus enim eu nibh placerat imperdiet. Nullam in mi in diam luctus scelerisque dignissim non erat. ",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": 6,
|
||||
"event_id": "$1000000000006:example.org",
|
||||
"origin_server_ts": 1000000000006,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
@@ -363,7 +363,7 @@
|
||||
"formatted_body": "<blockquote>\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed fringilla risus, eget lacinia risus. Suspendisse at magna id justo sagittis suscipit. Maecenas eros quam, pulvinar a consequat sed, varius vitae risus. Cras congue est eget felis porttitor lobortis. Nam cursus, nulla ut finibus suscipit, tellus eros tincidunt ante, a volutpat velit lectus sit amet turpis. Morbi leo justo, fringilla sed rutrum a, suscipit a quam. Proin rhoncus neque eget ligula ullamcorper pellentesque. Mauris volutpat malesuada nunc. Nullam finibus enim eu nibh placerat imperdiet. Nullam in mi in diam luctus scelerisque dignissim non erat.</p>\n</blockquote>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": 7,
|
||||
"event_id": "$1000000000007:example.org",
|
||||
"origin_server_ts": 1000000000007,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
@@ -371,6 +371,282 @@
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an example text message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "This is an example<br>text message",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$1000000000008:example.org",
|
||||
"origin_server_ts": 1000000000008,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "This is a highlight @bob:example.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$1000000000009:example.org",
|
||||
"origin_server_ts": 1000000000009,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1233
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"event_id": "$1000000000009:example.org",
|
||||
"key": "👍",
|
||||
"rel_type": "m.annotation"
|
||||
}
|
||||
},
|
||||
"origin_server_ts": 1000000000010,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@alice:example.org",
|
||||
"type": "m.reaction",
|
||||
"unsigned": {
|
||||
"age": 390159120
|
||||
},
|
||||
"event_id": "$1000000000010:example.org",
|
||||
"age": 390159120
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "reply",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "reply",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$1000000000008:example.org"
|
||||
}
|
||||
},
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"origin_server_ts": 1000000000011,
|
||||
"sender": "@alice:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 98
|
||||
},
|
||||
"event_id": "$1000000000011:example.org",
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||
},
|
||||
{
|
||||
"age": 96845207,
|
||||
"content": {
|
||||
"body": "Lat: 51.7035, Lon: -1.14394",
|
||||
"geo_uri": "geo:51.7035,-1.14394",
|
||||
"msgtype": "m.location",
|
||||
"org.matrix.msc1767.text": "Lat: 51.7035, Lon: -1.14394",
|
||||
"org.matrix.msc3488.asset": {
|
||||
"type": "m.pin"
|
||||
},
|
||||
"org.matrix.msc3488.location": {
|
||||
"uri": "geo:51.7035,-1.14394"
|
||||
}
|
||||
},
|
||||
"event_id": "$1000000000012:example.org",
|
||||
"origin_server_ts": 1000000000012,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 96845207
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "```cpp\nint main(int argc, char **argv)\n{\n QApplication app(argc, argv);\n\n KLocalizedString::setApplicationDomain(QByteArrayLiteral(\"neochat\"));\n\n QQmlApplicationEngine engine;\n engine.loadFromModule(\"org.kde.neochat.timeline-memtest\", \"Main\");\n\n return app.exec();\n}\n```",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<pre><code class=\"language-cpp\">int main(int argc, char **argv)\n{\n QApplication app(argc, argv);\n\n KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));\n\n QQmlApplicationEngine engine;\n engine.loadFromModule("org.kde.neochat.timeline-memtest", "Main");\n\n return app.exec();\n}\n</code></pre>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$1000000000013:example.org",
|
||||
"origin_server_ts": 1000000000013,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@bob:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1233
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed fringilla risus, eget lacinia risus. Suspendisse at magna id justo sagittis suscipit. Maecenas eros quam, pulvinar a consequat sed, varius vitae risus. Cras congue est eget felis porttitor lobortis. Nam cursus, nulla ut finibus suscipit, tellus eros tincidunt ante, a volutpat velit lectus sit amet turpis. Morbi leo justo, fringilla sed rutrum a, suscipit a quam. Proin rhoncus neque eget ligula ullamcorper pellentesque. Mauris volutpat malesuada nunc. Nullam finibus enim eu nibh placerat imperdiet. Nullam in mi in diam luctus scelerisque dignissim non erat. ",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$1000000000014:example.org",
|
||||
"origin_server_ts": 1000000000014,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed fringilla risus, eget lacinia risus. Suspendisse at magna id justo sagittis suscipit. Maecenas eros quam, pulvinar a consequat sed, varius vitae risus. Cras congue est eget felis porttitor lobortis. Nam cursus, nulla ut finibus suscipit, tellus eros tincidunt ante, a volutpat velit lectus sit amet turpis. Morbi leo justo, fringilla sed rutrum a, suscipit a quam. Proin rhoncus neque eget ligula ullamcorper pellentesque. Mauris volutpat malesuada nunc. Nullam finibus enim eu nibh placerat imperdiet. Nullam in mi in diam luctus scelerisque dignissim non erat. ",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<blockquote>\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed fringilla risus, eget lacinia risus. Suspendisse at magna id justo sagittis suscipit. Maecenas eros quam, pulvinar a consequat sed, varius vitae risus. Cras congue est eget felis porttitor lobortis. Nam cursus, nulla ut finibus suscipit, tellus eros tincidunt ante, a volutpat velit lectus sit amet turpis. Morbi leo justo, fringilla sed rutrum a, suscipit a quam. Proin rhoncus neque eget ligula ullamcorper pellentesque. Mauris volutpat malesuada nunc. Nullam finibus enim eu nibh placerat imperdiet. Nullam in mi in diam luctus scelerisque dignissim non erat.</p>\n</blockquote>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$1000000000015:example.org",
|
||||
"origin_server_ts": 1000000000015,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an example text message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "This is an example<br>text message",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$1000000000016:example.org",
|
||||
"origin_server_ts": 1000000000016,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "This is a highlight @bob:example.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$1000000000017:example.org",
|
||||
"origin_server_ts": 1000000000017,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1233
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"event_id": "$1000000000017:example.org",
|
||||
"key": "👍",
|
||||
"rel_type": "m.annotation"
|
||||
}
|
||||
},
|
||||
"origin_server_ts": 1000000000018,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@alice:example.org",
|
||||
"type": "m.reaction",
|
||||
"unsigned": {
|
||||
"age": 390159120
|
||||
},
|
||||
"event_id": "$1000000000018:example.org",
|
||||
"age": 390159120
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "reply",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "reply",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$1000000000016:example.org"
|
||||
}
|
||||
},
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"origin_server_ts": 1000000000019,
|
||||
"sender": "@alice:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 98
|
||||
},
|
||||
"event_id": "$1000000000019:example.org",
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||
},
|
||||
{
|
||||
"age": 96845207,
|
||||
"content": {
|
||||
"body": "Lat: 51.7035, Lon: -1.14394",
|
||||
"geo_uri": "geo:51.7035,-1.14394",
|
||||
"msgtype": "m.location",
|
||||
"org.matrix.msc1767.text": "Lat: 51.7035, Lon: -1.14394",
|
||||
"org.matrix.msc3488.asset": {
|
||||
"type": "m.pin"
|
||||
},
|
||||
"org.matrix.msc3488.location": {
|
||||
"uri": "geo:51.7035,-1.14394"
|
||||
}
|
||||
},
|
||||
"event_id": "$1000000000020:example.org",
|
||||
"origin_server_ts": 1000000000020,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 96845207
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "```cpp\nint main(int argc, char **argv)\n{\n QApplication app(argc, argv);\n\n KLocalizedString::setApplicationDomain(QByteArrayLiteral(\"neochat\"));\n\n QQmlApplicationEngine engine;\n engine.loadFromModule(\"org.kde.neochat.timeline-memtest\", \"Main\");\n\n return app.exec();\n}\n```",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<pre><code class=\"language-cpp\">int main(int argc, char **argv)\n{\n QApplication app(argc, argv);\n\n KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));\n\n QQmlApplicationEngine engine;\n engine.loadFromModule("org.kde.neochat.timeline-memtest", "Main");\n\n return app.exec();\n}\n</code></pre>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$1000000000021:example.org",
|
||||
"origin_server_ts": 1000000000021,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@bob:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1233
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed fringilla risus, eget lacinia risus. Suspendisse at magna id justo sagittis suscipit. Maecenas eros quam, pulvinar a consequat sed, varius vitae risus. Cras congue est eget felis porttitor lobortis. Nam cursus, nulla ut finibus suscipit, tellus eros tincidunt ante, a volutpat velit lectus sit amet turpis. Morbi leo justo, fringilla sed rutrum a, suscipit a quam. Proin rhoncus neque eget ligula ullamcorper pellentesque. Mauris volutpat malesuada nunc. Nullam finibus enim eu nibh placerat imperdiet. Nullam in mi in diam luctus scelerisque dignissim non erat. ",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$1000000000022:example.org",
|
||||
"origin_server_ts": 1000000000022,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed fringilla risus, eget lacinia risus. Suspendisse at magna id justo sagittis suscipit. Maecenas eros quam, pulvinar a consequat sed, varius vitae risus. Cras congue est eget felis porttitor lobortis. Nam cursus, nulla ut finibus suscipit, tellus eros tincidunt ante, a volutpat velit lectus sit amet turpis. Morbi leo justo, fringilla sed rutrum a, suscipit a quam. Proin rhoncus neque eget ligula ullamcorper pellentesque. Mauris volutpat malesuada nunc. Nullam finibus enim eu nibh placerat imperdiet. Nullam in mi in diam luctus scelerisque dignissim non erat. ",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<blockquote>\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed fringilla risus, eget lacinia risus. Suspendisse at magna id justo sagittis suscipit. Maecenas eros quam, pulvinar a consequat sed, varius vitae risus. Cras congue est eget felis porttitor lobortis. Nam cursus, nulla ut finibus suscipit, tellus eros tincidunt ante, a volutpat velit lectus sit amet turpis. Morbi leo justo, fringilla sed rutrum a, suscipit a quam. Proin rhoncus neque eget ligula ullamcorper pellentesque. Mauris volutpat malesuada nunc. Nullam finibus enim eu nibh placerat imperdiet. Nullam in mi in diam luctus scelerisque dignissim non erat.</p>\n</blockquote>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$1000000000023:example.org",
|
||||
"origin_server_ts": 1000000000023,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
|
||||
@@ -39,52 +39,10 @@ public:
|
||||
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + syncFileName);
|
||||
testSyncFile.open(QIODevice::ReadOnly);
|
||||
auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll()).object();
|
||||
auto timelineJson = testSyncJson["timeline"_L1].toObject();
|
||||
timelineJson["events"_L1] = multiplyEvents(timelineJson["events"_L1].toArray(), 100);
|
||||
testSyncJson["timeline"_L1] = timelineJson;
|
||||
Quotient::SyncRoomData roomData(id(), Quotient::JoinState::Join, testSyncJson);
|
||||
update(std::move(roomData));
|
||||
}
|
||||
}
|
||||
|
||||
QJsonArray multiplyEvents(QJsonArray events, int factor)
|
||||
{
|
||||
QJsonArray newArray;
|
||||
int eventNum = 0;
|
||||
int ts = 0;
|
||||
|
||||
for (int i = 0; i < factor; ++i) {
|
||||
for (const auto &event : events) {
|
||||
auto eventObject = event.toObject();
|
||||
auto contentJson = eventObject["content"_L1].toObject();
|
||||
if (contentJson.contains("m.relates_to"_L1)) {
|
||||
auto relatesToJson = contentJson["m.relates_to"_L1].toObject();
|
||||
if (relatesToJson.contains("m.in_reply_to"_L1)) {
|
||||
auto replyJson = relatesToJson["m.in_reply_to"_L1].toObject();
|
||||
const auto currentId = eventObject["event_id"_L1].toInt();
|
||||
const auto currentReplyId = replyJson["event_id"_L1].toInt();
|
||||
replyJson["event_id"_L1] = "$%1:example.org"_L1.arg(QString::number(eventNum - (currentId - currentReplyId)));
|
||||
relatesToJson["m.in_reply_to"_L1] = replyJson;
|
||||
} else if (relatesToJson.contains("event_id"_L1)) {
|
||||
const auto currentId = eventObject["event_id"_L1].toInt();
|
||||
const auto currentRelationId = relatesToJson["event_id"_L1].toInt();
|
||||
relatesToJson["event_id"_L1] = "$%1:example.org"_L1.arg(QString::number(eventNum - (currentId - currentRelationId)));
|
||||
}
|
||||
contentJson["m.relates_to"_L1] = relatesToJson;
|
||||
eventObject["content"_L1] = contentJson;
|
||||
}
|
||||
eventObject["event_id"_L1] = "$%1:example.org"_L1.arg(QString::number(eventNum));
|
||||
eventObject["origin_server_ts"_L1] = ts;
|
||||
auto unsignedJson = eventObject["unsigned"_L1].toObject();
|
||||
unsignedJson["age"_L1] = ts;
|
||||
eventObject["unsigned"_L1] = unsignedJson;
|
||||
newArray.append(eventObject);
|
||||
++eventNum;
|
||||
++ts;
|
||||
}
|
||||
}
|
||||
return newArray;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -70,13 +70,10 @@
|
||||
<summary xml:lang="ia">Conversation en ditecto sur Matrix</summary>
|
||||
<summary xml:lang="it">Chat su Matrix</summary>
|
||||
<summary xml:lang="ka">ისაუბრეთ Matrix-ზე</summary>
|
||||
<summary xml:lang="ko">Matrix에서 대화하기</summary>
|
||||
<summary xml:lang="lv">Tērzējiet „Matrix“ tīklā</summary>
|
||||
<summary xml:lang="nl">Chat op Matrix</summary>
|
||||
<summary xml:lang="nn">Prat med via Matrix</summary>
|
||||
<summary xml:lang="pl">Rozmawiaj na Matriksie</summary>
|
||||
<summary xml:lang="pt-BR">Bate-papo na Matrix</summary>
|
||||
<summary xml:lang="ru">Общение в Matrix</summary>
|
||||
<summary xml:lang="sa">Matrix इत्यत्र गपशपं कुर्वन्तु</summary>
|
||||
<summary xml:lang="sl">Klepet na Matrixu</summary>
|
||||
<summary xml:lang="sv">Chatta på Matrix</summary>
|
||||
@@ -105,12 +102,10 @@
|
||||
<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>
|
||||
<p xml:lang="ka">NeoChat ჩატის აპია, რომელიც საშუალება გაძლევთ, Matrix-ის ქსელის საშუალებები ბოლომდე გამოიყენოთ. ის გაძლევთ უსაფრთხო გზას, გააგზავნოთ ტექსტური შეტყობინებები, ვიდეოებ და აუდიოფაილები თქვენს ოჯახთან, კოლეგებთან და მეგობრებთან.</p>
|
||||
<p xml:lang="ko">NeoChat은 Matrix 네트워크를 사용하는 채팅 앱입니다. 텍스트 메시지, 동영상, 오디오 파일을 가족, 친구, 동료와 안전하게 공유할 수 있습니다.</p>
|
||||
<p xml:lang="lv">„NeoChat“ ir tērzēšanas programma, kas ļauj pilnvērtīgi izmantot „Matrix“ tīklu. Tā sniedz drošu veidu teksta ziņu, video un audio sūtīšanai ģimenes locekļiem, kolēģiem un draugiem.</p>
|
||||
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
|
||||
<p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p>
|
||||
<p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p>
|
||||
<p xml:lang="pt-BR">O NeoChat é um aplicativo de bate-papo que permite que você aproveite ao máximo a rede Matrix. Ele oferece uma maneira segura de enviar mensagens de texto, vídeos e arquivos de áudio para sua família, colegas e amigos.</p>
|
||||
<p xml:lang="ru">NeoChat — приложение для общения, предоставляющее все преимущества сети Matrix. С его помощью можно безопасно отправлять текстовые сообщения, видеозаписи и звуковые файлы родственникам, коллегам и друзьям.</p>
|
||||
<p xml:lang="sa">NeoChat इति एकं गपशप-अनुप्रयोगं यत् भवान् Matrix-जालस्य पूर्णं लाभं ग्रहीतुं शक्नोति । एतत् भवन्तं भवतः परिवाराय, सहकारिभ्यः, मित्रेभ्यः च पाठसन्देशान्, भिडियो, श्रव्यसञ्चिकाः च प्रेषयितुं सुरक्षितं मार्गं प्रदाति ।</p>
|
||||
<p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p>
|
||||
@@ -144,7 +139,6 @@
|
||||
<p xml:lang="nn">NeoChat har som mål å støtta all funksjonalitet i Matrix-spesifikasjonen. Førebels er alt i den gjeldande stabile spesifikasjonen støtta, med unntak av VoIP, trådar og nokre delar av ende-til-kryptering. Det finst òg andre småting som ikkje er støtta, sidan Matrix-spesifikasjon er i stadig endring, men målet er altså støtte for alt.</p>
|
||||
<p xml:lang="pl">NeoChat w zamyśle ma być pełnowartościową aplikacją wg wytycznych Matriksa. Z tego powodu, wszystko, co jest obecnie w stabilnych wytycznych z pominięciem VoIP, wątków i niektórych części szyfrowania Użytkownik-do-Użytkownika są obecnie obsługiwane. Pominięto też kilka mniejszych rzeczy ze względu na ciągły rozwój wytycznych Matriksa, lecz celem nadal jest zapewnienie obsługi wszystkich wytycznych.</p>
|
||||
<p xml:lang="pt">O NeoChat pretende ser uma aplicação completa para a especificação do Matrix. Como tal, tudo o que existe na especificação estável actual, com as notáveis excepções do VoIP, tópicos e alguns aspectos da Encriptação Ponto-a-Ponto, são suportados. Existem mais algumas omissões, devido ao facto que a norma do Matrix está em constante evolução, mas o objectivo continua a ser oferecer o suporte eventual para a norma por inteiro.</p>
|
||||
<p xml:lang="pt-BR">O NeoChat pretende ser um aplicativo completo para a especificação Matrix. Dessa forma, tudo na especificação estável atual, com as notáveis exceções de VoIP, tópicos e alguns aspectos da criptografia de ponta a ponta, é suportado. Há algumas outras pequenas omissões devido ao fato de a especificação Matrix estar em constante evolução, mas o objetivo continua sendo fornecer suporte eventual para toda a especificação.</p>
|
||||
<p xml:lang="ru">Целью создания NeoChat является полноценная реализация программы для спецификации Matrix. Как следствие, реализовано всё в текущей стабильной спецификации (за исключением голосовой интернет-связи, потоков и некоторых аспектов сквозного шифрования). Есть также несколько других незначительных пробелов, обусловленных постоянными изменениями спецификации Matrix. Тем не менее, стоит задача в итоге предоставить полную поддержку спецификации.</p>
|
||||
<p xml:lang="sa">NeoChat इत्यस्य उद्देश्यं Matrix विनिर्देशस्य कृते पूर्णतया विशेषतायुक्तः अनुप्रयोगः भवितुम् अस्ति । यथा तथा वर्तमानस्थिरविनिर्देशे सर्वं VoIP इत्यस्य उल्लेखनीयअपवादैः सह, थ्रेड्स तथा च End-to-End Encryption इत्यस्य केचन पक्षाः समर्थिताः सन्ति । अन्ये कतिचन लघु लोपाः सन्ति यतोहि Matrix spec निरन्तरं विकसितः अस्ति परन्तु उद्देश्यं सम्पूर्ण spec कृते अन्ततः समर्थनं प्रदातुं अवशिष्टम् अस्ति</p>
|
||||
<p xml:lang="sl">Neochat cilja, da bi bila popolna aplikacija po specifikaciji Matrixa. Kot takšna vsebuje vse v trenutni stabilni specifikaciji z pomembnimi izjemami pri VoIP, nitih in nekaterih vidikov šifriranja od konca do konca. Obstaja nekaj drugih manjših opustitev zaradi dejstva, da se specifikacija Matrix nenehno razvija, vendar cilj ostaja zagotoviti morebitno podporo celotni specifikaciji.</p>
|
||||
@@ -178,7 +172,6 @@
|
||||
<p xml:lang="nn">På grunn av måten Matrix-spesifikasjonen vert utvikla på, støttar NeoChat òg nokre uferdige funksjonar:</p>
|
||||
<p xml:lang="pl">Ze względu na sposób rozwoju Matriksa, NeoChat obsługuje także kilka niestabilnych możliwości. Obecnie są to:</p>
|
||||
<p xml:lang="pt">Devido à natureza do desenvolvimento da especificação do Matrix, o NeoChat também suporta diversas funcionalidades instáveis. De momento são:</p>
|
||||
<p xml:lang="pt-BR">Devido à natureza do desenvolvimento da especificação Matrix, o NeoChat também suporta diversos recursos instáveis. Atualmente, são eles:</p>
|
||||
<p xml:lang="ru">В силу природы разработки спецификации Matrix в NeoChat тоже предусмотрена поддержка многочисленных нестабильных возможностей. В текущей версии это следующие возможности:</p>
|
||||
<p xml:lang="sa">Matrix विनिर्देशविकासस्य प्रकृतेः कारणात् NeoChat अपि अनेकानाम् अस्थिरविशेषतानां समर्थनं करोति । सम्प्रति एते सन्ति :</p>
|
||||
<p xml:lang="sl">Zaradi narave razvoja specifikacije Matrixa NeoChat podpira tudi številne nestabilne zmožnosti. Trenutno so to:</p>
|
||||
@@ -191,8 +184,8 @@
|
||||
<ul>
|
||||
<li>Polls - MSC3381</li>
|
||||
<li xml:lang="ar">التصويت - MSC3381</li>
|
||||
<li xml:lang="ca">Votacions - MSC3381</li>
|
||||
<li xml:lang="ca-valencia">Votacions - MSC3381</li>
|
||||
<li xml:lang="ca">Enquestes - MSC3381</li>
|
||||
<li xml:lang="ca-valencia">Enquestes - MSC3381</li>
|
||||
<li xml:lang="el">Δημοσκοπήσεις - MSC3381</li>
|
||||
<li xml:lang="en-GB">Polls - MSC3381</li>
|
||||
<li xml:lang="eo">Enketoj - MSC3381</li>
|
||||
@@ -213,7 +206,6 @@
|
||||
<li xml:lang="nn">Avstemmingar – MSC3381</li>
|
||||
<li xml:lang="pl">Ankiety - MSC3381</li>
|
||||
<li xml:lang="pt">Inquéritos - MSC3381</li>
|
||||
<li xml:lang="pt-BR">Enquetes - MSC3381</li>
|
||||
<li xml:lang="ru">Голосования — MSC3381</li>
|
||||
<li xml:lang="sa">मतदान - MSC3381</li>
|
||||
<li xml:lang="sl">Polls - MSC3381</li>
|
||||
@@ -247,7 +239,6 @@
|
||||
<li xml:lang="nn">Klistremerke-pakkar – MSC2545</li>
|
||||
<li xml:lang="pl">Paczki naklejek - MSC2545</li>
|
||||
<li xml:lang="pt">Pacotes de Autocolantes - MSC2545</li>
|
||||
<li xml:lang="pt-BR">Pacotes de Stickers - MSC2545</li>
|
||||
<li xml:lang="ru">Наборы стикеров — MSC2545</li>
|
||||
<li xml:lang="sa">स्टिकर पैक - MSC2545</li>
|
||||
<li xml:lang="sl">Sticker Packs - MSC2545</li>
|
||||
@@ -281,7 +272,6 @@
|
||||
<li xml:lang="nn">Posisjonshendingar – MSC3488</li>
|
||||
<li xml:lang="pl">Wydarzenia w miejscach - MSC3488</li>
|
||||
<li xml:lang="pt">Eventos com Localizações - MSC3488</li>
|
||||
<li xml:lang="pt-BR">Localização de eventos - MSC3488</li>
|
||||
<li xml:lang="ru">События местоположения — MSC3488</li>
|
||||
<li xml:lang="sa">स्थान घटनाएँ - MSC3488</li>
|
||||
<li xml:lang="sl">Location Events - MSC3488</li>
|
||||
@@ -351,7 +341,6 @@
|
||||
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
|
||||
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
|
||||
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
|
||||
<caption xml:lang="pt-BR">Visão principal com lista de salas, bate-papo e informações sobre as salas</caption>
|
||||
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
|
||||
<caption xml:lang="sa">कक्षसूची, गपशपः, कक्षसूचना च सह मुख्यदृश्यम्</caption>
|
||||
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
|
||||
@@ -383,12 +372,10 @@
|
||||
<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>
|
||||
<caption xml:lang="ka">აღმოაჩინეთ ახალი საზოგადოებები Matrix Spaces-თან ერთად</caption>
|
||||
<caption xml:lang="ko">Matrix 스페이스에서 새로운 커뮤니티 탐험</caption>
|
||||
<caption xml:lang="lv">Atklājiet jaunas kopienas ar „Matrix“ telpām</caption>
|
||||
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
|
||||
<caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption>
|
||||
<caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption>
|
||||
<caption xml:lang="pt-BR">Descubra novas comunidades com os Espaços Matrix</caption>
|
||||
<caption xml:lang="ru">Поиск новых сообществ с помощью Matrix Spaces</caption>
|
||||
<caption xml:lang="sa">Matrix Spaces इत्यनेन सह नूतनानां समुदायानाम् अन्वेषणं कुर्वन्तु</caption>
|
||||
<caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption>
|
||||
@@ -433,7 +420,6 @@
|
||||
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
|
||||
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
|
||||
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
|
||||
<caption xml:lang="pt-BR">Visão principal com lista de salas, bate-papo e informações sobre as salas</caption>
|
||||
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
|
||||
<caption xml:lang="sa">कक्षसूची, गपशपः, कक्षसूचना च सह मुख्यदृश्यम्</caption>
|
||||
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
|
||||
@@ -472,7 +458,6 @@
|
||||
<caption xml:lang="nn">Innloggingsbilete</caption>
|
||||
<caption xml:lang="pl">Ekran logowania</caption>
|
||||
<caption xml:lang="pt">Ecrã de autenticação</caption>
|
||||
<caption xml:lang="pt-BR">Tela de login</caption>
|
||||
<caption xml:lang="ru">Окно входа</caption>
|
||||
<caption xml:lang="sa">लॉगिन् स्क्रीन</caption>
|
||||
<caption xml:lang="sl">Prijavni zaslon</caption>
|
||||
@@ -488,11 +473,6 @@
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release version="25.08.0" date="2025-08-14"/>
|
||||
<release version="25.04.3" date="2025-07-03"/>
|
||||
<release version="25.04.2" date="2025-06-05"/>
|
||||
<release version="25.04.1" date="2025-05-08"/>
|
||||
<release version="25.04.0" date="2025-04-17"/>
|
||||
<release version="24.12.3" date="2025-03-06"/>
|
||||
<release version="24.12.2" date="2025-02-06"/>
|
||||
<release version="24.12.1" date="2025-01-09"/>
|
||||
|
||||
@@ -109,12 +109,10 @@ Comment[hu]=Csevegés Matrixon
|
||||
Comment[ia]=Conversation en ditecto sur Matrix
|
||||
Comment[it]= su Matrix
|
||||
Comment[ka]=ჩატი Matrix-ზე
|
||||
Comment[ko]=Matrix에서 대화하기
|
||||
Comment[lv]=Tērzējiet „Matrix“ tīklā
|
||||
Comment[nl]=Chat op Matrix
|
||||
Comment[pl]=Rozmawiaj na Matriksie
|
||||
Comment[pt_BR]=Bate papo na Matrix
|
||||
Comment[ru]=Общение в Matrix
|
||||
Comment[sa]=Matrix इत्यत्र गपशपं कुर्वन्तु
|
||||
Comment[sl]=Klepet na Matrixu
|
||||
Comment[sv]=Chatta på Matrix
|
||||
|
||||
7271
po/ar/neochat.po
7271
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
6993
po/ast/neochat.po
6993
po/ast/neochat.po
File diff suppressed because it is too large
Load Diff
7947
po/az/neochat.po
7947
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
7311
po/ca/neochat.po
7311
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
7289
po/cs/neochat.po
7289
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
7657
po/da/neochat.po
7657
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
7682
po/de/neochat.po
7682
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
7659
po/el/neochat.po
7659
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
7608
po/en_GB/neochat.po
7608
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
7436
po/eo/neochat.po
7436
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
7309
po/es/neochat.po
7309
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
7259
po/eu/neochat.po
7259
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
7223
po/fi/neochat.po
7223
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
7481
po/fr/neochat.po
7481
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
7370
po/gl/neochat.po
7370
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
6685
po/he/neochat.po
6685
po/he/neochat.po
File diff suppressed because it is too large
Load Diff
7537
po/hi/neochat.po
7537
po/hi/neochat.po
File diff suppressed because it is too large
Load Diff
7617
po/hu/neochat.po
7617
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
7418
po/ia/neochat.po
7418
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
7881
po/id/neochat.po
7881
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
7660
po/ie/neochat.po
7660
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
7414
po/it/neochat.po
7414
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
6981
po/ja/neochat.po
6981
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
7327
po/ka/neochat.po
7327
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
8267
po/ko/neochat.po
8267
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
6913
po/lt/neochat.po
6913
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
7346
po/lv/neochat.po
7346
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
7408
po/nl/neochat.po
7408
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
7291
po/nn/neochat.po
7291
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
7929
po/pa/neochat.po
7929
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
7548
po/pl/neochat.po
7548
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
7909
po/pt/neochat.po
7909
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
9116
po/pt_BR/neochat.po
9116
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -1,122 +0,0 @@
|
||||
<?xml version="1.0" ?>
|
||||
<!DOCTYPE refentry PUBLIC "-//KDE//DTD DocBook XML V4.5-Based Variant V1.1//EN" "dtd/kdedbx45.dtd" [
|
||||
<!ENTITY % Russian "INCLUDE">
|
||||
]>
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
<refentry lang="&language;">
|
||||
<refentryinfo>
|
||||
<title
|
||||
>Руководство пользователя NeoChat</title>
|
||||
<author
|
||||
><firstname
|
||||
>Carl</firstname
|
||||
><surname
|
||||
>Schwan</surname
|
||||
> <contrib
|
||||
>man-страница NeoChat.</contrib
|
||||
> <email
|
||||
>carl@carlschwan.eu</email
|
||||
></author>
|
||||
<date
|
||||
>2022-11-01</date>
|
||||
<releaseinfo
|
||||
>22.09</releaseinfo>
|
||||
<productname
|
||||
>NeoChat</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>
|
||||
<command
|
||||
>neochat</command>
|
||||
</refentrytitle>
|
||||
<manvolnum
|
||||
>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname
|
||||
>neochat</refname>
|
||||
<refpurpose
|
||||
>Клиент для взаимодействия с протоколом обмена сообщениями Matrix</refpurpose>
|
||||
</refnamediv>
|
||||
<!-- body begins here -->
|
||||
<refsynopsisdiv id='synopsis'>
|
||||
<cmdsynopsis
|
||||
><command
|
||||
>neochat</command
|
||||
> <arg choice="opt"
|
||||
><replaceable
|
||||
>URI</replaceable
|
||||
></arg
|
||||
> </cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
||||
<refsect1 id="description">
|
||||
<title
|
||||
>Описание</title>
|
||||
<para
|
||||
><command
|
||||
>neochat</command
|
||||
> — приложение для настольных и мобильных устройств, позволяющее общаться в чатах с помощью протокола Matrix. </para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="options"
|
||||
><title
|
||||
>Параметры</title>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term
|
||||
><option
|
||||
>URI</option
|
||||
></term>
|
||||
<listitem>
|
||||
<para
|
||||
>URI-адрес пользователя или комнаты в Matrix, например: matrix:u/user:example.org и matrix:r/root:example.org. NeoChat попытается открыть указанную комнату или беседу. </para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="bug">
|
||||
<title
|
||||
>Отчёты об ошибках</title>
|
||||
<para
|
||||
>Сообщать об ошибках и отправлять предложения по улучшению можно по адресу <ulink url="https://bugs.kde.org/enter_bug.cgi?product=NeoChat&component=General"
|
||||
>https://bugs.kde.org/enter_bug.cgi?product=NeoChat&component=General</ulink
|
||||
></para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title
|
||||
>Смотрите также</title>
|
||||
<simplelist>
|
||||
<member
|
||||
>Список наиболее часто задаваемых вопросов о Matrix <ulink url="https://matrix.org/faq/"
|
||||
>https://matrix.org/faq/</ulink
|
||||
> </member>
|
||||
<member
|
||||
>kf5options(7)</member>
|
||||
<member
|
||||
>qt5options(7)</member>
|
||||
</simplelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="copyright"
|
||||
><title
|
||||
>Авторские права</title>
|
||||
<para
|
||||
>Авторские права © Tobias Fella, 2020–2022 </para>
|
||||
<para
|
||||
>Авторские права © Carl Schwan, 2020–2022 </para>
|
||||
<para
|
||||
>Лицензия: стандартная общественная лицензия GNU версии 3 или любой более поздней версии <<ulink url="https://www.gnu.org/licenses/gpl-3.0.html"
|
||||
>https://www.gnu.org/licenses/gpl-3.0.html</ulink
|
||||
>></para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
8055
po/ru/neochat.po
8055
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
7497
po/sa/neochat.po
7497
po/sa/neochat.po
File diff suppressed because it is too large
Load Diff
7870
po/sk/neochat.po
7870
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
7267
po/sl/neochat.po
7267
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
7367
po/sv/neochat.po
7367
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
7317
po/ta/neochat.po
7317
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
7492
po/tok/neochat.po
7492
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
7379
po/tr/neochat.po
7379
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
7336
po/uk/neochat.po
7336
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
7135
po/zh_CN/neochat.po
7135
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
7247
po/zh_TW/neochat.po
7247
po/zh_TW/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2024-2025 Scarlett Moore <sgmoore@kde.org>
|
||||
# SPDX-FileCopyrightText: 2024 Scarlett Moore <sgmoore@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
---
|
||||
@@ -24,9 +24,6 @@ apps:
|
||||
- network-manager-observe
|
||||
- password-manager-service
|
||||
- accounts-service
|
||||
environment:
|
||||
QT_PLUGIN_PATH: "$SNAP/usr/lib/$CRAFT_ARCH_TRIPLET/plugins/snap/kf6-core24/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/plugins"
|
||||
QML_IMPORT_PATH: "$SNAP/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/qml:/snap/kf6-core24/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/qml"
|
||||
|
||||
compression: lzo
|
||||
|
||||
@@ -44,12 +41,11 @@ parts:
|
||||
olm:
|
||||
source: https://gitlab.matrix.org/matrix-org/olm.git
|
||||
source-depth: 1
|
||||
source-tag: '3.2.16'
|
||||
source-tag: '3.2.12'
|
||||
plugin: cmake
|
||||
cmake-parameters:
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
prime:
|
||||
- -usr/include
|
||||
- -usr/lib/*/pkgconfig
|
||||
@@ -85,7 +81,8 @@ parts:
|
||||
plugin: cmake
|
||||
build-environment:
|
||||
- PATH: /snap/bin:${PATH}
|
||||
- PKG_CONFIG_PATH: "$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET/pkgconfig:$PKG_CONFIG_PATH"
|
||||
- PKG_CONFIG_PATH: "$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET\
|
||||
/pkgconfig:$PKG_CONFIG_PATH"
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
@@ -101,7 +98,7 @@ parts:
|
||||
- olm
|
||||
- qtkeychain
|
||||
source: https://github.com/quotient-im/libQuotient.git
|
||||
source-tag: 0.9.2
|
||||
source-tag: 0.9.1
|
||||
source-depth: 1
|
||||
plugin: cmake
|
||||
build-environment:
|
||||
@@ -140,24 +137,11 @@ parts:
|
||||
- -usr/lib/*/pkgconfig
|
||||
- -usr/lib/*/cmake
|
||||
|
||||
kunifiedpush:
|
||||
source: https://invent.kde.org/libraries/kunifiedpush.git
|
||||
plugin: cmake
|
||||
build-environment:
|
||||
- PATH: /snap/bin:${PATH}
|
||||
- PYTHONPATH: ${CRAFT_STAGE}/lib/python3.12/site-packages:${CRAFT_STAGE}/usr/lib/python3/dist-packages
|
||||
- LD_LIBRARY_PATH: "/snap/mesa-2404/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/kde-qt6-core24-sdk/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libproxy:$LD_LIBRARY_PATH"
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DBUILD_TESTING=OFF
|
||||
|
||||
neochat:
|
||||
after:
|
||||
- qtkeychain
|
||||
- libquotient
|
||||
- kquickimageeditor
|
||||
- kunifiedpush
|
||||
parse-info:
|
||||
- usr/share/metainfo/org.kde.neochat.appdata.xml
|
||||
source: .
|
||||
|
||||
@@ -1,18 +1,612 @@
|
||||
# SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
|
||||
# SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carl@carlschwan.eu>
|
||||
# SPDX-FileCopyrightText: 2020-2021 Nicolas Fella <nicolas.fella@gmx.de>
|
||||
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT NEOCHAT_FLATPAK AND NOT NEOCHAT_APPIMAGE)
|
||||
add_subdirectory(purpose)
|
||||
endif()
|
||||
|
||||
add_subdirectory(libneochat)
|
||||
add_subdirectory(login)
|
||||
add_subdirectory(rooms)
|
||||
add_subdirectory(roominfo)
|
||||
add_subdirectory(messagecontent)
|
||||
add_subdirectory(timeline)
|
||||
add_subdirectory(spaces)
|
||||
add_subdirectory(chatbar)
|
||||
add_library(neochat STATIC
|
||||
controller.cpp
|
||||
controller.h
|
||||
models/emojimodel.cpp
|
||||
models/emojimodel.h
|
||||
emojitones.cpp
|
||||
emojitones.h
|
||||
models/customemojimodel.cpp
|
||||
models/customemojimodel.h
|
||||
clipboard.cpp
|
||||
clipboard.h
|
||||
models/timelinemessagemodel.cpp
|
||||
models/timelinemessagemodel.h
|
||||
models/messagefiltermodel.cpp
|
||||
models/messagefiltermodel.h
|
||||
models/roomlistmodel.cpp
|
||||
models/roomlistmodel.h
|
||||
models/sortfilterspacelistmodel.cpp
|
||||
models/sortfilterspacelistmodel.h
|
||||
models/accountemoticonmodel.cpp
|
||||
models/accountemoticonmodel.h
|
||||
spacehierarchycache.cpp
|
||||
spacehierarchycache.h
|
||||
roommanager.cpp
|
||||
roommanager.h
|
||||
neochatroom.cpp
|
||||
neochatroom.h
|
||||
models/userlistmodel.cpp
|
||||
models/userlistmodel.h
|
||||
models/userfiltermodel.cpp
|
||||
models/userfiltermodel.h
|
||||
models/publicroomlistmodel.cpp
|
||||
models/publicroomlistmodel.h
|
||||
models/spacechildrenmodel.cpp
|
||||
models/spacechildrenmodel.h
|
||||
models/spacechildsortfiltermodel.cpp
|
||||
models/spacechildsortfiltermodel.h
|
||||
models/spacetreeitem.cpp
|
||||
models/spacetreeitem.h
|
||||
models/userdirectorylistmodel.cpp
|
||||
models/userdirectorylistmodel.h
|
||||
models/pushrulemodel.cpp
|
||||
models/pushrulemodel.h
|
||||
models/emoticonfiltermodel.cpp
|
||||
models/emoticonfiltermodel.h
|
||||
notificationsmanager.cpp
|
||||
notificationsmanager.h
|
||||
models/sortfilterroomlistmodel.cpp
|
||||
models/sortfilterroomlistmodel.h
|
||||
models/roomtreemodel.cpp
|
||||
models/roomtreemodel.h
|
||||
chatdocumenthandler.cpp
|
||||
chatdocumenthandler.h
|
||||
models/devicesmodel.cpp
|
||||
models/devicesmodel.h
|
||||
models/devicesproxymodel.cpp
|
||||
filetype.cpp
|
||||
filetype.h
|
||||
login.cpp
|
||||
login.h
|
||||
models/webshortcutmodel.cpp
|
||||
models/webshortcutmodel.h
|
||||
blurhash.cpp
|
||||
blurhash.h
|
||||
blurhashimageprovider.cpp
|
||||
blurhashimageprovider.h
|
||||
models/mediamessagefiltermodel.cpp
|
||||
models/mediamessagefiltermodel.h
|
||||
urlhelper.cpp
|
||||
urlhelper.h
|
||||
windowcontroller.cpp
|
||||
windowcontroller.h
|
||||
linkpreviewer.cpp
|
||||
linkpreviewer.h
|
||||
models/completionmodel.cpp
|
||||
models/completionmodel.h
|
||||
models/completionproxymodel.cpp
|
||||
models/completionproxymodel.h
|
||||
models/actionsmodel.cpp
|
||||
models/actionsmodel.h
|
||||
models/serverlistmodel.cpp
|
||||
models/serverlistmodel.h
|
||||
models/statemodel.cpp
|
||||
models/statemodel.h
|
||||
models/statefiltermodel.cpp
|
||||
models/statefiltermodel.h
|
||||
filetransferpseudojob.cpp
|
||||
filetransferpseudojob.h
|
||||
models/searchmodel.cpp
|
||||
models/searchmodel.h
|
||||
texthandler.cpp
|
||||
texthandler.h
|
||||
logger.cpp
|
||||
logger.h
|
||||
models/stickermodel.cpp
|
||||
models/stickermodel.h
|
||||
models/imagepacksmodel.cpp
|
||||
models/imagepacksmodel.h
|
||||
events/imagepackevent.cpp
|
||||
events/imagepackevent.h
|
||||
models/reactionmodel.cpp
|
||||
models/reactionmodel.h
|
||||
delegatesizehelper.cpp
|
||||
delegatesizehelper.h
|
||||
models/livelocationsmodel.cpp
|
||||
models/livelocationsmodel.h
|
||||
models/locationsmodel.cpp
|
||||
models/locationsmodel.h
|
||||
locationhelper.cpp
|
||||
locationhelper.h
|
||||
events/pollevent.cpp
|
||||
pollhandler.cpp
|
||||
utils.h
|
||||
utils.cpp
|
||||
registration.cpp
|
||||
neochatconnection.cpp
|
||||
neochatconnection.h
|
||||
jobs/neochatgetcommonroomsjob.cpp
|
||||
jobs/neochatgetcommonroomsjob.h
|
||||
mediasizehelper.cpp
|
||||
mediasizehelper.h
|
||||
eventhandler.cpp
|
||||
enums/delegatetype.h
|
||||
roomlastmessageprovider.cpp
|
||||
roomlastmessageprovider.h
|
||||
chatbarcache.cpp
|
||||
chatbarcache.h
|
||||
colorschemer.cpp
|
||||
colorschemer.h
|
||||
models/notificationsmodel.cpp
|
||||
models/notificationsmodel.h
|
||||
models/timelinemodel.cpp
|
||||
models/timelinemodel.h
|
||||
enums/pushrule.h
|
||||
models/itinerarymodel.cpp
|
||||
models/itinerarymodel.h
|
||||
proxycontroller.cpp
|
||||
proxycontroller.h
|
||||
models/linemodel.cpp
|
||||
models/linemodel.h
|
||||
events/locationbeaconevent.h
|
||||
events/widgetevent.h
|
||||
enums/messagecomponenttype.h
|
||||
models/messagecontentmodel.cpp
|
||||
models/messagecontentmodel.h
|
||||
enums/neochatroomtype.h
|
||||
models/sortfilterroomtreemodel.cpp
|
||||
models/sortfilterroomtreemodel.h
|
||||
mediamanager.cpp
|
||||
mediamanager.h
|
||||
models/statekeysmodel.cpp
|
||||
models/statekeysmodel.h
|
||||
sharehandler.cpp
|
||||
sharehandler.h
|
||||
models/roomtreeitem.cpp
|
||||
models/roomtreeitem.h
|
||||
foreigntypes.h
|
||||
models/threepidmodel.cpp
|
||||
models/threepidmodel.h
|
||||
threepidaddhelper.cpp
|
||||
threepidaddhelper.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
|
||||
models/threadmodel.cpp
|
||||
models/threadmodel.h
|
||||
enums/messagetype.h
|
||||
messagecomponent.h
|
||||
enums/roomsortparameter.cpp
|
||||
enums/roomsortparameter.h
|
||||
models/roomsortparametermodel.cpp
|
||||
models/roomsortparametermodel.h
|
||||
models/messagemodel.cpp
|
||||
models/messagemodel.h
|
||||
models/messagecontentfiltermodel.cpp
|
||||
models/messagecontentfiltermodel.h
|
||||
models/pinnedmessagemodel.cpp
|
||||
models/pinnedmessagemodel.h
|
||||
models/commonroomsmodel.cpp
|
||||
models/commonroomsmodel.h
|
||||
events/callencryptionkeysevent.h
|
||||
events/callmemberevent.h
|
||||
events/callnotifyevent.h
|
||||
calls/callcontroller.cpp
|
||||
calls/callcontroller.h
|
||||
livekitlogmodel.cpp
|
||||
livekitlogmodel.h
|
||||
events/callmemberevent.cpp
|
||||
events/callmemberevent.h
|
||||
)
|
||||
|
||||
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||
QT_QML_SINGLETON_TYPE TRUE
|
||||
)
|
||||
|
||||
if(ANDROID OR WIN32)
|
||||
set_source_files_properties(qml/ShareActionStub.qml PROPERTIES
|
||||
QT_QML_SOURCE_TYPENAME ShareAction
|
||||
)
|
||||
endif()
|
||||
|
||||
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat
|
||||
QML_FILES
|
||||
qml/Main.qml
|
||||
qml/AccountMenu.qml
|
||||
qml/ExploreComponent.qml
|
||||
qml/ExploreComponentMobile.qml
|
||||
qml/RoomContextMenu.qml
|
||||
qml/CollapsedRoomDelegate.qml
|
||||
qml/RoomDelegate.qml
|
||||
qml/RoomListPage.qml
|
||||
qml/SpaceListContextMenu.qml
|
||||
qml/UserInfo.qml
|
||||
qml/UserInfoDesktop.qml
|
||||
qml/RoomPage.qml
|
||||
qml/ExploreRoomsPage.qml
|
||||
qml/ManualRoomDialog.qml
|
||||
qml/ExplorerDelegate.qml
|
||||
qml/InviteUserPage.qml
|
||||
qml/ImageEditorPage.qml
|
||||
qml/NeochatMaximizeComponent.qml
|
||||
qml/TypingPane.qml
|
||||
qml/QuickSwitcher.qml
|
||||
qml/HoverActions.qml
|
||||
qml/AttachmentPane.qml
|
||||
qml/QuickFormatBar.qml
|
||||
qml/UserDetailDialog.qml
|
||||
qml/CreateRoomDialog.qml
|
||||
qml/OpenFileDialog.qml
|
||||
qml/KeyVerificationDialog.qml
|
||||
qml/ConfirmLogoutDialog.qml
|
||||
qml/PowerLevelDialog.qml
|
||||
qml/Message.qml
|
||||
qml/EmojiItem.qml
|
||||
qml/EmojiRow.qml
|
||||
qml/EmojiSas.qml
|
||||
qml/ConfirmDeactivateAccountDialog.qml
|
||||
qml/VerificationCanceled.qml
|
||||
qml/MessageDelegateContextMenu.qml
|
||||
qml/FileDelegateContextMenu.qml
|
||||
qml/MessageSourceSheet.qml
|
||||
qml/ConfirmEncryptionDialog.qml
|
||||
qml/RoomSearchPage.qml
|
||||
qml/RoomPinnedMessagesPage.qml
|
||||
qml/LocationChooser.qml
|
||||
qml/TimelineView.qml
|
||||
qml/InvitationView.qml
|
||||
qml/AvatarTabButton.qml
|
||||
qml/SpaceDrawer.qml
|
||||
qml/OsmLocationPlugin.qml
|
||||
qml/FullScreenMap.qml
|
||||
qml/LocationsPage.qml
|
||||
qml/LocationMapItem.qml
|
||||
qml/RoomDrawer.qml
|
||||
qml/RoomDrawerPage.qml
|
||||
qml/DirectChatDrawerHeader.qml
|
||||
qml/GroupChatDrawerHeader.qml
|
||||
qml/RoomInformation.qml
|
||||
qml/RoomMedia.qml
|
||||
qml/ChooseRoomDialog.qml
|
||||
qml/SpaceHomePage.qml
|
||||
qml/SpaceHierarchyDelegate.qml
|
||||
qml/RemoveChildDialog.qml
|
||||
qml/SelectParentDialog.qml
|
||||
qml/QrCodeMaximizeComponent.qml
|
||||
qml/SelectSpacesDialog.qml
|
||||
qml/NotificationsView.qml
|
||||
qml/SearchPage.qml
|
||||
qml/ServerComboBox.qml
|
||||
qml/UserSearchPage.qml
|
||||
qml/ManualUserDialog.qml
|
||||
qml/RecommendedSpaceDialog.qml
|
||||
qml/RoomTreeSection.qml
|
||||
qml/DelegateContextMenu.qml
|
||||
qml/ShareDialog.qml
|
||||
qml/UnlockSSSSDialog.qml
|
||||
qml/QrScannerPage.qml
|
||||
qml/JoinRoomDialog.qml
|
||||
qml/ConfirmUrlDialog.qml
|
||||
qml/AccountSwitchDialog.qml
|
||||
qml/ConfirmLeaveDialog.qml
|
||||
qml/CodeMaximizeComponent.qml
|
||||
qml/EditStateDialog.qml
|
||||
qml/ConsentDialog.qml
|
||||
qml/AskDirectChatConfirmation.qml
|
||||
qml/HoverLinkIndicator.qml
|
||||
qml/AvatarNotification.qml
|
||||
qml/ReasonDialog.qml
|
||||
qml/LivekitLogViewer.qml
|
||||
qml/CallPage.qml
|
||||
qml/IncomingCallDialog.qml
|
||||
SOURCES
|
||||
messageattached.cpp
|
||||
messageattached.h
|
||||
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(app)
|
||||
add_subdirectory(login)
|
||||
add_subdirectory(chatbar)
|
||||
|
||||
if(NOT ANDROID AND NOT WIN32)
|
||||
qt_target_qml_sources(neochat QML_FILES
|
||||
qml/ShareAction.qml
|
||||
qml/GlobalMenu.qml
|
||||
qml/EditMenu.qml
|
||||
)
|
||||
else()
|
||||
qt_target_qml_sources(neochat QML_FILES qml/ShareActionStub.qml)
|
||||
endif()
|
||||
|
||||
configure_file(config-neochat.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-neochat.h)
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(neochat PROPERTIES OUTPUT_NAME "neochatlib")
|
||||
endif()
|
||||
|
||||
ecm_qt_declare_logging_category(neochat
|
||||
HEADER "messagemodel_logging.h"
|
||||
IDENTIFIER "Message"
|
||||
CATEGORY_NAME "org.kde.neochat.messagemodel"
|
||||
DESCRIPTION "Neochat: messagemodel"
|
||||
DEFAULT_SEVERITY Info
|
||||
EXPORT NEOCHAT
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(neochat
|
||||
HEADER "publicroomlist_logging.h"
|
||||
IDENTIFIER "PublicRoomList"
|
||||
CATEGORY_NAME "org.kde.neochat.publicroomlistmodel"
|
||||
DESCRIPTION "Neochat: publicroomlistmodel"
|
||||
DEFAULT_SEVERITY Info
|
||||
EXPORT NEOCHAT
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(neochat
|
||||
HEADER "eventhandler_logging.h"
|
||||
IDENTIFIER "EventHandling"
|
||||
CATEGORY_NAME "org.kde.neochat.eventhandler"
|
||||
DEFAULT_SEVERITY Info
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(neochat
|
||||
HEADER "chatdocumenthandler_logging.h"
|
||||
IDENTIFIER "ChatDocumentHandling"
|
||||
CATEGORY_NAME "org.kde.neochat.chatdocumenthandler"
|
||||
DEFAULT_SEVERITY Info
|
||||
)
|
||||
|
||||
add_executable(neochat-app
|
||||
main.cpp
|
||||
)
|
||||
|
||||
if(TARGET Qt::WebView)
|
||||
target_link_libraries(neochat-app PUBLIC Qt::WebView)
|
||||
target_compile_definitions(neochat-app PUBLIC -DHAVE_WEBVIEW)
|
||||
endif()
|
||||
|
||||
target_include_directories(neochat-app PRIVATE ${CMAKE_BINARY_DIR})
|
||||
|
||||
target_link_libraries(neochat-app PRIVATE
|
||||
neochat
|
||||
)
|
||||
|
||||
ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
|
||||
|
||||
target_sources(neochat-app PRIVATE ${NEOCHAT_ICON})
|
||||
|
||||
if(NOT ANDROID)
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
target_sources(neochat PRIVATE trayicon_sni.cpp trayicon_sni.h)
|
||||
target_link_libraries(neochat PRIVATE KF6::StatusNotifierItem)
|
||||
else()
|
||||
target_sources(neochat PRIVATE trayicon.cpp trayicon.h)
|
||||
endif()
|
||||
target_link_libraries(neochat PUBLIC KF6::WindowSystem ICU::uc)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_WINDOWSYSTEM)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_ICU)
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_RUNNER)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_X11=1)
|
||||
target_sources(neochat PRIVATE runner.cpp)
|
||||
|
||||
if (TARGET KUnifiedPush)
|
||||
target_sources(neochat PRIVATE fakerunner.cpp)
|
||||
endif()
|
||||
else()
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_X11=0)
|
||||
endif()
|
||||
|
||||
qt_add_protobuf(neochat
|
||||
GENERATE_PACKAGE_SUBFOLDERS
|
||||
PROTO_FILES
|
||||
protocols/ffi.proto
|
||||
protocols/room.proto
|
||||
protocols/e2ee.proto
|
||||
protocols/audio_frame.proto
|
||||
protocols/video_frame.proto
|
||||
protocols/handle.proto
|
||||
protocols/participant.proto
|
||||
protocols/stats.proto
|
||||
protocols/track.proto
|
||||
protocols/rpc.proto
|
||||
)
|
||||
|
||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums ${CMAKE_CURRENT_SOURCE_DIR}/calls)
|
||||
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin devtoolsplugin loginplugin chatbarplugin)
|
||||
target_link_libraries(neochat PUBLIC
|
||||
Qt::Core
|
||||
Qt::Quick
|
||||
Qt::Qml
|
||||
Qt::Gui
|
||||
Qt::Multimedia
|
||||
Qt::Network
|
||||
Qt::QuickControls2
|
||||
Qt::Protobuf
|
||||
KF6::I18n
|
||||
KF6::Kirigami
|
||||
KF6::Notifications
|
||||
KF6::ConfigCore
|
||||
KF6::ConfigGui
|
||||
KF6::CoreAddons
|
||||
KF6::SonnetCore
|
||||
KF6::IconThemes
|
||||
KF6::ColorScheme
|
||||
KF6::ItemModels
|
||||
QuotientQt6
|
||||
cmark::cmark
|
||||
QCoro::Core
|
||||
QCoro::Network
|
||||
LiveKit
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
if(NEOCHAT_FLATPAK)
|
||||
target_compile_definitions(neochat PUBLIC NEOCHAT_FLATPAK)
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
target_sources(neochat PRIVATE notifyrc.qrc)
|
||||
target_link_libraries(neochat PRIVATE Qt::Svg OpenSSL::SSL)
|
||||
if(SQLite3_FOUND)
|
||||
target_link_libraries(neochat-app PRIVATE SQLite::SQLite3)
|
||||
endif()
|
||||
target_sources(neochat-app PRIVATE notifyrc.qrc)
|
||||
target_link_libraries(neochat PUBLIC Qt::Svg OpenSSL::SSL)
|
||||
kirigami_package_breeze_icons(ICONS
|
||||
"arrow-down-symbolic"
|
||||
"arrow-up-symbolic"
|
||||
"arrow-up-double-symbolic"
|
||||
"arrow-left-symbolic"
|
||||
"arrow-right-symbolic"
|
||||
"checkmark"
|
||||
"help-about"
|
||||
"im-user"
|
||||
"im-invisible-user"
|
||||
"im-kick-user"
|
||||
"mail-attachment"
|
||||
"dialog-cancel"
|
||||
"preferences-desktop-emoticons"
|
||||
"preferences-security"
|
||||
"document-open"
|
||||
"document-save"
|
||||
"document-send"
|
||||
"dialog-close"
|
||||
"edit-delete-remove"
|
||||
"code-context"
|
||||
"document-edit"
|
||||
"list-user-add"
|
||||
"list-add-user"
|
||||
"user-others"
|
||||
"media-playback-pause"
|
||||
"media-playback-start"
|
||||
"media-playback-stop"
|
||||
"go-previous"
|
||||
"go-up"
|
||||
"go-down"
|
||||
"list-add"
|
||||
"irc-join-channel"
|
||||
"settings-configure"
|
||||
"configure"
|
||||
"rating"
|
||||
"rating-unrated"
|
||||
"search"
|
||||
"mail-replied-symbolic"
|
||||
"edit-clear"
|
||||
"edit-copy"
|
||||
"gtk-quit"
|
||||
"compass"
|
||||
"computer"
|
||||
"network-connect"
|
||||
"list-remove-user"
|
||||
"org.kde.neochat"
|
||||
"org.kde.neochat.tray"
|
||||
"preferences-system-users"
|
||||
"preferences-desktop-theme-global"
|
||||
"notifications"
|
||||
"notifications-disabled"
|
||||
"audio-volume-high"
|
||||
"audio-volume-muted"
|
||||
"draw-highlight"
|
||||
"zoom-in"
|
||||
"zoom-out"
|
||||
"image-rotate-left-symbolic"
|
||||
"image-rotate-right-symbolic"
|
||||
"channel-secure-symbolic"
|
||||
"download"
|
||||
"smiley"
|
||||
"tools-check-spelling"
|
||||
"username-copy"
|
||||
"system-switch-user"
|
||||
"bookmark-new"
|
||||
"bookmark-remove"
|
||||
"favorite"
|
||||
"window-new"
|
||||
"globe"
|
||||
"visibility"
|
||||
"home"
|
||||
"preferences-desktop-notification"
|
||||
"computer-symbolic"
|
||||
"gps"
|
||||
"system-users-symbolic"
|
||||
"map-flat"
|
||||
"documentinfo"
|
||||
"view-list-details"
|
||||
"go-previous"
|
||||
"mail-forward-symbolic"
|
||||
"dialog-warning-symbolic"
|
||||
"object-rotate-left"
|
||||
"object-rotate-right"
|
||||
"add-subtitle"
|
||||
"security-high"
|
||||
"security-low"
|
||||
"security-low-symbolic"
|
||||
"kde"
|
||||
"list-remove-symbolic"
|
||||
"edit-delete"
|
||||
"user-home-symbolic"
|
||||
"pin-symbolic"
|
||||
"kt-restore-defaults-symbolic"
|
||||
"user-symbolic"
|
||||
)
|
||||
ecm_add_android_apk(neochat-app ANDROID_DIR ${CMAKE_SOURCE_DIR}/android)
|
||||
else()
|
||||
target_link_libraries(neochat PUBLIC Qt::Widgets KF6::KIOWidgets KF6::SyntaxHighlighting)
|
||||
install(FILES neochat.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
|
||||
endif()
|
||||
|
||||
if(NOT ANDROID)
|
||||
set_target_properties(neochat-app PROPERTIES OUTPUT_NAME "neochat")
|
||||
endif()
|
||||
|
||||
if(TARGET KF6::DBusAddons AND NOT WIN32)
|
||||
target_link_libraries(neochat PUBLIC KF6::DBusAddons)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_KDBUSADDONS)
|
||||
endif()
|
||||
|
||||
if (TARGET KF6::KIOWidgets)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_KIO)
|
||||
endif()
|
||||
|
||||
if (TARGET KUnifiedPush)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_KUNIFIEDPUSH)
|
||||
target_link_libraries(neochat PUBLIC KUnifiedPush)
|
||||
|
||||
if (NOT ANDROID)
|
||||
configure_file(org.kde.neochat.service.in ${CMAKE_CURRENT_BINARY_DIR}/org.kde.neochat.service)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.neochat.service DESTINATION ${KDE_INSTALL_DBUSSERVICEDIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(TARGETS neochat-app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins)
|
||||
endif()
|
||||
|
||||
@@ -1,366 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carl@carlschwan.eu>
|
||||
# SPDX-FileCopyrightText: 2020-2021 Nicolas Fella <nicolas.fella@gmx.de>
|
||||
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
add_library(neochat STATIC
|
||||
controller.cpp
|
||||
controller.h
|
||||
roommanager.cpp
|
||||
roommanager.h
|
||||
models/userdirectorylistmodel.cpp
|
||||
models/userdirectorylistmodel.h
|
||||
notificationsmanager.cpp
|
||||
notificationsmanager.h
|
||||
blurhash.cpp
|
||||
blurhash.h
|
||||
blurhashimageprovider.cpp
|
||||
blurhashimageprovider.h
|
||||
windowcontroller.cpp
|
||||
windowcontroller.h
|
||||
models/serverlistmodel.cpp
|
||||
models/serverlistmodel.h
|
||||
models/notificationsmodel.cpp
|
||||
models/notificationsmodel.h
|
||||
proxycontroller.cpp
|
||||
proxycontroller.h
|
||||
mediamanager.cpp
|
||||
mediamanager.h
|
||||
sharehandler.cpp
|
||||
sharehandler.h
|
||||
foreigntypes.h
|
||||
identityserverhelper.cpp
|
||||
identityserverhelper.h
|
||||
models/commonroomsmodel.cpp
|
||||
models/commonroomsmodel.h
|
||||
texttospeechhelper.h
|
||||
texttospeechhelper.cpp
|
||||
)
|
||||
|
||||
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||
QT_QML_SINGLETON_TYPE TRUE
|
||||
)
|
||||
|
||||
if(ANDROID OR WIN32)
|
||||
set_source_files_properties(qml/ShareActionStub.qml PROPERTIES
|
||||
QT_QML_SOURCE_TYPENAME ShareAction
|
||||
)
|
||||
set_source_files_properties(qml/GlobalMenuStub.qml PROPERTIES
|
||||
QT_QML_SOURCE_TYPENAME GlobalMenu
|
||||
)
|
||||
endif()
|
||||
|
||||
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat
|
||||
QML_FILES
|
||||
qml/Main.qml
|
||||
qml/AccountMenu.qml
|
||||
qml/CollapsedRoomDelegate.qml
|
||||
qml/RoomPage.qml
|
||||
qml/ManualRoomDialog.qml
|
||||
qml/ExplorerDelegate.qml
|
||||
qml/NeochatMaximizeComponent.qml
|
||||
qml/QuickSwitcher.qml
|
||||
qml/AttachmentPane.qml
|
||||
qml/QuickFormatBar.qml
|
||||
qml/UserDetailDialog.qml
|
||||
qml/OpenFileDialog.qml
|
||||
qml/KeyVerificationDialog.qml
|
||||
qml/ConfirmLogoutDialog.qml
|
||||
qml/VerificationMessage.qml
|
||||
qml/EmojiItem.qml
|
||||
qml/EmojiRow.qml
|
||||
qml/EmojiSas.qml
|
||||
qml/VerificationCanceled.qml
|
||||
qml/MessageSourceSheet.qml
|
||||
qml/LocationChooser.qml
|
||||
qml/InvitationView.qml
|
||||
qml/AvatarTabButton.qml
|
||||
qml/OsmLocationPlugin.qml
|
||||
qml/FullScreenMap.qml
|
||||
qml/ChooseRoomDialog.qml
|
||||
qml/QrCodeMaximizeComponent.qml
|
||||
qml/NotificationsView.qml
|
||||
qml/ServerComboBox.qml
|
||||
qml/UserSearchPage.qml
|
||||
qml/ManualUserDialog.qml
|
||||
qml/RecommendedSpaceDialog.qml
|
||||
qml/ShareDialog.qml
|
||||
qml/UnlockSSSSDialog.qml
|
||||
qml/QrScannerPage.qml
|
||||
qml/JoinRoomDialog.qml
|
||||
qml/ConfirmUrlDialog.qml
|
||||
qml/AccountSwitchDialog.qml
|
||||
qml/ConfirmLeaveDialog.qml
|
||||
qml/CodeMaximizeComponent.qml
|
||||
qml/EditStateDialog.qml
|
||||
qml/ConsentDialog.qml
|
||||
qml/AskDirectChatConfirmation.qml
|
||||
qml/HoverLinkIndicator.qml
|
||||
qml/AvatarNotification.qml
|
||||
qml/ReasonDialog.qml
|
||||
qml/NewPollDialog.qml
|
||||
qml/UserMenu.qml
|
||||
DEPENDENCIES
|
||||
QtCore
|
||||
QtQuick
|
||||
IMPORTS
|
||||
org.kde.neochat.libneochat
|
||||
org.kde.neochat.rooms
|
||||
org.kde.neochat.roominfo
|
||||
org.kde.neochat.messagecontent
|
||||
org.kde.neochat.timeline
|
||||
org.kde.neochat.spaces
|
||||
org.kde.neochat.settings
|
||||
org.kde.neochat.devtools
|
||||
org.kde.neochat.login
|
||||
org.kde.neochat.chatbar
|
||||
)
|
||||
|
||||
if(NOT ANDROID AND NOT WIN32)
|
||||
qt_target_qml_sources(neochat QML_FILES
|
||||
qml/ShareAction.qml
|
||||
qml/GlobalMenu.qml
|
||||
qml/EditMenu.qml
|
||||
)
|
||||
else()
|
||||
qt_target_qml_sources(neochat QML_FILES
|
||||
qml/ShareActionStub.qml
|
||||
qml/GlobalMenuStub.qml
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(neochat PROPERTIES OUTPUT_NAME "neochatlib")
|
||||
endif()
|
||||
|
||||
add_executable(neochat-app
|
||||
main.cpp
|
||||
)
|
||||
|
||||
if(TARGET Qt::WebView)
|
||||
target_link_libraries(neochat-app PUBLIC Qt::WebView)
|
||||
target_compile_definitions(neochat-app PUBLIC -DHAVE_WEBVIEW)
|
||||
endif()
|
||||
|
||||
target_include_directories(neochat-app PRIVATE ${CMAKE_BINARY_DIR})
|
||||
|
||||
target_link_libraries(neochat-app PRIVATE
|
||||
neochat
|
||||
)
|
||||
|
||||
ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
|
||||
|
||||
target_sources(neochat-app PRIVATE ${NEOCHAT_ICON})
|
||||
|
||||
if(NOT ANDROID)
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
target_sources(neochat PRIVATE trayicon_sni.cpp trayicon_sni.h)
|
||||
target_link_libraries(neochat PRIVATE KF6::StatusNotifierItem)
|
||||
else()
|
||||
target_sources(neochat PRIVATE trayicon.cpp trayicon.h)
|
||||
endif()
|
||||
target_link_libraries(neochat PUBLIC KF6::WindowSystem)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_WINDOWSYSTEM)
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_RUNNER)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_X11=1)
|
||||
target_sources(neochat PRIVATE runner.cpp)
|
||||
|
||||
if (TARGET KUnifiedPush)
|
||||
target_sources(neochat PRIVATE fakerunner.cpp)
|
||||
endif()
|
||||
else()
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_X11=0)
|
||||
endif()
|
||||
|
||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models)
|
||||
target_link_libraries(neochat PRIVATE Loginplugin Roomsplugin RoomInfoplugin MessageContentplugin Timelineplugin Spacesplugin Chatbarplugin Settingsplugin Devtoolsplugin)
|
||||
target_link_libraries(neochat PUBLIC
|
||||
LibNeoChat
|
||||
Timeline
|
||||
Settings
|
||||
Qt::Core
|
||||
Qt::Quick
|
||||
Qt::Qml
|
||||
Qt::Gui
|
||||
Qt::Multimedia
|
||||
Qt::Network
|
||||
Qt::QuickControls2
|
||||
Qt::TextToSpeech
|
||||
KF6::I18n
|
||||
KF6::Kirigami
|
||||
KF6::Notifications
|
||||
KF6::ConfigCore
|
||||
KF6::ConfigGui
|
||||
KF6::CoreAddons
|
||||
KF6::SonnetCore
|
||||
KF6::IconThemes
|
||||
KF6::ItemModels
|
||||
QuotientQt6
|
||||
Login
|
||||
Rooms
|
||||
MessageContent
|
||||
Spaces
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
if(NEOCHAT_FLATPAK)
|
||||
target_compile_definitions(neochat PUBLIC NEOCHAT_FLATPAK)
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
target_sources(neochat PRIVATE notifyrc.qrc)
|
||||
target_link_libraries(neochat PRIVATE Qt::Svg OpenSSL::SSL)
|
||||
if(SQLite3_FOUND)
|
||||
target_link_libraries(neochat-app PRIVATE SQLite::SQLite3)
|
||||
endif()
|
||||
target_sources(neochat-app PRIVATE notifyrc.qrc)
|
||||
target_link_libraries(neochat PUBLIC Qt::Svg OpenSSL::SSL)
|
||||
kirigami_package_breeze_icons(ICONS
|
||||
"arrow-down-symbolic"
|
||||
"arrow-up-symbolic"
|
||||
"arrow-up-double-symbolic"
|
||||
"arrow-left-symbolic"
|
||||
"arrow-right-symbolic"
|
||||
"checkmark"
|
||||
"help-about"
|
||||
"im-user"
|
||||
"im-invisible-user"
|
||||
"im-kick-user"
|
||||
"mail-attachment"
|
||||
"dialog-cancel"
|
||||
"preferences-desktop-emoticons"
|
||||
"preferences-security"
|
||||
"document-open"
|
||||
"document-save"
|
||||
"document-send"
|
||||
"dialog-close"
|
||||
"edit-delete-remove"
|
||||
"code-context"
|
||||
"document-edit"
|
||||
"list-user-add"
|
||||
"list-add-user"
|
||||
"user-others"
|
||||
"media-playback-pause"
|
||||
"media-playback-start"
|
||||
"media-playback-stop"
|
||||
"go-previous"
|
||||
"go-up"
|
||||
"go-down"
|
||||
"list-add"
|
||||
"irc-join-channel"
|
||||
"settings-configure"
|
||||
"configure"
|
||||
"rating"
|
||||
"rating-unrated"
|
||||
"search"
|
||||
"mail-replied-symbolic"
|
||||
"edit-clear"
|
||||
"edit-copy"
|
||||
"gtk-quit"
|
||||
"compass"
|
||||
"computer"
|
||||
"network-connect"
|
||||
"list-remove-user"
|
||||
"org.kde.neochat"
|
||||
"org.kde.neochat.tray"
|
||||
"preferences-system-users"
|
||||
"preferences-desktop-theme-global"
|
||||
"notifications"
|
||||
"notifications-disabled"
|
||||
"audio-volume-high"
|
||||
"audio-volume-muted"
|
||||
"draw-highlight"
|
||||
"zoom-in"
|
||||
"zoom-out"
|
||||
"image-rotate-left-symbolic"
|
||||
"image-rotate-right-symbolic"
|
||||
"channel-secure-symbolic"
|
||||
"download"
|
||||
"smiley"
|
||||
"tools-check-spelling"
|
||||
"username-copy"
|
||||
"system-switch-user"
|
||||
"bookmark-new"
|
||||
"bookmark-remove"
|
||||
"favorite"
|
||||
"window-new"
|
||||
"globe"
|
||||
"visibility"
|
||||
"home"
|
||||
"preferences-desktop-notification"
|
||||
"computer-symbolic"
|
||||
"gps"
|
||||
"system-users-symbolic"
|
||||
"map-flat"
|
||||
"documentinfo"
|
||||
"view-list-details"
|
||||
"go-previous"
|
||||
"mail-forward-symbolic"
|
||||
"dialog-warning-symbolic"
|
||||
"object-rotate-left"
|
||||
"object-rotate-right"
|
||||
"add-subtitle"
|
||||
"security-high"
|
||||
"security-low"
|
||||
"security-low-symbolic"
|
||||
"kde"
|
||||
"list-remove-symbolic"
|
||||
"edit-delete"
|
||||
"user-home-symbolic"
|
||||
"pin-symbolic"
|
||||
"kt-restore-defaults-symbolic"
|
||||
"user-symbolic"
|
||||
"mark-location-symbolic"
|
||||
|
||||
${KIRIGAMI_ADDONS_ICONS}
|
||||
)
|
||||
ecm_add_android_apk(neochat-app ANDROID_DIR ${CMAKE_SOURCE_DIR}/android)
|
||||
else()
|
||||
target_link_libraries(neochat PUBLIC Qt::Widgets KF6::KIOWidgets)
|
||||
install(FILES neochat.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
|
||||
endif()
|
||||
|
||||
if(NOT ANDROID)
|
||||
set_target_properties(neochat-app PROPERTIES OUTPUT_NAME "neochat")
|
||||
endif()
|
||||
|
||||
if(TARGET KF6::DBusAddons AND NOT WIN32)
|
||||
target_link_libraries(neochat PUBLIC KF6::DBusAddons)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_KDBUSADDONS)
|
||||
endif()
|
||||
|
||||
if (TARGET KF6::KIOWidgets)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_KIO)
|
||||
endif()
|
||||
|
||||
if (TARGET KUnifiedPush)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_KUNIFIEDPUSH)
|
||||
target_link_libraries(neochat PUBLIC KUnifiedPush)
|
||||
|
||||
if (NOT ANDROID)
|
||||
configure_file(org.kde.neochat.service.in ${CMAKE_CURRENT_BINARY_DIR}/org.kde.neochat.service)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.neochat.service DESTINATION ${KDE_INSTALL_DBUSSERVICEDIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(TARGETS neochat-app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set_target_properties(neochat-app PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.neochat")
|
||||
set_target_properties(neochat-app PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "NeoChat")
|
||||
set_target_properties(neochat-app PROPERTIES MACOSX_BUNDLE_SHORT_VERSION_STRING ${RELEASE_SERVICE_VERSION})
|
||||
set_target_properties(neochat-app PROPERTIES MACOSX_BUNDLE_BUNDLE_VERSION ${RELEASE_SERVICE_VERSION})
|
||||
endif ()
|
||||
@@ -1,415 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
|
||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
#include <qt6keychain/keychain.h>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QTimer>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <Quotient/csapi/notifications.h>
|
||||
#include <Quotient/events/roommemberevent.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
#include <Quotient/settings.h>
|
||||
|
||||
#include "accountmanager.h"
|
||||
#include "enums/roomsortparameter.h"
|
||||
#include "mediasizehelper.h"
|
||||
#include "models/actionsmodel.h"
|
||||
#include "models/messagemodel.h"
|
||||
#include "models/pushrulemodel.h"
|
||||
#include "models/roomlistmodel.h"
|
||||
#include "models/roomtreemodel.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"
|
||||
#elif !defined(Q_OS_ANDROID)
|
||||
#include "trayicon_sni.h"
|
||||
#endif
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
|
||||
#ifndef Q_OS_ANDROID
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusMessage>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
#include <kunifiedpush/connector.h>
|
||||
#endif
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
static std::function<bool(const Quotient::RoomEvent *)> hiddenEventFilter = [](const RoomEvent *event) -> bool {
|
||||
if (event->isStateEvent() && !NeoChatConfig::showStateEvent()) {
|
||||
return true;
|
||||
}
|
||||
if (auto roomMemberEvent = eventCast<const RoomMemberEvent>(event)) {
|
||||
if ((roomMemberEvent->isJoin() || roomMemberEvent->isLeave()) && !NeoChatConfig::showLeaveJoinEvent()) {
|
||||
return true;
|
||||
} else if (roomMemberEvent->isRename() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::showRename()) {
|
||||
return true;
|
||||
} else if (roomMemberEvent->isAvatarUpdate() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::showAvatarUpdate()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
Controller::Controller(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
Connection::setRoomType<NeoChatRoom>();
|
||||
|
||||
Connection::setDirectChatEncryptionDefault(NeoChatConfig::preferUsingEncryption());
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::PreferUsingEncryptionChanged, this, [] {
|
||||
Connection::setDirectChatEncryptionDefault(NeoChatConfig::preferUsingEncryption());
|
||||
});
|
||||
|
||||
NeoChatConnection::setGlobalUrlPreviewDefault(NeoChatConfig::showLinkPreview());
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, [this] {
|
||||
NeoChatConnection::setGlobalUrlPreviewDefault(NeoChatConfig::showLinkPreview());
|
||||
Q_EMIT globalUrlPreviewDefaultChanged();
|
||||
});
|
||||
|
||||
NeoChatConnection::setKeywordPushRuleDefault(static_cast<PushRuleAction::Action>(NeoChatConfig::keywordPushRuleDefault()));
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::KeywordPushRuleDefaultChanged, this, [] {
|
||||
NeoChatConnection::setKeywordPushRuleDefault(static_cast<PushRuleAction::Action>(NeoChatConfig::keywordPushRuleDefault()));
|
||||
});
|
||||
|
||||
ActionsModel::setAllowQuickEdit(NeoChatConfig::allowQuickEdit());
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::AllowQuickEditChanged, this, []() {
|
||||
ActionsModel::setAllowQuickEdit(NeoChatConfig::allowQuickEdit());
|
||||
});
|
||||
|
||||
MessageModel::setHiddenFilter(hiddenEventFilter);
|
||||
RoomListModel::setHiddenFilter(hiddenEventFilter);
|
||||
RoomTreeModel::setHiddenFilter(hiddenEventFilter);
|
||||
NeoChatRoom::setHiddenFilter(hiddenEventFilter);
|
||||
|
||||
MediaSizeHelper::setMaxSize(NeoChatConfig::mediaMaxWidth(), NeoChatConfig::mediaMaxHeight());
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::MediaMaxWidthChanged, this, []() {
|
||||
MediaSizeHelper::setMaxSize(NeoChatConfig::mediaMaxWidth(), NeoChatConfig::mediaMaxHeight());
|
||||
});
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::MediaMaxHeightChanged, this, []() {
|
||||
MediaSizeHelper::setMaxSize(NeoChatConfig::mediaMaxWidth(), NeoChatConfig::mediaMaxHeight());
|
||||
});
|
||||
|
||||
RoomSortParameter::setSortOrder(static_cast<RoomSortOrder::Order>(NeoChatConfig::sortOrder()));
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::SortOrderChanged, this, []() {
|
||||
RoomSortParameter::setSortOrder(static_cast<RoomSortOrder::Order>(NeoChatConfig::sortOrder()));
|
||||
});
|
||||
|
||||
QList<RoomSortParameter::Parameter> configParamList;
|
||||
const auto intList = NeoChatConfig::customSortOrder();
|
||||
std::transform(intList.constBegin(), intList.constEnd(), std::back_inserter(configParamList), [](int param) {
|
||||
return static_cast<RoomSortParameter::Parameter>(param);
|
||||
});
|
||||
RoomSortParameter::setCustomSortOrder(configParamList);
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::CustomSortOrderChanged, this, []() {
|
||||
QList<RoomSortParameter::Parameter> configParamList;
|
||||
const auto intList = NeoChatConfig::customSortOrder();
|
||||
std::transform(intList.constBegin(), intList.constEnd(), std::back_inserter(configParamList), [](int param) {
|
||||
return static_cast<RoomSortParameter::Parameter>(param);
|
||||
});
|
||||
RoomSortParameter::setCustomSortOrder(configParamList);
|
||||
});
|
||||
|
||||
ProxyController::instance().setApplicationProxy();
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
setQuitOnLastWindowClosed();
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::SystemTrayChanged, this, &Controller::setQuitOnLastWindowClosed);
|
||||
#endif
|
||||
|
||||
QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [this] {
|
||||
delete m_trayIcon;
|
||||
NeoChatConfig::self()->save();
|
||||
});
|
||||
|
||||
#ifndef Q_OS_WINDOWS
|
||||
const auto unixExitHandler = [](int) -> void {
|
||||
QCoreApplication::quit();
|
||||
};
|
||||
|
||||
const int quitSignals[] = {SIGQUIT, SIGINT, SIGTERM, SIGHUP};
|
||||
|
||||
sigset_t blockingMask;
|
||||
sigemptyset(&blockingMask);
|
||||
for (const auto sig : quitSignals) {
|
||||
sigaddset(&blockingMask, sig);
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = unixExitHandler;
|
||||
sa.sa_mask = blockingMask;
|
||||
sa.sa_flags = 0;
|
||||
|
||||
for (auto sig : quitSignals) {
|
||||
sigaction(sig, &sa, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
auto connector = new KUnifiedPush::Connector(u"org.kde.neochat"_s);
|
||||
connect(connector, &KUnifiedPush::Connector::endpointChanged, this, [this](const QString &endpoint) {
|
||||
if (!m_accountManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_endpoint = endpoint;
|
||||
for (auto "ientConnection : m_accountManager->accounts()->accounts()) {
|
||||
auto connection = dynamic_cast<NeoChatConnection *>(quotientConnection);
|
||||
connection->setupPushNotifications(endpoint);
|
||||
}
|
||||
});
|
||||
|
||||
connector->registerClient(
|
||||
i18nc("The reason for using push notifications, as in: '[Push notifications are used for] Receiving notifications for new messages'",
|
||||
"Receiving notifications for new messages"));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
Controller &Controller::instance()
|
||||
{
|
||||
static Controller _instance;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void Controller::setAccountManager(AccountManager *manager)
|
||||
{
|
||||
if (manager == m_accountManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_accountManager) {
|
||||
m_accountManager->disconnect(this);
|
||||
}
|
||||
|
||||
m_accountManager = manager;
|
||||
|
||||
if (m_accountManager) {
|
||||
connect(m_accountManager, &AccountManager::errorOccured, this, &Controller::errorOccured);
|
||||
connect(m_accountManager, &AccountManager::accountsLoadingChanged, this, &Controller::accountsLoadingChanged);
|
||||
connect(m_accountManager, &AccountManager::connectionAdded, this, &Controller::initConnection);
|
||||
connect(m_accountManager, &AccountManager::connectionDropped, this, &Controller::teardownConnection);
|
||||
connect(m_accountManager, &AccountManager::activeConnectionChanged, this, &Controller::initActiveConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::initConnection(NeoChatConnection *connection)
|
||||
{
|
||||
if (!connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
connect(
|
||||
connection,
|
||||
&NeoChatConnection::syncDone,
|
||||
this,
|
||||
[this, connection] {
|
||||
if (!m_endpoint.isEmpty()) {
|
||||
connection->setupPushNotifications(m_endpoint);
|
||||
}
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
connect(connection, &NeoChatConnection::syncDone, this, [this, connection]() {
|
||||
m_notificationsManager.handleNotifications(connection);
|
||||
});
|
||||
connect(this, &Controller::globalUrlPreviewDefaultChanged, connection, &NeoChatConnection::globalUrlPreviewEnabledChanged);
|
||||
connect(connection, &NeoChatConnection::roomAboutToBeLeft, &RoomManager::instance(), &RoomManager::roomLeft);
|
||||
Q_EMIT connectionAdded(connection);
|
||||
}
|
||||
|
||||
void Controller::teardownConnection(NeoChatConnection *connection)
|
||||
{
|
||||
if (!connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
connection->disconnect(this);
|
||||
Q_EMIT connectionDropped(connection);
|
||||
}
|
||||
|
||||
void Controller::initActiveConnection(NeoChatConnection *oldConnection, NeoChatConnection *newConnection)
|
||||
{
|
||||
if (oldConnection) {
|
||||
oldConnection->disconnect(this);
|
||||
}
|
||||
|
||||
if (newConnection) {
|
||||
connect(newConnection, &NeoChatConnection::errorOccured, this, &Controller::errorOccured);
|
||||
connect(newConnection, &NeoChatConnection::badgeNotificationCountChanged, this, &Controller::updateBadgeNotificationCount);
|
||||
newConnection->refreshBadgeNotificationCount();
|
||||
}
|
||||
Q_EMIT activeConnectionChanged(newConnection);
|
||||
}
|
||||
|
||||
bool Controller::supportSystemTray() const
|
||||
{
|
||||
#ifdef Q_OS_ANDROID
|
||||
return false;
|
||||
#else
|
||||
auto de = QString::fromLatin1(qgetenv("XDG_CURRENT_DESKTOP"));
|
||||
return de != u"GNOME"_s && de != u"Pantheon"_s;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Controller::setQuitOnLastWindowClosed()
|
||||
{
|
||||
#ifndef Q_OS_ANDROID
|
||||
if (supportSystemTray() && NeoChatConfig::self()->systemTray()) {
|
||||
m_trayIcon = new TrayIcon(this);
|
||||
m_trayIcon->show();
|
||||
} else {
|
||||
if (m_trayIcon) {
|
||||
delete m_trayIcon;
|
||||
m_trayIcon = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
NeoChatConnection *Controller::activeConnection() const
|
||||
{
|
||||
if (!m_accountManager) {
|
||||
return nullptr;
|
||||
}
|
||||
return m_accountManager->activeConnection();
|
||||
}
|
||||
|
||||
void Controller::setActiveConnection(NeoChatConnection *connection)
|
||||
{
|
||||
if (!m_accountManager) {
|
||||
return;
|
||||
}
|
||||
m_accountManager->setActiveConnection(connection);
|
||||
}
|
||||
|
||||
QStringList Controller::accountsLoading() const
|
||||
{
|
||||
if (!m_accountManager) {
|
||||
return {};
|
||||
}
|
||||
return m_accountManager->accountsLoading();
|
||||
}
|
||||
|
||||
void Controller::listenForNotifications()
|
||||
{
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
auto connector = new KUnifiedPush::Connector(u"org.kde.neochat"_s);
|
||||
|
||||
auto timer = new QTimer();
|
||||
connect(timer, &QTimer::timeout, qGuiApp, &QGuiApplication::quit);
|
||||
|
||||
connect(connector, &KUnifiedPush::Connector::messageReceived, [timer](const QByteArray &data) {
|
||||
instance().m_notificationsManager.postPushNotification(data);
|
||||
timer->stop();
|
||||
});
|
||||
|
||||
// Wait five seconds to see if we received any messages or this happened to be an erroneous activation.
|
||||
// Otherwise, messageReceived is never activated, and this daemon could stick around forever.
|
||||
timer->start(5000);
|
||||
|
||||
connector->registerClient(i18n("Receiving push notifications"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Controller::clearInvitationNotification(const QString &roomId)
|
||||
{
|
||||
m_notificationsManager.clearInvitationNotification(roomId);
|
||||
}
|
||||
|
||||
void Controller::updateBadgeNotificationCount(int count)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
|
||||
#ifndef Q_OS_ANDROID
|
||||
// copied from Telegram desktop
|
||||
const auto launcherUrl = "application://org.kde.neochat.desktop"_L1;
|
||||
// Gnome requires that count is a 64bit integer
|
||||
const qint64 counterSlice = std::min(count, 9999);
|
||||
QVariantMap dbusUnityProperties;
|
||||
|
||||
if (counterSlice > 0) {
|
||||
dbusUnityProperties["count"_L1] = counterSlice;
|
||||
dbusUnityProperties["count-visible"_L1] = true;
|
||||
} else {
|
||||
dbusUnityProperties["count-visible"_L1] = false;
|
||||
}
|
||||
|
||||
auto signal = QDBusMessage::createSignal("/com/canonical/unity/launcherentry/neochat"_L1, "com.canonical.Unity.LauncherEntry"_L1, "Update"_L1);
|
||||
|
||||
signal.setArguments({launcherUrl, dbusUnityProperties});
|
||||
|
||||
QDBusConnection::sessionBus().send(signal);
|
||||
#endif // Q_OS_ANDROID
|
||||
#else
|
||||
qGuiApp->setBadgeNumber(count);
|
||||
#endif // QT_VERSION_CHECK(6, 6, 0)
|
||||
}
|
||||
|
||||
bool Controller::isFlatpak() const
|
||||
{
|
||||
#ifdef NEOCHAT_FLATPAK
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
AccountRegistry *Controller::accounts()
|
||||
{
|
||||
return m_accountManager->accounts();
|
||||
}
|
||||
|
||||
QString Controller::loadFileContent(const QString &path) const
|
||||
{
|
||||
QUrl url(path);
|
||||
QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString());
|
||||
file.open(QFile::ReadOnly);
|
||||
return QString::fromLatin1(file.readAll());
|
||||
}
|
||||
|
||||
void Controller::removeConnection(const QString &userId)
|
||||
{
|
||||
m_accountManager->dropConnection(userId);
|
||||
}
|
||||
|
||||
void Controller::revertToDefaultConfig()
|
||||
{
|
||||
const auto config = NeoChatConfig::self();
|
||||
config->setDefaults();
|
||||
config->save();
|
||||
}
|
||||
|
||||
bool Controller::isImageShown(const QString &eventId)
|
||||
{
|
||||
return m_shownImages.contains(eventId);
|
||||
}
|
||||
|
||||
void Controller::markImageShown(const QString &eventId)
|
||||
{
|
||||
m_shownImages.append(eventId);
|
||||
}
|
||||
|
||||
void Controller::markImageHidden(const QString &eventId)
|
||||
{
|
||||
m_shownImages.removeAll(eventId);
|
||||
}
|
||||
|
||||
#include "moc_controller.cpp"
|
||||
@@ -1,11 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "mediamanager.h"
|
||||
|
||||
void MediaManager::startPlayback()
|
||||
{
|
||||
Q_EMIT playbackStarted();
|
||||
}
|
||||
|
||||
#include "moc_mediamanager.cpp"
|
||||
@@ -1,10 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Item {
|
||||
required property NeoChatConnection connection
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.components as KirigamiComponents
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property NeoChatRoom currentRoom
|
||||
readonly property var invitingMember: currentRoom.qmlSafeMember(currentRoom.invitingUserId)
|
||||
readonly property string inviteTimestamp: root.currentRoom.inviteTimestamp.toLocaleString(Qt.locale(), Locale.ShortFormat)
|
||||
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
KirigamiComponents.Avatar {
|
||||
id: avatar
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
name: root.invitingMember.displayName
|
||||
source: NeoChatConfig.hideImages ? undefined : root.invitingMember.avatarUrl
|
||||
color: root.invitingMember.color
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: !root.currentRoom.isDirectChat()
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
QQC2.Label {
|
||||
text: i18nc("@info:label 'Username' has invited you to this room at 'timestamp'.", "%1 has invited you to this room at %2.", root.invitingMember.displayName, root.inviteTimestamp)
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
text: root.currentRoom.displayName
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: root.currentRoom.isDirectChat()
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Kirigami.Heading {
|
||||
text: root.currentRoom.displayName
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
text: i18nc("@info:label This user invited you to chat at 'timestamp'", "This user invited you to chat at %1.", root.inviteTimestamp)
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormCard {
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing * 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
id: acceptInviteDelegate
|
||||
|
||||
icon.name: "dialog-ok-symbolic"
|
||||
text: i18nc("@action:button Accept this invite", "Accept Invite")
|
||||
focus: true
|
||||
|
||||
onClicked: root.currentRoom.acceptInvitation()
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
above: acceptInviteDelegate
|
||||
below: rejectInviteDelegate
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
id: rejectInviteDelegate
|
||||
|
||||
icon.name: "dialog-cancel-symbolic"
|
||||
text: i18nc("@action:button Reject this invite", "Reject Invite")
|
||||
|
||||
onClicked: root.currentRoom.forget()
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormCard {
|
||||
id: blockUserCard
|
||||
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
Layout.fillWidth: true
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
icon.name: "list-remove-symbolic"
|
||||
text: i18nc("@action:button Block the user", "Block %1", root.invitingMember.displayName)
|
||||
|
||||
onClicked: {
|
||||
root.currentRoom.forget()
|
||||
root.currentRoom.connection.addToIgnoredUsers(root.currentRoom.invitingUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: root.currentRoom.connection.canCheckMutualRooms
|
||||
spacing: 0
|
||||
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing * 2
|
||||
Layout.fillWidth: true
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Kirigami.Icon {
|
||||
source: "help-hint-symbolic"
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.small
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.small
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
text: i18nc("@info:label", "You can reject invitations from unknown users under Security settings.")
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
// + 5 to prevent it from wrapping unnecessarily
|
||||
Layout.maximumWidth: implicitWidth + 5
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||
import org.kde.kirigamiaddons.delegates as Delegates
|
||||
|
||||
import Quotient
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Kirigami.Dialog {
|
||||
id: root
|
||||
|
||||
required property NeoChatRoom room
|
||||
|
||||
standardButtons: Kirigami.Dialog.Cancel
|
||||
|
||||
customFooterActions: [
|
||||
Kirigami.Action {
|
||||
enabled: optionModel.allValuesSet && questionTextField.text.length > 0
|
||||
text: i18nc("@action:button", "Send")
|
||||
icon.name: "document-send"
|
||||
onTriggered: {
|
||||
root.room.postPoll(pollTypeCombo.currentValue, questionTextField.text, optionModel.values())
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
|
||||
title: i18nc("@title: create new poll in the room", "Create Poll")
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
FormCard.FormComboBoxDelegate {
|
||||
id: pollTypeCombo
|
||||
|
||||
text: i18n("Poll type:")
|
||||
currentIndex: 0
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
model: [
|
||||
{ value: PollKind.Disclosed, text: i18n("Open poll") },
|
||||
{ value: PollKind.Undisclosed, text: i18n("Closed poll") }
|
||||
]
|
||||
}
|
||||
FormCard.FormTextDelegate {
|
||||
verticalPadding: 0
|
||||
text: pollTypeCombo.currentValue == 0 ? i18n("Voters can see the result as soon as they have voted") : i18n("Results are revealed only after the poll has closed")
|
||||
}
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: questionTextField
|
||||
label: i18n("Question:")
|
||||
}
|
||||
Repeater {
|
||||
id: optionRepeater
|
||||
|
||||
model: ListModel {
|
||||
id: optionModel
|
||||
|
||||
readonly property bool allValuesSet: {
|
||||
for( var i = 0; i < optionModel.rowCount(); i++ ) {
|
||||
if (optionModel.get(i).optionText.length <= 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ListElement {
|
||||
optionText: ""
|
||||
}
|
||||
ListElement {
|
||||
optionText: ""
|
||||
}
|
||||
|
||||
function values() {
|
||||
let textValues = []
|
||||
for( var i = 0; i < optionModel.rowCount(); i++ ) {
|
||||
textValues.push(optionModel.get(i).optionText);
|
||||
}
|
||||
return textValues;
|
||||
}
|
||||
}
|
||||
delegate: FormCard.AbstractFormDelegate {
|
||||
id: optionDelegate
|
||||
|
||||
required property int index
|
||||
required property string optionText
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
QQC2.Label {
|
||||
id: optionLabel
|
||||
|
||||
Layout.fillWidth: true
|
||||
text: i18nc("As in first answer option to the poll", "Option %1:", optionDelegate.index + 1)
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.Wrap
|
||||
Accessible.ignored: true
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
QQC2.TextField {
|
||||
id: textField
|
||||
Layout.fillWidth: true
|
||||
Accessible.name: optionLabel.text
|
||||
onTextChanged: {
|
||||
optionModel.set(optionDelegate.index, {optionText: text})
|
||||
optionModel.allValuesSetChanged()
|
||||
}
|
||||
placeholderText: i18n("Enter option")
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
action: Kirigami.Action {
|
||||
id: removeOptionAction
|
||||
text: i18nc("@action:button", "Remove option")
|
||||
icon.name: "edit-delete-remove"
|
||||
onTriggered: optionModel.remove(optionDelegate.index)
|
||||
}
|
||||
QQC2.ToolTip {
|
||||
text: removeOptionAction.text
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: null
|
||||
}
|
||||
}
|
||||
Delegates.RoundedItemDelegate {
|
||||
Layout.fillWidth: true
|
||||
|
||||
horizontalPadding: Kirigami.Units.largeSpacing * 2
|
||||
leftInset: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
|
||||
rightInset: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
|
||||
|
||||
highlighted: true
|
||||
|
||||
icon.name: "list-add"
|
||||
text: i18nc("@action:button", "Add option")
|
||||
|
||||
onClicked: optionModel.append({optionText: ""})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.components as KirigamiComponents
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
import org.kde.neochat.devtools
|
||||
|
||||
KirigamiComponents.ConvergentContextMenu {
|
||||
id: root
|
||||
|
||||
required property Kirigami.ApplicationWindow window
|
||||
required property var author
|
||||
|
||||
headerContentItem: RowLayout {
|
||||
id: detailRow
|
||||
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
KirigamiComponents.Avatar {
|
||||
id: avatar
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
|
||||
|
||||
name: root.author.displayName
|
||||
source: root.author.avatarUrl
|
||||
color: root.author.color
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: 0
|
||||
|
||||
Kirigami.Heading {
|
||||
level: 1
|
||||
Layout.fillWidth: true
|
||||
font.bold: true
|
||||
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
text: root.author.displayName
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
id: idLabel
|
||||
textFormat: TextEdit.PlainText
|
||||
text: root.author.id
|
||||
elide: Qt.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
text: i18nc("@action:button", "Open Profile")
|
||||
icon.name: "im-user-symbolic"
|
||||
onTriggered: RoomManager.resolveResource(root.author.uri)
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
text: i18nc("@action:button", "Mention")
|
||||
icon.name: "username-copy-symbolic"
|
||||
onTriggered: {
|
||||
RoomManager.currentRoom.mainCache.mentionAdded(root.author.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 Ritchie Frodomar <ritchie@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "texttospeechhelper.h"
|
||||
|
||||
void TextToSpeechHelper::speak(const QString &textToSpeak)
|
||||
{
|
||||
if (!m_speech) {
|
||||
m_speech = new QTextToSpeech();
|
||||
}
|
||||
|
||||
m_speech->say(textToSpeak);
|
||||
}
|
||||
|
||||
#include "moc_texttospeechhelper.cpp"
|
||||
@@ -1,21 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 Ritchie Frodomar <ritchie@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QtTextToSpeech>
|
||||
|
||||
class TextToSpeechHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
public:
|
||||
Q_INVOKABLE void speak(const QString &textToSpeak);
|
||||
|
||||
private:
|
||||
QTextToSpeech *m_speech = nullptr;
|
||||
};
|
||||
0
src/calls/call.cpp
Normal file
0
src/calls/call.cpp
Normal file
74
src/calls/call.h
Normal file
74
src/calls/call.h
Normal file
@@ -0,0 +1,74 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include "neochatroom.h"
|
||||
|
||||
class Participant : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
void setVolume(float volume);
|
||||
void muteLocally();
|
||||
void unmuteLocally();
|
||||
|
||||
void ring(); // See MSC4075
|
||||
|
||||
// TODO: if these are possible; check livekit api
|
||||
void muteGlobally();
|
||||
void forceDisableCamera();
|
||||
void forceDisableScreenShare();
|
||||
|
||||
void setPermissions();
|
||||
void kick();
|
||||
void ban();
|
||||
|
||||
Q_SIGNALS:
|
||||
void muted();
|
||||
void unmuted();
|
||||
|
||||
void cameraEnabled();
|
||||
void cameraDisabled();
|
||||
|
||||
void screenShareEnabled();
|
||||
void screenShareDisabled();
|
||||
};
|
||||
|
||||
class Call : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
Q_PROPERTY(bool cameraEnabled READ cameraEnabled WRITE setCameraEnabled NOTIFY cameraEnabledChanged)
|
||||
Q_PROPERTY(bool microphoneMuted READ microphoneMuted WRITE setMicrophoneMuted NOTIFY microphoneMutedChanged)
|
||||
Q_PROPERTY(bool screenshareEnabled READ screenshareEnabled NOTIFY screenshareEnabledChanged)
|
||||
|
||||
Q_PROPERTY(NeoChatRoom *room READ room CONSTANT)
|
||||
|
||||
public:
|
||||
explicit Call(NeoChatRoom *room, QObject *parent = nullptr);
|
||||
|
||||
Q_SIGNALS:
|
||||
void participantJoined(const Participant &participant);
|
||||
void participantLeft(const Participant &participant);
|
||||
|
||||
private:
|
||||
QList<Participant *> m_participants;
|
||||
};
|
||||
|
||||
class CallParticipantsModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Roles {
|
||||
NameRoleRole,
|
||||
HasCameraRole,
|
||||
HasScreenShareRole,
|
||||
IsMutedRole,
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
};
|
||||
447
src/calls/callcontroller.cpp
Normal file
447
src/calls/callcontroller.cpp
Normal file
@@ -0,0 +1,447 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "callcontroller.h"
|
||||
|
||||
#include <QAudioSink>
|
||||
#include <QMediaDevices>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QProtobufSerializer>
|
||||
#include <QVideoFrame>
|
||||
#include <QVideoFrameFormat>
|
||||
#include <QVideoSink>
|
||||
|
||||
#include <livekit_ffi.h>
|
||||
|
||||
#include <Quotient/csapi/openid.h>
|
||||
|
||||
#include "audio_frame.qpb.h"
|
||||
#include "ffi.qpb.h"
|
||||
|
||||
#include "livekitlogmodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "track.qpb.h"
|
||||
#include "video_frame.qpb.h"
|
||||
|
||||
using namespace livekit::proto;
|
||||
using namespace Quotient;
|
||||
|
||||
extern "C" {
|
||||
void livekit_ffi_initialize(void(ffiCallbackFn(const uint8_t *, size_t)), bool capture_logs, const char *, const char *);
|
||||
}
|
||||
|
||||
void callback(const uint8_t *data, size_t length)
|
||||
{
|
||||
auto byteArrayData = QByteArray::fromRawData((const char *)data, length);
|
||||
QProtobufSerializer serializer;
|
||||
FfiEvent event;
|
||||
event.deserialize(&serializer, byteArrayData);
|
||||
CallController::instance().handleEvent(std::move(event));
|
||||
}
|
||||
|
||||
CallController::CallController()
|
||||
: QObject()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void CallController::init()
|
||||
{
|
||||
// qRegisterProtobufTypes();
|
||||
livekit_ffi_initialize(callback, true, "test", "1.0");
|
||||
}
|
||||
|
||||
static void handleLog(const LogRecordRepeated &&logs)
|
||||
{
|
||||
for (const auto &log : logs) {
|
||||
if (log.level() <= LogLevelGadget::LogLevel::LOG_WARN) {
|
||||
qWarning() << log.message();
|
||||
}
|
||||
}
|
||||
LivekitLogModel::instance().addMessages(logs);
|
||||
}
|
||||
|
||||
void CallController::handleConnect(ConnectCallback &&callback)
|
||||
{
|
||||
qWarning() << "Connecting to" << callback.result().room().info().name() << "with id" << callback.asyncId();
|
||||
if (!m_connectingRooms.contains(callback.asyncId()) || !m_connectingRooms[callback.asyncId()]
|
||||
|| m_connectingRooms[callback.asyncId()]->id() != callback.result().room().info().name()) {
|
||||
qWarning() << "Connecting to unexpected room";
|
||||
return;
|
||||
}
|
||||
m_connectingRooms.remove(callback.asyncId());
|
||||
m_rooms[callback.asyncId()] = callback.result().room();
|
||||
localParticipant = callback.result().localParticipant().handle().id_proto();
|
||||
Q_EMIT connected();
|
||||
}
|
||||
|
||||
void CallController::handleDispose(DisposeCallback &&callback)
|
||||
{
|
||||
qWarning() << "Disposing" << callback.asyncId();
|
||||
if (m_rooms.contains(callback.asyncId())) {
|
||||
qWarning() << " room" << m_rooms[callback.asyncId()].info().name();
|
||||
m_rooms.erase(callback.asyncId());
|
||||
} else {
|
||||
qWarning() << " unknown object";
|
||||
}
|
||||
}
|
||||
|
||||
void CallController::handleRoomEvent(livekit::proto::RoomEvent &&event)
|
||||
{
|
||||
if (event.hasParticipantConnected()) {
|
||||
qWarning() << "Participant connected" << event.participantConnected().info().info().identity();
|
||||
} else if (event.hasParticipantDisconnected()) {
|
||||
qWarning() << "Participant connected" << event.participantDisconnected().participantIdentity();
|
||||
} else if (event.hasLocalTrackPublished()) {
|
||||
qWarning() << "Local track published";
|
||||
m_localVideoTrackSid = event.localTrackPublished().trackSid();
|
||||
} else if (event.hasLocalTrackUnpublished()) {
|
||||
qWarning() << "Local track unpublished";
|
||||
} else if (event.hasTrackPublished()) {
|
||||
qWarning() << "Track published";
|
||||
} else if (event.hasTrackUnpublished()) {
|
||||
qWarning() << "Track unpublished";
|
||||
} else if (event.hasTrackSubscribed()) {
|
||||
qWarning() << "Track subscribed";
|
||||
|
||||
auto track = event.trackSubscribed().track();
|
||||
if (track.info().kind() == TrackKindGadget::TrackKind::KIND_AUDIO) {
|
||||
NewAudioStreamRequest audioStreamRequest;
|
||||
audioStreamRequest.setTrackHandle(track.handle().id_proto());
|
||||
FfiRequest request;
|
||||
request.setNewAudioStream(audioStreamRequest);
|
||||
QProtobufSerializer serializer;
|
||||
auto data = request.serialize(&serializer);
|
||||
const uint8_t *ret_data;
|
||||
size_t size;
|
||||
livekit_ffi_request((const uint8_t *)data.data(), data.length(), &ret_data, &size);
|
||||
FfiResponse newResponse;
|
||||
newResponse.deserialize(&serializer, QByteArray::fromRawData((const char *)ret_data, size));
|
||||
} else if (track.info().kind() == TrackKindGadget::TrackKind::KIND_VIDEO) {
|
||||
NewVideoStreamRequest videoStreamRequest;
|
||||
videoStreamRequest.setTrackHandle((track.handle().id_proto()));
|
||||
FfiRequest request;
|
||||
request.setNewVideoStream(videoStreamRequest);
|
||||
QProtobufSerializer serializer;
|
||||
auto data = request.serialize(&serializer);
|
||||
const uint8_t *ret_data;
|
||||
size_t size;
|
||||
livekit_ffi_request((const uint8_t *)data.data(), data.length(), &ret_data, &size);
|
||||
FfiResponse newResponse;
|
||||
newResponse.deserialize(&serializer, QByteArray::fromRawData((const char *)ret_data, size));
|
||||
}
|
||||
} else if (event.hasTrackUnsubscribed()) {
|
||||
qWarning() << "Track unsubscribed";
|
||||
} else if (event.hasTrackSubscriptionFailed()) {
|
||||
qWarning() << "Track subscription failed";
|
||||
} else if (event.hasTrackMuted()) {
|
||||
qWarning() << "Track muted";
|
||||
} else if (event.hasTrackUnmuted()) {
|
||||
qWarning() << "Track unmuted";
|
||||
} else if (event.hasActiveSpeakersChanged()) {
|
||||
qWarning() << "Active speakers changed";
|
||||
} else if (event.hasRoomMetadataChanged()) {
|
||||
qWarning() << "room metadata changed";
|
||||
} else if (event.hasParticipantMetadataChanged()) {
|
||||
qWarning() << "participant metadata changed";
|
||||
} else if (event.hasParticipantNameChanged()) {
|
||||
qWarning() << "participant name changed";
|
||||
} else if (event.hasConnectionQualityChanged()) {
|
||||
qWarning() << "connection quality changed to" << event.connectionQualityChanged().quality();
|
||||
} else if (event.hasDataPacketReceived()) {
|
||||
qWarning() << "data received";
|
||||
} else if (event.hasConnectionStateChanged()) {
|
||||
qWarning() << "connection state changed";
|
||||
} else if (event.hasDisconnected()) {
|
||||
qWarning() << "disconnected";
|
||||
} else if (event.hasReconnecting()) {
|
||||
qWarning() << "reconnecting";
|
||||
} else if (event.hasReconnected()) {
|
||||
qWarning() << "Reconnected";
|
||||
} else if (event.hasE2eeStateChanged()) {
|
||||
qWarning() << "e2eeStateChanged";
|
||||
} else if (event.hasEos()) {
|
||||
qWarning() << "eos";
|
||||
} else {
|
||||
qWarning() << "Unknown room event";
|
||||
}
|
||||
}
|
||||
|
||||
void saveByteArray(const QByteArray &data, const QString &name)
|
||||
{
|
||||
QFile file(u"/home/tobias/"_s + name);
|
||||
file.open(QFile::WriteOnly);
|
||||
file.write(data);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void CallController::handleEvent(FfiEvent &&event)
|
||||
{
|
||||
if (event.hasLogs()) {
|
||||
handleLog(std::move(event.logs().records()));
|
||||
} else if (event.hasRoomEvent()) {
|
||||
handleRoomEvent(std::move(event.roomEvent()));
|
||||
} else if (event.hasTrackEvent()) {
|
||||
qWarning() << "track event";
|
||||
} else if (event.hasVideoStreamEvent()) {
|
||||
qWarning() << "video stream event";
|
||||
auto video = event.videoStreamEvent();
|
||||
auto info = video.frameReceived().buffer().info();
|
||||
QByteArray data((const char *)info.dataPtr(), info.width() * info.height() * 1.5);
|
||||
auto frame = QVideoFrame(QVideoFrameFormat(QSize(info.width(), info.height()), QVideoFrameFormat::Format_YUV420P));
|
||||
frame.map(QVideoFrame::WriteOnly);
|
||||
memcpy(frame.bits(0), data.constData(), data.size() / 3 * 2);
|
||||
memcpy(frame.bits(1), data.constData() + data.size() / 3 * 2, data.size() / 6);
|
||||
memcpy(frame.bits(2), data.constData() + data.size() / 3 * 2 + data.size() / 6, data.size() / 6);
|
||||
qWarning() << frame.size() << data.toBase64();
|
||||
frame.unmap();
|
||||
m_sink->setVideoFrame(frame);
|
||||
delete (char *)info.dataPtr();
|
||||
} else if (event.hasAudioStreamEvent()) {
|
||||
return; //TODO remove
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
QAudioFormat format;
|
||||
format.setSampleRate(48000);
|
||||
format.setChannelCount(2);
|
||||
format.setSampleFormat(QAudioFormat::Int16);
|
||||
QAudioDevice info(QMediaDevices::defaultAudioOutput());
|
||||
if (!info.isFormatSupported(format)) {
|
||||
qWarning() << "Audio format not supported";
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
sink = new QAudioSink(format);
|
||||
audioData = sink->start();
|
||||
QProtobufSerializer serializer;
|
||||
NewAudioResamplerRequest narr;
|
||||
FfiRequest request;
|
||||
request.setNewAudioResampler(narr);
|
||||
auto data = request.serialize(&serializer);
|
||||
const uint8_t *ret_data;
|
||||
size_t size;
|
||||
livekit_ffi_request((const uint8_t *)data.data(), data.length(), &ret_data, &size);
|
||||
FfiResponse newResponse;
|
||||
newResponse.deserialize(&serializer, QByteArray::fromRawData((const char *)ret_data, size));
|
||||
resampler = newResponse.newAudioResampler().resampler().handle().id_proto();
|
||||
}
|
||||
|
||||
if (event.audioStreamEvent().hasFrameReceived()) {
|
||||
FfiRequest request;
|
||||
RemixAndResampleRequest rarr;
|
||||
rarr.setBuffer(event.audioStreamEvent().frameReceived().frame().info());
|
||||
rarr.setNumChannels(2);
|
||||
rarr.setSampleRate(48000);
|
||||
rarr.setResamplerHandle(resampler);
|
||||
request = FfiRequest();
|
||||
request.setRemixAndResample(rarr);
|
||||
static QProtobufSerializer serializer;
|
||||
auto data = request.serialize(&serializer);
|
||||
const uint8_t *ret_data;
|
||||
size_t size;
|
||||
livekit_ffi_request((const uint8_t *)data.data(), data.length(), &ret_data, &size);
|
||||
FfiResponse response;
|
||||
response.deserialize(&serializer, QByteArray::fromRawData((const char *)ret_data, size));
|
||||
Q_ASSERT(response.hasRemixAndResample());
|
||||
|
||||
auto info = response.remixAndResample().buffer().info();
|
||||
auto bytes = info.numChannels() * info.samplesPerChannel() * 2;
|
||||
data = QByteArray::fromRawData((const char *)info.dataPtr(), bytes);
|
||||
audioData->write(data);
|
||||
}
|
||||
} else if (event.hasConnect()) {
|
||||
handleConnect(std::move(event.connect()));
|
||||
} else if (event.hasDisconnect()) {
|
||||
qWarning() << "disconnect";
|
||||
} else if (event.hasDispose()) {
|
||||
handleDispose(std::move(event.dispose()));
|
||||
} else if (event.hasPublishTrack()) {
|
||||
qWarning() << "publish track";
|
||||
} else if (event.hasUnpublishTrack()) {
|
||||
qWarning() << "unpublish track";
|
||||
} else if (event.hasPublishData()) {
|
||||
qWarning() << "publish data";
|
||||
} else if (event.hasCaptureAudioFrame()) {
|
||||
qWarning() << "audio frame";
|
||||
} else if (event.hasGetStats()) {
|
||||
qWarning() << "get stats";
|
||||
} else if (event.hasGetSessionStats()) {
|
||||
qWarning() << "get session stats";
|
||||
} else if (event.hasPanic()) {
|
||||
qWarning() << "panic";
|
||||
} else {
|
||||
qWarning() << event.messageField();
|
||||
}
|
||||
}
|
||||
|
||||
void CallController::handleCallMemberEvent(const Quotient::CallMemberEvent *event, NeoChatRoom *room)
|
||||
{
|
||||
qWarning() << event->fullJson();
|
||||
Q_EMIT callStarted();
|
||||
const auto connection = room->connection();
|
||||
auto job = connection->callApi<RequestOpenIdTokenJob>(connection->userId());
|
||||
connect(job, &BaseJob::finished, this, [this, room, job, connection, event]() {
|
||||
auto nam = new QNetworkAccessManager;
|
||||
auto json = QJsonDocument(QJsonObject{
|
||||
{"room"_L1, room->id()},
|
||||
{"openid_token"_L1,
|
||||
QJsonObject{{"access_token"_L1, job->tokenData().accessToken},
|
||||
{"token_type"_L1, job->tokenData().tokenType},
|
||||
{"matrix_server_name"_L1, job->tokenData().matrixServerName}}},
|
||||
{"device_id"_L1, connection->deviceId()},
|
||||
})
|
||||
.toJson();
|
||||
// This is an old event!
|
||||
if (!event->contentJson().contains("foci_preferred"_L1)) {
|
||||
return;
|
||||
}
|
||||
QNetworkRequest request(QUrl((event->contentJson()["foci_preferred"_L1].toArray()[0]["livekit_service_url"_L1].toString() + "/sfu/get"_L1)));
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"_L1);
|
||||
auto reply = nam->post(request, json);
|
||||
connect(reply, &QNetworkReply::finished, this, [reply, this, room]() {
|
||||
auto json = QJsonDocument::fromJson(reply->readAll()).object();
|
||||
|
||||
FfiRequest message;
|
||||
ConnectRequest connectRequest;
|
||||
connectRequest.setUrl(json["url"_L1].toString());
|
||||
connectRequest.setToken(json["jwt"_L1].toString());
|
||||
message.setConnect(connectRequest);
|
||||
QProtobufSerializer serializer;
|
||||
auto data = message.serialize(&serializer);
|
||||
size_t size;
|
||||
const uint8_t *ret_data;
|
||||
livekit_ffi_request((const uint8_t *)data.data(), data.length(), &ret_data, &size);
|
||||
FfiResponse connectResponse;
|
||||
connectResponse.deserialize(&serializer, QByteArray::fromRawData((const char *)ret_data, size));
|
||||
if (!connectResponse.hasConnect()) {
|
||||
qWarning() << "connectResponse has unexpected content" << connectResponse.messageField();
|
||||
return;
|
||||
}
|
||||
m_connectingRooms[connectResponse.connect().asyncId()] = room;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
FfiResponse request(FfiRequest &&request)
|
||||
{
|
||||
static QProtobufSerializer serializer;
|
||||
auto data = request.serialize(&serializer);
|
||||
size_t responseLength;
|
||||
const char *responseData;
|
||||
livekit_ffi_request((const uint8_t *)data.constData(), data.size(), (const uint8_t **)&responseData, &responseLength);
|
||||
auto response = QByteArray::fromRawData(responseData, responseLength);
|
||||
FfiResponse ffiResponse;
|
||||
ffiResponse.deserialize(&serializer, response);
|
||||
return ffiResponse;
|
||||
}
|
||||
|
||||
void CallController::setCameraVideoSink(QVideoSink *videoSink)
|
||||
{
|
||||
m_cameraVideoSink = videoSink;
|
||||
connect(videoSink, &QVideoSink::videoFrameChanged, this, [videoSink, this]() {
|
||||
static bool initialized = false;
|
||||
if (localParticipant == 100000) {
|
||||
return; // TODO make less shitty
|
||||
}
|
||||
static QtProtobuf::uint64 handle;
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
|
||||
NewVideoSourceRequest newVideoSourceRequest;
|
||||
VideoSourceResolution resolution;
|
||||
resolution.setHeight(videoSink->videoSize().height());
|
||||
resolution.setWidth(videoSink->videoSize().width());
|
||||
newVideoSourceRequest.setResolution(resolution);
|
||||
newVideoSourceRequest.setType(VideoSourceTypeGadget::VideoSourceType::VIDEO_SOURCE_NATIVE);
|
||||
FfiRequest ffiRequest;
|
||||
ffiRequest.setNewVideoSource(newVideoSourceRequest);
|
||||
auto response = request(std::move(ffiRequest));
|
||||
handle = response.newVideoSource().source().handle().id_proto();
|
||||
m_localVideoTrackHandle = handle;
|
||||
|
||||
CreateVideoTrackRequest createVideoTrackRequest;
|
||||
createVideoTrackRequest.setName("Camera"_L1);
|
||||
createVideoTrackRequest.setSourceHandle(handle);
|
||||
|
||||
FfiRequest request;
|
||||
request.setCreateVideoTrack(createVideoTrackRequest);
|
||||
|
||||
auto createResponse = ::request(std::move(request));
|
||||
m_localVideoTrackId = createResponse.createVideoTrack().track().handle().id_proto();
|
||||
publishTrack(m_localVideoTrackId);
|
||||
}
|
||||
|
||||
auto image = videoSink->videoFrame().toImage();
|
||||
image.convertTo(QImage::Format_RGB888);
|
||||
CaptureVideoFrameRequest request;
|
||||
VideoBufferInfo buffer;
|
||||
buffer.setType(VideoBufferTypeGadget::VideoBufferType::RGB24);
|
||||
buffer.setWidth(image.width());
|
||||
buffer.setHeight(image.height());
|
||||
buffer.setDataPtr((QtProtobuf::uint64)image.bits());
|
||||
buffer.setStride(image.bytesPerLine());
|
||||
QList<VideoBufferInfo_QtProtobufNested::ComponentInfo> components;
|
||||
VideoBufferInfo_QtProtobufNested::ComponentInfo componentInfo;
|
||||
componentInfo.setStride(image.bytesPerLine());
|
||||
componentInfo.setDataPtr((QtProtobuf::uint64)image.bits());
|
||||
componentInfo.setSize(image.sizeInBytes());
|
||||
components += componentInfo;
|
||||
buffer.setComponents(components);
|
||||
request.setBuffer(buffer);
|
||||
request.setSourceHandle(handle);
|
||||
request.setTimestampUs(QDateTime::currentMSecsSinceEpoch() * 1000);
|
||||
request.setRotation(VideoRotationGadget::VideoRotation::VIDEO_ROTATION_0);
|
||||
FfiRequest ffiRequest;
|
||||
ffiRequest.setCaptureVideoFrame(request);
|
||||
auto response = ::request(std::move(ffiRequest));
|
||||
});
|
||||
}
|
||||
|
||||
void CallController::setVideoSink(QObject *sink)
|
||||
{
|
||||
m_sink = dynamic_cast<QVideoSink *>(sink);
|
||||
}
|
||||
|
||||
void LivekitVideoSink::setVideoSink(QVideoSink *videoSink)
|
||||
{
|
||||
m_videoSink = videoSink;
|
||||
CallController::instance().setVideoSink(videoSink);
|
||||
Q_EMIT videoSinkChanged();
|
||||
}
|
||||
QVideoSink *LivekitVideoSink::videoSink() const
|
||||
{
|
||||
return m_videoSink;
|
||||
}
|
||||
|
||||
void CallController::toggleCamera()
|
||||
{
|
||||
if (m_localVideoTrackSid.isEmpty()) {
|
||||
publishTrack(m_localVideoTrackId);
|
||||
} else {
|
||||
FfiRequest request;
|
||||
UnpublishTrackRequest unpublishRequest;
|
||||
unpublishRequest.setLocalParticipantHandle(localParticipant);
|
||||
unpublishRequest.setTrackSid(m_localVideoTrackSid);
|
||||
request.setUnpublishTrack(unpublishRequest);
|
||||
auto response = ::request(std::move(request));
|
||||
m_localVideoTrackSid = QString();
|
||||
}
|
||||
}
|
||||
|
||||
void CallController::publishTrack(uint64_t id)
|
||||
{
|
||||
|
||||
PublishTrackRequest publishTrackRequest;
|
||||
publishTrackRequest.setTrackHandle(id);
|
||||
publishTrackRequest.setLocalParticipantHandle(localParticipant);
|
||||
TrackPublishOptions options;
|
||||
options.setSource(TrackSourceGadget::TrackSource::SOURCE_CAMERA);
|
||||
options.setVideoCodec(VideoCodecGadget::VideoCodec::VP8);
|
||||
publishTrackRequest.setOptions(options);
|
||||
|
||||
auto request = FfiRequest();
|
||||
request.setPublishTrack(publishTrackRequest);
|
||||
auto publishResponse = ::request(std::move(request));
|
||||
}
|
||||
97
src/calls/callcontroller.h
Normal file
97
src/calls/callcontroller.h
Normal file
@@ -0,0 +1,97 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QVideoSink>
|
||||
|
||||
#include "events/callmemberevent.h"
|
||||
|
||||
#include "room.qpb.h"
|
||||
|
||||
namespace livekit::proto
|
||||
{
|
||||
class FfiEvent;
|
||||
class ConnectCallback;
|
||||
class DisposeCallback;
|
||||
class RoomEvent;
|
||||
}
|
||||
|
||||
class LivekitMediaPlayer;
|
||||
|
||||
class NeoChatRoom;
|
||||
class QAudioSink;
|
||||
|
||||
class CallController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
public:
|
||||
static CallController &instance()
|
||||
{
|
||||
static CallController _instance;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
static CallController *create(QQmlEngine *, QJSEngine *)
|
||||
{
|
||||
QQmlEngine::setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
|
||||
return &instance();
|
||||
}
|
||||
|
||||
void handleCallMemberEvent(const Quotient::CallMemberEvent *event, NeoChatRoom *room);
|
||||
|
||||
// Internal. Do not use.
|
||||
void handleEvent(livekit::proto::FfiEvent &&event);
|
||||
Q_INVOKABLE void setVideoSink(QObject *sink);
|
||||
Q_INVOKABLE void setCameraVideoSink(QVideoSink *videoSink);
|
||||
|
||||
Q_INVOKABLE void toggleCamera();
|
||||
|
||||
Q_SIGNALS:
|
||||
void callStarted();
|
||||
void connected();
|
||||
|
||||
private:
|
||||
CallController();
|
||||
void init();
|
||||
|
||||
QMap<uint64_t, QPointer<NeoChatRoom>> m_connectingRooms;
|
||||
std::map<uint64_t, livekit::proto::OwnedRoom> m_rooms;
|
||||
void handleConnect(livekit::proto::ConnectCallback &&callback);
|
||||
void handleDispose(livekit::proto::DisposeCallback &&callback);
|
||||
void handleRoomEvent(livekit::proto::RoomEvent &&event);
|
||||
void publishTrack(uint64_t id);
|
||||
|
||||
QIODevice *audioData = nullptr;
|
||||
QAudioSink *sink;
|
||||
QVideoSink *m_sink;
|
||||
uint64_t resampler;
|
||||
QVideoSink *m_cameraVideoSink = nullptr;
|
||||
uint64_t localParticipant = 100000;
|
||||
QString m_localVideoTrackSid;
|
||||
uint64_t m_localVideoTrackId;
|
||||
uint64_t m_localVideoTrackHandle;
|
||||
};
|
||||
|
||||
class LivekitVideoSink : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
public:
|
||||
Q_PROPERTY(QVideoSink *videoSink READ videoSink WRITE setVideoSink NOTIFY videoSinkChanged REQUIRED)
|
||||
using QObject::QObject;
|
||||
|
||||
void setVideoSink(QVideoSink *videoSink);
|
||||
QVideoSink *videoSink() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void videoSinkChanged();
|
||||
|
||||
private:
|
||||
QVideoSink *m_videoSink = nullptr;
|
||||
};
|
||||
0
src/calls/callparticipantsmodel.cpp
Normal file
0
src/calls/callparticipantsmodel.cpp
Normal file
0
src/calls/callparticipantsmodel.h
Normal file
0
src/calls/callparticipantsmodel.h
Normal file
0
src/calls/participant.cpp
Normal file
0
src/calls/participant.cpp
Normal file
0
src/calls/participant.h
Normal file
0
src/calls/participant.h
Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user