Compare commits
198 Commits
work/redst
...
release/23
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73e7ad9519 | ||
|
|
6e34a3bc90 | ||
|
|
549eba3449 | ||
|
|
c469893bdc | ||
|
|
181dbc7a23 | ||
|
|
d26a691647 | ||
|
|
0cd0a6a672 | ||
|
|
4892e12918 | ||
|
|
6baa374a1a | ||
|
|
d2f1fc7175 | ||
|
|
8e998ac72b | ||
|
|
eabc0e3a65 | ||
|
|
ba7b6da025 | ||
|
|
cb7c33075c | ||
|
|
3b94847b17 | ||
|
|
0e9512b9ea | ||
|
|
33f8b5eb55 | ||
|
|
028d8c0584 | ||
|
|
bf4f1a34dc | ||
|
|
5ea0f5d378 | ||
|
|
5b76e8cda8 | ||
|
|
4c62cf4b0a | ||
|
|
2dfa11a0aa | ||
|
|
367d106d14 | ||
|
|
6404e66686 | ||
|
|
1e3b5c5d13 | ||
|
|
ba26d998c0 | ||
|
|
e624f34710 | ||
|
|
36c3850cb3 | ||
|
|
10e570b429 | ||
|
|
5cd0872904 | ||
|
|
c1ce40ce99 | ||
|
|
0da2be9ad0 | ||
|
|
be24122eb8 | ||
|
|
e6803f4e28 | ||
|
|
3b367b7c9d | ||
|
|
71cda237b1 | ||
|
|
bb6f7ce985 | ||
|
|
17b14aa556 | ||
|
|
7341e5824a | ||
|
|
b9b5264fcf | ||
|
|
3388f8a60d | ||
|
|
d878ef1804 | ||
|
|
7e8a8615b1 | ||
|
|
c0f9b6ba93 | ||
|
|
4c0eb363fa | ||
|
|
2ee3e4d81d | ||
|
|
2cf3070e9f | ||
|
|
207b554c78 | ||
|
|
2a54dbc108 | ||
|
|
0aea54a119 | ||
|
|
753e879556 | ||
|
|
6e6636e384 | ||
|
|
3574c2e077 | ||
|
|
f947320191 | ||
|
|
0f8ed0bfc7 | ||
|
|
ccd0bd5c6d | ||
|
|
a4020a7def | ||
|
|
1c9487df7e | ||
|
|
526db7b73a | ||
|
|
d9024e98c9 | ||
|
|
8697dde08d | ||
|
|
9607424389 | ||
|
|
9e22b0b61a | ||
|
|
148ac2f91a | ||
|
|
9c4a4bb77f | ||
|
|
00f880f7ff | ||
|
|
703678ccea | ||
|
|
d2ac959a1c | ||
|
|
da0e84eacb | ||
|
|
463172eca6 | ||
|
|
ffbd92317e | ||
|
|
7c9173eb65 | ||
|
|
c6a5a119e8 | ||
|
|
edf2da1413 | ||
|
|
3cacb10b9c | ||
|
|
ce19275d8d | ||
|
|
1d050c5264 | ||
|
|
5f3ab888d0 | ||
|
|
a7993d7b59 | ||
|
|
eb837d2435 | ||
|
|
c57354f974 | ||
|
|
81a3ab95c5 | ||
|
|
0408956364 | ||
|
|
161412cbcf | ||
|
|
fdb02bebf5 | ||
|
|
a756288a07 | ||
|
|
4853212326 | ||
|
|
3f36cbc2a4 | ||
|
|
ac9629258f | ||
|
|
c085a5815b | ||
|
|
7a25b151d4 | ||
|
|
fe3933c96a | ||
|
|
3f59d988e7 | ||
|
|
4355afae30 | ||
|
|
99c7d0f5cc | ||
|
|
528ad54136 | ||
|
|
ee18e92d12 | ||
|
|
67c0bf1881 | ||
|
|
4e0c0fc3e4 | ||
|
|
4886f1c3b9 | ||
|
|
d1efa96b79 | ||
|
|
56d7d1f313 | ||
|
|
04682b077a | ||
|
|
ed496e04bb | ||
|
|
8439768cc3 | ||
|
|
33a4d476c6 | ||
|
|
bb2e2fddcc | ||
|
|
0d890ce3d6 | ||
|
|
081dd5f1b0 | ||
|
|
79bba63864 | ||
|
|
2b131b58a9 | ||
|
|
97b7047a48 | ||
|
|
91b5573016 | ||
|
|
8e9bb8a7e4 | ||
|
|
916755b4ad | ||
|
|
9ce288fe47 | ||
|
|
aa461fe078 | ||
|
|
15d8c39185 | ||
|
|
4a1aa97d7b | ||
|
|
e332179ac4 | ||
|
|
7534f293c5 | ||
|
|
fc60a7c13e | ||
|
|
48b8703156 | ||
|
|
5ca85dc670 | ||
|
|
bf8ea41e6a | ||
|
|
fda4ae8735 | ||
|
|
aa0e9fff15 | ||
|
|
28391b53e5 | ||
|
|
a8d7744b42 | ||
|
|
9fcc858841 | ||
|
|
f7092eee69 | ||
|
|
9a426f941a | ||
|
|
c77dee09f5 | ||
|
|
26dde017d0 | ||
|
|
5b27a84bcc | ||
|
|
fc3c36838c | ||
|
|
1fd2072cf8 | ||
|
|
7770d412b3 | ||
|
|
56184920b4 | ||
|
|
b61685b151 | ||
|
|
be0212a686 | ||
|
|
37ed09f957 | ||
|
|
1b7e8ae77a | ||
|
|
7571b0a9a3 | ||
|
|
9477e6343f | ||
|
|
83241ea956 | ||
|
|
f054467878 | ||
|
|
fb7154b492 | ||
|
|
973a75128c | ||
|
|
9ea70d7eac | ||
|
|
7f8285037c | ||
|
|
c5f19f12e5 | ||
|
|
ed6379ff85 | ||
|
|
9750df90e0 | ||
|
|
bd90658668 | ||
|
|
f78b634720 | ||
|
|
250476f77c | ||
|
|
e293d1f644 | ||
|
|
cde50588cb | ||
|
|
3972dab7b9 | ||
|
|
7c1b43bcad | ||
|
|
c22c74f735 | ||
|
|
9c42b3c1a7 | ||
|
|
d45cd5d9c9 | ||
|
|
8c503b8258 | ||
|
|
7078caac15 | ||
|
|
94bf65ab43 | ||
|
|
244e93c926 | ||
|
|
412856e882 | ||
|
|
2421f11717 | ||
|
|
2d42cfedec | ||
|
|
9308c84696 | ||
|
|
72fc242364 | ||
|
|
7d3240c597 | ||
|
|
9906ba8b7d | ||
|
|
b949092bc6 | ||
|
|
5cce3bd692 | ||
|
|
ef5d67e5bf | ||
|
|
4711b954c5 | ||
|
|
1688b00f48 | ||
|
|
410befa4bf | ||
|
|
b380c74b0e | ||
|
|
da8227931f | ||
|
|
fdc45ec5c8 | ||
|
|
94dd69ea02 | ||
|
|
80c3253a6e | ||
|
|
c5f6a6607e | ||
|
|
a10d5cbe75 | ||
|
|
584cad19b1 | ||
|
|
3745414635 | ||
|
|
147ce42c04 | ||
|
|
928feeca82 | ||
|
|
fc04af536d | ||
|
|
12b949d6cb | ||
|
|
6aaaabf159 | ||
|
|
fe45ef85a6 | ||
|
|
13e38ed982 |
@@ -2,9 +2,4 @@
|
||||
; SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
[BlueprintSettings]
|
||||
kde/unreleased/kirigami-addons.version=master
|
||||
kde/frameworks.version=master
|
||||
kde/libs.version=master
|
||||
kde/plasma.version=master
|
||||
kde/unreleased.version=master
|
||||
libs/qt.qtMajorVersion=6
|
||||
kde/applications/neochat.packageAppx = True
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"id": "org.kde.neochat",
|
||||
"branch": "master",
|
||||
"runtime": "org.kde.Platform",
|
||||
"runtime-version": "6.6-kf6preview",
|
||||
"runtime-version": "5.15-22.08",
|
||||
"sdk": "org.kde.Sdk",
|
||||
"command": "neochat",
|
||||
"tags": [
|
||||
@@ -12,14 +12,13 @@
|
||||
"finish-args": [
|
||||
"--share=network",
|
||||
"--share=ipc",
|
||||
"--socket=fallback-x11",
|
||||
"--socket=x11",
|
||||
"--socket=wayland",
|
||||
"--device=dri",
|
||||
"--filesystem=xdg-download",
|
||||
"--talk-name=org.freedesktop.Notifications",
|
||||
"--talk-name=org.kde.kwalletd5",
|
||||
"--talk-name=org.kde.StatusNotifierWatcher",
|
||||
"--talk-name=org.freedesktop.secrets",
|
||||
"--own-name=org.kde.StatusNotifierItem-2-2"
|
||||
],
|
||||
"modules": [
|
||||
@@ -27,11 +26,10 @@
|
||||
"name": "kirigamiaddons",
|
||||
"config-opts": [ "-DBUILD_TESTING=OFF" ],
|
||||
"buildsystem": "cmake-ninja",
|
||||
"sources": [ { "type": "git", "url": "https://invent.kde.org/libraries/kirigami-addons.git" } ]
|
||||
"sources": [ { "type": "git", "url": "https://invent.kde.org/libraries/kirigami-addons.git", "branch": "kf5" } ]
|
||||
},
|
||||
{
|
||||
"name": "kquickimageeditor",
|
||||
"config-opts": [ "-DBUILD_WITH_QT6=ON" ],
|
||||
"buildsystem": "cmake-ninja",
|
||||
"sources": [
|
||||
{
|
||||
@@ -86,8 +84,8 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/frankosterfeld/qtkeychain/archive/0.14.2.tar.gz",
|
||||
"sha256": "cf2e972b783ba66334a79a30f6b3a1ea794a1dc574d6c3bebae5ffd2f0399571",
|
||||
"url": "https://github.com/frankosterfeld/qtkeychain/archive/v0.13.2.tar.gz",
|
||||
"sha256": "20beeb32de7c4eb0af9039b21e18370faf847ac8697ab3045906076afbc4caa5",
|
||||
"x-checker-data": {
|
||||
"type": "anitya",
|
||||
"project-id": 4138,
|
||||
@@ -97,7 +95,6 @@
|
||||
}
|
||||
],
|
||||
"config-opts": [
|
||||
"-DBUILD_WITH_QT6=ON",
|
||||
"-DCMAKE_INSTALL_LIBDIR=/app/lib",
|
||||
"-DLIB_INSTALL_DIR=/app/lib",
|
||||
"-DBUILD_TRANSLATIONS=NO"
|
||||
@@ -115,7 +112,6 @@
|
||||
}
|
||||
],
|
||||
"config-opts": [
|
||||
"-DBUILD_WITH_QT6=ON",
|
||||
"-DQuotient_ENABLE_E2EE=ON",
|
||||
"-DBUILD_TESTING=OFF"
|
||||
]
|
||||
|
||||
@@ -5,9 +5,12 @@ include:
|
||||
- project: sysadmin/ci-utilities
|
||||
file:
|
||||
- /gitlab-templates/reuse-lint.yml
|
||||
- /gitlab-templates/android-qt6.yml
|
||||
- /gitlab-templates/android.yml
|
||||
# - /gitlab-templates/linux.yml
|
||||
- /gitlab-templates/linux-qt6.yml
|
||||
- /gitlab-templates/windows-qt6.yml
|
||||
- /gitlab-templates/freebsd-qt6.yml
|
||||
- /gitlab-templates/windows.yml
|
||||
# - /gitlab-templates/freebsd.yml
|
||||
- /gitlab-templates/flatpak.yml
|
||||
- /gitlab-templates/craft-android-qt6-apks.yml
|
||||
- /gitlab-templates/craft-android-apks.yml
|
||||
- /gitlab-templates/craft-windows-x86-64.yml
|
||||
- /gitlab-templates/craft-windows-appx-qt5.yml
|
||||
|
||||
45
.kde-ci.yml
45
.kde-ci.yml
@@ -2,7 +2,35 @@
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
Dependencies:
|
||||
- 'on': ['Linux', 'Android', 'FreeBSD', 'Windows']
|
||||
- 'on': ['Linux/Qt5', 'Android/Qt5', 'FreeBSD/Qt5', 'Windows/Qt5']
|
||||
'require':
|
||||
'frameworks/extra-cmake-modules': '@stable'
|
||||
'frameworks/kcoreaddons': '@stable'
|
||||
'frameworks/kirigami': '@stable'
|
||||
'frameworks/ki18n': '@stable'
|
||||
'frameworks/kconfig': '@stable'
|
||||
'frameworks/syntax-highlighting': '@stable'
|
||||
'frameworks/kitemmodels': '@stable'
|
||||
'frameworks/kquickcharts': '@stable'
|
||||
'frameworks/knotifications': '@stable'
|
||||
'libraries/kquickimageeditor': '@stable'
|
||||
'frameworks/sonnet': '@stable'
|
||||
'libraries/kirigami-addons': '@latest'
|
||||
'third-party/libquotient': '@latest'
|
||||
'third-party/qtkeychain': '@latest'
|
||||
'third-party/cmark': '@latest'
|
||||
'third-party/qcoro': '@latest'
|
||||
- 'on': ['Windows/Qt5', 'Linux/Qt5', 'FreeBSD/Qt5']
|
||||
'require':
|
||||
'frameworks/qqc2-desktop-style': '@stable'
|
||||
'frameworks/kio': '@stable'
|
||||
'frameworks/kwindowsystem': '@stable'
|
||||
'frameworks/kconfigwidgets': '@stable'
|
||||
- 'on': ['Linux/Qt5', 'FreeBSD/Qt5']
|
||||
'require':
|
||||
'frameworks/kdbusaddons': '@stable'
|
||||
|
||||
- 'on': ['Linux/Qt6', 'Android/Qt6', 'FreeBSD/Qt6', 'Windows/Qt6']
|
||||
'require':
|
||||
'frameworks/extra-cmake-modules': '@latest-kf6'
|
||||
'frameworks/kcoreaddons': '@latest-kf6'
|
||||
@@ -13,29 +41,22 @@ Dependencies:
|
||||
'frameworks/kitemmodels': '@latest-kf6'
|
||||
'frameworks/kquickcharts': '@latest-kf6'
|
||||
'frameworks/knotifications': '@latest-kf6'
|
||||
'frameworks/kcolorscheme': '@latest-kf6'
|
||||
'libraries/kquickimageeditor': '@latest-kf6'
|
||||
'frameworks/sonnet': '@latest-kf6'
|
||||
'frameworks/prison': '@latest-kf6'
|
||||
'libraries/kirigami-addons': '@latest-kf6'
|
||||
'third-party/libquotient': '@latest'
|
||||
'third-party/qtkeychain': '@latest'
|
||||
'third-party/cmark': '@latest'
|
||||
'third-party/qcoro': '@latest'
|
||||
- 'on': ['Windows', 'Linux', 'FreeBSD']
|
||||
- 'on': ['Windows/Qt6', 'Linux/Qt6', 'FreeBSD/Qt6']
|
||||
'require':
|
||||
'frameworks/qqc2-desktop-style': '@latest-kf6'
|
||||
'frameworks/kio': '@latest-kf6'
|
||||
'frameworks/kwindowsystem': '@latest-kf6'
|
||||
'frameworks/kstatusnotifieritem': '@latest-kf6'
|
||||
- 'on': ['Linux', 'FreeBSD']
|
||||
'frameworks/kconfigwidgets': '@latest-kf6'
|
||||
- 'on': ['Linux/Qt6', 'FreeBSD/Qt6']
|
||||
'require':
|
||||
'frameworks/kdbusaddons': '@latest-kf6'
|
||||
|
||||
- 'on': ['Linux']
|
||||
'require':
|
||||
'sdk/selenium-webdriver-at-spi': '@latest-kf6'
|
||||
|
||||
Options:
|
||||
per-test-timeout: 90
|
||||
require-passing-tests-on: [ '@all' ]
|
||||
require-passing-tests-on: [ 'Linux/Qt5', 'FreeBSD' ]
|
||||
|
||||
10
.reuse/dep5
10
.reuse/dep5
@@ -34,18 +34,10 @@ Files: src/neochat.notifyrc
|
||||
Copyright: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: src/qml/confetti.png src/qml/glowdot.png
|
||||
Files: src/qml/Component/confetti.png src/qml/Component/glowdot.png
|
||||
Copyright: 2021 Alexey Andreyev <aa13q@ya.ru>
|
||||
License: CC0-1.0
|
||||
|
||||
Files: .flatpak-manifest.json
|
||||
Copyright: 2020-2022 Tobias Fella <tobias.fella@kde.org>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: autotests/data/*
|
||||
Copyright: none
|
||||
License: CC0-1.0
|
||||
|
||||
Files: appiumtests/data/*
|
||||
Copyright: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
License: CC0-1.0
|
||||
|
||||
111
CMakeLists.txt
111
CMakeLists.txt
@@ -7,15 +7,18 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# KDE Applications version, managed by release script.
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "24")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "01")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "85")
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "23")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "08")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "5")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||
|
||||
set(KF_MIN_VERSION "5.240.0")
|
||||
set(QT_MIN_VERSION "6.5")
|
||||
set(KF_MIN_VERSION "5.105.0")
|
||||
set(QT_MIN_VERSION "5.15.2")
|
||||
if (ANDROID)
|
||||
set(QT_MIN_VERSION "5.15.10")
|
||||
endif()
|
||||
|
||||
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
|
||||
|
||||
@@ -24,7 +27,7 @@ set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(KDE_COMPILERSETTINGS_LEVEL 5.105)
|
||||
set(KDE_COMPILERSETTINGS_LEVEL 5.84)
|
||||
|
||||
include(FeatureSummary)
|
||||
include(ECMSetupVersion)
|
||||
@@ -37,7 +40,6 @@ include(ECMAddAppIcon)
|
||||
include(KDEGitCommitHooks)
|
||||
include(ECMCheckOutboundLicense)
|
||||
include(ECMQtDeclareLoggingCategory)
|
||||
include(ECMAddAndroidApk)
|
||||
if (NOT ANDROID)
|
||||
include(KDEClangFormat)
|
||||
endif()
|
||||
@@ -46,6 +48,28 @@ if(NEOCHAT_FLATPAK)
|
||||
include(cmake/Flatpak.cmake)
|
||||
endif()
|
||||
|
||||
if(QT_MAJOR_VERSION STREQUAL "6")
|
||||
set(BASICLISTITEM_BOLD "font.bold")
|
||||
set(OVERLAYSHEET_OPEN "onOpened")
|
||||
set(QTQUICK_MODULE_QML_VERSION "")
|
||||
set(QTLOCATION_MODULE_QML_VERSION "")
|
||||
set(QTMULTIMEDIA_MODULE_QML_VERSION "")
|
||||
set(QTMULTIMEDIA_AUDIO "MediaPlayer")
|
||||
# in Audio qt6 we don't have it but we disable it in qt5 => it seems ok
|
||||
set(QTMULTIMEDIA_AUDIO_AUTOLOAD "")
|
||||
# In Video qml qt6 we don't have it.
|
||||
set(QTMULTIMEDIA_VIDEO_FLUSHMODE "")
|
||||
else()
|
||||
set(BASICLISTITEM_BOLD "bold")
|
||||
set(OVERLAYSHEET_OPEN "onSheetOpenChanged")
|
||||
set(QTQUICK_MODULE_QML_VERSION "2.15")
|
||||
set(QTLOCATION_MODULE_QML_VERSION "5.15")
|
||||
set(QTMULTIMEDIA_MODULE_QML_VERSION "5.15")
|
||||
set(QTMULTIMEDIA_AUDIO "Audio")
|
||||
set(QTMULTIMEDIA_AUDIO_AUTOLOAD "autoLoad: false")
|
||||
set(QTMULTIMEDIA_VIDEO_FLUSHMODE "flushMode: VideoOutput.FirstFrame")
|
||||
endif()
|
||||
|
||||
set(QUOTIENT_FORCE_NAMESPACED_INCLUDES TRUE)
|
||||
|
||||
ecm_setup_version(${PROJECT_VERSION}
|
||||
@@ -53,21 +77,27 @@ ecm_setup_version(${PROJECT_VERSION}
|
||||
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
|
||||
)
|
||||
|
||||
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg WebView)
|
||||
set_package_properties(Qt6 PROPERTIES
|
||||
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg)
|
||||
set_package_properties(Qt${QT_MAJOR_VERSION} PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
)
|
||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels ColorScheme)
|
||||
set_package_properties(KF6 PROPERTIES
|
||||
find_package(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} COMPONENTS Kirigami2 I18n Notifications Config CoreAddons Sonnet ItemModels)
|
||||
set_package_properties(KF${QT_MAJOR_VERSION} PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
)
|
||||
set_package_properties(KF6Kirigami PROPERTIES
|
||||
set_package_properties(KF${QT_MAJOR_VERSION}Kirigami2 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Kirigami application UI framework"
|
||||
)
|
||||
find_package(KF${QT_MAJOR_VERSION}KirigamiAddons 0.7.2 REQUIRED)
|
||||
|
||||
find_package(Qt${QT_MAJOR_VERSION}Keychain)
|
||||
set_package_properties(Qt${QT_MAJOR_VERSION}Keychain PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Kirigami application UI framework"
|
||||
PURPOSE "Secure storage of account secrets"
|
||||
)
|
||||
find_package(KF6KirigamiAddons 0.7.2 REQUIRED)
|
||||
|
||||
if(ANDROID)
|
||||
find_package(OpenSSL)
|
||||
@@ -76,37 +106,30 @@ if(ANDROID)
|
||||
PURPOSE "Encrypted communications"
|
||||
)
|
||||
else()
|
||||
find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Widgets)
|
||||
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem)
|
||||
set_package_properties(KF6QQC2DesktopStyle PROPERTIES
|
||||
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} COMPONENTS Widgets)
|
||||
find_package(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle ConfigWidgets KIO WindowSystem)
|
||||
set_package_properties(KF${QT_MAJOR_VERSION}QQC2DesktopStyle PROPERTIES
|
||||
TYPE RUNTIME
|
||||
)
|
||||
ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0)
|
||||
|
||||
find_package(ICU 61.0 COMPONENTS uc)
|
||||
set_package_properties(ICU PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Unicode library"
|
||||
)
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED)
|
||||
find_package(KF${QT_MAJOR_VERSION}DBusAddons ${KF_MIN_VERSION} REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(QuotientQt6 0.7)
|
||||
set_package_properties(QuotientQt6 PROPERTIES
|
||||
if(QT_MAJOR_VERSION STREQUAL "6" AND NOT ANDROID AND NOT WIN32)
|
||||
set(QUOTIENT_SUFFIX "Qt6")
|
||||
endif()
|
||||
|
||||
find_package(Quotient${QUOTIENT_SUFFIX} 0.7)
|
||||
set_package_properties(Quotient${QUOTIENT_SUFFIX} PROPERTIES
|
||||
TYPE REQUIRED
|
||||
DESCRIPTION "Qt wrapper around Matrix API"
|
||||
URL "https://github.com/quotient-im/libQuotient/"
|
||||
PURPOSE "Talk with matrix server"
|
||||
)
|
||||
|
||||
if (NOT TARGET Olm::Olm)
|
||||
message(FATAL_ERROR "NeoChat requires Quotient with the E2EE feature enabled")
|
||||
endif()
|
||||
|
||||
|
||||
find_package(cmark)
|
||||
set_package_properties(cmark PROPERTIES
|
||||
TYPE REQUIRED
|
||||
@@ -118,8 +141,7 @@ set_package_properties(cmark PROPERTIES
|
||||
ecm_find_qmlmodule(org.kde.kquickimageeditor 1.0)
|
||||
ecm_find_qmlmodule(org.kde.kitemmodels 1.0)
|
||||
ecm_find_qmlmodule(org.kde.quickcharts 1.0)
|
||||
ecm_find_qmlmodule(QtLocation)
|
||||
ecm_find_qmlmodule(org.kde.prison)
|
||||
ecm_find_qmlmodule(QtLocation ${QTLOCATION_MODULE_QML_VERSION})
|
||||
|
||||
find_package(KQuickImageEditor COMPONENTS)
|
||||
set_package_properties(KQuickImageEditor PROPERTIES
|
||||
@@ -129,22 +151,19 @@ set_package_properties(KQuickImageEditor PROPERTIES
|
||||
PURPOSE "Add image editing capability to image attachments"
|
||||
)
|
||||
|
||||
find_package(QCoro6 0.4 COMPONENTS Core Network REQUIRED)
|
||||
find_package(QCoro${QT_MAJOR_VERSION} 0.4 COMPONENTS Core REQUIRED)
|
||||
|
||||
qcoro_enable_coroutines()
|
||||
|
||||
find_package(KF6DocTools ${KF_MIN_VERSION})
|
||||
set_package_properties(KF6DocTools PROPERTIES DESCRIPTION
|
||||
find_package(KF${QT_MAJOR_VERSION}DocTools ${KF_MIN_VERSION})
|
||||
set_package_properties(KF${QT_MAJOR_VERSION}DocTools PROPERTIES DESCRIPTION
|
||||
"Tools to generate documentation"
|
||||
TYPE OPTIONAL
|
||||
)
|
||||
|
||||
find_package(KUnifiedPush QUIET)
|
||||
set_package_properties(KUnifiedPush PROPERTIES
|
||||
TYPE OPTIONAL
|
||||
PURPOSE "Push notification support"
|
||||
URL "https://invent.kde.org/libraries/kunifiedpush"
|
||||
)
|
||||
if(NOT Quotient${QUOTIENT_SUFFIX}_VERSION_MINOR GREATER 6)
|
||||
cmake_policy(SET CMP0063 OLD)
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
find_package(Sqlite3)
|
||||
@@ -161,14 +180,12 @@ install(FILES org.kde.neochat.tray.svg DESTINATION ${KDE_INSTALL_FULL_ICONDIR}/h
|
||||
add_definitions(-DQT_NO_FOREACH)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
if (BUILD_TESTING)
|
||||
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test)
|
||||
if (BUILD_TESTING AND Quotient${QUOTIENT_SUFFIX}_VERSION_MINOR GREATER 6)
|
||||
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test)
|
||||
add_subdirectory(autotests)
|
||||
add_subdirectory(appiumtests)
|
||||
endif()
|
||||
|
||||
if(KF6DocTools_FOUND)
|
||||
if(KF${QT_MAJOR_VERSION}DocTools_FOUND)
|
||||
kdoctools_install(po)
|
||||
add_subdirectory(doc)
|
||||
endif()
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
|
||||
A Qt/QML based Matrix client.
|
||||
|
||||
<a href='https://matrix.org'><img src='https://matrix.org/docs/legacy/made-for-matrix.png' alt='Made for Matrix' height=64 target=_blank /></a>
|
||||
<a href='https://matrix.org'><img src='https://matrix.org/docs/projects/images/made-for-matrix.png' alt='Made for Matrix' height=64 target=_blank /></a>
|
||||
<a href='https://flathub.org/apps/details/org.kde.neochat'><img width='190px' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-i-en.png'/></a>
|
||||
<a href='https://snapcraft.io/neochat'><img width='190px' alt='Download on the Snap Store' src='https://snapcraft.io/static/images/badges/en/snap-store-black.svg'/></a>
|
||||
|
||||
## Introduction
|
||||
|
||||
@@ -96,7 +95,7 @@ As is the case throughout the KDE ecosystem contributions are welcome from all.
|
||||
|
||||
## Contact
|
||||
|
||||
The best place to reach the maintainers is on the KDE Matrix instance in the NeoChat channel, [#neochat:kde.org](https://go.kde.org/matrix/#/#neochat:kde.org). See [Matrix](https://community.kde.org/Matrix) for more details.
|
||||
The best place to reach the maintainers is on the KDE Matrix instance in the NeoChat channel, [#neochat:kde.org](https://matrix.to/#/#neochat:kde.org).
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
android:versionName="${versionName}"
|
||||
android:versionCode="${versionCode}"
|
||||
android:installLocation="auto">
|
||||
<application android:name="org.qtproject.qt.android.bindings.QtApplication" android:label="NeoChat" android:icon="@drawable/neochat" android:usesCleartextTraffic="true">
|
||||
<application android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="NeoChat" android:icon="@drawable/neochat">
|
||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation"
|
||||
android:name="org.qtproject.qt.android.bindings.QtActivity"
|
||||
android:name="org.qtproject.qt5.android.bindings.QtActivity"
|
||||
android:label="NeoChat"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:launchMode="singleTop"
|
||||
|
||||
@@ -12,7 +12,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.4.1'
|
||||
classpath 'com.android.tools.build:gradle:7.0.2'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ android {
|
||||
* The following variables:
|
||||
* - androidBuildToolsVersion,
|
||||
* - androidCompileSdkVersion
|
||||
* - qtAndroidDir - holds the path to qt android files
|
||||
* - qt5AndroidDir - holds the path to qt android files
|
||||
* needed to build any Qt application
|
||||
* on Android.
|
||||
*
|
||||
@@ -44,20 +44,17 @@ android {
|
||||
* Changing them manually might break the compilation!
|
||||
*******************************************************/
|
||||
|
||||
compileSdkVersion androidCompileSdkVersion
|
||||
compileSdkVersion androidCompileSdkVersion.toInteger()
|
||||
|
||||
buildToolsVersion androidBuildToolsVersion
|
||||
ndkVersion androidNdkVersion
|
||||
|
||||
// Extract native libraries from the APK
|
||||
packagingOptions.jniLibs.useLegacyPackaging true
|
||||
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = [qtAndroidDir + '/src', 'src', 'java']
|
||||
aidl.srcDirs = [qtAndroidDir + '/src', 'src', 'aidl']
|
||||
res.srcDirs = [qtAndroidDir + '/res', 'res']
|
||||
java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
|
||||
aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
|
||||
res.srcDirs = [qt5AndroidDir + '/res', 'res']
|
||||
resources.srcDirs = ['src']
|
||||
renderscript.srcDirs = ['src']
|
||||
assets.srcDirs = ['assets']
|
||||
@@ -92,7 +89,6 @@ android {
|
||||
exclude 'lib/*/*_imageformats_qtga_*'
|
||||
exclude 'lib/*/*_imageformats_qtiff_*'
|
||||
exclude 'lib/*/*_qmltooling_*'
|
||||
exclude 'lib/*/*_multimedia_ffmpeg*' // temporary qt6 android fix
|
||||
}
|
||||
|
||||
aaptOptions {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
# SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
|
||||
|
||||
|
||||
if(NOT BUILD_TESTING OR NOT CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
return()
|
||||
endif()
|
||||
|
||||
find_package(SeleniumWebDriverATSPI)
|
||||
set_package_properties(SeleniumWebDriverATSPI PROPERTIES
|
||||
DESCRIPTION "Server component for selenium tests using Linux accessibility infrastructure"
|
||||
PURPOSE "Needed for GUI tests"
|
||||
URL "https://invent.kde.org/sdk/selenium-webdriver-at-spi"
|
||||
TYPE OPTIONAL
|
||||
)
|
||||
if(NOT SeleniumWebDriverATSPI_FOUND)
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_test(
|
||||
NAME logintest
|
||||
COMMAND selenium-webdriver-at-spi-run ${CMAKE_CURRENT_SOURCE_DIR}/logintest.py
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME openuserdetailstest
|
||||
COMMAND selenium-webdriver-at-spi-run ${CMAKE_CURRENT_SOURCE_DIR}/openuserdetailstest.py
|
||||
)
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"next_batch": "batch1234"
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
{
|
||||
"next_batch": "batch1234",
|
||||
"rooms": {
|
||||
"join": {
|
||||
"!room_id_1234:localhost:1234": {
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"type": "m.room.member",
|
||||
"state_key": "@user:localhost:1234",
|
||||
"sender": "@user:localhost:1234",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"event_id": "$event_id_1234_0:localhost:1234",
|
||||
"room_id": "!room_id_1234:localhost:1234",
|
||||
"content": {
|
||||
"avatar_url": "",
|
||||
"displayname": "A Display Name",
|
||||
"membership": "join",
|
||||
"reason": "Nothing"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"type": "m.room.message",
|
||||
"sender": "@user:localhost:1234",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"event_id": "$event_id_1234_1:localhost:1234",
|
||||
"room_id": "!room_id_1234:localhost:1234",
|
||||
"content": {
|
||||
"body": "This is a message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<a href=\"https://matrix.to/#/@user:localhost:1234\">User</a>:",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
|
||||
import json
|
||||
from flask import Flask, request, abort
|
||||
import os
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/_matrix/client/v3/login", methods=["GET"])
|
||||
def login_get():
|
||||
result = dict()
|
||||
result["flows"] = [dict()]
|
||||
result["flows"][0]["type"] = "m.login.password"
|
||||
return result
|
||||
|
||||
@app.route("/_matrix/client/v3/account/whoami", methods=["GET"])
|
||||
def whoami():
|
||||
result = dict()
|
||||
result["device_id"] = "device_id_1234"
|
||||
result["user_id"] = "@user:localhost:1234"
|
||||
return result
|
||||
|
||||
@app.route("/_matrix/client/v3/login", methods=["POST"])
|
||||
def login_post():
|
||||
data = request.get_json()
|
||||
if data["identifier"]["user"] != "user" or data["password"] != "1234":
|
||||
abort(403)
|
||||
print(data)
|
||||
result = dict()
|
||||
result["access_token"] = "token_login"
|
||||
result["device_id"] = "device_1234"
|
||||
result["user_id"] = "@user:localhost:1234"
|
||||
return result
|
||||
|
||||
def load_json(name):
|
||||
parts = __file__.split("/")
|
||||
parts.pop()
|
||||
datadir = "/".join(parts)
|
||||
return json.loads(open(f"{datadir}/data/{name}.json").read())
|
||||
|
||||
|
||||
@app.route("/_matrix/client/r0/sync")
|
||||
def sync():
|
||||
|
||||
result = load_json("sync_response_no_rooms") if ("login" in request.headers.get("Authorization")) else load_json("sync_response_rooms")
|
||||
return result
|
||||
|
||||
@app.route("/.well-known/matrix/client")
|
||||
def well_known():
|
||||
reply = dict()
|
||||
reply["m.homeserver"] = dict()
|
||||
reply["m.homeserver"]["base_url"] = "https://localhost:1234"
|
||||
return reply
|
||||
|
||||
@app.route("/_matrix/client/v3/profile/<id>")
|
||||
def profile(id):
|
||||
reply = dict()
|
||||
reply["avatar_url"] = "mxc://localhost:1234/asdf1234"
|
||||
reply["displayname"] = "User123"
|
||||
return reply
|
||||
|
||||
@app.route("/_matrix/client/v3/keys/upload", methods=["POST"])
|
||||
def upload_keys():
|
||||
reply = dict()
|
||||
return reply
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(ssl_context='adhoc', port=1234)
|
||||
@@ -1,51 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# SPDX-License-Identifier: MIT
|
||||
# SPDX-FileCopyrightText: 2021-2022 Harald Sitter <sitter@kde.org>
|
||||
# SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from appium import webdriver
|
||||
from appium.options.common.base import AppiumOptions
|
||||
from appium.webdriver.common.appiumby import AppiumBy
|
||||
|
||||
|
||||
class LoginTest(unittest.TestCase):
|
||||
|
||||
mockServerProcess: subprocess.Popen
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
options = AppiumOptions()
|
||||
options.set_capability("app", "neochat --ignore-ssl-errors")
|
||||
cls.driver = webdriver.Remote(command_executor='http://127.0.0.1:4723', options=options)
|
||||
cls.mockServerProcess = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), "login-server.py")])
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
if not self._outcome.result.wasSuccessful():
|
||||
self.driver.get_screenshot_as_file("failed_test_shot_{}.png".format(self.id()))
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(self):
|
||||
self.mockServerProcess.terminate()
|
||||
self.driver.quit()
|
||||
|
||||
def test_login(self):
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Login").click()
|
||||
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Matrix ID").send_keys("@user:localhost:1234")
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Continue").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Password").send_keys("1234")
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Login").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Join some rooms to get started").click()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# SPDX-License-Identifier: MIT
|
||||
# SPDX-FileCopyrightText: 2021-2022 Harald Sitter <sitter@kde.org>
|
||||
# SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from appium import webdriver
|
||||
from appium.options.common.base import AppiumOptions
|
||||
from appium.webdriver.common.appiumby import AppiumBy
|
||||
|
||||
|
||||
class OpenUserDetailsTest(unittest.TestCase):
|
||||
|
||||
mockServerProcess: subprocess.Popen
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.mockServerProcess = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), "login-server.py")])
|
||||
options = AppiumOptions()
|
||||
options.set_capability("app", "neochat --ignore-ssl-errors --test")
|
||||
cls.driver = webdriver.Remote(command_executor='http://127.0.0.1:4723', options=options)
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
if not self._outcome.result.wasSuccessful():
|
||||
self.driver.get_screenshot_as_file("failed_test_shot_{}.png".format(self.id()))
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(self):
|
||||
self.mockServerProcess.terminate()
|
||||
self.driver.quit()
|
||||
|
||||
def test_open_sheet(self):
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="@user:localhost:1234").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Empty room (!room_id_1234:localhost:1234)").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="A Display Name").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Account Details")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_definitions(-DDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" )
|
||||
|
||||
ecm_add_test(
|
||||
neochatroomtest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
@@ -22,57 +20,3 @@ ecm_add_test(
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME delegatesizehelpertest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
mediasizehelpertest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME mediasizehelpertest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
eventhandlertest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME eventhandlertest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
chatbarcachetest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME chatbarcachetest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
chatdocumenthandlertest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME chatdocumenthandlertest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
messageeventmodeltest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME messageeventmodeltest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
actionshandlertest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME actionshandlertest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
windowcontrollertest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME windowcontrollertest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
pollhandlertest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME pollhandlertest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
reactionmodeltest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME reactionmodeltest
|
||||
)
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include "actionshandler.h"
|
||||
#include "chatbarcache.h"
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
class ActionsHandlerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Quotient::Connection *connection = Quotient::Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
ActionsHandler *actionsHandler = new ActionsHandler(this);
|
||||
|
||||
private Q_SLOTS:
|
||||
void nullObject();
|
||||
};
|
||||
|
||||
void ActionsHandlerTest::nullObject()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
|
||||
actionsHandler->handleMessageEvent(nullptr);
|
||||
|
||||
auto chatBarCache = new ChatBarCache(this);
|
||||
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
|
||||
actionsHandler->handleMessageEvent(chatBarCache);
|
||||
|
||||
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"));
|
||||
actionsHandler->setRoom(room);
|
||||
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
|
||||
actionsHandler->handleMessageEvent(nullptr);
|
||||
|
||||
// The final one should throw no warning so we make sure.
|
||||
QTest::failOnWarning("ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
|
||||
actionsHandler->handleMessageEvent(chatBarCache);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(ActionsHandlerTest)
|
||||
#include "actionshandlertest.moc"
|
||||
@@ -1,141 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
|
||||
#include <Quotient/syncdata.h>
|
||||
#include <qtestcase.h>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class ChatBarCacheTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
TestUtils::TestRoom *room = nullptr;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void empty();
|
||||
void noRoom();
|
||||
void badParent();
|
||||
void reply();
|
||||
void edit();
|
||||
void attachment();
|
||||
};
|
||||
|
||||
void ChatBarCacheTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-min-sync.json"));
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::empty()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||
|
||||
QCOMPARE(chatBarCache->text(), QString());
|
||||
QCOMPARE(chatBarCache->isReplying(), false);
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(nullptr));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::noRoom()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache());
|
||||
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
|
||||
|
||||
// These should return empty even though a reply ID has been set because the
|
||||
// ChatBarCache has no parent.
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationUser(), QVariantMap());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::badParent()
|
||||
{
|
||||
QScopedPointer<QObject> badParent(new QObject());
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(badParent.get()));
|
||||
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
|
||||
|
||||
// These should return empty even though a reply ID has been set because the
|
||||
// ChatBarCache has no parent.
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationUser(), QVariantMap());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::reply()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||
chatBarCache->setText(QLatin1String("some text"));
|
||||
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
|
||||
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
|
||||
|
||||
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
|
||||
QCOMPARE(chatBarCache->isReplying(), true);
|
||||
QCOMPARE(chatBarCache->replyId(), QLatin1String("$153456789:example.org"));
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(room->user(QLatin1String("@example:example.org"))));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QLatin1String("This is an example\ntext message"));
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::edit()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||
chatBarCache->setText(QLatin1String("some text"));
|
||||
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
|
||||
chatBarCache->setEditId(QLatin1String("$153456789:example.org"));
|
||||
|
||||
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
|
||||
QCOMPARE(chatBarCache->isReplying(), false);
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), true);
|
||||
QCOMPARE(chatBarCache->editId(), QLatin1String("$153456789:example.org"));
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(room->user(QLatin1String("@example:example.org"))));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QLatin1String("This is an example\ntext message"));
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
}
|
||||
|
||||
void ChatBarCacheTest::attachment()
|
||||
{
|
||||
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
|
||||
chatBarCache->setText(QLatin1String("some text"));
|
||||
chatBarCache->setEditId(QLatin1String("$153456789:example.org"));
|
||||
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
|
||||
|
||||
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
|
||||
QCOMPARE(chatBarCache->isReplying(), false);
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(nullptr));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QLatin1String("some/path"));
|
||||
}
|
||||
|
||||
QTEST_MAIN(ChatBarCacheTest)
|
||||
#include "chatbarcachetest.moc"
|
||||
@@ -1,36 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
|
||||
#include "chatdocumenthandler.h"
|
||||
#include "neochatconfig.h"
|
||||
|
||||
class ChatDocumentHandlerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
ChatDocumentHandler emptyHandler;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void nullComplete();
|
||||
};
|
||||
|
||||
void ChatDocumentHandlerTest::initTestCase()
|
||||
{
|
||||
// HACK: this is to stop KStatusNotifierItem SEGFAULTING on cleanup.
|
||||
NeoChatConfig::self()->setSystemTray(false);
|
||||
}
|
||||
|
||||
void ChatDocumentHandlerTest::nullComplete()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "complete called with m_document set to nullptr.");
|
||||
emptyHandler.complete(0);
|
||||
}
|
||||
|
||||
QTEST_MAIN(ChatDocumentHandlerTest)
|
||||
#include "chatdocumenthandlertest.moc"
|
||||
@@ -1,410 +0,0 @@
|
||||
{
|
||||
"account_data": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"tags": {
|
||||
"u.work": {
|
||||
"order": 0.9
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.tag"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"custom_config_key": "custom_config_value"
|
||||
},
|
||||
"type": "org.example.custom.room.config"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ephemeral": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"user_ids": [
|
||||
"@alice:matrix.org",
|
||||
"@bob:example.com"
|
||||
]
|
||||
},
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"type": "m.typing"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$153456789:example.org": {
|
||||
"m.read": {
|
||||
"@alice:matrix.org": {
|
||||
"ts": 1436451550453
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@bob:example.com": {
|
||||
"ts": 1436451550453
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@tim:example.com": {
|
||||
"ts": 1436451550454
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@jeff:example.com": {
|
||||
"ts": 1436451550455
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@tina:example.com": {
|
||||
"ts": 1436451550456
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@sally:example.com": {
|
||||
"ts": 1436451550457
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@fred:example.com": {
|
||||
"ts": 1436451550458
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Alice Margatroid",
|
||||
"membership": "join",
|
||||
"reason": "Looking for support"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"displayname": "Look\nat\nme\nI\nput\nnewlines\nin\nmy\ndisplay name",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$143273582443PhrSh:example.org",
|
||||
"origin_server_ts": 1432735824659,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@newline:example.org",
|
||||
"state_key": "@newline:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 12345
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"m.heroes": [
|
||||
"@alice:example.com",
|
||||
"@bob:example.com"
|
||||
],
|
||||
"m.invited_member_count": 0,
|
||||
"m.joined_member_count": 2
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an example\ntext message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example<br>text message</b>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$153456789:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://kde.org/123456",
|
||||
"displayname": "after",
|
||||
"membership": "join"
|
||||
},
|
||||
"origin_server_ts": 1690651134736,
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@example:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"replaces_state": "$1234567890:example.org",
|
||||
"prev_content": {
|
||||
"avatar_url": "mxc://kde.org/12345",
|
||||
"displayname": "before",
|
||||
"membership": "join"
|
||||
},
|
||||
"prev_sender": "@example:example.orgg",
|
||||
"age": 1234
|
||||
},
|
||||
"event_id": "$143273583553PhrSn:example.org",
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "This is a highlight @bob:kde.org and this is a link https://kde.org",
|
||||
"format": "org.matrix.custom.html",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$1532735824654:example.org",
|
||||
"origin_server_ts": 1532735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1233
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"event_id": "$153456789:example.org",
|
||||
"key": "👍",
|
||||
"rel_type": "m.annotation"
|
||||
}
|
||||
},
|
||||
"origin_server_ts": 1690322545182,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@alice:matrix.org",
|
||||
"type": "m.reaction",
|
||||
"unsigned": {
|
||||
"age": 390159120
|
||||
},
|
||||
"event_id": "$163456789:example.org",
|
||||
"age": 390159120
|
||||
},
|
||||
{
|
||||
"age": 4926305285,
|
||||
"content": {
|
||||
"body": "video caption",
|
||||
"filename": "video.mp4",
|
||||
"info": {
|
||||
"duration": 10,
|
||||
"h": 1080,
|
||||
"mimetype": "video/mp4",
|
||||
"size": 62650636,
|
||||
"w": 1920,
|
||||
"thumbnail_info": {
|
||||
"h": 450,
|
||||
"mimetype": "image/jpeg",
|
||||
"size": 382249,
|
||||
"w": 800
|
||||
},
|
||||
"thumbnail_url": "mxc://kde.org/2234567"
|
||||
},
|
||||
"msgtype": "m.video",
|
||||
"url": "mxc://kde.org/1234567"
|
||||
},
|
||||
"event_id": "$263456789:example.org",
|
||||
"origin_server_ts": 1685793783330,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 4926305285
|
||||
},
|
||||
"user_id": "@example:example.org"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "> <@example:example.org> This is an example\ntext message\n\nreply",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<mx-reply><blockquote><a href=\"https://matrix.to/#/!jEsUZKDJdhlrceRyVU:example.org/$153456789:example.org?via=kde.org&via=matrix.org\">In reply to</a> <a href=\"https://matrix.to/#/@example:example.org\">@example:example.org</a><br><b>This is an example<br>text message</b></blockquote></mx-reply>reply",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$153456789:example.org"
|
||||
}
|
||||
},
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"origin_server_ts": 1690725965572,
|
||||
"sender": "@alice:matrix.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 98
|
||||
},
|
||||
"event_id": "$154456789:example.org",
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "> <@example:example.org> video caption\n\nreply",
|
||||
"m.relates_to": {
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$263456789:example.org"
|
||||
}
|
||||
},
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"origin_server_ts": 1690725965573,
|
||||
"sender": "@alice:matrix.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 98
|
||||
},
|
||||
"event_id": "$154456799:example.org",
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
|
||||
},
|
||||
{
|
||||
"age": 96845207,
|
||||
"content": {
|
||||
"body": "Lat: 51.7035, Lon: -1.14394",
|
||||
"geo_uri": "geo:51.7035,-1.14394",
|
||||
"msgtype": "m.location",
|
||||
"org.matrix.msc1767.text": "Lat: 51.7035, Lon: -1.14394",
|
||||
"org.matrix.msc3488.asset": {
|
||||
"type": "m.pin"
|
||||
},
|
||||
"org.matrix.msc3488.location": {
|
||||
"uri": "geo:51.7035,-1.14394"
|
||||
}
|
||||
},
|
||||
"event_id": "$1544567999:example.org",
|
||||
"origin_server_ts": 1690821582876,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 96845207
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "Thread root",
|
||||
"format": "org.matrix.custom.html",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$threadroot:example.org",
|
||||
"origin_server_ts": 1690821582879,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "Thread message 1",
|
||||
"msgtype": "m.text",
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.thread",
|
||||
"event_id": "$threadroot:example.org",
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$threadroot:example.org"
|
||||
},
|
||||
"is_falling_back": true
|
||||
}
|
||||
},
|
||||
"event_id": "$threadmessage1:example.org",
|
||||
"origin_server_ts": 1690821582890,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1238
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "Thread message 2",
|
||||
"msgtype": "m.text",
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.thread",
|
||||
"event_id": "$threadroot:example.org",
|
||||
"m.in_reply_to": {
|
||||
"event_id": "$threadmessage1:example.org"
|
||||
},
|
||||
"is_falling_back": true
|
||||
}
|
||||
},
|
||||
"event_id": "$threadmessage2:example.org",
|
||||
"origin_server_ts": 1690821582890,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1238
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "A message from someone who thought it was a good idea to put newlines in their display name.",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$153456889:example.org",
|
||||
"origin_server_ts": 14327358246589,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@newline:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1230
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
{
|
||||
"account_data": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"tags": {
|
||||
"u.work": {
|
||||
"order": 0.9
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.tag"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"custom_config_key": "custom_config_value"
|
||||
},
|
||||
"type": "org.example.custom.room.config"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ephemeral": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"user_ids": [
|
||||
"@alice:matrix.org",
|
||||
"@bob:example.com"
|
||||
]
|
||||
},
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"type": "m.typing"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"displayname": "Example",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$exampleMember:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@example:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"m.heroes": [
|
||||
"@example:example.org"
|
||||
],
|
||||
"m.invited_member_count": 0,
|
||||
"m.joined_member_count": 2
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an example\ntext message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example<br>text message</b>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$153456789:example.org",
|
||||
"origin_server_ts": 1,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"displayname": "Example Changed",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$exampleMemberChnage:example.org",
|
||||
"origin_server_ts": 2,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@example:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"replaces_state": "$exampleMember:example.org",
|
||||
"prev_content": {
|
||||
"displayname": "Example",
|
||||
"membership": "join"
|
||||
},
|
||||
"prev_sender": "@example:example.org",
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
{
|
||||
"account_data": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"tags": {
|
||||
"u.work": {
|
||||
"order": 0.9
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.tag"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"custom_config_key": "custom_config_value"
|
||||
},
|
||||
"type": "org.example.custom.room.config"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ephemeral": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"user_ids": [
|
||||
"@alice:matrix.org",
|
||||
"@bob:example.com"
|
||||
]
|
||||
},
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"type": "m.typing"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Alice Margatroid",
|
||||
"membership": "join",
|
||||
"reason": "Looking for support"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"m.heroes": [
|
||||
"@alice:example.com",
|
||||
"@bob:example.com"
|
||||
],
|
||||
"m.invited_member_count": 0,
|
||||
"m.joined_member_count": 2
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an example\ntext message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example<br>text message</b>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$153456789:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "New plain message",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$pendingmerge:example.org",
|
||||
"origin_server_ts": 123456,
|
||||
"room_id":"#myroom:kde.org",
|
||||
"sender":"@bob:kde.org",
|
||||
"type":"m.room.message",
|
||||
"unsigned": {
|
||||
"transaction_id": "17017181543521"
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"org.matrix.msc1767.text": "test\n1. option1\n2. option 2",
|
||||
"org.matrix.msc3381.poll.start": {
|
||||
"answers": [
|
||||
{
|
||||
"id": "option1",
|
||||
"org.matrix.msc1767.text": "option1"
|
||||
},
|
||||
{
|
||||
"id": "option2",
|
||||
"org.matrix.msc1767.text": "option2"
|
||||
}
|
||||
],
|
||||
"kind": "org.matrix.msc3381.poll.disclosed",
|
||||
"max_selections": 1,
|
||||
"question": {
|
||||
"body": "test",
|
||||
"msgtype": "m.text",
|
||||
"org.matrix.msc1767.text": "test"
|
||||
}
|
||||
}
|
||||
},
|
||||
"event_id": "$153456789:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "org.matrix.msc3381.poll.start",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"event_id": "$153456789:example.org",
|
||||
"key": "👍",
|
||||
"rel_type": "m.annotation"
|
||||
}
|
||||
},
|
||||
"origin_server_ts": 1690322545183,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@bob:example.org",
|
||||
"type": "m.reaction",
|
||||
"unsigned": {
|
||||
"age": 390159121
|
||||
},
|
||||
"event_id": "$163456790:example.org",
|
||||
"age": 390159121
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"event_id": "$153456789:example.org",
|
||||
"key": "😆",
|
||||
"rel_type": "m.annotation"
|
||||
}
|
||||
},
|
||||
"origin_server_ts": 1690322545184,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@bob:example.org",
|
||||
"type": "m.reaction",
|
||||
"unsigned": {
|
||||
"age": 390159122
|
||||
},
|
||||
"event_id": "$163456791:example.org",
|
||||
"age": 390159122
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
{
|
||||
"account_data": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"tags": {
|
||||
"u.work": {
|
||||
"order": 0.9
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.tag"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"custom_config_key": "custom_config_value"
|
||||
},
|
||||
"type": "org.example.custom.room.config"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ephemeral": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"user_ids": [
|
||||
"@alice:matrix.org",
|
||||
"@bob:example.com"
|
||||
]
|
||||
},
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"type": "m.typing"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$153456789:example.org": {
|
||||
"m.read": {
|
||||
"@alice:matrix.org": {
|
||||
"ts": 1436451550453
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@bob:example.com": {
|
||||
"ts": 1436451550453
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@tim:example.com": {
|
||||
"ts": 1436451550454
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@jeff:example.com": {
|
||||
"ts": 1436451550455
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@tina:example.com": {
|
||||
"ts": 1436451550456
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@sally:example.com": {
|
||||
"ts": 1436451550457
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@fred:example.com": {
|
||||
"ts": 1436451550458
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Alice Margatroid",
|
||||
"membership": "join",
|
||||
"reason": "Looking for support"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"displayname": "Look\nat\nme\nI\nput\nnewlines\nin\nmy\ndisplay name",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$143273582443PhrSh:example.org",
|
||||
"origin_server_ts": 1432735824659,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@newline:example.org",
|
||||
"state_key": "@newline:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 12345
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"m.heroes": [
|
||||
"@alice:example.com",
|
||||
"@bob:example.com"
|
||||
],
|
||||
"m.invited_member_count": 0,
|
||||
"m.joined_member_count": 2
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an example\ntext message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example<br>text message</b>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$153456789:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"event_id": "$153456789:example.org",
|
||||
"key": "👍",
|
||||
"rel_type": "m.annotation"
|
||||
}
|
||||
},
|
||||
"origin_server_ts": 1690322545182,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@alice:matrix.org",
|
||||
"type": "m.reaction",
|
||||
"unsigned": {
|
||||
"age": 390159120
|
||||
},
|
||||
"event_id": "$163456789:example.org",
|
||||
"age": 390159120
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
{
|
||||
"account_data": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"tags": {
|
||||
"u.work": {
|
||||
"order": 0.9
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.tag"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"custom_config_key": "custom_config_value"
|
||||
},
|
||||
"type": "org.example.custom.room.config"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ephemeral": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"user_ids": [
|
||||
"@alice:matrix.org",
|
||||
"@bob:example.com"
|
||||
]
|
||||
},
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"type": "m.typing"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Alice Margatroid",
|
||||
"membership": "join",
|
||||
"reason": "Looking for support"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"m.heroes": [
|
||||
"@alice:example.com",
|
||||
"@bob:example.com"
|
||||
],
|
||||
"m.invited_member_count": 0,
|
||||
"m.joined_member_count": 2
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an **example** text message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example text message</b>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "/me This is an emote.",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "This is an emote.",
|
||||
"msgtype": "m.emote"
|
||||
},
|
||||
"event_id": "$153273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1532735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1231
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "tested",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$zrCiBxBnqqTn0Z5FY78qSZAszno_w8nJJXzfBULG-3E",
|
||||
"origin_server_ts": 1680948575928,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1747776,
|
||||
"m.relations": {
|
||||
"m.replace": {
|
||||
"event_id": "$UX0PlpyI7vYO32iHMuuYEP7ECMh4sX3XLGiB2SwM4mQ",
|
||||
"origin_server_ts": 1680948580992,
|
||||
"sender": "@example:example.org"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
}
|
||||
@@ -1,685 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
|
||||
#include "eventhandler.h"
|
||||
|
||||
#include <KFormat>
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
|
||||
#include "enums/delegatetype.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "models/reactionmodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class EventHandlerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
TestUtils::TestRoom *room = nullptr;
|
||||
EventHandler eventHandler;
|
||||
EventHandler emptyHandler;
|
||||
EventHandler noEventHandler;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void nullSetEvent();
|
||||
void eventId();
|
||||
void nullEventId();
|
||||
void delegateType_data();
|
||||
void delegateType();
|
||||
void nullDelegateType();
|
||||
void author();
|
||||
void nullAuthor();
|
||||
void authorDisplayName();
|
||||
void nullAuthorDisplayName();
|
||||
void singleLineSidplayName();
|
||||
void nullSingleLineDisplayName();
|
||||
void time();
|
||||
void nullTime();
|
||||
void timeString();
|
||||
void nullTimeString();
|
||||
void highlighted();
|
||||
void nullHighlighted();
|
||||
void hidden();
|
||||
void nullHidden();
|
||||
void body();
|
||||
void nullBody();
|
||||
void genericBody_data();
|
||||
void genericBody();
|
||||
void nullGenericBody();
|
||||
void subtitle();
|
||||
void nullSubtitle();
|
||||
void mediaInfo();
|
||||
void nullMediaInfo();
|
||||
void linkPreviewer();
|
||||
void nullLinkPreviewer();
|
||||
void hasReply();
|
||||
void nullHasReply();
|
||||
void replyId();
|
||||
void nullReplyId();
|
||||
void replyDelegateType();
|
||||
void nullReplyDelegateType();
|
||||
void replyAuthor();
|
||||
void nullReplyAuthor();
|
||||
void replyBody();
|
||||
void nullReplyBody();
|
||||
void replyMediaInfo();
|
||||
void nullReplyMediaInfo();
|
||||
void thread();
|
||||
void nullThread();
|
||||
void location();
|
||||
void nullLocation();
|
||||
void readMarkers();
|
||||
void nullReadMarkers();
|
||||
|
||||
void cleanup();
|
||||
};
|
||||
|
||||
void EventHandlerTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-eventhandler-sync.json"));
|
||||
|
||||
eventHandler.setRoom(room);
|
||||
noEventHandler.setRoom(room);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullSetEvent()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "cannot setEvent when m_room is set to nullptr.");
|
||||
emptyHandler.setEvent(room->messageEvents().at(0).get());
|
||||
}
|
||||
|
||||
void EventHandlerTest::eventId()
|
||||
{
|
||||
eventHandler.setEvent(room->messageEvents().at(0).get());
|
||||
|
||||
QCOMPARE(eventHandler.getId(), QStringLiteral("$153456789:example.org"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullEventId()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getId called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getId(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::delegateType_data()
|
||||
{
|
||||
QTest::addColumn<int>("eventNum");
|
||||
QTest::addColumn<DelegateType::Type>("delegateType");
|
||||
|
||||
QTest::newRow("message") << 0 << DelegateType::Message;
|
||||
QTest::newRow("state") << 1 << DelegateType::State;
|
||||
QTest::newRow("message 2") << 2 << DelegateType::Message;
|
||||
QTest::newRow("reaction") << 3 << DelegateType::Other;
|
||||
QTest::newRow("video") << 4 << DelegateType::Video;
|
||||
QTest::newRow("location") << 7 << DelegateType::Location;
|
||||
}
|
||||
|
||||
void EventHandlerTest::delegateType()
|
||||
{
|
||||
QFETCH(int, eventNum);
|
||||
QFETCH(DelegateType::Type, delegateType);
|
||||
|
||||
eventHandler.setEvent(room->messageEvents().at(eventNum).get());
|
||||
|
||||
QCOMPARE(eventHandler.getDelegateType(), delegateType);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullDelegateType()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getDelegateType called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getDelegateType(), DelegateType::Other);
|
||||
}
|
||||
|
||||
void EventHandlerTest::author()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
auto author = room->user(event->senderId());
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
auto eventHandlerAuthor = eventHandler.getAuthor();
|
||||
|
||||
QCOMPARE(eventHandlerAuthor["isLocalUser"_ls], author->id() == room->localUser()->id());
|
||||
QCOMPARE(eventHandlerAuthor["id"_ls], author->id());
|
||||
QCOMPARE(eventHandlerAuthor["displayName"_ls], author->displayname(room));
|
||||
QCOMPARE(eventHandlerAuthor["avatarSource"_ls], room->avatarForMember(author));
|
||||
QCOMPARE(eventHandlerAuthor["avatarMediaId"_ls], author->avatarMediaId(room));
|
||||
QCOMPARE(eventHandlerAuthor["color"_ls], Utils::getUserColor(author->hueF()));
|
||||
QCOMPARE(eventHandlerAuthor["object"_ls], QVariant::fromValue(author));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullAuthor()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getAuthor(), QVariantMap());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_event set to nullptr. Returning empty user.");
|
||||
QCOMPARE(noEventHandler.getAuthor(), room->getUser(nullptr));
|
||||
}
|
||||
|
||||
void EventHandlerTest::authorDisplayName()
|
||||
{
|
||||
auto event = room->messageEvents().at(1).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.getAuthorDisplayName(), QStringLiteral("before"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullAuthorDisplayName()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getAuthorDisplayName(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getAuthorDisplayName(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::singleLineSidplayName()
|
||||
{
|
||||
auto event = room->messageEvents().at(11).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.singleLineAuthorDisplayname(), QStringLiteral("Look at me I put newlines in my display name"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullSingleLineDisplayName()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.singleLineAuthorDisplayname(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.singleLineAuthorDisplayname(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::time()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.getTime(), QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC));
|
||||
QCOMPARE(eventHandler.getTime(true, QDateTime::fromMSecsSinceEpoch(1234, Qt::UTC)), QDateTime::fromMSecsSinceEpoch(1234, Qt::UTC));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullTime()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getTime called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getTime(), QDateTime());
|
||||
|
||||
eventHandler.setEvent(room->messageEvents().at(0).get());
|
||||
QTest::ignoreMessage(QtWarningMsg, "a value must be provided for lastUpdated for a pending event.");
|
||||
QCOMPARE(eventHandler.getTime(true), QDateTime());
|
||||
}
|
||||
|
||||
void EventHandlerTest::timeString()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
KFormat format;
|
||||
|
||||
QCOMPARE(eventHandler.getTimeString(false),
|
||||
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toLocalTime().time(), QLocale::ShortFormat));
|
||||
QCOMPARE(eventHandler.getTimeString(true),
|
||||
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toLocalTime().date(), QLocale::ShortFormat));
|
||||
QCOMPARE(eventHandler.getTimeString(false, QLocale::ShortFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
||||
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().time(), QLocale::ShortFormat));
|
||||
QCOMPARE(eventHandler.getTimeString(true, QLocale::ShortFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
||||
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::ShortFormat));
|
||||
QCOMPARE(eventHandler.getTimeString(false, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
||||
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().time(), QLocale::LongFormat));
|
||||
QCOMPARE(eventHandler.getTimeString(true, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
||||
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::LongFormat));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullTimeString()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getTimeString called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getTimeString(false), QString());
|
||||
|
||||
eventHandler.setEvent(room->messageEvents().at(0).get());
|
||||
QTest::ignoreMessage(QtWarningMsg, "a value must be provided for lastUpdated for a pending event.");
|
||||
QCOMPARE(eventHandler.getTimeString(false, QLocale::ShortFormat, true), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::highlighted()
|
||||
{
|
||||
auto event = room->messageEvents().at(2).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.isHighlighted(), true);
|
||||
|
||||
event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.isHighlighted(), false);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullHighlighted()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.isHighlighted(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.isHighlighted(), false);
|
||||
}
|
||||
|
||||
void EventHandlerTest::hidden()
|
||||
{
|
||||
auto event = room->messageEvents().at(3).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.isHidden(), true);
|
||||
|
||||
event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.isHidden(), false);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullHidden()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "isHidden called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.isHidden(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "isHidden called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.isHidden(), false);
|
||||
}
|
||||
|
||||
void EventHandlerTest::body()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.getRichBody(), QStringLiteral("<b>This is an example<br>text message</b>"));
|
||||
QCOMPARE(eventHandler.getRichBody(true), QStringLiteral("<b>This is an example text message</b>"));
|
||||
QCOMPARE(eventHandler.getPlainBody(), QStringLiteral("This is an example\ntext message"));
|
||||
QCOMPARE(eventHandler.getPlainBody(true), QStringLiteral("This is an example text message"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullBody()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getRichBody called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getRichBody(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getPlainBody called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getPlainBody(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::genericBody_data()
|
||||
{
|
||||
QTest::addColumn<int>("eventNum");
|
||||
QTest::addColumn<QString>("output");
|
||||
|
||||
QTest::newRow("message") << 0 << QStringLiteral("sent a message");
|
||||
QTest::newRow("member") << 1 << QStringLiteral("changed their display name and updated their avatar");
|
||||
QTest::newRow("message 2") << 2 << QStringLiteral("sent a message");
|
||||
QTest::newRow("reaction") << 3 << QStringLiteral("Unknown event");
|
||||
QTest::newRow("video") << 4 << QStringLiteral("sent a message");
|
||||
}
|
||||
|
||||
void EventHandlerTest::genericBody()
|
||||
{
|
||||
QFETCH(int, eventNum);
|
||||
QFETCH(QString, output);
|
||||
|
||||
eventHandler.setEvent(room->messageEvents().at(eventNum).get());
|
||||
|
||||
QCOMPARE(eventHandler.getGenericBody(), output);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullGenericBody()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getGenericBody called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getGenericBody(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::subtitle()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.subtitleText(), QStringLiteral("after: This is an example text message"));
|
||||
|
||||
event = room->messageEvents().at(2).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.subtitleText(), QStringLiteral("after: This is a highlight @bob:kde.org and this is a link https://kde.org"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullSubtitle()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "subtitleText called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.subtitleText(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::mediaInfo()
|
||||
{
|
||||
auto event = room->messageEvents().at(4).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
auto mediaInfo = eventHandler.getMediaInfo();
|
||||
auto thumbnailInfo = mediaInfo["tempInfo"_ls].toMap();
|
||||
|
||||
QCOMPARE(mediaInfo["source"_ls], room->makeMediaUrl(event->id(), QUrl("mxc://kde.org/1234567"_ls)));
|
||||
QCOMPARE(mediaInfo["mimeType"_ls], QStringLiteral("video/mp4"));
|
||||
QCOMPARE(mediaInfo["mimeIcon"_ls], QStringLiteral("video-mp4"));
|
||||
QCOMPARE(mediaInfo["size"_ls], 62650636);
|
||||
QCOMPARE(mediaInfo["duration"_ls], 10);
|
||||
QCOMPARE(mediaInfo["width"_ls], 1920);
|
||||
QCOMPARE(mediaInfo["height"_ls], 1080);
|
||||
QCOMPARE(thumbnailInfo["source"_ls], room->makeMediaUrl(event->id(), QUrl("mxc://kde.org/2234567"_ls)));
|
||||
QCOMPARE(thumbnailInfo["mimeType"_ls], QStringLiteral("image/jpeg"));
|
||||
QCOMPARE(thumbnailInfo["mimeIcon"_ls], QStringLiteral("image-jpeg"));
|
||||
QCOMPARE(thumbnailInfo["size"_ls], 382249);
|
||||
QCOMPARE(thumbnailInfo["width"_ls], 800);
|
||||
QCOMPARE(thumbnailInfo["height"_ls], 450);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullMediaInfo()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getMediaInfo called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getMediaInfo(), QVariantMap());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getMediaInfo called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getMediaInfo(), QVariantMap());
|
||||
}
|
||||
|
||||
void EventHandlerTest::linkPreviewer()
|
||||
{
|
||||
auto event = room->messageEvents().at(2).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.getLinkPreviewer()->url(), QUrl("https://kde.org"_ls));
|
||||
|
||||
event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.getLinkPreviewer(), nullptr);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullLinkPreviewer()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getLinkPreviewer called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getLinkPreviewer(), nullptr);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getLinkPreviewer called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getLinkPreviewer(), nullptr);
|
||||
}
|
||||
|
||||
void EventHandlerTest::hasReply()
|
||||
{
|
||||
auto event = room->messageEvents().at(5).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.hasReply(), true);
|
||||
|
||||
event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.hasReply(), false);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullHasReply()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "hasReply called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.hasReply(), false);
|
||||
}
|
||||
|
||||
void EventHandlerTest::replyId()
|
||||
{
|
||||
auto event = room->messageEvents().at(5).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.getReplyId(), QStringLiteral("$153456789:example.org"));
|
||||
|
||||
event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.getReplyId(), QStringLiteral(""));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReplyId()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyId called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReplyId(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::replyDelegateType()
|
||||
{
|
||||
auto event = room->messageEvents().at(5).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.getReplyDelegateType(), DelegateType::Message);
|
||||
|
||||
event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.getReplyDelegateType(), DelegateType::Other);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReplyDelegateType()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyDelegateType called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReplyDelegateType(), DelegateType::Other);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyDelegateType called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReplyDelegateType(), DelegateType::Other);
|
||||
}
|
||||
|
||||
void EventHandlerTest::replyAuthor()
|
||||
{
|
||||
auto event = room->messageEvents().at(5).get();
|
||||
auto replyEvent = room->messageEvents().at(0).get();
|
||||
auto replyAuthor = room->user(replyEvent->senderId());
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
auto eventHandlerReplyAuthor = eventHandler.getReplyAuthor();
|
||||
|
||||
QCOMPARE(eventHandlerReplyAuthor["isLocalUser"_ls], replyAuthor->id() == room->localUser()->id());
|
||||
QCOMPARE(eventHandlerReplyAuthor["id"_ls], replyAuthor->id());
|
||||
QCOMPARE(eventHandlerReplyAuthor["displayName"_ls], replyAuthor->displayname(room));
|
||||
QCOMPARE(eventHandlerReplyAuthor["avatarSource"_ls], room->avatarForMember(replyAuthor));
|
||||
QCOMPARE(eventHandlerReplyAuthor["avatarMediaId"_ls], replyAuthor->avatarMediaId(room));
|
||||
QCOMPARE(eventHandlerReplyAuthor["color"_ls], Utils::getUserColor(replyAuthor->hueF()));
|
||||
QCOMPARE(eventHandlerReplyAuthor["object"_ls], QVariant::fromValue(replyAuthor));
|
||||
|
||||
event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.getReplyAuthor(), room->getUser(nullptr));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReplyAuthor()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyAuthor called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReplyAuthor(), QVariantMap());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyAuthor called with m_event set to nullptr. Returning empty user.");
|
||||
QCOMPARE(noEventHandler.getReplyAuthor(), room->getUser(nullptr));
|
||||
}
|
||||
|
||||
void EventHandlerTest::replyBody()
|
||||
{
|
||||
auto event = room->messageEvents().at(5).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.getReplyRichBody(), QStringLiteral("<b>This is an example<br>text message</b>"));
|
||||
QCOMPARE(eventHandler.getReplyRichBody(true), QStringLiteral("<b>This is an example text message</b>"));
|
||||
QCOMPARE(eventHandler.getReplyPlainBody(), QStringLiteral("This is an example\ntext message"));
|
||||
QCOMPARE(eventHandler.getReplyPlainBody(true), QStringLiteral("This is an example text message"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReplyBody()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyRichBody called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReplyRichBody(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyPlainBody called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReplyPlainBody(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::replyMediaInfo()
|
||||
{
|
||||
auto event = room->messageEvents().at(6).get();
|
||||
auto replyEvent = room->messageEvents().at(4).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
auto mediaInfo = eventHandler.getReplyMediaInfo();
|
||||
auto thumbnailInfo = mediaInfo["tempInfo"_ls].toMap();
|
||||
|
||||
QCOMPARE(mediaInfo["source"_ls], room->makeMediaUrl(replyEvent->id(), QUrl("mxc://kde.org/1234567"_ls)));
|
||||
QCOMPARE(mediaInfo["mimeType"_ls], QStringLiteral("video/mp4"));
|
||||
QCOMPARE(mediaInfo["mimeIcon"_ls], QStringLiteral("video-mp4"));
|
||||
QCOMPARE(mediaInfo["size"_ls], 62650636);
|
||||
QCOMPARE(mediaInfo["duration"_ls], 10);
|
||||
QCOMPARE(mediaInfo["width"_ls], 1920);
|
||||
QCOMPARE(mediaInfo["height"_ls], 1080);
|
||||
QCOMPARE(thumbnailInfo["source"_ls], room->makeMediaUrl(replyEvent->id(), QUrl("mxc://kde.org/2234567"_ls)));
|
||||
QCOMPARE(thumbnailInfo["mimeType"_ls], QStringLiteral("image/jpeg"));
|
||||
QCOMPARE(thumbnailInfo["mimeIcon"_ls], QStringLiteral("image-jpeg"));
|
||||
QCOMPARE(thumbnailInfo["size"_ls], 382249);
|
||||
QCOMPARE(thumbnailInfo["width"_ls], 800);
|
||||
QCOMPARE(thumbnailInfo["height"_ls], 450);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReplyMediaInfo()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyMediaInfo called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReplyMediaInfo(), QVariantMap());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyMediaInfo called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReplyMediaInfo(), QVariantMap());
|
||||
}
|
||||
|
||||
void EventHandlerTest::thread()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.isThreaded(), false);
|
||||
QCOMPARE(eventHandler.threadRoot(), QString());
|
||||
|
||||
event = room->messageEvents().at(9).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.isThreaded(), true);
|
||||
QCOMPARE(eventHandler.threadRoot(), QStringLiteral("$threadroot:example.org"));
|
||||
QCOMPARE(eventHandler.getReplyId(), QStringLiteral("$threadroot:example.org"));
|
||||
|
||||
event = room->messageEvents().at(10).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.isThreaded(), true);
|
||||
QCOMPARE(eventHandler.threadRoot(), QStringLiteral("$threadroot:example.org"));
|
||||
QCOMPARE(eventHandler.getReplyId(), QStringLiteral("$threadmessage1:example.org"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullThread()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "isThreaded called with m_event set to nullptr.");
|
||||
QCOMPARE(emptyHandler.isThreaded(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "threadRoot called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.threadRoot(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::location()
|
||||
{
|
||||
auto event = room->messageEvents().at(7).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.getLatitude(), QStringLiteral("51.7035").toFloat());
|
||||
QCOMPARE(eventHandler.getLongitude(), QStringLiteral("-1.14394").toFloat());
|
||||
QCOMPARE(eventHandler.getLocationAssetType(), QStringLiteral("m.pin"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullLocation()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getLatitude called with m_event set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getLatitude(), -100.0);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getLongitude called with m_event set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getLongitude(), -200.0);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getLocationAssetType called with m_event set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getLocationAssetType(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::readMarkers()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.hasReadMarkers(), true);
|
||||
|
||||
auto readMarkers = eventHandler.getReadMarkers();
|
||||
|
||||
QCOMPARE(readMarkers.size(), 1);
|
||||
QCOMPARE(readMarkers[0].toMap()["id"_ls], QStringLiteral("@alice:matrix.org"));
|
||||
|
||||
QCOMPARE(eventHandler.getNumberExcessReadMarkers(), QString());
|
||||
QCOMPARE(eventHandler.getReadMarkersString(), QStringLiteral("1 user: @alice:matrix.org"));
|
||||
|
||||
event = room->messageEvents().at(2).get();
|
||||
eventHandler.setEvent(event);
|
||||
|
||||
QCOMPARE(eventHandler.hasReadMarkers(), true);
|
||||
|
||||
readMarkers = eventHandler.getReadMarkers();
|
||||
|
||||
QCOMPARE(readMarkers.size(), 5);
|
||||
|
||||
QCOMPARE(eventHandler.getNumberExcessReadMarkers(), QStringLiteral("+ 1"));
|
||||
// There are no guarantees on the order of the users it will be different every time so don't match the whole string.
|
||||
QCOMPARE(eventHandler.getReadMarkersString().startsWith(QStringLiteral("6 users:")), true);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReadMarkers()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "hasReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.hasReadMarkers(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReadMarkers(), QVariantList());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getNumberExcessReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getNumberExcessReadMarkers(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkersString called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReadMarkersString(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "hasReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.hasReadMarkers(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReadMarkers(), QVariantList());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getNumberExcessReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getNumberExcessReadMarkers(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkersString called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReadMarkersString(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::cleanup()
|
||||
{
|
||||
eventHandler.setEvent(nullptr);
|
||||
}
|
||||
|
||||
QTEST_MAIN(EventHandlerTest)
|
||||
#include "eventhandlertest.moc"
|
||||
@@ -1,72 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
#include <qglobal.h>
|
||||
#include <qtestcase.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "mediasizehelper.h"
|
||||
#include "neochatconfig.h"
|
||||
|
||||
class MediaSizeHelperTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void uninitialized();
|
||||
|
||||
void limits_data();
|
||||
void limits();
|
||||
};
|
||||
|
||||
void MediaSizeHelperTest::uninitialized()
|
||||
{
|
||||
MediaSizeHelper mediasizehelper;
|
||||
QCOMPARE(mediasizehelper.currentSize(), QSize(540, qRound(qreal(NeoChatConfig::self()->mediaMaxWidth()) / qreal(16.0) * qreal(9.0))));
|
||||
}
|
||||
|
||||
void MediaSizeHelperTest::limits_data()
|
||||
{
|
||||
QTest::addColumn<qreal>("mediaWidth");
|
||||
QTest::addColumn<qreal>("mediaHeight");
|
||||
QTest::addColumn<qreal>("contentMaxWidth");
|
||||
QTest::addColumn<qreal>("contentMaxHeight");
|
||||
QTest::addColumn<QSize>("currentSize");
|
||||
|
||||
QTest::newRow("media smaller than content limits") << qreal(200) << qreal(150) << qreal(400) << qreal(900) << QSize(200, 150);
|
||||
QTest::newRow("media smaller than max limits") << qreal(200) << qreal(150) << qreal(-1) << qreal(-1) << QSize(200, 150);
|
||||
|
||||
QTest::newRow("limit by max width") << qreal(600) << qreal(50) << qreal(-1) << qreal(-1) << QSize(540, qRound(qreal(540) / (qreal(600) / qreal(50))));
|
||||
QTest::newRow("limit by max height") << qreal(50) << qreal(600) << qreal(-1) << qreal(-1) << QSize(qRound(qreal(540) * (qreal(50) / qreal(600))), 540);
|
||||
|
||||
QTest::newRow("limit by content width") << qreal(600) << qreal(50) << qreal(300) << qreal(-1) << QSize(300, qRound(qreal(300) / (qreal(600) / qreal(50))));
|
||||
QTest::newRow("limit by content height") << qreal(50) << qreal(600) << qreal(-1) << qreal(300) << QSize(qRound(qreal(300) * (qreal(50) / qreal(600))), 300);
|
||||
|
||||
QTest::newRow("limit by content width tall media")
|
||||
<< qreal(400) << qreal(600) << qreal(100) << qreal(400) << QSize(100, qRound(qreal(100) / (qreal(400) / qreal(600))));
|
||||
QTest::newRow("limit by content height wide media")
|
||||
<< qreal(1000) << qreal(600) << qreal(400) << qreal(100) << QSize(qRound(qreal(100) * (qreal(1000) / qreal(600))), 100);
|
||||
}
|
||||
|
||||
void MediaSizeHelperTest::limits()
|
||||
{
|
||||
QFETCH(qreal, mediaWidth);
|
||||
QFETCH(qreal, mediaHeight);
|
||||
QFETCH(qreal, contentMaxWidth);
|
||||
QFETCH(qreal, contentMaxHeight);
|
||||
QFETCH(QSize, currentSize);
|
||||
|
||||
MediaSizeHelper mediasizehelper;
|
||||
mediasizehelper.setMediaWidth(mediaWidth);
|
||||
mediasizehelper.setMediaHeight(mediaHeight);
|
||||
mediasizehelper.setContentMaxWidth(contentMaxWidth);
|
||||
mediasizehelper.setContentMaxHeight(contentMaxHeight);
|
||||
|
||||
QCOMPARE(mediasizehelper.currentSize(), currentSize);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(MediaSizeHelperTest)
|
||||
#include "mediasizehelpertest.moc"
|
||||
@@ -1,215 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QObject>
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
|
||||
#include "enums/delegatetype.h"
|
||||
#include "models/messageeventmodel.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class MessageEventModelTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
MessageEventModel *model = nullptr;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void init();
|
||||
|
||||
void switchEmptyRoom();
|
||||
void switchSyncedRoom();
|
||||
void simpleTimeline();
|
||||
void syncNewEvents();
|
||||
void pendingEvent();
|
||||
void disconnect();
|
||||
void idToRow();
|
||||
|
||||
void cleanup();
|
||||
};
|
||||
|
||||
void MessageEventModelTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
}
|
||||
|
||||
void MessageEventModelTest::init()
|
||||
{
|
||||
QCOMPARE(model, nullptr);
|
||||
model = new MessageEventModel;
|
||||
}
|
||||
|
||||
// Make sure that basic empty rooms can be switched without crashing.
|
||||
void MessageEventModelTest::switchEmptyRoom()
|
||||
{
|
||||
auto firstRoom = new TestUtils::TestRoom(connection, QStringLiteral("#firstRoom:kde.org"));
|
||||
auto secondRoom = new TestUtils::TestRoom(connection, QStringLiteral("#secondRoom:kde.org"));
|
||||
|
||||
QSignalSpy spy(model, SIGNAL(roomChanged()));
|
||||
|
||||
QCOMPARE(model->room(), nullptr);
|
||||
model->setRoom(firstRoom);
|
||||
QCOMPARE(spy.count(), 1);
|
||||
QCOMPARE(model->room()->id(), QStringLiteral("#firstRoom:kde.org"));
|
||||
model->setRoom(secondRoom);
|
||||
QCOMPARE(spy.count(), 2);
|
||||
QCOMPARE(model->room()->id(), QStringLiteral("#secondRoom:kde.org"));
|
||||
model->setRoom(nullptr);
|
||||
QCOMPARE(spy.count(), 3);
|
||||
QCOMPARE(model->room(), nullptr);
|
||||
}
|
||||
|
||||
// Make sure that rooms with some events can be switched without crashing
|
||||
void MessageEventModelTest::switchSyncedRoom()
|
||||
{
|
||||
auto firstRoom = new TestUtils::TestRoom(connection, QStringLiteral("#firstRoom:kde.org"), QLatin1String("test-messageventmodel-sync.json"));
|
||||
auto secondRoom = new TestUtils::TestRoom(connection, QStringLiteral("#secondRoom:kde.org"), QLatin1String("test-messageventmodel-sync.json"));
|
||||
|
||||
QSignalSpy spy(model, SIGNAL(roomChanged()));
|
||||
|
||||
QCOMPARE(model->room(), nullptr);
|
||||
model->setRoom(firstRoom);
|
||||
QCOMPARE(spy.count(), 1);
|
||||
QCOMPARE(model->room()->id(), QStringLiteral("#firstRoom:kde.org"));
|
||||
model->setRoom(secondRoom);
|
||||
QCOMPARE(spy.count(), 2);
|
||||
QCOMPARE(model->room()->id(), QStringLiteral("#secondRoom:kde.org"));
|
||||
model->setRoom(nullptr);
|
||||
QCOMPARE(spy.count(), 3);
|
||||
QCOMPARE(model->room(), nullptr);
|
||||
}
|
||||
|
||||
void MessageEventModelTest::simpleTimeline()
|
||||
{
|
||||
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-messageventmodel-sync.json"));
|
||||
|
||||
model->setRoom(room);
|
||||
QCOMPARE(model->rowCount(), 2);
|
||||
|
||||
QCOMPARE(model->data(model->index(0), MessageEventModel::DelegateTypeRole), DelegateType::State);
|
||||
QCOMPARE(model->data(model->index(0)), QStringLiteral("changed their display name to Example Changed"));
|
||||
|
||||
QCOMPARE(model->data(model->index(1)), QStringLiteral("<b>This is an example<br>text message</b>"));
|
||||
QCOMPARE(model->data(model->index(1), MessageEventModel::DelegateTypeRole), DelegateType::Message);
|
||||
QCOMPARE(model->data(model->index(1), MessageEventModel::PlainText), QStringLiteral("This is an example\ntext message"));
|
||||
QCOMPARE(model->data(model->index(1), MessageEventModel::EventIdRole), QStringLiteral("$153456789:example.org"));
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "Index QModelIndex(-1,-1,0x0,QObject(0x0)) is not valid (expected valid)");
|
||||
QCOMPARE(model->data(model->index(-1)), QVariant());
|
||||
QTest::ignoreMessage(QtWarningMsg, "Index QModelIndex(-1,-1,0x0,QObject(0x0)) is not valid (expected valid)");
|
||||
QCOMPARE(model->data(model->index(model->rowCount())), QVariant());
|
||||
}
|
||||
|
||||
// Sync some events into the MessageEventModel's current room and don't crash.
|
||||
void MessageEventModelTest::syncNewEvents()
|
||||
{
|
||||
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"));
|
||||
QSignalSpy spy(room, SIGNAL(aboutToAddNewMessages(Quotient::RoomEventsRange)));
|
||||
|
||||
model->setRoom(room);
|
||||
QCOMPARE(model->rowCount(), 0);
|
||||
|
||||
room->syncNewEvents(QLatin1String("test-messageventmodel-sync.json"));
|
||||
|
||||
QCOMPARE(model->rowCount(), 2);
|
||||
QCOMPARE(spy.count(), 1);
|
||||
}
|
||||
|
||||
// Check the adding of pending events to the room doesn't cause any issues in the model.
|
||||
void MessageEventModelTest::pendingEvent()
|
||||
{
|
||||
QSignalSpy spyInsert(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
|
||||
QSignalSpy spyRemove(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
|
||||
QSignalSpy spyChanged(model, SIGNAL(dataChanged(const QModelIndex, const QModelIndex, const QList<int> &)));
|
||||
|
||||
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"));
|
||||
model->setRoom(room);
|
||||
QCOMPARE(model->rowCount(), 0);
|
||||
|
||||
auto txnId = room->postPlainText("New plain message"_ls);
|
||||
QCOMPARE(model->rowCount(), 1);
|
||||
QCOMPARE(spyInsert.count(), 1);
|
||||
|
||||
room->discardMessage(txnId);
|
||||
QCOMPARE(model->rowCount(), 0);
|
||||
QCOMPARE(spyRemove.count(), 1);
|
||||
|
||||
txnId = room->postPlainText("New plain message"_ls);
|
||||
QCOMPARE(model->rowCount(), 1);
|
||||
QCOMPARE(spyInsert.count(), 2);
|
||||
|
||||
// We need to manually set the transaction ID of the new message as it will be
|
||||
// different every time.
|
||||
QFile testSyncFile;
|
||||
testSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-pending-sync.json"));
|
||||
testSyncFile.open(QIODevice::ReadOnly);
|
||||
auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll());
|
||||
auto root = testSyncJson.object();
|
||||
auto timeline = root["timeline"_ls].toObject();
|
||||
auto events = timeline["events"_ls].toArray();
|
||||
auto firstEvent = events[0].toObject();
|
||||
firstEvent.insert(QLatin1String("unsigned"), QJsonObject{{QLatin1String("transaction_id"), txnId}});
|
||||
events[0] = firstEvent;
|
||||
timeline.insert("events"_ls, events);
|
||||
root.insert("timeline"_ls, timeline);
|
||||
testSyncJson.setObject(root);
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testSyncJson.object());
|
||||
room->update(std::move(roomData));
|
||||
|
||||
QCOMPARE(model->rowCount(), 1);
|
||||
// The model will throw multiple data changed signals we need the one that refreshes
|
||||
// the IsPendingRole.
|
||||
QCOMPARE(spyChanged.count() > 0, true);
|
||||
auto isPendingChanged = false;
|
||||
for (auto signal : spyChanged) {
|
||||
auto roles = signal.at(2).toList();
|
||||
if (roles.contains(MessageEventModel::IsPendingRole)) {
|
||||
isPendingChanged = true;
|
||||
}
|
||||
}
|
||||
QCOMPARE(isPendingChanged, true);
|
||||
}
|
||||
|
||||
// Make sure that the signals are disconnecting correctly when a room is switched.
|
||||
void MessageEventModelTest::disconnect()
|
||||
{
|
||||
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"));
|
||||
model->setRoom(room);
|
||||
|
||||
QSignalSpy spy(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
|
||||
|
||||
model->setRoom(nullptr);
|
||||
room->syncNewEvents(QLatin1String("test-messageventmodel-sync.json"));
|
||||
|
||||
QCOMPARE(spy.count(), 0);
|
||||
}
|
||||
|
||||
void MessageEventModelTest::idToRow()
|
||||
{
|
||||
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-min-sync.json"));
|
||||
model->setRoom(room);
|
||||
|
||||
QCOMPARE(model->eventIdToRow(QStringLiteral("$153456789:example.org")), 0);
|
||||
}
|
||||
|
||||
void MessageEventModelTest::cleanup()
|
||||
{
|
||||
delete model;
|
||||
model = nullptr;
|
||||
QCOMPARE(model, nullptr);
|
||||
}
|
||||
|
||||
QTEST_MAIN(MessageEventModelTest)
|
||||
#include "messageeventmodeltest.moc"
|
||||
@@ -5,30 +5,138 @@
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
|
||||
#include "neochatroom.h"
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class TestRoom : public NeoChatRoom
|
||||
{
|
||||
public:
|
||||
using NeoChatRoom::NeoChatRoom;
|
||||
|
||||
void update(SyncRoomData &&data, bool fromCache = false)
|
||||
{
|
||||
Room::updateData(std::move(data), fromCache);
|
||||
}
|
||||
};
|
||||
|
||||
class NeoChatRoomTest : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
TestUtils::TestRoom *room = nullptr;
|
||||
TestRoom *room = nullptr;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void subtitleTextTest();
|
||||
void eventTest();
|
||||
};
|
||||
|
||||
void NeoChatRoomTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), "test-min-sync.json"_ls);
|
||||
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||
|
||||
auto json = QJsonDocument::fromJson(R"EVENT({
|
||||
"account_data": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"tags": {
|
||||
"u.work": {
|
||||
"order": 0.9
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.tag"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"custom_config_key": "custom_config_value"
|
||||
},
|
||||
"type": "org.example.custom.room.config"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ephemeral": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"user_ids": [
|
||||
"@alice:matrix.org",
|
||||
"@bob:example.com"
|
||||
]
|
||||
},
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"type": "m.typing"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Alice Margatroid",
|
||||
"membership": "join",
|
||||
"reason": "Looking for support"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"m.heroes": [
|
||||
"@alice:example.com",
|
||||
"@bob:example.com"
|
||||
],
|
||||
"m.invited_member_count": 0,
|
||||
"m.joined_member_count": 2
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an **example** text message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example text message</b>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1235
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
})EVENT");
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object());
|
||||
room->update(std::move(roomData));
|
||||
}
|
||||
|
||||
void NeoChatRoomTest::subtitleTextTest()
|
||||
{
|
||||
QCOMPARE(room->timelineSize(), 1);
|
||||
QCOMPARE(room->lastEventToString(), QStringLiteral("@example:example.org: This is an example text message"));
|
||||
}
|
||||
|
||||
void NeoChatRoomTest::eventTest()
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QObject>
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
|
||||
#include "events/pollevent.h"
|
||||
#include "pollhandler.h"
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class PollHandlerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
TestUtils::TestRoom *room = nullptr;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void nullObject();
|
||||
void poll();
|
||||
};
|
||||
|
||||
void PollHandlerTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), "test-pollhandlerstart-sync.json"_ls);
|
||||
}
|
||||
|
||||
// Basically don't crash.
|
||||
void PollHandlerTest::nullObject()
|
||||
{
|
||||
auto pollHandler = PollHandler();
|
||||
|
||||
QCOMPARE(pollHandler.hasEnded(), false);
|
||||
QCOMPARE(pollHandler.answerCount(), 0);
|
||||
QCOMPARE(pollHandler.question(), QString());
|
||||
QCOMPARE(pollHandler.options(), QJsonArray());
|
||||
QCOMPARE(pollHandler.answers(), QJsonObject());
|
||||
QCOMPARE(pollHandler.counts(), QJsonObject());
|
||||
QCOMPARE(pollHandler.kind(), QString());
|
||||
}
|
||||
|
||||
void PollHandlerTest::poll()
|
||||
{
|
||||
auto startEvent = eventCast<const PollStartEvent>(room->messageEvents().at(0).get());
|
||||
auto pollHandler = PollHandler(room, startEvent);
|
||||
|
||||
auto options = QJsonArray{QJsonObject{{"id"_ls, "option1"_ls}, {"org.matrix.msc1767.text"_ls, "option1"_ls}},
|
||||
QJsonObject{{"id"_ls, "option2"_ls}, {"org.matrix.msc1767.text"_ls, "option2"_ls}}};
|
||||
|
||||
QCOMPARE(pollHandler.hasEnded(), false);
|
||||
QCOMPARE(pollHandler.answerCount(), 0);
|
||||
QCOMPARE(pollHandler.question(), QStringLiteral("test"));
|
||||
QCOMPARE(pollHandler.options(), options);
|
||||
QCOMPARE(pollHandler.answers(), QJsonObject());
|
||||
QCOMPARE(pollHandler.counts(), QJsonObject());
|
||||
QCOMPARE(pollHandler.kind(), QStringLiteral("org.matrix.msc3381.poll.disclosed"));
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(PollHandlerTest)
|
||||
#include "pollhandlertest.moc"
|
||||
@@ -1,83 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QObject>
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
|
||||
#include "models/reactionmodel.h"
|
||||
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class ReactionModelTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
TestUtils::TestRoom *room = nullptr;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void nullModel();
|
||||
void basicReaction();
|
||||
void newReaction();
|
||||
};
|
||||
|
||||
void ReactionModelTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-reactionmodel-sync.json"));
|
||||
}
|
||||
|
||||
void ReactionModelTest::nullModel()
|
||||
{
|
||||
auto model = ReactionModel(nullptr, nullptr);
|
||||
|
||||
QCOMPARE(model.rowCount(), 0);
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::TextContentRole), QVariant());
|
||||
}
|
||||
|
||||
void ReactionModelTest::basicReaction()
|
||||
{
|
||||
auto event = eventCast<const RoomMessageEvent>(room->messageEvents().at(0).get());
|
||||
auto model = ReactionModel(event, room);
|
||||
|
||||
QCOMPARE(model.rowCount(), 1);
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::TextContentRole), QStringLiteral("<span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::ReactionRole), QStringLiteral("👍"));
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::ToolTipRole),
|
||||
QStringLiteral("@alice:matrix.org reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
auto authorList = QVariantList{room->getUser(room->user(QStringLiteral("@alice:matrix.org")))};
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::AuthorsRole), authorList);
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::HasLocalUser), false);
|
||||
}
|
||||
|
||||
void ReactionModelTest::newReaction()
|
||||
{
|
||||
auto event = eventCast<const RoomMessageEvent>(room->messageEvents().at(0).get());
|
||||
auto model = new ReactionModel(event, room);
|
||||
|
||||
QCOMPARE(model->rowCount(), 1);
|
||||
QCOMPARE(model->data(model->index(0), ReactionModel::ToolTipRole),
|
||||
QStringLiteral("@alice:matrix.org reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
|
||||
QSignalSpy spy(model, SIGNAL(modelReset()));
|
||||
|
||||
room->syncNewEvents(QLatin1String("test-reactionmodel-extra-sync.json"));
|
||||
QCOMPARE(model->rowCount(), 2);
|
||||
QCOMPARE(spy.count(), 2); // Once for each of the 2 new reactions.
|
||||
QCOMPARE(model->data(model->index(1), ReactionModel::ReactionRole), QStringLiteral("😆"));
|
||||
QCOMPARE(model->data(model->index(0), ReactionModel::ToolTipRole),
|
||||
QStringLiteral("@alice:matrix.org and @bob:example.org reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
|
||||
delete model;
|
||||
}
|
||||
|
||||
QTEST_MAIN(ReactionModelTest)
|
||||
#include "reactionmodeltest.moc"
|
||||
@@ -1,41 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <Quotient/syncdata.h>
|
||||
|
||||
#include "neochatroom.h"
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class Connection;
|
||||
}
|
||||
|
||||
namespace TestUtils
|
||||
{
|
||||
class TestRoom : public NeoChatRoom
|
||||
{
|
||||
public:
|
||||
TestRoom(Quotient::Connection *connection, const QString &roomName, const QString &syncFileName = {})
|
||||
: NeoChatRoom(connection, roomName, Quotient::JoinState::Join)
|
||||
{
|
||||
syncNewEvents(syncFileName);
|
||||
}
|
||||
|
||||
void update(Quotient::SyncRoomData &&data, bool fromCache = false)
|
||||
{
|
||||
Room::updateData(std::move(data), fromCache);
|
||||
}
|
||||
|
||||
void syncNewEvents(const QString &syncFileName)
|
||||
{
|
||||
if (!syncFileName.isEmpty()) {
|
||||
QFile testSyncFile;
|
||||
testSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + syncFileName);
|
||||
testSyncFile.open(QIODevice::ReadOnly);
|
||||
const auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll());
|
||||
Quotient::SyncRoomData roomData(id(), Quotient::JoinState::Join, testSyncJson.object());
|
||||
update(std::move(roomData));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -10,21 +10,26 @@
|
||||
#include <Quotient/syncdata.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
#include "models/customemojimodel.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class TestRoom : public NeoChatRoom
|
||||
{
|
||||
public:
|
||||
using NeoChatRoom::NeoChatRoom;
|
||||
|
||||
void update(SyncRoomData &&data, bool fromCache = false)
|
||||
{
|
||||
Room::updateData(std::move(data), fromCache);
|
||||
}
|
||||
};
|
||||
|
||||
class TextHandlerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
TestUtils::TestRoom *room = nullptr;
|
||||
TestRoom *room = nullptr;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
@@ -33,7 +38,6 @@ private Q_SLOTS:
|
||||
void stripDisallowedTags();
|
||||
void stripDisallowedAttributes();
|
||||
void emptyCodeTags();
|
||||
void formatBlockQuote();
|
||||
|
||||
void sendSimpleStringCase();
|
||||
void sendSingleParaMarkup();
|
||||
@@ -41,9 +45,6 @@ private Q_SLOTS:
|
||||
void sendBadLinks();
|
||||
void sendEscapeCode();
|
||||
void sendCodeClass();
|
||||
void sendCustomEmoji();
|
||||
void sendCustomEmojiCode_data();
|
||||
void sendCustomEmojiCode();
|
||||
|
||||
void receiveStripReply();
|
||||
void receivePlainTextIn();
|
||||
@@ -63,7 +64,6 @@ private Q_SLOTS:
|
||||
void receiveRichEdited_data();
|
||||
void receiveRichEdited();
|
||||
void receiveLineSeparator();
|
||||
void receiveRichCodeUrl();
|
||||
|
||||
void linkPreviewsMatch_data();
|
||||
void linkPreviewsMatch();
|
||||
@@ -74,15 +74,134 @@ private Q_SLOTS:
|
||||
void TextHandlerTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
connection->setAccountData("im.ponies.user_emotes"_ls,
|
||||
QJsonObject{{"images"_ls,
|
||||
QJsonObject{{"test"_ls,
|
||||
QJsonObject{{"body"_ls, "Test custom emoji"_ls},
|
||||
{"url"_ls, "mxc://example.org/test"_ls},
|
||||
{"usage"_ls, QJsonArray{"emoticon"_ls}}}}}}});
|
||||
CustomEmojiModel::instance().setConnection(static_cast<NeoChatConnection *>(connection));
|
||||
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||
|
||||
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-texthandler-sync.json"));
|
||||
const auto json = QJsonDocument::fromJson(R"EVENT({
|
||||
"account_data": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"tags": {
|
||||
"u.work": {
|
||||
"order": 0.9
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.tag"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"custom_config_key": "custom_config_value"
|
||||
},
|
||||
"type": "org.example.custom.room.config"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ephemeral": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"user_ids": [
|
||||
"@alice:matrix.org",
|
||||
"@bob:example.com"
|
||||
]
|
||||
},
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"type": "m.typing"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Alice Margatroid",
|
||||
"membership": "join",
|
||||
"reason": "Looking for support"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"m.heroes": [
|
||||
"@alice:example.com",
|
||||
"@bob:example.com"
|
||||
],
|
||||
"m.invited_member_count": 0,
|
||||
"m.joined_member_count": 2
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an **example** text message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example text message</b>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1232
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "/me This is an emote.",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "This is an emote.",
|
||||
"msgtype": "m.emote"
|
||||
},
|
||||
"event_id": "$153273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1532735824654,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1231
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "tested",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$zrCiBxBnqqTn0Z5FY78qSZAszno_w8nJJXzfBULG-3E",
|
||||
"origin_server_ts": 1680948575928,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1747776,
|
||||
"m.relations": {
|
||||
"m.replace": {
|
||||
"event_id": "$UX0PlpyI7vYO32iHMuuYEP7ECMh4sX3XLGiB2SwM4mQ",
|
||||
"origin_server_ts": 1680948580992,
|
||||
"sender": "@example:example.org"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
})EVENT");
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object());
|
||||
room->update(std::move(roomData));
|
||||
}
|
||||
|
||||
void TextHandlerTest::allowedAttributes()
|
||||
@@ -144,16 +263,6 @@ void TextHandlerTest::emptyCodeTags()
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::formatBlockQuote()
|
||||
{
|
||||
auto input = QStringLiteral("<blockquote>\n<p>Lorem Ispum</p>\n</blockquote>");
|
||||
auto expectedOutput = QStringLiteral("<blockquote><table><tr><td>\u201CLorem Ispum\u201D</td></tr></table></blockquote>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(input);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), expectedOutput);
|
||||
}
|
||||
|
||||
void TextHandlerTest::sendSimpleStringCase()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("This data should just be put in a paragraph.");
|
||||
@@ -231,39 +340,6 @@ void TextHandlerTest::sendCodeClass()
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::sendCustomEmoji()
|
||||
{
|
||||
const QString testInputString = QStringLiteral(":test:");
|
||||
const QString testOutputString = QStringLiteral(
|
||||
"<p><img data-mx-emoticon=\"\" src=\"mxc://example.org/test\" alt=\":test:\" title=\":test:\" height=\"32\" vertical-align=\"middle\" /></p>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::sendCustomEmojiCode_data()
|
||||
{
|
||||
QTest::addColumn<QString>("testInputString");
|
||||
QTest::addColumn<QString>("testOutputString");
|
||||
|
||||
QTest::newRow("inline") << QStringLiteral("`:test:`") << QStringLiteral("<p><code>:test:</code></p>");
|
||||
QTest::newRow("block") << QStringLiteral("```\n:test:\n```") << QStringLiteral("<pre><code>:test:\n</code></pre>");
|
||||
}
|
||||
|
||||
// Custom emojis in code blocks should be left alone.
|
||||
void TextHandlerTest::sendCustomEmojiCode()
|
||||
{
|
||||
QFETCH(QString, testInputString);
|
||||
QFETCH(QString, testOutputString);
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveStripReply()
|
||||
{
|
||||
const QString testInputString = QStringLiteral(
|
||||
@@ -285,7 +361,6 @@ void TextHandlerTest::receiveRichInPlainOut_data()
|
||||
|
||||
QTest::newRow("ampersand") << QStringLiteral("a & b") << QStringLiteral("a & b");
|
||||
QTest::newRow("quote") << QStringLiteral(""a and b"") << QStringLiteral("\"a and b\"");
|
||||
QTest::newRow("new line") << QStringLiteral("new<br>line") << QStringLiteral("new\nline");
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveRichInPlainOut()
|
||||
@@ -480,10 +555,10 @@ void TextHandlerTest::receiveRichPlainUrl()
|
||||
void TextHandlerTest::receiveRichEmote()
|
||||
{
|
||||
auto event = room->messageEvents().at(1).get();
|
||||
auto author = room->user(event->senderId());
|
||||
auto author = static_cast<NeoChatUser *>(room->user(event->senderId()));
|
||||
const QString testInputString = QStringLiteral("This is an emote.");
|
||||
const QString testOutputString = QStringLiteral("* <a href=\"https://matrix.to/#/@example:example.org\" style=\"color:")
|
||||
+ Utils::getUserColor(author->hueF()).name() + QStringLiteral("\">@example:example.org</a> This is an emote.");
|
||||
const QString testOutputString = QStringLiteral("* <a href=\"https://matrix.to/#/@example:example.org\" style=\"color:") + author->color().name()
|
||||
+ QStringLiteral("\">@example:example.org</a> This is an emote.");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
@@ -499,9 +574,8 @@ void TextHandlerTest::receiveRichEdited_data()
|
||||
QTest::newRow("basic") << QStringLiteral("Edited") << QStringLiteral("Edited <span style=\"color:#000000\">(edited)</span>");
|
||||
QTest::newRow("multiple paragraphs") << QStringLiteral("<p>Edited</p>\n<p>Edited</p>")
|
||||
<< QStringLiteral("<p>Edited</p>\n<p>Edited <span style=\"color:#000000\">(edited)</span></p>");
|
||||
QTest::newRow("blockquote")
|
||||
<< QStringLiteral("<blockquote>Edited</blockquote>")
|
||||
<< QStringLiteral("<blockquote><table><tr><td>\u201CEdited\u201D</td></tr></table></blockquote><p> <span style=\"color:#000000\">(edited)</span></p>");
|
||||
QTest::newRow("blockquote") << QStringLiteral("<blockquote>Edited</blockquote>")
|
||||
<< QStringLiteral("<blockquote>Edited</blockquote><p> <span style=\"color:#000000\">(edited)</span></p>");
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveRichEdited()
|
||||
@@ -528,13 +602,13 @@ void TextHandlerTest::linkPreviewsMatch_data()
|
||||
QTest::addColumn<QString>("testInputString");
|
||||
QTest::addColumn<QList<QUrl>>("testOutputLinks");
|
||||
|
||||
QTest::newRow("plainHttps") << QStringLiteral("https://kde.org") << QList<QUrl>({QUrl("https://kde.org"_ls)});
|
||||
QTest::newRow("richHttps") << QStringLiteral("<a href=\"https://kde.org\">Rich Link</a>") << QList<QUrl>({QUrl("https://kde.org"_ls)});
|
||||
QTest::newRow("plainWww") << QStringLiteral("www.example.org") << QList<QUrl>({QUrl("www.example.org"_ls)});
|
||||
QTest::newRow("plainHttps") << QStringLiteral("https://kde.org") << QList<QUrl>({QUrl("https://kde.org")});
|
||||
QTest::newRow("richHttps") << QStringLiteral("<a href=\"https://kde.org\">Rich Link</a>") << QList<QUrl>({QUrl("https://kde.org")});
|
||||
QTest::newRow("plainWww") << QStringLiteral("www.example.org") << QList<QUrl>({QUrl("www.example.org")});
|
||||
QTest::newRow("multipleHttps") << QStringLiteral("https://kde.org www.example.org")
|
||||
<< QList<QUrl>({
|
||||
QUrl("https://kde.org"_ls),
|
||||
QUrl("www.example.org"_ls),
|
||||
QUrl("https://kde.org"),
|
||||
QUrl("www.example.org"),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -570,13 +644,5 @@ void TextHandlerTest::linkPreviewsReject()
|
||||
QCOMPARE(testTextHandler.getLinkPreviews(), testOutputLinks);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveRichCodeUrl()
|
||||
{
|
||||
auto input = QStringLiteral("<code>https://kde.org</code>");
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(input);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), input);
|
||||
}
|
||||
|
||||
QTEST_MAIN(TextHandlerTest)
|
||||
QTEST_GUILESS_MAIN(TextHandlerTest)
|
||||
#include "texthandlertest.moc"
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QTest>
|
||||
#include <QWindow>
|
||||
|
||||
#include <KConfig>
|
||||
#include <KSharedConfig>
|
||||
#include <KWindowConfig>
|
||||
|
||||
#include "windowcontroller.h"
|
||||
|
||||
class WindowControllerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void nullWindow();
|
||||
void geometry();
|
||||
void showAndRaise();
|
||||
void toggle();
|
||||
|
||||
void cleanup();
|
||||
};
|
||||
|
||||
// Basically don't crash when no window is set.
|
||||
void WindowControllerTest::nullWindow()
|
||||
{
|
||||
auto &instance = WindowController::instance();
|
||||
QCOMPARE(instance.window(), nullptr);
|
||||
|
||||
instance.restoreGeometry();
|
||||
instance.saveGeometry();
|
||||
instance.showAndRaiseWindow({});
|
||||
instance.toggleWindow();
|
||||
}
|
||||
|
||||
void WindowControllerTest::geometry()
|
||||
{
|
||||
auto &instance = WindowController::instance();
|
||||
|
||||
QWindow window;
|
||||
window.setGeometry(0, 0, 200, 200);
|
||||
instance.setWindow(&window);
|
||||
QCOMPARE(instance.window(), &window);
|
||||
|
||||
instance.saveGeometry();
|
||||
const auto stateConfig = KSharedConfig::openStateConfig();
|
||||
KConfigGroup windowGroup = stateConfig->group(QStringLiteral("Window"));
|
||||
QCOMPARE(KWindowConfig::hasSavedWindowSize(windowGroup), true);
|
||||
|
||||
window.setGeometry(0, 0, 400, 400);
|
||||
QCOMPARE(window.geometry(), QRect(0, 0, 400, 400));
|
||||
instance.restoreGeometry();
|
||||
QCOMPARE(window.geometry(), QRect(0, 0, 200, 200));
|
||||
}
|
||||
|
||||
void WindowControllerTest::showAndRaise()
|
||||
{
|
||||
auto &instance = WindowController::instance();
|
||||
QWindow window;
|
||||
instance.setWindow(&window);
|
||||
QCOMPARE(window.isVisible(), false);
|
||||
|
||||
instance.showAndRaiseWindow({});
|
||||
QCOMPARE(window.isVisible(), true);
|
||||
}
|
||||
|
||||
void WindowControllerTest::cleanup()
|
||||
{
|
||||
auto &instance = WindowController::instance();
|
||||
instance.setWindow(nullptr);
|
||||
QCOMPARE(instance.window(), nullptr);
|
||||
}
|
||||
|
||||
void WindowControllerTest::toggle()
|
||||
{
|
||||
auto &instance = WindowController::instance();
|
||||
QWindow window;
|
||||
instance.setWindow(&window);
|
||||
QCOMPARE(window.isVisible(), false);
|
||||
instance.toggleWindow();
|
||||
QCOMPARE(window.isVisible(), true);
|
||||
instance.toggleWindow();
|
||||
QCOMPARE(window.isVisible(), false);
|
||||
|
||||
// A window is classed as visible by qt when minimized but to the user this is not visible.
|
||||
// So in this case we expect to show it even though visibility is technically true.
|
||||
window.setVisibility(QWindow::Minimized);
|
||||
QCOMPARE(window.windowState(), Qt::WindowMinimized);
|
||||
QCOMPARE(window.isVisible(), true);
|
||||
instance.toggleWindow();
|
||||
QCOMPARE(window.windowState(), Qt::WindowNoState);
|
||||
QCOMPARE(window.isVisible(), true);
|
||||
instance.toggleWindow();
|
||||
QCOMPARE(window.windowState(), Qt::WindowNoState);
|
||||
QCOMPARE(window.isVisible(), false);
|
||||
}
|
||||
|
||||
QTEST_MAIN(WindowControllerTest)
|
||||
#include "windowcontrollertest.moc"
|
||||
@@ -2,4 +2,4 @@
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
kdoctools_create_manpage(man-neochat.1.docbook 1 INSTALL_DESTINATION ${KDE_INSTALL_MANDIR})
|
||||
kdoctools_create_manpage(man-neochat.1.docbook 1 INSTALL_DESTINATION ${MAN_INSTALL_DIR})
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
</provides>
|
||||
<name>NeoChat</name>
|
||||
<name xml:lang="ar">نيوتشات</name>
|
||||
<name xml:lang="ast">NeoChat</name>
|
||||
<name xml:lang="az">NeoChat</name>
|
||||
<name xml:lang="ca">NeoChat</name>
|
||||
<name xml:lang="ca-valencia">NeoChat</name>
|
||||
@@ -48,32 +47,44 @@
|
||||
<name xml:lang="x-test">xxNeoChatxx</name>
|
||||
<name xml:lang="zh-CN">NeoChat</name>
|
||||
<name xml:lang="zh-TW">NeoChat</name>
|
||||
<summary>Chat with your friends on matrix</summary>
|
||||
<summary xml:lang="ar">دردش مع أصدقائك على ماتركس</summary>
|
||||
<summary xml:lang="ca">Xategeu amb els vostres amics a Matrix</summary>
|
||||
<summary xml:lang="ca-valencia">Xategeu amb els vostres amics a Matrix</summary>
|
||||
<summary xml:lang="cs">Mluvte se svými přáteli na Matrixu</summary>
|
||||
<summary xml:lang="eo">Babilu kun viaj amikoj sur matrix</summary>
|
||||
<summary xml:lang="es">Charle con sus amigos en matrix</summary>
|
||||
<summary xml:lang="eu">Berriketan jardun zure lagunekin «Matrix»en</summary>
|
||||
<summary xml:lang="fi">Keskustelu ystäviesi kanssa Matrixissa</summary>
|
||||
<summary xml:lang="fr">Discuter avec vos ami(e)s sur le réseau Matrix</summary>
|
||||
<summary xml:lang="gl">Charle coas súas amizades en Matrix.</summary>
|
||||
<summary xml:lang="ia">Starta Conversation conntu amicos sur matrix</summary>
|
||||
<summary xml:lang="it">Conversa con i tuoi contatti su matrix</summary>
|
||||
<summary xml:lang="ka">ესაუბრეთ მეგობრებს Matrix-ზე</summary>
|
||||
<summary xml:lang="ko">Matrix를 사용하여 친구들과 대화하기</summary>
|
||||
<summary xml:lang="nl">Met uw vrienden chatten op matrix</summary>
|
||||
<summary xml:lang="nn">Prat med vennar på Matrix</summary>
|
||||
<summary xml:lang="pl">Rozmawiaj ze swoimi znajomymi w Matriksie</summary>
|
||||
<summary xml:lang="sl">Klepet z vašimi prijatelji na matrixu</summary>
|
||||
<summary xml:lang="sv">Chatta med dina vänner på Matrix</summary>
|
||||
<summary xml:lang="ta">மேட்ரிக்ஸு மூலம் உங்கள் நண்பர்களிடம் பேசலாம்</summary>
|
||||
<summary xml:lang="tr">Matrix'te arkadaşlarınızla sohbet edin</summary>
|
||||
<summary xml:lang="uk">Спілкуйтеся з вашими друзями у matrix</summary>
|
||||
<summary xml:lang="x-test">xxChat with your friends on matrixxx</summary>
|
||||
<summary xml:lang="zh-CN">在 Matrix 上与朋友聊天</summary>
|
||||
<summary xml:lang="zh-TW">在 Matrix 上與您的朋友聊天</summary>
|
||||
<summary>A client for matrix, the decentralized communication protocol</summary>
|
||||
<summary xml:lang="ar">عميل لماتركس، ميفاق الاتصال اللامركزي</summary>
|
||||
<summary xml:lang="az">Matrix üçün müştəri, mərkəzləşməmiş kommunikasiya protokolu</summary>
|
||||
<summary xml:lang="ca">Un client de Matrix, el protocol de comunicacions descentralitzat</summary>
|
||||
<summary xml:lang="ca-valencia">Un client de Matrix, el protocol de comunicacions descentralitzat</summary>
|
||||
<summary xml:lang="cs">Klient pro decentralizovaný komunikační protokol matrix</summary>
|
||||
<summary xml:lang="de">Ein Programm für Matrix, das dezentrale Kommunikationsprotokoll</summary>
|
||||
<summary xml:lang="el">Ένας πελάτης για το Matrix, το αποκεντρωμένο πρωτόκολλο επικοινωνίας</summary>
|
||||
<summary xml:lang="en-GB">A client for matrix, the decentralised communication protocol</summary>
|
||||
<summary xml:lang="eo">Kliento por matrix, la malcentra komunikprotokolo</summary>
|
||||
<summary xml:lang="es">Un cliente para Matrix, el protocolo de comunicaciones descentralizado</summary>
|
||||
<summary xml:lang="eu">Matrix, deszentralizatutako komunikazio protokolorako bezero bat</summary>
|
||||
<summary xml:lang="fi">Asiakas Matrixille, hajautetulle viestintäyhteyskäytännölle</summary>
|
||||
<summary xml:lang="fr">Un client pour « Matrix », le protocole décentralisé de communications.</summary>
|
||||
<summary xml:lang="gl">Un cliente para Matrix, o protocolo de comunicación descentralizada.</summary>
|
||||
<summary xml:lang="hu">Kliens a matrixhoz, a decentralizált kommunikációs protokollhoz</summary>
|
||||
<summary xml:lang="ia">Un cliente per Matrix, le protocollo de communication decentralisate</summary>
|
||||
<summary xml:lang="id">Klien untuk matrix, protokol komunikasi terdesentralisasi</summary>
|
||||
<summary xml:lang="ie">Un cliente de Matrix, li protocol de communication decentralisat</summary>
|
||||
<summary xml:lang="it">Un client per matrix, il protocollo di comunicazione decentralizzato</summary>
|
||||
<summary xml:lang="ka">კლიენტი Matrix-სთვის, დეცენტრალიზებული კომუნიკაციის პროტოკოლისთვის</summary>
|
||||
<summary xml:lang="ko">Matrix, 분산 대화 프로토콜 클라이언트</summary>
|
||||
<summary xml:lang="nl">Een client voor matrix, het gedecentraliseerde communicatieprotocol</summary>
|
||||
<summary xml:lang="nn">Ein klient for Matrix – protokollen for desentralisert kommunikasjon</summary>
|
||||
<summary xml:lang="pa">ਮੈਟਰਿਕਸ, ਸਰਬ-ਸਾਂਝੇ ਸੰਚਾਰ ਪਰੋਟੋਕਾਲ, ਲਈ ਕਲਾਈਂਟ ਹੈ</summary>
|
||||
<summary xml:lang="pl">Program do obsługi matriksa, rozproszonego protokołu porozumiewania się</summary>
|
||||
<summary xml:lang="pt">Um cliente para o Matrix, o protocolo de comunicação descentralizado</summary>
|
||||
<summary xml:lang="pt-BR">Um cliente do Matrix, o protocolo de comunicação descentralizado</summary>
|
||||
<summary xml:lang="ru">Клиент для Matrix — децентрализованного коммуникационного протокола</summary>
|
||||
<summary xml:lang="sk">Klient pre matrix, decentralizovaný komunikačný protokol</summary>
|
||||
<summary xml:lang="sl">Odjemalec za matrix, decentralizirani komunikacijski protokol</summary>
|
||||
<summary xml:lang="sv">En klient för Matrix, det decentraliserade kommunikationsprotokollet</summary>
|
||||
<summary xml:lang="ta">மையமில்லா தகவல் பரிமாற்ற நெறிமுறையான மேட்ரிக்ஸுக்கான செயலி</summary>
|
||||
<summary xml:lang="tr">Merkezi olmayan iletişim protokolü Matrix için bir istemci</summary>
|
||||
<summary xml:lang="uk">Клієнт matrix, децентралізованого протоколу обміну даними</summary>
|
||||
<summary xml:lang="x-test">xxA client for matrix, the decentralized communication protocolxx</summary>
|
||||
<summary xml:lang="zh-CN">分布式通讯协议 Matrix 的客户端</summary>
|
||||
<summary xml:lang="zh-TW">去中心化通訊協定 Matrix 的用戶端</summary>
|
||||
<description>
|
||||
<p>NeoChat is a client for Matrix, the decentralized communication protocol for instant messaging. It allows you to send text messages, videos and audio files to your family, colleagues and friends. It uses KDE frameworks and most notably Kirigami
|
||||
to provide a convergent experience across multiple platforms.</p>
|
||||
@@ -95,6 +106,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<p xml:lang="nn">NeoChat er ein klient for Matrix, ein protokoll for desentralisert kommunikasjon. Du kan utveksla tekst, lyd og videoar med kollegaar, vennar og familie. Programmet brukar KDE Frameworks og Kirigami for å gje ei brukarflate tilpassa ulike plattformer.</p>
|
||||
<p xml:lang="pl">NeoChat jest programem do Matriksa, protokołu rozproszonego porozumiewania się w czasie rzeczywistym. Umożliwia wysyłanie wiadomości tekstowych, filmów oraz dźwięku do twojej rodziny, znajomych oraz przyjaciół. Używa szkieletów KDE i głównie Kirigami, aby zapewnić spójne wrażenia na wielu platformach</p>
|
||||
<p xml:lang="pt">O NeoChat é um cliente do Matrix. O mesmo permite-lhe enviar mensagens de texto, ficheiros de vídeo e áudio para a sua família, colegas e amigos com o protocolo Matrix. Usa as plataformas do KDE, e principalmente o Kirigami, para oferecer uma experiência convergente entre várias plataformas.</p>
|
||||
<p xml:lang="ru">NeoChat — клиент для Matrix, децентрализованного коммуникационного протокола для мгновенного обмена сообщениями. С его помощью возможно отправлять текстовые сообщения, видео- и звуковые файлы родственникам, коллегам и друзьям. В программе использованы библиотеки KDE, в частности Kirigami, для обеспечения единообразной работы на различных платформах.</p>
|
||||
<p xml:lang="sl">Neochat je odjemalec za Matrix, decentralizirani komunikacijski protokol za takojšnje sporočanje. Omogoča vam pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek svoji družini, sodelavcem in prijateljem. Uporablja okvire ogrodje KDE frameworks in predvsem Kirigami za zagotavljanje konvergentne izkušnje na več platformah.</p>
|
||||
<p xml:lang="sv">NeoChat är en klient för Matrix, det decentraliserade kommunikationsprotokollet för direktmeddelanden. Den låter dig skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner. Den använder KDE Ramverk, i synnerhet Kirigami, för att tillhandahålla en konvergent upplevelse på flera plattformar.</p>
|
||||
<p xml:lang="tr">NeoChat, anlık iletileşme için merkezi olmayan iletişim protokolü olan Matrix için bir istemcidir. Ailenize, iş arkadaşlarınıza ve arkadaşlarınıza metin iletiler, videolar ve ses dosyaları göndermenize olanak tanır. Birden çok platformda yakınsak bir deneyim sağlamak için KDE Frameworks ve en önemlilerinden Kirigami'yi kullanır.</p>
|
||||
@@ -120,6 +132,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<p xml:lang="nn">NeoChat har som mål å støtta all funksjonalitet i Matrix-spesifikasjonen. Førebels er alt i den gjeldande stabile spesifikasjonen støtta, med unntak av VoIP, trådar og nokre delar av ende-til-kryptering. Det finst òg andre småting som ikkje er støtta, sidan Matrix-spesifikasjon er i stadig endring, men målet er altså støtte for alt.</p>
|
||||
<p xml:lang="pl">NeoChat w zamyśle ma być pełnowartościową aplikacją wg wytycznych Matriksa. Z tego powodu, wszystko, co jest obecnie w stabilnych wytycznych z pominięciem VoIP, wątków i niektórych części szyfrowania Użytkownik-do-Użytkownika są obecnie obsługiwane. Pominięto też kilka mniejszych rzeczy ze względu na ciągły rozwój wytycznych Matriksa, lecz celem nadal jest zapewnienie obsługi wszystkich wytycznych.</p>
|
||||
<p xml:lang="pt">O NeoChat pretende ser uma aplicação completa para a especificação do Matrix. Como tal, tudo o que existe na especificação estável actual, com as notáveis excepções do VoIP, tópicos e alguns aspectos da Encriptação Ponto-a-Ponto, são suportados. Existem mais algumas omissões, devido ao facto que a norma do Matrix está em constante evolução, mas o objectivo continua a ser oferecer o suporte eventual para a norma por inteiro.</p>
|
||||
<p xml:lang="ru">Целью создания NeoChat является полноценная реализация программы для спецификации Matrix. Как следствие, реализовано всё в текущей стабильной спецификации (за исключением голосовой интернет-связи, потоков и некоторых аспектов сквозного шифрования). Есть также несколько других незначительных пробелов, обусловленных постоянными изменениями спецификации Matrix. Тем не менее, стоит задача в итоге предоставить полную поддержку спецификации.</p>
|
||||
<p xml:lang="sl">Neochat cilja, da bi bila popolna aplikacija po specifikaciji Matrixa. Kot takšna vsebuje vse v trenutni stabilni specifikaciji z pomembnimi izjemami pri VoIP, nitih in nekaterih vidikov šifriranja od konca do konca. Obstaja nekaj drugih manjših opustitev zaradi dejstva, da se specifikacija Matrix nenehno razvija, vendar cilj ostaja zagotoviti morebitno podporo celotni specifikaciji.</p>
|
||||
<p xml:lang="sv">NeoChat har som mål att vara ett fullständigt program enligt Matrix-specifikationen. Som sådant stöds allt i den nuvarande stabila specifikationen, med de nämnvärda undantagen VoIP, trådar och några aspekter av kryptering hela vägen. Det finns några ytterligare utelämnanden på grund av att Matrix-specifikationen hela tiden utvecklas, men målet förblir att till slut erbjuda stöd för hela specifikationen.</p>
|
||||
<p xml:lang="tr">NeoChat, Matrix belirtimi için tam özellikli bir uygulama olmayı hedefler. Bu nedenle; VoIP, ileti zincirleri ve Uçtan Uca Şifreleme'nin bazı yönleri gibi dikkate değer istisnalar dışında var olan kararlı belirtimdeki her şey desteklenir. Matrix belirtiminin sürekli gelişmesi nedeniyle birkaç küçük eksiklik daha var; ancak amaç tüm belirtim için nihai destek sağlamak olmayı sürdürüyor.</p>
|
||||
@@ -145,6 +158,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<p xml:lang="nn">På grunn av måten Matrix-spesifikasjonen vert utvikla på, støttar NeoChat òg nokre uferdige funksjonar:</p>
|
||||
<p xml:lang="pl">Ze względu na sposób rozwoju Matriksa, NeoChat obsługuje także kilka niestabilnych możliwości. Obecnie są to:</p>
|
||||
<p xml:lang="pt">Devido à natureza do desenvolvimento da especificação do Matrix, o NeoChat também suporta diversas funcionalidades instáveis. De momento são:</p>
|
||||
<p xml:lang="ru">В силу природы разработки спецификации Matrix в NeoChat тоже предусмотрена поддержка многочисленных нестабильных возможностей. В текущей версии это следующие возможности:</p>
|
||||
<p xml:lang="sl">Zaradi narave razvoja specifikacije Matrixa NeoChat podpira tudi številne nestabilne zmožnosti. Trenutno so to:</p>
|
||||
<p xml:lang="sv">På grund av sättet Matrix-specifikationens utvecklas, stöder NeoChat också ett stor antal instabila funktioner. För närvarande är de:</p>
|
||||
<p xml:lang="ta">மேட்ரிக்ஸு நெறிமுறை வரையறுக்கப்படும் விதத்தின் காரணமாக, பல நிலையற்ற அம்சங்களையும் நியோச்சாட் ஆதரிக்கிறது. தற்போது ஆதரிக்கப்படுபவை:</p>
|
||||
@@ -172,6 +186,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<li xml:lang="nn">Avstemmingar – MSC3381</li>
|
||||
<li xml:lang="pl">Ankiety - MSC3381</li>
|
||||
<li xml:lang="pt">Inquéritos - MSC3381</li>
|
||||
<li xml:lang="ru">Голосования — MSC3381</li>
|
||||
<li xml:lang="sl">Polls - MSC3381</li>
|
||||
<li xml:lang="sv">Polls - MSC3381</li>
|
||||
<li xml:lang="ta">வாக்கெடுப்புகள் - MSC3381</li>
|
||||
@@ -198,6 +213,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<li xml:lang="nn">Klistremerke-pakkar – MSC2545</li>
|
||||
<li xml:lang="pl">Paczki naklejek - MSC2545</li>
|
||||
<li xml:lang="pt">Pacotes de Autocolantes - MSC2545</li>
|
||||
<li xml:lang="ru">Наборы наклеек — MSC2545</li>
|
||||
<li xml:lang="sl">Sticker Packs - MSC2545</li>
|
||||
<li xml:lang="sv">Sticker Packs - MSC2545</li>
|
||||
<li xml:lang="ta">ஒட்டி தொகுப்புகள் - MSC2545</li>
|
||||
@@ -224,6 +240,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<li xml:lang="nn">Posisjonshendingar – MSC3488</li>
|
||||
<li xml:lang="pl">Wydarzenia w miejscach - MSC3488</li>
|
||||
<li xml:lang="pt">Eventos com Localizações - MSC3488</li>
|
||||
<li xml:lang="ru">События местоположения — MSC3488</li>
|
||||
<li xml:lang="sl">Location Events - MSC3488</li>
|
||||
<li xml:lang="sv">Location Events - MSC3488</li>
|
||||
<li xml:lang="ta">இட நிகழ்வுகள் - MSC3488</li>
|
||||
@@ -233,8 +250,8 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<li xml:lang="zh-TW">位置事件 - MSC3488</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="homepage">https://apps.kde.org/neochat</url>
|
||||
<url type="bugtracker">https://bugs.kde.org/enter_bug.cgi?product=NeoChat</url>
|
||||
<url type="homepage">https://apps.kde.org/neochat/</url>
|
||||
<url type="bugtracker">https://bugs.kde.org/buglist.cgi?component=General&product=NeoChat</url>
|
||||
<categories>
|
||||
<category>Network</category>
|
||||
</categories>
|
||||
@@ -295,7 +312,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<screenshot type="default">
|
||||
<image>https://cdn.kde.org/screenshots/neochat/application.png</image>
|
||||
</screenshot>
|
||||
<screenshot environment="windows">
|
||||
<screenshot x-kde-os="windows">
|
||||
<image>https://cdn.kde.org/screenshots/neochat/NeoChat-Windows-Timeline.png</image>
|
||||
<caption>Main view with room list, chat, and room information</caption>
|
||||
<caption xml:lang="ar">العرض الرئيسة مع قائمة الغرف والدردشات و معلومات الغرفة</caption>
|
||||
@@ -315,6 +332,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
|
||||
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
|
||||
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
|
||||
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
|
||||
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
|
||||
<caption xml:lang="sv">Huvudvy med rumslista, chatt, och rumsinformation</caption>
|
||||
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
|
||||
@@ -323,7 +341,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<caption xml:lang="x-test">xxMain view with room list, chat, and room informationxx</caption>
|
||||
<caption xml:lang="zh-TW">主頁面,包含聊天室列表、聊天內容,與聊天室資訊</caption>
|
||||
</screenshot>
|
||||
<screenshot environment="windows">
|
||||
<screenshot x-kde-os="windows">
|
||||
<image>https://cdn.kde.org/screenshots/neochat/NeoChat-Windows-Login.png</image>
|
||||
<caption>Login screen</caption>
|
||||
<caption xml:lang="ar">شاشة الدخول</caption>
|
||||
@@ -344,6 +362,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<caption xml:lang="nn">Innloggingsbilete</caption>
|
||||
<caption xml:lang="pl">Ekran logowania</caption>
|
||||
<caption xml:lang="pt">Ecrã de autenticação</caption>
|
||||
<caption xml:lang="ru">Окно входа</caption>
|
||||
<caption xml:lang="sl">Prijavni zaslon</caption>
|
||||
<caption xml:lang="sv">Inloggningsfönster</caption>
|
||||
<caption xml:lang="ta">நுழைவுத் திரை</caption>
|
||||
@@ -357,6 +376,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release version="23.08.5" date="2024-02-15"/>
|
||||
<release version="23.08.4" date="2023-12-07"/>
|
||||
<release version="23.08.3" date="2023-11-09"/>
|
||||
<release version="23.08.2" date="2023-10-12"/>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
Version=1.5
|
||||
Name=NeoChat
|
||||
Name[ar]=نيوتشات
|
||||
Name[ast]=NeoChat
|
||||
Name[az]=NeoChat
|
||||
Name[ca]=NeoChat
|
||||
Name[ca@valencia]=NeoChat
|
||||
|
||||
@@ -1,8 +1 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<style type="text/css" id="current-color-scheme">.ColorScheme-Text{color:#232629}</style>
|
||||
<path class="ColorScheme-Text" fill-rule="evenodd" clip-rule="evenodd" d="M3 3H19V14H8.68787L4 18.1019V14H3V3ZM4 13H5V15.8981L8.31213 13H18V4H4V13Z" fill="currentColor"/>
|
||||
<path class="ColorScheme-Text" fill-rule="evenodd" clip-rule="evenodd" d="M17 15.2929L14.8536 13.1465L14.1465 13.8536L18 17.7071V13.5H17V15.2929Z" fill="currentColor"/>
|
||||
<path class="ColorScheme-Text" d="M5 6H15V7H5V6Z" fill="currentColor"/>
|
||||
<path class="ColorScheme-Text" d="M5 8H13V9H5V8Z" fill="currentColor"/>
|
||||
<path class="ColorScheme-Text" d="M5 10H11V11H5V10Z" fill="currentColor"/>
|
||||
</svg>
|
||||
<svg width="22" height="22" fill="none" version="1.1" id="svg13" xmlns="http://www.w3.org/2000/svg"><style type="text/css" id="current-color-scheme">.ColorScheme-Text{color:#232629}</style><path class="ColorScheme-Text" style="fill:currentColor;fill-opacity:1;stroke:none" fill-rule="evenodd" clip-rule="evenodd" d="M2 4h18v11H6.681L3 18.067V15H2zm1 10h1v1.933L6.319 14H19V5H3z" id="path3"/><path class="ColorScheme-Text" style="fill:currentColor;fill-opacity:1;stroke:none" id="rect5" d="M4 7h9v1H4z"/><path class="ColorScheme-Text" style="fill:currentColor;fill-opacity:1;stroke:none" id="rect7" d="M4 9h7v1H4z"/><path class="ColorScheme-Text" style="fill:currentColor;fill-opacity:1;stroke:none" id="rect9" d="M4 11h5v1H4z"/><path class="ColorScheme-Text" style="fill:currentColor;fill-opacity:1;stroke:none" fill-rule="evenodd" clip-rule="evenodd" d="m16 15.293-1.147-1.146-.707.707 2.853 2.853V14.5h-1z" id="path11"/></svg>
|
||||
|
Before Width: | Height: | Size: 752 B After Width: | Height: | Size: 928 B |
6900
po/ar/neochat.po
6900
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
4563
po/ast/neochat.po
4563
po/ast/neochat.po
File diff suppressed because it is too large
Load Diff
6755
po/az/neochat.po
6755
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -97,7 +97,7 @@ SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
>Vegeu també</title>
|
||||
<simplelist>
|
||||
<member
|
||||
>Una llista de les preguntes més freqüents quant a Matrix <ulink url="https://matrix.org/faq/"
|
||||
>Una llista de les preguntes més freqüents quan a Matrix <ulink url="https://matrix.org/faq/"
|
||||
>https://matrix.org/faq/</ulink
|
||||
> </member>
|
||||
<member
|
||||
|
||||
6245
po/ca/neochat.po
6245
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
6617
po/cs/neochat.po
6617
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
6786
po/da/neochat.po
6786
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
6572
po/de/neochat.po
6572
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
6792
po/el/neochat.po
6792
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
6970
po/en_GB/neochat.po
6970
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
4627
po/eo/neochat.po
4627
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
6381
po/es/neochat.po
6381
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
6439
po/eu/neochat.po
6439
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
6872
po/fi/neochat.po
6872
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
6210
po/fr/neochat.po
6210
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
6713
po/hu/neochat.po
6713
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
6442
po/ia/neochat.po
6442
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
6974
po/id/neochat.po
6974
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
6478
po/ie/neochat.po
6478
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
6386
po/it/neochat.po
6386
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
5395
po/ja/neochat.po
5395
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
6300
po/ka/neochat.po
6300
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
6435
po/ko/neochat.po
6435
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
6402
po/lt/neochat.po
6402
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
6375
po/nl/neochat.po
6375
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
6294
po/nn/neochat.po
6294
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
6400
po/pa/neochat.po
6400
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
6802
po/pl/neochat.po
6802
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
6489
po/pt/neochat.po
6489
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
6760
po/pt_BR/neochat.po
6760
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
7025
po/ru/neochat.po
7025
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
6707
po/sk/neochat.po
6707
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
6778
po/sl/neochat.po
6778
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
6563
po/sv/neochat.po
6563
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
6392
po/ta/neochat.po
6392
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
6711
po/tok/neochat.po
6711
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
6395
po/tr/neochat.po
6395
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
6826
po/uk/neochat.po
6826
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
5743
po/zh_CN/neochat.po
5743
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
6226
po/zh_TW/neochat.po
6226
po/zh_TW/neochat.po
File diff suppressed because it is too large
Load Diff
225
snapcraft.yaml
Normal file
225
snapcraft.yaml
Normal file
@@ -0,0 +1,225 @@
|
||||
|
||||
# SPDX-FileCopyrightText: 2023 Scarlett Moore <sgmoore@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
---
|
||||
name: neochat
|
||||
base: core22
|
||||
adopt-info: neochat
|
||||
grade: stable
|
||||
confinement: strict
|
||||
apps:
|
||||
neochat:
|
||||
# extensions:
|
||||
# - kde-neon
|
||||
command: usr/bin/neochat
|
||||
common-id: org.kde.neochat
|
||||
desktop: usr/share/applications/org.kde.neochat.desktop
|
||||
plugs:
|
||||
- home
|
||||
- removable-media
|
||||
- desktop
|
||||
- desktop-legacy
|
||||
- opengl
|
||||
- wayland
|
||||
- x11
|
||||
- audio-playback
|
||||
- unity7
|
||||
- network
|
||||
- network-bind
|
||||
- network-manager-observe
|
||||
- password-manager-service
|
||||
- accounts-service
|
||||
command-chain:
|
||||
- snap/command-chain/desktop-launch
|
||||
assumes:
|
||||
- snapd2.58.3
|
||||
compression: lzo
|
||||
plugs:
|
||||
desktop:
|
||||
mount-host-font-cache: false
|
||||
icon-themes:
|
||||
interface: content
|
||||
target: $SNAP/data-dir/icons
|
||||
default-provider: gtk-common-themes
|
||||
sound-themes:
|
||||
interface: content
|
||||
target: $SNAP/data-dir/sounds
|
||||
default-provider: gtk-common-themes
|
||||
kf5-5-111-qt-5-15-11-core22:
|
||||
content: kf5-5-111-qt-5-15-11-core22-all
|
||||
interface: content
|
||||
default-provider: kf5-5-111-qt-5-15-11-core22
|
||||
target: $SNAP/kf5
|
||||
environment:
|
||||
SNAP_DESKTOP_RUNTIME: $SNAP/kf5
|
||||
QML2_IMPORT_PATH: "$SNAP/usr/lib/$CRAFT_ARCH_TRIPLET/qt5/qml:$SNAP/kf5/usr/lib/$CRAFT_ARCH_TRIPLET/qt5/qml:$SNAP/usr/lib/$CRAFT_ARCH_TRIPLET/qml"
|
||||
QT_QUICK_CONTROLS_STYLE: org.kde.desktop
|
||||
XDG_DATA_DIRS: "$SNAP/.local"
|
||||
XDG_CONFIG_DIRS: "$SNAP/etc/xdg"
|
||||
hooks:
|
||||
configure:
|
||||
plugs:
|
||||
- desktop
|
||||
command-chain:
|
||||
- snap/command-chain/hooks-configure-desktop
|
||||
layout:
|
||||
/usr/share/X11:
|
||||
symlink: $SNAP/kf5/usr/share/X11
|
||||
slots:
|
||||
session-dbus-interface:
|
||||
interface: dbus
|
||||
name: org.kde.neochat
|
||||
bus: session
|
||||
package-repositories:
|
||||
- type: apt
|
||||
components:
|
||||
- main
|
||||
suites:
|
||||
- jammy
|
||||
key-id: 444DABCF3667D0283F894EDDE6D4736255751E5D
|
||||
url: http://origin.archive.neon.kde.org/user
|
||||
key-server: keyserver.ubuntu.com
|
||||
parts:
|
||||
kde-neon:
|
||||
source: /snap/snapcraft/current/share/snapcraft/extensions/desktop/kde-neon
|
||||
source-type: local
|
||||
plugin: make
|
||||
make-parameters:
|
||||
- PLATFORM_PLUG=kf5-5-111-qt-5-15-11-core22
|
||||
build-snaps:
|
||||
- kf5-5-111-qt-5-15-11-core22-sdk
|
||||
build-environment:
|
||||
- &id001
|
||||
PATH: /snap/kf5-5-111-qt-5-15-11-core22-sdk/current/usr/bin${PATH:+:$PATH}
|
||||
- &id002
|
||||
XDG_DATA_DIRS: $CRAFT_STAGE/usr/share:/snap/kf5-5-111-qt-5-15-11-core22-sdk/current/usr/share:/usr/share${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}
|
||||
- &id003
|
||||
XDG_CONFIG_HOME: $CRAFT_STAGE/etc/xdg:/snap/kf5-5-111-qt-5-15-11-core22-sdk/current/etc/xdg:/etc/xdg${XDG_CONFIG_HOME:+:$XDG_CONFIG_HOME}
|
||||
- &id004
|
||||
CRAFT_CMAKE_ARGS: -DCMAKE_FIND_ROOT_PATH=/snap/kf5-5-111-qt-5-15-11-core22-sdk/current${CRAFT_CMAKE_ARGS:+:$CRAFT_CMAKE_ARGS}
|
||||
qtkeychain:
|
||||
after:
|
||||
- kde-neon
|
||||
source: https://github.com/frankosterfeld/qtkeychain.git
|
||||
source-tag: v0.14.0
|
||||
source-depth: 0
|
||||
plugin: cmake
|
||||
build-packages:
|
||||
- libsecret-1-dev
|
||||
stage-packages:
|
||||
- libsecret-1-0
|
||||
- gnome-keyring
|
||||
cmake-parameters:
|
||||
- "-DKDE_INSTALL_USE_QT_SYS_PATHS=FALSE"
|
||||
- "-DCMAKE_INSTALL_PREFIX=/usr"
|
||||
- "-DCMAKE_BUILD_TYPE=RelWithDebInfo"
|
||||
- "-DENABLE_TESTING=OFF"
|
||||
- "-DBUILD_TESTING=OFF"
|
||||
- "-DKDE_SKIP_TEST_SETTINGS=ON"
|
||||
- "-DCMAKE_FIND_ROOT_PATH=$CRAFT_STAGE\\;/snap/kf5-5-111-qt-5-15-11-core22/current"
|
||||
- "-DBUILD_TRANSLATIONS=OFF"
|
||||
- "-DLIBSECRET_SUPPORT=ON"
|
||||
- "-DKEYCHAIN_DBUS=1"
|
||||
prime:
|
||||
- "-usr/include/qt5keychain"
|
||||
- "-usr/lib/$CRAFT_ARCH_TRIPLET/libqt5keychain.so"
|
||||
- "-usr/mkspecs"
|
||||
- "-usr/lib/$CRAFT_ARCH_TRIPLET/cmake"
|
||||
libmatrixclient:
|
||||
after:
|
||||
- kde-neon
|
||||
- qtkeychain
|
||||
source: https://github.com/quotient-im/libQuotient.git
|
||||
source-tag: 0.8.1.2
|
||||
source-depth: 0
|
||||
plugin: cmake
|
||||
build-packages:
|
||||
- qtmultimedia5-dev
|
||||
- libolm-dev
|
||||
- libssl-dev
|
||||
cmake-parameters:
|
||||
- "-DCMAKE_INSTALL_PREFIX=/usr"
|
||||
- "-DCMAKE_BUILD_TYPE=RelWithDebInfo"
|
||||
- "-DENABLE_TESTING=OFF"
|
||||
- "-DBUILD_TESTING=OFF"
|
||||
- "-DKDE_SKIP_TEST_SETTINGS=ON"
|
||||
- "-DQuotient_ENABLE_E2EE=ON"
|
||||
- "-DBUILDING_SHARED_QUOTIENT=ON"
|
||||
- "-DCMAKE_FIND_ROOT_PATH=$CRAFT_STAGE\\;/snap/kf5-5-111-qt-5-15-11-core22/current"
|
||||
neochat:
|
||||
after:
|
||||
- kde-neon
|
||||
- qtkeychain
|
||||
- libmatrixclient
|
||||
parse-info:
|
||||
- usr/share/metainfo/org.kde.neochat.appdata.xml
|
||||
source: .
|
||||
source-type: local
|
||||
plugin: cmake
|
||||
build-packages:
|
||||
- cmark
|
||||
- gcc-10
|
||||
- g++-10
|
||||
- libcmark-dev
|
||||
- kquickimageeditor-dev
|
||||
- libkf5qqc2desktopstyle-dev
|
||||
- qcoro-qt5-dev
|
||||
- kirigami-addons-dev
|
||||
- reuse
|
||||
- libkf5doctools-dev
|
||||
- qtbase5-private-dev
|
||||
- qml-module-org-kde-kquickimageeditor
|
||||
- qml-module-org-kde-qqc2desktopstyle
|
||||
- kirigami-addons-data
|
||||
- qml-module-org-kde-kirigamiaddons
|
||||
- qml-module-org-kde-quickcharts
|
||||
- libsqlite3-dev
|
||||
- libolm-dev
|
||||
stage-packages:
|
||||
- libcmark0.30.2
|
||||
- qml-module-org-kde-kquickimageeditor
|
||||
- qml-module-org-kde-qqc2desktopstyle
|
||||
- kirigami-addons-data
|
||||
- qml-module-org-kde-kirigamiaddons
|
||||
- qml-module-org-kde-quickcharts
|
||||
- qml-module-org-kde-kirigami2
|
||||
- reuse
|
||||
- libsqlite3-0
|
||||
- libolm3
|
||||
cmake-parameters:
|
||||
- "-DKDE_INSTALL_USE_QT_SYS_PATHS=FALSE"
|
||||
- "-DCMAKE_INSTALL_PREFIX=/usr"
|
||||
- "-DCMAKE_BUILD_TYPE=Release"
|
||||
- "-DENABLE_TESTING=OFF"
|
||||
- "-DBUILD_TESTING=OFF"
|
||||
- "-DKDE_SKIP_TEST_SETTINGS=ON"
|
||||
- "-DNEOCHAT_FLATPAK=ON"
|
||||
- "-DCMAKE_FIND_ROOT_PATH=$CRAFT_STAGE\\;/snap/kf5-5-111-qt-5-15-11-core22-sdk/current"
|
||||
- "-DKDE_INSTALL_PLUGINDIR=/usr/lib/$CRAFT_ARCH_TRIPLET/qt5/plugins/"
|
||||
prime:
|
||||
- "-usr/lib/*/cmake/*"
|
||||
- "-usr/include/*"
|
||||
- "-usr/share/ECM/*"
|
||||
- "-usr/share/doc/*"
|
||||
- "-usr/share/man/*"
|
||||
- "-usr/share/icons/breeze-dark*"
|
||||
- "-usr/bin/X11"
|
||||
- "-usr/lib/gcc/$CRAFT_ARCH_TRIPLET/6.0.0"
|
||||
- "-usr/lib/aspell/*"
|
||||
- "-usr/share/lintian"
|
||||
build-environment: &id005
|
||||
- *id001
|
||||
- *id002
|
||||
- *id003
|
||||
- *id004
|
||||
cleanup:
|
||||
after:
|
||||
- kde-neon
|
||||
- neochat
|
||||
plugin: nil
|
||||
override-prime: |
|
||||
set -eux
|
||||
# find $CRAFT_PRIME/usr/share/doc/ -type f -not -name 'copyright' -delete
|
||||
# find $CRAFT_PRIME/usr/share -type d -empty -delete | /bin/true
|
||||
# # Unused libraries found by linter
|
||||
@@ -3,6 +3,16 @@
|
||||
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
configure_file(qml/Page/RoomList/RoomDelegate.qml ${CMAKE_CURRENT_BINARY_DIR}/qml/Page/RoomList/RoomDelegate.qml)
|
||||
configure_file(qml/Component/QuickSwitcher.qml ${CMAKE_CURRENT_BINARY_DIR}/qml/Component/QuickSwitcher.qml)
|
||||
configure_file(qml/Dialog/UserDetailDialog.qml ${CMAKE_CURRENT_BINARY_DIR}/qml/Dialog/UserDetailDialog.qml)
|
||||
configure_file(qml/Dialog/PowerLevelDialog.qml ${CMAKE_CURRENT_BINARY_DIR}/qml/Dialog/PowerLevelDialog.qml)
|
||||
configure_file(qml/Component/Timeline/AudioDelegate.qml ${CMAKE_CURRENT_BINARY_DIR}/qml/Component/Timeline/AudioDelegate.qml)
|
||||
configure_file(qml/Component/Timeline/VideoDelegate.qml ${CMAKE_CURRENT_BINARY_DIR}/qml/Component/Timeline/VideoDelegate.qml)
|
||||
configure_file(qml/Component/Timeline/OsmLocationPlugin.qml ${CMAKE_CURRENT_BINARY_DIR}/qml/Component/Timeline/OsmLocationPlugin.qml)
|
||||
|
||||
configure_file(res.qrc ${CMAKE_CURRENT_SOURCE_DIR}/res.generated.qrc)
|
||||
|
||||
add_library(neochat STATIC
|
||||
controller.cpp
|
||||
controller.h
|
||||
@@ -34,18 +44,14 @@ add_library(neochat STATIC
|
||||
roommanager.h
|
||||
neochatroom.cpp
|
||||
neochatroom.h
|
||||
neochatuser.cpp
|
||||
neochatuser.h
|
||||
models/userlistmodel.cpp
|
||||
models/userlistmodel.h
|
||||
models/userfiltermodel.cpp
|
||||
models/userfiltermodel.h
|
||||
models/publicroomlistmodel.cpp
|
||||
models/publicroomlistmodel.h
|
||||
models/spacechildrenmodel.cpp
|
||||
models/spacechildrenmodel.h
|
||||
models/spacechildsortfiltermodel.cpp
|
||||
models/spacechildsortfiltermodel.h
|
||||
models/spacetreeitem.cpp
|
||||
models/spacetreeitem.h
|
||||
models/userdirectorylistmodel.cpp
|
||||
models/userdirectorylistmodel.h
|
||||
models/pushrulemodel.cpp
|
||||
@@ -61,8 +67,8 @@ add_library(neochat STATIC
|
||||
models/devicesmodel.cpp
|
||||
models/devicesmodel.h
|
||||
models/devicesproxymodel.cpp
|
||||
filetype.cpp
|
||||
filetype.h
|
||||
filetypesingleton.cpp
|
||||
filetypesingleton.h
|
||||
login.cpp
|
||||
login.h
|
||||
models/webshortcutmodel.cpp
|
||||
@@ -71,6 +77,8 @@ add_library(neochat STATIC
|
||||
blurhash.h
|
||||
blurhashimageprovider.cpp
|
||||
blurhashimageprovider.h
|
||||
models/collapsestateproxymodel.cpp
|
||||
models/collapsestateproxymodel.h
|
||||
models/mediamessagefiltermodel.cpp
|
||||
models/mediamessagefiltermodel.h
|
||||
urlhelper.cpp
|
||||
@@ -119,194 +127,8 @@ add_library(neochat STATIC
|
||||
locationhelper.h
|
||||
events/pollevent.cpp
|
||||
pollhandler.cpp
|
||||
utils.h
|
||||
registration.cpp
|
||||
neochatconnection.cpp
|
||||
neochatconnection.h
|
||||
jobs/neochatdeactivateaccountjob.cpp
|
||||
jobs/neochatdeactivateaccountjob.h
|
||||
jobs/neochatdeletedevicejob.cpp
|
||||
jobs/neochatdeletedevicejob.h
|
||||
jobs/neochatchangepasswordjob.cpp
|
||||
jobs/neochatchangepasswordjob.h
|
||||
mediasizehelper.cpp
|
||||
mediasizehelper.h
|
||||
eventhandler.cpp
|
||||
enums/delegatetype.h
|
||||
roomlastmessageprovider.cpp
|
||||
roomlastmessageprovider.h
|
||||
chatbarcache.cpp
|
||||
chatbarcache.h
|
||||
colorschemer.cpp
|
||||
colorschemer.h
|
||||
models/notificationsmodel.cpp
|
||||
models/notificationsmodel.h
|
||||
models/timelinemodel.cpp
|
||||
models/timelinemodel.h
|
||||
enums/pushrule.h
|
||||
)
|
||||
|
||||
qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
QML_FILES
|
||||
qml/main.qml
|
||||
qml/AccountMenu.qml
|
||||
qml/ExploreComponent.qml
|
||||
qml/ExploreComponentMobile.qml
|
||||
qml/ContextMenu.qml
|
||||
qml/CollapsedRoomDelegate.qml
|
||||
qml/RoomDelegate.qml
|
||||
qml/RoomListPage.qml
|
||||
qml/SpaceListContextMenu.qml
|
||||
qml/UserInfo.qml
|
||||
qml/UserInfoDesktop.qml
|
||||
qml/RoomPage.qml
|
||||
qml/RoomWindow.qml
|
||||
qml/JoinRoomPage.qml
|
||||
qml/ManualRoomDialog.qml
|
||||
qml/ExplorerDelegate.qml
|
||||
qml/InviteUserPage.qml
|
||||
qml/StartChatPage.qml
|
||||
qml/ImageEditorPage.qml
|
||||
qml/WelcomePage.qml
|
||||
qml/General.qml
|
||||
qml/RoomSecurity.qml
|
||||
qml/PushNotification.qml
|
||||
qml/Categories.qml
|
||||
qml/Permissions.qml
|
||||
qml/NeochatMaximizeComponent.qml
|
||||
qml/FancyEffectsContainer.qml
|
||||
qml/TypingPane.qml
|
||||
qml/QuickSwitcher.qml
|
||||
qml/HoverActions.qml
|
||||
qml/ChatBar.qml
|
||||
qml/AttachmentPane.qml
|
||||
qml/ReplyPane.qml
|
||||
qml/CompletionMenu.qml
|
||||
qml/PieProgressBar.qml
|
||||
qml/QuickFormatBar.qml
|
||||
qml/RoomData.qml
|
||||
qml/ServerData.qml
|
||||
qml/EmojiPicker.qml
|
||||
qml/TimelineDelegate.qml
|
||||
qml/ReplyComponent.qml
|
||||
qml/StateDelegate.qml
|
||||
qml/RichLabel.qml
|
||||
qml/MessageDelegate.qml
|
||||
qml/Bubble.qml
|
||||
qml/SectionDelegate.qml
|
||||
qml/VideoDelegate.qml
|
||||
qml/ReactionDelegate.qml
|
||||
qml/LinkPreviewDelegate.qml
|
||||
qml/AudioDelegate.qml
|
||||
qml/FileDelegate.qml
|
||||
qml/ImageDelegate.qml
|
||||
qml/EncryptedDelegate.qml
|
||||
qml/EventDelegate.qml
|
||||
qml/TextDelegate.qml
|
||||
qml/ReadMarkerDelegate.qml
|
||||
qml/PollDelegate.qml
|
||||
qml/MimeComponent.qml
|
||||
qml/StateComponent.qml
|
||||
qml/MessageEditComponent.qml
|
||||
qml/AvatarFlow.qml
|
||||
qml/LoginStep.qml
|
||||
qml/Login.qml
|
||||
qml/Homeserver.qml
|
||||
qml/Username.qml
|
||||
qml/RegisterPassword.qml
|
||||
qml/Captcha.qml
|
||||
qml/Terms.qml
|
||||
qml/Email.qml
|
||||
qml/Password.qml
|
||||
qml/LoginRegister.qml
|
||||
qml/Loading.qml
|
||||
qml/LoginMethod.qml
|
||||
qml/Sso.qml
|
||||
qml/UserDetailDialog.qml
|
||||
qml/CreateRoomDialog.qml
|
||||
qml/EmojiDialog.qml
|
||||
qml/OpenFileDialog.qml
|
||||
qml/KeyVerificationDialog.qml
|
||||
qml/ConfirmLogoutDialog.qml
|
||||
qml/PowerLevelDialog.qml
|
||||
qml/Message.qml
|
||||
qml/EmojiItem.qml
|
||||
qml/EmojiRow.qml
|
||||
qml/EmojiSas.qml
|
||||
qml/ConfirmDeactivateAccountDialog.qml
|
||||
qml/VerificationCanceled.qml
|
||||
qml/GlobalMenu.qml
|
||||
qml/EditMenu.qml
|
||||
qml/MessageDelegateContextMenu.qml
|
||||
qml/FileDelegateContextMenu.qml
|
||||
qml/MessageSourceSheet.qml
|
||||
qml/ReportSheet.qml
|
||||
qml/SettingsPage.qml
|
||||
qml/ThemeRadioButton.qml
|
||||
qml/ColorScheme.qml
|
||||
qml/GeneralSettingsPage.qml
|
||||
qml/EmoticonsPage.qml
|
||||
qml/EmoticonEditorPage.qml
|
||||
qml/EmoticonFormCard.qml
|
||||
qml/GlobalNotificationsPage.qml
|
||||
qml/NotificationRuleItem.qml
|
||||
qml/AppearanceSettingsPage.qml
|
||||
qml/AccountsPage.qml
|
||||
qml/AccountEditorPage.qml
|
||||
qml/DevicesPage.qml
|
||||
qml/DeviceDelegate.qml
|
||||
qml/DevicesCard.qml
|
||||
qml/About.qml
|
||||
qml/AboutKDE.qml
|
||||
qml/SonnetConfigPage.qml
|
||||
qml/NetworkProxyPage.qml
|
||||
qml/DevtoolsPage.qml
|
||||
qml/ConfirmEncryptionDialog.qml
|
||||
qml/RemoveSheet.qml
|
||||
qml/BanSheet.qml
|
||||
qml/EmojiTonesPicker.qml
|
||||
qml/EmojiDelegate.qml
|
||||
qml/EmojiGrid.qml
|
||||
qml/SearchPage.qml
|
||||
qml/LocationDelegate.qml
|
||||
qml/LocationChooser.qml
|
||||
qml/TimelineView.qml
|
||||
qml/InvitationView.qml
|
||||
qml/AvatarTabButton.qml
|
||||
qml/SpaceDrawer.qml
|
||||
qml/OsmLocationPlugin.qml
|
||||
qml/LiveLocationDelegate.qml
|
||||
qml/FullScreenMap.qml
|
||||
qml/LocationsPage.qml
|
||||
qml/LocationMapItem.qml
|
||||
qml/RoomDrawer.qml
|
||||
qml/RoomDrawerPage.qml
|
||||
qml/DirectChatDrawerHeader.qml
|
||||
qml/GroupChatDrawerHeader.qml
|
||||
qml/RoomInformation.qml
|
||||
qml/RoomMedia.qml
|
||||
qml/ChooseRoomDialog.qml
|
||||
qml/ShareAction.qml
|
||||
qml/SpaceHomePage.qml
|
||||
qml/SpaceHierarchyDelegate.qml
|
||||
qml/RemoveChildDialog.qml
|
||||
qml/SelectParentDialog.qml
|
||||
qml/Security.qml
|
||||
qml/QrCodeMaximizeComponent.qml
|
||||
qml/SelectSpacesDialog.qml
|
||||
qml/AttachDialog.qml
|
||||
qml/NotificationsView.qml
|
||||
qml/LoadingDelegate.qml
|
||||
qml/TimelineEndDelegate.qml
|
||||
RESOURCES
|
||||
qml/confetti.png
|
||||
qml/glowdot.png
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(neochat PROPERTIES OUTPUT_NAME "neochatlib")
|
||||
endif()
|
||||
|
||||
ecm_qt_declare_logging_category(neochat
|
||||
HEADER "messageeventmodel_logging.h"
|
||||
IDENTIFIER "MessageEvent"
|
||||
@@ -316,29 +138,11 @@ ecm_qt_declare_logging_category(neochat
|
||||
EXPORT NEOCHAT
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(neochat
|
||||
HEADER "eventhandler_logging.h"
|
||||
IDENTIFIER "EventHandling"
|
||||
CATEGORY_NAME "org.kde.neochat.eventhandler"
|
||||
DEFAULT_SEVERITY Info
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(neochat
|
||||
HEADER "chatdocumenthandler_logging.h"
|
||||
IDENTIFIER "ChatDocumentHandling"
|
||||
CATEGORY_NAME "org.kde.neochat.chatdocumenthandler"
|
||||
DEFAULT_SEVERITY Info
|
||||
)
|
||||
|
||||
add_executable(neochat-app
|
||||
main.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/res.generated.qrc
|
||||
)
|
||||
|
||||
if(TARGET Qt::WebView)
|
||||
target_link_libraries(neochat-app PUBLIC Qt::WebView)
|
||||
target_compile_definitions(neochat-app PUBLIC -DHAVE_WEBVIEW)
|
||||
endif()
|
||||
|
||||
target_include_directories(neochat-app PRIVATE ${CMAKE_BINARY_DIR})
|
||||
|
||||
target_link_libraries(neochat-app PRIVATE
|
||||
@@ -350,47 +154,28 @@ ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
|
||||
target_sources(neochat-app PRIVATE ${NEOCHAT_ICON})
|
||||
|
||||
if(NOT ANDROID)
|
||||
target_sources(neochat PRIVATE colorschemer.cpp colorschemer.h)
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
target_sources(neochat PRIVATE trayicon_sni.cpp trayicon_sni.h)
|
||||
target_link_libraries(neochat PRIVATE KF6::StatusNotifierItem)
|
||||
else()
|
||||
target_sources(neochat PRIVATE trayicon.cpp trayicon.h)
|
||||
endif()
|
||||
target_link_libraries(neochat PUBLIC KF6::WindowSystem ICU::uc)
|
||||
target_link_libraries(neochat PUBLIC KF${QT_MAJOR_VERSION}::ConfigWidgets KF${QT_MAJOR_VERSION}::WindowSystem)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_COLORSCHEME)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_WINDOWSYSTEM)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_ICU)
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
target_sources(neochat-app PRIVATE res_desktop.qrc)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_RUNNER)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_X11)
|
||||
target_sources(neochat PRIVATE runner.cpp)
|
||||
else()
|
||||
target_sources(neochat-app PRIVATE res_android.qrc)
|
||||
endif()
|
||||
|
||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
|
||||
target_link_libraries(neochat PUBLIC
|
||||
Qt::Core
|
||||
Qt::Quick
|
||||
Qt::Qml
|
||||
Qt::Gui
|
||||
Qt::Multimedia
|
||||
Qt::Network
|
||||
Qt::QuickControls2
|
||||
KF6::I18n
|
||||
KF6::Kirigami
|
||||
KF6::Notifications
|
||||
KF6::ConfigCore
|
||||
KF6::ConfigGui
|
||||
KF6::CoreAddons
|
||||
KF6::SonnetCore
|
||||
KF6::ColorScheme
|
||||
KF6::ItemModels
|
||||
QuotientQt6
|
||||
cmark::cmark
|
||||
QCoro::Core
|
||||
QCoro::Network
|
||||
)
|
||||
|
||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR})
|
||||
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF${QT_MAJOR_VERSION}::I18n KF${QT_MAJOR_VERSION}::Kirigami2 KF${QT_MAJOR_VERSION}::Notifications KF${QT_MAJOR_VERSION}::ConfigCore KF${QT_MAJOR_VERSION}::ConfigGui KF${QT_MAJOR_VERSION}::CoreAddons KF${QT_MAJOR_VERSION}::SonnetCore KF${QT_MAJOR_VERSION}::ItemModels Quotient${QUOTIENT_SUFFIX} cmark::cmark ${QTKEYCHAIN_LIBRARIES} QCoro::Core)
|
||||
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||
|
||||
if(NEOCHAT_FLATPAK)
|
||||
@@ -475,25 +260,9 @@ if(ANDROID)
|
||||
"preferences-desktop-notification"
|
||||
"computer-symbolic"
|
||||
"gps"
|
||||
"system-users-symbolic"
|
||||
"map-flat"
|
||||
"documentinfo"
|
||||
"view-list-details"
|
||||
"go-previous"
|
||||
"mail-forward-symbolic"
|
||||
"dialog-warning-symbolic"
|
||||
"object-rotate-left"
|
||||
"object-rotate-right"
|
||||
"add-subtitle"
|
||||
"security-low"
|
||||
"security-low-symbolic"
|
||||
"kde"
|
||||
"list-remove-symbolic"
|
||||
"edit-delete"
|
||||
)
|
||||
ecm_add_android_apk(neochat-app ANDROID_DIR ${CMAKE_SOURCE_DIR}/android)
|
||||
else()
|
||||
target_link_libraries(neochat PUBLIC Qt::Widgets KF6::KIOWidgets)
|
||||
target_link_libraries(neochat PUBLIC Qt::Widgets KF${QT_MAJOR_VERSION}::KIOWidgets)
|
||||
install(FILES neochat.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
|
||||
endif()
|
||||
|
||||
@@ -501,27 +270,18 @@ if(NOT ANDROID)
|
||||
set_target_properties(neochat-app PROPERTIES OUTPUT_NAME "neochat")
|
||||
endif()
|
||||
|
||||
if(TARGET KF6::DBusAddons)
|
||||
target_link_libraries(neochat PUBLIC KF6::DBusAddons)
|
||||
if(TARGET KF${QT_MAJOR_VERSION}::DBusAddons)
|
||||
target_link_libraries(neochat PUBLIC KF${QT_MAJOR_VERSION}::DBusAddons)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_KDBUSADDONS)
|
||||
endif()
|
||||
|
||||
if (TARGET KF6::KIOWidgets)
|
||||
if (TARGET KF${QT_MAJOR_VERSION}::KIOWidgets)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_KIO)
|
||||
endif()
|
||||
|
||||
if (TARGET KUnifiedPush)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_KUNIFIEDPUSH)
|
||||
target_link_libraries(neochat PUBLIC KUnifiedPush)
|
||||
|
||||
if (NOT ANDROID)
|
||||
configure_file(org.kde.neochat.service.in ${CMAKE_CURRENT_BINARY_DIR}/org.kde.neochat.service)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.neochat.service DESTINATION ${KDE_INSTALL_DBUSSERVICEDIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(TARGETS neochat-app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <QStringBuilder>
|
||||
|
||||
#include "models/actionsmodel.h"
|
||||
#include "models/customemojimodel.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "texthandler.h"
|
||||
|
||||
@@ -37,30 +38,45 @@ void ActionsHandler::setRoom(NeoChatRoom *room)
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
void ActionsHandler::handleMessageEvent(ChatBarCache *chatBarCache)
|
||||
void ActionsHandler::handleNewMessage()
|
||||
{
|
||||
if (!m_room || !chatBarCache) {
|
||||
qWarning() << "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.";
|
||||
return;
|
||||
}
|
||||
|
||||
checkEffects(chatBarCache->text());
|
||||
if (!chatBarCache->attachmentPath().isEmpty()) {
|
||||
QUrl url(chatBarCache->attachmentPath());
|
||||
checkEffects(m_room->chatBoxText());
|
||||
if (!m_room->chatBoxAttachmentPath().isEmpty()) {
|
||||
QUrl url(m_room->chatBoxAttachmentPath());
|
||||
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
|
||||
m_room->uploadFile(QUrl(path), chatBarCache->text().isEmpty() ? path.mid(path.lastIndexOf(u'/') + 1) : chatBarCache->text());
|
||||
chatBarCache->setAttachmentPath({});
|
||||
chatBarCache->setText({});
|
||||
m_room->uploadFile(path, m_room->chatBoxText().isEmpty() ? path.mid(path.lastIndexOf('/') + 1) : m_room->chatBoxText());
|
||||
m_room->setChatBoxAttachmentPath({});
|
||||
m_room->setChatBoxText({});
|
||||
return;
|
||||
}
|
||||
|
||||
QString handledText = chatBarCache->text();
|
||||
handledText = handleMentions(handledText, chatBarCache->mentions());
|
||||
handleMessage(m_room->mainCache()->text(), handledText, chatBarCache);
|
||||
QString handledText = m_room->chatBoxText();
|
||||
handledText = handleMentions(handledText);
|
||||
handleMessage(m_room->chatBoxText(), handledText);
|
||||
}
|
||||
|
||||
QString ActionsHandler::handleMentions(QString handledText, QList<Mention> *mentions)
|
||||
void ActionsHandler::handleEdit()
|
||||
{
|
||||
checkEffects(m_room->editText());
|
||||
|
||||
QString handledText = m_room->editText();
|
||||
handledText = handleMentions(handledText, true);
|
||||
handleMessage(m_room->editText(), handledText, true);
|
||||
}
|
||||
|
||||
QString ActionsHandler::handleMentions(QString handledText, const bool &isEdit)
|
||||
{
|
||||
if (!m_room) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
QVector<Mention> *mentions;
|
||||
if (isEdit) {
|
||||
mentions = m_room->editMentions();
|
||||
} else {
|
||||
mentions = m_room->mentions();
|
||||
}
|
||||
|
||||
std::sort(mentions->begin(), mentions->end(), [](const auto &a, const auto &b) -> bool {
|
||||
return a.cursor.anchor() > b.cursor.anchor();
|
||||
});
|
||||
@@ -71,17 +87,17 @@ QString ActionsHandler::handleMentions(QString handledText, QList<Mention> *ment
|
||||
}
|
||||
handledText = handledText.replace(mention.cursor.anchor(),
|
||||
mention.cursor.position() - mention.cursor.anchor(),
|
||||
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text.toHtmlEscaped(), mention.id));
|
||||
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text, mention.id));
|
||||
}
|
||||
mentions->clear();
|
||||
|
||||
return handledText;
|
||||
}
|
||||
|
||||
void ActionsHandler::handleMessage(const QString &text, QString handledText, ChatBarCache *chatBarCache)
|
||||
void ActionsHandler::handleMessage(const QString &text, QString handledText, const bool &isEdit)
|
||||
{
|
||||
if (NeoChatConfig::allowQuickEdit()) {
|
||||
QRegularExpression sed(QStringLiteral("^s/([^/]*)/([^/]*)(/g)?$"));
|
||||
QRegularExpression sed("^s/([^/]*)/([^/]*)(/g)?$");
|
||||
auto match = sed.match(text);
|
||||
if (match.hasMatch()) {
|
||||
const QString regex = match.captured(1);
|
||||
@@ -97,13 +113,13 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, Cha
|
||||
} else {
|
||||
originalString = event->plainBody();
|
||||
}
|
||||
if (flags == "/g"_ls) {
|
||||
m_room->postHtmlMessage(handledText, originalString.replace(regex, replacement), event->msgtype(), {}, event->id());
|
||||
if (flags == "/g") {
|
||||
m_room->postHtmlMessage(handledText, originalString.replace(regex, replacement), event->msgtype(), "", event->id());
|
||||
} else {
|
||||
m_room->postHtmlMessage(handledText,
|
||||
originalString.replace(originalString.indexOf(regex), regex.size(), replacement),
|
||||
event->msgtype(),
|
||||
{},
|
||||
"",
|
||||
event->id());
|
||||
}
|
||||
return;
|
||||
@@ -117,8 +133,8 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, Cha
|
||||
if (handledText.startsWith(QLatin1Char('/'))) {
|
||||
for (const auto &action : ActionsModel::instance().allActions()) {
|
||||
if (handledText.indexOf(action.prefix) == 1
|
||||
&& (handledText.indexOf(" "_ls) == action.prefix.length() + 1 || handledText.length() == action.prefix.length() + 1)) {
|
||||
handledText = action.handle(handledText.mid(action.prefix.length() + 1).trimmed(), m_room, chatBarCache);
|
||||
&& (handledText.indexOf(" ") == action.prefix.length() + 1 || handledText.length() == action.prefix.length() + 1)) {
|
||||
handledText = action.handle(handledText.mid(action.prefix.length() + 1).trimmed(), m_room);
|
||||
if (action.messageType.has_value()) {
|
||||
messageType = *action.messageType;
|
||||
}
|
||||
@@ -131,34 +147,35 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, Cha
|
||||
}
|
||||
}
|
||||
|
||||
handledText = CustomEmojiModel::instance().preprocessText(handledText);
|
||||
TextHandler textHandler;
|
||||
textHandler.setData(handledText);
|
||||
handledText = textHandler.handleSendText();
|
||||
|
||||
if (handledText.count("<p>"_ls) == 1 && handledText.count("</p>"_ls) == 1) {
|
||||
handledText.remove("<p>"_ls);
|
||||
handledText.remove("</p>"_ls);
|
||||
if (handledText.count("<p>") == 1 && handledText.count("</p>") == 1) {
|
||||
handledText.remove("<p>");
|
||||
handledText.remove("</p>");
|
||||
}
|
||||
|
||||
if (handledText.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_room->postMessage(text, handledText, messageType, chatBarCache->replyId(), chatBarCache->editId(), chatBarCache->threadId());
|
||||
m_room->postMessage(text, handledText, messageType, m_room->chatBoxReplyId(), isEdit ? m_room->chatBoxEditId() : "");
|
||||
}
|
||||
|
||||
void ActionsHandler::checkEffects(const QString &text)
|
||||
{
|
||||
std::optional<QString> effect = std::nullopt;
|
||||
if (text.contains(QStringLiteral("\u2744"))) {
|
||||
if (text.contains("\u2744")) {
|
||||
effect = QLatin1String("snowflake");
|
||||
} else if (text.contains(QStringLiteral("\u1F386"))) {
|
||||
} else if (text.contains("\u1F386")) {
|
||||
effect = QLatin1String("fireworks");
|
||||
} else if (text.contains(QStringLiteral("\u2F387"))) {
|
||||
} else if (text.contains("\u2F387")) {
|
||||
effect = QLatin1String("fireworks");
|
||||
} else if (text.contains(QStringLiteral("\u1F389"))) {
|
||||
} else if (text.contains("\u1F389")) {
|
||||
effect = QLatin1String("confetti");
|
||||
} else if (text.contains(QStringLiteral("\u1F38A"))) {
|
||||
} else if (text.contains("\u1F38A")) {
|
||||
effect = QLatin1String("confetti");
|
||||
}
|
||||
if (effect.has_value()) {
|
||||
|
||||
@@ -4,13 +4,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
class CustomEmojiModel;
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
@@ -34,7 +33,6 @@ class NeoChatRoom;
|
||||
class ActionsHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
/**
|
||||
* @brief The room that messages will be sent to.
|
||||
@@ -52,15 +50,21 @@ Q_SIGNALS:
|
||||
void showEffect(const QString &effect);
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
/**
|
||||
* @brief Pre-process text and send message event.
|
||||
* @brief Pre-process text and send message.
|
||||
*/
|
||||
void handleMessageEvent(ChatBarCache *chatBarCache);
|
||||
void handleNewMessage();
|
||||
|
||||
/**
|
||||
* @brief Pre-process text and send edit.
|
||||
*/
|
||||
void handleEdit();
|
||||
|
||||
private:
|
||||
NeoChatRoom *m_room = nullptr;
|
||||
void checkEffects(const QString &text);
|
||||
|
||||
QString handleMentions(QString handledText, QList<Mention> *mentions);
|
||||
void handleMessage(const QString &text, QString handledText, ChatBarCache *chatBarCache);
|
||||
QString handleMentions(QString handledText, const bool &isEdit = false);
|
||||
void handleMessage(const QString &text, QString handledText, const bool &isEdit = false);
|
||||
};
|
||||
|
||||
@@ -177,7 +177,7 @@ uint8_t *decode(const char *blurhash, int width, int height, int punch, int nCha
|
||||
uint8_t *pixelArray = createByteArray(bytesPerRow * height);
|
||||
|
||||
if (decodeToArray(blurhash, width, height, punch, nChannels, pixelArray) == -1) {
|
||||
return nullptr;
|
||||
return NULL;
|
||||
}
|
||||
return pixelArray;
|
||||
}
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "chatbarcache.h"
|
||||
|
||||
#include "eventhandler.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
ChatBarCache::ChatBarCache(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QString ChatBarCache::text() const
|
||||
{
|
||||
return m_text;
|
||||
}
|
||||
|
||||
void ChatBarCache::setText(const QString &text)
|
||||
{
|
||||
if (text == m_text) {
|
||||
return;
|
||||
}
|
||||
m_text = text;
|
||||
Q_EMIT textChanged();
|
||||
}
|
||||
|
||||
bool ChatBarCache::isReplying() const
|
||||
{
|
||||
return m_relationType == Reply && !m_relationId.isEmpty();
|
||||
}
|
||||
|
||||
QString ChatBarCache::replyId() const
|
||||
{
|
||||
if (m_relationType != Reply) {
|
||||
return {};
|
||||
}
|
||||
return m_relationId;
|
||||
}
|
||||
|
||||
void ChatBarCache::setReplyId(const QString &replyId)
|
||||
{
|
||||
if (m_relationType == Reply && m_relationId == replyId) {
|
||||
return;
|
||||
}
|
||||
m_relationId = replyId;
|
||||
if (m_relationId.isEmpty()) {
|
||||
m_relationType = None;
|
||||
} else {
|
||||
m_relationType = Reply;
|
||||
}
|
||||
m_attachmentPath = QString();
|
||||
Q_EMIT relationIdChanged();
|
||||
Q_EMIT attachmentPathChanged();
|
||||
}
|
||||
|
||||
bool ChatBarCache::isEditing() const
|
||||
{
|
||||
return m_relationType == Edit && !m_relationId.isEmpty();
|
||||
}
|
||||
|
||||
QString ChatBarCache::editId() const
|
||||
{
|
||||
if (m_relationType != Edit) {
|
||||
return {};
|
||||
}
|
||||
return m_relationId;
|
||||
}
|
||||
|
||||
void ChatBarCache::setEditId(const QString &editId)
|
||||
{
|
||||
if (m_relationType == Edit && m_relationId == editId) {
|
||||
return;
|
||||
}
|
||||
m_relationId = editId;
|
||||
if (m_relationId.isEmpty()) {
|
||||
m_relationType = None;
|
||||
} else {
|
||||
m_relationType = Edit;
|
||||
}
|
||||
m_attachmentPath = QString();
|
||||
Q_EMIT relationIdChanged();
|
||||
Q_EMIT attachmentPathChanged();
|
||||
}
|
||||
|
||||
QVariantMap ChatBarCache::relationUser() const
|
||||
{
|
||||
if (parent() == nullptr) {
|
||||
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return {};
|
||||
}
|
||||
auto room = dynamic_cast<NeoChatRoom *>(parent());
|
||||
if (room == nullptr) {
|
||||
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return {};
|
||||
}
|
||||
if (m_relationId.isEmpty()) {
|
||||
return room->getUser(nullptr);
|
||||
}
|
||||
return room->getUser(room->user((*room->findInTimeline(m_relationId))->senderId()));
|
||||
}
|
||||
|
||||
QString ChatBarCache::relationMessage() const
|
||||
{
|
||||
if (parent() == nullptr) {
|
||||
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return {};
|
||||
}
|
||||
if (m_relationId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
auto room = dynamic_cast<NeoChatRoom *>(parent());
|
||||
if (room == nullptr) {
|
||||
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return {};
|
||||
}
|
||||
EventHandler eventhandler;
|
||||
eventhandler.setRoom(room);
|
||||
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
|
||||
eventhandler.setEvent(&**event);
|
||||
return eventhandler.getPlainBody();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool ChatBarCache::isThreaded() const
|
||||
{
|
||||
return !m_threadId.isEmpty();
|
||||
}
|
||||
|
||||
QString ChatBarCache::threadId() const
|
||||
{
|
||||
return m_threadId;
|
||||
}
|
||||
|
||||
void ChatBarCache::setThreadId(const QString &threadId)
|
||||
{
|
||||
if (m_threadId == threadId) {
|
||||
return;
|
||||
}
|
||||
m_threadId = threadId;
|
||||
Q_EMIT threadIdChanged();
|
||||
}
|
||||
|
||||
QString ChatBarCache::attachmentPath() const
|
||||
{
|
||||
return m_attachmentPath;
|
||||
}
|
||||
|
||||
void ChatBarCache::setAttachmentPath(const QString &attachmentPath)
|
||||
{
|
||||
if (attachmentPath == m_attachmentPath) {
|
||||
return;
|
||||
}
|
||||
m_attachmentPath = attachmentPath;
|
||||
m_relationType = None;
|
||||
m_relationId = QString();
|
||||
Q_EMIT attachmentPathChanged();
|
||||
Q_EMIT relationIdChanged();
|
||||
}
|
||||
|
||||
QList<Mention> *ChatBarCache::mentions()
|
||||
{
|
||||
return &m_mentions;
|
||||
}
|
||||
|
||||
QString ChatBarCache::savedText() const
|
||||
{
|
||||
return m_savedText;
|
||||
}
|
||||
|
||||
void ChatBarCache::setSavedText(const QString &savedText)
|
||||
{
|
||||
m_savedText = savedText;
|
||||
}
|
||||
|
||||
#include "moc_chatbarcache.cpp"
|
||||
@@ -1,201 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QTextCursor>
|
||||
|
||||
/**
|
||||
* @brief Defines a user mention in the current chat or edit text.
|
||||
*/
|
||||
struct Mention {
|
||||
QTextCursor cursor; /**< Contains the mention's text and position in the text. */
|
||||
QString text; /**< The inserted text of the mention. */
|
||||
int start = 0; /**< Start position of the mention. */
|
||||
int position = 0; /**< End position of the mention. */
|
||||
QString id; /**< The id the mention (used to create link when sending the message). */
|
||||
};
|
||||
|
||||
/**
|
||||
* @class ChatBarCache
|
||||
*
|
||||
* A class to cache data from a chat bar.
|
||||
*
|
||||
* A chat bar can be anything that allows users to compose or edit message, it doesn't
|
||||
* necessarily have to use the ChatBar component, e.g. MessageEditComponent.
|
||||
*
|
||||
* This object is intended to allow the current contents of a chat bar to be cached
|
||||
* between different rooms, i.e. there is an expectation that each NeoChatRoom could
|
||||
* have a separate cache for each chat bar.
|
||||
*
|
||||
* @note The NeoChatRoom which this component is created in is expected to be set
|
||||
* as it's parent. This is necessary for certain functions which need to get
|
||||
* relevant room information.
|
||||
*
|
||||
* @sa ChatBar, MessageEditComponent, NeoChatRoom
|
||||
*/
|
||||
class ChatBarCache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
/**
|
||||
* @brief The text in the chat bar.
|
||||
*
|
||||
* Due to problems with QTextDocument, unlike the other properties here,
|
||||
* text is *not* used to store the text when switching rooms.
|
||||
*/
|
||||
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the chat bar is currently replying to a message.
|
||||
*/
|
||||
Q_PROPERTY(bool isReplying READ isReplying NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The Matrix message ID of an event being replied to, if any.
|
||||
*
|
||||
* Will return empty if the RelationType is currently set to None or Edit.
|
||||
*
|
||||
* @note Replying, editing and attachments are exclusive so setting this will
|
||||
* clear an edit or attachment.
|
||||
*
|
||||
* @sa RelationType
|
||||
*/
|
||||
Q_PROPERTY(QString replyId READ replyId WRITE setReplyId NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the chat bar is currently editing a message.
|
||||
*/
|
||||
Q_PROPERTY(bool isEditing READ isEditing NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The Matrix message ID of an event being edited, if any.
|
||||
*
|
||||
* Will return empty if the RelationType is currently set to None or Reply.
|
||||
*
|
||||
* @note Replying, editing and attachments are exclusive so setting this will
|
||||
* clear an reply or attachment.
|
||||
*
|
||||
* @sa RelationType
|
||||
*/
|
||||
Q_PROPERTY(QString editId READ editId WRITE setEditId NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Get the user for the message being replied to.
|
||||
*
|
||||
* This is different to getting a Quotient::User object
|
||||
* as neither of those can provide details like the displayName or avatarMediaId
|
||||
* without the room context as these can vary from room to room.
|
||||
*
|
||||
* Returns an empty user if not replying to a message.
|
||||
*
|
||||
* The user QVariantMap has the following properties:
|
||||
* - isLocalUser - Whether the user is the local user.
|
||||
* - id - The matrix ID of the user.
|
||||
* - displayName - Display name in the context of this room.
|
||||
* - avatarSource - The mxc URL for the user's avatar in the current room.
|
||||
* - avatarMediaId - Avatar id in the context of this room.
|
||||
* - color - Color for the user.
|
||||
* - object - The Quotient::User object for the user.
|
||||
*
|
||||
* @sa getUser, Quotient::User
|
||||
*/
|
||||
Q_PROPERTY(QVariantMap relationUser READ relationUser NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The content of the related message.
|
||||
*
|
||||
* Will be QString() if no related message.
|
||||
*/
|
||||
Q_PROPERTY(QString relationMessage READ relationMessage NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the chat bar is replying in a thread.
|
||||
*/
|
||||
Q_PROPERTY(bool isThreaded READ isThreaded NOTIFY threadIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The Matrix message ID of thread root event, if any.
|
||||
*/
|
||||
Q_PROPERTY(QString threadId READ threadId WRITE setThreadId NOTIFY threadIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The local path for a file to send, if any.
|
||||
*
|
||||
* @note Replying, editing and attachments are exclusive so setting this will
|
||||
* clear an edit or reply.
|
||||
*/
|
||||
Q_PROPERTY(QString attachmentPath READ attachmentPath WRITE setAttachmentPath NOTIFY attachmentPathChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Describes the type of relation which relationId can refer to.
|
||||
*
|
||||
* A chat bar can only be relating to a single message at a time making these
|
||||
* exclusive.
|
||||
*/
|
||||
enum RelationType {
|
||||
Reply, /**< The current relation is a message being replied to. */
|
||||
Edit, /**< The current relation is a message being edited. */
|
||||
None, /**< There is currently no relation event */
|
||||
};
|
||||
Q_ENUM(RelationType)
|
||||
|
||||
explicit ChatBarCache(QObject *parent = nullptr);
|
||||
|
||||
QString text() const;
|
||||
void setText(const QString &text);
|
||||
|
||||
bool isReplying() const;
|
||||
QString replyId() const;
|
||||
void setReplyId(const QString &replyId);
|
||||
|
||||
bool isEditing() const;
|
||||
QString editId() const;
|
||||
void setEditId(const QString &editId);
|
||||
|
||||
QVariantMap relationUser() const;
|
||||
|
||||
QString relationMessage() const;
|
||||
|
||||
bool isThreaded() const;
|
||||
QString threadId() const;
|
||||
void setThreadId(const QString &threadId);
|
||||
|
||||
QString attachmentPath() const;
|
||||
void setAttachmentPath(const QString &attachmentPath);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the mentions for the current chat bar text.
|
||||
*/
|
||||
QList<Mention> *mentions();
|
||||
|
||||
/**
|
||||
* @brief Get the saved chat bar text.
|
||||
*/
|
||||
QString savedText() const;
|
||||
|
||||
/**
|
||||
* @brief Save the chat bar text.
|
||||
*/
|
||||
void setSavedText(const QString &savedText);
|
||||
|
||||
Q_SIGNALS:
|
||||
void textChanged();
|
||||
void relationIdChanged();
|
||||
void threadIdChanged();
|
||||
void attachmentPathChanged();
|
||||
|
||||
private:
|
||||
QString m_text = QString();
|
||||
QString m_relationId = QString();
|
||||
RelationType m_relationType = RelationType::None;
|
||||
QString m_threadId = QString();
|
||||
QString m_attachmentPath = QString();
|
||||
QList<Mention> m_mentions;
|
||||
QString m_savedText;
|
||||
};
|
||||
@@ -14,8 +14,6 @@
|
||||
#include <Sonnet/BackgroundChecker>
|
||||
#include <Sonnet/Settings>
|
||||
|
||||
#include "chatdocumenthandler_logging.h"
|
||||
|
||||
class SyntaxHighlighter : public QSyntaxHighlighter
|
||||
{
|
||||
public:
|
||||
@@ -59,12 +57,11 @@ public:
|
||||
setFormat(error.first, error.second.size(), errorFormat);
|
||||
}
|
||||
}
|
||||
auto handler = dynamic_cast<ChatDocumentHandler *>(parent());
|
||||
auto room = handler->room();
|
||||
auto room = dynamic_cast<ChatDocumentHandler *>(parent())->room();
|
||||
if (!room) {
|
||||
return;
|
||||
}
|
||||
auto mentions = handler->chatBarCache()->mentions();
|
||||
auto mentions = room->mentions();
|
||||
mentions->erase(std::remove_if(mentions->begin(),
|
||||
mentions->end(),
|
||||
[this](auto &mention) {
|
||||
@@ -100,16 +97,21 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
||||
, m_document(nullptr)
|
||||
, m_cursorPosition(-1)
|
||||
, m_highlighter(new SyntaxHighlighter(this))
|
||||
, m_completionModel(new CompletionModel(this))
|
||||
, m_completionModel(new CompletionModel())
|
||||
{
|
||||
connect(this, &ChatDocumentHandler::roomChanged, this, [this]() {
|
||||
m_completionModel->setRoom(m_room);
|
||||
static QPointer<NeoChatRoom> previousRoom = nullptr;
|
||||
if (previousRoom) {
|
||||
disconnect(m_chatBarCache, &ChatBarCache::textChanged, this, nullptr);
|
||||
disconnect(previousRoom, &NeoChatRoom::chatBoxTextChanged, this, nullptr);
|
||||
disconnect(previousRoom, &NeoChatRoom::editTextChanged, this, nullptr);
|
||||
}
|
||||
previousRoom = m_room;
|
||||
connect(m_chatBarCache, &ChatBarCache::textChanged, this, [this]() {
|
||||
connect(m_room, &NeoChatRoom::chatBoxTextChanged, this, [this]() {
|
||||
int start = completionStartIndex();
|
||||
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::editTextChanged, this, [this]() {
|
||||
int start = completionStartIndex();
|
||||
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||
});
|
||||
@@ -132,7 +134,11 @@ int ChatDocumentHandler::completionStartIndex() const
|
||||
return 0;
|
||||
}
|
||||
|
||||
const qsizetype cursor = cursorPosition();
|
||||
#if !defined(Q_OS_ANDROID) && QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
|
||||
const long long cursor = cursorPosition();
|
||||
#else
|
||||
const auto cursor = cursorPosition();
|
||||
#endif
|
||||
const auto &text = getText();
|
||||
|
||||
auto start = std::min(cursor, text.size()) - 1;
|
||||
@@ -146,6 +152,20 @@ int ChatDocumentHandler::completionStartIndex() const
|
||||
return start;
|
||||
}
|
||||
|
||||
bool ChatDocumentHandler::isEdit() const
|
||||
{
|
||||
return m_isEdit;
|
||||
}
|
||||
|
||||
void ChatDocumentHandler::setIsEdit(bool edit)
|
||||
{
|
||||
if (edit == m_isEdit) {
|
||||
return;
|
||||
}
|
||||
m_isEdit = edit;
|
||||
Q_EMIT isEditChanged();
|
||||
}
|
||||
|
||||
QQuickTextDocument *ChatDocumentHandler::document() const
|
||||
{
|
||||
return m_document;
|
||||
@@ -195,31 +215,8 @@ void ChatDocumentHandler::setRoom(NeoChatRoom *room)
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
ChatBarCache *ChatDocumentHandler::chatBarCache() const
|
||||
{
|
||||
return m_chatBarCache;
|
||||
}
|
||||
|
||||
void ChatDocumentHandler::setChatBarCache(ChatBarCache *chatBarCache)
|
||||
{
|
||||
if (m_chatBarCache == chatBarCache) {
|
||||
return;
|
||||
}
|
||||
m_chatBarCache = chatBarCache;
|
||||
Q_EMIT chatBarCacheChanged();
|
||||
}
|
||||
|
||||
void ChatDocumentHandler::complete(int index)
|
||||
{
|
||||
if (m_document == nullptr) {
|
||||
qCWarning(ChatDocumentHandling) << "complete called with m_document set to nullptr.";
|
||||
return;
|
||||
}
|
||||
if (m_completionModel->autoCompletionType() == CompletionModel::None) {
|
||||
qCWarning(ChatDocumentHandling) << "complete called with m_completionModel->autoCompletionType() == CompletionModel::None.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_completionModel->autoCompletionType() == CompletionModel::User) {
|
||||
auto name = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::DisplayNameRole).toString();
|
||||
auto id = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
|
||||
@@ -228,7 +225,7 @@ void ChatDocumentHandler::complete(int index)
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
cursor.insertText(name + QStringLiteral(" "));
|
||||
cursor.insertText(name % " ");
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursor.position() + name.size(), QTextCursor::KeepAnchor);
|
||||
cursor.setKeepPositionOnInsert(true);
|
||||
@@ -249,7 +246,7 @@ void ChatDocumentHandler::complete(int index)
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
cursor.insertText(alias + QStringLiteral(" "));
|
||||
cursor.insertText(alias % " ");
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursor.position() + alias.size(), QTextCursor::KeepAnchor);
|
||||
cursor.setKeepPositionOnInsert(true);
|
||||
@@ -303,20 +300,26 @@ void ChatDocumentHandler::setSelectionEnd(int position)
|
||||
|
||||
QString ChatDocumentHandler::getText() const
|
||||
{
|
||||
if (!m_chatBarCache) {
|
||||
qCWarning(ChatDocumentHandling) << "getText called with m_chatBarCache set to nullptr.";
|
||||
return {};
|
||||
if (!m_room) {
|
||||
return QString();
|
||||
}
|
||||
if (m_isEdit) {
|
||||
return m_room->editText();
|
||||
} else {
|
||||
return m_room->chatBoxText();
|
||||
}
|
||||
return m_chatBarCache->text();
|
||||
}
|
||||
|
||||
void ChatDocumentHandler::pushMention(const Mention mention) const
|
||||
{
|
||||
if (!m_chatBarCache) {
|
||||
qCWarning(ChatDocumentHandling) << "pushMention called with m_chatBarCache set to nullptr.";
|
||||
if (!m_room) {
|
||||
return;
|
||||
}
|
||||
m_chatBarCache->mentions()->push_back(mention);
|
||||
if (m_isEdit) {
|
||||
m_room->editMentions()->push_back(mention);
|
||||
} else {
|
||||
m_room->mentions()->push_back(mention);
|
||||
}
|
||||
}
|
||||
|
||||
QColor ChatDocumentHandler::mentionColor() const
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickTextDocument>
|
||||
#include <QTextCursor>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "models/completionmodel.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
class QTextDocument;
|
||||
class NeoChatRoom;
|
||||
class SyntaxHighlighter;
|
||||
|
||||
@@ -60,7 +59,14 @@ class SyntaxHighlighter;
|
||||
class ChatDocumentHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
/**
|
||||
* @brief Is the instance being used to handle an edit message.
|
||||
*
|
||||
* This is needed to ensure that the text and mentions are saved and retrieved
|
||||
* from the correct parameters in the assigned room.
|
||||
*/
|
||||
Q_PROPERTY(bool isEdit READ isEdit WRITE setIsEdit NOTIFY isEditChanged)
|
||||
|
||||
/**
|
||||
* @brief The QQuickTextDocument that is being handled.
|
||||
@@ -88,31 +94,29 @@ class ChatDocumentHandler : public QObject
|
||||
* This is typically provided to a qml component to visualise the current
|
||||
* completion results.
|
||||
*/
|
||||
Q_PROPERTY(CompletionModel *completionModel READ completionModel CONSTANT)
|
||||
Q_PROPERTY(CompletionModel *completionModel READ completionModel NOTIFY completionModelChanged)
|
||||
|
||||
/**
|
||||
* @brief The current room that the the text document is being handled for.
|
||||
*/
|
||||
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||
|
||||
/**
|
||||
* @brief The cache for the chat bar the text document is being handled for.
|
||||
*/
|
||||
Q_PROPERTY(ChatBarCache *chatBarCache READ chatBarCache WRITE setChatBarCache NOTIFY chatBarCacheChanged)
|
||||
|
||||
/**
|
||||
* @brief The color to highlight user mentions.
|
||||
*/
|
||||
Q_PROPERTY(QColor mentionColor READ mentionColor WRITE setMentionColor NOTIFY mentionColorChanged)
|
||||
Q_PROPERTY(QColor mentionColor READ mentionColor WRITE setMentionColor NOTIFY mentionColorChanged);
|
||||
|
||||
/**
|
||||
* @brief The color to highlight spelling errors.
|
||||
*/
|
||||
Q_PROPERTY(QColor errorColor READ errorColor WRITE setErrorColor NOTIFY errorColorChanged)
|
||||
Q_PROPERTY(QColor errorColor READ errorColor WRITE setErrorColor NOTIFY errorColorChanged);
|
||||
|
||||
public:
|
||||
explicit ChatDocumentHandler(QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] bool isEdit() const;
|
||||
void setIsEdit(bool edit);
|
||||
|
||||
[[nodiscard]] QQuickTextDocument *document() const;
|
||||
void setDocument(QQuickTextDocument *document);
|
||||
|
||||
@@ -128,11 +132,9 @@ public:
|
||||
[[nodiscard]] NeoChatRoom *room() const;
|
||||
void setRoom(NeoChatRoom *room);
|
||||
|
||||
[[nodiscard]] ChatBarCache *chatBarCache() const;
|
||||
void setChatBarCache(ChatBarCache *chatBarCache);
|
||||
|
||||
Q_INVOKABLE void complete(int index);
|
||||
|
||||
void updateCompletions();
|
||||
CompletionModel *completionModel() const;
|
||||
|
||||
[[nodiscard]] QColor mentionColor() const;
|
||||
@@ -142,10 +144,11 @@ public:
|
||||
void setErrorColor(const QColor &color);
|
||||
|
||||
Q_SIGNALS:
|
||||
void isEditChanged();
|
||||
void documentChanged();
|
||||
void cursorPositionChanged();
|
||||
void roomChanged();
|
||||
void chatBarCacheChanged();
|
||||
void completionModelChanged();
|
||||
void selectionStartChanged();
|
||||
void selectionEndChanged();
|
||||
void errorColorChanged();
|
||||
@@ -154,10 +157,12 @@ Q_SIGNALS:
|
||||
private:
|
||||
int completionStartIndex() const;
|
||||
|
||||
bool m_isEdit = false;
|
||||
|
||||
QPointer<QQuickTextDocument> m_document;
|
||||
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
QPointer<ChatBarCache> m_chatBarCache;
|
||||
bool completionVisible = false;
|
||||
|
||||
QColor m_mentionColor;
|
||||
QColor m_errorColor;
|
||||
@@ -171,5 +176,7 @@ private:
|
||||
|
||||
SyntaxHighlighter *m_highlighter = nullptr;
|
||||
|
||||
CompletionModel::AutoCompletionType m_completionType = CompletionModel::None;
|
||||
|
||||
CompletionModel *m_completionModel = nullptr;
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
class QClipboard;
|
||||
class QImage;
|
||||
@@ -19,8 +18,6 @@ class QImage;
|
||||
class Clipboard : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
/**
|
||||
* @brief Whether the current clipboard content is an image.
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
class QAbstractItemModel;
|
||||
class KColorSchemeManager;
|
||||
@@ -20,8 +19,6 @@ class KColorSchemeManager;
|
||||
class ColorSchemer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
/**
|
||||
* @brief A QAbstractItemModel of all available color schemes.
|
||||
|
||||
@@ -4,14 +4,24 @@
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
#include <qt5keychain/keychain.h>
|
||||
#else
|
||||
#include <qt6keychain/keychain.h>
|
||||
#endif
|
||||
|
||||
#include <KConfig>
|
||||
#include <KConfigGroup>
|
||||
#include <KLocalizedString>
|
||||
#include <KWindowConfig>
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
#include <KWindowEffects>
|
||||
#endif
|
||||
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QGuiApplication>
|
||||
#include <QImageReader>
|
||||
#include <QNetworkProxy>
|
||||
#include <QQuickTextDocument>
|
||||
#include <QQuickWindow>
|
||||
@@ -23,15 +33,17 @@
|
||||
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/csapi/content-repo.h>
|
||||
#include <Quotient/csapi/logout.h>
|
||||
#include <Quotient/csapi/notifications.h>
|
||||
#include <Quotient/eventstats.h>
|
||||
#include <Quotient/csapi/profile.h>
|
||||
#include <Quotient/jobs/downloadfilejob.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
#include <Quotient/user.h>
|
||||
#include <Quotient/csapi/notifications.h>
|
||||
#include <Quotient/eventstats.h>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatuser.h"
|
||||
#include "notificationsmanager.h"
|
||||
#include "roommanager.h"
|
||||
#include "windowcontroller.h"
|
||||
@@ -42,14 +54,13 @@
|
||||
#include "trayicon_sni.h"
|
||||
#endif
|
||||
|
||||
bool testMode = false;
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
Controller::Controller(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
Connection::setRoomType<NeoChatRoom>();
|
||||
Connection::setUserType<NeoChatUser>();
|
||||
|
||||
setApplicationProxy();
|
||||
|
||||
@@ -58,21 +69,11 @@ Controller::Controller(QObject *parent)
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::SystemTrayChanged, this, &Controller::setQuitOnLastWindowClosed);
|
||||
#endif
|
||||
|
||||
if (!testMode) {
|
||||
QTimer::singleShot(0, this, [this] {
|
||||
invokeLogin();
|
||||
});
|
||||
} else {
|
||||
auto c = new NeoChatConnection(this);
|
||||
c->assumeIdentity(QStringLiteral("@user:localhost:1234"), QStringLiteral("token_1234"));
|
||||
connect(c, &Connection::connected, this, [c, this]() {
|
||||
m_accountRegistry.add(c);
|
||||
c->syncLoop();
|
||||
});
|
||||
}
|
||||
QTimer::singleShot(0, this, [this] {
|
||||
invokeLogin();
|
||||
});
|
||||
|
||||
QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [this] {
|
||||
delete m_trayIcon;
|
||||
QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [] {
|
||||
NeoChatConfig::self()->save();
|
||||
});
|
||||
|
||||
@@ -100,34 +101,22 @@ Controller::Controller(QObject *parent)
|
||||
}
|
||||
#endif
|
||||
|
||||
connect(&m_accountRegistry, &AccountRegistry::accountCountChanged, this, &Controller::activeConnectionIndexChanged);
|
||||
|
||||
static int oldAccountCount = 0;
|
||||
connect(&m_accountRegistry, &AccountRegistry::accountCountChanged, this, [this]() {
|
||||
if (m_accountRegistry.size() > oldAccountCount) {
|
||||
auto connection = dynamic_cast<NeoChatConnection *>(m_accountRegistry.accounts()[m_accountRegistry.size() - 1]);
|
||||
connect(connection, &NeoChatConnection::syncDone, this, [this, connection]() {
|
||||
auto connection = m_accountRegistry.accounts()[m_accountRegistry.size() - 1];
|
||||
connect(connection, &Connection::syncDone, this, [connection]() {
|
||||
NotificationsManager::instance().handleNotifications(connection);
|
||||
});
|
||||
connectSingleShot(connection, &NeoChatConnection::syncDone, this, [this, connection] {
|
||||
connection->setupPushNotifications(m_endpoint);
|
||||
});
|
||||
}
|
||||
oldAccountCount = m_accountRegistry.size();
|
||||
});
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
auto connector = new KUnifiedPush::Connector(QStringLiteral("org.kde.neochat"));
|
||||
connect(connector, &KUnifiedPush::Connector::endpointChanged, this, [this](const QString &endpoint) {
|
||||
m_endpoint = endpoint;
|
||||
for (auto "ientConnection : m_accountRegistry) {
|
||||
auto connection = dynamic_cast<NeoChatConnection *>(quotientConnection);
|
||||
connection->setupPushNotifications(endpoint);
|
||||
}
|
||||
QTimer::singleShot(0, this, [this] {
|
||||
m_pushRuleModel = new PushRuleModel;
|
||||
});
|
||||
|
||||
connector->registerClient(i18n("Receiving push notifications"));
|
||||
|
||||
m_endpoint = connector->endpoint();
|
||||
#endif
|
||||
}
|
||||
|
||||
Controller &Controller::instance()
|
||||
@@ -136,7 +125,43 @@ Controller &Controller::instance()
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void Controller::addConnection(NeoChatConnection *c)
|
||||
void Controller::showWindow()
|
||||
{
|
||||
WindowController::instance().showAndRaiseWindow(QString());
|
||||
}
|
||||
|
||||
void Controller::logout(Connection *conn, bool serverSideLogout)
|
||||
{
|
||||
if (!conn) {
|
||||
qCritical() << "Attempt to logout null connection";
|
||||
return;
|
||||
}
|
||||
|
||||
SettingsGroup("Accounts").remove(conn->userId());
|
||||
|
||||
QKeychain::DeletePasswordJob job(qAppName());
|
||||
job.setAutoDelete(true);
|
||||
job.setKey(conn->userId());
|
||||
QEventLoop loop;
|
||||
QKeychain::DeletePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
|
||||
job.start();
|
||||
loop.exec();
|
||||
|
||||
if (m_accountRegistry.count() > 1) {
|
||||
// Only set the connection if the the account being logged out is currently active
|
||||
if (conn == activeConnection()) {
|
||||
setActiveConnection(m_accountRegistry.accounts()[0]);
|
||||
}
|
||||
} else {
|
||||
setActiveConnection(nullptr);
|
||||
}
|
||||
if (!serverSideLogout) {
|
||||
return;
|
||||
}
|
||||
conn->logout();
|
||||
}
|
||||
|
||||
void Controller::addConnection(Connection *c)
|
||||
{
|
||||
Q_ASSERT_X(c, __FUNCTION__, "Attempt to add a null connection");
|
||||
|
||||
@@ -144,15 +169,17 @@ void Controller::addConnection(NeoChatConnection *c)
|
||||
|
||||
c->setLazyLoading(true);
|
||||
|
||||
connect(c, &NeoChatConnection::syncDone, this, [this, c] {
|
||||
connect(c, &Connection::syncDone, this, [this, c] {
|
||||
Q_EMIT syncDone();
|
||||
|
||||
c->sync(30000);
|
||||
c->saveState();
|
||||
});
|
||||
connect(c, &NeoChatConnection::loggedOut, this, [this, c] {
|
||||
connect(c, &Connection::loggedOut, this, [this, c] {
|
||||
dropConnection(c);
|
||||
});
|
||||
|
||||
connect(c, &NeoChatConnection::requestFailed, this, [this](BaseJob *job) {
|
||||
connect(c, &Connection::requestFailed, this, [this](BaseJob *job) {
|
||||
if (job->error() == BaseJob::UserConsentRequired) {
|
||||
Q_EMIT userConsentRequired(job->errorUrl());
|
||||
}
|
||||
@@ -161,24 +188,23 @@ void Controller::addConnection(NeoChatConnection *c)
|
||||
c->sync();
|
||||
|
||||
Q_EMIT connectionAdded(c);
|
||||
Q_EMIT accountCountChanged();
|
||||
}
|
||||
|
||||
void Controller::dropConnection(NeoChatConnection *c)
|
||||
void Controller::dropConnection(Connection *c)
|
||||
{
|
||||
Q_ASSERT_X(c, __FUNCTION__, "Attempt to drop a null connection");
|
||||
|
||||
m_accountRegistry.drop(c);
|
||||
Q_EMIT connectionDropped(c);
|
||||
Q_EMIT accountCountChanged();
|
||||
}
|
||||
|
||||
void Controller::invokeLogin()
|
||||
{
|
||||
const auto accounts = SettingsGroup("Accounts"_ls).childGroups();
|
||||
const auto accounts = SettingsGroup("Accounts").childGroups();
|
||||
QString id = NeoChatConfig::self()->activeConnection();
|
||||
for (const auto &accountId : accounts) {
|
||||
AccountSettings account{accountId};
|
||||
m_accountsLoading += accountId;
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
if (id.isEmpty()) {
|
||||
// handle case where the account config is empty
|
||||
id = accountId;
|
||||
@@ -189,42 +215,45 @@ void Controller::invokeLogin()
|
||||
AccountSettings account{accountId};
|
||||
QString accessToken;
|
||||
if (accessTokenLoadingJob->error() == QKeychain::Error::NoError) {
|
||||
accessToken = QString::fromLatin1(accessTokenLoadingJob->binaryData());
|
||||
accessToken = accessTokenLoadingJob->binaryData();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
auto connection = new NeoChatConnection(account.homeserver());
|
||||
connect(connection, &NeoChatConnection::connected, this, [this, connection, id] {
|
||||
auto connection = new Connection(account.homeserver());
|
||||
connect(connection, &Connection::connected, this, [this, connection, id] {
|
||||
connection->loadState();
|
||||
addConnection(connection);
|
||||
m_accountsLoading.removeAll(connection->userId());
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
if (connection->userId() == id) {
|
||||
setActiveConnection(connection);
|
||||
connectSingleShot(connection, &Connection::syncDone, this, &Controller::initiated);
|
||||
}
|
||||
});
|
||||
connect(connection, &NeoChatConnection::loginError, this, [this, connection](const QString &error, const QString &) {
|
||||
if (error == "Unrecognised access token"_ls) {
|
||||
Q_EMIT errorOccured(i18n("Login Failed: Access Token invalid or revoked"), {});
|
||||
connection->logout(false);
|
||||
} else if (error == "Connection closed"_ls) {
|
||||
Q_EMIT errorOccured(i18n("Login Failed: %1", error), {});
|
||||
connect(connection, &Connection::loginError, this, [this, connection](const QString &error, const QString &) {
|
||||
if (error == "Unrecognised access token") {
|
||||
Q_EMIT errorOccured(i18n("Login Failed: Access Token invalid or revoked"));
|
||||
logout(connection, false);
|
||||
} else if (error == "Connection closed") {
|
||||
Q_EMIT errorOccured(i18n("Login Failed: %1", error));
|
||||
// Failed due to network connection issue. This might happen when the homeserver is
|
||||
// temporary down, or the user trying to re-launch NeoChat in a network that cannot
|
||||
// connect to the homeserver. In this case, we don't want to do logout().
|
||||
} else {
|
||||
Q_EMIT errorOccured(i18n("Login Failed: %1", error), {});
|
||||
connection->logout(true);
|
||||
Q_EMIT errorOccured(i18n("Login Failed: %1", error));
|
||||
logout(connection, true);
|
||||
}
|
||||
Q_EMIT initiated();
|
||||
});
|
||||
connect(connection, &NeoChatConnection::networkError, this, [this](const QString &error, const QString &, int, int) {
|
||||
Q_EMIT errorOccured(i18n("Network Error: %1", error), {});
|
||||
connect(connection, &Connection::networkError, this, [this](const QString &error, const QString &, int, int) {
|
||||
Q_EMIT errorOccured(i18n("Network Error: %1", error));
|
||||
});
|
||||
connection->assumeIdentity(account.userId(), accessToken);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (accounts.isEmpty()) {
|
||||
Q_EMIT initiated();
|
||||
}
|
||||
}
|
||||
|
||||
QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const AccountSettings &account)
|
||||
@@ -241,17 +270,17 @@ QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const Accoun
|
||||
|
||||
switch (job->error()) {
|
||||
case QKeychain::EntryNotFound:
|
||||
Q_EMIT errorOccured(i18n("Access token wasn't found"), i18n("Maybe it was deleted?"));
|
||||
Q_EMIT globalErrorOccured(i18n("Access token wasn't found"), i18n("Maybe it was deleted?"));
|
||||
break;
|
||||
case QKeychain::AccessDeniedByUser:
|
||||
case QKeychain::AccessDenied:
|
||||
Q_EMIT errorOccured(i18n("Access to keychain was denied."), i18n("Please allow NeoChat to read the access token"));
|
||||
Q_EMIT globalErrorOccured(i18n("Access to keychain was denied."), i18n("Please allow NeoChat to read the access token"));
|
||||
break;
|
||||
case QKeychain::NoBackendAvailable:
|
||||
Q_EMIT errorOccured(i18n("No keychain available."), i18n("Please install a keychain, e.g. KWallet or GNOME keyring on Linux"));
|
||||
Q_EMIT globalErrorOccured(i18n("No keychain available."), i18n("Please install a keychain, e.g. KWallet or GNOME keyring on Linux"));
|
||||
break;
|
||||
case QKeychain::OtherError:
|
||||
Q_EMIT errorOccured(i18n("Unable to read access token"), job->errorString());
|
||||
Q_EMIT globalErrorOccured(i18n("Unable to read access token"), job->errorString());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -281,32 +310,111 @@ bool Controller::saveAccessTokenToKeyChain(const AccountSettings &account, const
|
||||
return true;
|
||||
}
|
||||
|
||||
void Controller::changeAvatar(Connection *conn, const QUrl &localFile)
|
||||
{
|
||||
auto job = conn->uploadFile(localFile.toLocalFile());
|
||||
connect(job, &BaseJob::success, this, [conn, job] {
|
||||
conn->callApi<SetAvatarUrlJob>(conn->userId(), job->contentUri());
|
||||
});
|
||||
}
|
||||
|
||||
void Controller::markAllMessagesAsRead(Connection *conn)
|
||||
{
|
||||
const auto rooms = conn->allRooms();
|
||||
for (auto room : rooms) {
|
||||
room->markAllMessagesAsRead();
|
||||
}
|
||||
}
|
||||
|
||||
bool Controller::supportSystemTray() const
|
||||
{
|
||||
#ifdef Q_OS_ANDROID
|
||||
return false;
|
||||
#else
|
||||
auto de = QString::fromLatin1(qgetenv("XDG_CURRENT_DESKTOP"));
|
||||
QString de = getenv("XDG_CURRENT_DESKTOP");
|
||||
return de != QStringLiteral("GNOME") && de != QStringLiteral("Pantheon");
|
||||
#endif
|
||||
}
|
||||
|
||||
void Controller::changePassword(Connection *connection, const QString ¤tPassword, const QString &newPassword)
|
||||
{
|
||||
NeochatChangePasswordJob *job = connection->callApi<NeochatChangePasswordJob>(newPassword, false);
|
||||
connect(job, &BaseJob::result, this, [this, job, currentPassword, newPassword, connection] {
|
||||
if (job->error() == 103) {
|
||||
QJsonObject replyData = job->jsonData();
|
||||
QJsonObject authData;
|
||||
authData["session"] = replyData["session"];
|
||||
authData["password"] = currentPassword;
|
||||
authData["type"] = "m.login.password";
|
||||
authData["user"] = connection->user()->id();
|
||||
QJsonObject identifier = {{"type", "m.id.user"}, {"user", connection->user()->id()}};
|
||||
authData["identifier"] = identifier;
|
||||
NeochatChangePasswordJob *innerJob = connection->callApi<NeochatChangePasswordJob>(newPassword, false, authData);
|
||||
connect(innerJob, &BaseJob::success, this, [this]() {
|
||||
Q_EMIT passwordStatus(PasswordStatus::Success);
|
||||
});
|
||||
connect(innerJob, &BaseJob::failure, this, [innerJob, this]() {
|
||||
if (innerJob->jsonData()["errcode"] == "M_FORBIDDEN") {
|
||||
Q_EMIT passwordStatus(PasswordStatus::Wrong);
|
||||
} else {
|
||||
Q_EMIT passwordStatus(PasswordStatus::Other);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool Controller::setAvatar(Connection *connection, const QUrl &avatarSource)
|
||||
{
|
||||
User *localUser = connection->user();
|
||||
QString decoded = avatarSource.path();
|
||||
if (decoded.isEmpty()) {
|
||||
connection->callApi<SetAvatarUrlJob>(localUser->id(), avatarSource);
|
||||
return true;
|
||||
}
|
||||
if (QImageReader(decoded).read().isNull()) {
|
||||
return false;
|
||||
} else {
|
||||
return localUser->setAvatar(decoded);
|
||||
}
|
||||
}
|
||||
|
||||
NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const Omittable<QJsonObject> &auth)
|
||||
: BaseJob(HttpVerb::Post, QStringLiteral("ChangePasswordJob"), "/_matrix/client/r0/account/password")
|
||||
{
|
||||
QJsonObject _data;
|
||||
addParam<>(_data, QStringLiteral("new_password"), newPassword);
|
||||
addParam<IfNotEmpty>(_data, QStringLiteral("logout_devices"), logoutDevices);
|
||||
addParam<IfNotEmpty>(_data, QStringLiteral("auth"), auth);
|
||||
setRequestData(_data);
|
||||
}
|
||||
|
||||
int Controller::accountCount() const
|
||||
{
|
||||
return m_accountRegistry.count();
|
||||
}
|
||||
|
||||
void Controller::setQuitOnLastWindowClosed()
|
||||
{
|
||||
#ifndef Q_OS_ANDROID
|
||||
if (NeoChatConfig::self()->systemTray()) {
|
||||
m_trayIcon = new TrayIcon(this);
|
||||
m_trayIcon->show();
|
||||
connect(m_trayIcon, &TrayIcon::showWindow, this, &Controller::showWindow);
|
||||
} else {
|
||||
if (m_trayIcon) {
|
||||
disconnect(m_trayIcon, &TrayIcon::showWindow, this, &Controller::showWindow);
|
||||
delete m_trayIcon;
|
||||
m_trayIcon = nullptr;
|
||||
}
|
||||
}
|
||||
QGuiApplication::setQuitOnLastWindowClosed(!NeoChatConfig::self()->systemTray());
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
NeoChatConnection *Controller::activeConnection() const
|
||||
Connection *Controller::activeConnection() const
|
||||
{
|
||||
if (m_connection.isNull()) {
|
||||
return nullptr;
|
||||
@@ -314,28 +422,54 @@ NeoChatConnection *Controller::activeConnection() const
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
void Controller::setActiveConnection(NeoChatConnection *connection)
|
||||
void Controller::setActiveConnection(Connection *connection)
|
||||
{
|
||||
if (connection == m_connection) {
|
||||
return;
|
||||
}
|
||||
if (m_connection != nullptr) {
|
||||
disconnect(m_connection, &NeoChatConnection::syncError, this, nullptr);
|
||||
disconnect(m_connection, &NeoChatConnection::accountDataChanged, this, nullptr);
|
||||
disconnect(m_connection, &Connection::syncError, this, nullptr);
|
||||
disconnect(m_connection, &Connection::accountDataChanged, this, nullptr);
|
||||
}
|
||||
m_connection = connection;
|
||||
if (connection != nullptr) {
|
||||
NeoChatConfig::self()->setActiveConnection(connection->userId());
|
||||
connect(connection, &NeoChatConnection::requestFailed, this, [](BaseJob *job) {
|
||||
if (dynamic_cast<DownloadFileJob *>(job) && job->jsonData()["errcode"_ls].toString() == "M_TOO_LARGE"_ls) {
|
||||
connect(connection, &Connection::networkError, this, [this]() {
|
||||
if (!m_isOnline) {
|
||||
return;
|
||||
}
|
||||
m_isOnline = false;
|
||||
Q_EMIT isOnlineChanged(false);
|
||||
});
|
||||
connect(connection, &Connection::syncDone, this, [this] {
|
||||
if (m_isOnline) {
|
||||
return;
|
||||
}
|
||||
m_isOnline = true;
|
||||
Q_EMIT isOnlineChanged(true);
|
||||
});
|
||||
connect(connection, &Connection::requestFailed, this, [](BaseJob *job) {
|
||||
if (dynamic_cast<DownloadFileJob *>(job) && job->jsonData()["errcode"].toString() == "M_TOO_LARGE"_ls) {
|
||||
RoomManager::instance().warning(i18n("File too large to download."), i18n("Contact your matrix server administrator for support."));
|
||||
}
|
||||
});
|
||||
connect(connection, &Connection::accountDataChanged, this, [this](const QString &type) {
|
||||
if (type == QLatin1String("org.kde.neochat.account_label")) {
|
||||
Q_EMIT activeAccountLabelChanged();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
NeoChatConfig::self()->setActiveConnection(QString());
|
||||
}
|
||||
NeoChatConfig::self()->save();
|
||||
Q_EMIT activeConnectionChanged();
|
||||
Q_EMIT activeConnectionIndexChanged();
|
||||
Q_EMIT activeAccountLabelChanged();
|
||||
}
|
||||
|
||||
PushRuleModel *Controller::pushRuleModel() const
|
||||
{
|
||||
return m_pushRuleModel;
|
||||
}
|
||||
|
||||
void Controller::saveWindowGeometry()
|
||||
@@ -343,33 +477,122 @@ void Controller::saveWindowGeometry()
|
||||
WindowController::instance().saveGeometry();
|
||||
}
|
||||
|
||||
NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const Omittable<QJsonObject> &auth)
|
||||
: Quotient::BaseJob(HttpVerb::Delete, QStringLiteral("DeleteDeviceJob"), QStringLiteral("/_matrix/client/r0/devices/%1").arg(deviceId).toLatin1())
|
||||
{
|
||||
QJsonObject _data;
|
||||
addParam<IfNotEmpty>(_data, QStringLiteral("auth"), auth);
|
||||
setRequestData(std::move(_data));
|
||||
}
|
||||
|
||||
void Controller::createRoom(const QString &name, const QString &topic)
|
||||
{
|
||||
auto createRoomJob = m_connection->createRoom(Connection::PublishRoom, "", name, topic, QStringList());
|
||||
connect(createRoomJob, &CreateRoomJob::failure, this, [this, createRoomJob] {
|
||||
Q_EMIT errorOccured(i18n("Room creation failed: %1", createRoomJob->errorString()));
|
||||
});
|
||||
connectSingleShot(this, &Controller::roomAdded, &RoomManager::instance(), &RoomManager::enterRoom, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void Controller::createSpace(const QString &name, const QString &topic)
|
||||
{
|
||||
auto createRoomJob = m_connection->createRoom(Connection::UnpublishRoom,
|
||||
{},
|
||||
name,
|
||||
topic,
|
||||
QStringList(),
|
||||
{},
|
||||
{},
|
||||
false,
|
||||
{},
|
||||
{},
|
||||
QJsonObject{
|
||||
{"type"_ls, "m.space"_ls},
|
||||
});
|
||||
connect(createRoomJob, &CreateRoomJob::failure, this, [this, createRoomJob] {
|
||||
Q_EMIT errorOccured(i18n("Space creation failed: %1", createRoomJob->errorString()));
|
||||
});
|
||||
connectSingleShot(this, &Controller::roomAdded, &RoomManager::instance(), &RoomManager::enterRoom, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
bool Controller::isOnline() const
|
||||
{
|
||||
return m_isOnline;
|
||||
}
|
||||
|
||||
// TODO: Remove in favor of RoomManager::joinRoom
|
||||
void Controller::joinRoom(const QString &alias)
|
||||
{
|
||||
if (!alias.contains(":")) {
|
||||
Q_EMIT errorOccured(i18n("The room id you are trying to join is not valid"));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto knownServer = alias.mid(alias.indexOf(":") + 1);
|
||||
RoomManager::instance().joinRoom(m_connection, alias, QStringList{knownServer});
|
||||
}
|
||||
|
||||
void Controller::openOrCreateDirectChat(NeoChatUser *user)
|
||||
{
|
||||
const auto existing = activeConnection()->directChats();
|
||||
|
||||
if (existing.contains(user)) {
|
||||
const auto &room = static_cast<NeoChatRoom *>(activeConnection()->room(existing.value(user)));
|
||||
if (room) {
|
||||
RoomManager::instance().enterRoom(room);
|
||||
return;
|
||||
}
|
||||
}
|
||||
activeConnection()->requestDirectChat(user);
|
||||
}
|
||||
|
||||
QString Controller::formatByteSize(double size, int precision) const
|
||||
{
|
||||
return QLocale().formattedDataSize(size, precision);
|
||||
}
|
||||
|
||||
QString Controller::formatDuration(quint64 msecs, KFormat::DurationFormatOptions options) const
|
||||
{
|
||||
return KFormat().formatDuration(msecs, options);
|
||||
}
|
||||
|
||||
void Controller::setBlur(QQuickItem *item, bool blur)
|
||||
{
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
auto setWindows = [item, blur]() {
|
||||
auto reg = QRect(QPoint(0, 0), item->window()->size());
|
||||
KWindowEffects::enableBackgroundContrast(item->window(), blur, 1, 1, 1, reg);
|
||||
KWindowEffects::enableBlurBehind(item->window(), blur, reg);
|
||||
};
|
||||
|
||||
disconnect(item->window(), &QQuickWindow::heightChanged, this, nullptr);
|
||||
disconnect(item->window(), &QQuickWindow::widthChanged, this, nullptr);
|
||||
connect(item->window(), &QQuickWindow::heightChanged, this, setWindows);
|
||||
connect(item->window(), &QQuickWindow::widthChanged, this, setWindows);
|
||||
setWindows();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Controller::hasWindowSystem() const
|
||||
{
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Controller::encryptionSupported() const
|
||||
{
|
||||
return Quotient::encryptionSupported();
|
||||
}
|
||||
|
||||
void Controller::forceRefreshTextDocument(QQuickTextDocument *textDocument, QQuickItem *item)
|
||||
{
|
||||
// HACK: Workaround bug QTBUG 93281
|
||||
connect(textDocument->textDocument(), SIGNAL(imagesLoaded()), item, SLOT(updateWholeDocument()));
|
||||
}
|
||||
|
||||
void Controller::listenForNotifications()
|
||||
{
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
auto connector = new KUnifiedPush::Connector(QStringLiteral("org.kde.neochat"));
|
||||
|
||||
auto timer = new QTimer();
|
||||
connect(timer, &QTimer::timeout, qGuiApp, &QGuiApplication::quit);
|
||||
|
||||
connect(connector, &KUnifiedPush::Connector::messageReceived, [timer](const QByteArray &data) {
|
||||
NotificationsManager::instance().postPushNotification(data);
|
||||
timer->stop();
|
||||
});
|
||||
|
||||
// Wait five seconds to see if we received any messages or this happened to be an erroneous activation.
|
||||
// Otherwise, messageReceived is never activated, and this daemon could stick around forever.
|
||||
timer->start(5000);
|
||||
|
||||
connector->registerClient(i18n("Receiving push notifications"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Controller::setApplicationProxy()
|
||||
{
|
||||
NeoChatConfig *cfg = NeoChatConfig::self();
|
||||
@@ -400,6 +623,20 @@ void Controller::setApplicationProxy()
|
||||
QNetworkProxy::setApplicationProxy(proxy);
|
||||
}
|
||||
|
||||
int Controller::activeConnectionIndex() const
|
||||
{
|
||||
auto result = std::find_if(m_accountRegistry.accounts().begin(), m_accountRegistry.accounts().end(), [this](const auto &it) {
|
||||
return it == m_connection;
|
||||
});
|
||||
return result - m_accountRegistry.accounts().begin();
|
||||
}
|
||||
|
||||
int Controller::quotientMinorVersion() const
|
||||
{
|
||||
// TODO libQuotient 0.7: Replace with version function from libQuotient
|
||||
return 7;
|
||||
}
|
||||
|
||||
bool Controller::isFlatpak() const
|
||||
{
|
||||
#ifdef NEOCHAT_FLATPAK
|
||||
@@ -409,14 +646,44 @@ bool Controller::isFlatpak() const
|
||||
#endif
|
||||
}
|
||||
|
||||
void Controller::setActiveAccountLabel(const QString &label)
|
||||
{
|
||||
if (!m_connection) {
|
||||
return;
|
||||
}
|
||||
QJsonObject json{
|
||||
{"account_label", label},
|
||||
};
|
||||
m_connection->setAccountData("org.kde.neochat.account_label", json);
|
||||
}
|
||||
|
||||
QString Controller::activeAccountLabel() const
|
||||
{
|
||||
if (!m_connection) {
|
||||
return {};
|
||||
}
|
||||
return m_connection->accountDataJson("org.kde.neochat.account_label")["account_label"].toString();
|
||||
}
|
||||
|
||||
QVariantList Controller::getSupportedRoomVersions(Quotient::Connection *connection)
|
||||
{
|
||||
auto roomVersions = connection->availableRoomVersions();
|
||||
|
||||
QVariantList supportedRoomVersions;
|
||||
for (const Quotient::Connection::SupportedRoomVersion &v : roomVersions) {
|
||||
QVariantMap roomVersionMap;
|
||||
roomVersionMap.insert("id", v.id);
|
||||
roomVersionMap.insert("status", v.status);
|
||||
roomVersionMap.insert("isStable", v.isStable());
|
||||
supportedRoomVersions.append(roomVersionMap);
|
||||
}
|
||||
|
||||
return supportedRoomVersions;
|
||||
}
|
||||
|
||||
AccountRegistry &Controller::accounts()
|
||||
{
|
||||
return m_accountRegistry;
|
||||
}
|
||||
|
||||
#include "moc_controller.cpp"
|
||||
|
||||
void Controller::setTestMode(bool test)
|
||||
{
|
||||
testMode = test;
|
||||
}
|
||||
|
||||
231
src/controller.h
231
src/controller.h
@@ -3,21 +3,28 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "models/pushrulemodel.h"
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickItem>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include <KFormat>
|
||||
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/settings.h>
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
#include <kunifiedpush/connector.h>
|
||||
#endif
|
||||
|
||||
class NeoChatRoom;
|
||||
class NeoChatUser;
|
||||
class TrayIcon;
|
||||
class QWindow;
|
||||
class QQuickTextDocument;
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class Connection;
|
||||
class Room;
|
||||
}
|
||||
|
||||
namespace QKeychain
|
||||
{
|
||||
class ReadPasswordJob;
|
||||
@@ -34,19 +41,66 @@ class ReadPasswordJob;
|
||||
class Controller : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
/**
|
||||
* @brief The number of logged in accounts.
|
||||
*/
|
||||
Q_PROPERTY(int accountCount READ accountCount NOTIFY accountCountChanged)
|
||||
|
||||
/**
|
||||
* @brief The current connection for the rest of NeoChat to use.
|
||||
*/
|
||||
Q_PROPERTY(NeoChatConnection *activeConnection READ activeConnection WRITE setActiveConnection NOTIFY activeConnectionChanged)
|
||||
Q_PROPERTY(Quotient::Connection *activeConnection READ activeConnection WRITE setActiveConnection NOTIFY activeConnectionChanged)
|
||||
|
||||
/**
|
||||
* @brief The PushRuleModel that has the active connection's push rules.
|
||||
*/
|
||||
Q_PROPERTY(PushRuleModel *pushRuleModel READ pushRuleModel CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The row number in the accounts directory of the active connection.
|
||||
*/
|
||||
Q_PROPERTY(int activeConnectionIndex READ activeConnectionIndex NOTIFY activeConnectionIndexChanged)
|
||||
|
||||
/**
|
||||
* @brief The account label for the active account.
|
||||
*
|
||||
* Account labels are a concept specific to NeoChat, allowing accounts to be
|
||||
* labelled, e.g. for "Work", "Private", etc.
|
||||
*
|
||||
* Set to an empty string to remove the label.
|
||||
*/
|
||||
Q_PROPERTY(QString activeAccountLabel READ activeAccountLabel WRITE setActiveAccountLabel NOTIFY activeAccountLabelChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the OS NeoChat is running on supports sytem tray icons.
|
||||
*/
|
||||
Q_PROPERTY(bool supportSystemTray READ supportSystemTray CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief Whether KWindowSystem specific features are available.
|
||||
*/
|
||||
Q_PROPERTY(bool hasWindowSystem READ hasWindowSystem CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief Whether NeoChat is currently able to connect to the server.
|
||||
*/
|
||||
Q_PROPERTY(bool isOnline READ isOnline NOTIFY isOnlineChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the ecryption support has been enabled.
|
||||
*/
|
||||
Q_PROPERTY(bool encryptionSupported READ encryptionSupported CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The current minor version number of libQuotient being used.
|
||||
*
|
||||
* This is the only way to gate NeoChat features by libQuotient version in QML.
|
||||
*
|
||||
* @note No major version because libQuotient doesn't have any; All are 0.x.
|
||||
*/
|
||||
Q_PROPERTY(int quotientMinorVersion READ quotientMinorVersion CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief Whether NeoChat is running as a flatpak.
|
||||
*
|
||||
@@ -54,36 +108,94 @@ class Controller : public QObject
|
||||
*/
|
||||
Q_PROPERTY(bool isFlatpak READ isFlatpak CONSTANT)
|
||||
|
||||
Q_PROPERTY(QStringList accountsLoading MEMBER m_accountsLoading NOTIFY accountsLoadingChanged)
|
||||
|
||||
public:
|
||||
static Controller &instance();
|
||||
static Controller *create(QQmlEngine *engine, QJSEngine *)
|
||||
{
|
||||
engine->setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
|
||||
return &instance();
|
||||
}
|
||||
/**
|
||||
* @brief Defines the status after an attempt to change the password on an account.
|
||||
*/
|
||||
enum PasswordStatus {
|
||||
Success, /**< The password was successfully changed. */
|
||||
Wrong, /**< The current password entered was wrong. */
|
||||
Other, /**< An unknown problem occurred. */
|
||||
};
|
||||
Q_ENUM(PasswordStatus)
|
||||
|
||||
void setActiveConnection(NeoChatConnection *connection);
|
||||
[[nodiscard]] NeoChatConnection *activeConnection() const;
|
||||
static Controller &instance();
|
||||
|
||||
[[nodiscard]] int accountCount() const;
|
||||
|
||||
void setActiveConnection(Quotient::Connection *connection);
|
||||
[[nodiscard]] Quotient::Connection *activeConnection() const;
|
||||
|
||||
[[nodiscard]] PushRuleModel *pushRuleModel() const;
|
||||
|
||||
/**
|
||||
* @brief Add a new connection to the account registry.
|
||||
*/
|
||||
void addConnection(NeoChatConnection *c);
|
||||
void addConnection(Quotient::Connection *c);
|
||||
|
||||
/**
|
||||
* @brief Drop a connection from the account registry.
|
||||
*/
|
||||
void dropConnection(NeoChatConnection *c);
|
||||
void dropConnection(Quotient::Connection *c);
|
||||
|
||||
int activeConnectionIndex() const;
|
||||
|
||||
[[nodiscard]] QString activeAccountLabel() const;
|
||||
void setActiveAccountLabel(const QString &label);
|
||||
|
||||
/**
|
||||
* @brief Save an access token to the keychain for the given account.
|
||||
*/
|
||||
bool saveAccessTokenToKeyChain(const Quotient::AccountSettings &account, const QByteArray &accessToken);
|
||||
|
||||
/**
|
||||
* @brief Change the password for an account.
|
||||
*
|
||||
* The function emits a passwordStatus signal with a PasswordStatus value when
|
||||
* complete.
|
||||
*
|
||||
* @sa PasswordStatus, passwordStatus
|
||||
*/
|
||||
Q_INVOKABLE void changePassword(Quotient::Connection *connection, const QString ¤tPassword, const QString &newPassword);
|
||||
|
||||
/**
|
||||
* @brief Change the avatar for an account.
|
||||
*/
|
||||
Q_INVOKABLE bool setAvatar(Quotient::Connection *connection, const QUrl &avatarSource);
|
||||
|
||||
/**
|
||||
* @brief Create new room for a group chat.
|
||||
*/
|
||||
Q_INVOKABLE void createRoom(const QString &name, const QString &topic);
|
||||
|
||||
/**
|
||||
* @brief Create new space.
|
||||
*/
|
||||
Q_INVOKABLE void createSpace(const QString &name, const QString &topic);
|
||||
|
||||
/**
|
||||
* @brief Join a room.
|
||||
*/
|
||||
Q_INVOKABLE void joinRoom(const QString &alias);
|
||||
|
||||
/**
|
||||
* @brief Join a direct chat with the given user.
|
||||
*
|
||||
* If a direct chat with the user doesn't exist one is created and then joined.
|
||||
*/
|
||||
Q_INVOKABLE void openOrCreateDirectChat(NeoChatUser *user);
|
||||
|
||||
[[nodiscard]] bool supportSystemTray() const;
|
||||
|
||||
/**
|
||||
* @brief Set the background blur status of the given item.
|
||||
*/
|
||||
Q_INVOKABLE void setBlur(QQuickItem *item, bool blur);
|
||||
|
||||
bool isOnline() const;
|
||||
|
||||
bool encryptionSupported() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the QNetworkProxy for the application.
|
||||
*
|
||||
@@ -91,8 +203,24 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE void setApplicationProxy();
|
||||
|
||||
int quotientMinorVersion() const;
|
||||
|
||||
bool isFlatpak() const;
|
||||
|
||||
/**
|
||||
* @brief Return a string for the input timestamp.
|
||||
*
|
||||
* The output format depends on the KFormat::DurationFormatOptions chosen.
|
||||
*
|
||||
* @sa KFormat::DurationFormatOptions
|
||||
*/
|
||||
Q_INVOKABLE QString formatDuration(quint64 msecs, KFormat::DurationFormatOptions options = KFormat::DefaultDuration) const;
|
||||
|
||||
/**
|
||||
* @brief Return a human readable string for a given input number of bytes.
|
||||
*/
|
||||
Q_INVOKABLE QString formatByteSize(double size, int precision = 1) const;
|
||||
|
||||
/**
|
||||
* @brief Force a QQuickTextDocument to refresh when images are loaded.
|
||||
*
|
||||
@@ -100,43 +228,76 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE void forceRefreshTextDocument(QQuickTextDocument *textDocument, QQuickItem *item);
|
||||
|
||||
/**
|
||||
* @brief Start listening for notifications in dbus-activated mode.
|
||||
* These notifications will quit the application when closed.
|
||||
*/
|
||||
static void listenForNotifications();
|
||||
Q_INVOKABLE QVariantList getSupportedRoomVersions(Quotient::Connection *connection);
|
||||
|
||||
Quotient::AccountRegistry &accounts();
|
||||
|
||||
static void setTestMode(bool testMode);
|
||||
|
||||
private:
|
||||
explicit Controller(QObject *parent = nullptr);
|
||||
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
QPointer<Quotient::Connection> m_connection;
|
||||
TrayIcon *m_trayIcon = nullptr;
|
||||
|
||||
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const Quotient::AccountSettings &account);
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings() const;
|
||||
bool m_isOnline = true;
|
||||
QMap<Quotient::Room *, int> m_notificationCounts;
|
||||
|
||||
bool hasWindowSystem() const;
|
||||
|
||||
QPointer<PushRuleModel> m_pushRuleModel;
|
||||
Quotient::AccountRegistry m_accountRegistry;
|
||||
QStringList m_accountsLoading;
|
||||
QString m_endpoint;
|
||||
|
||||
private Q_SLOTS:
|
||||
void invokeLogin();
|
||||
void showWindow();
|
||||
void setQuitOnLastWindowClosed();
|
||||
|
||||
Q_SIGNALS:
|
||||
void errorOccured(const QString &error, const QString &detail);
|
||||
void connectionAdded(NeoChatConnection *connection);
|
||||
void connectionDropped(NeoChatConnection *connection);
|
||||
/// Error occurred because of user inputs
|
||||
void errorOccured(const QString &error);
|
||||
|
||||
/// Error occurred because of server or bug in NeoChat
|
||||
void globalErrorOccured(QString error, QString detail);
|
||||
void syncDone();
|
||||
void connectionAdded(Quotient::Connection *_t1);
|
||||
void connectionDropped(Quotient::Connection *_t1);
|
||||
void accountCountChanged();
|
||||
void initiated();
|
||||
void notificationClicked(const QString &_t1, const QString &_t2);
|
||||
void quitOnLastWindowClosedChanged();
|
||||
void unreadCountChanged();
|
||||
void activeConnectionChanged();
|
||||
void passwordStatus(Controller::PasswordStatus _t1);
|
||||
void userConsentRequired(QUrl url);
|
||||
void accountsLoadingChanged();
|
||||
void testConnectionResult(const QString &connection, bool usable);
|
||||
void isOnlineChanged(bool isOnline);
|
||||
void keyVerificationRequest(int timeLeft, Quotient::Connection *connection, const QString &transactionId, const QString &deviceId);
|
||||
void keyVerificationStart();
|
||||
void keyVerificationAccept(const QString &commitment);
|
||||
void keyVerificationKey(const QString &sas);
|
||||
void activeConnectionIndexChanged();
|
||||
void roomAdded(NeoChatRoom *room);
|
||||
void activeAccountLabelChanged();
|
||||
|
||||
public Q_SLOTS:
|
||||
void logout(Quotient::Connection *conn, bool serverSideLogout);
|
||||
void changeAvatar(Quotient::Connection *conn, const QUrl &localFile);
|
||||
static void markAllMessagesAsRead(Quotient::Connection *conn);
|
||||
void saveWindowGeometry();
|
||||
};
|
||||
|
||||
// TODO libQuotient 0.7: Drop
|
||||
class NeochatChangePasswordJob : public Quotient::BaseJob
|
||||
{
|
||||
public:
|
||||
explicit NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const Quotient::Omittable<QJsonObject> &auth = Quotient::none);
|
||||
};
|
||||
|
||||
class NeochatDeleteDeviceJob : public Quotient::BaseJob
|
||||
{
|
||||
public:
|
||||
explicit NeochatDeleteDeviceJob(const QString &deviceId, const Quotient::Omittable<QJsonObject> &auth = Quotient::none);
|
||||
};
|
||||
|
||||
@@ -124,7 +124,7 @@ int DelegateSizeHelper::calculateCurrentPercentageWidth() const
|
||||
int maxPercentWidth = endPercentBigger ? m_endPercentWidth : m_startPercentWidth;
|
||||
int minPercentWidth = endPercentBigger ? m_startPercentWidth : m_endPercentWidth;
|
||||
|
||||
int calcPercentWidth = std::round(m * m_parentWidth + c);
|
||||
int calcPercentWidth = std::ceil(m * m_parentWidth + c);
|
||||
return std::clamp(calcPercentWidth, minPercentWidth, maxPercentWidth);
|
||||
}
|
||||
|
||||
@@ -146,9 +146,9 @@ qreal DelegateSizeHelper::currentWidth() const
|
||||
|
||||
qreal absoluteWidth = m_parentWidth * percentWidth * 0.01;
|
||||
if (m_maxWidth < 0.0) {
|
||||
return std::round(absoluteWidth);
|
||||
return std::ceil(absoluteWidth);
|
||||
} else {
|
||||
return std::round(std::min(absoluteWidth, m_maxWidth));
|
||||
return std::ceil(std::min(absoluteWidth, m_maxWidth));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
/**
|
||||
* @class DelegateSizeHelper
|
||||
@@ -24,7 +23,6 @@
|
||||
class DelegateSizeHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
/**
|
||||
* @brief The width of the component's parent.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,48 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
/**
|
||||
* @class DelegateType
|
||||
*
|
||||
* This class is designed to define the DelegateType enumeration.
|
||||
*/
|
||||
class DelegateType : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief The type of delegate that is needed for the event.
|
||||
*
|
||||
* @note While similar this is not the matrix event or message type. This is
|
||||
* to tell a QML ListView what delegate to show for each event. So while
|
||||
* similar to the spec it is not the same.
|
||||
*/
|
||||
enum Type {
|
||||
Emote, /**< A message that begins with /me. */
|
||||
Notice, /**< A notice event. */
|
||||
Image, /**< A message that is an image. */
|
||||
Audio, /**< A message that is an audio recording. */
|
||||
Video, /**< A message that is a video. */
|
||||
File, /**< A message that is a file. */
|
||||
Message, /**< A text message. */
|
||||
Sticker, /**< A message that is a sticker. */
|
||||
State, /**< A state event in the room. */
|
||||
Encrypted, /**< An encrypted message that cannot be decrypted. */
|
||||
ReadMarker, /**< The local user read marker. */
|
||||
Poll, /**< The initial event for a poll. */
|
||||
Location, /**< A location event. */
|
||||
LiveLocation, /**< The initial event of a shared live location (i.e., the place where this is supposed to be shown in the timeline). */
|
||||
Loading, /**< A delegate to tell the user more messages are being loaded. */
|
||||
TimelineEnd, /**< A delegate to inform that all messages are loaded. */
|
||||
Other, /**< Anything that cannot be classified as another type. */
|
||||
};
|
||||
Q_ENUM(Type);
|
||||
};
|
||||
@@ -1,186 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
/**
|
||||
* @class PushRuleKind
|
||||
*
|
||||
* A class with the Kind enum for push notifications and helper functions.
|
||||
*
|
||||
* The kind relates to the kinds of push rule defined in the matrix spec, see
|
||||
* https://spec.matrix.org/v1.7/client-server-api/#push-rules for full details.
|
||||
*/
|
||||
class PushRuleKind : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the different kinds of push rule.
|
||||
*/
|
||||
enum Kind {
|
||||
Override = 0, /**< The highest priority rules. */
|
||||
Content, /**< These configure behaviour for messages that match certain patterns. */
|
||||
Room, /**< These rules change the behaviour of all messages for a given room. */
|
||||
Sender, /**< These rules configure notification behaviour for messages from a specific Matrix user ID. */
|
||||
Underride, /**< These are identical to override rules, but have a lower priority than content, room and sender rules. */
|
||||
};
|
||||
Q_ENUM(Kind)
|
||||
|
||||
/**
|
||||
* @brief Translate the Kind enum value to a human readable string.
|
||||
*
|
||||
* @sa Kind
|
||||
*/
|
||||
static QString kindString(Kind kind)
|
||||
{
|
||||
switch (kind) {
|
||||
case Kind::Override:
|
||||
return QLatin1String("override");
|
||||
case Kind::Content:
|
||||
return QLatin1String("content");
|
||||
case Kind::Room:
|
||||
return QLatin1String("room");
|
||||
case Kind::Sender:
|
||||
return QLatin1String("sender");
|
||||
case Kind::Underride:
|
||||
return QLatin1String("underride");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @class PushRuleAction
|
||||
*
|
||||
* A class with the Action enum for push notifications.
|
||||
*
|
||||
* The action relates to the actions of push rule defined in the matrix spec, see
|
||||
* https://spec.matrix.org/v1.7/client-server-api/#push-rules for full details.
|
||||
*/
|
||||
class PushRuleAction : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the global push notification actions.
|
||||
*/
|
||||
enum Action {
|
||||
Unknown = 0, /**< The action has not yet been obtained from the server. */
|
||||
Off, /**< No push notifications are to be sent. */
|
||||
On, /**< Push notifications are on. */
|
||||
Noisy, /**< Push notifications are on, also trigger a notification sound. */
|
||||
Highlight, /**< Push notifications are on, also the event should be highlighted in chat. */
|
||||
NoisyHighlight, /**< Push notifications are on, also trigger a notification sound and highlight in chat. */
|
||||
};
|
||||
Q_ENUM(Action)
|
||||
};
|
||||
|
||||
/**
|
||||
* @class PushNotificationState
|
||||
*
|
||||
* A class with the State enum for room push notification state.
|
||||
*
|
||||
* The state define whether the room adheres to the global push rule states for the
|
||||
* account or is overridden for a room.
|
||||
*
|
||||
* @note This is different to the PushRuleAction which defines the type of notification
|
||||
* for an individual rule.
|
||||
*
|
||||
* @sa PushRuleAction
|
||||
*/
|
||||
class PushNotificationState : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Describes the push notification state for the room.
|
||||
*/
|
||||
enum State {
|
||||
Unknown, /**< The state has not yet been obtained from the server. */
|
||||
Default, /**< The room follows the globally configured rules for the local user. */
|
||||
Mute, /**< No notifications for messages in the room. */
|
||||
MentionKeyword, /**< Notifications only for local user mentions and keywords. */
|
||||
All, /**< Notifications for all messages. */
|
||||
};
|
||||
Q_ENUM(State)
|
||||
};
|
||||
|
||||
/**
|
||||
* @class PushRuleSection
|
||||
*
|
||||
* A class with the Section enum for push notifications and helper functions.
|
||||
*
|
||||
* @note This is different from the PushRuleKind and instead is used for sorting
|
||||
* in the settings page which is not necessarily by Kind.
|
||||
*
|
||||
* @sa PushRuleKind
|
||||
*/
|
||||
class PushRuleSection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the sections to sort push rules into.
|
||||
*/
|
||||
enum Section {
|
||||
Master = 0, /**< The master push rule */
|
||||
Room, /**< Push rules relating to all rooms. */
|
||||
Mentions, /**< Push rules relating to user mentions. */
|
||||
Keywords, /**< Global Keyword push rules. */
|
||||
RoomKeywords, /**< Keyword push rules that only apply to a specific room. */
|
||||
Invites, /**< Push rules relating to invites. */
|
||||
Unknown, /**< New default push rules that have not been added to the model yet. */
|
||||
/**
|
||||
* @brief Push rules that should never be shown.
|
||||
*
|
||||
* There are numerous rules that get set that shouldn't be shown in the general
|
||||
* list e.g. The array of rules used to override global settings in individual
|
||||
* rooms.
|
||||
*
|
||||
* This is specifically different to unknown which are just new default push
|
||||
* rule that haven't been added to the model yet.
|
||||
*/
|
||||
Undefined,
|
||||
};
|
||||
Q_ENUM(Section)
|
||||
|
||||
/**
|
||||
* @brief Translate the Section enum value to a human readable string.
|
||||
*
|
||||
* @sa Section
|
||||
*/
|
||||
static QString sectionString(Section section)
|
||||
{
|
||||
switch (section) {
|
||||
case Section::Master:
|
||||
return QLatin1String("Master");
|
||||
case Section::Room:
|
||||
return QLatin1String("Room Notifications");
|
||||
case Section::Mentions:
|
||||
return QLatin1String("@Mentions");
|
||||
case Section::Keywords:
|
||||
return QLatin1String("Keywords");
|
||||
case Section::Invites:
|
||||
return QLatin1String("Invites");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
};
|
||||
};
|
||||
1097
src/eventhandler.cpp
1097
src/eventhandler.cpp
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user