Compare commits
261 Commits
work/redst
...
v23.04.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2c37a93d0 | ||
|
|
d6b575f017 | ||
|
|
063d91c25b | ||
|
|
54cf9ad495 | ||
|
|
aa569e127b | ||
|
|
7e7b9728af | ||
|
|
c72cf64369 | ||
|
|
7aa68ce896 | ||
|
|
c204b36aa9 | ||
|
|
38584bc735 | ||
|
|
2e873fc49b | ||
|
|
5e898d68c2 | ||
|
|
ed9d2e1d02 | ||
|
|
07fb6bd90a | ||
|
|
11003775a1 | ||
|
|
79d8f35dca | ||
|
|
a1d5727a10 | ||
|
|
4e9c4f9752 | ||
|
|
d0e71114de | ||
|
|
a6db2aac70 | ||
|
|
a829b6c991 | ||
|
|
05a6d055b4 | ||
|
|
64f8df4dd2 | ||
|
|
8dcc231bdc | ||
|
|
5efe5cbb88 | ||
|
|
d2f8392ea2 | ||
|
|
57d79bf3fa | ||
|
|
13a573c7f6 | ||
|
|
64a1de8729 | ||
|
|
669ad9f6e2 | ||
|
|
269e29244a | ||
|
|
0b6da7b799 | ||
|
|
6eee09a560 | ||
|
|
2ad5a593b4 | ||
|
|
970ff95fd0 | ||
|
|
5a1a3fbd06 | ||
|
|
9493530b9c | ||
|
|
ec76df5e47 | ||
|
|
095c1fa4c3 | ||
|
|
41874701e6 | ||
|
|
58de466ad5 | ||
|
|
5800180836 | ||
|
|
2b2a864041 | ||
|
|
90399930ec | ||
|
|
8c4ac46a69 | ||
|
|
31951db498 | ||
|
|
fd593bb483 | ||
|
|
1243aef243 | ||
|
|
41ce874f77 | ||
|
|
4d69b70e02 | ||
|
|
b5a46b35f8 | ||
|
|
33ab3fd921 | ||
|
|
357d418cb4 | ||
|
|
76d3484a1e | ||
|
|
aef86017b2 | ||
|
|
40358cba85 | ||
|
|
3ee6877b15 | ||
|
|
e7a0d90746 | ||
|
|
484188c4e1 | ||
|
|
3e967e53bf | ||
|
|
c2f1e0de53 | ||
|
|
69a1d5477e | ||
|
|
a8f29de87a | ||
|
|
275a4f9222 | ||
|
|
95f4c5b9e1 | ||
|
|
d7412c965c | ||
|
|
a94dfde7f7 | ||
|
|
6c7e12c471 | ||
|
|
9949ba8f6d | ||
|
|
2c3ce1e88b | ||
|
|
33533a75bd | ||
|
|
8c6671265c | ||
|
|
bfa643557d | ||
|
|
2b0f6a1d4d | ||
|
|
0e8deee005 | ||
|
|
fe60d11ba3 | ||
|
|
e9d3862537 | ||
|
|
e03b2e9adf | ||
|
|
566743bc19 | ||
|
|
a3b4f05e2c | ||
|
|
977e9bbf99 | ||
|
|
ad11a509aa | ||
|
|
abbe68f9ab | ||
|
|
b7b0749cd8 | ||
|
|
f8ae06f8a7 | ||
|
|
9946ba008b | ||
|
|
376916212d | ||
|
|
1ea6ffe416 | ||
|
|
2d99ae0404 | ||
|
|
88eeed74da | ||
|
|
0032d417ac | ||
|
|
bd9fb097e3 | ||
|
|
ecd7a5edff | ||
|
|
d3f0902835 | ||
|
|
6eb258f8a3 | ||
|
|
dff908edd2 | ||
|
|
7e06606cbd | ||
|
|
f78f92cd0f | ||
|
|
c4fdfa22d9 | ||
|
|
3e5d1429f7 | ||
|
|
76b5463dac | ||
|
|
8b26a9f45f | ||
|
|
da1c664f94 | ||
|
|
498cfedfea | ||
|
|
8983129520 | ||
|
|
1c8acb2acd | ||
|
|
53fa4be4d8 | ||
|
|
bf30152e33 | ||
|
|
d86c61ac8a | ||
|
|
280d90e191 | ||
|
|
b4b24430a1 | ||
|
|
b7b7f1817a | ||
|
|
3d67a682f4 | ||
|
|
da6df18367 | ||
|
|
0b1b1b8c8c | ||
|
|
2493a00ba4 | ||
|
|
d6ebb1308c | ||
|
|
332c311023 | ||
|
|
27d33d121a | ||
|
|
e954fc204f | ||
|
|
d1f7e7091e | ||
|
|
bd4eeb405b | ||
|
|
e6a060c192 | ||
|
|
f53a7a27f6 | ||
|
|
34384dced4 | ||
|
|
b68cfafab2 | ||
|
|
58b836fd1e | ||
|
|
90cad05bae | ||
|
|
072f7cec37 | ||
|
|
39388e204e | ||
|
|
6ccb201110 | ||
|
|
178b516c7c | ||
|
|
ac88e13e58 | ||
|
|
8d3e145e0b | ||
|
|
f40d1b9f4e | ||
|
|
e9cd165457 | ||
|
|
c0c86c67b6 | ||
|
|
12afa43d23 | ||
|
|
386f637b94 | ||
|
|
d7bd9f4609 | ||
|
|
33c9edc9a3 | ||
|
|
3b2dbc731e | ||
|
|
51a29ac528 | ||
|
|
cc8bf79a9b | ||
|
|
8d47e58861 | ||
|
|
47ce8a4846 | ||
|
|
71c9537c61 | ||
|
|
8825e6ec83 | ||
|
|
44ec93f0a0 | ||
|
|
ed7688e66f | ||
|
|
6b49854b12 | ||
|
|
e8484ebc7a | ||
|
|
17d01c68c4 | ||
|
|
dae2cbab90 | ||
|
|
b8e8fa3ee5 | ||
|
|
9c4a925171 | ||
|
|
8996806b05 | ||
|
|
2bd4579c10 | ||
|
|
0891f32c08 | ||
|
|
5287c2d529 | ||
|
|
084b89f3dc | ||
|
|
2dd3197beb | ||
|
|
6d9dca7da8 | ||
|
|
e0f16054fc | ||
|
|
fa70679439 | ||
|
|
949bd20873 | ||
|
|
79a9eb0de0 | ||
|
|
e1b9bc7d0e | ||
|
|
a1abf22174 | ||
|
|
fa27d993e2 | ||
|
|
d4b750433e | ||
|
|
9df534c72c | ||
|
|
f785e4d5b0 | ||
|
|
f186be7314 | ||
|
|
3cbcc2b597 | ||
|
|
61f3d6c9ae | ||
|
|
d8b2436f0d | ||
|
|
ea76edce74 | ||
|
|
5482aad7ba | ||
|
|
aaa26571d1 | ||
|
|
554e3576fd | ||
|
|
8d2c7ff8c2 | ||
|
|
92261eb94f | ||
|
|
4dfb7fc3e2 | ||
|
|
2728446692 | ||
|
|
dfb32fe687 | ||
|
|
55507112a9 | ||
|
|
d9aa77a9f4 | ||
|
|
c0ea52c331 | ||
|
|
f1b9d2329f | ||
|
|
0707fd8189 | ||
|
|
6bef2205db | ||
|
|
ace62b4df1 | ||
|
|
d76e512785 | ||
|
|
db2692c411 | ||
|
|
4c9bdd03ce | ||
|
|
efa2c012a7 | ||
|
|
072dc1b6a3 | ||
|
|
4066427168 | ||
|
|
b901c1bdf2 | ||
|
|
532310d739 | ||
|
|
4f7d32df2b | ||
|
|
833d2159e7 | ||
|
|
ccf8701395 | ||
|
|
5d696aabc4 | ||
|
|
ebd521e2ee | ||
|
|
9d5303113e | ||
|
|
3850fe15a5 | ||
|
|
5392481d06 | ||
|
|
0ce048517b | ||
|
|
c51e16e16c | ||
|
|
765a95050d | ||
|
|
38dcefffcb | ||
|
|
391b61dcb4 | ||
|
|
d76c9fcbcf | ||
|
|
f7b8ae2af2 | ||
|
|
ddbf9b60d4 | ||
|
|
8948ff5faa | ||
|
|
d270e202a8 | ||
|
|
61e1dd14ba | ||
|
|
eee93e0f1f | ||
|
|
826760a55c | ||
|
|
749398df8f | ||
|
|
150968d226 | ||
|
|
d81df24e7c | ||
|
|
594a5cf6ca | ||
|
|
0af420b824 | ||
|
|
31fed8362a | ||
|
|
dbcf8c6327 | ||
|
|
8012392400 | ||
|
|
3e48578b44 | ||
|
|
d7e656e57f | ||
|
|
8e83b923d9 | ||
|
|
a739f0f09f | ||
|
|
bc74737b0f | ||
|
|
e92fccf904 | ||
|
|
20cbedfff5 | ||
|
|
e0d508d3dd | ||
|
|
af318a2bae | ||
|
|
6b4e81c763 | ||
|
|
dcac63aa04 | ||
|
|
7818747e45 | ||
|
|
7cb1f856ca | ||
|
|
5955c8e7dc | ||
|
|
dee3c279e8 | ||
|
|
94427280d8 | ||
|
|
8b245b1cc9 | ||
|
|
0513cd10c4 | ||
|
|
28b5631d06 | ||
|
|
5f2cd92da7 | ||
|
|
4535125c54 | ||
|
|
8831da956a | ||
|
|
f2ec6e1d4c | ||
|
|
93f7def532 | ||
|
|
782f5517d3 | ||
|
|
f238c18ce8 | ||
|
|
4fe0ea373f | ||
|
|
982d21dd58 | ||
|
|
7002132bde | ||
|
|
1ef931f7e7 | ||
|
|
8797015c6a |
@@ -2,7 +2,7 @@
|
||||
"id": "org.kde.neochat",
|
||||
"branch": "master",
|
||||
"runtime": "org.kde.Platform",
|
||||
"runtime-version": "5.15-21.08",
|
||||
"runtime-version": "5.15-22.08",
|
||||
"sdk": "org.kde.Sdk",
|
||||
"command": "neochat",
|
||||
"tags": [
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ compile_commands.json
|
||||
kate.project.ctags.*
|
||||
*.user
|
||||
.flatpak-builder/
|
||||
.idea/
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
include:
|
||||
- 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
|
||||
# TODO enable once we can have qt6 libQuotient on the CI
|
||||
# - 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/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
|
||||
|
||||
37
.kde-ci.yml
37
.kde-ci.yml
@@ -1,8 +1,8 @@
|
||||
# SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
|
||||
# SPDX-FileCopyrightText: 2021 Tobias Fella <tobias.fella@kde.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
Dependencies:
|
||||
- 'on': ['@all']
|
||||
- 'on': ['Linux/Qt5', 'Android/Qt5', 'FreeBSD/Qt5', 'Windows/Qt5']
|
||||
'require':
|
||||
'frameworks/extra-cmake-modules': '@stable'
|
||||
'frameworks/kcoreaddons': '@stable'
|
||||
@@ -19,15 +19,42 @@ Dependencies:
|
||||
'third-party/qtkeychain': '@latest'
|
||||
'third-party/cmark': '@latest'
|
||||
'third-party/qcoro': '@latest'
|
||||
- 'on': ['Windows', 'Linux', 'FreeBSD']
|
||||
- 'on': ['Windows/Qt5', 'Linux/Qt5', 'FreeBSD/Qt5']
|
||||
'require':
|
||||
'frameworks/qqc2-desktop-style': '@stable'
|
||||
'frameworks/kio': '@stable'
|
||||
'frameworks/kwindowsystem': '@stable'
|
||||
'frameworks/kconfigwidgets': '@stable'
|
||||
- 'on': ['Linux', 'FreeBSD']
|
||||
- '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'
|
||||
'frameworks/kirigami': '@latest-kf6'
|
||||
'frameworks/ki18n': '@latest-kf6'
|
||||
'frameworks/kconfig': '@latest-kf6'
|
||||
'frameworks/syntax-highlighting': '@latest-kf6'
|
||||
'frameworks/kitemmodels': '@latest-kf6'
|
||||
'frameworks/knotifications': '@latest-kf6'
|
||||
'libraries/kquickimageeditor': '@latest-kf6'
|
||||
'frameworks/sonnet': '@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/Qt6', 'Linux/Qt6', 'FreeBSD/Qt6']
|
||||
'require':
|
||||
'frameworks/qqc2-desktop-style': '@latest-kf6'
|
||||
'frameworks/kio': '@latest-kf6'
|
||||
'frameworks/kwindowsystem': '@latest-kf6'
|
||||
'frameworks/kconfigwidgets': '@latest-kf6'
|
||||
- 'on': ['Linux/Qt6', 'FreeBSD/Qt6']
|
||||
'require':
|
||||
'frameworks/kdbusaddons': '@latest-kf6'
|
||||
|
||||
Options:
|
||||
require-passing-tests-on: [ 'Linux', 'FreeBSD', 'Windows' ]
|
||||
require-passing-tests-on: [ 'Linux/Qt5', 'FreeBSD', 'Windows/Qt5' ]
|
||||
|
||||
@@ -7,7 +7,7 @@ Copyright: 2020 Carson Black <uhhadd@gmail.com>
|
||||
License: LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
|
||||
Files: android/res/drawable/splash.xml
|
||||
Copyright: 2020 Tobias Fella <fella@posteo.de>
|
||||
Copyright: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: .gitignore
|
||||
@@ -27,11 +27,11 @@ Copyright: 2021 Carl Schwan <carlschwan@kde.org>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: src/neochatconfig.kcfg
|
||||
Copyright: 2020-2021 Carl Schwan <carlschwan@kde.org>, Tobias Fella <fella@posteo.de>
|
||||
Copyright: 2020-2021 Carl Schwan <carlschwan@kde.org>, Tobias Fella <tobias.fella@kde.org>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: src/neochat.notifyrc
|
||||
Copyright: 2020 Tobias Fella <fella@posteo.de>
|
||||
Copyright: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: src/qml/Component/confetti.png src/qml/Component/glowdot.png
|
||||
@@ -39,5 +39,5 @@ Copyright: 2021 Alexey Andreyev <aa13q@ya.ru>
|
||||
License: CC0-1.0
|
||||
|
||||
Files: .flatpak-manifest.json
|
||||
Copyright: 2020-2022 Tobias Fella <fella@posteo.de>
|
||||
Copyright: 2020-2022 Tobias Fella <tobias.fella@kde.org>
|
||||
License: BSD-2-Clause
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
# SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carl@carlschwan.eu>
|
||||
# SPDX-FileCopyrightText: 2020-2021 Nicolas Fella <nicolas.fella@gmx.de>
|
||||
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <fella@posteo.de>
|
||||
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
||||
# SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(NeoChat)
|
||||
set(PROJECT_VERSION "22.11")
|
||||
# KDE Applications version, managed by release script.
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "23")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "04")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "3")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
set(KF5_MIN_VERSION "5.91.0")
|
||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||
|
||||
set(KF_MIN_VERSION "5.91.0")
|
||||
set(QT_MIN_VERSION "5.15.2")
|
||||
if (ANDROID)
|
||||
set(QT_MIN_VERSION "5.15.8")
|
||||
endif()
|
||||
|
||||
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
|
||||
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
|
||||
|
||||
@@ -49,16 +57,16 @@ set_package_properties(Qt${QT_MAJOR_VERSION} PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
)
|
||||
find_package(KF5 ${KF5_MIN_VERSION} COMPONENTS Kirigami2 I18n Notifications Config CoreAddons Sonnet ItemModels)
|
||||
set_package_properties(KF5 PROPERTIES
|
||||
find_package(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} COMPONENTS Kirigami2 I18n Notifications Config CoreAddons Sonnet ItemModels)
|
||||
set_package_properties(KF${QT_MAJOR_VERSION} PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
)
|
||||
set_package_properties(KF5Kirigami2 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Kirigami application UI framework"
|
||||
)
|
||||
find_package(KF5KirigamiAddons 0.6 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)
|
||||
|
||||
find_package(Qt${QT_MAJOR_VERSION}Keychain)
|
||||
set_package_properties(Qt${QT_MAJOR_VERSION}Keychain PROPERTIES
|
||||
@@ -74,15 +82,15 @@ if(ANDROID)
|
||||
)
|
||||
else()
|
||||
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} COMPONENTS Widgets)
|
||||
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle ConfigWidgets KIO WindowSystem)
|
||||
set_package_properties(KF5QQC2DesktopStyle PROPERTIES
|
||||
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)
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
find_package(KF5DBusAddons ${KF5_MIN_VERSION} REQUIRED)
|
||||
find_package(KF${QT_MAJOR_VERSION}DBusAddons ${KF_MIN_VERSION} REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(Quotient 0.6)
|
||||
@@ -116,12 +124,14 @@ find_package(QCoro${QT_MAJOR_VERSION} 0.4 COMPONENTS Core REQUIRED)
|
||||
|
||||
qcoro_enable_coroutines()
|
||||
|
||||
find_package(KF5DocTools ${KF5_MIN_VERSION})
|
||||
set_package_properties(KF5DocTools 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(Sqlite3)
|
||||
|
||||
if(NOT Quotient_VERSION_MINOR GREATER 6)
|
||||
cmake_policy(SET CMP0063 OLD)
|
||||
endif()
|
||||
@@ -145,7 +155,7 @@ if (BUILD_TESTING AND Quotient_VERSION_MINOR GREATER 6)
|
||||
add_subdirectory(autotests)
|
||||
endif()
|
||||
|
||||
if(KF5DocTools_FOUND)
|
||||
if(KF${QT_MAJOR_VERSION}DocTools_FOUND)
|
||||
kdoctools_install(po)
|
||||
add_subdirectory(doc)
|
||||
endif()
|
||||
|
||||
0
LICENSES/MIT.txt
Executable file → Normal file
0
LICENSES/MIT.txt
Executable file → Normal file
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carlschwan@kde.org>
|
||||
SPDX-FileCopyrightText: 2020-2021 Tobias Fella <fella@posteo.de>
|
||||
SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
# NeoChat
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
android:name="org.qtproject.qt5.android.bindings.QtActivity"
|
||||
android:label="NeoChat"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:launchMode="singleTop">
|
||||
android:launchMode="singleTop"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
@@ -22,7 +23,6 @@
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.lib_name" android:value="neochat-app"/>
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||
@@ -38,8 +38,6 @@
|
||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
||||
<!-- Messages maps -->
|
||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
||||
|
||||
<!-- Splash screen -->
|
||||
|
||||
@@ -12,7 +12,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.6.4'
|
||||
classpath 'com.android.tools.build:gradle:7.0.2'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,10 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion qtMinSdkVersion
|
||||
targetSdkVersion qtTargetSdkVersion
|
||||
applicationId "org.kde.neochat"
|
||||
namespace "org.kde.neochat"
|
||||
versionCode timestamp
|
||||
versionName projectVersionFull
|
||||
manifestPlaceholders = [versionName: projectVersionFull, versionCode: timestamp]
|
||||
}
|
||||
|
||||
|
||||
@@ -8,3 +8,9 @@ ecm_add_test(
|
||||
LINK_LIBRARIES neochat Qt::Test Quotient
|
||||
TEST_NAME neochatroomtest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
texthandlertest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME texthandlertest
|
||||
)
|
||||
|
||||
@@ -136,7 +136,7 @@ void NeoChatRoomTest::initTestCase()
|
||||
void NeoChatRoomTest::subtitleTextTest()
|
||||
{
|
||||
QCOMPARE(room->timelineSize(), 1);
|
||||
QCOMPARE(room->subtitleText(), QStringLiteral("@example:example.org: This is an example text message"));
|
||||
QCOMPARE(room->lastEventToString(), QStringLiteral("@example:example.org: This is an example text message"));
|
||||
}
|
||||
|
||||
void NeoChatRoomTest::eventTest()
|
||||
|
||||
512
autotests/texthandlertest.cpp
Normal file
512
autotests/texthandlertest.cpp
Normal file
@@ -0,0 +1,512 @@
|
||||
// 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 "texthandler.h"
|
||||
|
||||
#include <connection.h>
|
||||
#include <quotient_common.h>
|
||||
#include <syncdata.h>
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class TestRoom : public NeoChatRoom
|
||||
{
|
||||
public:
|
||||
using NeoChatRoom::NeoChatRoom;
|
||||
|
||||
void update(SyncRoomData &&data, bool fromCache = false)
|
||||
{
|
||||
Room::updateData(std::move(data), fromCache);
|
||||
}
|
||||
};
|
||||
|
||||
class TextHandlerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
TestRoom *room = nullptr;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void allowedAttributes();
|
||||
void stripDisallowedTags();
|
||||
void stripDisallowedAttributes();
|
||||
void emptyCodeTags();
|
||||
|
||||
void sendSimpleStringCase();
|
||||
void sendSingleParaMarkup();
|
||||
void sendMultipleSectionMarkup();
|
||||
void sendBadLinks();
|
||||
void sendEscapeCode();
|
||||
void sendCodeClass();
|
||||
|
||||
void receiveStripReply();
|
||||
void receivePlainTextIn();
|
||||
|
||||
void recieveRichInPlainOut();
|
||||
void receivePlainStripHtml();
|
||||
void receivePlainStripMarkup();
|
||||
void receiveStripNewlines();
|
||||
|
||||
void receiveRichUserPill();
|
||||
void receiveRichStrikethrough();
|
||||
void receiveRichtextIn();
|
||||
void receiveRichMxcUrl();
|
||||
void receiveRichPlainUrl();
|
||||
|
||||
void receiveLineSeparator();
|
||||
};
|
||||
|
||||
#ifdef QUOTIENT_07
|
||||
void TextHandlerTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||
|
||||
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": 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));
|
||||
}
|
||||
#endif
|
||||
|
||||
void TextHandlerTest::allowedAttributes()
|
||||
{
|
||||
const QString testInputString1 = QStringLiteral("<p><span data-mx-spoiler><font color=#FFFFFF>Test</font><span></p>");
|
||||
const QString testOutputString1 = QStringLiteral("<p><span data-mx-spoiler><font color=#FFFFFF>Test</font><span></p>");
|
||||
// Handle urls where the href has either single (') or double (") quotes.
|
||||
const QString testInputString2 = QStringLiteral("<p><a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a></p>");
|
||||
const QString testOutputString2 = QStringLiteral("<p><a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a></p>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString1);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString1);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString1);
|
||||
|
||||
testTextHandler.setData(testInputString2);
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString2);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString2);
|
||||
}
|
||||
|
||||
void TextHandlerTest::stripDisallowedTags()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("<p>Allowed</p> <span>Allowed</span> <body>Disallowed</body>");
|
||||
const QString testOutputString = QStringLiteral("<p>Allowed</p> <span>Allowed</span> Disallowed");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::stripDisallowedAttributes()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("<p style=\"font-size:50px;\" color=#FFFFFF>Test</p>");
|
||||
const QString testOutputString = QStringLiteral("<p>Test</p>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that empty code tags are handled.
|
||||
* (this was a bug during development hence the test)
|
||||
*/
|
||||
void TextHandlerTest::emptyCodeTags()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("<pre><code></code></pre>");
|
||||
const QString testOutputString = QStringLiteral("<pre><code></code></pre>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::sendSimpleStringCase()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("This data should just be put in a paragraph.");
|
||||
const QString testOutputString = QStringLiteral("<p>This data should just be put in a paragraph.</p>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::sendSingleParaMarkup()
|
||||
{
|
||||
const QString testInputString = QStringLiteral(
|
||||
"Text para with **bold**, *italic*, [link](https://kde.org), , `inline code`.");
|
||||
const QString testOutputString = QStringLiteral(
|
||||
"<p>Text para with <strong>bold</strong>, <em>italic</em>, <a href=\"https://kde.org\">link</a>, <img "
|
||||
"src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e\" alt=\"image\">, <code>inline code</code>.</p>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::sendMultipleSectionMarkup()
|
||||
{
|
||||
const QString testInputString =
|
||||
QStringLiteral("Text para\n> blockquote\n* List 1\n* List 2\n1. one\n2. two\n# Heading 1\n## Heading 2\nhorizontal rule\n\n---\n```\ncodeblock\n```");
|
||||
const QString testOutputString = QStringLiteral(
|
||||
"<p>Text para</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>\n<ul>\n<li>List 1</li>\n<li>List "
|
||||
"2</li>\n</ul>\n<ol>\n<li>one</li>\n<li>two</li>\n</ol>\n<h1>Heading 1</h1>\n<h2>Heading 2</h2>\n<p>horizontal "
|
||||
"rule</p>\n<hr>\n<pre><code>codeblock\n</code></pre>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::sendBadLinks()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("[link](kde.org), ");
|
||||
const QString testOutputString = QStringLiteral("<p><a>link</a>, <img alt=\"image\"></p>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
}
|
||||
|
||||
/**
|
||||
* All text between code tags is treated as plain so it should get escaped.
|
||||
*/
|
||||
void TextHandlerTest::sendEscapeCode()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("```\n<p>Test <span style=\"font-size:50px;\">some</span> code</p>\n```");
|
||||
const QString testOutputString =
|
||||
QStringLiteral("<pre><code><p>Test <span style="font-size:50px;">some</span> code</p>\n</code></pre>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::sendCodeClass()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("```html\nsome code\n```\n<pre><code class=\"code-underline\">some more code</code></pre>");
|
||||
const QString testOutputString = QStringLiteral("<pre><code class=\"language-html\">some code\n</code></pre>\n<pre><code>some more code</code></pre>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveStripReply()
|
||||
{
|
||||
const QString testInputString = QStringLiteral(
|
||||
"<mx-reply><blockquote><a href=\"https://matrix.to/#/!somewhere:example.org/$event:example.org\">In reply to</a><a "
|
||||
"href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a><br />Message replied to.</blockquote></mx-reply>Reply message.");
|
||||
const QString testOutputString = QStringLiteral("Reply message.");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::recieveRichInPlainOut()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("a & b");
|
||||
const QString testOutputString = QStringLiteral("a & b");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receivePlainTextIn()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("<plain text in tag bracket>\nTest link https://kde.org.");
|
||||
const QString testOutputStringRich = QStringLiteral("<plain text in tag bracket><br>Test link <a href=\"https://kde.org\">https://kde.org</a>.");
|
||||
QString testOutputStringPlain = QStringLiteral("<plain text in tag bracket>\nTest link https://kde.org.");
|
||||
|
||||
// Make sure quotes are maintained in a plain string.
|
||||
const QString testInputString2 = QStringLiteral("last line is \"Time to switch to a new topic.\"");
|
||||
const QString testOutputString2 = QStringLiteral("last line is \"Time to switch to a new topic.\"");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::PlainText), testOutputStringRich);
|
||||
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputStringPlain);
|
||||
|
||||
testTextHandler.setData(testInputString2);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::PlainText), testOutputString2);
|
||||
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString2);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveStripNewlines()
|
||||
{
|
||||
const QString testInputStringPlain = QStringLiteral("Test\nmany\nnew\nlines.");
|
||||
const QString testInputStringRich = QStringLiteral("Test<br>many<br />new<br>lines.");
|
||||
const QString testOutputString = QStringLiteral("Test many new lines.");
|
||||
|
||||
const QString testInputStringPlain2 = QStringLiteral("* List\n* Items");
|
||||
const QString testOutputString2 = QStringLiteral("List Items");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputStringPlain);
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::PlainText, true), testOutputString);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::PlainText, nullptr, nullptr, true), testOutputString);
|
||||
|
||||
testTextHandler.setData(testInputStringRich);
|
||||
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText, true), testOutputString);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, nullptr, nullptr, true), testOutputString);
|
||||
|
||||
testTextHandler.setData(testInputStringPlain2);
|
||||
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText, true), testOutputString2);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a plain text output of a received string all html is stripped except for
|
||||
* code which is unescaped if it's html.
|
||||
*/
|
||||
void TextHandlerTest::receivePlainStripHtml()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("<p>Test</p> <pre><code>Some code <strong>with tags</strong></code></pre>");
|
||||
const QString testOutputString = QStringLiteral("Test Some code <strong>with tags</strong>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receivePlainStripMarkup()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("**bold** `<p>inline code</p>` *italic*");
|
||||
const QString testOutputString = QStringLiteral("bold <p>inline code</p> italic");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveRichUserPill()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("<p><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></p>");
|
||||
const QString testOutputString = QStringLiteral("<p><b><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></b></p>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveRichStrikethrough()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("<p><del>Test</del></p>");
|
||||
const QString testOutputString = QStringLiteral("<p><s>Test</s></p>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveRichtextIn()
|
||||
{
|
||||
const QString testInputString = QStringLiteral("<p>Test</p> <pre><code>Some code <strong>with tags</strong></code></pre>");
|
||||
const QString testOutputString = QStringLiteral("<p>Test</p> <pre><code>Some code <strong>with tags</strong></code></pre>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||
}
|
||||
|
||||
#ifdef QUOTIENT_07
|
||||
void TextHandlerTest::receiveRichMxcUrl()
|
||||
{
|
||||
const QString testInputString = QStringLiteral(
|
||||
"<img src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e\" alt=\"image\"><img src=\"mxc://kde.org/34c3464b3a1bd7f55af2d559e07d2c773c430e73\" "
|
||||
"alt=\"image\">");
|
||||
const QString testOutputString = QStringLiteral(
|
||||
"<img "
|
||||
"src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e?user_id=@bob:kde.org&room_id=%23myroom:kde.org&event_id=$143273582443PhrSn:example.org\" "
|
||||
"alt=\"image\"><img "
|
||||
"src=\"mxc://kde.org/34c3464b3a1bd7f55af2d559e07d2c773c430e73?user_id=@bob:kde.org&room_id=%23myroom:kde.org&event_id=$143273582443PhrSn:example.org\" "
|
||||
"alt=\"image\">");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputString);
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, room, room->messageEvents().back().get()), testOutputString);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* For when your rich input string has a plain text url left in.
|
||||
*
|
||||
* This test is to show that a url that is already rich will be left alone but a
|
||||
* plain one will be linkified.
|
||||
*/
|
||||
void TextHandlerTest::receiveRichPlainUrl()
|
||||
{
|
||||
// This is an actual link that caused trouble which is why it's so long. Keeping
|
||||
// so we can confirm consistent behaviour for complex urls.
|
||||
const QString testInputStringLink1 = QStringLiteral(
|
||||
"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im "
|
||||
"<a "
|
||||
"href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/"
|
||||
"$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im\">Link already rich</a>");
|
||||
const QString testOutputStringLink1 = QStringLiteral(
|
||||
"<a "
|
||||
"href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/"
|
||||
"$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im\">https://matrix.to/#/"
|
||||
"!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im</a> <a "
|
||||
"href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/"
|
||||
"$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im\">Link already rich</a>");
|
||||
|
||||
// Another real case. The linkification wasn't handling it when a single link
|
||||
// contains what looks like and email. It was been broken into 3 but needs to
|
||||
// be just single link.
|
||||
const QString testInputStringLink2 = QStringLiteral("https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/");
|
||||
const QString testOutputStringLink2 = QStringLiteral(
|
||||
"<a "
|
||||
"href=\"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/\">https://lore.kernel.org/lkml/"
|
||||
"CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/</a>");
|
||||
|
||||
QString testInputStringEmail = QStringLiteral(R"(email@example.com <a href="mailto:email@example.com">Link already rich</a>)");
|
||||
QString testOutputStringEmail =
|
||||
QStringLiteral(R"(<a href="mailto:email@example.com">email@example.com</a> <a href="mailto:email@example.com">Link already rich</a>)");
|
||||
|
||||
QString testInputStringMxId = QStringLiteral("@user:kde.org <a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a>");
|
||||
QString testOutputStringMxId = QStringLiteral(
|
||||
"<b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a></b>");
|
||||
|
||||
TextHandler testTextHandler;
|
||||
testTextHandler.setData(testInputStringLink1);
|
||||
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringLink1);
|
||||
|
||||
testTextHandler.setData(testInputStringLink2);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringLink2);
|
||||
|
||||
testTextHandler.setData(testInputStringEmail);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringEmail);
|
||||
|
||||
testTextHandler.setData(testInputStringMxId);
|
||||
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringMxId);
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveLineSeparator()
|
||||
{
|
||||
auto text = QStringLiteral("foo\u2028bar");
|
||||
TextHandler textHandler;
|
||||
textHandler.setData(text);
|
||||
QCOMPARE(textHandler.handleRecievePlainText(Qt::PlainText, true), QStringLiteral("foo bar"));
|
||||
}
|
||||
|
||||
QTEST_MAIN(TextHandlerTest)
|
||||
#include "texthandlertest.moc"
|
||||
@@ -2,7 +2,7 @@
|
||||
<!--
|
||||
- SPDX-License-Identifier: CC0-1.0
|
||||
- SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carlschwan@kde.org>
|
||||
- SPDX-FileCopyrightText: 2020-2021 Tobias Fella <fella@posteo.de>
|
||||
- SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
||||
-->
|
||||
<component type="desktop">
|
||||
<id>org.kde.neochat</id>
|
||||
@@ -22,6 +22,7 @@
|
||||
<name xml:lang="eu">NeoChat</name>
|
||||
<name xml:lang="fi">NeoChat</name>
|
||||
<name xml:lang="fr">NeoChat</name>
|
||||
<name xml:lang="gl">NeoChat</name>
|
||||
<name xml:lang="hu">NeoChat</name>
|
||||
<name xml:lang="ia">Neochat</name>
|
||||
<name xml:lang="id">NeoChat</name>
|
||||
@@ -47,8 +48,8 @@
|
||||
<summary>A client for matrix, the decentralized communication protocol</summary>
|
||||
<summary xml:lang="ar">عميل لماتركس، ميفاق الاتصال اللامركزي</summary>
|
||||
<summary xml:lang="az">Matrix üçün müştəri, mərkəzləşməmiş kommunikasiya protokolu</summary>
|
||||
<summary xml:lang="ca">Un client per al Matrix, el protocol de comunicacions descentralitzat</summary>
|
||||
<summary xml:lang="ca-valencia">Un client per a Matrix, el protocol de comunicacions descentralitzat</summary>
|
||||
<summary xml:lang="ca">Un client de Matrix, el protocol de comunicacions descentralitzat</summary>
|
||||
<summary xml:lang="ca-valencia">Un client de Matrix, el protocol de comunicacions descentralitzat</summary>
|
||||
<summary xml:lang="cs">Klient pro decentralizovaný komunikační protokol matrix</summary>
|
||||
<summary xml:lang="de">Ein Programm für Matrix, das dezentrale Kommunikationsprotokoll</summary>
|
||||
<summary xml:lang="el">Ένας πελάτης για το Matrix, το αποκεντρωμένο πρωτόκολλο επικοινωνίας</summary>
|
||||
@@ -57,6 +58,7 @@
|
||||
<summary xml:lang="eu">Matrix, deszentralizatutako komunikazio protokolorako bezero bat</summary>
|
||||
<summary xml:lang="fi">Asiakas Matrixille, hajautetulle viestintäyhteyskäytännölle</summary>
|
||||
<summary xml:lang="fr">Un client pour « Matrix », le protocole décentralisé de communications.</summary>
|
||||
<summary xml:lang="gl">Un cliente para Matrix, o protocolo de comunicación descentralizada</summary>
|
||||
<summary xml:lang="hu">Kliens a matrixhoz, a decentralizált kommunikációs protokollhoz</summary>
|
||||
<summary xml:lang="ia">Un cliente per Matrix, le protocollo de communication decentralisate</summary>
|
||||
<summary xml:lang="id">Klien untuk matrix, protokol komunikasi terdesentralisasi</summary>
|
||||
@@ -65,7 +67,7 @@
|
||||
<summary xml:lang="ka">კლიენტი Matrix-სთვის, დეცენტრალიზებული კომუნიკაციის პროტოკოლისთვის</summary>
|
||||
<summary xml:lang="ko">Matrix, 분산 대화 프로토콜 클라이언트</summary>
|
||||
<summary xml:lang="nl">Een client voor matrix, het gedecentraliseerde communicatieprotocol</summary>
|
||||
<summary xml:lang="nn">Ein klient for Matrix, den desentraliserte lynmeldingsprotokollen</summary>
|
||||
<summary xml:lang="nn">Ein klient for Matrix – protokollen for desentralisert kommunikasjon</summary>
|
||||
<summary xml:lang="pa">ਮੈਟਰਿਕਸ, ਸਰਬ-ਸਾਂਝੇ ਸੰਚਾਰ ਪਰੋਟੋਕਾਲ, ਲਈ ਕਲਾਈਂਟ ਹੈ</summary>
|
||||
<summary xml:lang="pl">Program do obsługi matriksa, rozproszonego protokołu porozumiewania się</summary>
|
||||
<summary xml:lang="pt">Um cliente para o Matrix, o protocolo de comunicação descentralizado</summary>
|
||||
@@ -74,104 +76,90 @@
|
||||
<summary xml:lang="sk">Klient pre matrix, decentralizovaný komunikačný protokol</summary>
|
||||
<summary xml:lang="sl">Odjemalec za matrix, decentralizirani komunikacijski protokol</summary>
|
||||
<summary xml:lang="sv">En klient för Matrix, det decentraliserade kommunikationsprotokollet</summary>
|
||||
<summary xml:lang="ta">மையமில்லா தகவல் பரிமாற்ற நெறிமுறையான மேட்ரிக்ஸுக்கான செயலி</summary>
|
||||
<summary xml:lang="tr">Merkezi olmayan iletişim protokolü Matrix için bir istemci</summary>
|
||||
<summary xml:lang="uk">Клієнт matrix, децентралізованого протоколу обміну даними</summary>
|
||||
<summary xml:lang="x-test">xxA client for matrix, the decentralized communication protocolxx</summary>
|
||||
<summary xml:lang="zh-CN">分布式通讯协议 Matrix 的客户端</summary>
|
||||
<description>
|
||||
<p>NeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.</p>
|
||||
<p xml:lang="ar">نيوتشات هو عميل ماتركس Matrix. يتيح لك إرسال رسائل نصية ومقاطع فيديو وملفات صوتية إلى عائلتك وزملائك وأصدقائك باستخدام بروتوكول ماتركس</p>
|
||||
<p xml:lang="az">NeoChat Mtrix müştərisidir. O, Matrix protokolundan istifadə edərək, ailənizə, dostlarınıza, iş yoldaşlarınıza mətn, səsli və görüntülü ismarıclar göndərməyə imkan verir.</p>
|
||||
<p xml:lang="ca">El NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics usant el protocol Matrix.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics utilitzant el protocol Matrix.</p>
|
||||
<p xml:lang="de">NeoChat ist ein Matrix-Client. Er ermöglicht Ihnen das Senden von Textnachrichten, Videos und Audiodateien an Ihre Familie, Kollegen und Freunde unter Verwendung des Matrix-Protokolls.</p>
|
||||
<p xml:lang="el">Το NeoChat είναι μια εφαρμογή του Matrix. Σας επιτρέπει να στέλνετε μηνύματα κειμένου, βίντεο και ήχο στην οικογένειά σας, σε συναδέλφους και φίλους με το πρωτόκολλο Matrix.</p>
|
||||
<p xml:lang="en-GB">NeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.</p>
|
||||
<p xml:lang="es">NeoChat es un cliente para Matrix. Le permite enviar mensajes de texto, vídeos y archivos de sonido a su familia, compañeros de trabajo y amigos usando el protocolo Matrix.</p>
|
||||
<p xml:lang="eu">NeoChat Matrix bezero bat da. Familiari, lankideei eta lagunei testu-mezuak, bideoak eta audio-fitxategiak bidaltzeko aukera ematen du, Matrix protokoloa erabiliz.</p>
|
||||
<p xml:lang="fi">NeoChat on Matrix-asiakas. Sillä voi lähettää perheelle, tuttaville ja kavereille tekstiviestejä sekä video- ja äänitiedostoja Matrix-yhteyskäytännöllä.</p>
|
||||
<p xml:lang="fr">NeoChat est un client Matrix. Il vous permet d'envoyer des messages de texte, des vidéos et des fichiers audio à votre famille, vos collègues et vos amis en utilisant le protocole Matrix.</p>
|
||||
<p xml:lang="hu">A NeoChat egy Matrix kliens. Szöveges üzeneteket, videókat ésaudio fájlokat küldhet családjának, kollégáinak és barátainak a Matrix protokoll használatával.</p>
|
||||
<p xml:lang="ia">NeoChat es un cliente de Matrix. Illo te permitte inviar messager de texto, files de video e audio a tu familia, collegas e amicos usante le protocollo de Matrix.</p>
|
||||
<p xml:lang="id">NeoChat adalah sebuah klien Matrix. Memungkinkan Anda untuk mengirim pesan teks, file video dan audio ke keluarga, kolega dan teman Anda menggunakan protokol Matrix.</p>
|
||||
<p xml:lang="ie">NeoChat es un cliente de Matrix. On posse usar it por inviar missages textual, video- e audiofiles a su familie, colegos e amics med li protocol Matrix.</p>
|
||||
<p xml:lang="it">NeoChat è un client Matrix. Ti consente di inviare messaggi di testo, file video e audio a familiari, colleghi e amici utilizzando il protocollo Matrix.</p>
|
||||
<p xml:lang="ka">NeoChat არის Matrix კლიენტი. ის საშუალებას გაძლევთ გაგზავნოთ ტექსტური შეტყობინებები, ვიდეოები და აუდიო ფაილები თქვენს ოჯახს, კოლეგებსა და მეგობრებს მატრიქსის პროტოკოლის გამოყენებით.</p>
|
||||
<p xml:lang="ko">NeoChat은 Matrix 클라이언트입니다. Matrix 프로토콜을 사용하여 가족, 동료, 친구에게 텍스트 메시지, 동영상, 오디오 파일을 전송할 수 있습니다.</p>
|
||||
<p xml:lang="nl">NeoChat is een Matrix-client. Het biedt u het verzenden van tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden met het Matrix-protocol.</p>
|
||||
<p xml:lang="pl">NeoChat jest programem do Matrisa. Umożliwia wysyłanie wiadomości tekstowych, filmów oraz dźwięku do twojej rodziny, znajomych oraz przyjaciół poprzez protokół Matriksa.</p>
|
||||
<p xml:lang="pt">O NeoChat é um cliente do Matrix. O mesmo permite-lhe enviar mensagens de texto, ficheiros de vídeo e áudio para a sua família, colegas e amigos com o protocolo Matrix.</p>
|
||||
<p xml:lang="pt-BR">O NeoChat é um cliente Matrix. Ele permite a você enviar mensagens de texto, arquivos de vídeo e áudio para seus familiares, colegas e amigos usando o protocolo Matrix.</p>
|
||||
<p xml:lang="ru">NeoChat — это клиент, поддерживающий работу с протоколом Matrix. Он позволяет отправлять текстовые сообщения, видео и аудиофайлы.</p>
|
||||
<p xml:lang="sk">NeoChat je Matrix klient. Umožňuje vám posielať textové správy, videá a zvukové súbory rodine, kolegom a priateľom pomocou protokolu Matrix.</p>
|
||||
<p xml:lang="sl">NeoChat je odjemalec Matrixa. Dovoljuje vam pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, kolegom in prijateljem z uporabo protokola Matrix.</p>
|
||||
<p xml:lang="sv">NeoChat är en Matrix-klient. Den låter dig skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner med användning av Matrix-protokollet.</p>
|
||||
<p xml:lang="tr">NeoChat, bir Matrix istemcisidir. Matrix protokolünü kullanarak ailenize, iş arkadaşlarınıza, arkadaşlarınıza ve başkalarına metin iletileri, video ve ses dosyaların göndermenize olanak verir.</p>
|
||||
<p xml:lang="uk">NeoChat — клієнт мережі обміну повідомленнями Matrix. За допомогою цієї програми ви зможете надсилати текстові повідомлення, відео та звукові файли вашій родині, колегам та друзям за допомогою протоколу Matrix.</p>
|
||||
<p xml:lang="x-test">xxNeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.xx</p>
|
||||
<p xml:lang="zh-CN">NeoChat 是一个 Matrix 客户端。 它允许您使用 Matrix 协议向您的家人、同事和朋友发送文本消息、视频和音频文件。</p>
|
||||
<p>Matrix is a decentralized communication protocol, putting the user back in control. Currently NeoChat implements large part of the protocol with the exception of encrypted chats and video chat.</p>
|
||||
<p xml:lang="ar">ماتريكس هو بروتوكول اتصال لامركزي ، يعيد المستخدم إلى السيطرة. يطبق نيوتشات حاليًا جزءًا كبيرًا من الميفاق باستثناء الدردشات المشفرة ودردشة الفيديو.</p>
|
||||
<p xml:lang="az">Matrix, istifadəçini nəzarətdə saxlayan, mərkəzləşməmişi rabitə protokoludur. NeoChat, söhbətin və video əlaqəsinin şifrələnməsindən başqa bir çox protokolları həyata keçirə bilir.</p>
|
||||
<p xml:lang="ca">Matrix és un protocol de comunicacions descentralitzat, que retorna el control a l'usuari. Actualment el NeoChat implementa una gran part del protocol amb l'excepció dels xats encriptats i els xats de vídeo.</p>
|
||||
<p xml:lang="ca-valencia">Matrix és un protocol de comunicacions descentralitzat, que retorna el control a l'usuari. Actualment NeoChat implementa una gran part del protocol amb l'excepció dels xats encriptats i els xats de vídeo.</p>
|
||||
<p xml:lang="de">Matrix ist ein dezentralisiertes Kommunikationsprotokoll, das dem Benutzer wieder die Kontrolle zurückgibt. Derzeit implementiert NeoChat einen großen Teil des Protokolls mit der Ausnahme von verschlüsselten Chats und Video-Chat.</p>
|
||||
<p xml:lang="el">Το Matrix είναι ένα αποκεντρωμένο πρωτόκολλο επικοινωνίας, δίνοντας πίσω στον χρήστη τον έλεγχο. Προς το παρόν το NeoChat υλοποιεί ένα μεγάλο μέρος του πρωτοκόλλου με εξαίρεση τις κρυπτογραφημένες συνομιλίες και τη συνομιλία με βίντεο.</p>
|
||||
<p xml:lang="en-GB">Matrix is a decentralised communication protocol, putting the user back in control. Currently NeoChat implements large part of the protocol with the exception of encrypted chats and video chat.</p>
|
||||
<p xml:lang="es">Matrix es un protocolo de comunicaciones descentralizado, que devuelve el control al usuario. En la actualidad, NeoChat implementa gran parte del protocolo con la excepción de chats cifrados y chats de vídeo.</p>
|
||||
<p xml:lang="eu">Matrix komunikazio-protokolo deszentralizatu bat da, erabiltzaileari kontrola itzultzen diona. Gaur egun, NeoChat-ek protokoloaren zati handi bat inplementatzen du, berriketa zifratuak eta bideo berriketak izan ezik.</p>
|
||||
<p xml:lang="fi">Matrix on hajautettu viestintäyhteyskäytäntö, joka antaa hallinnan takaisin käyttäjille. NeoChat tarjoaa nykyisellään valtaosan yhteyskäytännöstä salattuja keskustelu- ja videokeskusteluja lukuun ottamatta.</p>
|
||||
<p xml:lang="fr">Matrix est un protocole de communication décentralisé, donnant le contrôle à l'utilisateur. Actuellement, NeoChat met en œuvre une grande partie du protocole, à l'exception des discussions chiffrées et du chat vidéo.</p>
|
||||
<p xml:lang="hu">A Matrix egy decentralizált kommunikációs protokoll, amely a felhasználók kezébe adja az irányítást.</p>
|
||||
<p xml:lang="ia">Matrix es un protocollo de communication decentrate, ponente le usator in le controlo. Currentemente NeoChat implementa un grande parte del protocollo con le exception de conversationes cryptate e conversationes video.</p>
|
||||
<p xml:lang="id">Matrix adalah protokol komunikasi terdesentralisasi, menempatkan pengguna kembali dalam kendali. Saat ini NeoChat mengimplementasikan sebagian besar protokol dengan pengecualian obrolan terenkripsi dan obrolan video.</p>
|
||||
<p xml:lang="ie">Matrix es un decentralisat protocol de communication quel retroda li control a un usator. Actualmen, NeoChat implementa un grand parte del protocol except ciffrat conversationes e videotelefonada.</p>
|
||||
<p xml:lang="it">Matrix è un protocollo di comunicazione decentralizzato, che restituisce all'utente il controllo. Attualmente NeoChat implementa gran parte del protocollo ad eccezione delle chat cifrate e delle chat video.</p>
|
||||
<p xml:lang="ka">Matrix -ი დეცენტრალიზებული კომუნიკაციის პროტოკოლია, რომელიც მომხმარებელს კონტროლის უფლებას უბრუნებს. ამჟამად NeoChat-ს პროტოკოლის ძირითადი ნაწილის მხარდაჭერა გააჩნია, დაშიფვრისა და ვიდეოჩატის გამოკლებით.</p>
|
||||
<p xml:lang="ko">Matrix는 사용자에게 제어권을 돌려 주는 분산 통신 프로토콜입니다. NeoChat은 암호화된 대화 및 영상 통화를 제외한 프로토콜의 대부분 기능을 구현합니다.</p>
|
||||
<p xml:lang="nl">Matrix is een gedecentraliseerd communicatieprotocol, dat de gebruiker de controle teruggeeft. Op dit moment implementeert NeoChat grote delen van het protocol met de uitzondering van versleutelde chats en video-chat.</p>
|
||||
<p xml:lang="pl">Matrix jest protokołem rozproszonego porozumiewania się oddający użytkownikowi jego władzę. Obecnie NeoChat obsługuje dużą część protokołu poza szyfrowanymi rozmowami tekstowymi i z obrazem.</p>
|
||||
<p xml:lang="pt">O Matrix é um protocolo de comunicações descentralizado, colocando de novo o utilizador no poder. De momento, o NeoChat implementa uma boa parte do protocolo, com a excepção das conversas encriptadas e as conversas de vídeo.</p>
|
||||
<p xml:lang="pt-BR">O Matrix é um protocolo de comunicação descentralizado, colocando o usuário de volta no controle. Atualmente o NeoChat implementa grande parte do protocolo com a exceção de bate-papos criptografados e bate-papo por vídeo.</p>
|
||||
<p xml:lang="ru">Matrix — это децентрализованный коммуникационный протокол, возвращающий пользователю контроль над своими данными. В настоящее время в приложении NeoChat реализована поддержка большей части протокола, за исключением зашифрованных чатов и видеочата.</p>
|
||||
<p xml:lang="sk">Matrix je decentralizovaný komunikačný protokol, ktorý používateľovi vracia kontrolu. V súčasnosti NeoChat implementuje veľkú časť protokolu s výnimkou šifrovaných chatov a videohovorov.</p>
|
||||
<p xml:lang="sl">Matrix je decentraliziran komunikacijski protokol, kjer ima uporabnik uporabnik kontrolo rabe. Trenutno ima NeoChat izveden velik del protokola z izjemo šifriranih klepetov in video klepetov.</p>
|
||||
<p xml:lang="sv">Matrix är ett decentraliserat kommunikationsprotokoll, som ger tillbaka kontrollen till användaren. För närvarande implementerar NeoChat en stor del av protokollet, med undantag för krypterad chatt och videochatt.</p>
|
||||
<p xml:lang="tr">Matrix; tam denetimi kullanıcıya bırakan, merkezi olmayan bir iletişim protokolüdür. Şu anda NeoChat, uçtan uca şifrelenmiş metin ve video sohbetleri dışında protokolün büyük bir bölümünü bünyesinde bulundurur.</p>
|
||||
<p xml:lang="uk">Matrix — протокол децентралізованого спілкування, який передає контроль над даними користувачеві. У поточній версії NeoChat реалізовано більшу частину протоколу, окрім зашифрованого спілкування та відеоспілкування.</p>
|
||||
<p xml:lang="x-test">xxMatrix is a decentralized communication protocol, putting the user back in control. Currently NeoChat implements large part of the protocol with the exception of encrypted chats and video chat.xx</p>
|
||||
<p xml:lang="zh-CN">Matrix 是一个分布式通讯协议,使用户重新得到控制权。 目前,NeoChat 实现了协议的大部分,除了加密聊天和视频聊天。</p>
|
||||
<p>NeoChat works both on mobile and desktop while providing a consistent user experience.</p>
|
||||
<p xml:lang="ar">يعمل نيوتشات على كل من الأجهزة المحمولة وسطح المكتب مع توفير تجربة مستخدم متسقة.</p>
|
||||
<p xml:lang="az">Vahid istifadəçi interfeysi ilə təmin olunan NeoChat, həm mobil telefonda həm də kompyuterlərdə işləyir.</p>
|
||||
<p xml:lang="ca">El NeoChat funciona en els mòbils i a l'escriptori, proporcionant una experiència d'usuari coherent.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat funciona en els mòbils i en l'escriptori, proporcionant una experiència d'usuari coherent.</p>
|
||||
<p xml:lang="de">NeoChat funktioniert sowohl auf Mobilgeräten als auch auf dem PC und bietet ein einheitliches Benutzererlebnis.</p>
|
||||
<p xml:lang="el">Το NeoChat λειτουργεί και στα κινητά και στους υπολογιστές γραφείου παρέχοντας μια αδιάλειπτη εμπειρία χρήσης.</p>
|
||||
<p xml:lang="en-GB">NeoChat works both on mobile and desktop while providing a consistent user experience.</p>
|
||||
<p xml:lang="es">NeoChat funciona en móviles y en el escritorio a la vez que proporciona una experiencia de usuario consistente.</p>
|
||||
<p xml:lang="eu">NeoChat mugikorretan eta mahaigainean dabil, erabiltzaile esperientzia koherentea eskainiz.</p>
|
||||
<p xml:lang="fi">NeoChat toimii sekä mobiili- että työpöytäalustoilla tarjoten yhdenmukaisen käyttökokemuksen.</p>
|
||||
<p xml:lang="fr">NeoChat fonctionne aussi bien sur les mobiles que sur les ordinateurs de bureau, tout en offrant une expérience utilisateur cohérente.</p>
|
||||
<p xml:lang="hu">A NeoChat mobilon és asztali számítógépen is működik, egységes felhasználói élményt nyújtva.</p>
|
||||
<p xml:lang="ia">NeoChat functiona sia sur mobile que ur scriptorio durante que forni un experientia de usator consistente.</p>
|
||||
<p xml:lang="id">NeoChat berfungsi baik di ponsel dan desktop sambil memberikan pengalaman pengguna yang konsisten.</p>
|
||||
<p xml:lang="ie">NeoChat supporta amb computatores e aparates mobil, ancor providente un consistent experientie de usator.</p>
|
||||
<p xml:lang="it">NeoChat funziona sia su dispositivi mobili che desktop, fornendo un'esperienza utente coerente.</p>
|
||||
<p xml:lang="ka">NeoChat მუშაობს როგორც მობილურ, ისე ჩვეულებრივ კომპიუტერებზე იმ დროს, როცა მომხმარებლის ინტერფეისი არ იცვლება.</p>
|
||||
<p xml:lang="ko">NeoChat은 모바일과 데스크톱 모두에서 일관된 사용자 경험을 제공합니다.</p>
|
||||
<p xml:lang="nl">NeoChat werkt zowel op de mobiel en het bureaublad met het leveren van een consistente gebruikerservaring.</p>
|
||||
<p xml:lang="pl">NeoChat działa zarówno na urządzeniach przenośnych jak i biurkowych, zapewniając spójne wrażenia użytkownika</p>
|
||||
<p xml:lang="pt">O NeoChat funciona tanto em dispositivos móveis como no computador, fornecendo uma experiência de utilizador consistente.</p>
|
||||
<p xml:lang="pt-BR">O NeoChat funciona tanto no celular como no computador enquanto fornece uma experiência consistente ao usuário.</p>
|
||||
<p xml:lang="ru">NeoChat работает как на мобильных устройствах, так и на настольных компьютерах, обеспечивая единый пользовательский интерфейс.</p>
|
||||
<p xml:lang="sk">NeoChat funguje na mobilných aj stolových počítačoch a poskytuje konzistentný používateľský zážitok.</p>
|
||||
<p xml:lang="sl">NeoChat deluje tako na mobilnih kot na namiznih platformah z zagotavljanjem konsistentne uporabniške izkušnje.</p>
|
||||
<p xml:lang="sv">NeoChat fungerar både på mobil och skrivbord och tillhandahåller en konsekvent användarupplevelse.</p>
|
||||
<p xml:lang="tr">NeoChat, hem masaüstü hem de taşınabilir ortamlarda çalışarak tutarlı bir kullanıcı deneyimi sunar.</p>
|
||||
<p xml:lang="uk">NeoChat працює на мобільних пристроях та звичайних комп'ютерах, маючи однорідний інтерфейс на усіх підтримуваних пристроях.</p>
|
||||
<p xml:lang="x-test">xxNeoChat works both on mobile and desktop while providing a consistent user experience.xx</p>
|
||||
<p xml:lang="zh-CN">NeoChat 在移动设备和桌面上均可用,并提供一致的用户体验。</p>
|
||||
<p>NeoChat is a client for Matrix, the decentralized communication protocol for instant messaging. It allows you to send text messages, videos and audio files to your family, colleagues and friends. It uses KDE frameworks and most notably Kirigami
|
||||
to provide a convergent experience across multiple platforms.</p>
|
||||
<p xml:lang="ar">نيوتشات هو عميل ماتركس Matrix، (ميفاق الاتصال اللامركزي للمراسلة الفورية). يتيح لك نيوتشات إرسال رسائل نصية ومقاطع فيديو وملفات صوتية إلى عائلتك وزملائك وأصدقائك. يستخدم أطر عمل كيدي وأبرزها Kirigami لتوفير تجربة متقاربة عبر منصات متعددة.</p>
|
||||
<p xml:lang="ca">El NeoChat és un client de Matrix, el protocol descentralitzat de comunicacions de missatgeria instantània. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics. Fa servir els Frameworks de KDE i, sobretot, el Kirigami per a proporcionar una experiència convergent a través de diverses plataformes.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat és un client de Matrix, el protocol descentralitzat de comunicacions de missatgeria instantània. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics. Utilitza els Frameworks de KDE i, sobretot, Kirigami per a proporcionar una experiència convergent a través de diverses plataformes.</p>
|
||||
<p xml:lang="en-GB">NeoChat is a client for Matrix, the decentralised communication protocol for instant messaging. It allows you to send text messages, videos and audio files to your family, colleagues and friends. It uses KDE frameworks and most notably Kirigami to provide a convergent experience across multiple platforms.</p>
|
||||
<p xml:lang="es">NeoChat es un cliente para Matrix, el protocolo de comunicaciones descentralizado para mensajería instantánea. Le permite enviar mensajes de texto, vídeos y archivos de sonido a su familia, compañeros de trabajo y amigos. Usa la infraestructura de KDE y, en particular, Kirigami para proporcionar una experiencia convergente en muchas plataformas.</p>
|
||||
<p xml:lang="fi">NeoChat on asiakassovellus Matrixille, hajautetulle pikaviestinyhteyskäytännölle. Sillä voi lähettää teksti-, video- ja ääniviestejä perheelle, tutuille ja ystäville. Se käyttää KDE-kehystä ja erityisesti Kirigamia tuottaakseen mukautuvan monialustaisen käyttökokemuksen.</p>
|
||||
<p xml:lang="fr">NeoChat est un client pour le protocole Matrix, un protocole décentralisé de communications pour messagerie instantané. Il vous permet d'envoyer des messages de texte, des vidéos et des fichiers audio à votre famille, vos collègues et vos amis. Il utilise les environnements de développement et plus précisément Kirigami pour fournir une expérience convergente sur plusieurs plate-formes. </p>
|
||||
<p xml:lang="gl">NeoChat é un cliente para Matrix, o protocolo de comunicación descentralizada para mensaxería instantánea. Podes enviar mensaxes de texto, vídeos e ficheiros de son á túa familia, colegas e amizades. Usas infraestruturas de KDE e principalmente Kirigami para proporcionar unha experiencia de uso converxente para varias plataformas.</p>
|
||||
<p xml:lang="ia">NeoChat es un cliente per Matrix, le protocollo de communication decentralisate per messager instantanee. Illo te permitte inviar messager de texto, files de video e audio a tu familia, collegas e amicos usante. Illo usa KDE frameworks e super toto Kirigamii forni un experientia convergente trans platteforme multiple.</p>
|
||||
<p xml:lang="it">NeoChat è un client per Matrix, il protocollo di comunicazione decentralizzato per la messaggistica istantanea. Ti consente di inviare messaggi di testo, video e file audio a familiari, colleghi e amici. Utilizza i framework KDE e in particolare Kirigami per fornire un'esperienza convergente su più piattaforme.</p>
|
||||
<p xml:lang="ka">NeoChat არის Matrix კლიენტი. ის საშუალებას გაძლევთ გაგზავნოთ ტექსტური შეტყობინებები, ვიდეოები და აუდიო ფაილები თქვენს ოჯახს, კოლეგებსა და მეგობრებს მატრიქსის პროტოკოლის გამოყენებით. ის იყენებს KDE-ის შესაძლებლობებს და განსაკუთრებით კი Kirigami-ის, რათა პლატფორმებს შორის თავსებადობა უზრუნველყოს.</p>
|
||||
<p xml:lang="nl">NeoChat is een client voor Matrix, het gedecentraliseerde communicatieprotocol voor instant messages. Het biedt u het verzenden van tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden. Het gebruik KDE frameworks en het meest opmerkelijk Kirigami om een convergente ervaring te leveren op meerdere platforms.</p>
|
||||
<p xml:lang="nn">NeoChat er ein klient for Matrix, ein protokoll for desentralisert kommunikasjon. Du kan utveksla tekst, lyd og videoar med kollegaar, vennar og familie. Programmet brukar KDE Frameworks og Kirigami for å gje ei brukarflate tilpassa ulike plattformer.</p>
|
||||
<p xml:lang="pl">NeoChat jest programem do Matriksa, protokołu rozproszonego porozumiewania się w czasie rzeczywistym. Umożliwia wysyłanie wiadomości tekstowych, filmów oraz dźwięku do twojej rodziny, znajomych oraz przyjaciół. Używa szkieletów KDE i głównie Kirigami, aby zapewnić spójne wrażenia na wielu platformach</p>
|
||||
<p xml:lang="sl">Neochat je odjemalec za Matrix, decentralizirani komunikacijski protokol za takojšnje sporočanje. Omogoča vam pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek svoji družini, sodelavcem in prijateljem. Uporablja okvire KDE in predvsem Kirigami, da zagotavlja konvergentno izkušnjo na več platformah.</p>
|
||||
<p xml:lang="tr">NeoChat, anlık iletileşme için merkezi olmayan iletişim protokolü olan Matrix için bir istemcidir. Ailenize, iş arkadaşlarınıza ve arkadaşlarınıza metin iletiler, videolar ve ses dosyaları göndermenize olanak tanır. Birden çok platformda yakınsak bir deneyim sağlamak için KDE Frameworks ve en önemlilerinden Kirigami'yi kullanır.</p>
|
||||
<p xml:lang="uk">NeoChat — клієнт Matrix, децентралізованого протоколу спілкування для миттєвого обміну повідомленнями. За його допомогою ви можете надсилати текстові повідомлення, відео та звукові файли вашій родин, колегами та друзям. У програмі використано бібліотеки KDE, зокрема Kirigami, для надання однорідного середовища на декількох програмних та апаратних платформах.</p>
|
||||
<p xml:lang="x-test">xxNeoChat is a client for Matrix, the decentralized communication protocol for instant messaging. It allows you to send text messages, videos and audio files to your family, colleagues and friends. It uses KDE frameworks and most notably Kirigami to provide a convergent experience across multiple platforms.xx</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 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="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>
|
||||
<p xml:lang="fi">NeoChat pyrkii olemaan Matrix-määritelmän täysominaisuuksinen sovellus, joten se tukee kaikkea nykyisessä vakaassa määritelmässä muutamaa huomattavaa poikkeusta lukuun ottamatta (VoIP, säikeet ja jotkin piirteet päästä päähän -salauksessa). Joitakin pienempiäkin puutteita on Matrix-määritelmän jatkuvan kehityksen vuoksi, mutta lopputavoitteena on tarjota määritelmän täysi tuki.</p>
|
||||
<p xml:lang="fr">L'objectif de NeoChat est d'être une application complète pour le protocole Matrix. En tant que tel, tout dans la spécification stable actuelle avec les exceptions notables de VoIP, les processus et certains aspects du chiffrement de bout en bout sont pris en charge. Il y a quelques autres petites omissions en raison du fait que la spécification du protocole Matrix est en constante évolution. Cependant, l'objectif reste de fournir un soutien éventuel pour l'ensemble de la spécification.</p>
|
||||
<p xml:lang="gl">NeoChat pretende ser unha aplicación completa para a especificación de Matrix. Coas excepcións de VoIP, conversas fiadas e algúns aspectos da cifraxe de extremo a extremo, a versión estábel segue as especificacións. Existen algunhas outras pequenas omisións debido ao feito de que Matrix está en continua evolución pero a intención é implementar a especificación completa.</p>
|
||||
<p xml:lang="ia">NeoChat aspira a esser un application plenemente eminente per le specification de Matrix. Tal como omne cosas in le specification currentemente stabile con le exceptiones notabile de VOIP, threads e alcun aspectos del cryptation End-to-End es supportate. Il ha ltere pauc omissiones, debite al facto que le specification de Matrix es in evolution constante ma le aspiration remane a fornir supporto eventual per le integre specification.</p>
|
||||
<p xml:lang="it">NeoChat mira ad essere un'applicazione completa per le specifiche Matrix. Pertanto, sono supportati tutti gli elementi dell'attuale specifica stabile con le notevoli eccezioni di VoIP, conversazioni e alcuni aspetti della cifratura end-to-end. Ci sono alcune altre piccole omissioni dovute al fatto che le specifiche Matrix sono in continua evoluzione, ma l'obiettivo rimane quello di fornire un eventuale supporto per l'intera specifica.</p>
|
||||
<p xml:lang="ka">NeoChat-ი მიზნად ისახავს Matrix სპეციფიკაციის სრული განხორციელება ჰქონდეს. როგორც ასეთი, ყველაფერი მიმდინარე სპეციფიკაციიდან, VoIP-ის, ძაფებისა და გამჭოლი დაშიფვრის ზოგიერთი ასპექტის გარდა, მხარდაჭერილია. შეძლება ასევე იყოს მცირე ლაფსუსებიც იმის გამო, რომ Matrix-ის სპეციფიკაცია მუდმივად ვითარგდება, მაგრამ ჩვენი მიზანი მისი სრული მხარდაჭერაა.</p>
|
||||
<p xml:lang="nl">NeoChat richt zich op het volledig bieden van alle mogelijkheden van de Matrix-specificatie. Alles in de huidige stabiele specificatie met merkbare uitzondering van VoIP, gekoppelde discussies en sommige aspecten van eind-tot-eind versleuteling worden ondersteund. Er zijn een paar andere kleinere omissies vanwege het feit dat de Matrix specificatie constant evolueert maar het doel blijft het eventueel bieden van ondersteuning van de gehele specificatie.</p>
|
||||
<p xml:lang="nn">NeoChat har som mål å støtta all funksjonalitet i Matrix-spesifikasjonen. Førebels er alt i den gjeldande stabile spesifikasjonen støtta, med unntak av VoIP, trådar og nokre delar av ende-til-kryptering. Det finst òg andre småting som ikkje er støtta, sidan Matrix-spesifikasjon er i stadig endring, men målet er altså støtte for alt.</p>
|
||||
<p xml:lang="pl">NeoChat w zamyśle ma być pełnowartościową aplikacją wg wytycznych Matriksa. Z tego powodu, wszystko, co jest obecnie w stabilnych wytycznych z pominięciem VoIP, wątków i niektórych części szyfrowania Użytkownik-do-Użytkownika są obecnie obsługiwane. Pominięto też kilka mniejszych rzeczy ze względu na ciągły rozwój wytycznych Matriksa, lecz celem nadal jest zapewnienie obsługi wszystkich wytycznych.</p>
|
||||
<p xml:lang="sl">Neochat cilja, da bi bila popolna aplikacija po specifikaciji Matrixa. Kot takšna vsebuje vse v trenutni stabilni specifikaciji z pomembnimi izjemami pri VoIP, nitih in nekaterih vidikov šifriranja od konca do konca. Obstaja nekaj drugih manjših opustitev zaradi dejstva, da se specifikacija Matrix nenehno razvija, vendar cilj ostaja zagotoviti morebitno podporo celotni specifikaciji.</p>
|
||||
<p xml:lang="tr">NeoChat, Matrix belirtimi için tam özellikli bir uygulama olmayı hedefler. Bu nedenle; VoIP, ileti zincirleri ve Uçtan Uca Şifreleme'nin bazı yönleri gibi dikkate değer istisnalar dışında var olan kararlı belirtimdeki her şey desteklenir. Matrix belirtiminin sürekli gelişmesi nedeniyle birkaç küçük eksiklik daha var; ancak amaç tüm belirtim için nihai destek sağlamak olmayı sürdürüyor.</p>
|
||||
<p xml:lang="uk">Метою створення NeoChat є повноцінна реалізація програми для специфікації Matrix. Як наслідок, реалізовано усе у поточній стабільній специфікації, окрім голосового інтернет-зв'язку, потоків та деяких аспектів міжвузлового шифрування. Є також декілька інших незначних прогалин через те, що специфікація Matrix постійно змінюється, але метою лишається повна підтримка специфікації.</p>
|
||||
<p xml:lang="x-test">xxNeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.xx</p>
|
||||
<p>Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:</p>
|
||||
<p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يدعم نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p>
|
||||
<p xml:lang="ca">A causa de la naturalesa del desenvolupament de l'especificació de Matrix, el NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
|
||||
<p xml:lang="ca-valencia">A causa de la naturalea del desenvolupament de l'especificació de Matrix, NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
|
||||
<p xml:lang="en-GB">Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:</p>
|
||||
<p xml:lang="es">Debido a la naturaleza del desarrollo de la especificación de Matrix, NeoChat también permite numerosas funciones no estables, como:</p>
|
||||
<p xml:lang="fi">Matrix-määritelmän kehittyessä NeoChat tukee myös monia epävakaita ominaisuuksia. Tällä hetkellä näitä ovat:</p>
|
||||
<p xml:lang="fr">En raison de la nature du développement des spécifications du protocole Matrix, NeoChat prend également en charge de nombreuses fonctionnalités instables. Actuellement, ce sont :</p>
|
||||
<p xml:lang="gl">Debido á natureza do desenvolvemento da especificación de Matrix, NeoChat tamén inclúe varias funcionalidades non estábeis:</p>
|
||||
<p xml:lang="ia">Debite al natura del disveloppamento de specification de Matrix NeoChat tamben supporta numerose characteristicas instabile. Currentemente istes es:</p>
|
||||
<p xml:lang="it">A causa della natura dello sviluppo delle specifiche Matrix, NeoChat supporta anche numerose funzionalità instabili. Attualmente queste sono:</p>
|
||||
<p xml:lang="ka">Matrix-ის სპეციფიკაციის განვითარების ბუნების გამო NeoChat-ს ასევე აქვს უამრავი არასტაბილური ფუნქციაც. ახლა ისინია:</p>
|
||||
<p xml:lang="nl">Vanwege de aard van de ontwikkeling van de Matrix specificatie ondersteunt NeoChat ook talloze onstabiele mogelijkheden. Dit zijn nu:</p>
|
||||
<p xml:lang="nn">På grunn av måten Matrix-spesifikasjonen vert utvikla på, støttar NeoChat òg nokre uferdige funksjonar:</p>
|
||||
<p xml:lang="pl">Ze względu na sposób rozwoju Matriksa, NeoChat obsługuje także kilka niestabilnych możliwości. Obecnie są to:</p>
|
||||
<p xml:lang="sl">Zaradi narave razvoja specifikacije Matrixa NeoChat podpira tudi številne nestabilne zmožnosti. Trenutno so to:</p>
|
||||
<p xml:lang="tr">NeoChat, Matrix belirtimi geliştirmesinin doğası gereği çok sayıda kararsız özelliği de destekler. Şu anda bunlar:</p>
|
||||
<p xml:lang="uk">Через природу розробки специфікації Matrix, у NeoChat також передбачено підтримку численних нестабільних можливостей. У поточній версії цими можливостями є:</p>
|
||||
<p xml:lang="x-test">xxDue to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:xx</p>
|
||||
<ul>>
|
||||
</ul>
|
||||
<ul xml:lang="ca">></ul>
|
||||
<ul xml:lang="ca-valencia">></ul>
|
||||
<ul xml:lang="cs">></ul>
|
||||
<ul xml:lang="en-GB">></ul>
|
||||
<ul xml:lang="es">></ul>
|
||||
<ul xml:lang="fi">></ul>
|
||||
<ul xml:lang="fr">></ul>
|
||||
<ul xml:lang="gl">></ul>
|
||||
<ul xml:lang="ia">></ul>
|
||||
<ul xml:lang="it">></ul>
|
||||
<ul xml:lang="ka">></ul>
|
||||
<ul xml:lang="nl">></ul>
|
||||
<ul xml:lang="nn">></ul>
|
||||
<ul xml:lang="pl">></ul>
|
||||
<ul xml:lang="pt-BR">></ul>
|
||||
<ul xml:lang="sl">></ul>
|
||||
<ul xml:lang="uk">></ul>
|
||||
<ul xml:lang="x-test">xx>xx</ul>
|
||||
</description>
|
||||
<url type="homepage">https://apps.kde.org/neochat/</url>
|
||||
<url type="bugtracker">https://bugs.kde.org/buglist.cgi?component=General&product=NeoChat</url>
|
||||
@@ -179,7 +167,7 @@
|
||||
<category>Network</category>
|
||||
</categories>
|
||||
<developer_name>The KDE Community</developer_name>
|
||||
<developer_name xml:lang="ar">مجتمع كدي</developer_name>
|
||||
<developer_name xml:lang="ar">مجتمع كِيدِي</developer_name>
|
||||
<developer_name xml:lang="az">KDE Cəmiyyəti</developer_name>
|
||||
<developer_name xml:lang="ca">La comunitat KDE</developer_name>
|
||||
<developer_name xml:lang="ca-valencia">La comunitat KDE</developer_name>
|
||||
@@ -191,6 +179,7 @@
|
||||
<developer_name xml:lang="eu">KDE komunitatea</developer_name>
|
||||
<developer_name xml:lang="fi">KDE-yhteisö</developer_name>
|
||||
<developer_name xml:lang="fr">La communauté de KDE</developer_name>
|
||||
<developer_name xml:lang="gl">A comunidade KDE</developer_name>
|
||||
<developer_name xml:lang="hu">A KDE Közösség</developer_name>
|
||||
<developer_name xml:lang="ia">Le communitate de KDE</developer_name>
|
||||
<developer_name xml:lang="id">Komunitas KDE</developer_name>
|
||||
@@ -208,6 +197,7 @@
|
||||
<developer_name xml:lang="sk">KDE Komunita</developer_name>
|
||||
<developer_name xml:lang="sl">Skupnost KDE</developer_name>
|
||||
<developer_name xml:lang="sv">KDE-gemenskapen</developer_name>
|
||||
<developer_name xml:lang="ta">கே.டீ.யீ. சமூகம்</developer_name>
|
||||
<developer_name xml:lang="tr">KDE Topluluğu</developer_name>
|
||||
<developer_name xml:lang="uk">Спільнота KDE</developer_name>
|
||||
<developer_name xml:lang="x-test">xxThe KDE Communityxx</developer_name>
|
||||
@@ -231,6 +221,24 @@
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release version="23.04.3" date="2023-07-06"/>
|
||||
<release version="23.04.2" date="2023-06-08"/>
|
||||
<release version="23.04.1" date="2023-05-11"/>
|
||||
<release version="23.04.0" date="2023-04-20"/>
|
||||
<release version="23.01" date="2023-01-30">
|
||||
<url>https://plasma-mobile.org/2023/01/30/january-blog-post/</url>
|
||||
<description>
|
||||
<p>New features and bugfixes:</p>
|
||||
<ul>
|
||||
<li>Notifications will now be shown for all accounts, not just the active one</li>
|
||||
<li>There is a new "compact" mode for the room list</li>
|
||||
<li>You can now search in the room history</li>
|
||||
<li>Emojis and Reactions have been significantly improved</li>
|
||||
<li>Fixed several crashes around user invitations</li>
|
||||
<li>Room permission settings can now be configured</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="22.11" date="2022-11-30">
|
||||
<url>https://plasma-mobile.org/2022/11/30/plasma-mobile-gear-22-11/</url>
|
||||
</release>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
# SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
|
||||
# SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||
[Desktop Entry]
|
||||
Version=1.5
|
||||
Name=NeoChat
|
||||
@@ -15,6 +15,7 @@ Name[es]=NeoChat
|
||||
Name[eu]=NeoChat
|
||||
Name[fi]=NeoChat
|
||||
Name[fr]=NeoChat
|
||||
Name[gl]=NeoChat
|
||||
Name[hu]=NeoChat
|
||||
Name[ia]=Neochat
|
||||
Name[id]=NeoChat
|
||||
@@ -52,6 +53,7 @@ GenericName[es]=Cliente para Matrix
|
||||
GenericName[eu]=Matrix bezeroa
|
||||
GenericName[fi]=Matrix-asiakas
|
||||
GenericName[fr]=Client « Matrix »
|
||||
GenericName[gl]=Cliente de Matrix
|
||||
GenericName[hu]=Matrix kliens
|
||||
GenericName[ia]=Cliente de Matrice
|
||||
GenericName[id]=Klien Matrix
|
||||
@@ -88,6 +90,7 @@ Comment[es]=Cliente para el protocolo Matrix
|
||||
Comment[eu]=Matrix protokolorako bezeroa
|
||||
Comment[fi]=Asiakas Matrix-yhteyskäytännölle
|
||||
Comment[fr]=Client pour le protocole « Matrix »
|
||||
Comment[gl]=Cliente para o protocolo Matrix
|
||||
Comment[hu]=Kliens a Matrix protokollhoz
|
||||
Comment[ia]=Cliente per le protocollo de Matrix
|
||||
Comment[id]=Klien untuk protokol Matrix
|
||||
@@ -97,7 +100,7 @@ Comment[ka]=კლიენტი Matrix-ის პროტოკოლის
|
||||
Comment[ko]=Matrix 프로토콜용 클라이언트
|
||||
Comment[lt]=Matrix protokolo kliento programa
|
||||
Comment[nl]=Client voor het Matrix-protocol
|
||||
Comment[nn]=Lynmeldingsklient for Matrix-protokollen
|
||||
Comment[nn]=Klient for Matrix-protokollen
|
||||
Comment[pa]=ਮੈਟਰਿਕਸ ਪਰੋਟੋਕਾਲ ਲਈ ਕਲਾਈਂਟ ਹੈ
|
||||
Comment[pl]=Program obsługi protokołu Matriksa
|
||||
Comment[pt]=Cliente para o protocolo Matrix
|
||||
|
||||
2071
po/ar/neochat.po
2071
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
2287
po/az/neochat.po
2287
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
2131
po/ca/neochat.po
2131
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2123
po/cs/neochat.po
2123
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
2088
po/da/neochat.po
2088
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
2168
po/de/neochat.po
2168
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
2856
po/el/neochat.po
2856
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
2061
po/en_GB/neochat.po
2061
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
2084
po/es/neochat.po
2084
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
2082
po/eu/neochat.po
2082
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
2916
po/fi/neochat.po
2916
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
2102
po/fr/neochat.po
2102
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
2289
po/hu/neochat.po
2289
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
2618
po/ia/neochat.po
2618
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
2191
po/id/neochat.po
2191
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
2156
po/ie/neochat.po
2156
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
2125
po/it/neochat.po
2125
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
2023
po/ja/neochat.po
2023
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
2078
po/ka/neochat.po
2078
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
2260
po/ko/neochat.po
2260
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
3789
po/lt/neochat.po
Normal file
3789
po/lt/neochat.po
Normal file
File diff suppressed because it is too large
Load Diff
2066
po/nl/neochat.po
2066
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
2757
po/nn/neochat.po
2757
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
2261
po/pa/neochat.po
2261
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
2225
po/pl/neochat.po
2225
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
2090
po/pt/neochat.po
2090
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
2316
po/pt_BR/neochat.po
2316
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
2169
po/ru/neochat.po
2169
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
2300
po/sk/neochat.po
2300
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
2061
po/sl/neochat.po
2061
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
2269
po/sv/neochat.po
2269
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
2077
po/ta/neochat.po
2077
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
2102
po/tok/neochat.po
2102
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
5
po/tr/docs/neochat/CMakeLists.txt
Normal file
5
po/tr/docs/neochat/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
# SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
kdoctools_create_manpage(man-neochat.1.docbook 1 INSTALL_DESTINATION ${MAN_INSTALL_DIR})
|
||||
122
po/tr/docs/neochat/man-neochat.1.docbook
Normal file
122
po/tr/docs/neochat/man-neochat.1.docbook
Normal file
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" ?>
|
||||
<!DOCTYPE refentry PUBLIC "-//KDE//DTD DocBook XML V4.5-Based Variant V1.1//EN" "dtd/kdedbx45.dtd" [
|
||||
<!ENTITY % Turkish "INCLUDE">
|
||||
]>
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
<refentry lang="&language;">
|
||||
<refentryinfo>
|
||||
<title
|
||||
>NeoChat Kullanıcı Kılavuzu</title>
|
||||
<author
|
||||
><firstname
|
||||
>Carl</firstname
|
||||
><surname
|
||||
>Schwan</surname
|
||||
> <contrib
|
||||
>NeoChat man page.</contrib
|
||||
> <email
|
||||
>carl@carlschwan.eu</email
|
||||
></author>
|
||||
<date
|
||||
>2022-11-01</date>
|
||||
<releaseinfo
|
||||
>22.09</releaseinfo>
|
||||
<productname
|
||||
>NeoChat</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>
|
||||
<command
|
||||
>neochat</command>
|
||||
</refentrytitle>
|
||||
<manvolnum
|
||||
>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname
|
||||
>neochat</refname>
|
||||
<refpurpose
|
||||
>Matrix iletileşme protokolü ile etkileşim için istemci</refpurpose>
|
||||
</refnamediv>
|
||||
<!-- body begins here -->
|
||||
<refsynopsisdiv id='synopsis'>
|
||||
<cmdsynopsis
|
||||
><command
|
||||
>neochat</command
|
||||
> <arg choice="opt"
|
||||
><replaceable
|
||||
>URI</replaceable
|
||||
></arg
|
||||
> </cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
||||
<refsect1 id="description">
|
||||
<title
|
||||
>Açıklama</title>
|
||||
<para
|
||||
><command
|
||||
>neochat</command
|
||||
>, Matrix protokolü için hem masaüstünde hem de taşınabilir aygıtlarda çalışan bir sohbet uygulamasıdır. </para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="options"
|
||||
><title
|
||||
>Seçenekler</title>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term
|
||||
><option
|
||||
>URI</option
|
||||
></term>
|
||||
<listitem>
|
||||
<para
|
||||
>Bir kullanıcı veya oda için matrix URI'si; örneğin, matrix:u/kullanıcı:örnek.org ve matrix:r/kök:örnek.org. Bu, NeoChat'in verilen odayı veya konuşmayı açmayı denemesini sağlar. </para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="bug">
|
||||
<title
|
||||
>Hata Bildirme</title>
|
||||
<para
|
||||
>Hataları veya özellik isteklerini <ulink url="https://bugs.kde.org/enter_bug.cgi?product=NeoChat&component=General"
|
||||
>https://bugs.kde.org/enter_bug.cgi?product=NeoChat&component=General</ulink
|
||||
> bağlantısından bildirebilirsiniz</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title
|
||||
>Ayrıca</title>
|
||||
<simplelist>
|
||||
<member
|
||||
>Matrix üzerine sıkça sorulan soruların bir listesi: <ulink url="https://matrix.org/faq/"
|
||||
>https://matrix.org/faq/</ulink
|
||||
> </member>
|
||||
<member
|
||||
>kf5options(7)</member>
|
||||
<member
|
||||
>qt5options(7)</member>
|
||||
</simplelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="copyright"
|
||||
><title
|
||||
>Telif Hakkı</title>
|
||||
<para
|
||||
>Telif hakkı © 2020-2022 Tobias Fella </para>
|
||||
<para
|
||||
>Telif hakkı © 2020-2022 Carl Schwan </para>
|
||||
<para
|
||||
>Lisans: GNU Genel Kamu Lisansa, 3. sürüm veya sonrası <<ulink url="https://www.gnu.org/licenses/gpl-3.0.html"
|
||||
>https://www.gnu.org/licenses/gpl-3.0.html</ulink
|
||||
>></para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
2096
po/tr/neochat.po
2096
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
2082
po/uk/neochat.po
2082
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
2041
po/zh_CN/neochat.po
2041
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
2015
po/zh_TW/neochat.po
2015
po/zh_TW/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -1,52 +1,52 @@
|
||||
# SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carl@carlschwan.eu>
|
||||
# SPDX-FileCopyrightText: 2020-2021 Nicolas Fella <nicolas.fella@gmx.de>
|
||||
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <fella@posteo.de>
|
||||
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
add_library(neochat STATIC
|
||||
controller.cpp
|
||||
actionshandler.cpp
|
||||
emojimodel.cpp
|
||||
models/emojimodel.cpp
|
||||
emojitones.cpp
|
||||
customemojimodel.cpp
|
||||
models/customemojimodel.cpp
|
||||
clipboard.cpp
|
||||
matriximageprovider.cpp
|
||||
messageeventmodel.cpp
|
||||
messagefiltermodel.cpp
|
||||
roomlistmodel.cpp
|
||||
sortfilterspacelistmodel.cpp
|
||||
models/messageeventmodel.cpp
|
||||
models/messagefiltermodel.cpp
|
||||
models/roomlistmodel.cpp
|
||||
models/sortfilterspacelistmodel.cpp
|
||||
spacehierarchycache.cpp
|
||||
roommanager.cpp
|
||||
neochatroom.cpp
|
||||
neochatuser.cpp
|
||||
userlistmodel.cpp
|
||||
userfiltermodel.cpp
|
||||
publicroomlistmodel.cpp
|
||||
userdirectorylistmodel.cpp
|
||||
keywordnotificationrulemodel.cpp
|
||||
utils.cpp
|
||||
models/userlistmodel.cpp
|
||||
models/userfiltermodel.cpp
|
||||
models/publicroomlistmodel.cpp
|
||||
models/userdirectorylistmodel.cpp
|
||||
models/keywordnotificationrulemodel.cpp
|
||||
notificationsmanager.cpp
|
||||
sortfilterroomlistmodel.cpp
|
||||
models/sortfilterroomlistmodel.cpp
|
||||
chatdocumenthandler.cpp
|
||||
devicesmodel.cpp
|
||||
models/devicesmodel.cpp
|
||||
filetypesingleton.cpp
|
||||
login.cpp
|
||||
stickerevent.cpp
|
||||
webshortcutmodel.cpp
|
||||
models/webshortcutmodel.cpp
|
||||
blurhash.cpp
|
||||
blurhashimageprovider.cpp
|
||||
joinrulesevent.cpp
|
||||
collapsestateproxymodel.cpp
|
||||
models/collapsestateproxymodel.cpp
|
||||
urlhelper.cpp
|
||||
windowcontroller.cpp
|
||||
linkpreviewer.cpp
|
||||
completionmodel.cpp
|
||||
completionproxymodel.cpp
|
||||
actionsmodel.cpp
|
||||
serverlistmodel.cpp
|
||||
statemodel.cpp
|
||||
models/completionmodel.cpp
|
||||
models/completionproxymodel.cpp
|
||||
models/actionsmodel.cpp
|
||||
models/serverlistmodel.cpp
|
||||
models/statemodel.cpp
|
||||
filetransferpseudojob.cpp
|
||||
searchmodel.cpp
|
||||
models/searchmodel.cpp
|
||||
texthandler.cpp
|
||||
)
|
||||
|
||||
add_executable(neochat-app
|
||||
@@ -79,7 +79,7 @@ if(NOT ANDROID)
|
||||
else()
|
||||
target_sources(neochat PRIVATE trayicon.cpp)
|
||||
endif()
|
||||
target_link_libraries(neochat PUBLIC KF5::ConfigWidgets KF5::WindowSystem)
|
||||
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)
|
||||
endif()
|
||||
@@ -87,13 +87,14 @@ 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})
|
||||
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF5::I18n KF5::Kirigami2 KF5::Notifications KF5::ConfigCore KF5::ConfigGui KF5::CoreAddons KF5::SonnetCore KF5::ItemModels Quotient cmark::cmark ${QTKEYCHAIN_LIBRARIES} QCoro::Core)
|
||||
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF${QT_MAJOR_VERSION}::I18n KF${QT_MAJOR_VERSION}::Kirigami2 KF${QT_MAJOR_VERSION}::Notifications KF${QT_MAJOR_VERSION}::ConfigCore KF${QT_MAJOR_VERSION}::ConfigGui KF${QT_MAJOR_VERSION}::CoreAddons KF${QT_MAJOR_VERSION}::SonnetCore KF${QT_MAJOR_VERSION}::ItemModels Quotient cmark::cmark ${QTKEYCHAIN_LIBRARIES} QCoro::Core)
|
||||
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||
|
||||
if(NEOCHAT_FLATPAK)
|
||||
@@ -103,6 +104,9 @@ endif()
|
||||
if(ANDROID)
|
||||
target_sources(neochat PRIVATE notifyrc.qrc)
|
||||
target_link_libraries(neochat PRIVATE Qt::Svg OpenSSL::SSL)
|
||||
if(SQLite3_FOUND)
|
||||
target_link_libraries(neochat-app PRIVATE SQLite::SQLite3)
|
||||
endif()
|
||||
target_sources(neochat-app PRIVATE notifyrc.qrc)
|
||||
target_link_libraries(neochat PUBLIC Qt::Svg OpenSSL::SSL)
|
||||
kirigami_package_breeze_icons(ICONS
|
||||
@@ -173,7 +177,7 @@ if(ANDROID)
|
||||
"visibility"
|
||||
)
|
||||
else()
|
||||
target_link_libraries(neochat PUBLIC Qt::Widgets KF5::KIOWidgets)
|
||||
target_link_libraries(neochat PUBLIC Qt::Widgets KF${QT_MAJOR_VERSION}::KIOWidgets)
|
||||
install(FILES neochat.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
|
||||
endif()
|
||||
|
||||
@@ -181,12 +185,12 @@ if(NOT ANDROID)
|
||||
set_target_properties(neochat-app PROPERTIES OUTPUT_NAME "neochat")
|
||||
endif()
|
||||
|
||||
if(TARGET KF5::DBusAddons)
|
||||
target_link_libraries(neochat PUBLIC KF5::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 KF5::KIOWidgets)
|
||||
if (TARGET KF${QT_MAJOR_VERSION}::KIOWidgets)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_KIO)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -13,31 +13,17 @@
|
||||
#include <KLocalizedString>
|
||||
#include <QStringBuilder>
|
||||
|
||||
#include "actionsmodel.h"
|
||||
#include "controller.h"
|
||||
#include "customemojimodel.h"
|
||||
#include "models/actionsmodel.h"
|
||||
#include "models/customemojimodel.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatuser.h"
|
||||
#include "roommanager.h"
|
||||
#include "texthandler.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
QString markdownToHTML(const QString &markdown)
|
||||
{
|
||||
const auto str = markdown.toUtf8();
|
||||
char *tmp_buf = cmark_markdown_to_html(str.constData(), str.size(), CMARK_OPT_HARDBREAKS | CMARK_OPT_UNSAFE);
|
||||
|
||||
const std::string html(tmp_buf);
|
||||
|
||||
free(tmp_buf);
|
||||
|
||||
auto result = QString::fromStdString(html).trimmed();
|
||||
|
||||
result.replace("<!-- raw HTML omitted -->", "");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ActionsHandler::ActionsHandler(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
@@ -58,9 +44,9 @@ void ActionsHandler::setRoom(NeoChatRoom *room)
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
void ActionsHandler::handleMessage()
|
||||
void ActionsHandler::handleNewMessage()
|
||||
{
|
||||
checkEffects();
|
||||
checkEffects(m_room->chatBoxText());
|
||||
if (!m_room->chatBoxAttachmentPath().isEmpty()) {
|
||||
QUrl url(m_room->chatBoxAttachmentPath());
|
||||
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
|
||||
@@ -69,13 +55,39 @@ void ActionsHandler::handleMessage()
|
||||
m_room->setChatBoxText({});
|
||||
return;
|
||||
}
|
||||
QString handledText = m_room->chatBoxText();
|
||||
|
||||
std::sort(m_room->mentions()->begin(), m_room->mentions()->end(), [](const auto &a, const auto &b) -> bool {
|
||||
QString handledText = m_room->chatBoxText();
|
||||
handledText = handleMentions(handledText);
|
||||
handleMessage(m_room->chatBoxText(), handledText);
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
for (const auto &mention : *m_room->mentions()) {
|
||||
for (const auto &mention : *mentions) {
|
||||
if (mention.text.isEmpty() || mention.id.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
@@ -83,11 +95,16 @@ void ActionsHandler::handleMessage()
|
||||
mention.cursor.position() - mention.cursor.anchor(),
|
||||
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text, mention.id));
|
||||
}
|
||||
m_room->mentions()->clear();
|
||||
mentions->clear();
|
||||
|
||||
return handledText;
|
||||
}
|
||||
|
||||
void ActionsHandler::handleMessage(const QString &text, QString handledText, const bool &isEdit)
|
||||
{
|
||||
if (NeoChatConfig::allowQuickEdit()) {
|
||||
QRegularExpression sed("^s/([^/]*)/([^/]*)(/g)?$");
|
||||
auto match = sed.match(m_room->chatBoxText());
|
||||
auto match = sed.match(text);
|
||||
if (match.hasMatch()) {
|
||||
const QString regex = match.captured(1);
|
||||
const QString replacement = match.captured(2).toHtmlEscaped();
|
||||
@@ -137,7 +154,10 @@ void ActionsHandler::handleMessage()
|
||||
}
|
||||
|
||||
handledText = CustomEmojiModel::instance().preprocessText(handledText);
|
||||
handledText = markdownToHTML(handledText);
|
||||
TextHandler textHandler;
|
||||
textHandler.setData(handledText);
|
||||
handledText = textHandler.handleSendText();
|
||||
|
||||
if (handledText.count("<p>") == 1 && handledText.count("</p>") == 1) {
|
||||
handledText.remove("<p>");
|
||||
handledText.remove("</p>");
|
||||
@@ -146,13 +166,13 @@ void ActionsHandler::handleMessage()
|
||||
if (handledText.length() == 0) {
|
||||
return;
|
||||
}
|
||||
m_room->postMessage(m_room->chatBoxText(), handledText, messageType, m_room->chatBoxReplyId(), m_room->chatBoxEditId());
|
||||
|
||||
m_room->postMessage(text, handledText, messageType, m_room->chatBoxReplyId(), isEdit ? m_room->chatBoxEditId() : "");
|
||||
}
|
||||
|
||||
void ActionsHandler::checkEffects()
|
||||
void ActionsHandler::checkEffects(const QString &text)
|
||||
{
|
||||
std::optional<QString> effect = std::nullopt;
|
||||
const auto &text = m_room->chatBoxText();
|
||||
if (text.contains("\u2744")) {
|
||||
effect = QLatin1String("snowflake");
|
||||
} else if (text.contains("\u1F386")) {
|
||||
|
||||
@@ -33,14 +33,20 @@ Q_SIGNALS:
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
/// \brief Post a message.
|
||||
///
|
||||
/// This also interprets commands if any.
|
||||
void handleMessage();
|
||||
/**
|
||||
* @brief Pre-process text and send message.
|
||||
*/
|
||||
void handleNewMessage();
|
||||
|
||||
/**
|
||||
* @brief Pre-process text and send edit.
|
||||
*/
|
||||
void handleEdit();
|
||||
|
||||
private:
|
||||
NeoChatRoom *m_room = nullptr;
|
||||
void checkEffects();
|
||||
};
|
||||
void checkEffects(const QString &text);
|
||||
|
||||
QString markdownToHTML(const QString &markdown);
|
||||
QString handleMentions(QString handledText, const bool &isEdit = false);
|
||||
void handleMessage(const QString &text, QString handledText, const bool &isEdit = false);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: 2021 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "blurhashimageprovider.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: 2021 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
#include <Sonnet/BackgroundChecker>
|
||||
#include <Sonnet/Settings>
|
||||
|
||||
#include "actionsmodel.h"
|
||||
#include "models/actionsmodel.h"
|
||||
#include "models/roomlistmodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "roomlistmodel.h"
|
||||
|
||||
class SyntaxHighlighter : public QSyntaxHighlighter
|
||||
{
|
||||
@@ -105,14 +105,19 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
||||
{
|
||||
connect(this, &ChatDocumentHandler::roomChanged, this, [this]() {
|
||||
m_completionModel->setRoom(m_room);
|
||||
static NeoChatRoom *previousRoom = nullptr;
|
||||
static QPointer<NeoChatRoom> previousRoom = nullptr;
|
||||
if (previousRoom) {
|
||||
disconnect(previousRoom, &NeoChatRoom::chatBoxTextChanged, this, nullptr);
|
||||
disconnect(previousRoom, &NeoChatRoom::editTextChanged, this, nullptr);
|
||||
}
|
||||
previousRoom = m_room;
|
||||
connect(m_room, &NeoChatRoom::chatBoxTextChanged, this, [this]() {
|
||||
int start = completionStartIndex();
|
||||
m_completionModel->setText(m_room->chatBoxText().mid(start, cursorPosition() - start), m_room->chatBoxText().mid(start));
|
||||
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));
|
||||
});
|
||||
});
|
||||
connect(this, &ChatDocumentHandler::documentChanged, this, [this]() {
|
||||
@@ -123,7 +128,7 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
||||
return;
|
||||
}
|
||||
int start = completionStartIndex();
|
||||
m_completionModel->setText(m_room->chatBoxText().mid(start, cursorPosition() - start), m_room->chatBoxText().mid(start));
|
||||
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -138,7 +143,7 @@ int ChatDocumentHandler::completionStartIndex() const
|
||||
#else
|
||||
const auto cursor = cursorPosition();
|
||||
#endif
|
||||
const auto &text = m_room->chatBoxText();
|
||||
const auto &text = getText();
|
||||
auto start = std::min(cursor, text.size()) - 1;
|
||||
while (start > -1) {
|
||||
if (text.at(start) == QLatin1Char(' ')) {
|
||||
@@ -150,6 +155,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;
|
||||
@@ -204,7 +223,7 @@ void ChatDocumentHandler::complete(int index)
|
||||
if (m_completionModel->autoCompletionType() == CompletionModel::User) {
|
||||
auto name = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::Text).toString();
|
||||
auto id = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::Subtitle).toString();
|
||||
auto text = m_room->chatBoxText();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char('@'), cursorPosition() - 1);
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
@@ -213,11 +232,11 @@ void ChatDocumentHandler::complete(int index)
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursor.position() + name.size(), QTextCursor::KeepAnchor);
|
||||
cursor.setKeepPositionOnInsert(true);
|
||||
m_room->mentions()->push_back({cursor, name, 0, 0, id});
|
||||
pushMention({cursor, name, 0, 0, id});
|
||||
m_highlighter->rehighlight();
|
||||
} else if (m_completionModel->autoCompletionType() == CompletionModel::Command) {
|
||||
auto command = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedText).toString();
|
||||
auto text = m_room->chatBoxText();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char('/'));
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
@@ -225,7 +244,7 @@ void ChatDocumentHandler::complete(int index)
|
||||
cursor.insertText(QStringLiteral("/%1 ").arg(command));
|
||||
} else if (m_completionModel->autoCompletionType() == CompletionModel::Room) {
|
||||
auto alias = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::Subtitle).toString();
|
||||
auto text = m_room->chatBoxText();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char('#'), cursorPosition() - 1);
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
@@ -234,11 +253,11 @@ void ChatDocumentHandler::complete(int index)
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursor.position() + alias.size(), QTextCursor::KeepAnchor);
|
||||
cursor.setKeepPositionOnInsert(true);
|
||||
m_room->mentions()->push_back({cursor, alias, 0, 0, alias});
|
||||
pushMention({cursor, alias, 0, 0, alias});
|
||||
m_highlighter->rehighlight();
|
||||
} else if (m_completionModel->autoCompletionType() == CompletionModel::Emoji) {
|
||||
auto shortcode = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedText).toString();
|
||||
auto text = m_room->chatBoxText();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char(':'));
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
@@ -281,3 +300,27 @@ void ChatDocumentHandler::setSelectionEnd(int position)
|
||||
m_selectionEnd = position;
|
||||
Q_EMIT selectionEndChanged();
|
||||
}
|
||||
|
||||
QString ChatDocumentHandler::getText() const
|
||||
{
|
||||
if (!m_room) {
|
||||
return QString();
|
||||
}
|
||||
if (m_isEdit) {
|
||||
return m_room->editText();
|
||||
} else {
|
||||
return m_room->chatBoxText();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatDocumentHandler::pushMention(const Mention mention) const
|
||||
{
|
||||
if (!m_room) {
|
||||
return;
|
||||
}
|
||||
if (m_isEdit) {
|
||||
m_room->editMentions()->push_back(mention);
|
||||
} else {
|
||||
m_room->mentions()->push_back(mention);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
#include <QQuickTextDocument>
|
||||
#include <QTextCursor>
|
||||
|
||||
#include "completionmodel.h"
|
||||
#include "userlistmodel.h"
|
||||
#include "models/completionmodel.h"
|
||||
#include "models/userlistmodel.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
class QTextDocument;
|
||||
class NeoChatRoom;
|
||||
@@ -17,6 +18,14 @@ class SyntaxHighlighter;
|
||||
class ChatDocumentHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
/**
|
||||
* @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)
|
||||
Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged)
|
||||
Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
|
||||
Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
|
||||
@@ -24,11 +33,14 @@ class ChatDocumentHandler : public QObject
|
||||
|
||||
Q_PROPERTY(CompletionModel *completionModel READ completionModel NOTIFY completionModelChanged)
|
||||
|
||||
Q_PROPERTY(NeoChatRoom *room READ room NOTIFY roomChanged)
|
||||
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||
|
||||
public:
|
||||
explicit ChatDocumentHandler(QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] bool isEdit() const;
|
||||
void setIsEdit(bool edit);
|
||||
|
||||
[[nodiscard]] QQuickTextDocument *document() const;
|
||||
void setDocument(QQuickTextDocument *document);
|
||||
|
||||
@@ -49,6 +61,7 @@ public:
|
||||
void updateCompletions();
|
||||
CompletionModel *completionModel() const;
|
||||
Q_SIGNALS:
|
||||
void isEditChanged();
|
||||
void documentChanged();
|
||||
void cursorPositionChanged();
|
||||
void roomChanged();
|
||||
@@ -59,15 +72,20 @@ Q_SIGNALS:
|
||||
private:
|
||||
int completionStartIndex() const;
|
||||
|
||||
bool m_isEdit;
|
||||
|
||||
QQuickTextDocument *m_document;
|
||||
|
||||
NeoChatRoom *m_room = nullptr;
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
bool completionVisible = false;
|
||||
|
||||
int m_cursorPosition;
|
||||
int m_selectionStart;
|
||||
int m_selectionEnd;
|
||||
|
||||
QString getText() const;
|
||||
void pushMention(const Mention mention) const;
|
||||
|
||||
SyntaxHighlighter *m_highlighter = nullptr;
|
||||
|
||||
CompletionModel::AutoCompletionType m_completionType = CompletionModel::None;
|
||||
|
||||
@@ -33,13 +33,14 @@ QImage Clipboard::image() const
|
||||
|
||||
QString Clipboard::saveImage(QString localPath) const
|
||||
{
|
||||
if (!QDir().exists(QStringLiteral("%1/screenshots").arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)))) {
|
||||
QDir().mkdir(QStringLiteral("%1/screenshots").arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)));
|
||||
QString imageDir(QStringLiteral("%1/screenshots").arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)));
|
||||
|
||||
if (!QDir().exists(imageDir)) {
|
||||
QDir().mkdir(imageDir);
|
||||
}
|
||||
|
||||
if (localPath.isEmpty()) {
|
||||
localPath = QStringLiteral("file://%1/screenshots/%2.png")
|
||||
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation),
|
||||
QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd-hh-mm-ss")));
|
||||
localPath = QStringLiteral("file://%1/%2.png").arg(imageDir, QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd-hh-mm-ss")));
|
||||
}
|
||||
QUrl url(localPath);
|
||||
if (!url.isLocalFile()) {
|
||||
@@ -51,21 +52,30 @@ QString Clipboard::saveImage(QString localPath) const
|
||||
return {};
|
||||
}
|
||||
|
||||
QDir dir;
|
||||
if (!dir.exists(QFileInfo(url.fileName()).absoluteFilePath())) {
|
||||
dir.mkpath(QFileInfo(url.fileName()).absoluteFilePath());
|
||||
if (image.save(url.toLocalFile())) {
|
||||
return localPath;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
image.save(url.toLocalFile());
|
||||
|
||||
return localPath;
|
||||
}
|
||||
|
||||
void Clipboard::saveText(QString message)
|
||||
{
|
||||
QRegularExpression re("<[^>]*>");
|
||||
static QRegularExpression re(QStringLiteral("<[^>]*>"));
|
||||
auto *mineData = new QMimeData; // ownership is transferred to clipboard
|
||||
mineData->setHtml(message);
|
||||
mineData->setText(message.replace(re, ""));
|
||||
mineData->setText(message.replace(re, QString()));
|
||||
m_clipboard->setMimeData(mineData);
|
||||
}
|
||||
|
||||
void Clipboard::setImage(const QUrl &url)
|
||||
{
|
||||
if (url.isLocalFile()) {
|
||||
QImage img(url.path());
|
||||
auto *mimeData = new QMimeData;
|
||||
mimeData->setImageData(img);
|
||||
if (!img.isNull()) {
|
||||
m_clipboard->setMimeData(mimeData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ public:
|
||||
Q_INVOKABLE QString saveImage(QString localPath = {}) const;
|
||||
|
||||
Q_INVOKABLE void saveText(QString message);
|
||||
Q_INVOKABLE void setImage(const QUrl &image);
|
||||
|
||||
private:
|
||||
QClipboard *m_clipboard;
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "collapsestateproxymodel.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
bool CollapseStateProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||
{
|
||||
Q_UNUSED(source_parent);
|
||||
return sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::EventTypeRole)
|
||||
!= MessageEventModel::DelegateType::State // If this is not a state, show it
|
||||
|| sourceModel()->data(sourceModel()->index(source_row + 1, 0), MessageEventModel::EventTypeRole)
|
||||
!= MessageEventModel::DelegateType::State // If this is the first state in a block, show it. TODO hidden events?
|
||||
|| sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::ShowSectionRole).toBool() // If it's a new day, show it
|
||||
|| sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::EventResolvedTypeRole)
|
||||
!= sourceModel()->data(sourceModel()->index(source_row + 1, 0),
|
||||
MessageEventModel::EventResolvedTypeRole) // Also show it if it's of a different type than the one before TODO improve in
|
||||
|| sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::AuthorIdRole)
|
||||
!= sourceModel()->data(sourceModel()->index(source_row + 1, 0), MessageEventModel::AuthorIdRole); // Also show it if it's a different author
|
||||
}
|
||||
|
||||
QVariant CollapseStateProxyModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (role == AggregateDisplayRole) {
|
||||
return aggregateEventToString(mapToSource(index).row());
|
||||
}
|
||||
return sourceModel()->data(mapToSource(index), role);
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> CollapseStateProxyModel::roleNames() const
|
||||
{
|
||||
auto roles = sourceModel()->roleNames();
|
||||
roles[AggregateDisplayRole] = "aggregateDisplay";
|
||||
return roles;
|
||||
}
|
||||
|
||||
QString CollapseStateProxyModel::aggregateEventToString(int sourceRow) const
|
||||
{
|
||||
QStringList parts;
|
||||
for (int i = sourceRow; i >= 0; i--) {
|
||||
parts += sourceModel()->data(sourceModel()->index(i, 0), Qt::DisplayRole).toString();
|
||||
if (sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::EventTypeRole)
|
||||
!= MessageEventModel::DelegateType::State // If it's not a state event
|
||||
|| (i > 0
|
||||
&& sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::EventResolvedTypeRole)
|
||||
!= sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::EventResolvedTypeRole)) // or of a different type
|
||||
|| (i > 0
|
||||
&& sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::AuthorIdRole)
|
||||
!= sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::AuthorIdRole)) // or by a different author
|
||||
|| sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::ShowSectionRole).toBool() // or the section needs to be visible
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!parts.isEmpty()) {
|
||||
QStringList chunks;
|
||||
while (!parts.isEmpty()) {
|
||||
chunks += QString();
|
||||
int count = 1;
|
||||
auto part = parts.takeFirst();
|
||||
chunks.last() += part;
|
||||
while (!parts.isEmpty() && parts.first() == part) {
|
||||
parts.removeFirst();
|
||||
count++;
|
||||
}
|
||||
if (count > 1) {
|
||||
chunks.last() += i18ncp("[user did something] n times", " %1 time", " %1 times", count);
|
||||
}
|
||||
}
|
||||
QString text = chunks.takeFirst();
|
||||
|
||||
if (chunks.size() > 0) {
|
||||
while (chunks.size() > 1) {
|
||||
text += i18nc("[action 1], [action 2 and action 3]", ", ");
|
||||
text += chunks.takeFirst();
|
||||
}
|
||||
text += i18nc("[action 1, action 2] and [action 3]", " and ");
|
||||
text += chunks.takeFirst();
|
||||
}
|
||||
return text;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "messageeventmodel.h"
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class CollapseStateProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Roles {
|
||||
AggregateDisplayRole = MessageEventModel::LastRole + 1,
|
||||
};
|
||||
[[nodiscard]] bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
[[nodiscard]] QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
||||
|
||||
[[nodiscard]] QString aggregateEventToString(int row) const;
|
||||
};
|
||||
@@ -1,10 +1,14 @@
|
||||
// SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
|
||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#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>
|
||||
@@ -37,6 +41,7 @@
|
||||
#include <csapi/content-repo.h>
|
||||
#include <csapi/logout.h>
|
||||
#include <csapi/profile.h>
|
||||
#include <jobs/downloadfilejob.h>
|
||||
#include <qt_connection_util.h>
|
||||
|
||||
#ifdef QUOTIENT_07
|
||||
@@ -128,16 +133,7 @@ Controller::Controller(QObject *parent)
|
||||
if (AccountRegistry::instance().size() > oldAccountCount) {
|
||||
auto connection = AccountRegistry::instance().accounts()[AccountRegistry::instance().size() - 1];
|
||||
connect(connection, &Connection::syncDone, this, [=]() {
|
||||
bool changes = false;
|
||||
for (const auto &room : connection->allRooms()) {
|
||||
if (m_notificationCounts[room] != room->unreadStats().notableCount) {
|
||||
m_notificationCounts[room] = room->unreadStats().notableCount;
|
||||
changes = true;
|
||||
}
|
||||
}
|
||||
if (changes) {
|
||||
handleNotifications();
|
||||
}
|
||||
handleNotifications(connection);
|
||||
});
|
||||
}
|
||||
oldAccountCount = AccountRegistry::instance().size();
|
||||
@@ -146,19 +142,16 @@ Controller::Controller(QObject *parent)
|
||||
}
|
||||
|
||||
#ifdef QUOTIENT_07
|
||||
void Controller::handleNotifications()
|
||||
void Controller::handleNotifications(QPointer<Quotient::Connection> connection)
|
||||
{
|
||||
static bool initial = true;
|
||||
static QStringList initial;
|
||||
static QStringList oldNotifications;
|
||||
if (!m_connection) {
|
||||
return;
|
||||
}
|
||||
auto job = m_connection->callApi<GetNotificationsJob>();
|
||||
auto job = connection->callApi<GetNotificationsJob>();
|
||||
|
||||
connect(job, &BaseJob::success, this, [this, job]() {
|
||||
connect(job, &BaseJob::success, this, [job, connection]() {
|
||||
const auto notifications = job->jsonData()["notifications"].toArray();
|
||||
if (initial) {
|
||||
initial = false;
|
||||
if (!initial.contains(connection->user()->id())) {
|
||||
initial.append(connection->user()->id());
|
||||
for (const auto &n : notifications) {
|
||||
oldNotifications += n.toObject()["event"].toObject()["event_id"].toString();
|
||||
}
|
||||
@@ -174,10 +167,12 @@ void Controller::handleNotifications()
|
||||
continue;
|
||||
}
|
||||
oldNotifications += notification["event"].toObject()["event_id"].toString();
|
||||
auto room = m_connection->room(notification["room_id"].toString());
|
||||
auto room = connection->room(notification["room_id"].toString());
|
||||
|
||||
// If room exists, room is NOT active OR the application is NOT active, show notification
|
||||
if (room && !(room->id() == RoomManager::instance().currentRoom()->id() && QGuiApplication::applicationState() == Qt::ApplicationActive)) {
|
||||
if (room
|
||||
&& !(RoomManager::instance().currentRoom() && room->id() == RoomManager::instance().currentRoom()->id()
|
||||
&& QGuiApplication::applicationState() == Qt::ApplicationActive)) {
|
||||
// The room might have been deleted (for example rejected invitation).
|
||||
auto sender = room->user(notification["event"].toObject()["sender"].toString());
|
||||
|
||||
@@ -196,7 +191,7 @@ void Controller::handleNotifications()
|
||||
|
||||
if (notification["event"]["type"] == "m.room.encrypted") {
|
||||
#ifdef Quotient_E2EE_ENABLED
|
||||
auto decrypted = m_connection->decryptNotification(notification);
|
||||
auto decrypted = connection->decryptNotification(notification);
|
||||
body = decrypted["content"].toObject()["body"].toString();
|
||||
#endif
|
||||
if (body.isEmpty()) {
|
||||
@@ -381,6 +376,11 @@ void Controller::invokeLogin()
|
||||
if (error == "Unrecognised access token") {
|
||||
Q_EMIT errorOccured(i18n("Login Failed: Access Token invalid or revoked"));
|
||||
logout(connection, false);
|
||||
} else if (error == "Connection closed") {
|
||||
Q_EMIT errorOccured(i18n("Login Failed: %1", error));
|
||||
// Failed due to network connection issue. This might happen when the homeserver is
|
||||
// temporary down, or the user trying to re-launch NeoChat in a network that cannot
|
||||
// connect to the homeserver. In this case, we don't want to do logout().
|
||||
} else {
|
||||
Q_EMIT errorOccured(i18n("Login Failed: %1", error));
|
||||
logout(connection, true);
|
||||
@@ -611,6 +611,11 @@ void Controller::setActiveConnection(Connection *connection)
|
||||
m_isOnline = true;
|
||||
Q_EMIT isOnlineChanged(true);
|
||||
});
|
||||
connect(connection, &Connection::requestFailed, this, [=](BaseJob *job) {
|
||||
if (dynamic_cast<DownloadFileJob *>(job) && job->jsonData()["errcode"].toString() == "M_TOO_LARGE"_ls) {
|
||||
RoomManager::instance().warning(i18n("File too large to download."), i18n("Contact your matrix server administrator for support."));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
NeoChatConfig::self()->setActiveConnection(QString());
|
||||
}
|
||||
@@ -639,9 +644,17 @@ NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const Om
|
||||
void Controller::createRoom(const QString &name, const QString &topic)
|
||||
{
|
||||
auto createRoomJob = m_connection->createRoom(Connection::PublishRoom, "", name, topic, QStringList());
|
||||
Quotient::CreateRoomJob::connect(createRoomJob, &CreateRoomJob::failure, [this, createRoomJob] {
|
||||
connect(createRoomJob, &CreateRoomJob::failure, this, [this, createRoomJob] {
|
||||
Q_EMIT errorOccured(i18n("Room creation failed: \"%1\"", createRoomJob->errorString()));
|
||||
});
|
||||
connectSingleShot(
|
||||
this,
|
||||
&Controller::roomAdded,
|
||||
this,
|
||||
[this](NeoChatRoom *room) {
|
||||
RoomManager::instance().enterRoom(room);
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
bool Controller::isOnline() const
|
||||
|
||||
@@ -119,7 +119,7 @@ private:
|
||||
|
||||
bool hasWindowSystem() const;
|
||||
#ifdef QUOTIENT_07
|
||||
void handleNotifications();
|
||||
void handleNotifications(QPointer<Quotient::Connection> connection);
|
||||
#endif
|
||||
|
||||
private Q_SLOTS:
|
||||
@@ -151,6 +151,7 @@ Q_SIGNALS:
|
||||
void keyVerificationAccept(const QString &commitment);
|
||||
void keyVerificationKey(const QString &sas);
|
||||
void activeConnectionIndexChanged();
|
||||
void roomAdded(NeoChatRoom *room);
|
||||
|
||||
public Q_SLOTS:
|
||||
void logout(Quotient::Connection *conn, bool serverSideLogout);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "emojitones.h"
|
||||
#include "emojimodel.h"
|
||||
#include "models/emojimodel.h"
|
||||
|
||||
QMultiHash<QString, QVariant> EmojiTones::_tones = {
|
||||
#include "emojitones_data.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "filetransferpseudojob.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "login.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
44
src/main.cpp
44
src/main.cpp
@@ -42,39 +42,39 @@
|
||||
#include "blurhashimageprovider.h"
|
||||
#include "chatdocumenthandler.h"
|
||||
#include "clipboard.h"
|
||||
#include "collapsestateproxymodel.h"
|
||||
#include "controller.h"
|
||||
#include "customemojimodel.h"
|
||||
#include "devicesmodel.h"
|
||||
#include "emojimodel.h"
|
||||
#include "filetypesingleton.h"
|
||||
#include "joinrulesevent.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "keywordnotificationrulemodel.h"
|
||||
#include "login.h"
|
||||
#include "matriximageprovider.h"
|
||||
#include "messageeventmodel.h"
|
||||
#include "messagefiltermodel.h"
|
||||
#include "models/collapsestateproxymodel.h"
|
||||
#include "models/customemojimodel.h"
|
||||
#include "models/devicesmodel.h"
|
||||
#include "models/emojimodel.h"
|
||||
#include "models/keywordnotificationrulemodel.h"
|
||||
#include "models/messageeventmodel.h"
|
||||
#include "models/messagefiltermodel.h"
|
||||
#include "models/publicroomlistmodel.h"
|
||||
#include "models/roomlistmodel.h"
|
||||
#include "models/searchmodel.h"
|
||||
#include "models/serverlistmodel.h"
|
||||
#include "models/sortfilterroomlistmodel.h"
|
||||
#include "models/sortfilterspacelistmodel.h"
|
||||
#include "models/userdirectorylistmodel.h"
|
||||
#include "models/userfiltermodel.h"
|
||||
#include "models/userlistmodel.h"
|
||||
#include "models/webshortcutmodel.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatuser.h"
|
||||
#include "notificationsmanager.h"
|
||||
#include "searchmodel.h"
|
||||
#ifdef QUOTIENT_07
|
||||
#include "pollhandler.h"
|
||||
#endif
|
||||
#include "publicroomlistmodel.h"
|
||||
#include "roomlistmodel.h"
|
||||
#include "roommanager.h"
|
||||
#include "serverlistmodel.h"
|
||||
#include "sortfilterroomlistmodel.h"
|
||||
#include "sortfilterspacelistmodel.h"
|
||||
#include "spacehierarchycache.h"
|
||||
#include "urlhelper.h"
|
||||
#include "userdirectorylistmodel.h"
|
||||
#include "userfiltermodel.h"
|
||||
#include "userlistmodel.h"
|
||||
#include "webshortcutmodel.h"
|
||||
#include "windowcontroller.h"
|
||||
#ifdef QUOTIENT_07
|
||||
#include <keyverificationsession.h>
|
||||
@@ -82,15 +82,19 @@
|
||||
#ifdef HAVE_COLORSCHEME
|
||||
#include "colorschemer.h"
|
||||
#endif
|
||||
#include "completionmodel.h"
|
||||
#include "models/completionmodel.h"
|
||||
#include "models/statemodel.h"
|
||||
#include "neochatuser.h"
|
||||
#include "statemodel.h"
|
||||
|
||||
#ifdef HAVE_RUNNER
|
||||
#include "runner.h"
|
||||
#include <QDBusConnection>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
|
||||
@@ -153,7 +157,7 @@ int main(int argc, char *argv[])
|
||||
KAboutLicense::GPL_V3,
|
||||
i18n("© 2018-2020 Black Hat, 2020-2022 KDE Community"));
|
||||
about.addAuthor(i18n("Carl Schwan"), i18n("Maintainer"), QStringLiteral("carl@carlschwan.eu"), QStringLiteral("https://carlschwan.eu"));
|
||||
about.addAuthor(i18n("Tobias Fella"), i18n("Maintainer"), QStringLiteral("fella@posteo.de"), QStringLiteral("https://tobiasfella.de"));
|
||||
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"));
|
||||
about.addCredit(i18n("Alexey Rusakov"), i18n("Maintainer of Quotient"), QStringLiteral("Kitsune-Ral@users.sf.net"));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "actionsmodel.h"
|
||||
@@ -115,7 +115,7 @@ QVector<ActionsModel::Action> actions{
|
||||
QStringLiteral("spoiler"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
// Ideally, we would just return rainbowText and let that do the rest, but the colors don't survive markdownToHTML.
|
||||
room->postMessage(QStringLiteral("/rainbow %1").arg(text),
|
||||
room->postMessage(QStringLiteral("/spoiler %1").arg(text),
|
||||
QStringLiteral("<span data-mx-spoiler>%1</span>").arg(text),
|
||||
RoomMessageEvent::MsgType::Text,
|
||||
room->chatBoxReplyId(),
|
||||
@@ -158,11 +158,12 @@ QVector<ActionsModel::Action> actions{
|
||||
return QString();
|
||||
}
|
||||
#ifdef QUOTIENT_07
|
||||
if (room->currentState().get<RoomMemberEvent>(text)->membership() == Membership::Invite) {
|
||||
const RoomMemberEvent *roomMemberEvent = room->currentState().get<RoomMemberEvent>(text);
|
||||
if (roomMemberEvent && roomMemberEvent->membership() == Membership::Invite) {
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is already invited to this room.", "%1 is already invited to this room.", text));
|
||||
return QString();
|
||||
}
|
||||
if (room->currentState().get<RoomMemberEvent>(text)->membership() == Membership::Ban) {
|
||||
if (roomMemberEvent && roomMemberEvent->membership() == Membership::Ban) {
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is banned from this room.", "%1 is banned from this room.", text));
|
||||
return QString();
|
||||
}
|
||||
@@ -208,6 +209,40 @@ QVector<ActionsModel::Action> actions{
|
||||
kli18n("<room alias or id>"),
|
||||
kli18n("Joins the given room"),
|
||||
},
|
||||
#ifdef QUOTIENT_07
|
||||
Action{
|
||||
QStringLiteral("knock"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
auto parts = text.split(QLatin1String(" "));
|
||||
QString roomName = parts[0];
|
||||
QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"));
|
||||
auto regexMatch = roomRegex.match(roomName);
|
||||
if (!regexMatch.hasMatch()) {
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Error,
|
||||
i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
|
||||
return QString();
|
||||
}
|
||||
auto targetRoom = text.startsWith(QLatin1Char('!')) ? room->connection()->room(text) : room->connection()->roomByAlias(text);
|
||||
if (targetRoom) {
|
||||
RoomManager::instance().enterRoom(dynamic_cast<NeoChatRoom *>(targetRoom));
|
||||
return QString();
|
||||
}
|
||||
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Knocking room <roomname>.", "Knocking room %1.", text));
|
||||
auto connection = Controller::instance().activeConnection();
|
||||
const auto knownServer = roomName.mid(roomName.indexOf(":") + 1);
|
||||
if (parts.length() >= 2) {
|
||||
RoomManager::instance().knockRoom(connection, roomName, parts[1], QStringList{knownServer});
|
||||
} else {
|
||||
RoomManager::instance().knockRoom(connection, roomName, QString(), QStringList{knownServer});
|
||||
}
|
||||
return QString();
|
||||
},
|
||||
false,
|
||||
std::nullopt,
|
||||
kli18n("<room alias or id> [<reason>]"),
|
||||
kli18n("Requests to join the given room"),
|
||||
},
|
||||
#endif
|
||||
Action{
|
||||
QStringLiteral("j"),
|
||||
[](const QString &text, NeoChatRoom *room) {
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
134
src/models/collapsestateproxymodel.cpp
Normal file
134
src/models/collapsestateproxymodel.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "collapsestateproxymodel.h"
|
||||
#include "messageeventmodel.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
bool CollapseStateProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||
{
|
||||
Q_UNUSED(source_parent);
|
||||
return sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::EventTypeRole)
|
||||
!= MessageEventModel::DelegateType::State // If this is not a state, show it
|
||||
|| sourceModel()->data(sourceModel()->index(source_row + 1, 0), MessageEventModel::EventTypeRole)
|
||||
!= MessageEventModel::DelegateType::State // If this is the first state in a block, show it. TODO hidden events?
|
||||
|| sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::ShowSectionRole).toBool(); // If it's a new day, show it
|
||||
}
|
||||
|
||||
QVariant CollapseStateProxyModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (role == AggregateDisplayRole) {
|
||||
return aggregateEventToString(mapToSource(index).row());
|
||||
} else if (role == StateEventsRole) {
|
||||
return stateEventsList(mapToSource(index).row());
|
||||
} else if (role == AuthorListRole) {
|
||||
return authorList(mapToSource(index).row());
|
||||
}
|
||||
return sourceModel()->data(mapToSource(index), role);
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> CollapseStateProxyModel::roleNames() const
|
||||
{
|
||||
auto roles = sourceModel()->roleNames();
|
||||
roles[AggregateDisplayRole] = "aggregateDisplay";
|
||||
roles[StateEventsRole] = "stateEvents";
|
||||
roles[AuthorListRole] = "authorList";
|
||||
return roles;
|
||||
}
|
||||
|
||||
QString CollapseStateProxyModel::aggregateEventToString(int sourceRow) const
|
||||
{
|
||||
QStringList parts;
|
||||
QVariantList uniqueAuthors;
|
||||
for (int i = sourceRow; i >= 0; i--) {
|
||||
parts += sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::GenericDisplayRole).toString();
|
||||
QVariant nextAuthor = sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::AuthorRole);
|
||||
if (!uniqueAuthors.contains(nextAuthor)) {
|
||||
uniqueAuthors.append(nextAuthor);
|
||||
}
|
||||
if (sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::EventTypeRole)
|
||||
!= MessageEventModel::DelegateType::State // If it's not a state event
|
||||
|| sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::ShowSectionRole).toBool() // or the section needs to be visible
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
parts.sort(); // Sort them so that all identical events can be collected.
|
||||
if (!parts.isEmpty()) {
|
||||
QStringList chunks;
|
||||
while (!parts.isEmpty()) {
|
||||
chunks += QString();
|
||||
int count = 1;
|
||||
auto part = parts.takeFirst();
|
||||
chunks.last() += part;
|
||||
while (!parts.isEmpty() && parts.first() == part) {
|
||||
parts.removeFirst();
|
||||
count++;
|
||||
}
|
||||
if (count > 1 && uniqueAuthors.length() == 1) {
|
||||
chunks.last() += i18ncp("n times", " %1 time ", " %1 times ", count);
|
||||
}
|
||||
}
|
||||
chunks.removeDuplicates();
|
||||
QString text = "<style>a {text-decoration: none;}</style>"; // There can be links in the event text so make sure all are styled.
|
||||
// The author text is either "n users" if > 1 user or the matrix.to link to a single user.
|
||||
QString userText = uniqueAuthors.length() > 1 ? i18ncp("n users", " %1 user ", " %1 users ", uniqueAuthors.length())
|
||||
: QStringLiteral("<a href=\"https://matrix.to/#/%1\" style=\"color: %2\">%3</a> ")
|
||||
.arg(uniqueAuthors[0].toMap()["id"].toString(),
|
||||
uniqueAuthors[0].toMap()["color"].toString(),
|
||||
uniqueAuthors[0].toMap()["displayName"].toString());
|
||||
text += userText;
|
||||
text += chunks.takeFirst();
|
||||
|
||||
if (chunks.size() > 0) {
|
||||
while (chunks.size() > 1) {
|
||||
text += i18nc("[action 1], [action 2 and/or action 3]", ", ");
|
||||
text += chunks.takeFirst();
|
||||
}
|
||||
text += uniqueAuthors.length() > 1 ? i18nc("[action 1, action 2] or [action 3]", " or ") : i18nc("[action 1, action 2] and [action 3]", " and ");
|
||||
text += chunks.takeFirst();
|
||||
}
|
||||
return text;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList CollapseStateProxyModel::stateEventsList(int sourceRow) const
|
||||
{
|
||||
QVariantList stateEvents;
|
||||
for (int i = sourceRow; i >= 0; i--) {
|
||||
auto nextState = QVariantMap{
|
||||
{"author", sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::AuthorRole)},
|
||||
{"authorDisplayName", sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::AuthorDisplayNameRole).toString()},
|
||||
{"text", sourceModel()->data(sourceModel()->index(i, 0), Qt::DisplayRole).toString()},
|
||||
};
|
||||
stateEvents.append(nextState);
|
||||
if (sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::EventTypeRole)
|
||||
!= MessageEventModel::DelegateType::State // If it's not a state event
|
||||
|| sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::ShowSectionRole).toBool() // or the section needs to be visible
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return stateEvents;
|
||||
}
|
||||
|
||||
QVariantList CollapseStateProxyModel::authorList(int sourceRow) const
|
||||
{
|
||||
QVariantList uniqueAuthors;
|
||||
for (int i = sourceRow; i >= 0; i--) {
|
||||
QVariant nextAvatar = sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::AuthorRole);
|
||||
if (!uniqueAuthors.contains(nextAvatar)) {
|
||||
uniqueAuthors.append(nextAvatar);
|
||||
}
|
||||
if (sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::EventTypeRole)
|
||||
!= MessageEventModel::DelegateType::State // If it's not a state event
|
||||
|| sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::ShowSectionRole).toBool() // or the section needs to be visible
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return uniqueAuthors;
|
||||
}
|
||||
40
src/models/collapsestateproxymodel.h
Normal file
40
src/models/collapsestateproxymodel.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "messageeventmodel.h"
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class CollapseStateProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Roles {
|
||||
AggregateDisplayRole = MessageEventModel::LastRole + 1,
|
||||
StateEventsRole,
|
||||
AuthorListRole,
|
||||
};
|
||||
[[nodiscard]] bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
[[nodiscard]] QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
||||
|
||||
/**
|
||||
* @brief QString aggregating the text of consecutive state events starting at row.
|
||||
*
|
||||
* If state events happen on different days they will be split into two aggregate
|
||||
* events.
|
||||
*/
|
||||
[[nodiscard]] QString aggregateEventToString(int row) const;
|
||||
/**
|
||||
* @brief Return a list of consecutive state events starting at row.
|
||||
*
|
||||
* If state events happen on different days they will be split into two aggregate
|
||||
* events.
|
||||
*/
|
||||
[[nodiscard]] QVariantList stateEventsList(int row) const;
|
||||
/**
|
||||
* @brief List of unique authors for the aggregate state events starting at row.
|
||||
*/
|
||||
[[nodiscard]] QVariantList authorList(int row) const;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "completionmodel.h"
|
||||
@@ -16,7 +16,7 @@ CompletionModel::CompletionModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, m_filterModel(new CompletionProxyModel())
|
||||
, m_userListModel(new UserListModel(this))
|
||||
, m_emojiModel(new KConcatenateRowsProxyModel(this))
|
||||
, m_emojiModel(new QConcatenateTablesProxyModel(this))
|
||||
{
|
||||
connect(this, &CompletionModel::textChanged, this, &CompletionModel::updateCompletion);
|
||||
connect(this, &CompletionModel::roomChanged, this, [this]() {
|
||||
@@ -145,7 +145,7 @@ void CompletionModel::updateCompletion()
|
||||
m_filterModel->setFullText(m_fullText);
|
||||
m_filterModel->setFilterText(m_text);
|
||||
m_filterModel->invalidate();
|
||||
} else if (text().startsWith(QLatin1Char(':'))
|
||||
} else if (text().startsWith(QLatin1Char(':')) && !text()[1].isUpper()
|
||||
&& (m_fullText.indexOf(QLatin1Char(':'), 1) == -1
|
||||
|| (m_fullText.indexOf(QLatin1Char(' ')) != -1 && m_fullText.indexOf(QLatin1Char(':'), 1) > m_fullText.indexOf(QLatin1Char(' '), 1)))) {
|
||||
m_filterModel->setSourceModel(m_emojiModel);
|
||||
@@ -1,12 +1,11 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QConcatenateTablesProxyModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include <KConcatenateRowsProxyModel>
|
||||
|
||||
#include "roomlistmodel.h"
|
||||
|
||||
class CompletionProxyModel;
|
||||
@@ -75,6 +74,6 @@ private:
|
||||
|
||||
UserListModel *m_userListModel;
|
||||
RoomListModel *m_roomListModel;
|
||||
KConcatenateRowsProxyModel *m_emojiModel;
|
||||
QConcatenateTablesProxyModel *m_emojiModel;
|
||||
};
|
||||
Q_DECLARE_METATYPE(CompletionModel::AutoCompletionType);
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "completionproxymodel.h"
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@@ -4,8 +4,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <memory>
|
||||
#include <QRegularExpression>
|
||||
#include <memory>
|
||||
|
||||
struct CustomEmoji {
|
||||
QString name; // with :semicolons:
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "devicesmodel.h"
|
||||
@@ -70,7 +70,12 @@ void DevicesModel::logout(int index, const QString &password)
|
||||
auto job = Controller::instance().activeConnection()->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId);
|
||||
|
||||
connect(job, &BaseJob::result, this, [this, job, password, index] {
|
||||
if (job->error() != 0) {
|
||||
auto onSuccess = [this, index]() {
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
m_devices.remove(index);
|
||||
endRemoveRows();
|
||||
};
|
||||
if (job->error() != BaseJob::Success) {
|
||||
QJsonObject replyData = job->jsonData();
|
||||
QJsonObject authData;
|
||||
authData["session"] = replyData["session"];
|
||||
@@ -79,11 +84,9 @@ void DevicesModel::logout(int index, const QString &password)
|
||||
QJsonObject identifier = {{"type", "m.id.user"}, {"user", Controller::instance().activeConnection()->user()->id()}};
|
||||
authData["identifier"] = identifier;
|
||||
auto *innerJob = Controller::instance().activeConnection()->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId, authData);
|
||||
connect(innerJob, &BaseJob::success, this, [this, index]() {
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
m_devices.remove(index);
|
||||
endRemoveRows();
|
||||
});
|
||||
connect(innerJob, &BaseJob::success, this, onSuccess);
|
||||
} else {
|
||||
onSuccess();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include "neochatuser.h"
|
||||
#include "utils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
@@ -54,6 +53,9 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
|
||||
roles[UserMarkerRole] = "userMarker";
|
||||
roles[ShowAuthorRole] = "showAuthor";
|
||||
roles[ShowSectionRole] = "showSection";
|
||||
roles[ReadMarkersRole] = "readMarkers";
|
||||
roles[ReadMarkersStringRole] = "readMarkersString";
|
||||
roles[ShowReadMarkersRole] = "showReadMarkers";
|
||||
roles[ReactionRole] = "reaction";
|
||||
roles[IsEditedRole] = "isEdited";
|
||||
roles[SourceRole] = "source";
|
||||
@@ -64,9 +66,9 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
|
||||
roles[VerifiedRole] = "verified";
|
||||
roles[DisplayNameForInitialsRole] = "displayNameForInitials";
|
||||
roles[AuthorDisplayNameRole] = "authorDisplayName";
|
||||
roles[IsNameChangeRole] = "isNameChange";
|
||||
roles[IsAvatarChangeRole] = "isAvatarChange";
|
||||
roles[IsRedactedRole] = "isRedacted";
|
||||
roles[GenericDisplayRole] = "genericDisplay";
|
||||
roles[IsPendingRole] = "isPending";
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -175,6 +177,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
});
|
||||
connect(m_currentRoom, &Room::pendingEventAdded, this, &MessageEventModel::endInsertRows);
|
||||
connect(m_currentRoom, &Room::pendingEventAboutToMerge, this, [this](RoomEvent *, int i) {
|
||||
Q_EMIT dataChanged(index(i, 0), index(i, 0), {IsPendingRole});
|
||||
if (i == 0) {
|
||||
return; // No need to move anything, just refresh
|
||||
}
|
||||
@@ -213,6 +216,12 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
}
|
||||
refreshEventRoles(eventId, {ReactionRole, Qt::DisplayRole});
|
||||
});
|
||||
connect(m_currentRoom, &Room::changed, this, [this]() {
|
||||
for (auto it = m_currentRoom->messageEvents().rbegin(); it != m_currentRoom->messageEvents().rend(); ++it) {
|
||||
auto event = it->event();
|
||||
refreshEventRoles(event->id(), {ReadMarkersRole, ReadMarkersStringRole});
|
||||
}
|
||||
});
|
||||
connect(m_currentRoom, &Room::newFileTransfer, this, &MessageEventModel::refreshEvent);
|
||||
connect(m_currentRoom, &Room::fileTransferProgress, this, &MessageEventModel::refreshEvent);
|
||||
connect(m_currentRoom, &Room::fileTransferCompleted, this, &MessageEventModel::refreshEvent);
|
||||
@@ -439,7 +448,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
case EventTypeRole:
|
||||
return DelegateType::ReadMarker;
|
||||
case TimeRole: {
|
||||
const QDateTime eventDate = data(index(m_lastReadEventIndex.row() + 1, 0), TimeRole).toDateTime();
|
||||
const QDateTime eventDate = data(index(m_lastReadEventIndex.row() + 1, 0), TimeRole).toDateTime().toLocalTime();
|
||||
const KFormat format;
|
||||
return format.formatRelativeDateTime(eventDate, QLocale::ShortFormat);
|
||||
}
|
||||
@@ -462,6 +471,14 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
return m_currentRoom->eventToString(evt, Qt::RichText);
|
||||
}
|
||||
|
||||
if (role == GenericDisplayRole) {
|
||||
if (evt.isRedacted()) {
|
||||
return i18n("<i>[This message was deleted]</i>");
|
||||
}
|
||||
|
||||
return m_currentRoom->eventToGenericString(evt);
|
||||
}
|
||||
|
||||
if (role == FormattedBodyRole) {
|
||||
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
||||
if (e->hasTextContent() && e->mimeType().name() != "text/plain") {
|
||||
@@ -560,7 +577,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
if (role == HighlightRole) {
|
||||
return m_currentRoom->isEventHighlighted(&evt);
|
||||
return !m_currentRoom->isDirectChat() && m_currentRoom->isEventHighlighted(&evt);
|
||||
}
|
||||
|
||||
if (role == FileMimetypeIcon) {
|
||||
@@ -588,12 +605,25 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
|
||||
if (role == SpecialMarksRole) {
|
||||
if (isPending) {
|
||||
// A pending event with an m.new_content key will be merged into the
|
||||
// original event so don't show.
|
||||
if (evt.contentJson().contains("m.new_content")) {
|
||||
return EventStatus::Hidden;
|
||||
}
|
||||
return pendingIt->deliveryStatus();
|
||||
}
|
||||
|
||||
auto *memberEvent = timelineIt->viewAs<RoomMemberEvent>();
|
||||
if (memberEvent) {
|
||||
if ((memberEvent->isJoin() || memberEvent->isLeave()) && !NeoChatConfig::self()->showLeaveJoinEvent()) {
|
||||
if (evt.isStateEvent() && !NeoChatConfig::self()->showStateEvent()) {
|
||||
return EventStatus::Hidden;
|
||||
}
|
||||
|
||||
if (auto roomMemberEvent = eventCast<const RoomMemberEvent>(&evt)) {
|
||||
if ((roomMemberEvent->isJoin() || roomMemberEvent->isLeave()) && !NeoChatConfig::self()->showLeaveJoinEvent()) {
|
||||
return EventStatus::Hidden;
|
||||
} else if (roomMemberEvent->isRename() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::self()->showRename()) {
|
||||
return EventStatus::Hidden;
|
||||
} else if (roomMemberEvent->isAvatarUpdate() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave()
|
||||
&& !NeoChatConfig::self()->showAvatarUpdate()) {
|
||||
return EventStatus::Hidden;
|
||||
}
|
||||
}
|
||||
@@ -780,6 +810,65 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
return false;
|
||||
}
|
||||
|
||||
if (role == ReadMarkersRole) {
|
||||
#ifdef QUOTIENT_07
|
||||
auto userIds = room()->userIdsAtEvent(evt.id());
|
||||
userIds.remove(m_currentRoom->localUser()->id());
|
||||
#else
|
||||
auto userIds = room()->usersAtEventId(evt.id());
|
||||
userIds.removeAll(m_currentRoom->localUser());
|
||||
#endif
|
||||
|
||||
QVariantList users;
|
||||
users.reserve(userIds.size());
|
||||
for (const auto &userId : userIds) {
|
||||
#ifdef QUOTIENT_07
|
||||
auto user = static_cast<NeoChatUser *>(m_currentRoom->user(userId));
|
||||
#else
|
||||
auto user = static_cast<NeoChatUser *>(userId);
|
||||
#endif
|
||||
users += userAtEvent(user, m_currentRoom, evt);
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
if (role == ReadMarkersStringRole) {
|
||||
#ifdef QUOTIENT_07
|
||||
auto userIds = room()->userIdsAtEvent(evt.id());
|
||||
userIds.remove(m_currentRoom->localUser()->id());
|
||||
#else
|
||||
auto userIds = room()->usersAtEventId(evt.id());
|
||||
userIds.removeAll(m_currentRoom->localUser());
|
||||
#endif
|
||||
/**
|
||||
* The string ends up in the form
|
||||
* "x users: user1DisplayName, user2DisplayName, etc."
|
||||
*/
|
||||
QString readMarkersString = i18np("1 user: ", "%1 users: ", userIds.size());
|
||||
for (const auto &userId : userIds) {
|
||||
#ifdef QUOTIENT_07
|
||||
auto user = static_cast<NeoChatUser *>(m_currentRoom->user(userId));
|
||||
#else
|
||||
auto user = static_cast<NeoChatUser *>(userId);
|
||||
#endif
|
||||
readMarkersString += user->displayname(m_currentRoom) + i18nc("list separator", ", ");
|
||||
}
|
||||
readMarkersString.chop(2);
|
||||
return readMarkersString;
|
||||
}
|
||||
|
||||
if (role == ShowReadMarkersRole) {
|
||||
#ifdef QUOTIENT_07
|
||||
auto userIds = room()->userIdsAtEvent(evt.id());
|
||||
userIds.remove(m_currentRoom->localUser()->id());
|
||||
#else
|
||||
auto userIds = room()->usersAtEventId(evt.id());
|
||||
userIds.removeAll(m_currentRoom->localUser());
|
||||
#endif
|
||||
return userIds.size() > 0;
|
||||
}
|
||||
|
||||
if (role == ReactionRole) {
|
||||
const auto &annotations = m_currentRoom->relatedEvents(evt, EventRelation::Annotation());
|
||||
if (annotations.isEmpty()) {
|
||||
@@ -880,25 +969,14 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
}
|
||||
|
||||
if (role == IsNameChangeRole) {
|
||||
auto roomMemberEvent = eventCast<const RoomMemberEvent>(&evt);
|
||||
if (roomMemberEvent) {
|
||||
return roomMemberEvent->isRename();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (role == IsAvatarChangeRole) {
|
||||
auto roomMemberEvent = eventCast<const RoomMemberEvent>(&evt);
|
||||
if (roomMemberEvent) {
|
||||
return roomMemberEvent->isAvatarUpdate();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (role == IsRedactedRole) {
|
||||
return evt.isRedacted();
|
||||
}
|
||||
|
||||
if (role == IsPendingRole) {
|
||||
return row < m_currentRoom->pendingEvents().size();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -966,6 +1044,9 @@ QVariant MessageEventModel::getLatestMessageFromIndex(const int baseline)
|
||||
for (auto it = timelineBottom; it != limit; ++it) {
|
||||
auto evt = it->event();
|
||||
auto e = eventCast<const RoomMessageEvent>(evt);
|
||||
if (!e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto content = (*it)->contentJson();
|
||||
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
AnnotationRole,
|
||||
UserMarkerRole,
|
||||
FormattedBodyRole,
|
||||
GenericDisplayRole,
|
||||
|
||||
MimeTypeRole,
|
||||
FileMimetypeIcon,
|
||||
@@ -56,6 +57,9 @@ public:
|
||||
ShowAuthorRole,
|
||||
ShowSectionRole,
|
||||
|
||||
ReadMarkersRole, /**< QVariantList of users at the event for read marker tracking. */
|
||||
ReadMarkersStringRole, /**< QString with the display name and mxID of the users at the event. */
|
||||
ShowReadMarkersRole, /**< bool with whether there are any other user read markers to be shown. */
|
||||
ReactionRole,
|
||||
|
||||
IsEditedRole,
|
||||
@@ -69,9 +73,8 @@ public:
|
||||
DisplayNameForInitialsRole,
|
||||
// The displayname for the event's sender; for name change events, the old displayname
|
||||
AuthorDisplayNameRole,
|
||||
IsNameChangeRole,
|
||||
IsAvatarChangeRole,
|
||||
IsRedactedRole,
|
||||
IsPendingRole,
|
||||
LastRole, // Keep this last
|
||||
};
|
||||
Q_ENUM(EventRoles)
|
||||
@@ -11,23 +11,20 @@ using namespace Quotient;
|
||||
MessageFilterModel::MessageFilterModel(QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowStateEventChanged, this, [this] {
|
||||
invalidateFilter();
|
||||
});
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLeaveJoinEventChanged, this, [this] {
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
invalidateFilter();
|
||||
});
|
||||
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowRenameChanged, this, [this] {
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
invalidateFilter();
|
||||
});
|
||||
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowAvatarUpdateChanged, this, [this] {
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
invalidateFilter();
|
||||
});
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowDeletedMessagesChanged, this, [this] {
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
invalidateFilter();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -35,18 +32,11 @@ bool MessageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
|
||||
{
|
||||
const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
|
||||
const int specialMarks = index.data(MessageEventModel::SpecialMarksRole).toInt();
|
||||
if (index.data(MessageEventModel::IsNameChangeRole).toBool() && !NeoChatConfig::self()->showRename()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index.data(MessageEventModel::IsAvatarChangeRole).toBool() && !NeoChatConfig::self()->showAvatarUpdate()) {
|
||||
return false;
|
||||
}
|
||||
if (index.data(MessageEventModel::IsRedactedRole).toBool() && !NeoChatConfig::self()->showDeletedMessages()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int specialMarks = index.data(MessageEventModel::SpecialMarksRole).toInt();
|
||||
if (specialMarks == EventStatus::Hidden || specialMarks == EventStatus::Replaced) {
|
||||
return false;
|
||||
}
|
||||
@@ -57,9 +47,5 @@ bool MessageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NeoChatConfig::self()->showLeaveJoinEvent() && eventType == MessageEventModel::State) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "roomlistmodel.h"
|
||||
|
||||
#include "controller.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroom.h"
|
||||
#include "roommanager.h"
|
||||
@@ -143,6 +144,7 @@ void RoomListModel::doAddRoom(Room *r)
|
||||
m_rooms.append(room);
|
||||
connectRoomSignals(room);
|
||||
Q_EMIT roomAdded(room);
|
||||
Q_EMIT Controller::instance().roomAdded(room);
|
||||
} else {
|
||||
qCritical() << "Attempt to add nullptr to the room list";
|
||||
Q_ASSERT(false);
|
||||
@@ -178,29 +180,12 @@ void RoomListModel::connectRoomSignals(NeoChatRoom *room)
|
||||
#ifndef QUOTIENT_07
|
||||
connect(room, &Room::notificationCountChanged, this, &RoomListModel::handleNotifications);
|
||||
#endif
|
||||
connect(room, &Room::highlightCountChanged, this, [this, room] {
|
||||
if (room->highlightCount() == 0) {
|
||||
return;
|
||||
}
|
||||
if (room->timelineSize() == 0) {
|
||||
return;
|
||||
}
|
||||
auto *lastEvent = room->lastEvent();
|
||||
|
||||
if (!lastEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!lastEvent->isStateEvent()) {
|
||||
return;
|
||||
}
|
||||
User *sender = room->user(lastEvent->senderId());
|
||||
if (sender == room->localUser()) {
|
||||
return;
|
||||
}
|
||||
Q_EMIT newHighlight(room->id(), lastEvent->id(), room->displayName(), sender->displayname(), room->eventToString(*lastEvent), room->avatar(128));
|
||||
});
|
||||
#ifndef QUOTIENT_07
|
||||
connect(room, &Room::notificationCountChanged, this, &RoomListModel::refreshNotificationCount);
|
||||
#else
|
||||
connect(room, &Room::unreadStatsChanged, this, &RoomListModel::refreshNotificationCount);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef QUOTIENT_07
|
||||
@@ -229,10 +214,13 @@ void RoomListModel::handleNotifications()
|
||||
continue;
|
||||
}
|
||||
oldNotifications += notification["event"].toObject()["event_id"].toString();
|
||||
|
||||
auto room = m_connection->room(notification["room_id"].toString());
|
||||
auto currentRoom = RoomManager::instance().currentRoom();
|
||||
bool roomIsActive = currentRoom && room->id() == currentRoom->id();
|
||||
|
||||
// If room exists, room is NOT active OR the application is NOT active, show notification
|
||||
if (room && !(room->id() == RoomManager::instance().currentRoom()->id() && QGuiApplication::applicationState() == Qt::ApplicationActive)) {
|
||||
if (room && !(roomIsActive && QGuiApplication::applicationState() == Qt::ApplicationActive)) {
|
||||
// The room might have been deleted (for example rejected invitation).
|
||||
auto sender = room->user(notification["event"].toObject()["sender"].toString());
|
||||
|
||||
@@ -408,7 +396,7 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
|
||||
return m_categoryVisibility.value(data(index, CategoryRole).toInt(), true);
|
||||
}
|
||||
if (role == SubtitleTextRole) {
|
||||
return room->subtitleText();
|
||||
return room->lastEventToString(Qt::PlainText, true);
|
||||
}
|
||||
if (role == AvatarImageRole) {
|
||||
return room->avatar(128);
|
||||
@@ -116,5 +116,4 @@ Q_SIGNALS:
|
||||
void notificationCountChanged();
|
||||
|
||||
void roomAdded(NeoChatRoom *_t1);
|
||||
void newHighlight(const QString &_t1, const QString &_t2, const QString &_t3, const QString &_t4, const QString &_t5, const QImage &_t6);
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "searchmodel.h"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user