Compare commits
102 Commits
work/redst
...
v25.08.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ea6e0425a | ||
|
|
fe55ff19d2 | ||
|
|
c35bdc0592 | ||
|
|
c6fa5a10dd | ||
|
|
f956c33b82 | ||
|
|
0b9295e67e | ||
|
|
0fdade83e0 | ||
|
|
6a25945131 | ||
|
|
218222bc58 | ||
|
|
4300bba804 | ||
|
|
13f766d166 | ||
|
|
fd4e701c51 | ||
|
|
d8ff639374 | ||
|
|
4a50281152 | ||
|
|
acc9289d06 | ||
|
|
235bd21eaf | ||
|
|
c7c1c8fd5c | ||
|
|
fdbee5a508 | ||
|
|
fb58003451 | ||
|
|
debbe8e478 | ||
|
|
0ce86e5a08 | ||
|
|
6935d887c4 | ||
|
|
963346e0f4 | ||
|
|
e37dd88c43 | ||
|
|
2aacb640c8 | ||
|
|
2d63a92702 | ||
|
|
de6731cfda | ||
|
|
7b3c40757c | ||
|
|
87f243ba8b | ||
|
|
192cdc1ff3 | ||
|
|
c72f77f7b6 | ||
|
|
d978f8de50 | ||
|
|
cf216268ab | ||
|
|
f9741a66c4 | ||
|
|
aac3bfda88 | ||
|
|
2722a6f2f0 | ||
|
|
ecf4b85f00 | ||
|
|
ec1413d1ce | ||
|
|
0b7a6df0a3 | ||
|
|
040efa46f9 | ||
|
|
ef4b41e6f8 | ||
|
|
80f81847f4 | ||
|
|
681a3c4036 | ||
|
|
fd27c70b85 | ||
|
|
435124ffe8 | ||
|
|
e2ca698389 | ||
|
|
7e9cfbedc9 | ||
|
|
eeed8a7277 | ||
|
|
aa8c515432 | ||
|
|
ba30014d40 | ||
|
|
d131030d47 | ||
|
|
23a0d91627 | ||
|
|
11d5a37ffe | ||
|
|
256a8e5a1e | ||
|
|
b608921d43 | ||
|
|
45a3984bf9 | ||
|
|
570e0425e9 | ||
|
|
09ed1bd616 | ||
|
|
3201426886 | ||
|
|
96dc83d807 | ||
|
|
ab1fb8ae97 | ||
|
|
c5a4b2a50a | ||
|
|
17fdad3afd | ||
|
|
64bc2691cb | ||
|
|
c7df34a9c8 | ||
|
|
1525c74b10 | ||
|
|
58b85622c5 | ||
|
|
796470d0e0 | ||
|
|
3a43d99575 | ||
|
|
a62798ef1e | ||
|
|
349d0c5f5f | ||
|
|
c917fc0166 | ||
|
|
a4f2d8fca1 | ||
|
|
fd18f88adf | ||
|
|
546694b08e | ||
|
|
699026fc2f | ||
|
|
c61c2d7437 | ||
|
|
56babbc1c5 | ||
|
|
74b3e703c1 | ||
|
|
f620221113 | ||
|
|
896c001430 | ||
|
|
defee77c96 | ||
|
|
4328ab8e89 | ||
|
|
54b081abba | ||
|
|
29686608e1 | ||
|
|
b720ecf29d | ||
|
|
fc859d679a | ||
|
|
3595ad9293 | ||
|
|
73f8ebc54e | ||
|
|
19cf534acd | ||
|
|
9b86088e26 | ||
|
|
a93117fcd6 | ||
|
|
ee20c90498 | ||
|
|
860a2267d5 | ||
|
|
cb9b2648ca | ||
|
|
1e798b6c15 | ||
|
|
124ffba5e0 | ||
|
|
3aaaa610df | ||
|
|
18e883834c | ||
|
|
6acbd2dffd | ||
|
|
dc5c27aa2d | ||
|
|
26774bbe56 |
@@ -1,2 +0,0 @@
|
|||||||
[General]
|
|
||||||
disableUnqualifiedAccess = "i18nc,xi18nc,i18ncp,i18n"
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
"id": "org.kde.neochat",
|
"id": "org.kde.neochat",
|
||||||
"branch": "master",
|
"branch": "master",
|
||||||
"runtime": "org.kde.Platform",
|
"runtime": "org.kde.Platform",
|
||||||
"runtime-version": "6.10",
|
"runtime-version": "6.9",
|
||||||
"sdk": "org.kde.Sdk",
|
"sdk": "org.kde.Sdk",
|
||||||
"command": "neochat",
|
"command": "neochat",
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -20,32 +20,21 @@
|
|||||||
"--talk-name=org.kde.kwalletd5",
|
"--talk-name=org.kde.kwalletd5",
|
||||||
"--talk-name=org.kde.StatusNotifierWatcher",
|
"--talk-name=org.kde.StatusNotifierWatcher",
|
||||||
"--talk-name=org.freedesktop.secrets",
|
"--talk-name=org.freedesktop.secrets",
|
||||||
"--talk-name=org.kde.kuiserver",
|
|
||||||
"--own-name=org.kde.StatusNotifierItem-2-2"
|
"--own-name=org.kde.StatusNotifierItem-2-2"
|
||||||
],
|
],
|
||||||
"cleanup": [
|
|
||||||
"/include",
|
|
||||||
"/lib/*.a",
|
|
||||||
"/lib/cmake",
|
|
||||||
"/lib/pkgconfig",
|
|
||||||
"/share/ndk-modules"
|
|
||||||
],
|
|
||||||
"modules": [
|
"modules": [
|
||||||
{
|
{
|
||||||
"name": "opencv",
|
"name": "kirigamiaddons",
|
||||||
"config-opts": [
|
"config-opts": [
|
||||||
"-DBUILD_TESTS=OFF",
|
"-DBUILD_TESTING=OFF"
|
||||||
"-DWITH_GTK=OFF",
|
|
||||||
"-DBUILD_LIST=core,imgproc"
|
|
||||||
],
|
],
|
||||||
"buildsystem": "cmake-ninja",
|
"buildsystem": "cmake-ninja",
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/opencv/opencv"
|
"url": "https://invent.kde.org/libraries/kirigami-addons.git"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"builddir": true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "kquickimageeditor",
|
"name": "kquickimageeditor",
|
||||||
@@ -65,7 +54,6 @@
|
|||||||
"name": "olm",
|
"name": "olm",
|
||||||
"buildsystem": "cmake-ninja",
|
"buildsystem": "cmake-ninja",
|
||||||
"config-opts": [
|
"config-opts": [
|
||||||
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
|
||||||
"-DOLM_TESTS=OFF"
|
"-DOLM_TESTS=OFF"
|
||||||
],
|
],
|
||||||
"sources": [
|
"sources": [
|
||||||
@@ -94,8 +82,8 @@
|
|||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "archive",
|
"type": "archive",
|
||||||
"url": "https://download.gnome.org/sources/libsecret/0.21/libsecret-0.21.7.tar.xz",
|
"url": "https://download.gnome.org/sources/libsecret/0.21/libsecret-0.21.6.tar.xz",
|
||||||
"sha256": "6b452e4750590a2b5617adc40026f28d2f4903de15f1250e1d1c40bfd68ed55e",
|
"sha256": "747b8c175be108c880d3adfb9c3537ea66e520e4ad2dccf5dce58003aeeca090",
|
||||||
"x-checker-data": {
|
"x-checker-data": {
|
||||||
"type": "gnome",
|
"type": "gnome",
|
||||||
"name": "libsecret",
|
"name": "libsecret",
|
||||||
@@ -165,20 +153,16 @@
|
|||||||
"name": "kunifiedpush",
|
"name": "kunifiedpush",
|
||||||
"buildsystem": "cmake-ninja",
|
"buildsystem": "cmake-ninja",
|
||||||
"builddir": true,
|
"builddir": true,
|
||||||
"config-opts": [
|
|
||||||
"-DENABLE_TESTING=OFF",
|
|
||||||
"-DKUNIFIEDPUSH_CLIENT_ONLY=ON"
|
|
||||||
],
|
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "archive",
|
"type": "archive",
|
||||||
"url": "https://download.kde.org/stable/release-service/25.08.3/src/kunifiedpush-25.08.3.tar.xz",
|
"url": "https://download.kde.org/stable/kunifiedpush/kunifiedpush-1.0.0.tar.xz",
|
||||||
"sha256": "e8c924438d5359f0fa0930ab35111012076e3a0ff4e959d6929595571383320a",
|
"sha256": "2ddeba21306d0307114ec50a2c38159ec62359f9fc6cdd58da30a369fbd550cf",
|
||||||
"x-checker-data": {
|
"x-checker-data": {
|
||||||
"type": "anitya",
|
"type": "anitya",
|
||||||
"project-id": 8763,
|
"project-id": 375055,
|
||||||
"stable-only": true,
|
"stable-only": true,
|
||||||
"url-template": "https://download.kde.org/stable/release-service/$version/src/kunifiedpush-$version.tar.xz"
|
"url-template": "https://download.kde.org/stable/kunifiedpush/kunifiedpush-$version.tar.xz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ Dependencies:
|
|||||||
'frameworks/ki18n': '@latest-kf6'
|
'frameworks/ki18n': '@latest-kf6'
|
||||||
'frameworks/kconfig': '@latest-kf6'
|
'frameworks/kconfig': '@latest-kf6'
|
||||||
'frameworks/syntax-highlighting': '@latest-kf6'
|
'frameworks/syntax-highlighting': '@latest-kf6'
|
||||||
'frameworks/kiconthemes': '@latest-kf6'
|
|
||||||
'frameworks/kitemmodels': '@latest-kf6'
|
'frameworks/kitemmodels': '@latest-kf6'
|
||||||
'frameworks/kquickcharts': '@latest-kf6'
|
'frameworks/kquickcharts': '@latest-kf6'
|
||||||
'frameworks/knotifications': '@latest-kf6'
|
'frameworks/knotifications': '@latest-kf6'
|
||||||
'frameworks/kcolorscheme': '@latest-kf6'
|
'frameworks/kcolorscheme': '@latest-kf6'
|
||||||
|
'frameworks/kiconthemes': '@latest-kf6'
|
||||||
'libraries/kquickimageeditor': '@latest-kf6'
|
'libraries/kquickimageeditor': '@latest-kf6'
|
||||||
'frameworks/sonnet': '@latest-kf6'
|
'frameworks/sonnet': '@latest-kf6'
|
||||||
'frameworks/prison': '@latest-kf6'
|
'frameworks/prison': '@latest-kf6'
|
||||||
@@ -29,6 +29,7 @@ Dependencies:
|
|||||||
'frameworks/kio': '@latest-kf6'
|
'frameworks/kio': '@latest-kf6'
|
||||||
'frameworks/kwindowsystem': '@latest-kf6'
|
'frameworks/kwindowsystem': '@latest-kf6'
|
||||||
'frameworks/kstatusnotifieritem': '@latest-kf6'
|
'frameworks/kstatusnotifieritem': '@latest-kf6'
|
||||||
|
'frameworks/kcrash': '@latest-kf6'
|
||||||
- 'on': ['Linux', 'FreeBSD']
|
- 'on': ['Linux', 'FreeBSD']
|
||||||
'require':
|
'require':
|
||||||
'frameworks/kdbusaddons': '@latest-kf6'
|
'frameworks/kdbusaddons': '@latest-kf6'
|
||||||
@@ -42,4 +43,3 @@ Dependencies:
|
|||||||
Options:
|
Options:
|
||||||
per-test-timeout: 90
|
per-test-timeout: 90
|
||||||
require-passing-tests-on: ['Linux', 'Android', 'FreeBSD', 'Windows']
|
require-passing-tests-on: ['Linux', 'Android', 'FreeBSD', 'Windows']
|
||||||
run-qmllint: True
|
|
||||||
|
|||||||
@@ -7,15 +7,15 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
# KDE Applications version, managed by release script.
|
# KDE Applications version, managed by release script.
|
||||||
set(RELEASE_SERVICE_VERSION_MAJOR "26")
|
set(RELEASE_SERVICE_VERSION_MAJOR "25")
|
||||||
set(RELEASE_SERVICE_VERSION_MINOR "03")
|
set(RELEASE_SERVICE_VERSION_MINOR "08")
|
||||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
set(RELEASE_SERVICE_VERSION_MICRO "3")
|
||||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||||
|
|
||||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||||
|
|
||||||
set(KF_MIN_VERSION "6.17")
|
set(KF_MIN_VERSION "6.16")
|
||||||
set(QT_MIN_VERSION "6.9")
|
set(QT_MIN_VERSION "6.5")
|
||||||
|
|
||||||
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
|
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 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
set(KDE_COMPILERSETTINGS_LEVEL 6.17)
|
set(KDE_COMPILERSETTINGS_LEVEL 6.0)
|
||||||
|
|
||||||
include(FeatureSummary)
|
include(FeatureSummary)
|
||||||
include(ECMSetupVersion)
|
include(ECMSetupVersion)
|
||||||
@@ -39,16 +39,17 @@ include(ECMCheckOutboundLicense)
|
|||||||
include(ECMQtDeclareLoggingCategory)
|
include(ECMQtDeclareLoggingCategory)
|
||||||
include(ECMAddAndroidApk)
|
include(ECMAddAndroidApk)
|
||||||
include(ECMQmlModule)
|
include(ECMQmlModule)
|
||||||
include(ECMDeprecationSettings)
|
|
||||||
include(GenerateExportHeader)
|
include(GenerateExportHeader)
|
||||||
include(ECMGenerateHeaders)
|
include(ECMGenerateHeaders)
|
||||||
if (NOT ANDROID)
|
if (NOT ANDROID)
|
||||||
include(KDEClangFormat)
|
include(KDEClangFormat)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(QUOTIENT_FORCE_NAMESPACED_INCLUDES TRUE)
|
if(NEOCHAT_FLATPAK)
|
||||||
|
include(cmake/Flatpak.cmake)
|
||||||
|
endif()
|
||||||
|
|
||||||
ecm_set_disabled_deprecation_versions(Qt 6.9.0 KF 6.17.0)
|
set(QUOTIENT_FORCE_NAMESPACED_INCLUDES TRUE)
|
||||||
|
|
||||||
ecm_setup_version(${PROJECT_VERSION}
|
ecm_setup_version(${PROJECT_VERSION}
|
||||||
VARIABLE_PREFIX NEOCHAT
|
VARIABLE_PREFIX NEOCHAT
|
||||||
@@ -65,7 +66,7 @@ if (QT_KNOWN_POLICY_QTP0004)
|
|||||||
qt_policy(SET QTP0004 NEW)
|
qt_policy(SET QTP0004 NEW)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels ColorScheme IconThemes)
|
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels IconThemes ColorScheme)
|
||||||
set_package_properties(KF6 PROPERTIES
|
set_package_properties(KF6 PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
PURPOSE "Basic application components"
|
PURPOSE "Basic application components"
|
||||||
@@ -74,7 +75,7 @@ set_package_properties(KF6Kirigami PROPERTIES
|
|||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
PURPOSE "Kirigami application UI framework"
|
PURPOSE "Kirigami application UI framework"
|
||||||
)
|
)
|
||||||
find_package(KF6KirigamiAddons 1.10.0 REQUIRED)
|
find_package(KF6KirigamiAddons 1.6.0 REQUIRED)
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT NEOCHAT_FLATPAK AND NOT NEOCHAT_APPIMAGE)
|
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)
|
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Purpose)
|
||||||
@@ -88,7 +89,7 @@ if(ANDROID)
|
|||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Widgets)
|
find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Widgets)
|
||||||
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem)
|
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem Crash)
|
||||||
find_package(KF6SyntaxHighlighting ${KF_MIN_VERSION} REQUIRED)
|
find_package(KF6SyntaxHighlighting ${KF_MIN_VERSION} REQUIRED)
|
||||||
set_package_properties(KF6QQC2DesktopStyle PROPERTIES
|
set_package_properties(KF6QQC2DesktopStyle PROPERTIES
|
||||||
TYPE RUNTIME
|
TYPE RUNTIME
|
||||||
@@ -106,7 +107,7 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
|
|||||||
find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED)
|
find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(QuotientQt6 0.9.3)
|
find_package(QuotientQt6 0.9.1)
|
||||||
set_package_properties(QuotientQt6 PROPERTIES
|
set_package_properties(QuotientQt6 PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
DESCRIPTION "Qt wrapper around Matrix API"
|
DESCRIPTION "Qt wrapper around Matrix API"
|
||||||
|
|||||||
@@ -88,9 +88,3 @@ path = "memorytests/memtest-sync.json"
|
|||||||
precedence = "aggregate"
|
precedence = "aggregate"
|
||||||
SPDX-FileCopyrightText = "2024 James Graham <james.h.graham@protonmail.com>"
|
SPDX-FileCopyrightText = "2024 James Graham <james.h.graham@protonmail.com>"
|
||||||
SPDX-License-Identifier = "BSD-2-Clause"
|
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"
|
|
||||||
|
|||||||
@@ -92,15 +92,3 @@ ecm_add_test(
|
|||||||
LINK_LIBRARIES neochat Qt::Test neochat_server
|
LINK_LIBRARIES neochat Qt::Test neochat_server
|
||||||
TEST_NAME actionstest
|
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
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ void ActionsTest::testActions_data()
|
|||||||
QTest::addColumn<std::optional<QString>>("resultText");
|
QTest::addColumn<std::optional<QString>>("resultText");
|
||||||
QTest::addColumn<std::optional<Quotient::RoomMessageEvent::MsgType>>("type");
|
QTest::addColumn<std::optional<Quotient::RoomMessageEvent::MsgType>>("type");
|
||||||
|
|
||||||
QTest::newRow("shrug") << u"/shrug Hello"_s << std::make_optional(u"¯\\\\\\_(ツ)\\_/¯ Hello"_s)
|
QTest::newRow("shrug") << u"/shrug Hello"_s << std::make_optional(u"¯\\\\_(ツ)_/¯ Hello"_s)
|
||||||
<< std::make_optional(Quotient::RoomMessageEvent::MsgType::Text);
|
<< std::make_optional(Quotient::RoomMessageEvent::MsgType::Text);
|
||||||
QTest::newRow("lenny") << u"/lenny Hello"_s << std::make_optional(u"( ͡° ͜ʖ ͡°) Hello"_s) << std::make_optional(Quotient::RoomMessageEvent::MsgType::Text);
|
QTest::newRow("lenny") << u"/lenny Hello"_s << std::make_optional(u"( ͡° ͜ʖ ͡°) Hello"_s) << std::make_optional(Quotient::RoomMessageEvent::MsgType::Text);
|
||||||
QTest::newRow("tableflip") << u"/tableflip Hello"_s << std::make_optional(u"(╯°□°)╯︵ ┻━┻ Hello"_s)
|
QTest::newRow("tableflip") << u"/tableflip Hello"_s << std::make_optional(u"(╯°□°)╯︵ ┻━┻ Hello"_s)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTest>
|
#include <QTest>
|
||||||
|
|
||||||
#include <QSignalSpy>
|
|
||||||
#include <Quotient/roommember.h>
|
#include <Quotient/roommember.h>
|
||||||
#include <Quotient/syncdata.h>
|
#include <Quotient/syncdata.h>
|
||||||
#include <qtestcase.h>
|
#include <qtestcase.h>
|
||||||
@@ -33,7 +32,6 @@ private Q_SLOTS:
|
|||||||
void noRoom();
|
void noRoom();
|
||||||
void badParent();
|
void badParent();
|
||||||
void reply();
|
void reply();
|
||||||
void replyMissingUser();
|
|
||||||
void edit();
|
void edit();
|
||||||
void attachment();
|
void attachment();
|
||||||
};
|
};
|
||||||
@@ -104,33 +102,6 @@ void ChatBarCacheTest::reply()
|
|||||||
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@example:example.org"_s));
|
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@example:example.org"_s));
|
||||||
QCOMPARE(chatBarCache->relationMessage(), u"This is an example\ntext message"_s);
|
QCOMPARE(chatBarCache->relationMessage(), u"This is an example\ntext message"_s);
|
||||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||||
QCOMPARE(chatBarCache->relationAuthorIsPresent(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChatBarCacheTest::replyMissingUser()
|
|
||||||
{
|
|
||||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
|
||||||
chatBarCache->setText(u"some text"_s);
|
|
||||||
chatBarCache->setAttachmentPath(u"some/path"_s);
|
|
||||||
chatBarCache->setReplyId(u"$153456789:example.org"_s);
|
|
||||||
|
|
||||||
QCOMPARE(chatBarCache->text(), u"some text"_s);
|
|
||||||
QCOMPARE(chatBarCache->isReplying(), true);
|
|
||||||
QCOMPARE(chatBarCache->replyId(), u"$153456789:example.org"_s);
|
|
||||||
QCOMPARE(chatBarCache->isEditing(), false);
|
|
||||||
QCOMPARE(chatBarCache->editId(), QString());
|
|
||||||
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@example:example.org"_s));
|
|
||||||
QCOMPARE(chatBarCache->relationMessage(), u"This is an example\ntext message"_s);
|
|
||||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
|
||||||
QCOMPARE(chatBarCache->relationAuthorIsPresent(), true);
|
|
||||||
|
|
||||||
QSignalSpy relationAuthorIsPresentSpy(chatBarCache.get(), &ChatBarCache::relationAuthorIsPresentChanged);
|
|
||||||
|
|
||||||
// sync again, which will simulate the reply user leaving the room
|
|
||||||
room->syncNewEvents(u"test-min-sync-extra-sync.json"_s);
|
|
||||||
|
|
||||||
QTRY_COMPARE(relationAuthorIsPresentSpy.count(), 1);
|
|
||||||
QCOMPARE(chatBarCache->relationAuthorIsPresent(), false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatBarCacheTest::edit()
|
void ChatBarCacheTest::edit()
|
||||||
|
|||||||
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
#include <Quotient/roommember.h>
|
#include <Quotient/roommember.h>
|
||||||
#include <Quotient/syncdata.h>
|
#include <Quotient/syncdata.h>
|
||||||
|
|
||||||
#include "models/eventmessagecontentmodel.h"
|
#include "models/messagecontentmodel.h"
|
||||||
|
|
||||||
#include "neochatconnection.h"
|
#include "neochatconnection.h"
|
||||||
#include "testutils.h"
|
#include "testutils.h"
|
||||||
@@ -39,17 +39,17 @@ void MessageContentModelTest::initTestCase()
|
|||||||
void MessageContentModelTest::missingEvent()
|
void MessageContentModelTest::missingEvent()
|
||||||
{
|
{
|
||||||
auto room = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s);
|
auto room = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s);
|
||||||
auto model1 = EventMessageContentModel(room, u"$153456789:example.org"_s);
|
auto model1 = MessageContentModel(room, u"$153456789:example.org"_s);
|
||||||
|
|
||||||
QCOMPARE(model1.rowCount(), 1);
|
QCOMPARE(model1.rowCount(), 1);
|
||||||
QCOMPARE(model1.data(model1.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
|
QCOMPARE(model1.data(model1.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
|
||||||
QCOMPARE(model1.data(model1.index(0), MessageContentModel::DisplayRole), u"Loading…"_s);
|
QCOMPARE(model1.data(model1.index(0), MessageContentModel::DisplayRole), u"Loading"_s);
|
||||||
|
|
||||||
auto model2 = EventMessageContentModel(room, u"$153456789:example.org"_s, true);
|
auto model2 = MessageContentModel(room, u"$153456789:example.org"_s, true);
|
||||||
|
|
||||||
QCOMPARE(model2.rowCount(), 1);
|
QCOMPARE(model2.rowCount(), 1);
|
||||||
QCOMPARE(model2.data(model2.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
|
QCOMPARE(model2.data(model2.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
|
||||||
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);
|
room->syncNewEvents(u"test-min-sync.json"_s);
|
||||||
QCOMPARE(model1.rowCount(), 2);
|
QCOMPARE(model1.rowCount(), 2);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#include <Quotient/events/roommessageevent.h>
|
#include <Quotient/events/roommessageevent.h>
|
||||||
|
|
||||||
#include "models/eventmessagecontentmodel.h"
|
#include "models/messagecontentmodel.h"
|
||||||
#include "testutils.h"
|
#include "testutils.h"
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
@@ -21,7 +21,7 @@ class ReactionModelTest : public QObject
|
|||||||
private:
|
private:
|
||||||
Connection *connection = nullptr;
|
Connection *connection = nullptr;
|
||||||
TestUtils::TestRoom *room = nullptr;
|
TestUtils::TestRoom *room = nullptr;
|
||||||
EventMessageContentModel *parentModel;
|
MessageContentModel *parentModel;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
@@ -34,7 +34,7 @@ void ReactionModelTest::initTestCase()
|
|||||||
{
|
{
|
||||||
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
|
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
|
||||||
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-reactionmodel-sync.json"_s);
|
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-reactionmodel-sync.json"_s);
|
||||||
parentModel = new EventMessageContentModel(room, "123456"_L1);
|
parentModel = new MessageContentModel(room, "123456"_L1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReactionModelTest::basicReaction()
|
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"
|
|
||||||
@@ -4,12 +4,15 @@
|
|||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QHttpServer>
|
||||||
#include <QHttpServerResponder>
|
#include <QHttpServerResponder>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QSslCertificate>
|
#include <QSslCertificate>
|
||||||
#include <QSslKey>
|
#include <QSslKey>
|
||||||
|
#include <QSslServer>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
|
||||||
#include <Quotient/networkaccessmanager.h>
|
#include <Quotient/networkaccessmanager.h>
|
||||||
@@ -106,20 +109,98 @@ void Server::start()
|
|||||||
m_server.route(u"/_matrix/client/v3/rooms/<arg>/invite"_s,
|
m_server.route(u"/_matrix/client/v3/rooms/<arg>/invite"_s,
|
||||||
QHttpServerRequest::Method::Post,
|
QHttpServerRequest::Method::Post,
|
||||||
[this](const QString &roomId, QHttpServerResponder &responder, const QHttpServerRequest &request) {
|
[this](const QString &roomId, QHttpServerResponder &responder, const QHttpServerRequest &request) {
|
||||||
Changes changes;
|
m_invitedUsers[roomId] += QJsonDocument::fromJson(request.body()).object()[u"user_id"_s].toString();
|
||||||
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);
|
responder.write(QJsonDocument(QJsonObject{}), QHttpServerResponder::StatusCode::Ok);
|
||||||
});
|
});
|
||||||
|
|
||||||
m_server.route(u"/_matrix/client/r0/sync"_s, QHttpServerRequest::Method::Get, this, &Server::sync);
|
m_server.route(u"/_matrix/client/r0/sync"_s, QHttpServerRequest::Method::Get, [this](QHttpServerResponder &responder) {
|
||||||
|
QMap<QString, QJsonArray> stateEvents;
|
||||||
|
|
||||||
|
for (const auto &[roomId, matrixId] : m_roomsToCreate) {
|
||||||
|
stateEvents[roomId] += QJsonObject{
|
||||||
|
{u"content"_s, QJsonObject{{u"room_version"_s, u"11"_s}}},
|
||||||
|
{u"event_id"_s, generateEventId()},
|
||||||
|
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
|
||||||
|
{u"room_id"_s, roomId},
|
||||||
|
{u"sender"_s, matrixId},
|
||||||
|
{u"state_key"_s, QString()},
|
||||||
|
{u"type"_s, u"m.room.create"_s},
|
||||||
|
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||||
|
};
|
||||||
|
stateEvents[roomId] += QJsonObject{
|
||||||
|
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"join"_s}}},
|
||||||
|
{u"event_id"_s, generateEventId()},
|
||||||
|
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
|
||||||
|
{u"room_id"_s, roomId},
|
||||||
|
{u"sender"_s, matrixId},
|
||||||
|
{u"state_key"_s, matrixId},
|
||||||
|
{u"type"_s, u"m.room.member"_s},
|
||||||
|
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
m_roomsToCreate.clear();
|
||||||
|
for (const auto &roomId : m_invitedUsers.keys()) {
|
||||||
|
const auto &values = m_invitedUsers[roomId];
|
||||||
|
for (const auto &value : values) {
|
||||||
|
stateEvents[roomId] += QJsonObject{
|
||||||
|
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"invite"_s}}},
|
||||||
|
{u"event_id"_s, generateEventId()},
|
||||||
|
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
|
||||||
|
{u"room_id"_s, roomId},
|
||||||
|
{u"sender"_s, u"@user:localhost:1234"_s},
|
||||||
|
{u"state_key"_s, value},
|
||||||
|
{u"type"_s, u"m.room.member"_s},
|
||||||
|
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_invitedUsers.clear();
|
||||||
|
|
||||||
|
for (const auto &roomId : m_bannedUsers.keys()) {
|
||||||
|
const auto &values = m_bannedUsers[roomId];
|
||||||
|
for (const auto &value : values) {
|
||||||
|
stateEvents[roomId] += QJsonObject{
|
||||||
|
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"ban"_s}}},
|
||||||
|
{u"event_id"_s, generateEventId()},
|
||||||
|
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
|
||||||
|
{u"room_id"_s, roomId},
|
||||||
|
{u"sender"_s, u"@user:localhost:1234"_s},
|
||||||
|
{u"state_key"_s, value},
|
||||||
|
{u"type"_s, u"m.room.member"_s},
|
||||||
|
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_bannedUsers.clear();
|
||||||
|
|
||||||
|
for (const auto &roomId : m_joinedUsers.keys()) {
|
||||||
|
const auto &values = m_joinedUsers[roomId];
|
||||||
|
for (const auto &value : values) {
|
||||||
|
stateEvents[roomId] += QJsonObject{
|
||||||
|
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"join"_s}}},
|
||||||
|
{u"event_id"_s, generateEventId()},
|
||||||
|
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
|
||||||
|
{u"room_id"_s, roomId},
|
||||||
|
{u"sender"_s, u"@user:localhost:1234"_s},
|
||||||
|
{u"state_key"_s, value},
|
||||||
|
{u"type"_s, u"m.room.member"_s},
|
||||||
|
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_joinedUsers.clear();
|
||||||
|
|
||||||
|
QJsonObject rooms;
|
||||||
|
for (const auto &roomId : stateEvents.keys()) {
|
||||||
|
rooms[roomId] = QJsonObject{{u"state"_s, QJsonObject{{u"events"_s, stateEvents[roomId]}}}};
|
||||||
|
}
|
||||||
|
|
||||||
|
responder.write(QJsonDocument(QJsonObject{{u"rooms"_s, QJsonObject{{u"join"_s, rooms}}}}), QHttpServerResponder::StatusCode::Ok);
|
||||||
|
});
|
||||||
|
|
||||||
QSslConfiguration config;
|
QSslConfiguration config;
|
||||||
QFile key(QStringLiteral(DATA_DIR) + u"/localhost.key"_s);
|
QFile key(QStringLiteral(DATA_DIR) + u"/localhost.key"_s);
|
||||||
void(key.open(QFile::ReadOnly));
|
key.open(QFile::ReadOnly);
|
||||||
config.setPrivateKey(QSslKey(&key, QSsl::Rsa));
|
config.setPrivateKey(QSslKey(&key, QSsl::Rsa));
|
||||||
config.setLocalCertificate(QSslCertificate::fromPath(QStringLiteral(DATA_DIR) + u"/localhost.crt"_s).front());
|
config.setLocalCertificate(QSslCertificate::fromPath(QStringLiteral(DATA_DIR) + u"/localhost.crt"_s).front());
|
||||||
m_sslServer.setSslConfiguration(config);
|
m_sslServer.setSslConfiguration(config);
|
||||||
@@ -133,239 +214,22 @@ void Server::start()
|
|||||||
|
|
||||||
QString Server::createRoom(const QString &matrixId)
|
QString Server::createRoom(const QString &matrixId)
|
||||||
{
|
{
|
||||||
const auto roomId = generateRoomId();
|
auto roomId = generateRoomId();
|
||||||
Changes changes;
|
m_roomsToCreate += {roomId, matrixId};
|
||||||
changes.newRooms += Changes::NewRoom{
|
|
||||||
.initialMembers = {matrixId},
|
|
||||||
.roomId = {roomId},
|
|
||||||
.tags = {},
|
|
||||||
};
|
|
||||||
m_state += changes;
|
|
||||||
return roomId;
|
return roomId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::inviteUser(const QString &roomId, const QString &matrixId)
|
void Server::inviteUser(const QString &roomId, const QString &matrixId)
|
||||||
{
|
{
|
||||||
Changes changes;
|
m_invitedUsers[roomId] += matrixId;
|
||||||
changes.invitations += Changes::InviteUser{
|
|
||||||
.userId = matrixId,
|
|
||||||
.roomId = roomId,
|
|
||||||
};
|
|
||||||
m_state += changes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::banUser(const QString &roomId, const QString &matrixId)
|
void Server::banUser(const QString &roomId, const QString &matrixId)
|
||||||
{
|
{
|
||||||
Changes changes;
|
m_bannedUsers[roomId] += matrixId;
|
||||||
changes.bans += Changes::BanUser{
|
|
||||||
.userId = matrixId,
|
|
||||||
.roomId = roomId,
|
|
||||||
};
|
|
||||||
m_state += changes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::joinUser(const QString &roomId, const QString &matrixId)
|
void Server::joinUser(const QString &roomId, const QString &matrixId)
|
||||||
{
|
{
|
||||||
Changes changes;
|
m_joinedUsers[roomId] += matrixId;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,51 +2,10 @@
|
|||||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
#include <QHttpServer>
|
#include <QHttpServer>
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QSslServer>
|
#include <QSslServer>
|
||||||
|
|
||||||
struct Changes {
|
class Server
|
||||||
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:
|
public:
|
||||||
Server();
|
Server();
|
||||||
|
|
||||||
@@ -62,17 +21,13 @@ public:
|
|||||||
void banUser(const QString &roomId, const QString &matrixId);
|
void banUser(const QString &roomId, const QString &matrixId);
|
||||||
void joinUser(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:
|
private:
|
||||||
QHttpServer m_server;
|
QHttpServer m_server;
|
||||||
QSslServer m_sslServer;
|
QSslServer m_sslServer;
|
||||||
|
|
||||||
void sync(const QHttpServerRequest &request, QHttpServerResponder &responder);
|
QHash<QString, QList<QString>> m_invitedUsers;
|
||||||
|
QHash<QString, QList<QString>> m_bannedUsers;
|
||||||
|
QHash<QString, QList<QString>> m_joinedUsers;
|
||||||
|
|
||||||
QList<Changes> m_state;
|
QList<std::pair<QString, QString>> m_roomsToCreate;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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-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
|
// 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/events/event.h>
|
||||||
#include <Quotient/syncdata.h>
|
#include <Quotient/syncdata.h>
|
||||||
|
|
||||||
@@ -33,7 +32,7 @@ public:
|
|||||||
if (!syncFileName.isEmpty()) {
|
if (!syncFileName.isEmpty()) {
|
||||||
QFile testSyncFile;
|
QFile testSyncFile;
|
||||||
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + syncFileName);
|
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + syncFileName);
|
||||||
Q_UNUSED(testSyncFile.open(QIODevice::ReadOnly));
|
testSyncFile.open(QIODevice::ReadOnly);
|
||||||
const auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll());
|
const auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll());
|
||||||
Quotient::SyncRoomData roomData(id(), Quotient::JoinState::Join, testSyncJson.object());
|
Quotient::SyncRoomData roomData(id(), Quotient::JoinState::Join, testSyncJson.object());
|
||||||
update(std::move(roomData));
|
update(std::move(roomData));
|
||||||
@@ -47,7 +46,7 @@ inline Quotient::event_ptr_tt<EventT> loadEventFromFile(const QString &eventFile
|
|||||||
if (!eventFileName.isEmpty()) {
|
if (!eventFileName.isEmpty()) {
|
||||||
QFile testEventFile;
|
QFile testEventFile;
|
||||||
testEventFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + eventFileName);
|
testEventFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + eventFileName);
|
||||||
Q_UNUSED(testEventFile.open(QIODevice::ReadOnly));
|
testEventFile.open(QIODevice::ReadOnly);
|
||||||
auto testSyncJson = QJsonDocument::fromJson(testEventFile.readAll()).object();
|
auto testSyncJson = QJsonDocument::fromJson(testEventFile.readAll()).object();
|
||||||
return Quotient::loadEvent<EventT>(testSyncJson);
|
return Quotient::loadEvent<EventT>(testSyncJson);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,10 +34,6 @@ private Q_SLOTS:
|
|||||||
void stripDisallowedTags();
|
void stripDisallowedTags();
|
||||||
void stripDisallowedAttributes();
|
void stripDisallowedAttributes();
|
||||||
void emptyCodeTags();
|
void emptyCodeTags();
|
||||||
void addStyle_data();
|
|
||||||
void addStyle();
|
|
||||||
void dontAddStyle_data();
|
|
||||||
void dontAddStyle();
|
|
||||||
|
|
||||||
void sendSimpleStringCase();
|
void sendSimpleStringCase();
|
||||||
void sendSingleParaMarkup();
|
void sendSingleParaMarkup();
|
||||||
@@ -75,9 +71,6 @@ private Q_SLOTS:
|
|||||||
|
|
||||||
void componentOutput_data();
|
void componentOutput_data();
|
||||||
void componentOutput();
|
void componentOutput();
|
||||||
|
|
||||||
void updateSpoiler_data();
|
|
||||||
void updateSpoiler();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void TextHandlerTest::initTestCase()
|
void TextHandlerTest::initTestCase()
|
||||||
@@ -96,26 +89,21 @@ void TextHandlerTest::initTestCase()
|
|||||||
|
|
||||||
void TextHandlerTest::allowedAttributes()
|
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 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 testOutputString1 = 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());
|
|
||||||
// Handle urls where the href has either single (') or double (") quotes.
|
// 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 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 testOutputString2 = 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;
|
|
||||||
|
|
||||||
TextHandler testTextHandler;
|
TextHandler testTextHandler;
|
||||||
testTextHandler.setData(testInputString1);
|
testTextHandler.setData(testInputString1);
|
||||||
|
|
||||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString1S);
|
QCOMPARE(testTextHandler.handleSendText(), testOutputString1);
|
||||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString1R);
|
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString1);
|
||||||
|
|
||||||
testTextHandler.setData(testInputString2);
|
testTextHandler.setData(testInputString2);
|
||||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString2S);
|
QCOMPARE(testTextHandler.handleSendText(), testOutputString2);
|
||||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString2R);
|
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextHandlerTest::stripDisallowedTags()
|
void TextHandlerTest::stripDisallowedTags()
|
||||||
@@ -158,56 +146,6 @@ void TextHandlerTest::emptyCodeTags()
|
|||||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
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()
|
void TextHandlerTest::sendSimpleStringCase()
|
||||||
{
|
{
|
||||||
const QString testInputString = u"This data should just be left alone."_s;
|
const QString testInputString = u"This data should just be left alone."_s;
|
||||||
@@ -400,8 +338,7 @@ void TextHandlerTest::receiveRichInPlainOut()
|
|||||||
void TextHandlerTest::receivePlainTextIn()
|
void TextHandlerTest::receivePlainTextIn()
|
||||||
{
|
{
|
||||||
const QString testInputString = u"<plain text in tag bracket>\nTest link https://kde.org."_s;
|
const QString testInputString = u"<plain text in tag bracket>\nTest link https://kde.org."_s;
|
||||||
const QString testOutputStringRich =
|
const QString testOutputStringRich = u"<plain text in tag bracket><br>Test link <a href=\"https://kde.org\">https://kde.org</a>."_s;
|
||||||
u"<plain text in tag bracket><br>Test link <a href=\"https://kde.org\" style=\"text-decoration: none;\">https://kde.org</a>."_s;
|
|
||||||
QString testOutputStringPlain = u"<plain text in tag bracket>\nTest link https://kde.org."_s;
|
QString testOutputStringPlain = u"<plain text in tag bracket>\nTest link https://kde.org."_s;
|
||||||
|
|
||||||
// Make sure quotes are maintained in a plain string.
|
// Make sure quotes are maintained in a plain string.
|
||||||
@@ -471,7 +408,7 @@ void TextHandlerTest::receivePlainStripMarkup()
|
|||||||
void TextHandlerTest::receiveRichUserPill()
|
void TextHandlerTest::receiveRichUserPill()
|
||||||
{
|
{
|
||||||
const QString testInputString = u"<p><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></p>"_s;
|
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;
|
TextHandler testTextHandler;
|
||||||
testTextHandler.setData(testInputString);
|
testTextHandler.setData(testInputString);
|
||||||
@@ -523,23 +460,21 @@ void TextHandlerTest::receiveRichPlainUrl_data()
|
|||||||
// so we can confirm consistent behaviour for complex urls.
|
// so we can confirm consistent behaviour for complex urls.
|
||||||
QTest::addRow("link 1")
|
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"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;
|
<< 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
|
// 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
|
// contains what looks like and email. It was broken into 3 but needs to
|
||||||
// be just single link.
|
// be just single link.
|
||||||
QTest::addRow("link 2")
|
QTest::addRow("link 2")
|
||||||
<< u"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/"_s
|
<< 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;
|
<< u"<a href=\"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/\">https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/</a>"_s;
|
||||||
|
|
||||||
QTest::addRow("email")
|
QTest::addRow("email") << uR"(email@example.com <a href="mailto:email@example.com">Link already rich</a>)"_s
|
||||||
<< uR"(email@example.com <a href="mailto:email@example.com">Link already rich</a>)"_s
|
<< uR"(<a href="mailto:email@example.com">email@example.com</a> <a href="mailto:email@example.com">Link already rich</a>)"_s;
|
||||||
<< 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")
|
QTest::addRow("mxid")
|
||||||
<< u"@user:kde.org <a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a>"_s
|
<< 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;
|
<< u"<b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a></b>"_s;
|
||||||
QTest::addRow("mxid with prefix") << u"a @user:kde.org b"_s
|
QTest::addRow("mxid with prefix") << u"a @user:kde.org b"_s << u"a <b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> b"_s;
|
||||||
<< u"a <b><a href=\"https://matrix.to/#/@user:kde.org\" style=\"text-decoration: none;\">@user:kde.org</a></b> b"_s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -661,35 +596,5 @@ void TextHandlerTest::componentOutput()
|
|||||||
QCOMPARE(testTextHandler.textComponents(testInputString), testOutputComponents);
|
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)
|
QTEST_MAIN(TextHandlerTest)
|
||||||
#include "texthandlertest.moc"
|
#include "texthandlertest.moc"
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ void TimelineMessageModelTest::pendingEvent()
|
|||||||
// different every time.
|
// different every time.
|
||||||
QFile testSyncFile;
|
QFile testSyncFile;
|
||||||
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + u"test-pending-sync.json"_s);
|
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 testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll());
|
||||||
auto root = testSyncJson.object();
|
auto root = testSyncJson.object();
|
||||||
auto timeline = root["timeline"_L1].toObject();
|
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);
|
auto room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-min-sync.json"_s);
|
||||||
model->setRoom(room);
|
model->setRoom(room);
|
||||||
|
|
||||||
QCOMPARE(model->indexForEventId(u"$153456789:example.org"_s).row(), 0);
|
QCOMPARE(model->indexforEventId(u"$153456789:example.org"_s).row(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimelineMessageModelTest::cleanup()
|
void TimelineMessageModelTest::cleanup()
|
||||||
|
|||||||
14
cmake/Flatpak.cmake
Normal file
14
cmake/Flatpak.cmake
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
# Include FontConfig config which uses the Emoji One font from the
|
||||||
|
# KDE Flatpak SDK.
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Flatpak/99-noto-mono-color-emoji.conf
|
||||||
|
DESTINATION
|
||||||
|
${CMAKE_INSTALL_SYSCONFDIR}/fonts/local.conf
|
||||||
|
)
|
||||||
|
|
||||||
23
cmake/Flatpak/99-noto-mono-color-emoji.conf
Normal file
23
cmake/Flatpak/99-noto-mono-color-emoji.conf
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||||
|
<fontconfig>
|
||||||
|
<alias>
|
||||||
|
<family>serif</family>
|
||||||
|
<prefer>
|
||||||
|
<family>Noto Color Emoji</family>
|
||||||
|
</prefer>
|
||||||
|
</alias>
|
||||||
|
<alias>
|
||||||
|
<family>sans-serif</family>
|
||||||
|
<prefer>
|
||||||
|
<family>Noto Color Emoji</family>
|
||||||
|
</prefer>
|
||||||
|
</alias>
|
||||||
|
<alias>
|
||||||
|
<family>monospace</family>
|
||||||
|
<prefer>
|
||||||
|
<family>Noto Color Emoji</family>
|
||||||
|
</prefer>
|
||||||
|
</alias>
|
||||||
|
</fontconfig>
|
||||||
|
|
||||||
@@ -3,12 +3,12 @@
|
|||||||
|
|
||||||
add_definitions(-DDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}" )
|
add_definitions(-DDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}" )
|
||||||
|
|
||||||
qt_add_executable(timeline_memtest
|
qt_add_executable(timeline-memtest
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(timeline_memtest PRIVATE neochatplugin Timelineplugin)
|
target_link_libraries(timeline-memtest PRIVATE neochatplugin Timelineplugin)
|
||||||
target_link_libraries(timeline_memtest PUBLIC
|
target_link_libraries(timeline-memtest PUBLIC
|
||||||
Qt::Core
|
Qt::Core
|
||||||
Qt::Quick
|
Qt::Quick
|
||||||
Qt::Qml
|
Qt::Qml
|
||||||
@@ -16,13 +16,14 @@ target_link_libraries(timeline_memtest PUBLIC
|
|||||||
Qt::QuickControls2
|
Qt::QuickControls2
|
||||||
Qt::Widgets
|
Qt::Widgets
|
||||||
KF6::I18nQml
|
KF6::I18nQml
|
||||||
|
KF6::Kirigami
|
||||||
QuotientQt6
|
QuotientQt6
|
||||||
LibNeoChat
|
LibNeoChat
|
||||||
Timeline
|
Timeline
|
||||||
)
|
)
|
||||||
|
|
||||||
ecm_add_qml_module(timeline_memtest URI org.kde.neochat.timeline_memtest GENERATE_PLUGIN_SOURCE
|
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
|
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/timeline-memtest
|
||||||
QML_FILES
|
QML_FILES
|
||||||
Main.qml
|
Main.qml
|
||||||
SOURCES
|
SOURCES
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ int main(int argc, char **argv)
|
|||||||
engine.rootContext()->setContextProperty(u"memTestTimelineModel"_s, memTestTimelineModel);
|
engine.rootContext()->setContextProperty(u"memTestTimelineModel"_s, memTestTimelineModel);
|
||||||
engine.rootContext()->setContextProperty(u"messageFilterModel"_s, messageFilterModel);
|
engine.rootContext()->setContextProperty(u"messageFilterModel"_s, messageFilterModel);
|
||||||
|
|
||||||
engine.loadFromModule("org.kde.neochat.timeline_memtest", "Main");
|
engine.loadFromModule("org.kde.neochat.timeline-memtest", "Main");
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,11 +37,7 @@ public:
|
|||||||
if (!syncFileName.isEmpty()) {
|
if (!syncFileName.isEmpty()) {
|
||||||
QFile testSyncFile;
|
QFile testSyncFile;
|
||||||
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + syncFileName);
|
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + syncFileName);
|
||||||
auto ok = testSyncFile.open(QIODevice::ReadOnly);
|
testSyncFile.open(QIODevice::ReadOnly);
|
||||||
if (!ok) {
|
|
||||||
qWarning() << "Failed to open" << testSyncFile.fileName() << testSyncFile.errorString();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll()).object();
|
auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll()).object();
|
||||||
auto timelineJson = testSyncJson["timeline"_L1].toObject();
|
auto timelineJson = testSyncJson["timeline"_L1].toObject();
|
||||||
timelineJson["events"_L1] = multiplyEvents(timelineJson["events"_L1].toArray(), 100);
|
timelineJson["events"_L1] = multiplyEvents(timelineJson["events"_L1].toArray(), 100);
|
||||||
|
|||||||
@@ -306,8 +306,8 @@
|
|||||||
<keyword>Matrix</keyword>
|
<keyword>Matrix</keyword>
|
||||||
<keyword>Kirigami</keyword>
|
<keyword>Kirigami</keyword>
|
||||||
</keywords>
|
</keywords>
|
||||||
<developer id="org.kde">
|
<developer id="kde.org">
|
||||||
<name translate="no">KDE</name>
|
<name>The KDE Community</name>
|
||||||
<url>https://kde.org</url>
|
<url>https://kde.org</url>
|
||||||
</developer>
|
</developer>
|
||||||
<metadata_license>CC0-1.0</metadata_license>
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
@@ -320,6 +320,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::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::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::windows_store::PromotionalArt16x9">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/promoimage-1920x1080.png</value>
|
||||||
|
<value key="KDE::supporters">Tanguy Fardet;[dabe](https://freeradical.zone/@dabe);[lengau](https://mastodon.world/@lengau);Joshua Strobl;Stuart Turton</value>
|
||||||
</custom>
|
</custom>
|
||||||
<launchable type="desktop-id">org.kde.neochat.desktop</launchable>
|
<launchable type="desktop-id">org.kde.neochat.desktop</launchable>
|
||||||
<screenshots>
|
<screenshots>
|
||||||
@@ -487,8 +488,6 @@
|
|||||||
<content_attribute id="social-chat">intense</content_attribute>
|
<content_attribute id="social-chat">intense</content_attribute>
|
||||||
</content_rating>
|
</content_rating>
|
||||||
<releases>
|
<releases>
|
||||||
<release version="25.12.1" date="2026-01-08"/>
|
|
||||||
<release version="25.12.0" date="2025-12-11"/>
|
|
||||||
<release version="25.08.3" date="2025-11-06"/>
|
<release version="25.08.3" date="2025-11-06"/>
|
||||||
<release version="25.08.2" date="2025-10-09"/>
|
<release version="25.08.2" date="2025-10-09"/>
|
||||||
<release version="25.08.1" date="2025-09-11"/>
|
<release version="25.08.1" date="2025-09-11"/>
|
||||||
|
|||||||
@@ -108,7 +108,6 @@ Comment[ia]=Conversation en ditecto sur Matrix
|
|||||||
Comment[it]= su Matrix
|
Comment[it]= su Matrix
|
||||||
Comment[ka]=ჩატი Matrix-ზე
|
Comment[ka]=ჩატი Matrix-ზე
|
||||||
Comment[ko]=Matrix에서 대화하기
|
Comment[ko]=Matrix에서 대화하기
|
||||||
Comment[lt]=Pokalbiai per Matrix
|
|
||||||
Comment[lv]=Tērzējiet „Matrix“ tīklā
|
Comment[lv]=Tērzējiet „Matrix“ tīklā
|
||||||
Comment[nl]=Chat op Matrix
|
Comment[nl]=Chat op Matrix
|
||||||
Comment[pl]=Rozmawiaj na Matriksie
|
Comment[pl]=Rozmawiaj na Matriksie
|
||||||
|
|||||||
3604
po/ar/neochat.po
3604
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
3097
po/ast/neochat.po
3097
po/ast/neochat.po
File diff suppressed because it is too large
Load Diff
3675
po/az/neochat.po
3675
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
3533
po/ca/neochat.po
3533
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
3484
po/cs/neochat.po
3484
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
3639
po/da/neochat.po
3639
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
4227
po/de/neochat.po
4227
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
3823
po/el/neochat.po
3823
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
3996
po/en_GB/neochat.po
3996
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
3965
po/eo/neochat.po
3965
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
3291
po/es/neochat.po
3291
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
3703
po/eu/neochat.po
3703
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
3726
po/fi/neochat.po
3726
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
3686
po/fr/neochat.po
3686
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
7146
po/ga/neochat.po
7146
po/ga/neochat.po
File diff suppressed because it is too large
Load Diff
4173
po/gl/neochat.po
4173
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
3554
po/he/neochat.po
3554
po/he/neochat.po
File diff suppressed because it is too large
Load Diff
3967
po/hi/neochat.po
3967
po/hi/neochat.po
File diff suppressed because it is too large
Load Diff
3953
po/hu/neochat.po
3953
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
3921
po/ia/neochat.po
3921
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
3846
po/id/neochat.po
3846
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
3598
po/ie/neochat.po
3598
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
3625
po/it/neochat.po
3625
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
3094
po/ja/neochat.po
3094
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
3592
po/ka/neochat.po
3592
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
3823
po/ko/neochat.po
3823
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
3493
po/lt/neochat.po
3493
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
3819
po/lv/neochat.po
3819
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
3593
po/nl/neochat.po
3593
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
3570
po/nn/neochat.po
3570
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
3652
po/pa/neochat.po
3652
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
3645
po/pl/neochat.po
3645
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
3829
po/pt/neochat.po
3829
po/pt/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 % Brazilian-Portuguese "INCLUDE">
|
|
||||||
]>
|
|
||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
|
||||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
<refentry lang="&language;">
|
|
||||||
<refentryinfo>
|
|
||||||
<title
|
|
||||||
>Manual do Usuário do NeoChat</title>
|
|
||||||
<author
|
|
||||||
><firstname
|
|
||||||
>Carl</firstname
|
|
||||||
><surname
|
|
||||||
>Schwan</surname
|
|
||||||
> <contrib
|
|
||||||
>NeoChat man page.</contrib
|
|
||||||
> <email
|
|
||||||
>carl@carlschwan.eu</email
|
|
||||||
></author>
|
|
||||||
<date
|
|
||||||
>01/11/2022</date>
|
|
||||||
<releaseinfo
|
|
||||||
>22.09</releaseinfo>
|
|
||||||
<productname
|
|
||||||
>NeoChat</productname>
|
|
||||||
</refentryinfo>
|
|
||||||
|
|
||||||
<refmeta>
|
|
||||||
<refentrytitle>
|
|
||||||
<command
|
|
||||||
>neochat</command>
|
|
||||||
</refentrytitle>
|
|
||||||
<manvolnum
|
|
||||||
>1</manvolnum>
|
|
||||||
</refmeta>
|
|
||||||
|
|
||||||
<refnamediv>
|
|
||||||
<refname
|
|
||||||
>neochat</refname>
|
|
||||||
<refpurpose
|
|
||||||
>Cliente para interação com o protocolo de mensagens 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
|
|
||||||
>Descrição</title>
|
|
||||||
<para
|
|
||||||
>O <command
|
|
||||||
>neochat</command
|
|
||||||
> é um aplicativo de bate-papo para o protocolo Matrix. Ele funciona tanto em computadores quanto em dispositivos móveis. </para>
|
|
||||||
</refsect1>
|
|
||||||
|
|
||||||
<refsect1 id="options"
|
|
||||||
><title
|
|
||||||
>Opções</title>
|
|
||||||
<variablelist>
|
|
||||||
<varlistentry>
|
|
||||||
<term
|
|
||||||
><option
|
|
||||||
>URI</option
|
|
||||||
></term>
|
|
||||||
<listitem>
|
|
||||||
<para
|
|
||||||
>O URI da matriz para um usuário ou uma sala. Por exemplo, matrix:u/usuário:exemplo.org e matrix:r/root:exemplo.org. Isso fará com que o NeoChat tente abrir a sala ou conversa especificada. </para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
</variablelist>
|
|
||||||
</refsect1>
|
|
||||||
|
|
||||||
<refsect1 id="bug">
|
|
||||||
<title
|
|
||||||
>Relatar bugs</title>
|
|
||||||
<para
|
|
||||||
>Você pode reportar erros e solicitar novas funcionalidades em <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
|
|
||||||
>Veja também</title>
|
|
||||||
<simplelist>
|
|
||||||
<member
|
|
||||||
>Lista de perguntas frequentes sobre o 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
|
|
||||||
>Direitos autorais</title>
|
|
||||||
<para
|
|
||||||
>Direitos autorais © 2020-2022 Tobias Fella </para>
|
|
||||||
<para
|
|
||||||
>Direitos autorais © 2020-2022 Carl Schwan </para>
|
|
||||||
<para
|
|
||||||
>Licença: GNU General Public Versão 3 ou posterior <ulink url="https://www.gnu.org/licenses/gpl-3.0.html"
|
|
||||||
>https://www.gnu.org/licenses/gpl-3.0.html</ulink
|
|
||||||
>></para>
|
|
||||||
</refsect1>
|
|
||||||
</refentry>
|
|
||||||
3598
po/pt_BR/neochat.po
3598
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
3385
po/ro/neochat.po
3385
po/ro/neochat.po
File diff suppressed because it is too large
Load Diff
4163
po/ru/neochat.po
4163
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
3969
po/sa/neochat.po
3969
po/sa/neochat.po
File diff suppressed because it is too large
Load Diff
3741
po/sk/neochat.po
3741
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
3604
po/sl/neochat.po
3604
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
3819
po/sv/neochat.po
3819
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
4026
po/ta/neochat.po
4026
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
3488
po/tok/neochat.po
3488
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,7 @@ SPDX-License-Identifier: CC-BY-SA-4.0
|
|||||||
>carl@carlschwan.eu</email
|
>carl@carlschwan.eu</email
|
||||||
></author>
|
></author>
|
||||||
<date
|
<date
|
||||||
>2022‒11‒01</date>
|
>2022-11-01</date>
|
||||||
<releaseinfo
|
<releaseinfo
|
||||||
>22.09</releaseinfo>
|
>22.09</releaseinfo>
|
||||||
<productname
|
<productname
|
||||||
@@ -111,9 +111,9 @@ SPDX-License-Identifier: CC-BY-SA-4.0
|
|||||||
><title
|
><title
|
||||||
>Telif Hakkı</title>
|
>Telif Hakkı</title>
|
||||||
<para
|
<para
|
||||||
>Telif hakkı © 2020–2022 Tobias Fella </para>
|
>Telif hakkı © 2020-2022 Tobias Fella </para>
|
||||||
<para
|
<para
|
||||||
>Telif hakkı © 2020–2022 Carl Schwan </para>
|
>Telif hakkı © 2020-2022 Carl Schwan </para>
|
||||||
<para
|
<para
|
||||||
>Lisans: GNU Genel Kamu Lisansa, 3. sürüm veya sonrası <<ulink url="https://www.gnu.org/licenses/gpl-3.0.html"
|
>Lisans: GNU Genel Kamu Lisansa, 3. sürüm veya sonrası <<ulink url="https://www.gnu.org/licenses/gpl-3.0.html"
|
||||||
>https://www.gnu.org/licenses/gpl-3.0.html</ulink
|
>https://www.gnu.org/licenses/gpl-3.0.html</ulink
|
||||||
|
|||||||
3580
po/tr/neochat.po
3580
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
3586
po/uk/neochat.po
3586
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
3285
po/zh_CN/neochat.po
3285
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
3571
po/zh_TW/neochat.po
3571
po/zh_TW/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
|||||||
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
qt_add_library(neochat STATIC
|
add_library(neochat STATIC
|
||||||
controller.cpp
|
controller.cpp
|
||||||
controller.h
|
controller.h
|
||||||
roommanager.cpp
|
roommanager.cpp
|
||||||
@@ -35,8 +35,6 @@ qt_add_library(neochat STATIC
|
|||||||
models/commonroomsmodel.h
|
models/commonroomsmodel.h
|
||||||
texttospeechhelper.h
|
texttospeechhelper.h
|
||||||
texttospeechhelper.cpp
|
texttospeechhelper.cpp
|
||||||
models/limitermodel.cpp
|
|
||||||
models/limitermodel.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||||
@@ -50,8 +48,6 @@ if(ANDROID OR WIN32)
|
|||||||
set_source_files_properties(qml/GlobalMenuStub.qml PROPERTIES
|
set_source_files_properties(qml/GlobalMenuStub.qml PROPERTIES
|
||||||
QT_QML_SOURCE_TYPENAME GlobalMenu
|
QT_QML_SOURCE_TYPENAME GlobalMenu
|
||||||
)
|
)
|
||||||
else()
|
|
||||||
set(EXTRA_IMPORTS org.kde.purpose)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||||
@@ -105,12 +101,9 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
|||||||
qml/ReasonDialog.qml
|
qml/ReasonDialog.qml
|
||||||
qml/NewPollDialog.qml
|
qml/NewPollDialog.qml
|
||||||
qml/UserMenu.qml
|
qml/UserMenu.qml
|
||||||
qml/MeetingDialog.qml
|
|
||||||
qml/SeenByDialog.qml
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
QtCore
|
QtCore
|
||||||
QtQuick
|
QtQuick
|
||||||
io.github.quotient_im.libquotient
|
|
||||||
IMPORTS
|
IMPORTS
|
||||||
org.kde.neochat.libneochat
|
org.kde.neochat.libneochat
|
||||||
org.kde.neochat.rooms
|
org.kde.neochat.rooms
|
||||||
@@ -122,15 +115,13 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
|||||||
org.kde.neochat.devtools
|
org.kde.neochat.devtools
|
||||||
org.kde.neochat.login
|
org.kde.neochat.login
|
||||||
org.kde.neochat.chatbar
|
org.kde.neochat.chatbar
|
||||||
org.kde.config
|
|
||||||
org.kde.syntaxhighlighting
|
|
||||||
${EXTRA_IMPORTS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT ANDROID AND NOT WIN32)
|
if(NOT ANDROID AND NOT WIN32)
|
||||||
qt_target_qml_sources(neochat QML_FILES
|
qt_target_qml_sources(neochat QML_FILES
|
||||||
qml/ShareAction.qml
|
qml/ShareAction.qml
|
||||||
qml/GlobalMenu.qml
|
qml/GlobalMenu.qml
|
||||||
|
qml/EditMenu.qml
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
qt_target_qml_sources(neochat QML_FILES
|
qt_target_qml_sources(neochat QML_FILES
|
||||||
@@ -143,17 +134,10 @@ if(WIN32)
|
|||||||
set_target_properties(neochat PROPERTIES OUTPUT_NAME "neochatlib")
|
set_target_properties(neochat PROPERTIES OUTPUT_NAME "neochatlib")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
qt_add_executable(neochat-app
|
add_executable(neochat-app
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(ANDROID)
|
|
||||||
set_target_properties(neochat-app PROPERTIES
|
|
||||||
OUTPUT_NAME "neochat-app"
|
|
||||||
PREFIX "lib"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(TARGET Qt::WebView)
|
if(TARGET Qt::WebView)
|
||||||
target_link_libraries(neochat-app PUBLIC Qt::WebView)
|
target_link_libraries(neochat-app PUBLIC Qt::WebView)
|
||||||
target_compile_definitions(neochat-app PUBLIC -DHAVE_WEBVIEW)
|
target_compile_definitions(neochat-app PUBLIC -DHAVE_WEBVIEW)
|
||||||
@@ -163,7 +147,6 @@ target_include_directories(neochat-app PRIVATE ${CMAKE_BINARY_DIR})
|
|||||||
|
|
||||||
target_link_libraries(neochat-app PRIVATE
|
target_link_libraries(neochat-app PRIVATE
|
||||||
neochat
|
neochat
|
||||||
KF6::IconThemes
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
|
ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
|
||||||
@@ -194,7 +177,7 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models)
|
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models)
|
||||||
target_link_libraries(neochat PRIVATE neochatplugin Loginplugin Roomsplugin RoomInfoplugin MessageContentplugin Timelineplugin Spacesplugin Chatbarplugin Settingsplugin Devtoolsplugin)
|
target_link_libraries(neochat PRIVATE Loginplugin Roomsplugin RoomInfoplugin MessageContentplugin Timelineplugin Spacesplugin Chatbarplugin Settingsplugin Devtoolsplugin)
|
||||||
target_link_libraries(neochat PUBLIC
|
target_link_libraries(neochat PUBLIC
|
||||||
LibNeoChat
|
LibNeoChat
|
||||||
Timeline
|
Timeline
|
||||||
@@ -214,9 +197,8 @@ target_link_libraries(neochat PUBLIC
|
|||||||
KF6::ConfigGui
|
KF6::ConfigGui
|
||||||
KF6::CoreAddons
|
KF6::CoreAddons
|
||||||
KF6::SonnetCore
|
KF6::SonnetCore
|
||||||
|
KF6::IconThemes
|
||||||
KF6::ItemModels
|
KF6::ItemModels
|
||||||
KF6::I18nQml
|
|
||||||
KirigamiApp
|
|
||||||
QuotientQt6
|
QuotientQt6
|
||||||
Login
|
Login
|
||||||
Rooms
|
Rooms
|
||||||
@@ -224,6 +206,10 @@ target_link_libraries(neochat PUBLIC
|
|||||||
Spaces
|
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)
|
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)
|
if(NEOCHAT_FLATPAK)
|
||||||
@@ -334,7 +320,6 @@ if(ANDROID)
|
|||||||
"kt-restore-defaults-symbolic"
|
"kt-restore-defaults-symbolic"
|
||||||
"user-symbolic"
|
"user-symbolic"
|
||||||
"mark-location-symbolic"
|
"mark-location-symbolic"
|
||||||
"amarok_playcount"
|
|
||||||
|
|
||||||
${KIRIGAMI_ADDONS_ICONS}
|
${KIRIGAMI_ADDONS_ICONS}
|
||||||
)
|
)
|
||||||
@@ -353,7 +338,14 @@ if(TARGET KF6::DBusAddons AND NOT WIN32)
|
|||||||
target_compile_definitions(neochat PUBLIC -DHAVE_KDBUSADDONS)
|
target_compile_definitions(neochat PUBLIC -DHAVE_KDBUSADDONS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (TARGET KF6::KIOWidgets)
|
||||||
|
target_compile_definitions(neochat PUBLIC -DHAVE_KIO)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (TARGET KUnifiedPush)
|
if (TARGET KUnifiedPush)
|
||||||
|
target_compile_definitions(neochat PUBLIC -DHAVE_KUNIFIEDPUSH)
|
||||||
|
target_link_libraries(neochat PUBLIC KUnifiedPush)
|
||||||
|
|
||||||
if (NOT ANDROID)
|
if (NOT ANDROID)
|
||||||
configure_file(org.kde.neochat.service.in ${CMAKE_CURRENT_BINARY_DIR}/org.kde.neochat.service)
|
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})
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.neochat.service DESTINATION ${KDE_INSTALL_DBUSSERVICEDIR})
|
||||||
@@ -363,8 +355,7 @@ endif()
|
|||||||
install(TARGETS neochat-app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
|
install(TARGETS neochat-app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||||
|
|
||||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||||
# krunner plugin must be the same as the app id for flatpak to export it
|
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins)
|
||||||
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins RENAME org.kde.neochat.desktop)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
|
|
||||||
#include <Quotient/connection.h>
|
#include <Quotient/connection.h>
|
||||||
|
#include <qt6keychain/keychain.h>
|
||||||
|
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
|
|
||||||
@@ -13,19 +14,23 @@
|
|||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <Quotient/csapi/notifications.h>
|
||||||
#include <Quotient/events/roommemberevent.h>
|
#include <Quotient/events/roommemberevent.h>
|
||||||
#include <Quotient/qt_connection_util.h>
|
#include <Quotient/qt_connection_util.h>
|
||||||
#include <Quotient/settings.h>
|
#include <Quotient/settings.h>
|
||||||
|
|
||||||
|
#include "accountmanager.h"
|
||||||
#include "enums/roomsortparameter.h"
|
#include "enums/roomsortparameter.h"
|
||||||
#include "general_logging.h"
|
|
||||||
#include "mediasizehelper.h"
|
#include "mediasizehelper.h"
|
||||||
#include "models/actionsmodel.h"
|
#include "models/actionsmodel.h"
|
||||||
#include "models/messagemodel.h"
|
#include "models/messagemodel.h"
|
||||||
|
#include "models/pushrulemodel.h"
|
||||||
#include "models/roomlistmodel.h"
|
#include "models/roomlistmodel.h"
|
||||||
#include "models/roomtreemodel.h"
|
#include "models/roomtreemodel.h"
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
|
#include "neochatconnection.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
|
#include "notificationsmanager.h"
|
||||||
#include "proxycontroller.h"
|
#include "proxycontroller.h"
|
||||||
#include "roommanager.h"
|
#include "roommanager.h"
|
||||||
|
|
||||||
@@ -35,6 +40,14 @@
|
|||||||
#include "trayicon_sni.h"
|
#include "trayicon_sni.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
|
||||||
|
#ifndef Q_OS_ANDROID
|
||||||
|
#include <QDBusConnection>
|
||||||
|
#include <QDBusInterface>
|
||||||
|
#include <QDBusMessage>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_KUNIFIEDPUSH
|
#ifdef HAVE_KUNIFIEDPUSH
|
||||||
#include <kunifiedpush/connector.h>
|
#include <kunifiedpush/connector.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -123,10 +136,8 @@ Controller::Controller(QObject *parent)
|
|||||||
connect(NeoChatConfig::self(), &NeoChatConfig::SystemTrayChanged, this, &Controller::setQuitOnLastWindowClosed);
|
connect(NeoChatConfig::self(), &NeoChatConfig::SystemTrayChanged, this, &Controller::setQuitOnLastWindowClosed);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [this] {
|
QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [this] {
|
||||||
#ifndef Q_OS_ANDROID
|
|
||||||
delete m_trayIcon;
|
delete m_trayIcon;
|
||||||
#endif
|
|
||||||
NeoChatConfig::self()->save();
|
NeoChatConfig::self()->save();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -192,15 +203,13 @@ void Controller::setAccountManager(AccountManager *manager)
|
|||||||
|
|
||||||
m_accountManager = manager;
|
m_accountManager = manager;
|
||||||
|
|
||||||
if (!m_accountManager) {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
void Controller::initConnection(NeoChatConnection *connection)
|
||||||
@@ -246,10 +255,7 @@ void Controller::initActiveConnection(NeoChatConnection *oldConnection, NeoChatC
|
|||||||
if (newConnection) {
|
if (newConnection) {
|
||||||
connect(newConnection, &NeoChatConnection::errorOccured, this, &Controller::errorOccured);
|
connect(newConnection, &NeoChatConnection::errorOccured, this, &Controller::errorOccured);
|
||||||
connect(newConnection, &NeoChatConnection::badgeNotificationCountChanged, this, &Controller::updateBadgeNotificationCount);
|
connect(newConnection, &NeoChatConnection::badgeNotificationCountChanged, this, &Controller::updateBadgeNotificationCount);
|
||||||
|
|
||||||
// Refresh and update manually, in case we init too late for the badge count to actually change.
|
|
||||||
newConnection->refreshBadgeNotificationCount();
|
newConnection->refreshBadgeNotificationCount();
|
||||||
updateBadgeNotificationCount(newConnection->badgeNotificationCount());
|
|
||||||
}
|
}
|
||||||
Q_EMIT activeConnectionChanged(newConnection);
|
Q_EMIT activeConnectionChanged(newConnection);
|
||||||
}
|
}
|
||||||
@@ -259,8 +265,8 @@ bool Controller::supportSystemTray() const
|
|||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
QStringList unsupportedPlatforms{u"GNOME"_s, u"Pantheon"_s};
|
auto de = QString::fromLatin1(qgetenv("XDG_CURRENT_DESKTOP"));
|
||||||
return !unsupportedPlatforms.contains(QString::fromLatin1(qgetenv("XDG_CURRENT_DESKTOP")));
|
return de != u"GNOME"_s && de != u"Pantheon"_s;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,8 +276,11 @@ void Controller::setQuitOnLastWindowClosed()
|
|||||||
if (supportSystemTray() && NeoChatConfig::self()->systemTray()) {
|
if (supportSystemTray() && NeoChatConfig::self()->systemTray()) {
|
||||||
m_trayIcon = new TrayIcon(this);
|
m_trayIcon = new TrayIcon(this);
|
||||||
m_trayIcon->show();
|
m_trayIcon->show();
|
||||||
} else if (m_trayIcon) {
|
} else {
|
||||||
delete m_trayIcon;
|
if (m_trayIcon) {
|
||||||
|
delete m_trayIcon;
|
||||||
|
m_trayIcon = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -327,7 +336,30 @@ void Controller::clearInvitationNotification(const QString &roomId)
|
|||||||
|
|
||||||
void Controller::updateBadgeNotificationCount(int count)
|
void Controller::updateBadgeNotificationCount(int count)
|
||||||
{
|
{
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
|
||||||
|
#ifndef Q_OS_ANDROID
|
||||||
|
// copied from Telegram desktop
|
||||||
|
const auto launcherUrl = "application://org.kde.neochat.desktop"_L1;
|
||||||
|
// Gnome requires that count is a 64bit integer
|
||||||
|
const qint64 counterSlice = std::min(count, 9999);
|
||||||
|
QVariantMap dbusUnityProperties;
|
||||||
|
|
||||||
|
if (counterSlice > 0) {
|
||||||
|
dbusUnityProperties["count"_L1] = counterSlice;
|
||||||
|
dbusUnityProperties["count-visible"_L1] = true;
|
||||||
|
} else {
|
||||||
|
dbusUnityProperties["count-visible"_L1] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto signal = QDBusMessage::createSignal("/com/canonical/unity/launcherentry/neochat"_L1, "com.canonical.Unity.LauncherEntry"_L1, "Update"_L1);
|
||||||
|
|
||||||
|
signal.setArguments({launcherUrl, dbusUnityProperties});
|
||||||
|
|
||||||
|
QDBusConnection::sessionBus().send(signal);
|
||||||
|
#endif // Q_OS_ANDROID
|
||||||
|
#else
|
||||||
qGuiApp->setBadgeNumber(count);
|
qGuiApp->setBadgeNumber(count);
|
||||||
|
#endif // QT_VERSION_CHECK(6, 6, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::isFlatpak() const
|
bool Controller::isFlatpak() const
|
||||||
@@ -348,10 +380,7 @@ QString Controller::loadFileContent(const QString &path) const
|
|||||||
{
|
{
|
||||||
QUrl url(path);
|
QUrl url(path);
|
||||||
QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString());
|
QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString());
|
||||||
if (!file.open(QFile::ReadOnly)) {
|
file.open(QFile::ReadOnly);
|
||||||
qCWarning(GENERAL) << "Failed to open file" << path;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return QString::fromLatin1(file.readAll());
|
return QString::fromLatin1(file.readAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,9 +110,8 @@ private:
|
|||||||
void initActiveConnection(NeoChatConnection *oldConnection, NeoChatConnection *newConnection);
|
void initActiveConnection(NeoChatConnection *oldConnection, NeoChatConnection *newConnection);
|
||||||
|
|
||||||
QPointer<NeoChatConnection> m_connection;
|
QPointer<NeoChatConnection> m_connection;
|
||||||
#ifndef Q_OS_ANDROID
|
TrayIcon *m_trayIcon = nullptr;
|
||||||
QPointer<TrayIcon> m_trayIcon;
|
|
||||||
#endif
|
|
||||||
QString m_endpoint;
|
QString m_endpoint;
|
||||||
QStringList m_shownImages;
|
QStringList m_shownImages;
|
||||||
|
|
||||||
|
|||||||
@@ -33,10 +33,13 @@
|
|||||||
#include <KWindowSystem>
|
#include <KWindowSystem>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if __has_include("KCrash")
|
||||||
|
#include <KCrash>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <KIconTheme>
|
#include <KIconTheme>
|
||||||
#include <KLocalizedQmlContext>
|
#include <KLocalizedContext>
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
#include <KirigamiApp>
|
|
||||||
|
|
||||||
#include "neochat-version.h"
|
#include "neochat-version.h"
|
||||||
|
|
||||||
@@ -101,11 +104,8 @@ Q_DECL_EXPORT
|
|||||||
#endif
|
#endif
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
|
||||||
|
|
||||||
// We currently need to do this ourselves,
|
|
||||||
// KirigamiApp currently called this after constructing the app which breaks icons on Windows.
|
|
||||||
KIconTheme::initTheme();
|
KIconTheme::initTheme();
|
||||||
|
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||||
|
|
||||||
#ifdef HAVE_WEBVIEW
|
#ifdef HAVE_WEBVIEW
|
||||||
QtWebView::initialize();
|
QtWebView::initialize();
|
||||||
@@ -113,10 +113,24 @@ int main(int argc, char *argv[])
|
|||||||
QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
|
QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
KirigamiApp::App app(argc, argv);
|
#ifdef Q_OS_ANDROID
|
||||||
KirigamiApp kirigamiApp;
|
QGuiApplication app(argc, argv);
|
||||||
|
QQuickStyle::setStyle(u"org.kde.breeze"_s);
|
||||||
|
#else
|
||||||
|
QIcon::setFallbackThemeName("breeze"_L1);
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
// Default to org.kde.desktop style unless the user forces another style
|
||||||
|
if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) {
|
||||||
|
QQuickStyle::setStyle(u"org.kde.desktop"_s);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_WINDOWS
|
#ifdef Q_OS_WINDOWS
|
||||||
|
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||||
|
freopen("CONOUT$", "w", stdout);
|
||||||
|
freopen("CONOUT$", "w", stderr);
|
||||||
|
}
|
||||||
|
|
||||||
QApplication::setStyle(u"breeze"_s);
|
QApplication::setStyle(u"breeze"_s);
|
||||||
QFont font(u"Segoe UI Emoji"_s);
|
QFont font(u"Segoe UI Emoji"_s);
|
||||||
font.setPointSize(10);
|
font.setPointSize(10);
|
||||||
@@ -163,9 +177,19 @@ int main(int argc, char *argv[])
|
|||||||
KAboutData::setApplicationData(about);
|
KAboutData::setApplicationData(about);
|
||||||
QGuiApplication::setWindowIcon(QIcon::fromTheme(u"org.kde.neochat"_s));
|
QGuiApplication::setWindowIcon(QIcon::fromTheme(u"org.kde.neochat"_s));
|
||||||
|
|
||||||
|
#if __has_include("KCrash")
|
||||||
|
KCrash::initialize();
|
||||||
|
#endif
|
||||||
|
|
||||||
Connection::setEncryptionDefault(true);
|
Connection::setEncryptionDefault(true);
|
||||||
Connection::setDirectChatEncryptionDefault(true);
|
Connection::setDirectChatEncryptionDefault(true);
|
||||||
|
|
||||||
|
#ifdef NEOCHAT_FLATPAK
|
||||||
|
// Copy over the included FontConfig configuration to the
|
||||||
|
// app's config dir:
|
||||||
|
QFile::copy(u"/app/etc/fonts/conf.d/99-noto-mono-color-emoji.conf"_s, u"/var/config/fontconfig/conf.d/99-noto-mono-color-emoji.conf"_s);
|
||||||
|
#endif
|
||||||
|
|
||||||
ColorSchemer colorScheme;
|
ColorSchemer colorScheme;
|
||||||
|
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
@@ -261,7 +285,7 @@ int main(int argc, char *argv[])
|
|||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
KLocalization::setupLocalizedContext(&engine);
|
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
|
||||||
engine.setNetworkAccessManagerFactory(new NetworkAccessManagerFactory());
|
engine.setNetworkAccessManagerFactory(new NetworkAccessManagerFactory());
|
||||||
|
|
||||||
if (parser.isSet("ignore-ssl-errors"_L1)) {
|
if (parser.isSet("ignore-ssl-errors"_L1)) {
|
||||||
@@ -276,9 +300,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
engine.addImageProvider(u"blurhash"_s, new BlurhashImageProvider);
|
engine.addImageProvider(u"blurhash"_s, new BlurhashImageProvider);
|
||||||
|
|
||||||
if (!kirigamiApp.start("org.kde.neochat", "Main", &engine)) {
|
engine.loadFromModule("org.kde.neochat", "Main");
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!parser.positionalArguments().isEmpty() && !parser.isSet("share"_L1)) {
|
if (!parser.positionalArguments().isEmpty() && !parser.isSet("share"_L1)) {
|
||||||
RoomManager::instance().setUrlArgument(parser.positionalArguments()[0]);
|
RoomManager::instance().setUrlArgument(parser.positionalArguments()[0]);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include "jobs/neochatgetcommonroomsjob.h"
|
#include "jobs/neochatgetcommonroomsjob.h"
|
||||||
|
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <Quotient/room.h>
|
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
@@ -40,22 +39,8 @@ void CommonRoomsModel::setUserId(const QString &userId)
|
|||||||
|
|
||||||
QVariant CommonRoomsModel::data(const QModelIndex &index, int roleName) const
|
QVariant CommonRoomsModel::data(const QModelIndex &index, int roleName) const
|
||||||
{
|
{
|
||||||
auto roomId = m_commonRooms[index.row()];
|
Q_UNUSED(index)
|
||||||
auto room = connection()->room(roomId);
|
Q_UNUSED(roleName)
|
||||||
if (!room) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (roleName) {
|
|
||||||
case Qt::DisplayRole:
|
|
||||||
case RoomNameRole:
|
|
||||||
return room->displayName();
|
|
||||||
case RoomAvatarRole:
|
|
||||||
return room->avatarUrl();
|
|
||||||
case RoomIdRole:
|
|
||||||
return roomId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,15 +50,6 @@ int CommonRoomsModel::rowCount(const QModelIndex &parent) const
|
|||||||
return m_commonRooms.size();
|
return m_commonRooms.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> CommonRoomsModel::roleNames() const
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
{RoomIdRole, "roomId"},
|
|
||||||
{RoomNameRole, "roomName"},
|
|
||||||
{RoomAvatarRole, "roomAvatar"},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void CommonRoomsModel::reload()
|
void CommonRoomsModel::reload()
|
||||||
{
|
{
|
||||||
if (!m_connection || m_userId.isEmpty()) {
|
if (!m_connection || m_userId.isEmpty()) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
|
|
||||||
#include "neochatconnection.h"
|
#include "neochatconnection.h"
|
||||||
|
#include "neochatroom.h"
|
||||||
|
|
||||||
#include <Quotient/events/roommessageevent.h>
|
#include <Quotient/events/roommessageevent.h>
|
||||||
#include <Quotient/roommember.h>
|
#include <Quotient/roommember.h>
|
||||||
@@ -24,9 +25,11 @@ class CommonRoomsModel : public QAbstractListModel
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
enum Roles {
|
enum Roles {
|
||||||
RoomIdRole = Qt::UserRole,
|
TextRole = Qt::DisplayRole,
|
||||||
RoomNameRole,
|
LongitudeRole,
|
||||||
RoomAvatarRole,
|
LatitudeRole,
|
||||||
|
AssetRole,
|
||||||
|
AuthorRole,
|
||||||
};
|
};
|
||||||
Q_ENUM(Roles)
|
Q_ENUM(Roles)
|
||||||
|
|
||||||
@@ -41,8 +44,6 @@ public:
|
|||||||
[[nodiscard]] QVariant data(const QModelIndex &index, int roleName) const override;
|
[[nodiscard]] QVariant data(const QModelIndex &index, int roleName) const override;
|
||||||
[[nodiscard]] Q_INVOKABLE int rowCount(const QModelIndex &parent = {}) const override;
|
[[nodiscard]] Q_INVOKABLE int rowCount(const QModelIndex &parent = {}) const override;
|
||||||
|
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void connectionChanged();
|
void connectionChanged();
|
||||||
void userIdChanged();
|
void userIdChanged();
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
||||||
|
|
||||||
#include "models/limitermodel.h"
|
|
||||||
|
|
||||||
LimiterModel::LimiterModel(QObject *parent)
|
|
||||||
: QSortFilterProxyModel(parent)
|
|
||||||
{
|
|
||||||
connect(this, &QSortFilterProxyModel::rowsInserted, this, &LimiterModel::extraCountChanged);
|
|
||||||
connect(this, &QSortFilterProxyModel::rowsRemoved, this, &LimiterModel::extraCountChanged);
|
|
||||||
connect(this, &QSortFilterProxyModel::modelReset, this, &LimiterModel::extraCountChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
int LimiterModel::maximumCount() const
|
|
||||||
{
|
|
||||||
return m_maximumCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LimiterModel::setMaximumCount(int maximumCount)
|
|
||||||
{
|
|
||||||
if (m_maximumCount != maximumCount) {
|
|
||||||
m_maximumCount = maximumCount;
|
|
||||||
Q_EMIT maximumCountChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int LimiterModel::extraCount() const
|
|
||||||
{
|
|
||||||
if (sourceModel()) {
|
|
||||||
return std::max(sourceModel()->rowCount() - maximumCount(), 0);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LimiterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(source_parent)
|
|
||||||
return source_row < maximumCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "moc_limitermodel.cpp"
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QQmlEngine>
|
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class LimiterModel
|
|
||||||
*
|
|
||||||
* @brief Takes a source QAbstractItemModel model and only displays a desired maximum amount.
|
|
||||||
*
|
|
||||||
* Also gives you the remaining (filtered out) items, useful for sticking in a label or somesuch.
|
|
||||||
*/
|
|
||||||
class LimiterModel : public QSortFilterProxyModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
QML_ELEMENT
|
|
||||||
|
|
||||||
Q_PROPERTY(int maximumCount READ maximumCount WRITE setMaximumCount NOTIFY maximumCountChanged)
|
|
||||||
Q_PROPERTY(int extraCount READ extraCount NOTIFY extraCountChanged)
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit LimiterModel(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
[[nodiscard]] int maximumCount() const;
|
|
||||||
void setMaximumCount(int maximumCount);
|
|
||||||
|
|
||||||
[[nodiscard]] int extraCount() const;
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void maximumCountChanged();
|
|
||||||
void extraCountChanged();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int m_maximumCount = 0;
|
|
||||||
};
|
|
||||||
@@ -58,7 +58,7 @@ QVariant NotificationsModel::data(const QModelIndex &index, int role) const
|
|||||||
QHash<int, QByteArray> NotificationsModel::roleNames() const
|
QHash<int, QByteArray> NotificationsModel::roleNames() const
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
{TextRole, "notificationText"},
|
{TextRole, "text"},
|
||||||
{RoomIdRole, "roomId"},
|
{RoomIdRole, "roomId"},
|
||||||
{AuthorName, "authorName"},
|
{AuthorName, "authorName"},
|
||||||
{AuthorAvatar, "authorAvatar"},
|
{AuthorAvatar, "authorAvatar"},
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ Comment[sa]=कक्षस्य नूतनं निमन्त्रणम
|
|||||||
Comment[sl]=Tam je novo povabilo v sobo
|
Comment[sl]=Tam je novo povabilo v sobo
|
||||||
Comment[sv]=Det finns en ny inbjudan till ett rum
|
Comment[sv]=Det finns en ny inbjudan till ett rum
|
||||||
Comment[ta]=ஓர் அரங்கிற்கான புதிய அழைப்பிதழ் உள்ளது
|
Comment[ta]=ஓர் அரங்கிற்கான புதிய அழைப்பிதழ் உள்ளது
|
||||||
Comment[tr]=Bir odaya yeni bir davet var
|
Comment[tr]=Bir odaya yeni bir davetiye var
|
||||||
Comment[uk]=У кімнаті нове запрошення
|
Comment[uk]=У кімнаті нове запрошення
|
||||||
Comment[zh_CN]=有新的聊天室邀请
|
Comment[zh_CN]=有新的聊天室邀请
|
||||||
Comment[zh_TW]=有新的加入聊天室邀請
|
Comment[zh_TW]=有新的加入聊天室邀請
|
||||||
@@ -287,7 +287,6 @@ Name[ia]=Comparti
|
|||||||
Name[it]=Condivisione
|
Name[it]=Condivisione
|
||||||
Name[ka]=გაზიარება
|
Name[ka]=გაზიარება
|
||||||
Name[ko]=공유
|
Name[ko]=공유
|
||||||
Name[lt]=Bendrinti
|
|
||||||
Name[lv]=Kopīgot
|
Name[lv]=Kopīgot
|
||||||
Name[nl]=Gedeelde
|
Name[nl]=Gedeelde
|
||||||
Name[nn]=Del
|
Name[nn]=Del
|
||||||
@@ -323,7 +322,6 @@ Comment[ia]=Le exito de compartir un pecietta de contento
|
|||||||
Comment[it]=Il risultato della condivisione di un contenuto
|
Comment[it]=Il risultato della condivisione di un contenuto
|
||||||
Comment[ka]=შემცველობის ნაწილის გაზიარების შედეგი
|
Comment[ka]=შემცველობის ნაწილის გაზიარების შედეგი
|
||||||
Comment[ko]=콘텐츠 공유 결과
|
Comment[ko]=콘텐츠 공유 결과
|
||||||
Comment[lt]=Turinio dalies bendrinimo rezultatas
|
|
||||||
Comment[lv]=Satura kopīgošanas rezultāts
|
Comment[lv]=Satura kopīgošanas rezultāts
|
||||||
Comment[nl]=Het resultaat van het delen van een stukje inhoud
|
Comment[nl]=Het resultaat van het delen van een stukje inhoud
|
||||||
Comment[nn]=Resultatet av deling av innhald
|
Comment[nn]=Resultatet av deling av innhald
|
||||||
|
|||||||
@@ -66,10 +66,6 @@
|
|||||||
</entry>
|
</entry>
|
||||||
</group>
|
</group>
|
||||||
<group name="Timeline">
|
<group name="Timeline">
|
||||||
<entry name="FontScale" type="double">
|
|
||||||
<label>Scaling factor for font sizes</label>
|
|
||||||
<default>1.0</default>
|
|
||||||
</entry>
|
|
||||||
<entry name="ShowAvatarInTimeline" type="bool">
|
<entry name="ShowAvatarInTimeline" type="bool">
|
||||||
<label>Show avatar in the timeline</label>
|
<label>Show avatar in the timeline</label>
|
||||||
<default>true</default>
|
<default>true</default>
|
||||||
@@ -193,10 +189,6 @@
|
|||||||
<label>Don't hide any events in the timeline</label>
|
<label>Don't hide any events in the timeline</label>
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
</entry>
|
</entry>
|
||||||
<entry name="RelateAnyEvent" type="bool">
|
|
||||||
<label>Send relations to any event, including state events and events normally hidden.</label>
|
|
||||||
<default>false</default>
|
|
||||||
</entry>
|
|
||||||
<entry name="AlwaysVerifyDevice" type="bool">
|
<entry name="AlwaysVerifyDevice" type="bool">
|
||||||
<label>Always allow device verification</label>
|
<label>Always allow device verification</label>
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
@@ -211,6 +203,10 @@
|
|||||||
<label>Enable threads</label>
|
<label>Enable threads</label>
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
</entry>
|
</entry>
|
||||||
|
<entry name="SecretBackup" type="bool">
|
||||||
|
<label>Enable secret backup</label>
|
||||||
|
<default>false</default>
|
||||||
|
</entry>
|
||||||
<entry name="Phone3PId" type="bool">
|
<entry name="Phone3PId" type="bool">
|
||||||
<label>Enable add phone numbers as 3PIDs</label>
|
<label>Enable add phone numbers as 3PIDs</label>
|
||||||
<default>false</default>
|
<default>false</default>
|
||||||
|
|||||||
@@ -60,8 +60,9 @@ void NotificationsManager::startNotificationJob(QPointer<NeoChatConnection> conn
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!m_connActiveJob.contains(connection->user()->id())) {
|
if (!m_connActiveJob.contains(connection->user()->id())) {
|
||||||
|
auto job = connection->callApi<GetNotificationsJob>();
|
||||||
m_connActiveJob.append(connection->user()->id());
|
m_connActiveJob.append(connection->user()->id());
|
||||||
connection->callApi<GetNotificationsJob>().onResult([this, connection](const auto &job) {
|
connect(job, &BaseJob::success, this, [this, job, connection]() {
|
||||||
m_connActiveJob.removeAll(connection->user()->id());
|
m_connActiveJob.removeAll(connection->user()->id());
|
||||||
processNotificationJob(connection, job, !m_oldNotifications.contains(connection->user()->id()));
|
processNotificationJob(connection, job, !m_oldNotifications.contains(connection->user()->id()));
|
||||||
});
|
});
|
||||||
@@ -216,12 +217,12 @@ void NotificationsManager::postNotification(NeoChatRoom *room,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
notification->setTitle(room->displayName());
|
|
||||||
|
|
||||||
QString entry;
|
QString entry;
|
||||||
if (room->isDirectChat()) {
|
if (sender == room->displayName()) {
|
||||||
|
notification->setTitle(sender);
|
||||||
entry = text.toHtmlEscaped();
|
entry = text.toHtmlEscaped();
|
||||||
} else {
|
} else {
|
||||||
|
notification->setTitle(room->displayName());
|
||||||
entry = i18n("%1: %2", sender, text.toHtmlEscaped());
|
entry = i18n("%1: %2", sender, text.toHtmlEscaped());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,9 +254,7 @@ void NotificationsManager::postNotification(NeoChatRoom *room,
|
|||||||
notification->setReplyAction(std::move(replyAction));
|
notification->setReplyAction(std::move(replyAction));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Controller::instance().accounts()->rowCount() > 1) {
|
notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
|
||||||
notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
|
|
||||||
}
|
|
||||||
notification->sendEvent();
|
notification->sendEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,9 +348,7 @@ void NotificationsManager::doPostInviteNotification(QPointer<NeoChatRoom> room)
|
|||||||
m_invitations.remove(room->id());
|
m_invitations.remove(room->id());
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Controller::instance().accounts()->rowCount() > 1) {
|
notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
|
||||||
notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
|
|
||||||
}
|
|
||||||
|
|
||||||
notification->sendEvent();
|
notification->sendEvent();
|
||||||
}
|
}
|
||||||
@@ -434,7 +431,7 @@ QPixmap NotificationsManager::createNotificationImage(const QImage &icon, NeoCha
|
|||||||
|
|
||||||
if (room != nullptr) {
|
if (room != nullptr) {
|
||||||
const QImage roomAvatar = room->avatar(imageRect.width(), imageRect.height());
|
const QImage roomAvatar = room->avatar(imageRect.width(), imageRect.height());
|
||||||
if (!roomAvatar.isNull() && icon != roomAvatar) {
|
if (icon != roomAvatar) {
|
||||||
const QRect lowerQuarter{imageRect.center(), imageRect.size() / 2};
|
const QRect lowerQuarter{imageRect.center(), imageRect.size() / 2};
|
||||||
|
|
||||||
painter.setBrush(Qt::white);
|
painter.setBrush(Qt::white);
|
||||||
|
|||||||
@@ -92,3 +92,4 @@ X-Plasma-API=DBus
|
|||||||
X-Plasma-DBusRunner-Service=org.kde.neochat
|
X-Plasma-DBusRunner-Service=org.kde.neochat
|
||||||
X-Plasma-DBusRunner-Path=/RoomRunner
|
X-Plasma-DBusRunner-Path=/RoomRunner
|
||||||
X-Plasma-Request-Actions-Once=true
|
X-Plasma-Request-Actions-Once=true
|
||||||
|
X-Plasma-Runner-Min-Letter-Count=3
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls as QQC2
|
import QtQuick.Controls as QQC2
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.components as KirigamiComponents
|
import org.kde.kirigamiaddons.components as KirigamiComponents
|
||||||
|
|
||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
import org.kde.neochat.settings
|
import org.kde.neochat.settings
|
||||||
|
import org.kde.neochat.devtools
|
||||||
|
|
||||||
KirigamiComponents.ConvergentContextMenu {
|
KirigamiComponents.ConvergentContextMenu {
|
||||||
id: root
|
id: root
|
||||||
@@ -18,17 +18,21 @@ KirigamiComponents.ConvergentContextMenu {
|
|||||||
required property NeoChatConnection connection
|
required property NeoChatConnection connection
|
||||||
required property Kirigami.ApplicationWindow window
|
required property Kirigami.ApplicationWindow window
|
||||||
|
|
||||||
Kirigami.Action {
|
QQC2.Action {
|
||||||
text: i18nc("@action:button", "Show QR Code")
|
text: i18nc("@action:button", "Show QR Code")
|
||||||
icon.name: "view-barcode-qr-symbolic"
|
icon.name: "view-barcode-qr-symbolic"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
(Qt.createComponent('org.kde.neochat', 'QrCodeMaximizeComponent').createObject(QQC2.Overlay.overlay, {
|
let qrMax = Qt.createComponent('org.kde.neochat', 'QrCodeMaximizeComponent').createObject(QQC2.Overlay.overlay, {
|
||||||
text: "https://matrix.to/#/" + root.connection.localUser.id,
|
text: "https://matrix.to/#/" + root.connection.localUser.id,
|
||||||
title: root.connection.localUser.displayName,
|
title: root.connection.localUser.displayName,
|
||||||
subtitle: root.connection.localUser.id,
|
subtitle: root.connection.localUser.id,
|
||||||
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
|
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
|
||||||
avatarSource: root.connection.localUser.avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(root.connection.localUser.avatarUrl) : ""
|
avatarSource: root.connection.localUser.avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(root.connection.localUser.avatarUrl) : ""
|
||||||
}) as QrCodeMaximizeComponent).open();
|
});
|
||||||
|
if (typeof root.closeDialog === "function") {
|
||||||
|
root.closeDialog();
|
||||||
|
}
|
||||||
|
qrMax.open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,27 +40,26 @@ KirigamiComponents.ConvergentContextMenu {
|
|||||||
text: i18nc("@action:inmenu", "Switch Account")
|
text: i18nc("@action:inmenu", "Switch Account")
|
||||||
icon.name: "system-switch-user"
|
icon.name: "system-switch-user"
|
||||||
shortcut: "Ctrl+U"
|
shortcut: "Ctrl+U"
|
||||||
onTriggered: (Qt.createComponent("org.kde.neochat", "AccountSwitchDialog").createObject(QQC2.Overlay.overlay, {
|
onTriggered: accountSwitchDialog.createObject(QQC2.Overlay.overlay, {
|
||||||
connection: root.connection
|
connection: root.connection
|
||||||
}) as Kirigami.Dialog).open();
|
}).open();
|
||||||
}
|
}
|
||||||
|
QQC2.Action {
|
||||||
Kirigami.Action {
|
text: i18n("Edit This Account")
|
||||||
text: i18nc("@action:inmenu", "Edit This Account")
|
|
||||||
icon.name: "document-edit"
|
icon.name: "document-edit"
|
||||||
onTriggered: NeoChatSettingsView.openWithInitialProperties("accounts", {initialAccount: root.connection});
|
onTriggered: NeoChatSettingsView.openWithInitialProperties("accounts", {initialAccount: root.connection});
|
||||||
}
|
}
|
||||||
|
|
||||||
Kirigami.Action {
|
QQC2.Action {
|
||||||
text: i18nc("@action:inmenu", "Notification Settings")
|
text: i18n("Notification Settings")
|
||||||
icon.name: "notifications"
|
icon.name: "notifications"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
NeoChatSettingsView.open('notifications');
|
NeoChatSettingsView.open('notifications');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Kirigami.Action {
|
QQC2.Action {
|
||||||
text: i18nc("@action:inmenu", "Devices")
|
text: i18n("Devices")
|
||||||
icon.name: "computer-symbolic"
|
icon.name: "computer-symbolic"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
NeoChatSettingsView.open('devices');
|
NeoChatSettingsView.open('devices');
|
||||||
@@ -64,10 +67,10 @@ KirigamiComponents.ConvergentContextMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18nc("@action:inmenu", "Open Developer Tools")
|
text: i18n("Open Developer Tools")
|
||||||
icon.name: "tools"
|
icon.name: "tools"
|
||||||
visible: NeoChatConfig.developerTools
|
visible: NeoChatConfig.developerTools
|
||||||
onTriggered: root.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'DevtoolsPage'), {
|
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'DevtoolsPage'), {
|
||||||
connection: root.connection
|
connection: root.connection
|
||||||
}, {
|
}, {
|
||||||
title: i18nc("@title:window", "Developer Tools"),
|
title: i18nc("@title:window", "Developer Tools"),
|
||||||
@@ -79,15 +82,15 @@ KirigamiComponents.ConvergentContextMenu {
|
|||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18nc("@action:inmenu", "Open Secret Backup")
|
text: i18nc("@action:inmenu", "Open Secret Backup")
|
||||||
icon.name: "unlock"
|
icon.name: "unlock"
|
||||||
|
visible: NeoChatConfig.secretBackup
|
||||||
onTriggered: root.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UnlockSSSSDialog'), {}, {
|
onTriggered: root.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UnlockSSSSDialog'), {}, {
|
||||||
title: i18nc("@title:window", "Open Key Backup")
|
title: i18nc("@title:window", "Open Key Backup")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Kirigami.Action {
|
QQC2.Action {
|
||||||
text: i18nc("@action:inmenu", "Verify This Device")
|
text: i18nc("@action:inmenu", "Verify This Device")
|
||||||
icon.name: "security-low"
|
icon.name: "security-low"
|
||||||
visible: !root.connection.isVerifiedSession
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
root.connection.startSelfVerification();
|
root.connection.startSelfVerification();
|
||||||
const dialog = Qt.createComponent("org.kde.kirigami", "PromptDialog").createObject(QQC2.Overlay.overlay, {
|
const dialog = Qt.createComponent("org.kde.kirigami", "PromptDialog").createObject(QQC2.Overlay.overlay, {
|
||||||
@@ -96,17 +99,19 @@ KirigamiComponents.ConvergentContextMenu {
|
|||||||
standardButtons: Kirigami.Dialog.Ok
|
standardButtons: Kirigami.Dialog.Ok
|
||||||
})
|
})
|
||||||
dialog.open();
|
dialog.open();
|
||||||
root.connection.newKeyVerificationSession.connect(() => {
|
root.connection.onNewKeyVerificationSession.connect(() => {
|
||||||
dialog.close();
|
dialog.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Kirigami.Action {
|
QQC2.Action {
|
||||||
text: i18nc("@action:inmenu", "Logout…")
|
text: i18n("Logout")
|
||||||
icon.name: "im-kick-user"
|
icon.name: "im-kick-user"
|
||||||
onTriggered: (Qt.createComponent("org.kde.neochat", "ConfirmLogoutDialog").createObject(QQC2.Overlay.overlay, {
|
onTriggered: confirmLogoutDialogComponent.createObject(root).open()
|
||||||
connection: root.connection
|
}
|
||||||
}) as Kirigami.Dialog).open()
|
|
||||||
|
readonly property Component confirmLogoutDialogComponent: ConfirmLogoutDialog {
|
||||||
|
connection: root.connection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls as QQC2
|
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
@@ -19,6 +16,8 @@ Kirigami.Dialog {
|
|||||||
|
|
||||||
required property NeoChatConnection connection
|
required property NeoChatConnection connection
|
||||||
|
|
||||||
|
parent: applicationWindow().overlay
|
||||||
|
|
||||||
leftPadding: 0
|
leftPadding: 0
|
||||||
rightPadding: 0
|
rightPadding: 0
|
||||||
topPadding: 0
|
topPadding: 0
|
||||||
@@ -26,7 +25,7 @@ Kirigami.Dialog {
|
|||||||
|
|
||||||
standardButtons: Kirigami.Dialog.NoButton
|
standardButtons: Kirigami.Dialog.NoButton
|
||||||
|
|
||||||
width: Math.min(QQC2.ApplicationWindow.window.width, Kirigami.Units.gridUnit * 24)
|
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
|
||||||
title: i18nc("@title: dialog to switch between logged in accounts", "Switch Account")
|
title: i18nc("@title: dialog to switch between logged in accounts", "Switch Account")
|
||||||
|
|
||||||
onVisibleChanged: if (visible) {
|
onVisibleChanged: if (visible) {
|
||||||
@@ -54,14 +53,14 @@ Kirigami.Dialog {
|
|||||||
}
|
}
|
||||||
text: i18nc("@button: login to or register a new account.", "Add Account")
|
text: i18nc("@button: login to or register a new account.", "Add Account")
|
||||||
contentItem: Delegates.SubtitleContentItem {
|
contentItem: Delegates.SubtitleContentItem {
|
||||||
itemDelegate: addDelegate
|
itemDelegate: parent
|
||||||
subtitle: i18nc("@info", "Log in or create a new account")
|
subtitle: i18n("Log in or create a new account")
|
||||||
labelItem.textFormat: Text.PlainText
|
labelItem.textFormat: Text.PlainText
|
||||||
subtitleItem.textFormat: Text.PlainText
|
subtitleItem.textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
((root.QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat.login', 'WelcomePage'), {}, {
|
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.login', 'WelcomePage'), {}, {
|
||||||
title: i18nc("@title:window", "Login")
|
title: i18nc("@title:window", "Login")
|
||||||
});
|
});
|
||||||
root.close();
|
root.close();
|
||||||
@@ -95,8 +94,8 @@ Kirigami.Dialog {
|
|||||||
accountView.decrementCurrentIndex();
|
accountView.decrementCurrentIndex();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Keys.onEnterPressed: ((accountView.currentItem ?? accountView.footerItem) as Delegates.RoundedItemDelegate).clicked()
|
Keys.onEnterPressed: accountView.currentItem.clicked()
|
||||||
Keys.onReturnPressed: ((accountView.currentItem ?? accountView.footerItem) as Delegates.RoundedItemDelegate).clicked()
|
Keys.onReturnPressed: accountView.currentItem.clicked()
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
for (let i = 0; i < accountView.count; i++) {
|
for (let i = 0; i < accountView.count; i++) {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import QtQuick.Controls as QQC2
|
|||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat
|
||||||
|
|
||||||
Kirigami.Dialog {
|
Kirigami.Dialog {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
@@ -18,7 +20,7 @@ Kirigami.Dialog {
|
|||||||
title: i18nc("@title:dialog", "Start a chat")
|
title: i18nc("@title:dialog", "Start a chat")
|
||||||
|
|
||||||
contentItem: QQC2.Label {
|
contentItem: QQC2.Label {
|
||||||
text: i18nc("@info", "Do you want to start a chat with %1?", root.user.displayName)
|
text: i18n("Do you want to start a chat with %1?", root.user.displayName)
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
horizontalAlignment: Qt.AlignHCenter
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
QQC2.ToolButton {
|
QQC2.ToolButton {
|
||||||
id: editImageButton
|
id: editImageButton
|
||||||
visible: root.hasImage
|
visible: hasImage
|
||||||
icon.name: "document-edit"
|
icon.name: "document-edit"
|
||||||
text: i18n("Edit")
|
text: i18n("Edit")
|
||||||
display: QQC2.AbstractButton.IconOnly
|
display: QQC2.AbstractButton.IconOnly
|
||||||
@@ -46,9 +46,9 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let imageEditor = (Kirigami.PageStack.pageStack as Kirigami.PageRow).pushDialogLayer(imageEditorPage);
|
let imageEditor = applicationWindow().pageStack.pushDialogLayer(imageEditorPage);
|
||||||
imageEditor.newPathChanged.connect(function (newPath) {
|
imageEditor.newPathChanged.connect(function (newPath) {
|
||||||
imageEditor.closeDialog();
|
applicationWindow().pageStack.layers.pop();
|
||||||
root.attachmentPath = newPath;
|
root.attachmentPath = newPath;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -58,18 +58,14 @@ ColumnLayout {
|
|||||||
QQC2.ToolButton {
|
QQC2.ToolButton {
|
||||||
id: cancelAttachmentButton
|
id: cancelAttachmentButton
|
||||||
display: QQC2.AbstractButton.IconOnly
|
display: QQC2.AbstractButton.IconOnly
|
||||||
text: i18nc("@action:button", "Cancel sending attachment")
|
action: Kirigami.Action {
|
||||||
icon.name: "dialog-close"
|
text: i18n("Cancel sending attachment")
|
||||||
onClicked: root.attachmentCancelled()
|
icon.name: "dialog-close"
|
||||||
|
onTriggered: attachmentCancelled()
|
||||||
|
shortcut: "Escape"
|
||||||
|
}
|
||||||
QQC2.ToolTip.text: text
|
QQC2.ToolTip.text: text
|
||||||
QQC2.ToolTip.visible: hovered
|
QQC2.ToolTip.visible: hovered
|
||||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
||||||
|
|
||||||
Kirigami.Action {
|
|
||||||
shortcut: "Escape"
|
|
||||||
onTriggered: cancelAttachmentButton.clicked()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,8 +75,8 @@ ColumnLayout {
|
|||||||
|
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
cache: false // Cache is not needed. Images will rarely be shown repeatedly.
|
cache: false // Cache is not needed. Images will rarely be shown repeatedly.
|
||||||
source: root.hasImage ? root.attachmentPath : ""
|
source: hasImage ? root.attachmentPath : ""
|
||||||
visible: root.hasImage
|
visible: hasImage
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
onSourceChanged: {
|
onSourceChanged: {
|
||||||
@@ -118,11 +114,11 @@ ColumnLayout {
|
|||||||
id: mimetypeIcon
|
id: mimetypeIcon
|
||||||
implicitWidth: Kirigami.Units.iconSizes.smallMedium
|
implicitWidth: Kirigami.Units.iconSizes.smallMedium
|
||||||
implicitHeight: Kirigami.Units.iconSizes.smallMedium
|
implicitHeight: Kirigami.Units.iconSizes.smallMedium
|
||||||
source: root.attachmentMimetype.iconName
|
source: attachmentMimetype.iconName
|
||||||
}
|
}
|
||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
id: fileLabel
|
id: fileLabel
|
||||||
text: root.baseFileName
|
text: baseFileName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls as QQC2
|
import QtQuick.Controls as QQC2
|
||||||
|
import QtQuick.Templates as T
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.delegates as Delegates
|
import org.kde.kirigamiaddons.delegates as Delegates
|
||||||
|
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||||
|
|
||||||
Delegates.RoundedItemDelegate {
|
Delegates.RoundedItemDelegate {
|
||||||
id: root
|
id: root
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ Components.AbstractMaximizeComponent {
|
|||||||
implicitWidth: Kirigami.Units.iconSizes.medium
|
implicitWidth: Kirigami.Units.iconSizes.medium
|
||||||
implicitHeight: Kirigami.Units.iconSizes.medium
|
implicitHeight: Kirigami.Units.iconSizes.medium
|
||||||
|
|
||||||
name: root.author.displayName
|
name: root.author.name ?? root.author.displayName
|
||||||
source: root.author.avatarUrl
|
source: root.author.avatarUrl
|
||||||
color: root.author.color
|
color: root.author.color
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@ Components.AbstractMaximizeComponent {
|
|||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
id: userLabel
|
id: userLabel
|
||||||
|
|
||||||
text: root.author.displayName
|
text: root.author.name ?? root.author.displayName
|
||||||
color: root.author.color
|
color: root.author.color
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
@@ -91,7 +91,6 @@ Components.AbstractMaximizeComponent {
|
|||||||
color: Kirigami.Theme.textColor
|
color: Kirigami.Theme.textColor
|
||||||
|
|
||||||
font.family: "monospace"
|
font.family: "monospace"
|
||||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize * NeoChatConfig.fontScale
|
|
||||||
|
|
||||||
Kirigami.SpellCheck.enabled: false
|
Kirigami.SpellCheck.enabled: false
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import QtQml.Models
|
|||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||||
|
import org.kde.kitemmodels
|
||||||
|
|
||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
|
|
||||||
|
|||||||
86
src/app/qml/EditMenu.qml
Normal file
86
src/app/qml/EditMenu.qml
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2021 Carson Black <uhhadd@gmail.com>
|
||||||
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
|
import Qt.labs.platform as Labs
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
Labs.Menu {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property Item field
|
||||||
|
|
||||||
|
Labs.MenuItem {
|
||||||
|
enabled: root.field !== null && root.field.canUndo
|
||||||
|
text: i18nc("text editing menu action", "Undo")
|
||||||
|
shortcut: StandardKey.Undo
|
||||||
|
onTriggered: {
|
||||||
|
root.field.undo();
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Labs.MenuItem {
|
||||||
|
enabled: root.field !== null && root.field.canRedo
|
||||||
|
text: i18nc("text editing menu action", "Redo")
|
||||||
|
shortcut: StandardKey.Redo
|
||||||
|
onTriggered: {
|
||||||
|
root.field.undo();
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Labs.MenuSeparator {}
|
||||||
|
|
||||||
|
Labs.MenuItem {
|
||||||
|
enabled: root.field !== null && root.field.selectedText
|
||||||
|
text: i18nc("text editing menu action", "Cut")
|
||||||
|
shortcut: StandardKey.Cut
|
||||||
|
onTriggered: {
|
||||||
|
root.field.cut();
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Labs.MenuItem {
|
||||||
|
enabled: root.field !== null && root.field.selectedText
|
||||||
|
text: i18nc("text editing menu action", "Copy")
|
||||||
|
shortcut: StandardKey.Copy
|
||||||
|
onTriggered: {
|
||||||
|
root.field.copy();
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Labs.MenuItem {
|
||||||
|
enabled: root.field !== null && root.field.canPaste
|
||||||
|
text: i18nc("text editing menu action", "Paste")
|
||||||
|
shortcut: StandardKey.Paste
|
||||||
|
onTriggered: {
|
||||||
|
root.field.paste();
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Labs.MenuItem {
|
||||||
|
enabled: root.field !== null && root.field.selectedText !== ""
|
||||||
|
text: i18nc("text editing menu action", "Delete")
|
||||||
|
shortcut: ""
|
||||||
|
onTriggered: {
|
||||||
|
root.field.remove(root.field.selectionStart, root.field.selectionEnd);
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Labs.MenuSeparator {}
|
||||||
|
|
||||||
|
Labs.MenuItem {
|
||||||
|
enabled: root.field !== null
|
||||||
|
text: i18nc("text editing menu action", "Select All")
|
||||||
|
shortcut: StandardKey.SelectAll
|
||||||
|
onTriggered: {
|
||||||
|
root.field.selectAll();
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,15 +6,16 @@ import QtQuick.Controls as QQC2
|
|||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
|
import org.kde.neochat
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property string emoji
|
property alias emoji: emojiLabel.text
|
||||||
required property string description
|
property alias description: descriptionLabel.text
|
||||||
|
|
||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
text: root.emoji
|
id: emojiLabel
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
|
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
|
||||||
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
|
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
|
||||||
@@ -24,7 +25,7 @@ ColumnLayout {
|
|||||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 4
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 4
|
||||||
}
|
}
|
||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
text: root.description
|
id: descriptionLabel
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ RowLayout {
|
|||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: repeater
|
id: repeater
|
||||||
delegate: EmojiItem {}
|
delegate: EmojiItem {
|
||||||
|
emoji: modelData.emoji
|
||||||
|
description: modelData.description
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ ApplicationWindow {
|
|||||||
property real longitude: NaN
|
property real longitude: NaN
|
||||||
property string asset
|
property string asset
|
||||||
property var author
|
property var author
|
||||||
property LiveLocationsModel liveLocationModel: null
|
property QtObject liveLocationModel: null
|
||||||
|
|
||||||
flags: Qt.FramelessWindowHint | Qt.WA_TranslucentBackground
|
flags: Qt.FramelessWindowHint | Qt.WA_TranslucentBackground
|
||||||
visibility: Qt.WindowFullScreen
|
visibility: Qt.WindowFullScreen
|
||||||
@@ -59,7 +59,7 @@ ApplicationWindow {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: mapView.map
|
target: mapView.map
|
||||||
function onCopyrightLinkActivated(link: string) {
|
function onCopyrightLinkActivated() {
|
||||||
Qt.openUrlExternally(link);
|
Qt.openUrlExternally(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import Qt.labs.platform as Labs
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Window
|
import QtQuick.Window
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
@@ -15,48 +16,12 @@ Labs.MenuBar {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property NeoChatConnection connection
|
required property NeoChatConnection connection
|
||||||
required property Kirigami.ApplicationWindow appWindow
|
|
||||||
|
|
||||||
Labs.Menu {
|
Labs.Menu {
|
||||||
title: i18nc("menu", "File")
|
title: i18nc("menu", "NeoChat")
|
||||||
|
|
||||||
Labs.MenuItem {
|
Labs.MenuItem {
|
||||||
icon.name: "list-add-user"
|
enabled: pageStack.layers.currentItem.title !== i18n("Configure NeoChat…")
|
||||||
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 public rooms and spaces", "Explore")
|
|
||||||
enabled: root.connection
|
|
||||||
onTriggered: {
|
|
||||||
let dialog = root.appWindow.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), {
|
|
||||||
connection: root.connection
|
|
||||||
}, {});
|
|
||||||
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…")
|
text: i18nc("menu", "Configure NeoChat…")
|
||||||
|
|
||||||
shortcut: StandardKey.Preferences
|
shortcut: StandardKey.Preferences
|
||||||
@@ -69,15 +34,58 @@ Labs.MenuBar {
|
|||||||
onTriggered: Qt.quit()
|
onTriggered: Qt.quit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Labs.Menu {
|
||||||
|
title: i18nc("menu", "File")
|
||||||
|
|
||||||
|
Labs.MenuItem {
|
||||||
|
icon.name: "list-add-user"
|
||||||
|
text: i18nc("@action:inmenu", "Find your Friends")
|
||||||
|
enabled: pageStack.layers.currentItem.title !== i18n("Find your friends") && AccountRegistry.accountCount > 0
|
||||||
|
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
|
||||||
|
connection: root.connection
|
||||||
|
}, {
|
||||||
|
title: i18nc("@title", "Find your friends")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Labs.MenuItem {
|
||||||
|
icon.name: "system-users-symbolic"
|
||||||
|
text: i18nc("@action:inmenu", "Create a Room…")
|
||||||
|
enabled: pageStack.layers.currentItem.title !== i18n("Find your friends") && AccountRegistry.accountCount > 0
|
||||||
|
shortcut: StandardKey.New
|
||||||
|
onTriggered: {
|
||||||
|
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'CreateRoomDialog'), {
|
||||||
|
connection: root.connection
|
||||||
|
}, {
|
||||||
|
title: i18nc("@title", "Create a Room")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Labs.MenuItem {
|
||||||
|
icon.name: "compass-symbolic"
|
||||||
|
text: i18nc("@action:inmenu", "Explore Rooms")
|
||||||
|
onTriggered: {
|
||||||
|
let dialog = 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");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EditMenu {
|
||||||
|
title: i18nc("menu", "Edit")
|
||||||
|
field: (root.activeFocusItem instanceof TextEdit || root.activeFocusItem instanceof TextInput) ? root.activeFocusItem : null
|
||||||
|
}
|
||||||
Labs.Menu {
|
Labs.Menu {
|
||||||
title: i18nc("menu", "View")
|
title: i18nc("menu", "View")
|
||||||
|
|
||||||
Labs.MenuItem {
|
Labs.MenuItem {
|
||||||
icon.name: "search-symbolic"
|
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")
|
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()
|
onTriggered: quickSwitcher.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Labs.Menu {
|
Labs.Menu {
|
||||||
@@ -85,8 +93,8 @@ Labs.MenuBar {
|
|||||||
|
|
||||||
Labs.MenuItem {
|
Labs.MenuItem {
|
||||||
icon.name: "view-fullscreen-symbolic"
|
icon.name: "view-fullscreen-symbolic"
|
||||||
text: root.appWindow.visibility === Window.FullScreen ? i18nc("menu", "Exit Full Screen") : i18nc("menu", "Enter Full Screen")
|
text: root.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()
|
onTriggered: root.visibility === Window.FullScreen ? root.showNormal() : root.showFullScreen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Labs.Menu {
|
Labs.Menu {
|
||||||
@@ -95,12 +103,12 @@ Labs.MenuBar {
|
|||||||
Labs.MenuItem {
|
Labs.MenuItem {
|
||||||
icon.name: "help-about-symbolic"
|
icon.name: "help-about-symbolic"
|
||||||
text: i18nc("menu", "About NeoChat")
|
text: i18nc("menu", "About NeoChat")
|
||||||
onTriggered: root.appWindow.pageStack.pushDialogLayer(Qt.createComponent("org.kde.kirigamiaddons.formcard", "AboutPage"))
|
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.kirigamiaddons.formcard", "AboutPage"))
|
||||||
}
|
}
|
||||||
Labs.MenuItem {
|
Labs.MenuItem {
|
||||||
icon.name: "kde-symbolic"
|
icon.name: "kde-symbolic"
|
||||||
text: i18nc("menu", "About KDE")
|
text: i18nc("menu", "About KDE")
|
||||||
onTriggered: root.appWindow.pageStack.pushDialogLayer(Qt.createComponent("org.kde.kirigamiaddons.formcard", "AboutKDEPage"))
|
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.kirigamiaddons.formcard", "AboutKDEPage"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,8 @@
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
|
||||||
import org.kde.neochat
|
import org.kde.neochat
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
required property NeoChatConnection connection
|
required property NeoChatConnection connection
|
||||||
required property Kirigami.ApplicationWindow appWindow
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user