Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b75f8b527c | ||
|
|
0a7fccaa5c | ||
|
|
dd3488efbe | ||
|
|
617eb25bbb | ||
|
|
7cf20a473c | ||
|
|
749b59e5eb | ||
|
|
c8170f5a96 | ||
|
|
142adbeb00 | ||
|
|
a1c213dc46 | ||
|
|
f642b1488a | ||
|
|
3e683eac77 | ||
|
|
299d8e9c40 | ||
|
|
45f9aeb5bf | ||
|
|
6961442090 | ||
|
|
f4b31838bc | ||
|
|
8e3ffd3e9d | ||
|
|
02cfb0df9c | ||
|
|
c95778b3ac | ||
|
|
8e66f635fd | ||
|
|
355b5a33b7 | ||
|
|
e40d8230c0 | ||
|
|
a76d4496da | ||
|
|
4ad1a628b6 | ||
|
|
87ea0ef5b2 | ||
|
|
7ed43044ce | ||
|
|
074e5d4c5a | ||
|
|
12ce1e460e | ||
|
|
74a6cd28eb | ||
|
|
53307f9358 | ||
|
|
c683df2b40 | ||
|
|
3b1e19c740 | ||
|
|
4a5c4559bf | ||
|
|
7d2dc0f9ac | ||
|
|
f5a7fb0d13 | ||
|
|
3e09099958 | ||
|
|
4474efd03f | ||
|
|
8fe4cf3f89 | ||
|
|
e25060c12d | ||
|
|
c56ec01637 | ||
|
|
5e07a07f38 | ||
|
|
dcc184a45f | ||
|
|
790ab54d0f | ||
|
|
606f8a1c99 | ||
|
|
46c706e364 | ||
|
|
32bea56a6d | ||
|
|
661cf22667 | ||
|
|
ad1254fb71 | ||
|
|
7514a8a6f7 |
@@ -1,2 +0,0 @@
|
||||
[General]
|
||||
disableUnqualifiedAccess = "i18nc,xi18nc,i18ncp,i18n"
|
||||
@@ -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": [
|
||||
@@ -20,16 +20,8 @@
|
||||
"--talk-name=org.kde.kwalletd5",
|
||||
"--talk-name=org.kde.StatusNotifierWatcher",
|
||||
"--talk-name=org.freedesktop.secrets",
|
||||
"--talk-name=org.kde.kuiserver",
|
||||
"--own-name=org.kde.StatusNotifierItem-2-2"
|
||||
],
|
||||
"cleanup": [
|
||||
"/include",
|
||||
"/lib/*.a",
|
||||
"/lib/cmake",
|
||||
"/lib/pkgconfig",
|
||||
"/share/ndk-modules"
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"name": "kirigamiaddons",
|
||||
@@ -44,22 +36,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "opencv",
|
||||
"config-opts": [
|
||||
"-DBUILD_TESTS=OFF",
|
||||
"-DWITH_GTK=OFF",
|
||||
"-DBUILD_LIST=core,imgproc"
|
||||
],
|
||||
"buildsystem": "cmake-ninja",
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/opencv/opencv"
|
||||
}
|
||||
],
|
||||
"builddir": true
|
||||
},
|
||||
{
|
||||
"name": "kquickimageeditor",
|
||||
"config-opts": [
|
||||
@@ -106,8 +82,8 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://download.gnome.org/sources/libsecret/0.21/libsecret-0.21.7.tar.xz",
|
||||
"sha256": "6b452e4750590a2b5617adc40026f28d2f4903de15f1250e1d1c40bfd68ed55e",
|
||||
"url": "https://download.gnome.org/sources/libsecret/0.21/libsecret-0.21.6.tar.xz",
|
||||
"sha256": "747b8c175be108c880d3adfb9c3537ea66e520e4ad2dccf5dce58003aeeca090",
|
||||
"x-checker-data": {
|
||||
"type": "gnome",
|
||||
"name": "libsecret",
|
||||
@@ -174,23 +150,22 @@
|
||||
"builddir": true
|
||||
},
|
||||
{
|
||||
"name": "kunifiedpush",
|
||||
"name": "qcoro",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"builddir": true,
|
||||
"config-opts": [
|
||||
"-DENABLE_TESTING=OFF",
|
||||
"-DKUNIFIEDPUSH_CLIENT_ONLY=ON"
|
||||
"-DQCORO_BUILD_EXAMPLES=OFF",
|
||||
"-DBUILD_TESTING=OFF"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://download.kde.org/stable/release-service/25.08.0/src/kunifiedpush-25.08.0.tar.xz",
|
||||
"sha256": "846db6ffc7d93f6afea7ce0d5a9f10b52792157ceb593856542279f4197f3518",
|
||||
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.11.0.tar.gz",
|
||||
"sha256": "9942c5b4c533192f6c5954dc6d10178b3829075e6a621b67df73f0a4b74d8297",
|
||||
"x-checker-data": {
|
||||
"type": "anitya",
|
||||
"project-id": 8763,
|
||||
"project-id": 236236,
|
||||
"stable-only": true,
|
||||
"url-template": "https://download.kde.org/stable/release-service/$version/src/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':
|
||||
@@ -43,4 +42,3 @@ Dependencies:
|
||||
Options:
|
||||
per-test-timeout: 90
|
||||
require-passing-tests-on: ['Linux', 'Android', 'FreeBSD', 'Windows']
|
||||
run-qmllint: True
|
||||
|
||||
@@ -8,14 +8,14 @@ cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# KDE Applications version, managed by release script.
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "25")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "11")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "80")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "04")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "1")
|
||||
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.17")
|
||||
set(QT_MIN_VERSION "6.9")
|
||||
set(KF_MIN_VERSION "6.6")
|
||||
set(QT_MIN_VERSION "6.5")
|
||||
|
||||
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
|
||||
|
||||
@@ -24,7 +24,7 @@ set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(KDE_COMPILERSETTINGS_LEVEL 6.17)
|
||||
set(KDE_COMPILERSETTINGS_LEVEL 6.0)
|
||||
|
||||
include(FeatureSummary)
|
||||
include(ECMSetupVersion)
|
||||
@@ -39,7 +39,6 @@ include(ECMCheckOutboundLicense)
|
||||
include(ECMQtDeclareLoggingCategory)
|
||||
include(ECMAddAndroidApk)
|
||||
include(ECMQmlModule)
|
||||
include(ECMDeprecationSettings)
|
||||
include(GenerateExportHeader)
|
||||
include(ECMGenerateHeaders)
|
||||
if (NOT ANDROID)
|
||||
@@ -52,14 +51,12 @@ endif()
|
||||
|
||||
set(QUOTIENT_FORCE_NAMESPACED_INCLUDES TRUE)
|
||||
|
||||
ecm_set_disabled_deprecation_versions(Qt 6.9.0 KF 6.17.0)
|
||||
|
||||
ecm_setup_version(${PROJECT_VERSION}
|
||||
VARIABLE_PREFIX NEOCHAT
|
||||
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 WebView)
|
||||
set_package_properties(Qt6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
@@ -78,7 +75,7 @@ set_package_properties(KF6Kirigami PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Kirigami application UI framework"
|
||||
)
|
||||
find_package(KF6KirigamiAddons 1.10.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)
|
||||
@@ -110,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.3)
|
||||
find_package(QuotientQt6 0.9)
|
||||
set_package_properties(QuotientQt6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
DESCRIPTION "Qt wrapper around Matrix API"
|
||||
@@ -150,24 +147,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)
|
||||
@@ -182,9 +171,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()
|
||||
@@ -195,7 +184,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)
|
||||
|
||||
10
REUSE.toml
10
REUSE.toml
@@ -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"
|
||||
@@ -88,9 +88,3 @@ path = "memorytests/memtest-sync.json"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2024 James Graham <james.h.graham@protonmail.com>"
|
||||
SPDX-License-Identifier = "BSD-2-Clause"
|
||||
|
||||
[[annotations]]
|
||||
path = ".contextProperties.ini"
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "2025 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,18 +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
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
servernoticestest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test neochat_server
|
||||
TEST_NAME servernoticestest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
roommanagertest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test neochat_server
|
||||
TEST_NAME roommanagertest
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
|
||||
#include <QSignalSpy>
|
||||
#include <Quotient/roommember.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
#include <qtestcase.h>
|
||||
@@ -33,7 +32,6 @@ private Q_SLOTS:
|
||||
void noRoom();
|
||||
void badParent();
|
||||
void reply();
|
||||
void replyMissingUser();
|
||||
void edit();
|
||||
void attachment();
|
||||
};
|
||||
@@ -104,33 +102,6 @@ void ChatBarCacheTest::reply()
|
||||
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@example:example.org"_s));
|
||||
QCOMPARE(chatBarCache->relationMessage(), u"This is an example\ntext message"_s);
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
QCOMPARE(chatBarCache->relationAuthorIsPresent(), true);
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::replyMissingUser()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||
chatBarCache->setText(u"some text"_s);
|
||||
chatBarCache->setAttachmentPath(u"some/path"_s);
|
||||
chatBarCache->setReplyId(u"$153456789:example.org"_s);
|
||||
|
||||
QCOMPARE(chatBarCache->text(), u"some text"_s);
|
||||
QCOMPARE(chatBarCache->isReplying(), true);
|
||||
QCOMPARE(chatBarCache->replyId(), u"$153456789:example.org"_s);
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@example:example.org"_s));
|
||||
QCOMPARE(chatBarCache->relationMessage(), u"This is an example\ntext message"_s);
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
QCOMPARE(chatBarCache->relationAuthorIsPresent(), true);
|
||||
|
||||
QSignalSpy relationAuthorIsPresentSpy(chatBarCache.get(), &ChatBarCache::relationAuthorIsPresentChanged);
|
||||
|
||||
// sync again, which will simulate the reply user leaving the room
|
||||
room->syncNewEvents(u"test-min-sync-extra-sync.json"_s);
|
||||
|
||||
QTRY_COMPARE(relationAuthorIsPresentSpy.count(), 1);
|
||||
QCOMPARE(chatBarCache->relationAuthorIsPresent(), false);
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::edit()
|
||||
|
||||
@@ -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-----
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"membership": "leave"
|
||||
},
|
||||
"event_id": "$1432735824666PhrSA:example.org",
|
||||
"origin_server_ts": 1432735824666,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@example:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"replaces_state": "$143273582443PhrSn:example.org"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
#include <Quotient/roommember.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
|
||||
#include "models/eventmessagecontentmodel.h"
|
||||
#include "models/messagecontentmodel.h"
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
@@ -33,23 +32,23 @@ private Q_SLOTS:
|
||||
|
||||
void MessageContentModelTest::initTestCase()
|
||||
{
|
||||
connection = new NeoChatConnection;
|
||||
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
|
||||
}
|
||||
|
||||
void MessageContentModelTest::missingEvent()
|
||||
{
|
||||
auto room = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s);
|
||||
auto model1 = EventMessageContentModel(room, u"$153456789:example.org"_s);
|
||||
auto model1 = MessageContentModel(room, u"$153456789:example.org"_s);
|
||||
|
||||
QCOMPARE(model1.rowCount(), 1);
|
||||
QCOMPARE(model1.data(model1.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
|
||||
QCOMPARE(model1.data(model1.index(0), MessageContentModel::DisplayRole), u"Loading…"_s);
|
||||
QCOMPARE(model1.data(model1.index(0), MessageContentModel::DisplayRole), u"Loading"_s);
|
||||
|
||||
auto model2 = EventMessageContentModel(room, u"$153456789:example.org"_s, true);
|
||||
auto model2 = MessageContentModel(room, u"$153456789:example.org"_s, true);
|
||||
|
||||
QCOMPARE(model2.rowCount(), 1);
|
||||
QCOMPARE(model2.data(model2.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
|
||||
QCOMPARE(model2.data(model2.index(0), MessageContentModel::DisplayRole), u"Loading reply…"_s);
|
||||
QCOMPARE(model2.data(model2.index(0), MessageContentModel::DisplayRole), u"Loading reply"_s);
|
||||
|
||||
room->syncNewEvents(u"test-min-sync.json"_s);
|
||||
QCOMPARE(model1.rowCount(), 2);
|
||||
|
||||
@@ -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/eventmessagecontentmodel.h"
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
@@ -21,7 +20,7 @@ class ReactionModelTest : public QObject
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
TestUtils::TestRoom *room = nullptr;
|
||||
EventMessageContentModel *parentModel;
|
||||
MessageContentModel *parentModel;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
@@ -34,7 +33,7 @@ void ReactionModelTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
|
||||
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-reactionmodel-sync.json"_s);
|
||||
parentModel = new EventMessageContentModel(room, "123456"_L1);
|
||||
parentModel = new MessageContentModel(room, "123456"_L1);
|
||||
}
|
||||
|
||||
void ReactionModelTest::basicReaction()
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QObject>
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
#include <QVariantList>
|
||||
|
||||
#include "accountmanager.h"
|
||||
#include "models/actionsmodel.h"
|
||||
#include "roommanager.h"
|
||||
|
||||
#include "server.h"
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class RoomManagerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
NeoChatConnection *connection = nullptr;
|
||||
NeoChatRoom *room = nullptr;
|
||||
|
||||
Server server;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void testMaximizeMedia();
|
||||
void testResolveMatrixLinks();
|
||||
};
|
||||
|
||||
void RoomManagerTest::initTestCase()
|
||||
{
|
||||
Connection::setRoomType<NeoChatRoom>();
|
||||
server.start();
|
||||
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
|
||||
auto accountManager = new AccountManager(true);
|
||||
QSignalSpy spy(accountManager, &AccountManager::connectionAdded);
|
||||
connection = dynamic_cast<NeoChatConnection *>(accountManager->accounts()->front());
|
||||
QVERIFY(connection);
|
||||
auto roomId = server.createRoom(u"@user:localhost:1234"_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);
|
||||
RoomManager::instance().setConnection(connection);
|
||||
QSignalSpy roomSpy(&RoomManager::instance(), &RoomManager::currentRoomChanged);
|
||||
RoomManager::instance().resolveResource(room->id());
|
||||
QVERIFY(roomSpy.size() > 0);
|
||||
}
|
||||
|
||||
void RoomManagerTest::testMaximizeMedia()
|
||||
{
|
||||
QSignalSpy spy(&RoomManager::instance(), &RoomManager::showMaximizedMedia);
|
||||
QSignalSpy syncSpy(connection, &Connection::syncDone);
|
||||
|
||||
QTest::ignoreMessage(QtMsgType::QtWarningMsg, "Tried to open media for empty event id");
|
||||
RoomManager::instance().maximizeMedia(QString());
|
||||
QVERIFY(!spy.wait(10));
|
||||
|
||||
QTest::ignoreMessage(QtMsgType::QtWarningMsg, "Tried to open media for unknown event id \"Doesn't exist\"");
|
||||
RoomManager::instance().maximizeMedia(u"Doesn't exist"_s);
|
||||
QVERIFY(!spy.wait(10));
|
||||
|
||||
const auto eventWithoutMedia = server.sendEvent(room->id(),
|
||||
u"m.room.message"_s,
|
||||
QJsonObject({
|
||||
{u"body"_s, u"Foo"_s},
|
||||
{u"format"_s, u"org.matrix.custom.html"_s},
|
||||
{u"formatted_body"_s, u"Foo"_s},
|
||||
{u"msgtype"_s, u"m.text"_s},
|
||||
}));
|
||||
QVERIFY(syncSpy.wait());
|
||||
QVERIFY(syncSpy.wait());
|
||||
QTest::ignoreMessage(QtMsgType::QtWarningMsg, u"Tried to open media for unknown event id \"%1\""_s.arg(eventWithoutMedia).toLatin1().data());
|
||||
RoomManager::instance().maximizeMedia(eventWithoutMedia);
|
||||
QVERIFY(!spy.wait(10));
|
||||
|
||||
// NOTE: This is supposed to test that maximizing pending media works correctly. This probably doesn't work in the UI yet, but at least the backend supports
|
||||
// it. If the server ever learns how to process events, this becomes pointless and we need to find a way of preventing *these* events from arriving
|
||||
auto pendingEventWithoutMedia = room->postText(u"Hello"_s);
|
||||
QTest::ignoreMessage(QtMsgType::QtWarningMsg, u"Tried to open media for unknown event id \"%1\""_s.arg(pendingEventWithoutMedia).toLatin1().data());
|
||||
RoomManager::instance().maximizeMedia(pendingEventWithoutMedia);
|
||||
QVERIFY(!spy.wait(10));
|
||||
|
||||
const auto eventWithMedia = server.sendEvent(room->id(),
|
||||
u"m.room.message"_s,
|
||||
QJsonObject({
|
||||
{u"body"_s, u"Foo"_s},
|
||||
{u"filename"_s, u"foo.jpg"_s},
|
||||
{u"info"_s,
|
||||
QJsonObject{
|
||||
{u"h"_s, 1000},
|
||||
{u"w"_s, 2000},
|
||||
{u"size"_s, 10000},
|
||||
{u"mimetype"_s, u"image/png"_s},
|
||||
}},
|
||||
{u"msgtype"_s, u"m.image"_s},
|
||||
{u"url"_s, u"mxc://foo.bar/asdf"_s},
|
||||
}));
|
||||
QVERIFY(syncSpy.wait());
|
||||
QVERIFY(syncSpy.wait());
|
||||
QVERIFY(syncSpy.wait());
|
||||
RoomManager::instance().maximizeMedia(eventWithMedia);
|
||||
QVERIFY(spy.size() == 1);
|
||||
QVERIFY(spy[0][0] == 0);
|
||||
|
||||
auto pendingEventWithMedia = room->postJson(u"m.room.message"_s,
|
||||
QJsonObject({
|
||||
{u"body"_s, u"Foo"_s},
|
||||
{u"filename"_s, u"foo.jpg"_s},
|
||||
{u"info"_s,
|
||||
QJsonObject{
|
||||
{u"h"_s, 1000},
|
||||
{u"w"_s, 2000},
|
||||
{u"size"_s, 10000},
|
||||
{u"mimetype"_s, u"image/png"_s},
|
||||
}},
|
||||
{u"msgtype"_s, u"m.image"_s},
|
||||
{u"url"_s, u"mxc://foo.bar/asdf"_s},
|
||||
}));
|
||||
RoomManager::instance().maximizeMedia(pendingEventWithMedia);
|
||||
QVERIFY(spy.size() == 2);
|
||||
QVERIFY(spy[1][0] == 0);
|
||||
}
|
||||
|
||||
void RoomManagerTest::testResolveMatrixLinks()
|
||||
{
|
||||
// Test if resolving a non-joined room will bring up the confirmation dialog.
|
||||
const QSignalSpy askToJoinSpy(&RoomManager::instance(), &RoomManager::askJoinRoom);
|
||||
RoomManager::instance().resolveResource(QStringLiteral("matrix:r/testbuild:matrix.org"), QStringLiteral("join"));
|
||||
QTRY_COMPARE(askToJoinSpy.size(), 1);
|
||||
}
|
||||
|
||||
QTEST_MAIN(RoomManagerTest)
|
||||
#include "roommanagertest.moc"
|
||||
@@ -1,371 +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 <QHttpServerResponder>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QNetworkReply>
|
||||
#include <QSslCertificate>
|
||||
#include <QSslKey>
|
||||
#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) {
|
||||
Changes changes;
|
||||
changes.invitations += Changes::InviteUser{
|
||||
.userId = QJsonDocument::fromJson(request.body()).object()[u"user_id"_s].toString(),
|
||||
.roomId = roomId,
|
||||
};
|
||||
m_state += changes;
|
||||
responder.write(QJsonDocument(QJsonObject{}), QHttpServerResponder::StatusCode::Ok);
|
||||
});
|
||||
|
||||
m_server.route(u"/_matrix/client/r0/sync"_s, QHttpServerRequest::Method::Get, this, &Server::sync);
|
||||
|
||||
QSslConfiguration config;
|
||||
QFile key(QStringLiteral(DATA_DIR) + u"/localhost.key"_s);
|
||||
void(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)
|
||||
{
|
||||
const auto roomId = generateRoomId();
|
||||
Changes changes;
|
||||
changes.newRooms += Changes::NewRoom{
|
||||
.initialMembers = {matrixId},
|
||||
.roomId = {roomId},
|
||||
.tags = {},
|
||||
};
|
||||
m_state += changes;
|
||||
return roomId;
|
||||
}
|
||||
|
||||
void Server::inviteUser(const QString &roomId, const QString &matrixId)
|
||||
{
|
||||
Changes changes;
|
||||
changes.invitations += Changes::InviteUser{
|
||||
.userId = matrixId,
|
||||
.roomId = roomId,
|
||||
};
|
||||
m_state += changes;
|
||||
}
|
||||
|
||||
void Server::banUser(const QString &roomId, const QString &matrixId)
|
||||
{
|
||||
Changes changes;
|
||||
changes.bans += Changes::BanUser{
|
||||
.userId = matrixId,
|
||||
.roomId = roomId,
|
||||
};
|
||||
m_state += changes;
|
||||
}
|
||||
|
||||
void Server::joinUser(const QString &roomId, const QString &matrixId)
|
||||
{
|
||||
Changes changes;
|
||||
changes.joins += Changes::JoinUser{
|
||||
.userId = matrixId,
|
||||
.roomId = roomId,
|
||||
};
|
||||
m_state += changes;
|
||||
}
|
||||
|
||||
QString Server::createServerNoticesRoom(const QString &matrixId)
|
||||
{
|
||||
const auto roomId = generateRoomId();
|
||||
Changes changes;
|
||||
changes.newRooms += Changes::NewRoom{
|
||||
.initialMembers = {matrixId},
|
||||
.roomId = {roomId},
|
||||
.tags = {u"m.server_notice"_s},
|
||||
};
|
||||
m_state += changes;
|
||||
return roomId;
|
||||
}
|
||||
|
||||
QString Server::sendEvent(const QString &roomId, const QString &eventType, const QJsonObject &content)
|
||||
{
|
||||
Changes changes;
|
||||
const auto eventId = generateEventId();
|
||||
changes.events += Changes::Event{
|
||||
.fullJson = QJsonObject{{u"type"_s, eventType},
|
||||
{u"content"_s, content},
|
||||
{u"sender"_s, u"@foo:server.com"_s},
|
||||
{u"event_id"_s, eventId},
|
||||
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
|
||||
{u"room_id"_s, roomId}},
|
||||
};
|
||||
m_state += changes;
|
||||
return eventId;
|
||||
}
|
||||
|
||||
void Server::sync(const QHttpServerRequest &request, QHttpServerResponder &responder)
|
||||
{
|
||||
QJsonObject joinRooms;
|
||||
auto token = request.query().queryItemValue(u"since"_s).toInt();
|
||||
|
||||
for (const auto &change : m_state.mid(token)) {
|
||||
for (const auto &newRoom : change.newRooms) {
|
||||
QJsonArray stateEvents;
|
||||
stateEvents += 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, newRoom.roomId},
|
||||
{u"sender"_s, newRoom.initialMembers[0]},
|
||||
{u"state_key"_s, QString()},
|
||||
{u"type"_s, u"m.room.create"_s},
|
||||
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||
};
|
||||
for (const auto &member : newRoom.initialMembers) {
|
||||
stateEvents += 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, newRoom.roomId},
|
||||
{u"sender"_s, member},
|
||||
{u"state_key"_s, member},
|
||||
{u"type"_s, u"m.room.member"_s},
|
||||
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||
};
|
||||
}
|
||||
|
||||
auto room = QJsonObject{{u"state"_s, QJsonObject{{u"events"_s, stateEvents}}}};
|
||||
|
||||
QJsonArray roomAccountData;
|
||||
QJsonObject tags;
|
||||
for (const auto &tag : newRoom.tags) {
|
||||
tags[tag] = QJsonObject();
|
||||
}
|
||||
if (!tags.empty()) {
|
||||
roomAccountData += QJsonObject{{u"type"_s, u"m.tag"_s}, {u"content"_s, QJsonObject{{u"tags"_s, tags}}}};
|
||||
}
|
||||
|
||||
if (roomAccountData.size() > 0) {
|
||||
room[u"account_data"] = QJsonObject{{u"events"_s, roomAccountData}};
|
||||
}
|
||||
|
||||
joinRooms[newRoom.roomId] = room;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &change : m_state.mid(token)) {
|
||||
for (const auto &invitation : change.invitations) {
|
||||
// TODO: The invitation could be for a room we haven't joined yet. Shouldn't be necessary for now, though.
|
||||
auto stateEvents = joinRooms[invitation.roomId][u"state"_s][u"events"_s].toArray();
|
||||
stateEvents += 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, invitation.roomId},
|
||||
{u"sender"_s, u"@user:localhost:1234"_s},
|
||||
{u"state_key"_s, invitation.userId},
|
||||
{u"type"_s, u"m.room.member"_s},
|
||||
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||
};
|
||||
if (joinRooms.contains(invitation.roomId)) {
|
||||
auto room = joinRooms[invitation.roomId].toObject();
|
||||
room[u"state"_s] = QJsonObject{{u"events"_s, stateEvents}};
|
||||
joinRooms[invitation.roomId] = room;
|
||||
} else {
|
||||
joinRooms[invitation.roomId] = QJsonObject{{u"state"_s,
|
||||
QJsonObject{
|
||||
{u"events"_s, stateEvents},
|
||||
}}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &change : m_state.mid(token)) {
|
||||
for (const auto &ban : change.bans) {
|
||||
// TODO: The ban could be for a room we haven't joined yet. Shouldn't be necessary for now, though.
|
||||
auto stateEvents = joinRooms[ban.roomId][u"state"_s][u"events"_s].toArray();
|
||||
stateEvents += 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, ban.roomId},
|
||||
{u"sender"_s, u"@user:localhost:1234"_s},
|
||||
{u"state_key"_s, ban.userId},
|
||||
{u"type"_s, u"m.room.member"_s},
|
||||
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||
};
|
||||
if (joinRooms.contains(ban.roomId)) {
|
||||
auto room = joinRooms[ban.roomId].toObject();
|
||||
room[u"state"_s] = QJsonObject{{u"events"_s, stateEvents}};
|
||||
joinRooms[ban.roomId] = room;
|
||||
} else {
|
||||
joinRooms[ban.roomId] = QJsonObject{{u"state"_s,
|
||||
QJsonObject{
|
||||
{u"events"_s, stateEvents},
|
||||
}}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &change : m_state.mid(token)) {
|
||||
for (const auto &join : change.joins) {
|
||||
// TODO: The join could be for a room we haven't joined yet. Shouldn't be necessary for now, though.
|
||||
auto stateEvents = joinRooms[join.roomId][u"state"_s][u"events"_s].toArray();
|
||||
stateEvents += 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, join.roomId},
|
||||
{u"sender"_s, u"@user:localhost:1234"_s},
|
||||
{u"state_key"_s, join.userId},
|
||||
{u"type"_s, u"m.room.member"_s},
|
||||
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||
};
|
||||
if (joinRooms.contains(join.roomId)) {
|
||||
auto room = joinRooms[join.roomId].toObject();
|
||||
room[u"state"_s] = QJsonObject{{u"events"_s, stateEvents}};
|
||||
joinRooms[join.roomId] = room;
|
||||
} else {
|
||||
joinRooms[join.roomId] = QJsonObject{{u"state"_s,
|
||||
QJsonObject{
|
||||
{u"events"_s, stateEvents},
|
||||
}}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &change : m_state.mid(token)) {
|
||||
for (const auto &event : change.events) {
|
||||
// TODO the room might be in a different join state.
|
||||
auto timeline = joinRooms[event.fullJson[u"room_id"_s].toString()][u"timeline"_s][u"events"_s].toArray();
|
||||
timeline += event.fullJson;
|
||||
if (joinRooms.contains(event.fullJson[u"room_id"_s].toString())) {
|
||||
auto room = joinRooms[event.fullJson[u"room_id"_s].toString()].toObject();
|
||||
room[u"timeline"_s] = QJsonObject{{u"events"_s, timeline}};
|
||||
joinRooms[event.fullJson[u"room_id"_s].toString()] = room;
|
||||
} else {
|
||||
joinRooms[event.fullJson[u"room_id"_s].toString()] = QJsonObject{
|
||||
{u"timeline"_s, QJsonObject{{u"events"_s, timeline}}},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject syncData = {
|
||||
// {u"account_data"_s, QJsonObject {}},
|
||||
// {u"presence"_s, QJsonObject {}},
|
||||
{u"next_batch"_s, QString::number(m_state.size())},
|
||||
};
|
||||
|
||||
QJsonObject rooms;
|
||||
if (!joinRooms.isEmpty()) {
|
||||
rooms[u"join"_s] = joinRooms;
|
||||
}
|
||||
|
||||
if (!rooms.empty()) {
|
||||
syncData[u"rooms"_s] = rooms;
|
||||
}
|
||||
|
||||
qWarning() << syncData;
|
||||
responder.write(QJsonDocument(syncData), QHttpServerResponder::StatusCode::Ok);
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include <QHttpServer>
|
||||
#include <QJsonObject>
|
||||
#include <QSslServer>
|
||||
|
||||
struct Changes {
|
||||
struct NewRoom {
|
||||
QStringList initialMembers;
|
||||
QString roomId;
|
||||
QStringList tags;
|
||||
};
|
||||
QList<NewRoom> newRooms;
|
||||
|
||||
struct InviteUser {
|
||||
QString userId;
|
||||
QString roomId;
|
||||
};
|
||||
QList<InviteUser> invitations;
|
||||
|
||||
struct BanUser {
|
||||
QString userId;
|
||||
QString roomId;
|
||||
};
|
||||
QList<BanUser> bans;
|
||||
|
||||
struct JoinUser {
|
||||
QString userId;
|
||||
QString roomId;
|
||||
};
|
||||
QList<JoinUser> joins;
|
||||
|
||||
struct Event {
|
||||
QJsonObject fullJson;
|
||||
};
|
||||
QList<Event> events;
|
||||
};
|
||||
|
||||
struct RoomData {
|
||||
QStringList members;
|
||||
QString id;
|
||||
QStringList tags;
|
||||
};
|
||||
|
||||
class Server : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* Create a server notices room.
|
||||
*/
|
||||
QString createServerNoticesRoom(const QString &matrixId);
|
||||
QString sendEvent(const QString &roomId, const QString &eventType, const QJsonObject &content);
|
||||
|
||||
private:
|
||||
QHttpServer m_server;
|
||||
QSslServer m_sslServer;
|
||||
|
||||
void sync(const QHttpServerRequest &request, QHttpServerResponder &responder);
|
||||
|
||||
QList<Changes> m_state;
|
||||
};
|
||||
@@ -1,87 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QObject>
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/eventstats.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
|
||||
#include "accountmanager.h"
|
||||
#include "neochatroom.h"
|
||||
#include "roommanager.h"
|
||||
#include "server.h"
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class ServerNoticesTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
NeoChatConnection *connection = nullptr;
|
||||
Server server;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void test();
|
||||
};
|
||||
|
||||
void ServerNoticesTest::initTestCase()
|
||||
{
|
||||
Connection::setRoomType<NeoChatRoom>();
|
||||
server.start();
|
||||
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
|
||||
auto accountManager = new AccountManager(true);
|
||||
QSignalSpy spy(accountManager, &AccountManager::connectionAdded);
|
||||
connection = dynamic_cast<NeoChatConnection *>(accountManager->accounts()->front());
|
||||
QVERIFY(connection);
|
||||
auto roomId = server.createRoom(u"@user:localhost:1234"_s);
|
||||
RoomManager::instance().setConnection(connection);
|
||||
|
||||
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());
|
||||
auto room = dynamic_cast<NeoChatRoom *>(connection->room(roomId));
|
||||
QVERIFY(room);
|
||||
}
|
||||
|
||||
void ServerNoticesTest::test()
|
||||
{
|
||||
auto roomTreeModel = RoomManager::instance().roomTreeModel();
|
||||
QCOMPARE(roomTreeModel->rowCount(roomTreeModel->index(NeoChatRoomType::ServerNotice, 0)), 0);
|
||||
auto sortFilterRoomTreeModel = RoomManager::instance().sortFilterRoomTreeModel();
|
||||
const auto roomId = server.createServerNoticesRoom(u"@user:localhost:1234"_s);
|
||||
QSignalSpy syncSpy(connection, &Connection::syncDone);
|
||||
QVERIFY(syncSpy.wait());
|
||||
QVERIFY(syncSpy.wait());
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(connection->room(roomId));
|
||||
QVERIFY(connection->room(roomId)->isServerNoticeRoom());
|
||||
QCOMPARE(roomTreeModel->rowCount(roomTreeModel->index(NeoChatRoomType::ServerNotice, 0)), 1);
|
||||
QCOMPARE(sortFilterRoomTreeModel->mapFromSource(roomTreeModel->indexForRoom(room)).parent().row(), 1 /* Below the normal room */);
|
||||
server.sendEvent(roomId,
|
||||
u"m.room.message"_s,
|
||||
QJsonObject{
|
||||
{u"body"_s, u"Foo"_s},
|
||||
{u"format"_s, u"org.matrix.custom.html"_s},
|
||||
{u"formatted_body"_s, u"Foo"_s},
|
||||
{u"msgtype"_s, u"m.text"_s},
|
||||
});
|
||||
QVERIFY(syncSpy.wait());
|
||||
QVERIFY(syncSpy.wait());
|
||||
sortFilterRoomTreeModel->invalidate();
|
||||
QCOMPARE(sortFilterRoomTreeModel->mapFromSource(roomTreeModel->indexForRoom(room)).parent().row(), 0);
|
||||
room->markAllMessagesAsRead();
|
||||
QCOMPARE(sortFilterRoomTreeModel->mapFromSource(roomTreeModel->indexForRoom(room)).parent().row(), 1 /* Below the normal room */);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(ServerNoticesTest)
|
||||
#include "servernoticestest.moc"
|
||||
@@ -1,7 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QTest>
|
||||
#include <Quotient/events/event.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
|
||||
@@ -33,7 +32,7 @@ public:
|
||||
if (!syncFileName.isEmpty()) {
|
||||
QFile testSyncFile;
|
||||
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + syncFileName);
|
||||
Q_UNUSED(testSyncFile.open(QIODevice::ReadOnly));
|
||||
testSyncFile.open(QIODevice::ReadOnly);
|
||||
const auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll());
|
||||
Quotient::SyncRoomData roomData(id(), Quotient::JoinState::Join, testSyncJson.object());
|
||||
update(std::move(roomData));
|
||||
@@ -47,7 +46,7 @@ inline Quotient::event_ptr_tt<EventT> loadEventFromFile(const QString &eventFile
|
||||
if (!eventFileName.isEmpty()) {
|
||||
QFile testEventFile;
|
||||
testEventFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + eventFileName);
|
||||
Q_UNUSED(testEventFile.open(QIODevice::ReadOnly));
|
||||
testEventFile.open(QIODevice::ReadOnly);
|
||||
auto testSyncJson = QJsonDocument::fromJson(testEventFile.readAll()).object();
|
||||
return Quotient::loadEvent<EventT>(testSyncJson);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -34,10 +33,6 @@ private Q_SLOTS:
|
||||
void stripDisallowedTags();
|
||||
void stripDisallowedAttributes();
|
||||
void emptyCodeTags();
|
||||
void addStyle_data();
|
||||
void addStyle();
|
||||
void dontAddStyle_data();
|
||||
void dontAddStyle();
|
||||
|
||||
void sendSimpleStringCase();
|
||||
void sendSingleParaMarkup();
|
||||
@@ -48,8 +43,6 @@ private Q_SLOTS:
|
||||
void sendCustomEmoji();
|
||||
void sendCustomEmojiCode_data();
|
||||
void sendCustomEmojiCode();
|
||||
void sendCustomTags_data();
|
||||
void sendCustomTags();
|
||||
|
||||
void receiveSpacelessSelfClosingTag();
|
||||
void receiveStripReply();
|
||||
@@ -65,7 +58,6 @@ private Q_SLOTS:
|
||||
void receiveRichStrikethrough();
|
||||
void receiveRichtextIn();
|
||||
void receiveRichMxcUrl();
|
||||
void receiveRichPlainUrl_data();
|
||||
void receiveRichPlainUrl();
|
||||
void receiveRichEdited_data();
|
||||
void receiveRichEdited();
|
||||
@@ -75,9 +67,6 @@ private Q_SLOTS:
|
||||
|
||||
void componentOutput_data();
|
||||
void componentOutput();
|
||||
|
||||
void updateSpoiler_data();
|
||||
void updateSpoiler();
|
||||
};
|
||||
|
||||
void TextHandlerTest::initTestCase()
|
||||
@@ -96,26 +85,21 @@ void TextHandlerTest::initTestCase()
|
||||
|
||||
void TextHandlerTest::allowedAttributes()
|
||||
{
|
||||
auto theme = static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
|
||||
const QString testInputString1 = u"<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>"_s;
|
||||
const QString testOutputString1S = u"<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>"_s;
|
||||
const QString testOutputString1R = u"<span data-mx-spoiler style=\"color: transparent; background: %1;\"><font color=#FFFFFF>Test</font><span>"_s.arg(
|
||||
theme->alternateBackgroundColor().name());
|
||||
const QString testOutputString1 = u"<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>"_s;
|
||||
// Handle urls where the href has either single (') or double (") quotes.
|
||||
const QString testInputString2 = u"<a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a>"_s;
|
||||
const QString testOutputString2S = u"<a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a>"_s;
|
||||
const QString testOutputString2R =
|
||||
u"<a href=\"https://kde.org\" style=\"text-decoration: none;\">link</a><a href='https://kde.org' style=\"text-decoration: none;\">link</a>"_s;
|
||||
const QString testOutputString2 = u"<a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a>"_s;
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString1);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString1S);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString1R);
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString1);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString1);
|
||||
|
||||
testTextHandler.setData(testInputString2);
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString2S);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString2R);
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString2);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString2);
|
||||
}
|
||||
|
||||
void TextHandlerTest::stripDisallowedTags()
|
||||
@@ -158,56 +142,6 @@ void TextHandlerTest::emptyCodeTags()
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::addStyle_data()
|
||||
{
|
||||
QTest::addColumn<QString>("testInputString");
|
||||
QTest::addColumn<QString>("testOutputString");
|
||||
|
||||
QTest::newRow("link") << u"<a href=\"https://kde.org\">link</a>"_s << u"<a href=\"https://kde.org\" style=\"text-decoration: none;\">link</a>"_s;
|
||||
QTest::newRow("table")
|
||||
<< u"<table><tr><th>Company</th><th>Contact</th><th>Country</th></tr><tr><td>Alfreds Futterkiste</td><td>Maria Anders</td><td>Germany</td></tr><tr><td>Centro comercial Moctezuma</td><td>Francisco Chang</td><td>Mexico</td></tr></table>"_s
|
||||
<< u"<table style=\"width: 100%; border-collapse: collapse; border: 1px; border-style: solid;\"><tr><th style=\"border: 1px solid black; padding: 3px;\">Company</th><th style=\"border: 1px solid black; padding: 3px;\">Contact</th><th style=\"border: 1px solid black; padding: 3px;\">Country</th></tr><tr><td style=\"border: 1px solid black; padding: 3px;\">Alfreds Futterkiste</td><td style=\"border: 1px solid black; padding: 3px;\">Maria Anders</td><td style=\"border: 1px solid black; padding: 3px;\">Germany</td></tr><tr><td style=\"border: 1px solid black; padding: 3px;\">Centro comercial Moctezuma</td><td style=\"border: 1px solid black; padding: 3px;\">Francisco Chang</td><td style=\"border: 1px solid black; padding: 3px;\">Mexico</td></tr></table>"_s;
|
||||
auto theme = static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
|
||||
QTest::newRow("spoiler") << u"<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>"_s
|
||||
<< u"<span data-mx-spoiler style=\"color: transparent; background: %1;\"><font color=#FFFFFF>Test</font><span>"_s.arg(
|
||||
theme->alternateBackgroundColor().name());
|
||||
}
|
||||
|
||||
void TextHandlerTest::addStyle()
|
||||
{
|
||||
QFETCH(QString, testInputString);
|
||||
QFETCH(QString, testOutputString);
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::dontAddStyle_data()
|
||||
{
|
||||
QTest::addColumn<QString>("testInputString");
|
||||
QTest::addColumn<QString>("testOutputString");
|
||||
|
||||
QTest::newRow("link") << u"<a href=\"https://kde.org\">link</a>"_s << u"<a href=\"https://kde.org\">link</a>"_s;
|
||||
QTest::newRow("table")
|
||||
<< u"<table><tr><th>Company</th><th>Contact</th><th>Country</th></tr><tr><td>Alfreds Futterkiste</td><td>Maria Anders</td><td>Germany</td></tr><tr><td>Centro comercial Moctezuma</td><td>Francisco Chang</td><td>Mexico</td></tr></table>"_s
|
||||
<< u"<table><tr><th>Company</th><th>Contact</th><th>Country</th></tr><tr><td>Alfreds Futterkiste</td><td>Maria Anders</td><td>Germany</td></tr><tr><td>Centro comercial Moctezuma</td><td>Francisco Chang</td><td>Mexico</td></tr></table>"_s;
|
||||
QTest::newRow("spoiler") << u"<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>"_s
|
||||
<< u"<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>"_s;
|
||||
}
|
||||
|
||||
void TextHandlerTest::dontAddStyle()
|
||||
{
|
||||
QFETCH(QString, testInputString);
|
||||
QFETCH(QString, testOutputString);
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::sendSimpleStringCase()
|
||||
{
|
||||
const QString testInputString = u"This data should just be left alone."_s;
|
||||
@@ -315,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;
|
||||
@@ -400,8 +300,7 @@ void TextHandlerTest::receiveRichInPlainOut()
|
||||
void TextHandlerTest::receivePlainTextIn()
|
||||
{
|
||||
const QString testInputString = u"<plain text in tag bracket>\nTest link https://kde.org."_s;
|
||||
const QString testOutputStringRich =
|
||||
u"<plain text in tag bracket><br>Test link <a href=\"https://kde.org\" style=\"text-decoration: none;\">https://kde.org</a>."_s;
|
||||
const QString testOutputStringRich = u"<plain text in tag bracket><br>Test link <a href=\"https://kde.org\">https://kde.org</a>."_s;
|
||||
QString testOutputStringPlain = u"<plain text in tag bracket>\nTest link https://kde.org."_s;
|
||||
|
||||
// Make sure quotes are maintained in a plain string.
|
||||
@@ -471,7 +370,7 @@ void TextHandlerTest::receivePlainStripMarkup()
|
||||
void TextHandlerTest::receiveRichUserPill()
|
||||
{
|
||||
const QString testInputString = u"<p><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></p>"_s;
|
||||
const QString testOutputString = u"<b><a href=\"https://matrix.to/#/@alice:example.org\" style=\"text-decoration: none;\">@alice:example.org</a></b>"_s;
|
||||
const QString testOutputString = u"<b><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></b>"_s;
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
@@ -514,34 +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\" style=\"text-decoration: none;\">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\" style=\"text-decoration: none;\">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/\" style=\"text-decoration: none;\">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" style="text-decoration: none;">email@example.com</a> <a href="mailto:email@example.com" style="text-decoration: none;">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\" style=\"text-decoration: none;\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\" style=\"text-decoration: none;\">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\" style=\"text-decoration: none;\">@user:kde.org</a></b> b"_s;
|
||||
}
|
||||
|
||||
/**
|
||||
* For when your rich input string has a plain text url left in.
|
||||
*
|
||||
@@ -550,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()
|
||||
@@ -564,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()
|
||||
@@ -610,6 +511,8 @@ void TextHandlerTest::receiveRichColor()
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
qInfo() << testTextHandler.handleRecieveRichText();
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||
}
|
||||
|
||||
@@ -661,35 +564,5 @@ void TextHandlerTest::componentOutput()
|
||||
QCOMPARE(testTextHandler.textComponents(testInputString), testOutputComponents);
|
||||
}
|
||||
|
||||
void TextHandlerTest::updateSpoiler_data()
|
||||
{
|
||||
QTest::addColumn<QString>("testInputString");
|
||||
QTest::addColumn<QString>("testOutputString");
|
||||
QTest::addColumn<bool>("spoilerRevealed");
|
||||
|
||||
auto theme = static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
|
||||
QTest::newRow("same length") << u"<span data-mx-spoiler style=\"color: #123456; background: #123456;\">Test<span>"_s
|
||||
<< u"<span data-mx-spoiler style=\"color: transparent; background: %1;\">Test<span>"_s.arg(
|
||||
theme->alternateBackgroundColor().name())
|
||||
<< false;
|
||||
QTest::newRow("different length") << u"<span data-mx-spoiler style=\"color: short; background: looooooooooong;\">Test<span>"_s
|
||||
<< u"<span data-mx-spoiler style=\"color: transparent; background: %1;\">Test<span>"_s.arg(
|
||||
theme->alternateBackgroundColor().name())
|
||||
<< false;
|
||||
QTest::newRow("spoiler revealed")
|
||||
<< u"<span data-mx-spoiler style=\"color: transparent; background: %1;\">Test<span>"_s.arg(theme->alternateBackgroundColor().name())
|
||||
<< u"<span data-mx-spoiler style=\"color: %1; background: %2;\">Test<span>"_s.arg(theme->textColor().name(), theme->alternateBackgroundColor().name())
|
||||
<< true;
|
||||
}
|
||||
|
||||
void TextHandlerTest::updateSpoiler()
|
||||
{
|
||||
QFETCH(QString, testInputString);
|
||||
QFETCH(QString, testOutputString);
|
||||
QFETCH(bool, spoilerRevealed);
|
||||
|
||||
QCOMPARE(TextHandler::updateSpoilerText(this, testInputString, spoilerRevealed), testOutputString);
|
||||
}
|
||||
|
||||
QTEST_MAIN(TextHandlerTest)
|
||||
#include "texthandlertest.moc"
|
||||
|
||||
@@ -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);
|
||||
@@ -161,7 +161,7 @@ void TimelineMessageModelTest::pendingEvent()
|
||||
// different every time.
|
||||
QFile testSyncFile;
|
||||
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + u"test-pending-sync.json"_s);
|
||||
QVERIFY(testSyncFile.open(QIODevice::ReadOnly));
|
||||
testSyncFile.open(QIODevice::ReadOnly);
|
||||
auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll());
|
||||
auto root = testSyncJson.object();
|
||||
auto timeline = root["timeline"_L1].toObject();
|
||||
@@ -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()
|
||||
|
||||
@@ -3,26 +3,24 @@
|
||||
|
||||
add_definitions(-DDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}" )
|
||||
|
||||
qt_add_executable(timeline_memtest
|
||||
qt_add_executable(timeline-memtest
|
||||
main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(timeline_memtest PRIVATE neochatplugin Timelineplugin)
|
||||
target_link_libraries(timeline_memtest PUBLIC
|
||||
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
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/timeline_memtest
|
||||
ecm_add_qml_module(timeline-memtest URI org.kde.neochat.timeline-memtest GENERATE_PLUGIN_SOURCE
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/timeline-memtest
|
||||
QML_FILES
|
||||
Main.qml
|
||||
SOURCES
|
||||
@@ -32,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,
|
||||
|
||||
@@ -37,58 +37,12 @@ public:
|
||||
if (!syncFileName.isEmpty()) {
|
||||
QFile testSyncFile;
|
||||
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + syncFileName);
|
||||
auto ok = testSyncFile.open(QIODevice::ReadOnly);
|
||||
if (!ok) {
|
||||
qWarning() << "Failed to open" << testSyncFile.fileName() << testSyncFile.errorString();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
<name xml:lang="pl">NeoChat</name>
|
||||
<name xml:lang="pt">NeoChat</name>
|
||||
<name xml:lang="pt-BR">NeoChat</name>
|
||||
<name xml:lang="ro">NeoChat</name>
|
||||
<name xml:lang="ru">NeoChat</name>
|
||||
<name xml:lang="sa">नवचैट्</name>
|
||||
<name xml:lang="sk">NeoChat</name>
|
||||
@@ -50,6 +49,7 @@
|
||||
<name xml:lang="ta">நியோச்சாட்</name>
|
||||
<name xml:lang="tr">NeoChat</name>
|
||||
<name xml:lang="uk">NeoChat</name>
|
||||
<name xml:lang="x-test">xxNeoChatxx</name>
|
||||
<name xml:lang="zh-CN">NeoChat</name>
|
||||
<name xml:lang="zh-TW">NeoChat</name>
|
||||
<summary>Chat on Matrix</summary>
|
||||
@@ -75,8 +75,6 @@
|
||||
<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="ro">Discutați pe Matrix</summary>
|
||||
<summary xml:lang="ru">Общение в Matrix</summary>
|
||||
<summary xml:lang="sa">Matrix इत्यत्र गपशपं कुर्वन्तु</summary>
|
||||
<summary xml:lang="sl">Klepet na Matrixu</summary>
|
||||
@@ -84,6 +82,7 @@
|
||||
<summary xml:lang="ta">மேட்ரிக்ஸுக்கான உரையாடல் செயலி</summary>
|
||||
<summary xml:lang="tr">Matrix Üzerinde Sohbet</summary>
|
||||
<summary xml:lang="uk">Спілкування у Matrix</summary>
|
||||
<summary xml:lang="x-test">xxChat on Matrixxx</summary>
|
||||
<summary xml:lang="zh-TW">在 Matrix 上聊天</summary>
|
||||
<description>
|
||||
<p>NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
|
||||
@@ -110,17 +109,16 @@
|
||||
<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="ro">NeoChat e o aplicație de discuții ce vă ajută să profitați din plin de rețeaua Matrix. Aceasta oferă o modalitate sigură de a trimite mesaje textuale, videoclipuri și fișiere audio familiei, colegilor și prietenilor.</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>
|
||||
<p xml:lang="sv">NeoChat är ett chattprogram som låter dig dra full nytta av Matrix-nätverket. Det ger dig ett säkert sätt att skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner.</p>
|
||||
<p xml:lang="tr">NeoChat, Matrix ağının tüm özelliklerini kullanan bir sohbet uygulamasıdır. Ailenize, arkadaşlarınıza ve iş arkadaşlarınıza metin iletileri, ses ve video dosyaları göndermenin kolay bir yolunu sunar.</p>
|
||||
<p xml:lang="uk">NeoChat є програмою для спілкування, за допомогою якої ви можете скористатися усіма перевагами мережі Matrix. За її допомогою ви можете безпечно надсилати текстові повідомлення, відео та звукові файли вашим родичам, колегам та друзям.</p>
|
||||
<p xml:lang="x-test">xxNeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.xx</p>
|
||||
<p xml:lang="zh-TW">NeoChat 是一個讓您能夠完全利用 Matrix 網路的聊天應用程式。它讓您安全地傳送文字訊息、影片或音訊檔給家人、同事或朋友等等。</p>
|
||||
<p>NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
|
||||
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. يوفر نيوتشات كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP و تعدد الخيوط وبعض جوانب التعمية من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار، ولكن يبقى الهدف توفير تطبيق للمواصفات بأكملها.</p>
|
||||
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. على هذا النحو يتم دعم كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP والخيوط وبعض جوانب التشفير من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار ، ولكن يبقى الهدف توفير الدعم النهائي للمواصفات بأكملها.</p>
|
||||
<p xml:lang="ca">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptatge d'extrem a extrem. Hi ha algunes altres omissions més petites a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu segueix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="de">NeoChat versucht eine vollumfängliche Anwendung für die Spezifikation von Matrix zu sein. Damit wird alles der aktuellen stabilen Spezifikation mit den erwähnenswerten Ausnahmen von VoIP, Diskussionsfäden und ein paar Teilen der Ende-zu-Ende-Verschlüsselung unterstützt. Zudem sind andere kleinere Auslassungen vorhanden, da sich die Matrixspezifikation ständig weiterentwickelt. Nichtsdestotrotz soll letztendlich die gesamte Spezifikation unterstützt werden.</p>
|
||||
@@ -144,17 +142,16 @@
|
||||
<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="ro">NeoChat vrea să fie o aplicație completă pentru specificațiile Matrix. Astfel, susține tot ce se găsește acum în specificațiile stabile cu excepția VoIP, a firelor de discuții, și a unor părți din criptarea punct-la-punct. Sunt și câteva omisiuni minore din cauza faptului că specificația Matrix evoluează continuu, dar scopul rămâne acela de a implementa întreaga specificație.</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>
|
||||
<p xml:lang="sv">NeoChat har som mål att vara ett fullständigt program enligt Matrix-specifikationen. Som sådant stöds allt i den nuvarande stabila specifikationen, med de nämnvärda undantagen VoIP, trådar och några aspekter av kryptering hela vägen. Det finns några ytterligare utelämnanden på grund av att Matrix-specifikationen hela tiden utvecklas, men målet förblir att till slut erbjuda stöd för hela specifikationen.</p>
|
||||
<p xml:lang="tr">NeoChat, Matrix belirtimi için tam özellikli bir uygulama olmayı hedefler. Bu nedenle; VoIP, ileti zincirleri ve Uçtan Uca Şifreleme’nin bazı yönleri gibi dikkate değer istisnalar dışında var olan kararlı belirtimdeki her şey desteklenir. Matrix belirtiminin sürekli gelişmesi nedeniyle birkaç küçük eksiklik daha var; ancak amaç tüm belirtim için nihai destek sağlamak olmayı sürdürüyor.</p>
|
||||
<p xml:lang="uk">Метою створення NeoChat є повноцінна реалізація програми для специфікації Matrix. Як наслідок, реалізовано усе у поточній стабільній специфікації, окрім голосового інтернет-зв'язку, потоків та деяких аспектів міжвузлового шифрування. Є також декілька інших незначних прогалин через те, що специфікація Matrix постійно змінюється, але метою лишається повна підтримка специфікації.</p>
|
||||
<p xml:lang="x-test">xxNeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.xx</p>
|
||||
<p xml:lang="zh-TW">NeoChat 以完整支援 Matrix 標準為目標,因此目前穩定版標準除了 VoIP、對話串與端對端加密的某些部分以外的所有部分都有支援。其他部分還有一些較小的不支援的部分,這是因為 Matrix 標準隨時都在改進,但目標仍然時最終提供整個標準的完整支援。</p>
|
||||
<p>Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:</p>
|
||||
<p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يوفر نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p>
|
||||
<p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يدعم نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p>
|
||||
<p xml:lang="ca">A causa de la naturalesa del desenvolupament de l'especificació de Matrix, el NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
|
||||
<p xml:lang="ca-valencia">A causa de la naturalea del desenvolupament de l'especificació de Matrix, NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
|
||||
<p xml:lang="de">Durch die Weiterentwicklung der Matrix-Spezifikation unterstützt auch NeoChat einige als noch instabil gekennzeichnete Funktionen. Derzeit sind das:</p>
|
||||
@@ -178,8 +175,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="ro">Datorită modului de dezvoltare a specificațiilor Matrix, NeoChat susține și numeroase caracteristici nestabile. Acum, acestea sunt:</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>
|
||||
@@ -187,12 +182,13 @@
|
||||
<p xml:lang="ta">மேட்ரிக்ஸு நெறிமுறை வரையறுக்கப்படும் விதத்தின் காரணமாக, பல நிலையற்ற அம்சங்களையும் நியோச்சாட் ஆதரிக்கிறது. தற்போது ஆதரிக்கப்படுபவை:</p>
|
||||
<p xml:lang="tr">NeoChat, Matrix belirtimi geliştirmesinin doğası gereği çok sayıda kararsız özelliği de destekler. Şu anda bunlar:</p>
|
||||
<p xml:lang="uk">Через природу розробки специфікації Matrix, у NeoChat також передбачено підтримку численних нестабільних можливостей. У поточній версії цими можливостями є:</p>
|
||||
<p xml:lang="x-test">xxDue to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:xx</p>
|
||||
<p xml:lang="zh-TW">由於 Matrix 標準的開發流程的緣故,NeoChat 也支援數個非穩定版的功能。目前這些功能是:</p>
|
||||
<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,8 +209,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="ro">Sondaje - MSC3381</li>
|
||||
<li xml:lang="ru">Голосования — MSC3381</li>
|
||||
<li xml:lang="sa">मतदान - MSC3381</li>
|
||||
<li xml:lang="sl">Polls - MSC3381</li>
|
||||
@@ -222,6 +216,7 @@
|
||||
<li xml:lang="ta">வாக்கெடுப்புகள் - MSC3381</li>
|
||||
<li xml:lang="tr">Anketler — MSC3381</li>
|
||||
<li xml:lang="uk">Опитування - MSC3381</li>
|
||||
<li xml:lang="x-test">xxPolls - MSC3381xx</li>
|
||||
<li xml:lang="zh-TW">投票 - MSC3381</li>
|
||||
<li>Sticker Packs - MSC2545</li>
|
||||
<li xml:lang="ar">حزم الملصقات - MSC2545</li>
|
||||
@@ -247,8 +242,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="ro">Colecții de abțibilduri - MSC2545</li>
|
||||
<li xml:lang="ru">Наборы стикеров — MSC2545</li>
|
||||
<li xml:lang="sa">स्टिकर पैक - MSC2545</li>
|
||||
<li xml:lang="sl">Sticker Packs - MSC2545</li>
|
||||
@@ -256,6 +249,7 @@
|
||||
<li xml:lang="ta">ஒட்டி தொகுப்புகள் - MSC2545</li>
|
||||
<li xml:lang="tr">Çıkartma Paketleri — MSC2545</li>
|
||||
<li xml:lang="uk">Пакунки наліпок - MSC2545</li>
|
||||
<li xml:lang="x-test">xxSticker Packs - MSC2545xx</li>
|
||||
<li xml:lang="zh-TW">貼圖包 - MSC2545</li>
|
||||
<li>Location Events - MSC3488</li>
|
||||
<li xml:lang="ar">موقع الأحداث - MSC3488</li>
|
||||
@@ -281,8 +275,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="ro">Evenimente de amplasare - MSC3488</li>
|
||||
<li xml:lang="ru">События местоположения — MSC3488</li>
|
||||
<li xml:lang="sa">स्थान घटनाएँ - MSC3488</li>
|
||||
<li xml:lang="sl">Location Events - MSC3488</li>
|
||||
@@ -290,6 +282,7 @@
|
||||
<li xml:lang="ta">இட நிகழ்வுகள் - MSC3488</li>
|
||||
<li xml:lang="tr">Konum Etkinlikleri — MSC3488</li>
|
||||
<li xml:lang="uk">Місцеві зустрічі - MSC3488</li>
|
||||
<li xml:lang="x-test">xxLocation Events - MSC3488xx</li>
|
||||
<li xml:lang="zh-TW">位置事件 - MSC3488</li>
|
||||
</ul>
|
||||
</description>
|
||||
@@ -306,8 +299,8 @@
|
||||
<keyword>Matrix</keyword>
|
||||
<keyword>Kirigami</keyword>
|
||||
</keywords>
|
||||
<developer id="org.kde">
|
||||
<name translate="no">KDE</name>
|
||||
<developer id="kde.org">
|
||||
<name>The KDE Community</name>
|
||||
<url>https://kde.org</url>
|
||||
</developer>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
@@ -320,7 +313,7 @@
|
||||
<value key="KDE::windows_store::StoreLogoSquare">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/storelogo-1080x1080.png</value>
|
||||
<value key="KDE::windows_store::Icon">https://invent.kde.org/network/neochat/-/raw/master/icons/300-apps-neochat.png</value>
|
||||
<value key="KDE::windows_store::PromotionalArt16x9">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/promoimage-1920x1080.png</value>
|
||||
<value key="KDE::supporters">Anonymous donor, Akseli</value>
|
||||
<value key="KDE::supporters">Tanguy Fardet;[dabe](https://freeradical.zone/@dabe);[lengau](https://mastodon.world/@lengau);Joshua Strobl;Stuart Turton</value>
|
||||
</custom>
|
||||
<launchable type="desktop-id">org.kde.neochat.desktop</launchable>
|
||||
<screenshots>
|
||||
@@ -351,8 +344,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="ro">Vederea principală cu lista de camere, discuție, și informații despre cameră</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>
|
||||
@@ -360,6 +351,7 @@
|
||||
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
|
||||
<caption xml:lang="tr">Oda listesini, sohbet penceresini ve oda bilgisini gösteren ana görünüm</caption>
|
||||
<caption xml:lang="uk">Головна панель із списком кімнат, спілкуванням та даними щодо кімнати</caption>
|
||||
<caption xml:lang="x-test">xxMain view with room list, chat, and room informationxx</caption>
|
||||
<caption xml:lang="zh-TW">主頁面,包含聊天室列表、聊天內容,與聊天室資訊</caption>
|
||||
</screenshot>
|
||||
<screenshot type="default">
|
||||
@@ -388,8 +380,6 @@
|
||||
<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="ro">Descoperiți comunități noi cu Spații 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>
|
||||
@@ -397,6 +387,7 @@
|
||||
<caption xml:lang="ta">மேட்ரிக்ஸு இடங்களின் மூலம் புதிய சமூகங்களைக் கண்டுபிடிக்கலாம்</caption>
|
||||
<caption xml:lang="tr">Matrix Alanlar ile yeni topluluklar keşfedin</caption>
|
||||
<caption xml:lang="uk">Пошук нових спільнот за допомогою Matrix Spaces</caption>
|
||||
<caption xml:lang="x-test">xxDiscover new communities with Matrix Spacesxx</caption>
|
||||
<caption xml:lang="zh-TW">利用 Matrix 聊天空間發現新的社群</caption>
|
||||
</screenshot>
|
||||
<!--
|
||||
@@ -433,8 +424,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="ro">Vederea principală cu lista de camere, discuție, și informații despre cameră</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>
|
||||
@@ -442,6 +431,7 @@
|
||||
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
|
||||
<caption xml:lang="tr">Oda listesini, sohbet penceresini ve oda bilgisini gösteren ana görünüm</caption>
|
||||
<caption xml:lang="uk">Головна панель із списком кімнат, спілкуванням та даними щодо кімнати</caption>
|
||||
<caption xml:lang="x-test">xxMain view with room list, chat, and room informationxx</caption>
|
||||
<caption xml:lang="zh-TW">主頁面,包含聊天室列表、聊天內容,與聊天室資訊</caption>
|
||||
</screenshot>
|
||||
<screenshot environment="windows">
|
||||
@@ -472,8 +462,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="ro">Ecran de autentificare</caption>
|
||||
<caption xml:lang="ru">Окно входа</caption>
|
||||
<caption xml:lang="sa">लॉगिन् स्क्रीन</caption>
|
||||
<caption xml:lang="sl">Prijavni zaslon</caption>
|
||||
@@ -481,6 +469,7 @@
|
||||
<caption xml:lang="ta">நுழைவுத் திரை</caption>
|
||||
<caption xml:lang="tr">Oturum açma ekranı</caption>
|
||||
<caption xml:lang="uk">Вікно входу</caption>
|
||||
<caption xml:lang="x-test">xxLogin screenxx</caption>
|
||||
<caption xml:lang="zh-TW">登入畫面</caption>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
@@ -488,12 +477,6 @@
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release version="25.08.3" date="2025-11-06"/>
|
||||
<release version="25.08.2" date="2025-10-09"/>
|
||||
<release version="25.08.1" date="2025-09-11"/>
|
||||
<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"/>
|
||||
|
||||
@@ -44,6 +44,7 @@ Name[sv]=NeoChat
|
||||
Name[ta]=நியோச்சாட்
|
||||
Name[tr]=NeoChat
|
||||
Name[uk]=NeoChat
|
||||
Name[x-test]=xxNeoChatxx
|
||||
Name[zh_CN]=NeoChat
|
||||
Name[zh_TW]=NeoChat
|
||||
GenericName=Matrix Client
|
||||
@@ -87,6 +88,7 @@ GenericName[sv]=Matrix-klient
|
||||
GenericName[ta]=Matrix வாங்கி
|
||||
GenericName[tr]=Matrix İstemcisi
|
||||
GenericName[uk]=Клієнт Matrix
|
||||
GenericName[x-test]=xxMatrix Clientxx
|
||||
GenericName[zh_CN]=Matrix 客户端
|
||||
GenericName[zh_TW]=Matrix 用戶端
|
||||
Comment=Chat on Matrix
|
||||
@@ -112,7 +114,6 @@ 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[ro]=Discutați pe Matrix
|
||||
Comment[ru]=Общение в Matrix
|
||||
Comment[sa]=Matrix इत्यत्र गपशपं कुर्वन्तु
|
||||
Comment[sl]=Klepet na Matrixu
|
||||
@@ -120,6 +121,7 @@ Comment[sv]=Chatta på Matrix
|
||||
Comment[ta]=மேட்ரிக்ஸில் உரையாட உதவும்
|
||||
Comment[tr]=Matrix üzerinde sohbet edin
|
||||
Comment[uk]=Спілкування у Matrix
|
||||
Comment[x-test]=xxChat on Matrixxx
|
||||
Comment[zh_CN]=在 Matrix 上聊天
|
||||
Comment[zh_TW]=在 Matrix 上聊天
|
||||
MimeType=x-scheme-handler/matrix;
|
||||
|
||||
8377
po/ar/neochat.po
8377
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
7820
po/ast/neochat.po
7820
po/ast/neochat.po
File diff suppressed because it is too large
Load Diff
9334
po/az/neochat.po
9334
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
8341
po/ca/neochat.po
8341
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
8094
po/cs/neochat.po
8094
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
8874
po/da/neochat.po
8874
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
9198
po/de/neochat.po
9198
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
8925
po/el/neochat.po
8925
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
9121
po/en_GB/neochat.po
9121
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
8976
po/eo/neochat.po
8976
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
8186
po/es/neochat.po
8186
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
8552
po/eu/neochat.po
8552
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
8504
po/fi/neochat.po
8504
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
8561
po/fr/neochat.po
8561
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
8938
po/gl/neochat.po
8938
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
8336
po/he/neochat.po
8336
po/he/neochat.po
File diff suppressed because it is too large
Load Diff
9035
po/hi/neochat.po
9035
po/hi/neochat.po
File diff suppressed because it is too large
Load Diff
8928
po/hu/neochat.po
8928
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
8641
po/ia/neochat.po
8641
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
9223
po/id/neochat.po
9223
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
9017
po/ie/neochat.po
9017
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
8470
po/it/neochat.po
8470
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
7807
po/ja/neochat.po
7807
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
8378
po/ka/neochat.po
8378
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
8427
po/ko/neochat.po
8427
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
8364
po/lt/neochat.po
8364
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
8516
po/lv/neochat.po
8516
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
8456
po/nl/neochat.po
8456
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
8763
po/nn/neochat.po
8763
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
9273
po/pa/neochat.po
9273
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
8501
po/pl/neochat.po
8501
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
9252
po/pt/neochat.po
9252
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
10897
po/pt_BR/neochat.po
10897
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
7049
po/ro/neochat.po
7049
po/ro/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>
|
||||
9364
po/ru/neochat.po
9364
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
8995
po/sa/neochat.po
8995
po/sa/neochat.po
File diff suppressed because it is too large
Load Diff
9070
po/sk/neochat.po
9070
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
8439
po/sl/neochat.po
8439
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
8488
po/sv/neochat.po
8488
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
8742
po/ta/neochat.po
8742
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
8847
po/tok/neochat.po
8847
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
8457
po/tr/neochat.po
8457
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
8500
po/uk/neochat.po
8500
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
8016
po/zh_CN/neochat.po
8016
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
8380
po/zh_TW/neochat.po
8380
po/zh_TW/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -101,7 +101,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:
|
||||
|
||||
@@ -1,18 +1,580 @@
|
||||
# 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
|
||||
)
|
||||
|
||||
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
|
||||
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()
|
||||
|
||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
|
||||
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin devtoolsplugin loginplugin chatbarplugin)
|
||||
target_link_libraries(neochat PUBLIC
|
||||
Qt::Core
|
||||
Qt::Quick
|
||||
Qt::Qml
|
||||
Qt::Gui
|
||||
Qt::Multimedia
|
||||
Qt::Network
|
||||
Qt::QuickControls2
|
||||
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
|
||||
)
|
||||
|
||||
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,368 +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
|
||||
)
|
||||
else()
|
||||
set(EXTRA_IMPORTS org.kde.purpose)
|
||||
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
|
||||
qml/MeetingDialog.qml
|
||||
DEPENDENCIES
|
||||
QtCore
|
||||
QtQuick
|
||||
io.github.quotient_im.libquotient
|
||||
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
|
||||
org.kde.config
|
||||
org.kde.syntaxhighlighting
|
||||
${EXTRA_IMPORTS}
|
||||
)
|
||||
|
||||
if(NOT ANDROID AND NOT WIN32)
|
||||
qt_target_qml_sources(neochat QML_FILES
|
||||
qml/ShareAction.qml
|
||||
qml/GlobalMenu.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
|
||||
KF6::I18nQml
|
||||
KirigamiApp
|
||||
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"
|
||||
"amarok_playcount"
|
||||
|
||||
${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 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,382 +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 <KLocalizedString>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QTimer>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <Quotient/events/roommemberevent.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
#include <Quotient/settings.h>
|
||||
|
||||
#include "enums/roomsortparameter.h"
|
||||
#include "general_logging.h"
|
||||
#include "mediasizehelper.h"
|
||||
#include "models/actionsmodel.h"
|
||||
#include "models/messagemodel.h"
|
||||
#include "models/roomlistmodel.h"
|
||||
#include "models/roomtreemodel.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroom.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
|
||||
|
||||
#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
|
||||
|
||||
connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [this] {
|
||||
#ifndef Q_OS_ANDROID
|
||||
delete m_trayIcon;
|
||||
#endif
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
QStringList unsupportedPlatforms{u"GNOME"_s, u"Pantheon"_s};
|
||||
return !unsupportedPlatforms.contains(QString::fromLatin1(qgetenv("XDG_CURRENT_DESKTOP")));
|
||||
#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;
|
||||
}
|
||||
#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) {
|
||||
NotificationsManager::postPushNotification(data);
|
||||
});
|
||||
|
||||
// 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)
|
||||
{
|
||||
qGuiApp->setBadgeNumber(count);
|
||||
}
|
||||
|
||||
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());
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
qCWarning(GENERAL) << "Failed to open file" << path;
|
||||
return {};
|
||||
}
|
||||
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,108 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2021 Carson Black <uhhadd@gmail.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import Qt.labs.platform as Labs
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
|
||||
Labs.MenuBar {
|
||||
id: root
|
||||
|
||||
required property NeoChatConnection connection
|
||||
required property Kirigami.ApplicationWindow appWindow
|
||||
|
||||
Labs.Menu {
|
||||
title: i18nc("menu", "File")
|
||||
|
||||
Labs.MenuItem {
|
||||
icon.name: "list-add-user"
|
||||
text: i18nc("@action:inmenu", "Find User")
|
||||
enabled: root.connection
|
||||
onTriggered: root.appWindow.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18nc("@title", "Find User")
|
||||
})
|
||||
}
|
||||
Labs.MenuItem {
|
||||
icon.name: "system-users-symbolic"
|
||||
text: i18nc("@action:inmenu", "Create a Room…")
|
||||
enabled: root.connection
|
||||
shortcut: StandardKey.New
|
||||
onTriggered: {
|
||||
Qt.createComponent('org.kde.neochat', 'CreateRoomDialog').createObject(root.appWindow, {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18nc("@title", "Create a Room")
|
||||
}).open();
|
||||
}
|
||||
}
|
||||
Labs.MenuItem {
|
||||
icon.name: "compass-symbolic"
|
||||
text: i18nc("@action:inmenu", "Explore Rooms")
|
||||
enabled: root.connection
|
||||
onTriggered: {
|
||||
let dialog = root.appWindow.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18nc("@title", "Explore Rooms")
|
||||
});
|
||||
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
RoomManager.resolveResource(roomId.length > 0 ? roomId : alias, isJoined ? "" : "join");
|
||||
});
|
||||
}
|
||||
}
|
||||
Labs.MenuItem {
|
||||
text: i18nc("menu", "Configure NeoChat…")
|
||||
|
||||
shortcut: StandardKey.Preferences
|
||||
onTriggered: NeoChatSettingsView.open()
|
||||
}
|
||||
Labs.MenuItem {
|
||||
text: i18nc("menu", "Quit NeoChat")
|
||||
|
||||
shortcut: StandardKey.Quit
|
||||
onTriggered: Qt.quit()
|
||||
}
|
||||
}
|
||||
|
||||
Labs.Menu {
|
||||
title: i18nc("menu", "View")
|
||||
|
||||
Labs.MenuItem {
|
||||
icon.name: "search-symbolic"
|
||||
enabled: root.connection
|
||||
text: i18nc("@action:inmenu opens a UI element called the 'Quick Switcher', which offers a fast keyboard-based interface for switching in between chats.", "Search Rooms")
|
||||
onTriggered: (root.appWindow as Main).quickSwitcher.open()
|
||||
}
|
||||
}
|
||||
Labs.Menu {
|
||||
title: i18nc("menu", "Window")
|
||||
|
||||
Labs.MenuItem {
|
||||
icon.name: "view-fullscreen-symbolic"
|
||||
text: root.appWindow.visibility === Window.FullScreen ? i18nc("menu", "Exit Full Screen") : i18nc("menu", "Enter Full Screen")
|
||||
onTriggered: root.appWindow.visibility === Window.FullScreen ? root.appWindow.showNormal() : root.appWindow.showFullScreen()
|
||||
}
|
||||
}
|
||||
Labs.Menu {
|
||||
title: i18nc("menu", "Help")
|
||||
|
||||
Labs.MenuItem {
|
||||
icon.name: "help-about-symbolic"
|
||||
text: i18nc("menu", "About NeoChat")
|
||||
onTriggered: root.appWindow.pageStack.pushDialogLayer(Qt.createComponent("org.kde.kirigamiaddons.formcard", "AboutPage"))
|
||||
}
|
||||
Labs.MenuItem {
|
||||
icon.name: "kde-symbolic"
|
||||
text: i18nc("menu", "About KDE")
|
||||
onTriggered: root.appWindow.pageStack.pushDialogLayer(Qt.createComponent("org.kde.kirigamiaddons.formcard", "AboutKDEPage"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.neochat
|
||||
|
||||
Item {
|
||||
required property NeoChatConnection connection
|
||||
required property Kirigami.ApplicationWindow appWindow
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-FileCopyrightText: 2024 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls as QQC2
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
property string text
|
||||
|
||||
onTextChanged: {
|
||||
// This is done so the text doesn't disappear for a split second while in the opacity transition
|
||||
if (root.text.length > 0) {
|
||||
urlLabel.text = root.text
|
||||
}
|
||||
}
|
||||
|
||||
z: 99
|
||||
spacing: 0
|
||||
|
||||
opacity: (!root.text.startsWith("https://matrix.to/") && root.text.length > 0) ? 1 : 0
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
OpacityAnimator {
|
||||
duration: Kirigami.Units.shortDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Control {
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
|
||||
Accessible.ignored: true
|
||||
|
||||
contentItem: QQC2.Label {
|
||||
id: urlLabel
|
||||
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
background: Kirigami.ShadowedRectangle {
|
||||
corners.topRightRadius: Kirigami.Units.cornerRadius
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
border {
|
||||
color: Kirigami.ColorUtils.linearInterpolation(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, Kirigami.Theme.frameContrast)
|
||||
width: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,201 +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
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
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.SelectableLabel {
|
||||
Layout.fillWidth: true
|
||||
font: Kirigami.Theme.smallFont
|
||||
textFormat: TextEdit.PlainText
|
||||
visible: root.currentRoom && root.currentRoom.canonicalAlias
|
||||
text: root.currentRoom && root.currentRoom.canonicalAlias ? root.currentRoom.canonicalAlias : ""
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
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.invitingMember.displayName
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
text: root.invitingMember.id
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
|
||||
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: xi18nc("@info:label Ensure you are referring to the same translation used for that settings page", "You can reject invitations from unknown users under the <interface>Security & Safety</interface> settings.")
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
// + 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,22 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
import QtQuick
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
Kirigami.PromptDialog {
|
||||
id: root
|
||||
|
||||
required property bool hasExistingMeeting
|
||||
|
||||
title: hasExistingMeeting ? i18nc("@title", "Join Meeting") : i18nc("@title", "Start Meeting")
|
||||
subtitle: hasExistingMeeting ? i18nc("@info:label", "You are about to join a Jitsi meeting in your web browser.") : i18nc("@info:label", "You are about to start a new Jitsi meeting in your web browser.")
|
||||
standardButtons: Kirigami.Dialog.Cancel
|
||||
|
||||
customFooterActions: Kirigami.Action {
|
||||
icon.name: "camera-video-symbolic"
|
||||
text: hasExistingMeeting ? i18nc("@action:button Join the Jitsi meeting", "Join") : i18nc("@action:button Start a new Jitsi meeting", "Start")
|
||||
onTriggered: root.accept()
|
||||
}
|
||||
}
|
||||
@@ -1,151 +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
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
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.delegates as Delegates
|
||||
|
||||
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(QQC2.ApplicationWindow.window.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: i18nc("@label", "Poll type:")
|
||||
currentIndex: 0
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
model: [
|
||||
{ value: PollKind.Disclosed, text: i18nc("@item:inlistbox", "Open poll") },
|
||||
{ value: PollKind.Undisclosed, text: i18nc("@item:inlistbox", "Closed poll") }
|
||||
]
|
||||
}
|
||||
FormCard.FormTextDelegate {
|
||||
verticalPadding: 0
|
||||
text: pollTypeCombo.currentValue == 0 ? i18nc("@info", "Voters can see the result as soon as they have voted") : i18nc("@info", "Results are revealed only after the poll has closed")
|
||||
}
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: questionTextField
|
||||
label: i18nc("@label", "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: i18nc("@placeholder", "Enter option")
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
text: i18nc("@action:button", "Remove option")
|
||||
icon.name: "edit-delete-remove"
|
||||
onClicked: optionModel.remove(optionDelegate.index)
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
QQC2.ToolTip.visible: hovered
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,399 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2018-2020 Black Hat <bhat@encom.eu.org>
|
||||
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Window
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Kirigami.Page {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* @brief The NeoChatRoom the delegate is being displayed in.
|
||||
*/
|
||||
readonly property NeoChatRoom currentRoom: RoomManager.currentRoom
|
||||
|
||||
/**
|
||||
* @brief The TimelineModel to use.
|
||||
*
|
||||
* Required so that new events can be requested when the end of the current
|
||||
* local timeline is reached.
|
||||
*
|
||||
* @note For loading a room in a different window, override this with a new
|
||||
* TimelineModel set with the room to be shown.
|
||||
*
|
||||
* @sa TimelineModel
|
||||
*/
|
||||
property TimelineModel timelineModel: RoomManager.timelineModel
|
||||
|
||||
/**
|
||||
* @brief The MessageFilterModel to use.
|
||||
*
|
||||
* This model has the filtered list of events that should be shown in the timeline.
|
||||
*
|
||||
* @note For loading a room in a different window, override this with a new
|
||||
* MessageFilterModel with the new TimelineModel as the source model.
|
||||
*
|
||||
* @sa TimelineModel, MessageFilterModel
|
||||
*/
|
||||
property MessageFilterModel messageFilterModel: RoomManager.messageFilterModel
|
||||
|
||||
/**
|
||||
* @brief The MediaMessageFilterModel to use.
|
||||
*
|
||||
* This model has the filtered list of media events that should be shown in
|
||||
* the timeline.
|
||||
*
|
||||
* @note For loading a room in a different window, override this with a new
|
||||
* MediaMessageFilterModel with the new MessageFilterModel as the source model.
|
||||
*
|
||||
* @sa TimelineModel, MessageFilterModel
|
||||
*/
|
||||
property MediaMessageFilterModel mediaMessageFilterModel: RoomManager.mediaMessageFilterModel
|
||||
|
||||
/**
|
||||
* @brief The WidgetModel to use.
|
||||
*
|
||||
* This model has the list of widgets available in the current room.
|
||||
*
|
||||
* @note For loading a room in a different window, override this with a new
|
||||
* WidgetModel.
|
||||
*
|
||||
* @sa WidgetModel
|
||||
*/
|
||||
property WidgetModel widgetModel: RoomManager.widgetModel
|
||||
|
||||
title: root.currentRoom ? root.currentRoom.displayName : ""
|
||||
focus: true
|
||||
padding: 0
|
||||
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
id: jitsiMeetingAction
|
||||
|
||||
readonly property bool hasExistingMeeting: root.widgetModel.jitsiIndex >= 0
|
||||
readonly property bool canStartNewMeeting: root.currentRoom.canSendState("im.vector.modular.widgets")
|
||||
|
||||
tooltip: {
|
||||
if (hasExistingMeeting) {
|
||||
return i18nc("@action:button", "Join Jitsi meeting…");
|
||||
}
|
||||
|
||||
return canStartNewMeeting ? i18nc("@action:button", "Start Jitsi meeting…") : i18nc("@action:button", "You do not have permissions to start Jitsi meetings")
|
||||
}
|
||||
icon {
|
||||
name: "camera-video-symbolic"
|
||||
color: hasExistingMeeting ? Kirigami.Theme.highlightColor : "transparent"
|
||||
}
|
||||
enabled: hasExistingMeeting || canStartNewMeeting
|
||||
visible: root.currentRoom && !root.currentRoom.isSpace
|
||||
onTriggered: {
|
||||
const dialog = Qt.createComponent("org.kde.neochat", "MeetingDialog").createObject(QQC2.Overlay.overlay, { hasExistingMeeting });
|
||||
dialog.onAccepted.connect(doAction);
|
||||
dialog.open();
|
||||
}
|
||||
|
||||
function doAction(): void {
|
||||
let url;
|
||||
if (!hasExistingMeeting) {
|
||||
url = root.widgetModel.addJitsiConference();
|
||||
} else {
|
||||
let idx = root.widgetModel.index(root.widgetModel.jitsiIndex, 0);
|
||||
url = root.widgetModel.data(idx, WidgetModel.UrlRole);
|
||||
}
|
||||
Qt.openUrlExternally(url);
|
||||
}
|
||||
},
|
||||
Kirigami.Action {
|
||||
visible: Kirigami.Settings.isMobile || !(root.Kirigami.PageStack.pageStack as Kirigami.PageRow).wideMode
|
||||
icon.name: "view-right-new"
|
||||
onTriggered: (root.QQC2.ApplicationWindow.window as Main).openRoomDrawer()
|
||||
}
|
||||
]
|
||||
|
||||
Kirigami.Action {
|
||||
enabled: root.currentRoom && !root.currentRoom.isSpace
|
||||
shortcut: "Ctrl+F"
|
||||
onTriggered: {
|
||||
((root.QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'RoomSearchPage'), {
|
||||
room: root.currentRoom
|
||||
}, {
|
||||
title: i18nc("@action:title", "Search")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigation.left: (root.Kirigami.PageStack.pageStack as Kirigami.PageRow).get(0)
|
||||
|
||||
onCurrentRoomChanged: {
|
||||
if (!Kirigami.Settings.isMobile && chatBarLoader.item) {
|
||||
(chatBarLoader.item as ChatBar).forceActiveFocus();
|
||||
}
|
||||
|
||||
if (root.currentRoom.tagNames.includes("m.server_notice")) {
|
||||
banner.text = i18nc("@info", "This room contains official messages from your homeserver.")
|
||||
banner.show("message");
|
||||
} else {
|
||||
banner.hideIf("message");
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.currentRoom.connection
|
||||
function onIsOnlineChanged() {
|
||||
if (!root.currentRoom.connection.isOnline) {
|
||||
banner.text = i18nc("@info:status", "NeoChat is offline. Please check your network connection.");
|
||||
banner.type = Kirigami.MessageType.Error;
|
||||
banner.show("offline");
|
||||
} else {
|
||||
banner.hideIf("offline");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header: ColumnLayout {
|
||||
id: headerLayout
|
||||
|
||||
spacing: 0
|
||||
|
||||
readonly property bool shouldShowPins: root.currentRoom.pinnedMessage.length > 0 && !Kirigami.Settings.isMobile
|
||||
|
||||
QQC2.Control {
|
||||
id: pinControl
|
||||
|
||||
visible: headerLayout.shouldShowPins
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
background: Rectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Kirigami.Icon {
|
||||
source: "pin-symbolic"
|
||||
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
text: root.currentRoom.pinnedMessage
|
||||
maximumLineCount: 1
|
||||
elide: Text.ElideRight
|
||||
|
||||
onLinkActivated: link => UrlHelper.openUrl(link)
|
||||
onHoveredLinkChanged: if (hoveredLink.length > 0 && hoveredLink !== "1") {
|
||||
(QQC2.ApplicationWindow.window as Main).hoverLinkIndicator.text = hoveredLink;
|
||||
} else {
|
||||
(QQC2.ApplicationWindow.window as Main).hoverLinkIndicator.text = "";
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
onTapped: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'RoomPinnedMessagesPage'), {
|
||||
room: root.currentRoom
|
||||
}, {
|
||||
title: i18nc("@title", "Pinned Messages")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
visible: headerLayout.shouldShowPins
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
id: banner
|
||||
|
||||
// Used to keep track of messages so we can hide the right one at the right time
|
||||
property string messageId
|
||||
|
||||
showCloseButton: true
|
||||
visible: false
|
||||
position: Kirigami.InlineMessage.Position.Header
|
||||
|
||||
function show(msgid: string): void {
|
||||
messageId = msgid;
|
||||
visible = true;
|
||||
}
|
||||
|
||||
function hideIf(msgid: string): void {
|
||||
if (messageId == msgid) {
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: timelineViewLoader
|
||||
anchors.fill: parent
|
||||
// We need the loader to be active but invisible while the room is loading messages so signals in TimelineView work.
|
||||
active: root.currentRoom && !root.currentRoom.isInvite && !root.currentRoom.isSpace
|
||||
sourceComponent: TimelineView {
|
||||
id: timelineView
|
||||
messageFilterModel: root.messageFilterModel
|
||||
compactLayout: NeoChatConfig.compactLayout
|
||||
fileDropEnabled: !Controller.isFlatpak
|
||||
markReadCondition: NeoChatConfig.markReadCondition
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: invitationLoader
|
||||
active: root.currentRoom && root.currentRoom.isInvite
|
||||
anchors.fill: parent
|
||||
sourceComponent: InvitationView {
|
||||
currentRoom: root.currentRoom
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: spaceLoader
|
||||
active: root.currentRoom && root.currentRoom.isSpace
|
||||
anchors.fill: parent
|
||||
sourceComponent: SpaceHomePage {
|
||||
room: root.currentRoom
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: !RoomManager.currentRoom
|
||||
anchors.centerIn: parent
|
||||
sourceComponent: Kirigami.PlaceholderMessage {
|
||||
icon.name: "org.kde.neochat"
|
||||
text: i18nc("@title", "Welcome to NeoChat")
|
||||
explanation: i18nc("@info:usagetip", "Select or join a room to get started")
|
||||
}
|
||||
}
|
||||
|
||||
footer: Loader {
|
||||
id: chatBarLoader
|
||||
height: active ? (item as ChatBar).implicitHeight : 0
|
||||
active: timelineViewLoader.active && !root.currentRoom.readOnly
|
||||
sourceComponent: ChatBar {
|
||||
id: chatBar
|
||||
width: parent.width
|
||||
currentRoom: root.currentRoom
|
||||
connection: root.currentRoom.connection as NeoChatConnection
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: RoomManager
|
||||
function onCurrentRoomChanged() {
|
||||
if (root.currentRoom && root.currentRoom.isInvite) {
|
||||
Controller.clearInvitationNotification(root.currentRoom.id);
|
||||
}
|
||||
}
|
||||
|
||||
function onGoToEvent(eventId) {
|
||||
(timelineViewLoader.item as TimelineView).goToEvent(eventId);
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.currentRoom.connection
|
||||
function onJoinedRoom(room, invited) {
|
||||
if (root.currentRoom.id === invited.id) {
|
||||
RoomManager.resolveResource(room.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_PageUp) {
|
||||
event.accepted = true;
|
||||
(timelineViewLoader.item as TimelineView).pageUp();
|
||||
} else if (event.key === Qt.Key_PageDown) {
|
||||
event.accepted = true;
|
||||
(timelineViewLoader.item as TimelineView).pageDown();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: RoomManager
|
||||
|
||||
function onShowMessage(messageType, message) {
|
||||
banner.text = message;
|
||||
banner.type = messageType;
|
||||
banner.show("generic");
|
||||
}
|
||||
|
||||
function onShowEventSource(eventId) {
|
||||
(root.Kirigami.PageStack.pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
sourceText: root.currentRoom.getEventJsonSource(eventId)
|
||||
}, {
|
||||
title: i18nc("@title:dialog", "Message Source"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
});
|
||||
}
|
||||
|
||||
function onShowDelegateMenu(eventId: string, author, messageComponentType, plainText: string, richText: string, mimeType: string, progressInfo, isThread: bool, selectedText: string, hoveredLink: string) {
|
||||
(delegateContextMenu.createObject(root, {
|
||||
author: author,
|
||||
eventId: eventId,
|
||||
plainText: plainText,
|
||||
mimeType: mimeType,
|
||||
progressInfo: progressInfo,
|
||||
messageComponentType: messageComponentType,
|
||||
}) as DelegateContextMenu).popup();
|
||||
}
|
||||
|
||||
function onShowMaximizedMedia(index) {
|
||||
var popup = maximizeComponent.createObject(QQC2.Overlay.overlay, {
|
||||
initialIndex: index
|
||||
});
|
||||
popup.closed.connect(() => {
|
||||
timelineViewLoader.item.interactive = true;
|
||||
popup.destroy();
|
||||
});
|
||||
popup.open();
|
||||
}
|
||||
|
||||
function onShowMaximizedCode(author, time, codeText, language) {
|
||||
(Qt.createComponent('org.kde.neochat', 'CodeMaximizeComponent').createObject(QQC2.Overlay.overlay, {
|
||||
author: author,
|
||||
time: time,
|
||||
codeText: codeText,
|
||||
language: language
|
||||
}) as CodeMaximizeComponent).open();
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: delegateContextMenu
|
||||
DelegateContextMenu {
|
||||
room: root.currentRoom
|
||||
connection: root.currentRoom.connection as NeoChatConnection
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: maximizeComponent
|
||||
NeochatMaximizeComponent {
|
||||
currentRoom: root.currentRoom
|
||||
model: root.mediaMessageFilterModel
|
||||
parent: root.QQC2.Overlay.overlay
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
|
||||
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
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.prison
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Kirigami.Dialog {
|
||||
id: root
|
||||
|
||||
// This dialog is sometimes used outside the context of a room, e.g., when scanning a user's QR code.
|
||||
// Make sure that code is prepared to deal with this property being null
|
||||
property NeoChatRoom room
|
||||
property var user
|
||||
|
||||
property NeoChatConnection connection
|
||||
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
standardButtons: Kirigami.Dialog.NoButton
|
||||
|
||||
width: Math.min(QQC2.ApplicationWindow.window.width, Kirigami.Units.gridUnit * 24)
|
||||
title: i18nc("@title:menu Account details dialog", "Account Details")
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 0
|
||||
RowLayout {
|
||||
id: detailRow
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
KirigamiComponents.Avatar {
|
||||
id: avatar
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
|
||||
|
||||
name: root.room ? root.room.member(root.user.id).displayName : root.user.displayName
|
||||
source: root.room ? root.room.member(root.user.id).avatarUrl : root.user.avatarUrl
|
||||
color: root.room ? root.room.member(root.user.id).color : QmlUtils.getUserColor(root.user.hueF)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: 0
|
||||
|
||||
Kirigami.Heading {
|
||||
level: 1
|
||||
Layout.fillWidth: true
|
||||
font.bold: true
|
||||
clip: true // Intentional to limit insane Unicode in display names
|
||||
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
text: root.room ? root.room.member(root.user.id).displayName : root.user.displayName
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
|
||||
Kirigami.SelectableLabel {
|
||||
id: idLabel
|
||||
textFormat: TextEdit.PlainText
|
||||
text: idLabelTextMetrics.elidedText
|
||||
|
||||
TextMetrics {
|
||||
id: idLabelTextMetrics
|
||||
text: root.user.id
|
||||
elide: Qt.ElideRight
|
||||
elideWidth: root.availableWidth - avatar.width - qrButton.width - detailRow.spacing * 2 - detailRow.Layout.leftMargin - detailRow.Layout.rightMargin
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
property CommonRoomsModel model: CommonRoomsModel {
|
||||
connection: root.connection
|
||||
userId: root.user.id
|
||||
}
|
||||
|
||||
text: i18ncp("@info", "One mutual room", "%1 mutual rooms", model.count)
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
visible: model.count > 0
|
||||
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
}
|
||||
}
|
||||
QQC2.AbstractButton {
|
||||
id: qrButton
|
||||
Layout.minimumHeight: avatar.height * 0.75
|
||||
Layout.maximumHeight: avatar.height * 1.5
|
||||
Layout.maximumWidth: avatar.height * 1.5
|
||||
|
||||
contentItem: Barcode {
|
||||
id: barcode
|
||||
barcodeType: Barcode.QRCode
|
||||
content: "https://matrix.to/#/" + root.user.id
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
let qrCode = Qt.createComponent('org.kde.neochat', 'QrCodeMaximizeComponent').createObject(QQC2.Overlay.overlay, {
|
||||
text: barcode.content,
|
||||
title: root.room ? root.room.member(root.user.id).displayName : root.user.displayName,
|
||||
subtitle: root.user.id,
|
||||
avatarColor: root.room?.member(root.user.id).color,
|
||||
avatarSource: root.room? root.room.member(root.user.id).avatarUrl : root.user.avatarUrl
|
||||
}) as QrCodeMaximizeComponent;
|
||||
root.close();
|
||||
qrCode.open();
|
||||
}
|
||||
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.text: barcode.content
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Chip {
|
||||
visible: root.room
|
||||
text: root.room ? QmlUtils.nameForPowerLevelValue(root.room.memberEffectivePowerLevel(root.user.id)) : ""
|
||||
closable: false
|
||||
checkable: false
|
||||
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: root.user.id !== root.connection.localUserId && !!root.user
|
||||
text: !!root.user && root.connection.isIgnored(root.user.id) ? i18n("Unignore this user") : i18n("Ignore this user")
|
||||
icon.name: "im-invisible-user"
|
||||
onClicked: {
|
||||
root.close();
|
||||
root.connection.isIgnored(root.user.id) ? root.connection.removeFromIgnoredUsers(root.user.id) : root.connection.addToIgnoredUsers(root.user.id);
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: root.room && root.user.id !== root.connection.localUserId && root.room.canSendState("kick") && root.room.containsUser(root.user.id) && root.room.memberEffectivePowerLevel(root.user.id) < root.room.memberEffectivePowerLevel(root.connection.localUserId)
|
||||
|
||||
text: i18nc("@action:button", "Kick this user")
|
||||
icon.name: "im-kick-user"
|
||||
onClicked: {
|
||||
let dialog = (root.QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ReasonDialog'), {
|
||||
title: i18nc("@title:dialog", "Kick User"),
|
||||
placeholder: i18nc("@info:placeholder", "Reason for kicking this user"),
|
||||
actionText: i18nc("@action:button 'Kick' as in 'Kick this user from the room'", "Kick"),
|
||||
icon: "im-kick-user"
|
||||
}, {
|
||||
title: i18nc("@title:dialog", "Kick User"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
});
|
||||
dialog.accepted.connect(reason => {
|
||||
root.room.kickMember(root.user.id, reason);
|
||||
});
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: root.room && root.user.id !== root.connection.localUserId && root.room.canSendState("invite") && !root.room.containsUser(root.user.id)
|
||||
|
||||
enabled: root.room && !root.room.isUserBanned(root.user.id)
|
||||
text: i18nc("@action:button", "Invite this user")
|
||||
icon.name: "list-add-user"
|
||||
onClicked: {
|
||||
root.room.inviteToRoom(root.user.id);
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: root.room && root.user.id !== root.connection.localUserId && root.room.canSendState("ban") && !root.room.isUserBanned(root.user.id) && root.room.memberEffectivePowerLevel(root.user.id) < root.room.memberEffectivePowerLevel(root.connection.localUserId)
|
||||
|
||||
text: i18nc("@action:button", "Ban this user")
|
||||
icon.name: "im-ban-user"
|
||||
icon.color: Kirigami.Theme.negativeTextColor
|
||||
onClicked: {
|
||||
let dialog = (root.QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ReasonDialog'), {
|
||||
title: i18nc("@title:dialog", "Ban User"),
|
||||
placeholder: i18nc("@info:placeholder", "Reason for banning this user"),
|
||||
actionText: i18nc("@action:button 'Ban' as in 'Ban this user'", "Ban"),
|
||||
icon: "im-ban-user"
|
||||
}, {
|
||||
title: i18nc("@title:dialog", "Ban User"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
});
|
||||
dialog.accepted.connect(reason => {
|
||||
root.room.ban(root.user.id, reason);
|
||||
});
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: root.room && root.user.id !== root.connection.localUserId && root.room.canSendState("ban") && root.room.isUserBanned(root.user.id)
|
||||
|
||||
text: i18nc("@action:button", "Unban this user")
|
||||
icon.name: "im-irc"
|
||||
icon.color: Kirigami.Theme.negativeTextColor
|
||||
onClicked: {
|
||||
root.room.unban(root.user.id);
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: root.room && root.room.canSendState("m.room.power_levels")
|
||||
text: i18nc("@action:button", "Set user power level")
|
||||
icon.name: "visibility"
|
||||
onClicked: {
|
||||
(powerLevelDialog.createObject(this, {
|
||||
room: root.room,
|
||||
userId: root.user.id,
|
||||
powerLevel: root.room.memberEffectivePowerLevel(root.user.id)
|
||||
}) as PowerLevelDialog).open();
|
||||
root.close();
|
||||
}
|
||||
|
||||
Component {
|
||||
id: powerLevelDialog
|
||||
PowerLevelDialog {
|
||||
id: powerLevelDialog
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: root.room && (root.user.id === root.connection.localUserId || root.room.canSendState("redact"))
|
||||
|
||||
text: i18nc("@action:button", "Remove recent messages by this user")
|
||||
icon.name: "delete"
|
||||
icon.color: Kirigami.Theme.negativeTextColor
|
||||
onClicked: {
|
||||
let dialog = ((QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ReasonDialog'), {
|
||||
title: i18nc("@title:dialog", "Remove Messages"),
|
||||
placeholder: i18nc("@info:placeholder", "Reason for removing this user's recent messages"),
|
||||
actionText: i18nc("@action:button 'Remove' as in 'Remove these messages'", "Remove"),
|
||||
icon: "delete"
|
||||
}, {
|
||||
title: i18nc("@title", "Remove Messages"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
});
|
||||
dialog.accepted.connect(reason => {
|
||||
root.room.deleteMessagesByUser(root.user.id, reason);
|
||||
});
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: root.user.id !== root.connection.localUserId
|
||||
text: root.connection.directChatExists(root.user) ? i18nc("%1 is the name of the user.", "Chat with %1", root.room ? root.room.member(root.user.id).htmlSafeDisplayName : QmlUtils.escapeString(root.user.displayName)) : i18n("Invite to private chat")
|
||||
icon.name: "document-send"
|
||||
onClicked: {
|
||||
root.connection.requestDirectChat(root.user.id);
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18nc("@action:button %1 is the name of the user.", "Search room for %1's messages", root.room ? root.room.member(root.user.id).htmlSafeDisplayName : QmlUtils.escapeString(root.user.displayName))
|
||||
icon.name: "search-symbolic"
|
||||
onClicked: {
|
||||
((QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'RoomSearchPage'), {
|
||||
room: root.room,
|
||||
senderId: root.user.id
|
||||
}, {
|
||||
title: i18nc("@action:title", "Search")
|
||||
});
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18n("Copy link")
|
||||
icon.name: "username-copy"
|
||||
onClicked: Clipboard.saveText("https://matrix.to/#/" + root.user.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user