Compare commits
1 Commits
v24.01.80
...
work/carl/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29cc585b06 |
10
.craft.ini
10
.craft.ini
@@ -1,10 +0,0 @@
|
||||
; SPDX-FileCopyrightText: None
|
||||
; 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
|
||||
@@ -19,7 +19,6 @@
|
||||
"--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": [
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
include:
|
||||
- project: sysadmin/ci-utilities
|
||||
file:
|
||||
- /gitlab-templates/reuse-lint.yml
|
||||
- /gitlab-templates/android-qt6.yml
|
||||
- /gitlab-templates/linux-qt6.yml
|
||||
- /gitlab-templates/windows-qt6.yml
|
||||
- /gitlab-templates/freebsd-qt6.yml
|
||||
# - /gitlab-templates/flatpak.yml
|
||||
- /gitlab-templates/craft-android-qt6-apks.yml
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/reuse-lint.yml
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android.yml
|
||||
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android-qt6.yml
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows.yml
|
||||
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows-qt6.yml
|
||||
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd.yml
|
||||
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd-qt6.yml
|
||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/flatpak.yml
|
||||
|
||||
43
.kde-ci.yml
43
.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,28 @@ 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'
|
||||
'frameworks/kstatusnotifieritem': '@latest-kf6'
|
||||
|
||||
- 'on': ['Linux']
|
||||
- 'on': ['Linux/Qt6', 'Linux/Qt5']
|
||||
'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', 'Windows' ]
|
||||
|
||||
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
|
||||
|
||||
100
CMakeLists.txt
100
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 "80")
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "23")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "11")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||
|
||||
set(KF_MIN_VERSION "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)
|
||||
|
||||
@@ -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,25 @@ 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 WebView)
|
||||
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 SyntaxHighlighting)
|
||||
set_package_properties(KF${QT_MAJOR_VERSION} PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
)
|
||||
set_package_properties(KF6Kirigami PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Kirigami application UI framework"
|
||||
)
|
||||
find_package(KF6KirigamiAddons 0.7.2 REQUIRED)
|
||||
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)
|
||||
|
||||
if(QT_MAJOR_VERSION STREQUAL "6")
|
||||
find_package(KF6StatusNotifierItem ${KF_MIN_VERSION} REQUIRED)
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
find_package(OpenSSL)
|
||||
@@ -76,26 +104,24 @@ 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/"
|
||||
@@ -118,8 +144,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,23 +154,16 @@ 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(ANDROID)
|
||||
find_package(Sqlite3)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/android/version.gradle.in ${CMAKE_BINARY_DIR}/version.gradle)
|
||||
@@ -163,12 +181,12 @@ add_definitions(-DQT_NO_FOREACH)
|
||||
add_subdirectory(src)
|
||||
|
||||
if (BUILD_TESTING)
|
||||
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test)
|
||||
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()
|
||||
|
||||
@@ -11,7 +11,6 @@ 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://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" android:usesCleartextTraffic="true">
|
||||
<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']
|
||||
|
||||
@@ -21,8 +21,3 @@ 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,11 +1,9 @@
|
||||
# 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__)
|
||||
|
||||
from flask import Flask, request, abort
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/_matrix/client/v3/login", methods=["GET"])
|
||||
def login_get():
|
||||
@@ -14,13 +12,6 @@ def login_get():
|
||||
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()
|
||||
@@ -28,22 +19,15 @@ def login_post():
|
||||
abort(403)
|
||||
print(data)
|
||||
result = dict()
|
||||
result["access_token"] = "token_login"
|
||||
result["access_token"] = "token_1234"
|
||||
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")
|
||||
result = dict()
|
||||
result["next_batch"] = "batch1234"
|
||||
return result
|
||||
|
||||
@app.route("/.well-known/matrix/client")
|
||||
@@ -53,18 +37,6 @@ def well_known():
|
||||
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)
|
||||
|
||||
@@ -4,26 +4,25 @@
|
||||
# SPDX-FileCopyrightText: 2021-2022 Harald Sitter <sitter@kde.org>
|
||||
# SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
|
||||
import unittest
|
||||
from appium import webdriver
|
||||
from appium.webdriver.common.appiumby import AppiumBy
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
import os
|
||||
import time
|
||||
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 setUpClass(self):
|
||||
desired_caps = {}
|
||||
desired_caps["app"] = "neochat --ignore-ssl-errors"
|
||||
desired_caps["timeouts"] = {'implicit': 10000}
|
||||
self.driver = webdriver.Remote(
|
||||
command_executor='http://127.0.0.1:4723',
|
||||
desired_capabilities=desired_caps)
|
||||
self.mockServerProcess = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), "login-server.py")])
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
@@ -46,6 +45,5 @@ class LoginTest(unittest.TestCase):
|
||||
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
|
||||
@@ -34,15 +32,3 @@ ecm_add_test(
|
||||
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
|
||||
)
|
||||
|
||||
@@ -1,157 +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"
|
||||
|
||||
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 ChatBarCacheTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
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 TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||
|
||||
QFile testMinSyncFile;
|
||||
testMinSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-min-sync.json"));
|
||||
testMinSyncFile.open(QIODevice::ReadOnly);
|
||||
const auto testMinSyncJson = QJsonDocument::fromJson(testMinSyncFile.readAll());
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testMinSyncJson.object());
|
||||
room->update(std::move(roomData));
|
||||
}
|
||||
|
||||
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,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,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"
|
||||
}
|
||||
}
|
||||
@@ -39,63 +39,33 @@ private:
|
||||
Connection *connection = nullptr;
|
||||
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 mediaInfo();
|
||||
void nullMediaInfo();
|
||||
void linkPreviewer();
|
||||
void nullLinkPreviewer();
|
||||
void reactions();
|
||||
void nullReactions();
|
||||
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()
|
||||
@@ -103,21 +73,332 @@ void EventHandlerTest::initTestCase()
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||
|
||||
QFile testEventHandlerSyncFile;
|
||||
testEventHandlerSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-eventhandler-sync.json"));
|
||||
testEventHandlerSyncFile.open(QIODevice::ReadOnly);
|
||||
const auto testEventHandlerSyncJson = QJsonDocument::fromJson(testEventHandlerSyncFile.readAll());
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testEventHandlerSyncJson.object());
|
||||
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"
|
||||
},
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"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
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
}
|
||||
})EVENT");
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object());
|
||||
room->update(std::move(roomData));
|
||||
|
||||
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()
|
||||
@@ -127,12 +408,6 @@ void EventHandlerTest::eventId()
|
||||
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");
|
||||
@@ -156,12 +431,6 @@ void EventHandlerTest::delegateType()
|
||||
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();
|
||||
@@ -179,15 +448,6 @@ void EventHandlerTest::author()
|
||||
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();
|
||||
@@ -196,32 +456,6 @@ void EventHandlerTest::authorDisplayName()
|
||||
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();
|
||||
@@ -231,16 +465,6 @@ void EventHandlerTest::time()
|
||||
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();
|
||||
@@ -262,16 +486,6 @@ void EventHandlerTest::timeString()
|
||||
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();
|
||||
@@ -285,15 +499,6 @@ void EventHandlerTest::highlighted()
|
||||
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();
|
||||
@@ -307,15 +512,6 @@ void EventHandlerTest::hidden()
|
||||
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();
|
||||
@@ -327,15 +523,6 @@ void EventHandlerTest::body()
|
||||
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");
|
||||
@@ -358,12 +545,6 @@ void EventHandlerTest::genericBody()
|
||||
QCOMPARE(eventHandler.getGenericBody(), output);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullGenericBody()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getGenericBody called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getGenericBody(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::mediaInfo()
|
||||
{
|
||||
auto event = room->messageEvents().at(4).get();
|
||||
@@ -387,15 +568,6 @@ void EventHandlerTest::mediaInfo()
|
||||
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();
|
||||
@@ -409,15 +581,6 @@ void EventHandlerTest::linkPreviewer()
|
||||
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::reactions()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
@@ -426,15 +589,6 @@ void EventHandlerTest::reactions()
|
||||
QCOMPARE(eventHandler.getReactions()->rowCount(), 1);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReactions()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReactions called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReactions(), nullptr);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReactions called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReactions(), nullptr);
|
||||
}
|
||||
|
||||
void EventHandlerTest::hasReply()
|
||||
{
|
||||
auto event = room->messageEvents().at(5).get();
|
||||
@@ -448,12 +602,6 @@ void EventHandlerTest::hasReply()
|
||||
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();
|
||||
@@ -467,12 +615,6 @@ void EventHandlerTest::replyId()
|
||||
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();
|
||||
@@ -486,15 +628,6 @@ void EventHandlerTest::replyDelegateType()
|
||||
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();
|
||||
@@ -518,15 +651,6 @@ void EventHandlerTest::replyAuthor()
|
||||
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();
|
||||
@@ -538,15 +662,6 @@ void EventHandlerTest::replyBody()
|
||||
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();
|
||||
@@ -571,47 +686,6 @@ void EventHandlerTest::replyMediaInfo()
|
||||
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();
|
||||
@@ -622,18 +696,6 @@ void EventHandlerTest::location()
|
||||
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();
|
||||
@@ -663,37 +725,5 @@ void EventHandlerTest::readMarkers()
|
||||
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"
|
||||
|
||||
@@ -42,18 +42,101 @@ void NeoChatRoomTest::initTestCase()
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||
|
||||
QFile testMinSyncFile;
|
||||
testMinSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-min-sync.json"));
|
||||
testMinSyncFile.open(QIODevice::ReadOnly);
|
||||
const auto testMinSyncJson = QJsonDocument::fromJson(testMinSyncFile.readAll());
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testMinSyncJson.object());
|
||||
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\ntext message"));
|
||||
QCOMPARE(room->lastEventToString(), QStringLiteral("@example:example.org: This is an example text message"));
|
||||
}
|
||||
|
||||
void NeoChatRoomTest::eventTest()
|
||||
|
||||
@@ -40,7 +40,6 @@ private Q_SLOTS:
|
||||
void stripDisallowedTags();
|
||||
void stripDisallowedAttributes();
|
||||
void emptyCodeTags();
|
||||
void formatBlockQuote();
|
||||
|
||||
void sendSimpleStringCase();
|
||||
void sendSingleParaMarkup();
|
||||
@@ -67,7 +66,6 @@ private Q_SLOTS:
|
||||
void receiveRichEdited_data();
|
||||
void receiveRichEdited();
|
||||
void receiveLineSeparator();
|
||||
void receiveRichCodeUrl();
|
||||
|
||||
void linkPreviewsMatch_data();
|
||||
void linkPreviewsMatch();
|
||||
@@ -80,11 +78,131 @@ void TextHandlerTest::initTestCase()
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||
|
||||
QFile testTextHandlerSyncFile;
|
||||
testTextHandlerSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-texthandler-sync.json"));
|
||||
testTextHandlerSyncFile.open(QIODevice::ReadOnly);
|
||||
const auto testTextHandlerSyncJson = QJsonDocument::fromJson(testTextHandlerSyncFile.readAll());
|
||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testTextHandlerSyncJson.object());
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -147,16 +265,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.");
|
||||
@@ -469,9 +577,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()
|
||||
@@ -540,13 +647,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)
|
||||
#include "texthandlertest.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>
|
||||
@@ -54,16 +53,13 @@
|
||||
<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>
|
||||
@@ -99,7 +95,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<p>NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
|
||||
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. على هذا النحو يتم دعم كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP والخيوط وبعض جوانب التشفير من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار ، ولكن يبقى الهدف توفير الدعم النهائي للمواصفات بأكملها.</p>
|
||||
<p xml:lang="ca">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptatge d'extrem a extrem. Hi ha algunes altres omissions més petites a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu segueix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="en-GB">NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
|
||||
<p xml:lang="eo">NeoChat celas esti plene kapabla aplikaĵo por la Matrix-specifo. Kiel tia, ĉio en la nuna stabila specifo kun la rimarkindaj esceptoj de VoIP, fadenoj kaj kelkaj aspektoj de Fin-al-Fina Ĉifrado estas subtenataj. Estas kelkaj aliaj pli malgrandaj preterlasoj pro la fakto, ke la Matrix-speco konstante evoluas, sed la celo restas provizi finfine subtenon por la tuta specifaĵo.</p>
|
||||
<p xml:lang="es">NeoChat pretende ser una aplicación con todas las funciones para la especificación de Matrix. Como tal, admite todo en la especificación estable actual, con las notables excepciones de VoIP, subprocesos y algunas funciones de cifrado de extremo a extremo. Existen algunas omisiones menos importantes debido al hecho de que la especificación de Matrix está en constante evolución, pero el objetivo sigue siendo brindar compatibilidad final con toda la especificación.</p>
|
||||
@@ -284,7 +280,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>
|
||||
@@ -296,7 +292,6 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<caption xml:lang="fi">Päänäkymä, jossa huoneluettelo, keskustelu ja huoneen tiedot</caption>
|
||||
<caption xml:lang="fr">Vue principale avec la liste des salons ainsi que des informations sur les salons et forums de discussions</caption>
|
||||
<caption xml:lang="gl">Vista principal coa lista de salas, a charla, e información da sala.</caption>
|
||||
<caption xml:lang="ia">Vista principal con lista de sala, chat e information de sala</caption>
|
||||
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
|
||||
<caption xml:lang="ka">მთავარი ხედი სურათების სიით, ჩატით და ოთახის ინფორმაციით</caption>
|
||||
<caption xml:lang="ko">대화방 목록, 채팅, 대화방 정보가 표시된 주 보기</caption>
|
||||
@@ -311,7 +306,7 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<caption xml:lang="uk">Головна панель із списком кімнат, спілкуванням та даними щодо кімнати</caption>
|
||||
<caption xml:lang="x-test">xxMain view with room list, chat, and room informationxx</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>
|
||||
@@ -323,7 +318,6 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<caption xml:lang="fi">Kirjautumisnäkymä</caption>
|
||||
<caption xml:lang="fr">Écran de connexion</caption>
|
||||
<caption xml:lang="gl">Pantalla de identificación.</caption>
|
||||
<caption xml:lang="ia">Schermo de accesso</caption>
|
||||
<caption xml:lang="it">Schermata di accesso</caption>
|
||||
<caption xml:lang="ka">შესვლის ეკრანი</caption>
|
||||
<caption xml:lang="ko">로그인 화면</caption>
|
||||
@@ -343,8 +337,6 @@ to provide a convergent experience across multiple platforms.</p>
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release version="23.08.3" date="2023-11-09"/>
|
||||
<release version="23.08.2" date="2023-10-12"/>
|
||||
<release version="23.08.0" date="2023-08-24">
|
||||
<url>https://kde.org/announcements/gear/23.08.0/#neochathttpsappskdeorgneochat</url>
|
||||
<description>
|
||||
|
||||
@@ -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 |
6178
po/ar/neochat.po
6178
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
4499
po/ast/neochat.po
4499
po/ast/neochat.po
File diff suppressed because it is too large
Load Diff
6021
po/az/neochat.po
6021
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
|
||||
|
||||
5644
po/ca/neochat.po
5644
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
6013
po/cs/neochat.po
6013
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
5549
po/da/neochat.po
5549
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
5823
po/de/neochat.po
5823
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
5960
po/el/neochat.po
5960
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
6410
po/en_GB/neochat.po
6410
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
4555
po/eo/neochat.po
4555
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
5631
po/es/neochat.po
5631
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
5726
po/eu/neochat.po
5726
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
6150
po/fi/neochat.po
6150
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
5502
po/fr/neochat.po
5502
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
5959
po/hu/neochat.po
5959
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
6172
po/ia/neochat.po
6172
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
6402
po/id/neochat.po
6402
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
5856
po/ie/neochat.po
5856
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
5645
po/it/neochat.po
5645
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
4921
po/ja/neochat.po
4921
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
5563
po/ka/neochat.po
5563
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
5833
po/ko/neochat.po
5833
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
5852
po/lt/neochat.po
5852
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
5617
po/nl/neochat.po
5617
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
6216
po/nn/neochat.po
6216
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
5848
po/pa/neochat.po
5848
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
6177
po/pl/neochat.po
6177
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
5757
po/pt/neochat.po
5757
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
5922
po/pt_BR/neochat.po
5922
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
6357
po/ru/neochat.po
6357
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
5983
po/sk/neochat.po
5983
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
5609
po/sl/neochat.po
5609
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
5736
po/sv/neochat.po
5736
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
5651
po/ta/neochat.po
5651
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
6120
po/tok/neochat.po
6120
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
5742
po/tr/neochat.po
5742
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
5654
po/uk/neochat.po
5654
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
5578
po/zh_CN/neochat.po
5578
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
4921
po/zh_TW/neochat.po
4921
po/zh_TW/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,15 @@
|
||||
# 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/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
|
||||
@@ -40,12 +49,6 @@ add_library(neochat STATIC
|
||||
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 +64,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
|
||||
@@ -133,179 +136,9 @@ add_library(neochat STATIC
|
||||
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
|
||||
messageformatter.cpp
|
||||
)
|
||||
|
||||
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"
|
||||
@@ -322,15 +155,9 @@ ecm_qt_declare_logging_category(neochat
|
||||
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)
|
||||
@@ -349,46 +176,31 @@ 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)
|
||||
if(QT_MAJOR_VERSION STREQUAL "6")
|
||||
target_link_libraries(neochat PRIVATE KF6::StatusNotifierItem)
|
||||
endif()
|
||||
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 Qt::Xml 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 KF${QT_MAJOR_VERSION}::SyntaxHighlighting Quotient${QUOTIENT_SUFFIX} cmark::cmark QCoro::Core)
|
||||
|
||||
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||
|
||||
@@ -474,11 +286,9 @@ if(ANDROID)
|
||||
"preferences-desktop-notification"
|
||||
"computer-symbolic"
|
||||
"gps"
|
||||
"system-users-symbolic"
|
||||
)
|
||||
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()
|
||||
|
||||
@@ -486,27 +296,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()
|
||||
|
||||
|
||||
@@ -38,33 +38,45 @@ void ActionsHandler::setRoom(NeoChatRoom *room)
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
void ActionsHandler::handleMessageEvent(ChatBarCache *chatBarCache)
|
||||
void ActionsHandler::handleNewMessage()
|
||||
{
|
||||
if (!chatBarCache) {
|
||||
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(QUrl(path), m_room->chatBoxText().isEmpty() ? path.mid(path.lastIndexOf(u'/') + 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();
|
||||
});
|
||||
@@ -75,14 +87,14 @@ 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)?$"));
|
||||
@@ -122,7 +134,7 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, Cha
|
||||
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 = action.handle(handledText.mid(action.prefix.length() + 1).trimmed(), m_room);
|
||||
if (action.messageType.has_value()) {
|
||||
messageType = *action.messageType;
|
||||
}
|
||||
@@ -149,7 +161,7 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, Cha
|
||||
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() : QString());
|
||||
}
|
||||
|
||||
void ActionsHandler::checkEffects(const QString &text)
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
@@ -34,7 +32,6 @@ class NeoChatRoom;
|
||||
class ActionsHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
/**
|
||||
* @brief The room that messages will be sent to.
|
||||
@@ -52,15 +49,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();
|
||||
@@ -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,11 +4,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickTextDocument>
|
||||
#include <QTextCursor>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "models/completionmodel.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
@@ -60,7 +58,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,18 +93,13 @@ 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.
|
||||
*/
|
||||
@@ -113,6 +113,9 @@ class ChatDocumentHandler : public QObject
|
||||
public:
|
||||
explicit ChatDocumentHandler(QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] bool isEdit() const;
|
||||
void setIsEdit(bool edit);
|
||||
|
||||
[[nodiscard]] QQuickTextDocument *document() const;
|
||||
void setDocument(QQuickTextDocument *document);
|
||||
|
||||
@@ -128,11 +131,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 +143,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 +156,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 +175,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,10 +4,19 @@
|
||||
|
||||
#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>
|
||||
@@ -42,8 +51,6 @@
|
||||
#include "trayicon_sni.h"
|
||||
#endif
|
||||
|
||||
bool testMode = false;
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
Controller::Controller(QObject *parent)
|
||||
@@ -58,22 +65,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();
|
||||
Q_EMIT initiated();
|
||||
});
|
||||
}
|
||||
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();
|
||||
});
|
||||
|
||||
@@ -105,30 +101,16 @@ Controller::Controller(QObject *parent)
|
||||
connect(&m_accountRegistry, &AccountRegistry::accountCountChanged, this, [this]() {
|
||||
if (m_accountRegistry.size() > oldAccountCount) {
|
||||
auto connection = dynamic_cast<NeoChatConnection *>(m_accountRegistry.accounts()[m_accountRegistry.size() - 1]);
|
||||
connect(connection, &NeoChatConnection::syncDone, this, [this, connection]() {
|
||||
connect(connection, &NeoChatConnection::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()
|
||||
@@ -197,8 +179,6 @@ void Controller::invokeLogin()
|
||||
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;
|
||||
@@ -218,8 +198,6 @@ void Controller::invokeLogin()
|
||||
connect(connection, &NeoChatConnection::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, &NeoChatConnection::syncDone, this, &Controller::initiated);
|
||||
@@ -330,6 +308,9 @@ void Controller::setQuitOnLastWindowClosed()
|
||||
m_trayIcon = nullptr;
|
||||
}
|
||||
}
|
||||
QGuiApplication::setQuitOnLastWindowClosed(!NeoChatConfig::self()->systemTray());
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -353,6 +334,20 @@ void Controller::setActiveConnection(NeoChatConnection *connection)
|
||||
m_connection = connection;
|
||||
if (connection != nullptr) {
|
||||
NeoChatConfig::self()->setActiveConnection(connection->userId());
|
||||
connect(connection, &NeoChatConnection::networkError, this, [this]() {
|
||||
if (!m_isOnline) {
|
||||
return;
|
||||
}
|
||||
m_isOnline = false;
|
||||
Q_EMIT isOnlineChanged(false);
|
||||
});
|
||||
connect(connection, &NeoChatConnection::syncDone, this, [this] {
|
||||
if (m_isOnline) {
|
||||
return;
|
||||
}
|
||||
m_isOnline = true;
|
||||
Q_EMIT isOnlineChanged(true);
|
||||
});
|
||||
connect(connection, &NeoChatConnection::requestFailed, this, [](BaseJob *job) {
|
||||
if (dynamic_cast<DownloadFileJob *>(job) && job->jsonData()["errcode"_ls].toString() == "M_TOO_LARGE"_ls) {
|
||||
RoomManager::instance().warning(i18n("File too large to download."), i18n("Contact your matrix server administrator for support."));
|
||||
@@ -363,6 +358,12 @@ void Controller::setActiveConnection(NeoChatConnection *connection)
|
||||
}
|
||||
NeoChatConfig::self()->save();
|
||||
Q_EMIT activeConnectionChanged();
|
||||
Q_EMIT activeConnectionIndexChanged();
|
||||
}
|
||||
|
||||
PushRuleModel *Controller::pushRuleModel() const
|
||||
{
|
||||
return m_pushRuleModel;
|
||||
}
|
||||
|
||||
void Controller::saveWindowGeometry()
|
||||
@@ -370,25 +371,79 @@ void Controller::saveWindowGeometry()
|
||||
WindowController::instance().saveGeometry();
|
||||
}
|
||||
|
||||
bool Controller::isOnline() const
|
||||
{
|
||||
return m_isOnline;
|
||||
}
|
||||
|
||||
// TODO: Remove in favor of RoomManager::joinRoom
|
||||
void Controller::joinRoom(const QString &alias)
|
||||
{
|
||||
if (!alias.contains(":"_ls)) {
|
||||
Q_EMIT errorOccured(i18n("The room id you are trying to join is not valid"));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto knownServer = alias.mid(alias.indexOf(":"_ls) + 1);
|
||||
RoomManager::instance().joinRoom(m_connection, alias, QStringList{knownServer});
|
||||
}
|
||||
|
||||
void Controller::openOrCreateDirectChat(User *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
|
||||
}
|
||||
|
||||
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"));
|
||||
|
||||
connect(connector, &KUnifiedPush::Connector::messageReceived, [](const QByteArray &data) {
|
||||
NotificationsManager::instance().postPushNotification(data);
|
||||
});
|
||||
|
||||
connector->registerClient(i18n("Receiving push notifications"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Controller::setApplicationProxy()
|
||||
{
|
||||
NeoChatConfig *cfg = NeoChatConfig::self();
|
||||
@@ -419,6 +474,14 @@ 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();
|
||||
}
|
||||
|
||||
bool Controller::isFlatpak() const
|
||||
{
|
||||
#ifdef NEOCHAT_FLATPAK
|
||||
@@ -434,8 +497,3 @@ AccountRegistry &Controller::accounts()
|
||||
}
|
||||
|
||||
#include "moc_controller.cpp"
|
||||
|
||||
void Controller::setTestMode(bool test)
|
||||
{
|
||||
testMode = test;
|
||||
}
|
||||
|
||||
@@ -3,19 +3,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "models/pushrulemodel.h"
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickItem>
|
||||
|
||||
#include <KFormat>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/settings.h>
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
#include <kunifiedpush/connector.h>
|
||||
#endif
|
||||
|
||||
class NeoChatRoom;
|
||||
class TrayIcon;
|
||||
class QQuickTextDocument;
|
||||
@@ -42,19 +40,37 @@ class ReadPasswordJob;
|
||||
class Controller : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
/**
|
||||
* @brief The current connection for the rest of NeoChat to use.
|
||||
*/
|
||||
Q_PROPERTY(NeoChatConnection *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 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 NeoChat is running as a flatpak.
|
||||
*
|
||||
@@ -62,8 +78,6 @@ class Controller : public QObject
|
||||
*/
|
||||
Q_PROPERTY(bool isFlatpak READ isFlatpak CONSTANT)
|
||||
|
||||
Q_PROPERTY(QStringList accountsLoading MEMBER m_accountsLoading NOTIFY accountsLoadingChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the status after an attempt to change the password on an account.
|
||||
@@ -76,15 +90,12 @@ public:
|
||||
Q_ENUM(PasswordStatus)
|
||||
|
||||
static Controller &instance();
|
||||
static Controller *create(QQmlEngine *engine, QJSEngine *)
|
||||
{
|
||||
engine->setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
|
||||
return &instance();
|
||||
}
|
||||
|
||||
void setActiveConnection(NeoChatConnection *connection);
|
||||
[[nodiscard]] NeoChatConnection *activeConnection() const;
|
||||
|
||||
[[nodiscard]] PushRuleModel *pushRuleModel() const;
|
||||
|
||||
/**
|
||||
* @brief Add a new connection to the account registry.
|
||||
*/
|
||||
@@ -95,13 +106,34 @@ public:
|
||||
*/
|
||||
void dropConnection(NeoChatConnection *c);
|
||||
|
||||
int activeConnectionIndex() const;
|
||||
|
||||
/**
|
||||
* @brief Save an access token to the keychain for the given account.
|
||||
*/
|
||||
bool saveAccessTokenToKeyChain(const Quotient::AccountSettings &account, const QByteArray &accessToken);
|
||||
|
||||
/**
|
||||
* @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(Quotient::User *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;
|
||||
|
||||
/**
|
||||
* @brief Sets the QNetworkProxy for the application.
|
||||
*
|
||||
@@ -111,6 +143,20 @@ public:
|
||||
|
||||
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.
|
||||
*
|
||||
@@ -118,16 +164,8 @@ 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();
|
||||
|
||||
Quotient::AccountRegistry &accounts();
|
||||
|
||||
static void setTestMode(bool testMode);
|
||||
|
||||
private:
|
||||
explicit Controller(QObject *parent = nullptr);
|
||||
|
||||
@@ -138,11 +176,13 @@ private:
|
||||
|
||||
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();
|
||||
@@ -164,7 +204,8 @@ Q_SIGNALS:
|
||||
void activeConnectionChanged();
|
||||
void passwordStatus(Controller::PasswordStatus status);
|
||||
void userConsentRequired(QUrl url);
|
||||
void accountsLoadingChanged();
|
||||
void isOnlineChanged(bool isOnline);
|
||||
void activeConnectionIndexChanged();
|
||||
|
||||
public Q_SLOTS:
|
||||
void saveWindowGeometry();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -4,18 +4,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
/**
|
||||
* @class DelegateType
|
||||
*
|
||||
* This class is designed to define the DelegateType enumeration.
|
||||
*/
|
||||
class DelegateType : public QObject
|
||||
class DelegateType
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
Q_GADGET
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -40,8 +37,6 @@ public:
|
||||
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);
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include <Quotient/events/stickerevent.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
|
||||
#include "delegatetype.h"
|
||||
#include "eventhandler_logging.h"
|
||||
#include "events/pollevent.h"
|
||||
#include "linkpreviewer.h"
|
||||
@@ -51,10 +50,6 @@ const Quotient::Event *EventHandler::getEvent() const
|
||||
|
||||
void EventHandler::setEvent(const Quotient::RoomEvent *event)
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "cannot setEvent when m_room is set to nullptr.";
|
||||
return;
|
||||
}
|
||||
if (event == m_event) {
|
||||
return;
|
||||
}
|
||||
@@ -169,28 +164,6 @@ QString EventHandler::getAuthorDisplayName(bool isPending) const
|
||||
}
|
||||
}
|
||||
|
||||
QString EventHandler::singleLineAuthorDisplayname(bool isPending) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getAuthorDisplayName called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getAuthorDisplayName called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto author = isPending ? m_room->localUser() : m_room->user(m_event->senderId());
|
||||
auto displayName = m_room->htmlSafeMemberName(author->id());
|
||||
displayName.replace(QStringLiteral("<br>\n"), QStringLiteral(" "));
|
||||
displayName.replace(QStringLiteral("<br>"), QStringLiteral(" "));
|
||||
displayName.replace(QStringLiteral("<br />\n"), QStringLiteral(" "));
|
||||
displayName.replace(QStringLiteral("<br />"), QStringLiteral(" "));
|
||||
displayName.replace(u'\n', QStringLiteral(" "));
|
||||
displayName.replace(u'\u2028', QStringLiteral(" "));
|
||||
return displayName;
|
||||
}
|
||||
|
||||
QDateTime EventHandler::getTime(bool isPending, QDateTime lastUpdated) const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
@@ -208,7 +181,7 @@ QDateTime EventHandler::getTime(bool isPending, QDateTime lastUpdated) const
|
||||
QString EventHandler::getTimeString(bool relative, QLocale::FormatType format, bool isPending, QDateTime lastUpdated) const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getTimeString called with m_event set to nullptr.";
|
||||
qCWarning(EventHandling) << "getTime called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (isPending && lastUpdated == QDateTime()) {
|
||||
@@ -243,15 +216,6 @@ bool EventHandler::isHighlighted()
|
||||
|
||||
bool EventHandler::isHidden()
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "isHidden called with m_room set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "isHidden called with m_event set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_event->isStateEvent() && !NeoChatConfig::self()->showStateEvent()) {
|
||||
return true;
|
||||
}
|
||||
@@ -647,14 +611,6 @@ QString EventHandler::getGenericBody() const
|
||||
|
||||
QVariantMap EventHandler::getMediaInfo() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getMediaInfo called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getMediaInfo called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
return getMediaInfoForEvent(m_event);
|
||||
}
|
||||
|
||||
@@ -685,12 +641,12 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo
|
||||
QVariantMap mediaInfo;
|
||||
|
||||
// Get the mxc URL for the media.
|
||||
if (!fileInfo->url().isValid() || fileInfo->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
||||
if (!fileInfo->url().isValid() || eventId.isEmpty()) {
|
||||
mediaInfo["source"_ls] = QUrl();
|
||||
} else {
|
||||
QUrl source = m_room->makeMediaUrl(eventId, fileInfo->url());
|
||||
|
||||
if (source.isValid()) {
|
||||
if (source.isValid() && source.scheme() == QStringLiteral("mxc")) {
|
||||
mediaInfo["source"_ls] = source;
|
||||
} else {
|
||||
mediaInfo["source"_ls] = QUrl();
|
||||
@@ -767,14 +723,6 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo
|
||||
|
||||
QSharedPointer<LinkPreviewer> EventHandler::getLinkPreviewer() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getLinkPreviewer called with m_room set to nullptr.";
|
||||
return nullptr;
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getLinkPreviewer called with m_event set to nullptr.";
|
||||
return nullptr;
|
||||
}
|
||||
if (!m_event->is<RoomMessageEvent>()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -806,7 +754,7 @@ QSharedPointer<ReactionModel> EventHandler::getReactions() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReactions called with m_room set to nullptr.";
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReactions called with m_event set to nullptr.";
|
||||
@@ -858,33 +806,16 @@ QSharedPointer<ReactionModel> EventHandler::getReactions() const
|
||||
|
||||
bool EventHandler::hasReply() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "hasReply called with m_event set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
return !m_event->contentJson()["m.relates_to"_ls].toObject()["m.in_reply_to"_ls].toObject()["event_id"_ls].toString().isEmpty();
|
||||
}
|
||||
|
||||
QString EventHandler::getReplyId() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReplyId called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
return m_event->contentJson()["m.relates_to"_ls].toObject()["m.in_reply_to"_ls].toObject()["event_id"_ls].toString();
|
||||
}
|
||||
|
||||
DelegateType::Type EventHandler::getReplyDelegateType() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReplyDelegateType called with m_room set to nullptr.";
|
||||
return DelegateType::Other;
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReplyDelegateType called with m_event set to nullptr.";
|
||||
return DelegateType::Other;
|
||||
}
|
||||
|
||||
auto replyEvent = m_room->getReplyForEvent(*m_event);
|
||||
if (replyEvent == nullptr) {
|
||||
return DelegateType::Other;
|
||||
@@ -970,45 +901,8 @@ QVariantMap EventHandler::getReplyMediaInfo() const
|
||||
return getMediaInfoForEvent(replyPtr);
|
||||
}
|
||||
|
||||
bool EventHandler::isThreaded() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "isThreaded called with m_event set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return (m_event->contentPart<QJsonObject>("m.relates_to"_ls).contains("rel_type"_ls)
|
||||
&& m_event->contentPart<QJsonObject>("m.relates_to"_ls)["rel_type"_ls].toString() == "m.thread"_ls)
|
||||
|| (!m_event->unsignedPart<QJsonObject>("m.relations"_ls).isEmpty() && m_event->unsignedPart<QJsonObject>("m.relations"_ls).contains("m.thread"_ls));
|
||||
}
|
||||
|
||||
QString EventHandler::threadRoot() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "threadRoot called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get the thread root ID from m.relates_to if it exists.
|
||||
if (m_event->contentPart<QJsonObject>("m.relates_to"_ls).contains("rel_type"_ls)
|
||||
&& m_event->contentPart<QJsonObject>("m.relates_to"_ls)["rel_type"_ls].toString() == "m.thread"_ls) {
|
||||
return m_event->contentPart<QJsonObject>("m.relates_to"_ls)["event_id"_ls].toString();
|
||||
}
|
||||
// For thread root events they have an m.relations in the unsigned part with a m.thread object.
|
||||
// If so return the event ID as it is the root.
|
||||
if (!m_event->unsignedPart<QJsonObject>("m.relations"_ls).isEmpty() && m_event->unsignedPart<QJsonObject>("m.relations"_ls).contains("m.thread"_ls)) {
|
||||
return getId();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
float EventHandler::getLatitude() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getLatitude called with m_event set to nullptr.";
|
||||
return -100.0;
|
||||
}
|
||||
|
||||
const auto geoUri = m_event->contentJson()["geo_uri"_ls].toString();
|
||||
if (geoUri.isEmpty()) {
|
||||
return -100.0; // latitude runs from -90deg to +90deg so -100 is out of range.
|
||||
@@ -1019,11 +913,6 @@ float EventHandler::getLatitude() const
|
||||
|
||||
float EventHandler::getLongitude() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getLongitude called with m_event set to nullptr.";
|
||||
return -200.0;
|
||||
}
|
||||
|
||||
const auto geoUri = m_event->contentJson()["geo_uri"_ls].toString();
|
||||
if (geoUri.isEmpty()) {
|
||||
return -200.0; // longitude runs from -180deg to +180deg so -200 is out of range.
|
||||
@@ -1034,11 +923,6 @@ float EventHandler::getLongitude() const
|
||||
|
||||
QString EventHandler::getLocationAssetType() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getLocationAssetType called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto assetType = m_event->contentJson()["org.matrix.msc3488.asset"_ls].toObject()["type"_ls].toString();
|
||||
if (assetType.isEmpty()) {
|
||||
return {};
|
||||
@@ -1048,15 +932,6 @@ QString EventHandler::getLocationAssetType() const
|
||||
|
||||
bool EventHandler::hasReadMarkers() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "hasReadMarkers called with m_room set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "hasReadMarkers called with m_event set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
return userIds.size() > 0;
|
||||
@@ -1064,15 +939,6 @@ bool EventHandler::hasReadMarkers() const
|
||||
|
||||
QVariantList EventHandler::getReadMarkers(int maxMarkers) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkers called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkers called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds_temp = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds_temp.remove(m_room->localUser()->id());
|
||||
|
||||
@@ -1093,15 +959,6 @@ QVariantList EventHandler::getReadMarkers(int maxMarkers) const
|
||||
|
||||
QString EventHandler::getNumberExcessReadMarkers(int maxMarkers) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getNumberExcessReadMarkers called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getNumberExcessReadMarkers called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
|
||||
@@ -1114,15 +971,6 @@ QString EventHandler::getNumberExcessReadMarkers(int maxMarkers) const
|
||||
|
||||
QString EventHandler::getReadMarkersString() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkersString called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkersString called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
|
||||
@@ -1138,5 +986,3 @@ QString EventHandler::getReadMarkersString() const
|
||||
readMarkersString.chop(2);
|
||||
return readMarkersString;
|
||||
}
|
||||
|
||||
#include "moc_eventhandler.cpp"
|
||||
|
||||
@@ -111,17 +111,6 @@ public:
|
||||
*/
|
||||
QString getAuthorDisplayName(bool isPending = false) const;
|
||||
|
||||
/**
|
||||
* @brief Get the display name of the event author but with any newlines removed.
|
||||
*
|
||||
* Turns out you can put newlines in your display name so we need to handle that
|
||||
* primarily for the room list subtitle.
|
||||
*
|
||||
* @param isPending whether the event is pending as this cannot be derived from
|
||||
* just the event object.
|
||||
*/
|
||||
QString singleLineAuthorDisplayname(bool isPending = false) const;
|
||||
|
||||
/**
|
||||
* @brief Return a QDateTime object for the event timestamp.
|
||||
*/
|
||||
@@ -337,20 +326,6 @@ public:
|
||||
*/
|
||||
QVariantMap getReplyMediaInfo() const;
|
||||
|
||||
/**
|
||||
* @brief Whether the message is part of a thread.
|
||||
*
|
||||
* i.e. There is a rel_type of m.thread.
|
||||
*/
|
||||
bool isThreaded() const;
|
||||
|
||||
/**
|
||||
* @brief Return the Matrix ID of the thread's root message.
|
||||
*
|
||||
* Empty if this not part of a thread.
|
||||
*/
|
||||
QString threadRoot() const;
|
||||
|
||||
/**
|
||||
* @brief Return the latitude for the event.
|
||||
*
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QList>
|
||||
#include <QVector>
|
||||
#include <Quotient/events/eventcontent.h>
|
||||
#include <Quotient/events/stateevent.h>
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
*
|
||||
* @sa ImagePackImage
|
||||
*/
|
||||
QList<ImagePackEventContent::ImagePackImage> images;
|
||||
QVector<ImagePackEventContent::ImagePackImage> images;
|
||||
|
||||
explicit ImagePackEventContent(const QJsonObject &o);
|
||||
|
||||
|
||||
116
src/filetype.cpp
116
src/filetype.cpp
@@ -1,116 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com>
|
||||
// SPDX-License-Identifier: LicenseRef-KDE-Accepted-LGPL
|
||||
|
||||
#include "filetype.h"
|
||||
#include <QImageReader>
|
||||
#include <QMovie>
|
||||
|
||||
static QStringList byteArrayListToStringList(const QByteArrayList &byteArrayList)
|
||||
{
|
||||
QStringList stringList;
|
||||
for (const QByteArray &byteArray : byteArrayList) {
|
||||
stringList.append(QString::fromLocal8Bit(byteArray));
|
||||
}
|
||||
return stringList;
|
||||
}
|
||||
|
||||
class FileTypePrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(FileType)
|
||||
Q_DISABLE_COPY(FileTypePrivate)
|
||||
public:
|
||||
FileTypePrivate(FileType *qq);
|
||||
FileType *const q_ptr;
|
||||
QMimeDatabase mimetypeDatabase;
|
||||
QStringList supportedImageFormats = byteArrayListToStringList(QImageReader::supportedImageFormats());
|
||||
QStringList supportedAnimatedImageFormats = byteArrayListToStringList(QMovie::supportedFormats());
|
||||
};
|
||||
|
||||
FileTypePrivate::FileTypePrivate(FileType *qq)
|
||||
: q_ptr(qq)
|
||||
{
|
||||
}
|
||||
|
||||
FileType::FileType(QObject *parent)
|
||||
: QObject(parent)
|
||||
, d_ptr(new FileTypePrivate(this))
|
||||
{
|
||||
}
|
||||
|
||||
FileType::~FileType() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
QMimeType FileType::mimeTypeForName(const QString &nameOrAlias) const
|
||||
{
|
||||
Q_D(const FileType);
|
||||
return d->mimetypeDatabase.mimeTypeForName(nameOrAlias);
|
||||
}
|
||||
|
||||
QMimeType FileType::mimeTypeForFile(const QString &fileName, MatchMode mode) const
|
||||
{
|
||||
Q_D(const FileType);
|
||||
return d->mimetypeDatabase.mimeTypeForFile(fileName, static_cast<QMimeDatabase::MatchMode>(mode));
|
||||
}
|
||||
|
||||
QMimeType FileType::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const
|
||||
{
|
||||
Q_D(const FileType);
|
||||
return d->mimetypeDatabase.mimeTypeForFile(fileInfo, static_cast<QMimeDatabase::MatchMode>(mode));
|
||||
}
|
||||
|
||||
QList<QMimeType> FileType::mimeTypesForFileName(const QString &fileName) const
|
||||
{
|
||||
Q_D(const FileType);
|
||||
return d->mimetypeDatabase.mimeTypesForFileName(fileName);
|
||||
}
|
||||
|
||||
QMimeType FileType::mimeTypeForData(const QByteArray &data) const
|
||||
{
|
||||
Q_D(const FileType);
|
||||
return d->mimetypeDatabase.mimeTypeForData(data);
|
||||
}
|
||||
|
||||
QMimeType FileType::mimeTypeForData(QIODevice *device) const
|
||||
{
|
||||
Q_D(const FileType);
|
||||
return d->mimetypeDatabase.mimeTypeForData(device);
|
||||
}
|
||||
|
||||
QMimeType FileType::mimeTypeForUrl(const QUrl &url) const
|
||||
{
|
||||
Q_D(const FileType);
|
||||
return d->mimetypeDatabase.mimeTypeForUrl(url);
|
||||
}
|
||||
|
||||
QMimeType FileType::mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const
|
||||
{
|
||||
Q_D(const FileType);
|
||||
return d->mimetypeDatabase.mimeTypeForFileNameAndData(fileName, device);
|
||||
}
|
||||
|
||||
QMimeType FileType::mimeTypeForFileNameAndData(const QString &fileName, const QByteArray &data) const
|
||||
{
|
||||
Q_D(const FileType);
|
||||
return d->mimetypeDatabase.mimeTypeForFileNameAndData(fileName, data);
|
||||
}
|
||||
|
||||
QString FileType::suffixForFileName(const QString &fileName) const
|
||||
{
|
||||
Q_D(const FileType);
|
||||
return d->mimetypeDatabase.suffixForFileName(fileName);
|
||||
}
|
||||
|
||||
QStringList FileType::supportedImageFormats() const
|
||||
{
|
||||
Q_D(const FileType);
|
||||
return d->supportedImageFormats;
|
||||
}
|
||||
|
||||
QStringList FileType::supportedAnimatedImageFormats() const
|
||||
{
|
||||
Q_D(const FileType);
|
||||
return d->supportedAnimatedImageFormats;
|
||||
}
|
||||
|
||||
#include "moc_filetype.cpp"
|
||||
116
src/filetypesingleton.cpp
Normal file
116
src/filetypesingleton.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
// SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com>
|
||||
// SPDX-License-Identifier: LicenseRef-KDE-Accepted-LGPL
|
||||
|
||||
#include "filetypesingleton.h"
|
||||
#include <QImageReader>
|
||||
#include <QMovie>
|
||||
|
||||
static QStringList byteArrayListToStringList(const QByteArrayList &byteArrayList)
|
||||
{
|
||||
QStringList stringList;
|
||||
for (const QByteArray &byteArray : byteArrayList) {
|
||||
stringList.append(QString::fromLocal8Bit(byteArray));
|
||||
}
|
||||
return stringList;
|
||||
}
|
||||
|
||||
class FileTypeSingletonPrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(FileTypeSingleton)
|
||||
Q_DISABLE_COPY(FileTypeSingletonPrivate)
|
||||
public:
|
||||
FileTypeSingletonPrivate(FileTypeSingleton *qq);
|
||||
FileTypeSingleton *const q_ptr;
|
||||
QMimeDatabase mimetypeDatabase;
|
||||
QStringList supportedImageFormats = byteArrayListToStringList(QImageReader::supportedImageFormats());
|
||||
QStringList supportedAnimatedImageFormats = byteArrayListToStringList(QMovie::supportedFormats());
|
||||
};
|
||||
|
||||
FileTypeSingletonPrivate::FileTypeSingletonPrivate(FileTypeSingleton *qq)
|
||||
: q_ptr(qq)
|
||||
{
|
||||
}
|
||||
|
||||
FileTypeSingleton::FileTypeSingleton(QObject *parent)
|
||||
: QObject(parent)
|
||||
, d_ptr(new FileTypeSingletonPrivate(this))
|
||||
{
|
||||
}
|
||||
|
||||
FileTypeSingleton::~FileTypeSingleton() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
QMimeType FileTypeSingleton::mimeTypeForName(const QString &nameOrAlias) const
|
||||
{
|
||||
Q_D(const FileTypeSingleton);
|
||||
return d->mimetypeDatabase.mimeTypeForName(nameOrAlias);
|
||||
}
|
||||
|
||||
QMimeType FileTypeSingleton::mimeTypeForFile(const QString &fileName, MatchMode mode) const
|
||||
{
|
||||
Q_D(const FileTypeSingleton);
|
||||
return d->mimetypeDatabase.mimeTypeForFile(fileName, static_cast<QMimeDatabase::MatchMode>(mode));
|
||||
}
|
||||
|
||||
QMimeType FileTypeSingleton::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const
|
||||
{
|
||||
Q_D(const FileTypeSingleton);
|
||||
return d->mimetypeDatabase.mimeTypeForFile(fileInfo, static_cast<QMimeDatabase::MatchMode>(mode));
|
||||
}
|
||||
|
||||
QList<QMimeType> FileTypeSingleton::mimeTypesForFileName(const QString &fileName) const
|
||||
{
|
||||
Q_D(const FileTypeSingleton);
|
||||
return d->mimetypeDatabase.mimeTypesForFileName(fileName);
|
||||
}
|
||||
|
||||
QMimeType FileTypeSingleton::mimeTypeForData(const QByteArray &data) const
|
||||
{
|
||||
Q_D(const FileTypeSingleton);
|
||||
return d->mimetypeDatabase.mimeTypeForData(data);
|
||||
}
|
||||
|
||||
QMimeType FileTypeSingleton::mimeTypeForData(QIODevice *device) const
|
||||
{
|
||||
Q_D(const FileTypeSingleton);
|
||||
return d->mimetypeDatabase.mimeTypeForData(device);
|
||||
}
|
||||
|
||||
QMimeType FileTypeSingleton::mimeTypeForUrl(const QUrl &url) const
|
||||
{
|
||||
Q_D(const FileTypeSingleton);
|
||||
return d->mimetypeDatabase.mimeTypeForUrl(url);
|
||||
}
|
||||
|
||||
QMimeType FileTypeSingleton::mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const
|
||||
{
|
||||
Q_D(const FileTypeSingleton);
|
||||
return d->mimetypeDatabase.mimeTypeForFileNameAndData(fileName, device);
|
||||
}
|
||||
|
||||
QMimeType FileTypeSingleton::mimeTypeForFileNameAndData(const QString &fileName, const QByteArray &data) const
|
||||
{
|
||||
Q_D(const FileTypeSingleton);
|
||||
return d->mimetypeDatabase.mimeTypeForFileNameAndData(fileName, data);
|
||||
}
|
||||
|
||||
QString FileTypeSingleton::suffixForFileName(const QString &fileName) const
|
||||
{
|
||||
Q_D(const FileTypeSingleton);
|
||||
return d->mimetypeDatabase.suffixForFileName(fileName);
|
||||
}
|
||||
|
||||
QStringList FileTypeSingleton::supportedImageFormats() const
|
||||
{
|
||||
Q_D(const FileTypeSingleton);
|
||||
return d->supportedImageFormats;
|
||||
}
|
||||
|
||||
QStringList FileTypeSingleton::supportedAnimatedImageFormats() const
|
||||
{
|
||||
Q_D(const FileTypeSingleton);
|
||||
return d->supportedAnimatedImageFormats;
|
||||
}
|
||||
|
||||
#include "moc_filetypesingleton.cpp"
|
||||
@@ -8,10 +8,9 @@
|
||||
#include <QFileInfo>
|
||||
#include <QMimeDatabase>
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <qqml.h>
|
||||
|
||||
class FileTypePrivate;
|
||||
class FileTypeSingletonPrivate;
|
||||
|
||||
/**
|
||||
* @class FileTypeSingleton
|
||||
@@ -20,11 +19,9 @@ class FileTypePrivate;
|
||||
*
|
||||
* @sa QMimeDatabase
|
||||
*/
|
||||
class FileType : public QObject
|
||||
class FileTypeSingleton : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
/**
|
||||
* @brief List of supported image formats.
|
||||
@@ -40,9 +37,12 @@ class FileType : public QObject
|
||||
*/
|
||||
Q_PROPERTY(QStringList supportedAnimatedImageFormats READ supportedAnimatedImageFormats CONSTANT FINAL)
|
||||
|
||||
QML_NAMED_ELEMENT(FileType)
|
||||
QML_SINGLETON
|
||||
|
||||
public:
|
||||
explicit FileType(QObject *parent = nullptr);
|
||||
~FileType();
|
||||
explicit FileTypeSingleton(QObject *parent = nullptr);
|
||||
~FileTypeSingleton();
|
||||
|
||||
/**
|
||||
* @brief Returns a MIME type for nameOrAlias or an invalid one if none found.
|
||||
@@ -59,14 +59,14 @@ public:
|
||||
*
|
||||
* @sa QMimeDatabase
|
||||
*/
|
||||
Q_INVOKABLE QMimeType mimeTypeForFile(const QString &fileName, FileType::MatchMode mode = MatchDefault) const;
|
||||
Q_INVOKABLE QMimeType mimeTypeForFile(const QString &fileName, FileTypeSingleton::MatchMode mode = MatchDefault) const;
|
||||
|
||||
/**
|
||||
* @brief Returns a MIME type for fileInfo.
|
||||
*
|
||||
* @sa QMimeDatabase
|
||||
*/
|
||||
Q_INVOKABLE QMimeType mimeTypeForFile(const QFileInfo &fileInfo, FileType::MatchMode mode = MatchDefault) const;
|
||||
Q_INVOKABLE QMimeType mimeTypeForFile(const QFileInfo &fileInfo, FileTypeSingleton::MatchMode mode = MatchDefault) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the MIME types for the file name fileName.
|
||||
@@ -121,7 +121,9 @@ public:
|
||||
QStringList supportedAnimatedImageFormats() const;
|
||||
|
||||
private:
|
||||
const QScopedPointer<FileTypePrivate> d_ptr;
|
||||
Q_DECLARE_PRIVATE(FileType)
|
||||
Q_DISABLE_COPY(FileType)
|
||||
const QScopedPointer<FileTypeSingletonPrivate> d_ptr;
|
||||
Q_DECLARE_PRIVATE(FileTypeSingleton)
|
||||
Q_DISABLE_COPY(FileTypeSingleton)
|
||||
};
|
||||
|
||||
QML_DECLARE_TYPE(FileTypeSingleton)
|
||||
@@ -4,7 +4,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QUrl>
|
||||
|
||||
class NeoChatRoom;
|
||||
@@ -20,8 +19,6 @@ class NeoChatRoom;
|
||||
class LinkPreviewer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
/**
|
||||
* @brief The URL to get the preview for.
|
||||
*/
|
||||
|
||||
@@ -2,18 +2,14 @@
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
#pragma once
|
||||
|
||||
#include "linkpreviewer.h"
|
||||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QRectF>
|
||||
|
||||
/** Location related helper functions for QML. */
|
||||
class LocationHelper : public QObject
|
||||
class LocationHelper
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
Q_GADGET
|
||||
public:
|
||||
/** Unite two rectanlges. */
|
||||
Q_INVOKABLE static QRectF unite(const QRectF &r1, const QRectF &r2);
|
||||
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
file.write(buf.constData(), buf.size());
|
||||
file.flush();
|
||||
|
||||
if (oldHandler && (!context.category || (strcmp(context.category, "quotient.e2ee") != 0 || e2eeDebugEnabled))) {
|
||||
if (oldHandler && (strcmp(context.category, "quotient.e2ee") != 0 || e2eeDebugEnabled)) {
|
||||
oldHandler(type, context, message);
|
||||
}
|
||||
}
|
||||
@@ -197,6 +197,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
sInstance()->log(QtInfoMsg, context, message);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +212,7 @@ void filter(QLoggingCategory *category)
|
||||
|
||||
void initLogging()
|
||||
{
|
||||
e2eeDebugEnabled = QLoggingCategory("quotient.e2ee", QtInfoMsg).isEnabled(QtDebugMsg);
|
||||
e2eeDebugEnabled = QLoggingCategory("quotient.e2ee", QtDebugMsg).isEnabled(QtDebugMsg);
|
||||
oldCategoryFilter = QLoggingCategory::installFilter(filter);
|
||||
oldHandler = qInstallMessageHandler(messageHandler);
|
||||
sInstance->setOrigHandler(oldHandler);
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
LoginHelper::LoginHelper(QObject *parent)
|
||||
Login::Login(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void LoginHelper::init()
|
||||
void Login::init()
|
||||
{
|
||||
m_homeserverReachable = false;
|
||||
m_connection = new NeoChatConnection();
|
||||
@@ -31,7 +31,7 @@ void LoginHelper::init()
|
||||
m_supportsPassword = false;
|
||||
m_ssoUrl = QUrl();
|
||||
|
||||
connect(this, &LoginHelper::matrixIdChanged, this, [this]() {
|
||||
connect(this, &Login::matrixIdChanged, this, [this]() {
|
||||
setHomeserverReachable(false);
|
||||
QRegularExpression validator(QStringLiteral("^\\@?[a-zA-Z0-9\\._=\\-/]+\\:[a-zA-Z0-9\\-]+(\\.[a-zA-Z0-9\\-]+)*(\\:[0-9]+)?$"));
|
||||
if (!validator.match(m_matrixId).hasMatch()) {
|
||||
@@ -100,29 +100,28 @@ void LoginHelper::init()
|
||||
Q_EMIT Controller::instance().globalErrorOccured(i18n("Network Error"), std::move(error));
|
||||
});
|
||||
|
||||
connectSingleShot(m_connection, &Connection::syncDone, this, [this]() {
|
||||
Q_EMIT loaded();
|
||||
connectSingleShot(m_connection, &Connection::syncDone, this, []() {
|
||||
Q_EMIT Controller::instance().initiated();
|
||||
});
|
||||
}
|
||||
|
||||
void LoginHelper::setHomeserverReachable(bool reachable)
|
||||
void Login::setHomeserverReachable(bool reachable)
|
||||
{
|
||||
m_homeserverReachable = reachable;
|
||||
Q_EMIT homeserverReachableChanged();
|
||||
}
|
||||
|
||||
bool LoginHelper::homeserverReachable() const
|
||||
bool Login::homeserverReachable() const
|
||||
{
|
||||
return m_homeserverReachable;
|
||||
}
|
||||
|
||||
QString LoginHelper::matrixId() const
|
||||
QString Login::matrixId() const
|
||||
{
|
||||
return m_matrixId;
|
||||
}
|
||||
|
||||
void LoginHelper::setMatrixId(const QString &matrixId)
|
||||
void Login::setMatrixId(const QString &matrixId)
|
||||
{
|
||||
m_matrixId = matrixId;
|
||||
if (!m_matrixId.startsWith(QLatin1Char('@'))) {
|
||||
@@ -131,30 +130,30 @@ void LoginHelper::setMatrixId(const QString &matrixId)
|
||||
Q_EMIT matrixIdChanged();
|
||||
}
|
||||
|
||||
QString LoginHelper::password() const
|
||||
QString Login::password() const
|
||||
{
|
||||
return m_password;
|
||||
}
|
||||
|
||||
void LoginHelper::setPassword(const QString &password)
|
||||
void Login::setPassword(const QString &password)
|
||||
{
|
||||
setInvalidPassword(false);
|
||||
m_password = password;
|
||||
Q_EMIT passwordChanged();
|
||||
}
|
||||
|
||||
QString LoginHelper::deviceName() const
|
||||
QString Login::deviceName() const
|
||||
{
|
||||
return m_deviceName;
|
||||
}
|
||||
|
||||
void LoginHelper::setDeviceName(const QString &deviceName)
|
||||
void Login::setDeviceName(const QString &deviceName)
|
||||
{
|
||||
m_deviceName = deviceName;
|
||||
Q_EMIT deviceNameChanged();
|
||||
}
|
||||
|
||||
void LoginHelper::login()
|
||||
void Login::login()
|
||||
{
|
||||
m_isLoggingIn = true;
|
||||
Q_EMIT isLoggingInChanged();
|
||||
@@ -165,22 +164,22 @@ void LoginHelper::login()
|
||||
m_connection->loginWithPassword(username, m_password, m_deviceName, QString());
|
||||
}
|
||||
|
||||
bool LoginHelper::supportsPassword() const
|
||||
bool Login::supportsPassword() const
|
||||
{
|
||||
return m_supportsPassword;
|
||||
}
|
||||
|
||||
bool LoginHelper::supportsSso() const
|
||||
bool Login::supportsSso() const
|
||||
{
|
||||
return m_supportsSso;
|
||||
}
|
||||
|
||||
QUrl LoginHelper::ssoUrl() const
|
||||
QUrl Login::ssoUrl() const
|
||||
{
|
||||
return m_ssoUrl;
|
||||
}
|
||||
|
||||
void LoginHelper::loginWithSso()
|
||||
void Login::loginWithSso()
|
||||
{
|
||||
m_connection->resolveServer(m_matrixId);
|
||||
connectSingleShot(m_connection, &Connection::loginFlowsChanged, this, [this]() {
|
||||
@@ -190,28 +189,28 @@ void LoginHelper::loginWithSso()
|
||||
});
|
||||
}
|
||||
|
||||
bool LoginHelper::testing() const
|
||||
bool Login::testing() const
|
||||
{
|
||||
return m_testing;
|
||||
}
|
||||
|
||||
bool LoginHelper::isLoggingIn() const
|
||||
bool Login::isLoggingIn() const
|
||||
{
|
||||
return m_isLoggingIn;
|
||||
}
|
||||
|
||||
bool LoginHelper::isLoggedIn() const
|
||||
bool Login::isLoggedIn() const
|
||||
{
|
||||
return m_isLoggedIn;
|
||||
}
|
||||
|
||||
void LoginHelper::setInvalidPassword(bool invalid)
|
||||
void Login::setInvalidPassword(bool invalid)
|
||||
{
|
||||
m_invalidPassword = invalid;
|
||||
Q_EMIT isInvalidPasswordChanged();
|
||||
}
|
||||
|
||||
bool LoginHelper::isInvalidPassword() const
|
||||
bool Login::isInvalidPassword() const
|
||||
{
|
||||
return m_invalidPassword;
|
||||
}
|
||||
|
||||
21
src/login.h
21
src/login.h
@@ -4,21 +4,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QUrl>
|
||||
|
||||
class NeoChatConnection;
|
||||
|
||||
/**
|
||||
* @class LoginHelper
|
||||
* @class Login
|
||||
*
|
||||
* A helper class for logging into a Matrix account.
|
||||
*/
|
||||
class LoginHelper : public QObject
|
||||
class Login : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
/**
|
||||
* @brief Whether the home server for the account is reachable.
|
||||
@@ -79,17 +76,7 @@ class LoginHelper : public QObject
|
||||
Q_PROPERTY(bool isInvalidPassword READ isInvalidPassword NOTIFY isInvalidPasswordChanged)
|
||||
|
||||
public:
|
||||
static LoginHelper &instance()
|
||||
{
|
||||
static LoginHelper _instance;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
static LoginHelper *create(QQmlEngine *engine, QJSEngine *)
|
||||
{
|
||||
engine->setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
|
||||
return &instance();
|
||||
}
|
||||
explicit Login(QObject *parent = nullptr);
|
||||
|
||||
Q_INVOKABLE void init();
|
||||
|
||||
@@ -135,7 +122,6 @@ Q_SIGNALS:
|
||||
void isLoggingInChanged();
|
||||
void isLoggedInChanged();
|
||||
void isInvalidPasswordChanged();
|
||||
void loaded();
|
||||
|
||||
private:
|
||||
void setHomeserverReachable(bool reachable);
|
||||
@@ -152,5 +138,4 @@ private:
|
||||
bool m_isLoggingIn = false;
|
||||
bool m_isLoggedIn = false;
|
||||
bool m_invalidPassword = false;
|
||||
explicit LoginHelper(QObject *parent = nullptr);
|
||||
};
|
||||
|
||||
231
src/main.cpp
231
src/main.cpp
@@ -34,29 +34,82 @@
|
||||
|
||||
#include "neochat-version.h"
|
||||
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/keyverificationsession.h>
|
||||
#include <Quotient/networkaccessmanager.h>
|
||||
#include <Quotient/room.h>
|
||||
#include <Quotient/user.h>
|
||||
#include <Quotient/util.h>
|
||||
|
||||
#include "actionshandler.h"
|
||||
#include "blurhashimageprovider.h"
|
||||
#include "colorschemer.h"
|
||||
#include "chatdocumenthandler.h"
|
||||
#include "clipboard.h"
|
||||
#include "controller.h"
|
||||
#include "delegatesizehelper.h"
|
||||
#include "enums/delegatetype.h"
|
||||
#include "filetypesingleton.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "locationhelper.h"
|
||||
#include "logger.h"
|
||||
#include "login.h"
|
||||
#include "matriximageprovider.h"
|
||||
#include "mediasizehelper.h"
|
||||
#include "models/accountemoticonmodel.h"
|
||||
#include "models/customemojimodel.h"
|
||||
#include "models/devicesmodel.h"
|
||||
#include "models/devicesproxymodel.h"
|
||||
#include "models/emojimodel.h"
|
||||
#include "models/emoticonfiltermodel.h"
|
||||
#include "models/imagepacksmodel.h"
|
||||
#include "models/livelocationsmodel.h"
|
||||
#include "models/locationsmodel.h"
|
||||
#include "models/mediamessagefiltermodel.h"
|
||||
#include "models/messageeventmodel.h"
|
||||
#include "models/messagefiltermodel.h"
|
||||
#include "models/publicroomlistmodel.h"
|
||||
#include "models/pushrulemodel.h"
|
||||
#include "models/reactionmodel.h"
|
||||
#include "models/roomlistmodel.h"
|
||||
#include "models/searchmodel.h"
|
||||
#include "models/serverlistmodel.h"
|
||||
#include "models/sortfilterroomlistmodel.h"
|
||||
#include "models/sortfilterspacelistmodel.h"
|
||||
#include "models/statefiltermodel.h"
|
||||
#include "models/stickermodel.h"
|
||||
#include "models/userdirectorylistmodel.h"
|
||||
#include "models/userfiltermodel.h"
|
||||
#include "models/userlistmodel.h"
|
||||
#include "models/webshortcutmodel.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "notificationsmanager.h"
|
||||
#include "pollhandler.h"
|
||||
#include "roommanager.h"
|
||||
#include "spacehierarchycache.h"
|
||||
#include "urlhelper.h"
|
||||
#include "windowcontroller.h"
|
||||
|
||||
#ifdef HAVE_COLORSCHEME
|
||||
#include "colorschemer.h"
|
||||
#endif
|
||||
#include "models/completionmodel.h"
|
||||
#include "models/statemodel.h"
|
||||
|
||||
#ifdef HAVE_RUNNER
|
||||
#include "runner.h"
|
||||
#include <QDBusConnection>
|
||||
#endif
|
||||
#include "registration.h"
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
using namespace Quotient;
|
||||
#include "messageformatter.h"
|
||||
|
||||
void qml_register_types_org_kde_neochat();
|
||||
using namespace Quotient;
|
||||
|
||||
class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
|
||||
{
|
||||
@@ -89,13 +142,19 @@ Q_DECL_EXPORT
|
||||
#endif
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
#endif
|
||||
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||
|
||||
#ifdef HAVE_WEBVIEW
|
||||
QtWebView::initialize();
|
||||
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
|
||||
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
|
||||
QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
QGuiApplication app(argc, argv);
|
||||
@@ -121,7 +180,7 @@ int main(int argc, char *argv[])
|
||||
font.setHintingPreference(QFont::PreferNoHinting);
|
||||
app.setFont(font);
|
||||
#endif
|
||||
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
|
||||
KLocalizedString::setApplicationDomain("neochat");
|
||||
|
||||
QGuiApplication::setOrganizationName("KDE"_ls);
|
||||
|
||||
@@ -131,11 +190,7 @@ int main(int argc, char *argv[])
|
||||
i18n("Matrix client"),
|
||||
KAboutLicense::GPL_V3,
|
||||
i18n("© 2018-2020 Black Hat, 2020-2023 KDE Community"));
|
||||
about.addAuthor(i18n("Carl Schwan"),
|
||||
i18n("Maintainer"),
|
||||
QStringLiteral("carl@carlschwan.eu"),
|
||||
QStringLiteral("https://carlschwan.eu"),
|
||||
QStringLiteral("https://carlschwan.eu/avatar.png"));
|
||||
about.addAuthor(i18n("Carl Schwan"), i18n("Maintainer"), QStringLiteral("carl@carlschwan.eu"), QStringLiteral("https://carlschwan.eu"));
|
||||
about.addAuthor(i18n("Tobias Fella"), i18n("Maintainer"), QStringLiteral("tobias.fella@kde.org"), QStringLiteral("https://tobiasfella.de"));
|
||||
about.addAuthor(i18n("James Graham"), i18n("Maintainer"), QStringLiteral("james.h.graham@protonmail.com"));
|
||||
about.addCredit(i18n("Black Hat"), i18n("Original author of Spectral"), QStringLiteral("bhat@encom.eu.org"));
|
||||
@@ -144,7 +199,7 @@ int main(int argc, char *argv[])
|
||||
about.setOrganizationDomain("kde.org");
|
||||
|
||||
about.addComponent(QStringLiteral("libQuotient"),
|
||||
i18n("A Qt library to write cross-platform clients for Matrix"),
|
||||
i18n("A Qt5 library to write cross-platform clients for Matrix"),
|
||||
i18nc("<version number> (built against <possibly different version number>)",
|
||||
"%1 (built against %2)",
|
||||
Quotient::versionString(),
|
||||
@@ -157,7 +212,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
initLogging();
|
||||
|
||||
#if Quotient_VERSION_MINOR == 8
|
||||
Connection::setEncryptionDefault(true);
|
||||
#endif
|
||||
|
||||
#ifdef NEOCHAT_FLATPAK
|
||||
// Copy over the included FontConfig configuration to the
|
||||
@@ -166,54 +223,111 @@ int main(int argc, char *argv[])
|
||||
QStringLiteral("/var/config/fontconfig/conf.d/99-noto-mono-color-emoji.conf"));
|
||||
#endif
|
||||
|
||||
Clipboard clipboard;
|
||||
auto config = NeoChatConfig::self();
|
||||
FileTypeSingleton fileTypeSingleton;
|
||||
|
||||
Login *login = new Login();
|
||||
UrlHelper urlHelper;
|
||||
|
||||
MessageFormatter formatter;
|
||||
// formatter.formatInternal("<p>hrrejoire</p>\n<pre><code class=\"language-js\">var i = 0; i++; function\n</code></pre>\n<p>rekore</p>\n", new
|
||||
// QTextDocument);
|
||||
|
||||
// return 0;
|
||||
|
||||
#ifdef HAVE_COLORSCHEME
|
||||
ColorSchemer colorScheme;
|
||||
if (!NeoChatConfig::self()->colorScheme().isEmpty()) {
|
||||
colorScheme.apply(NeoChatConfig::self()->colorScheme());
|
||||
}
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(i18n("Client for the matrix communication protocol"));
|
||||
parser.addPositionalArgument(QStringLiteral("urls"), i18n("Supports matrix: url scheme"));
|
||||
parser.addOption(QCommandLineOption("ignore-ssl-errors"_ls, i18n("Ignore all SSL Errors, e.g., unsigned certificates.")));
|
||||
|
||||
QCommandLineOption testOption("test"_ls, i18n("Only used for autotests"));
|
||||
testOption.setFlags(QCommandLineOption::HiddenFromHelp);
|
||||
parser.addOption(testOption);
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
QCommandLineOption dbusActivatedOption(QStringLiteral("dbus-activated"), i18n("Internal usage only."));
|
||||
dbusActivatedOption.setFlags(QCommandLineOption::Flag::HiddenFromHelp);
|
||||
parser.addOption(dbusActivatedOption);
|
||||
#endif
|
||||
|
||||
about.setupCommandLine(&parser);
|
||||
parser.process(app);
|
||||
about.processCommandLine(&parser);
|
||||
Controller::setTestMode(parser.isSet("test"_ls));
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
if (parser.isSet(dbusActivatedOption)) {
|
||||
// We want to be replaceable by the main client
|
||||
KDBusService service(KDBusService::Replace);
|
||||
|
||||
Controller::listenForNotifications();
|
||||
return QCoreApplication::exec();
|
||||
qmlRegisterSingletonInstance<ColorSchemer>("org.kde.neochat", 1, 0, "ColorSchemer", &colorScheme);
|
||||
if (!config->colorScheme().isEmpty()) {
|
||||
colorScheme.apply(config->colorScheme());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KDBUSADDONS
|
||||
KDBusService service(KDBusService::Unique);
|
||||
qmlRegisterSingletonInstance<MessageFormatter>("org.kde.neochat", 1, 0, "MessageFormatter", &formatter);
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Controller", &Controller::instance());
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "NotificationsManager", &NotificationsManager::instance());
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Clipboard", &clipboard);
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Config", config);
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "RoomManager", &RoomManager::instance());
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "FileType", &fileTypeSingleton);
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "LoginHelper", login);
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "UrlHelper", &urlHelper);
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "EmojiModel", &EmojiModel::instance());
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "AccountRegistry", &Controller::instance().accounts());
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "SpaceHierarchyCache", &SpaceHierarchyCache::instance());
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "CustomEmojiModel", &CustomEmojiModel::instance());
|
||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Registration", &Registration::instance());
|
||||
qmlRegisterType<ActionsHandler>("org.kde.neochat", 1, 0, "ActionsHandler");
|
||||
qmlRegisterType<ChatDocumentHandler>("org.kde.neochat", 1, 0, "ChatDocumentHandler");
|
||||
qmlRegisterType<RoomListModel>("org.kde.neochat", 1, 0, "RoomListModel");
|
||||
qmlRegisterType<KWebShortcutModel>("org.kde.neochat", 1, 0, "WebShortcutModel");
|
||||
qmlRegisterType<UserListModel>("org.kde.neochat", 1, 0, "UserListModel");
|
||||
qmlRegisterType<MessageEventModel>("org.kde.neochat", 1, 0, "MessageEventModel");
|
||||
qmlRegisterType<ReactionModel>("org.kde.neochat", 1, 0, "ReactionModel");
|
||||
qmlRegisterType<MediaMessageFilterModel>("org.kde.neochat", 1, 0, "MediaMessageFilterModel");
|
||||
qmlRegisterType<MessageFilterModel>("org.kde.neochat", 1, 0, "MessageFilterModel");
|
||||
qmlRegisterType<UserFilterModel>("org.kde.neochat", 1, 0, "UserFilterModel");
|
||||
qmlRegisterType<PublicRoomListModel>("org.kde.neochat", 1, 0, "PublicRoomListModel");
|
||||
qmlRegisterType<UserDirectoryListModel>("org.kde.neochat", 1, 0, "UserDirectoryListModel");
|
||||
qmlRegisterType<ServerListModel>("org.kde.neochat", 1, 0, "ServerListModel");
|
||||
qmlRegisterType<SortFilterRoomListModel>("org.kde.neochat", 1, 0, "SortFilterRoomListModel");
|
||||
qmlRegisterType<SortFilterSpaceListModel>("org.kde.neochat", 1, 0, "SortFilterSpaceListModel");
|
||||
qmlRegisterType<DevicesModel>("org.kde.neochat", 1, 0, "DevicesModel");
|
||||
qmlRegisterType<DevicesProxyModel>("org.kde.neochat", 1, 0, "DevicesProxyModel");
|
||||
qmlRegisterType<LinkPreviewer>("org.kde.neochat", 1, 0, "LinkPreviewer");
|
||||
qmlRegisterType<CompletionModel>("org.kde.neochat", 1, 0, "CompletionModel");
|
||||
qmlRegisterType<StateModel>("org.kde.neochat", 1, 0, "StateModel");
|
||||
qmlRegisterType<StateFilterModel>("org.kde.neochat", 1, 0, "StateFilterModel");
|
||||
qmlRegisterType<SearchModel>("org.kde.neochat", 1, 0, "SearchModel");
|
||||
qmlRegisterType<LiveLocationsModel>("org.kde.neochat", 1, 0, "LiveLocationsModel");
|
||||
qmlRegisterType<LocationsModel>("org.kde.neochat", 1, 0, "LocationsModel");
|
||||
qmlRegisterType<PollHandler>("org.kde.neochat", 1, 0, "PollHandler");
|
||||
qmlRegisterType<PushRuleModel>("org.kde.neochat", 1, 0, "PushRuleModel");
|
||||
qmlRegisterType<StickerModel>("org.kde.neochat", 1, 0, "StickerModel");
|
||||
qmlRegisterType<ImagePacksModel>("org.kde.neochat", 1, 0, "ImagePacksModel");
|
||||
qmlRegisterType<AccountEmoticonModel>("org.kde.neochat", 1, 0, "AccountEmoticonModel");
|
||||
qmlRegisterType<EmoticonFilterModel>("org.kde.neochat", 1, 0, "EmoticonFilterModel");
|
||||
qmlRegisterType<DelegateSizeHelper>("org.kde.neochat", 1, 0, "DelegateSizeHelper");
|
||||
qmlRegisterType<MediaSizeHelper>("org.kde.neochat", 1, 0, "MediaSizeHelper");
|
||||
qmlRegisterUncreatableType<DelegateType>("org.kde.neochat", 1, 0, "DelegateType", "ENUM"_ls);
|
||||
qmlRegisterUncreatableType<PushNotificationKind>("org.kde.neochat", 1, 0, "PushNotificationKind", "ENUM"_ls);
|
||||
qmlRegisterUncreatableType<PushNotificationSection>("org.kde.neochat", 1, 0, "PushNotificationSection", "ENUM"_ls);
|
||||
qmlRegisterUncreatableType<PushNotificationState>("org.kde.neochat", 1, 0, "PushNotificationState", "ENUM"_ls);
|
||||
qmlRegisterUncreatableType<PushNotificationAction>("org.kde.neochat", 1, 0, "PushNotificationAction", "ENUM"_ls);
|
||||
qmlRegisterUncreatableType<NeoChatRoomType>("org.kde.neochat", 1, 0, "NeoChatRoomType", "ENUM"_ls);
|
||||
qmlRegisterUncreatableType<User>("org.kde.neochat", 1, 0, "User", {});
|
||||
qmlRegisterUncreatableType<NeoChatRoom>("org.kde.neochat", 1, 0, "NeoChatRoom", {});
|
||||
qmlRegisterUncreatableType<NeoChatConnection>("org.kde.neochat", 1, 0, "NeoChatConnection", {});
|
||||
|
||||
qRegisterMetaType<User *>("User*");
|
||||
qRegisterMetaType<User *>("const User*");
|
||||
qRegisterMetaType<User *>("const Quotient::User*");
|
||||
qRegisterMetaType<Room *>("Room*");
|
||||
qRegisterMetaType<MessageEventType>("MessageEventType");
|
||||
qRegisterMetaType<NeoChatRoom *>("NeoChatRoom*");
|
||||
qRegisterMetaType<User *>("User*");
|
||||
qRegisterMetaType<GetRoomEventsJob *>("GetRoomEventsJob*");
|
||||
qRegisterMetaType<QMimeType>("QMimeType");
|
||||
qRegisterMetaType<KeyVerificationSession *>("KeyVerificationSession*");
|
||||
qmlRegisterUncreatableType<KeyVerificationSession>("org.kde.neochat", 1, 0, "KeyVerificationSession", {});
|
||||
qRegisterMetaType<QVector<EmojiEntry>>("QVector<EmojiEntry>");
|
||||
qmlRegisterSingletonType("org.kde.neochat", 1, 0, "About", [](QQmlEngine *engine, QJSEngine *) -> QJSValue {
|
||||
return engine->toScriptValue(KAboutData::applicationData());
|
||||
});
|
||||
qmlRegisterSingletonType(QUrl("qrc:/OsmLocationPlugin.qml"_ls), "org.kde.neochat", 1, 0, "OsmLocationPlugin");
|
||||
qmlRegisterSingletonType("org.kde.neochat", 1, 0, "LocationHelper", [](QQmlEngine *engine, QJSEngine *) -> QJSValue {
|
||||
return engine->toScriptValue(LocationHelper());
|
||||
});
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
qRegisterMetaTypeStreamOperators<Emoji>();
|
||||
#endif
|
||||
|
||||
qml_register_types_org_kde_neochat();
|
||||
qmlRegisterSingletonInstance("org.kde.neochat.config", 1, 0, "Config", NeoChatConfig::self());
|
||||
qmlRegisterSingletonInstance("org.kde.neochat.accounts", 1, 0, "AccountRegistry", &Controller::instance().accounts());
|
||||
|
||||
qmlRegisterUncreatableType<KeyVerificationSession>("com.github.quotient_im.libquotient", 1, 0, "KeyVerificationSession", {});
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
#ifdef HAVE_KDBUSADDONS
|
||||
KDBusService service(KDBusService::Unique);
|
||||
service.connect(&service,
|
||||
&KDBusService::activateRequested,
|
||||
&RoomManager::instance(),
|
||||
@@ -232,7 +346,7 @@ int main(int argc, char *argv[])
|
||||
auto args = arguments;
|
||||
args.removeFirst();
|
||||
for (const auto &arg : args) {
|
||||
RoomManager::instance().resolveResource(arg);
|
||||
RoomManager::instance().openResource(arg);
|
||||
}
|
||||
});
|
||||
#endif
|
||||
@@ -241,16 +355,25 @@ int main(int argc, char *argv[])
|
||||
QObject::connect(&engine, &QQmlApplicationEngine::quit, &app, &QCoreApplication::quit);
|
||||
engine.setNetworkAccessManagerFactory(new NetworkAccessManagerFactory());
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(i18n("Client for the matrix communication protocol"));
|
||||
parser.addPositionalArgument(QStringLiteral("urls"), i18n("Supports matrix: url scheme"));
|
||||
parser.addOption(QCommandLineOption("ignore-ssl-errors"_ls, i18n("Ignore all SSL Errors, e.g., unsigned certificates.")));
|
||||
|
||||
about.setupCommandLine(&parser);
|
||||
parser.process(app);
|
||||
about.processCommandLine(&parser);
|
||||
|
||||
if (parser.isSet("ignore-ssl-errors"_ls)) {
|
||||
QObject::connect(NetworkAccessManager::instance(), &QNetworkAccessManager::sslErrors, NetworkAccessManager::instance(), [](QNetworkReply *reply) {
|
||||
reply->ignoreSslErrors();
|
||||
});
|
||||
}
|
||||
|
||||
engine.addImageProvider(QLatin1String("mxc"), MatrixImageProvider::create(&engine, &engine));
|
||||
engine.addImageProvider(QLatin1String("mxc"), new MatrixImageProvider);
|
||||
engine.addImageProvider(QLatin1String("blurhash"), new BlurhashImageProvider);
|
||||
|
||||
engine.load(QUrl(QStringLiteral("qrc:/org/kde/neochat/qml/main.qml")));
|
||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||
if (engine.rootObjects().isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
@@ -260,8 +383,8 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
#ifdef HAVE_RUNNER
|
||||
auto runner = Runner::create(&engine, &engine);
|
||||
QDBusConnection::sessionBus().registerObject("/RoomRunner"_ls, runner, QDBusConnection::ExportScriptableContents);
|
||||
Runner runner;
|
||||
QDBusConnection::sessionBus().registerObject("/RoomRunner"_ls, &runner, QDBusConnection::ExportScriptableContents);
|
||||
#endif
|
||||
|
||||
QWindow *window = windowFromEngine(&engine);
|
||||
|
||||
@@ -13,13 +13,11 @@
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include "controller.h"
|
||||
#include "neochatconnection.h"
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
ThumbnailResponse::ThumbnailResponse(QString id, QSize size, NeoChatConnection *connection)
|
||||
ThumbnailResponse::ThumbnailResponse(QString id, QSize size)
|
||||
: mediaId(std::move(id))
|
||||
, requestedSize(size)
|
||||
, localFile(QStringLiteral("%1/image_provider/%2-%3x%4.png")
|
||||
@@ -27,7 +25,6 @@ ThumbnailResponse::ThumbnailResponse(QString id, QSize size, NeoChatConnection *
|
||||
mediaId,
|
||||
QString::number(requestedSize.width()),
|
||||
QString::number(requestedSize.height())))
|
||||
, m_connection(connection)
|
||||
, errorStr("Image request hasn't started"_ls)
|
||||
{
|
||||
if (requestedSize.isEmpty()) {
|
||||
@@ -54,24 +51,24 @@ ThumbnailResponse::ThumbnailResponse(QString id, QSize size, NeoChatConnection *
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_connection) {
|
||||
if (!Controller::instance().activeConnection()) {
|
||||
qWarning() << "Current connection is null";
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute a request on the main thread asynchronously
|
||||
moveToThread(m_connection->thread());
|
||||
moveToThread(Controller::instance().activeConnection()->thread());
|
||||
QMetaObject::invokeMethod(this, &ThumbnailResponse::startRequest, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void ThumbnailResponse::startRequest()
|
||||
{
|
||||
if (!m_connection) {
|
||||
if (!Controller::instance().activeConnection()) {
|
||||
return;
|
||||
}
|
||||
// Runs in the main thread, not QML thread
|
||||
Q_ASSERT(QThread::currentThread() == m_connection->thread());
|
||||
job = m_connection->getThumbnail(mediaId, requestedSize);
|
||||
Q_ASSERT(QThread::currentThread() == Controller::instance().activeConnection()->thread());
|
||||
job = Controller::instance().activeConnection()->getThumbnail(mediaId, requestedSize);
|
||||
// Connect to any possible outcome including abandonment
|
||||
// to make sure the QML thread is not left stuck forever.
|
||||
connect(job, &BaseJob::finished, this, &ThumbnailResponse::prepareResult);
|
||||
@@ -121,7 +118,7 @@ QString ThumbnailResponse::errorString() const
|
||||
|
||||
QQuickImageResponse *MatrixImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
|
||||
{
|
||||
return new ThumbnailResponse(id, requestedSize, m_connection);
|
||||
return new ThumbnailResponse(id, requestedSize);
|
||||
}
|
||||
|
||||
#include "moc_matriximageprovider.cpp"
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
|
||||
#include <QReadWriteLock>
|
||||
|
||||
class NeoChatConnection;
|
||||
|
||||
/**
|
||||
* @class ThumbnailResponse
|
||||
*
|
||||
@@ -23,7 +21,7 @@ class ThumbnailResponse : public QQuickImageResponse
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ThumbnailResponse(QString mediaId, QSize requestedSize, NeoChatConnection *m_connection);
|
||||
explicit ThumbnailResponse(QString mediaId, QSize requestedSize);
|
||||
~ThumbnailResponse() override = default;
|
||||
|
||||
private Q_SLOTS:
|
||||
@@ -35,7 +33,6 @@ private:
|
||||
QSize requestedSize;
|
||||
const QString localFile;
|
||||
Quotient::MediaThumbnailJob *job = nullptr;
|
||||
NeoChatConnection *m_connection;
|
||||
|
||||
QImage image;
|
||||
QString errorStr;
|
||||
@@ -54,27 +51,11 @@ private:
|
||||
*/
|
||||
class MatrixImageProvider : public QQuickAsyncImageProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
Q_PROPERTY(NeoChatConnection *connection MEMBER m_connection)
|
||||
public:
|
||||
static MatrixImageProvider *create(QQmlEngine *engine, QJSEngine *)
|
||||
{
|
||||
static MatrixImageProvider *instance = new MatrixImageProvider;
|
||||
engine->setObjectOwnership(instance, QQmlEngine::CppOwnership);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return a job to provide the image with the given ID.
|
||||
*
|
||||
* @sa QQuickAsyncImageProvider::requestImageResponse
|
||||
*/
|
||||
QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
|
||||
|
||||
private:
|
||||
NeoChatConnection *m_connection = nullptr;
|
||||
MatrixImageProvider() = default;
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QSize>
|
||||
|
||||
/**
|
||||
@@ -29,7 +28,6 @@
|
||||
class MediaSizeHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
/**
|
||||
* @brief The maximum width (in px) the media can be.
|
||||
|
||||
133
src/messageformatter.cpp
Normal file
133
src/messageformatter.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
// SPDX-FileCopyrightText: 2021 Carson Black <uhhadd@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "messageformatter.h"
|
||||
|
||||
#include <QDomDocument>
|
||||
#include <QGuiApplication>
|
||||
#include <QPalette>
|
||||
#include <QQmlContext>
|
||||
#include <QQmlProperty>
|
||||
#include <QTextCursor>
|
||||
#include <QTextDocumentFragment>
|
||||
|
||||
#include <KSyntaxHighlighting/definition.h>
|
||||
#include <KSyntaxHighlighting/repository.h>
|
||||
#include <KSyntaxHighlighting/syntaxhighlighter.h>
|
||||
#include <KSyntaxHighlighting/theme.h>
|
||||
|
||||
QTextDocumentFragment copyTextLayoutFrom(QTextDocument *document)
|
||||
{
|
||||
QTextCursor sourceCursor(document);
|
||||
sourceCursor.select(QTextCursor::Document);
|
||||
|
||||
QTextDocument helper;
|
||||
|
||||
// copy the content fragment from the source document into our helper document
|
||||
QTextCursor curs(&helper);
|
||||
curs.insertFragment(sourceCursor.selection());
|
||||
curs.select(QTextCursor::Document);
|
||||
|
||||
// not sure why, but fonts get lost. since this is for codeblocks, we can
|
||||
// just force the mono font. anyone copying this code would probably want
|
||||
// to fix the problem proper if it's not also for codeblocks.
|
||||
const auto fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||
|
||||
const int docStart = sourceCursor.selectionStart();
|
||||
const int docEnd = helper.characterCount() - 1;
|
||||
|
||||
// since the copied fragment above lost the qsyntaxhighlighter stuff,
|
||||
// we gotta go through the qtextlayout and apply those styles to the
|
||||
// document
|
||||
const auto end = document->findBlock(sourceCursor.selectionEnd()).next();
|
||||
for (auto current = document->findBlock(docStart); current.isValid() && current != end; current = current.next()) {
|
||||
const auto layout = current.layout();
|
||||
|
||||
// iterate through the formats, applying them to our document
|
||||
for (const auto &span : layout->formats()) {
|
||||
const int start = current.position() + span.start - docStart;
|
||||
const int end = start + span.length;
|
||||
|
||||
curs.setPosition(qMax(start, 0));
|
||||
curs.setPosition(qMin(end, docEnd), QTextCursor::KeepAnchor);
|
||||
|
||||
auto fmt = span.format;
|
||||
fmt.setFont(fixedFont);
|
||||
|
||||
curs.setCharFormat(fmt);
|
||||
}
|
||||
}
|
||||
|
||||
return QTextDocumentFragment(&helper);
|
||||
}
|
||||
|
||||
QTextDocumentFragment highlight(const QString &code, const QString &language)
|
||||
{
|
||||
using namespace KSyntaxHighlighting;
|
||||
|
||||
static Repository repo;
|
||||
|
||||
auto theme = repo.themeForPalette(QGuiApplication::palette());
|
||||
auto definition = repo.definitionForFileName(QLatin1String("file.") + language);
|
||||
|
||||
QTextDocument doku(code);
|
||||
|
||||
QScopedPointer<SyntaxHighlighter> highlighter(new SyntaxHighlighter(&doku));
|
||||
highlighter->setTheme(theme);
|
||||
highlighter->setDefinition(definition);
|
||||
|
||||
return copyTextLayoutFrom(&doku);
|
||||
}
|
||||
|
||||
bool extractCodeBlock(QTextCursor cursor, QDomElement element)
|
||||
{
|
||||
const auto codeNode = element.firstChild();
|
||||
if (!codeNode.isNull()) {
|
||||
const auto code = codeNode.toElement();
|
||||
if (!code.isNull() && code.tagName() == QLatin1String("code")) {
|
||||
QString lang;
|
||||
auto langClass = code.attribute(QLatin1String("class"), QLatin1String("none"));
|
||||
if (langClass != QLatin1String("none") && langClass.startsWith(QLatin1String("language-"))) {
|
||||
lang = langClass.remove(0, 9);
|
||||
}
|
||||
|
||||
if (!lang.isNull()) {
|
||||
cursor.insertFragment(highlight(code.text(), lang));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QString MessageFormatter::formatInternal(const QString &messageBody, QTextDocument *document)
|
||||
{
|
||||
QTextCursor curs(document);
|
||||
QDomDocument doc(QLatin1String("htmlement"));
|
||||
doc.setContent(QStringLiteral("<div>%1</div>").arg(messageBody));
|
||||
QDomElement docElem = doc.documentElement();
|
||||
QDomNode n = docElem.firstChild();
|
||||
while (!n.isNull()) {
|
||||
QDomElement e = n.toElement();
|
||||
if (!e.isNull()) {
|
||||
if (e.tagName() != QLatin1String("pre") || !extractCodeBlock(curs, e)) {
|
||||
QString outText;
|
||||
QTextStream out(&outText);
|
||||
e.save(out, 0);
|
||||
curs.insertHtml(outText);
|
||||
}
|
||||
}
|
||||
n = n.nextSibling();
|
||||
}
|
||||
|
||||
Q_EMIT document->contentsChanged();
|
||||
return document->toHtml();
|
||||
}
|
||||
|
||||
QString MessageFormatter::format(const QString &messageBody, QQuickTextDocument *doc, QQuickItem *item)
|
||||
{
|
||||
QColor linkColor = QQmlProperty(item, QLatin1String("Kirigami.Theme.linkColor"), qmlContext(item)).read().value<QColor>();
|
||||
|
||||
return formatInternal(messageBody, doc->textDocument());
|
||||
}
|
||||
13
src/messageformatter.h
Normal file
13
src/messageformatter.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <QQuickItem>
|
||||
#include <QQuickTextDocument>
|
||||
|
||||
class MessageFormatter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Q_INVOKABLE QString format(const QString &messageBody, QQuickTextDocument *doc, QQuickItem *item);
|
||||
Q_INVOKABLE QString formatInternal(const QString &messageBody, QTextDocument *doc);
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user