Compare commits
441 Commits
work/jz/fl
...
v24.08.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
588266d7c3 | ||
|
|
258e799138 | ||
|
|
73cd253e25 | ||
|
|
eee81329a4 | ||
|
|
9b425e5a54 | ||
|
|
fb83d5cff8 | ||
|
|
21cabeb20c | ||
|
|
bed04cc59f | ||
|
|
18db04d073 | ||
|
|
0f05c1b921 | ||
|
|
765292e0e9 | ||
|
|
7c27308a59 | ||
|
|
b574e68164 | ||
|
|
66d16dd16b | ||
|
|
b1c810c242 | ||
|
|
8f0b3a16f1 | ||
|
|
043519784b | ||
|
|
aafe42c4cc | ||
|
|
d9b9b18872 | ||
|
|
18e26c9e3a | ||
|
|
19c93e9d82 | ||
|
|
af6187706e | ||
|
|
bf18fcbbba | ||
|
|
ee5502d7bf | ||
|
|
d8d6a4ef3b | ||
|
|
2803c6fd58 | ||
|
|
f2f6406403 | ||
|
|
63dc8a5857 | ||
|
|
3dafc62f04 | ||
|
|
5be112e0e2 | ||
|
|
b15ee49691 | ||
|
|
fa5872ec5a | ||
|
|
54061c744f | ||
|
|
b6dac3bbdf | ||
|
|
3a838596c5 | ||
|
|
cc22e80adc | ||
|
|
1c0a5edd2e | ||
|
|
47408d536d | ||
|
|
fc21eea7e7 | ||
|
|
ca95eb3505 | ||
|
|
639cbed343 | ||
|
|
be3cffaf3a | ||
|
|
7af520d0ae | ||
|
|
d145177014 | ||
|
|
75aa2c0e8d | ||
|
|
7eda952aa2 | ||
|
|
22d0d84fee | ||
|
|
941a724381 | ||
|
|
7e880c6663 | ||
|
|
22315b810c | ||
|
|
d0c41d5224 | ||
|
|
a7e06375fd | ||
|
|
e638f61ff1 | ||
|
|
2218b39a50 | ||
|
|
b34936a017 | ||
|
|
5b3e650854 | ||
|
|
86d85c6ce7 | ||
|
|
863e20394a | ||
|
|
908dcea75e | ||
|
|
c59d5bc75d | ||
|
|
ff4ba4df9c | ||
|
|
0c1494e74a | ||
|
|
18d4da8b42 | ||
|
|
9680cbbb38 | ||
|
|
3b5ba470b6 | ||
|
|
e1066aede2 | ||
|
|
2bf4ed26f0 | ||
|
|
b460b588a8 | ||
|
|
05270c38bf | ||
|
|
18afad83db | ||
|
|
d390433b2b | ||
|
|
0017be1c0f | ||
|
|
63eda3796d | ||
|
|
3246076a0b | ||
|
|
e905cdd151 | ||
|
|
0372074beb | ||
|
|
09e97f2bdb | ||
|
|
8e324c16f3 | ||
|
|
2bb55eece7 | ||
|
|
2877e40647 | ||
|
|
768ec242fa | ||
|
|
194751627f | ||
|
|
bc701c51d9 | ||
|
|
35939b4af4 | ||
|
|
a178b8b6ca | ||
|
|
a6994318de | ||
|
|
5022ed1a63 | ||
|
|
799ffa18c4 | ||
|
|
6323e27040 | ||
|
|
03acb26109 | ||
|
|
34fc1f6a6b | ||
|
|
042032ec46 | ||
|
|
73de99f661 | ||
|
|
cc068f9ebb | ||
|
|
2bfc2b1944 | ||
|
|
029936112f | ||
|
|
d5e400e8a4 | ||
|
|
9dbb9c3f3b | ||
|
|
a101e6b0a7 | ||
|
|
c525ea55ce | ||
|
|
8ca45f298f | ||
|
|
cb4c6cb677 | ||
|
|
d574a97a35 | ||
|
|
20f9a86ad9 | ||
|
|
a4a411cf1f | ||
|
|
029bda5734 | ||
|
|
0fd578e6aa | ||
|
|
95e1bee5e6 | ||
|
|
c8dc10f311 | ||
|
|
a00f4e393b | ||
|
|
5a7dea8857 | ||
|
|
27e8970fff | ||
|
|
7848274ba1 | ||
|
|
145bf0298b | ||
|
|
0392a33b54 | ||
|
|
b211f46e3e | ||
|
|
709711c3ca | ||
|
|
841607406f | ||
|
|
70b726b04d | ||
|
|
2799248106 | ||
|
|
2321299084 | ||
|
|
aa00773a3b | ||
|
|
733de1d0e1 | ||
|
|
c16e4ad412 | ||
|
|
d1cf7a07f1 | ||
|
|
8751f6fea7 | ||
|
|
ea1b577ec7 | ||
|
|
a41afa70eb | ||
|
|
e11c97cdc0 | ||
|
|
d76e44512e | ||
|
|
fd1931377d | ||
|
|
68ce6c4ed7 | ||
|
|
a2ee330307 | ||
|
|
c3a17f951e | ||
|
|
443c0f34d7 | ||
|
|
8725600368 | ||
|
|
3b83b7f190 | ||
|
|
e0c3a1c143 | ||
|
|
bec1ad7bee | ||
|
|
9e6c00f78c | ||
|
|
abbbd9d705 | ||
|
|
4a5c012f05 | ||
|
|
319862b3d4 | ||
|
|
1fcf58024d | ||
|
|
9db162d0fc | ||
|
|
13b15390c3 | ||
|
|
799b62e9d2 | ||
|
|
d91ed535ad | ||
|
|
c48b9874bf | ||
|
|
6a788f6c32 | ||
|
|
53519a604e | ||
|
|
2818b87f02 | ||
|
|
50e10133b9 | ||
|
|
068161719e | ||
|
|
c8d5d095e0 | ||
|
|
52d07320ef | ||
|
|
158b9ea2ca | ||
|
|
6a100bfbab | ||
|
|
b3bd6ee176 | ||
|
|
04696b27eb | ||
|
|
332937dbc1 | ||
|
|
92d932ce5c | ||
|
|
af3c4f536a | ||
|
|
b98ca5af52 | ||
|
|
a15a11f44b | ||
|
|
1460132772 | ||
|
|
76d68bb3e4 | ||
|
|
08df0ebbea | ||
|
|
e918db2cc1 | ||
|
|
2e0dc8db94 | ||
|
|
b78a9f2a9c | ||
|
|
832e6b9de0 | ||
|
|
feec7ca408 | ||
|
|
408f0a12e2 | ||
|
|
35ab4e6e09 | ||
|
|
430bafafe7 | ||
|
|
58d727350d | ||
|
|
c6a4057659 | ||
|
|
60ac806690 | ||
|
|
e9263fc596 | ||
|
|
3dd28a0382 | ||
|
|
066cb8184e | ||
|
|
373e22b999 | ||
|
|
fd8725f649 | ||
|
|
3d433762b1 | ||
|
|
ea4cb5bf62 | ||
|
|
a6ca3b8203 | ||
|
|
bc4ceb6d52 | ||
|
|
24480229cd | ||
|
|
8b10573197 | ||
|
|
cbc81e8285 | ||
|
|
0b9a978061 | ||
|
|
e974e5d13b | ||
|
|
5a42e86bf6 | ||
|
|
889946e186 | ||
|
|
5c47e8044e | ||
|
|
1a974ac305 | ||
|
|
db1bf61805 | ||
|
|
5456b4a7ff | ||
|
|
a08ffaae77 | ||
|
|
18445f55f0 | ||
|
|
2daf3b5c4b | ||
|
|
923b844212 | ||
|
|
0aec9f8472 | ||
|
|
23b0c8a143 | ||
|
|
3c2c2e2bd8 | ||
|
|
c1465a7368 | ||
|
|
be1dadab74 | ||
|
|
1cba39eae9 | ||
|
|
a0c8bdf021 | ||
|
|
6fdb22a5b5 | ||
|
|
19c370a273 | ||
|
|
861336ea97 | ||
|
|
24219bcb03 | ||
|
|
77ed762e2c | ||
|
|
d45b6cb03d | ||
|
|
9a921b2e0d | ||
|
|
c17e213e11 | ||
|
|
6275d7afaa | ||
|
|
364eda6400 | ||
|
|
ccf34cfe20 | ||
|
|
7daae6a2d9 | ||
|
|
277a4ad124 | ||
|
|
b11d46e34a | ||
|
|
e8ad0a055d | ||
|
|
8a8c745d77 | ||
|
|
a523fe7674 | ||
|
|
dc9a150929 | ||
|
|
be66ffef0f | ||
|
|
f278cc0c86 | ||
|
|
7f72808a9a | ||
|
|
1d5297c0f0 | ||
|
|
e40528ba45 | ||
|
|
29972b5867 | ||
|
|
91109ca845 | ||
|
|
8e5ccb5461 | ||
|
|
8a75967953 | ||
|
|
3f4965b182 | ||
|
|
2ac266029c | ||
|
|
3261231d07 | ||
|
|
95ce6385b0 | ||
|
|
64c5894602 | ||
|
|
8c4839c300 | ||
|
|
80c2bc1a52 | ||
|
|
bcd417e039 | ||
|
|
ac216f697f | ||
|
|
9893fae27c | ||
|
|
cb183efa66 | ||
|
|
87d707bc21 | ||
|
|
227ebd610a | ||
|
|
ab4af48e52 | ||
|
|
78a8227219 | ||
|
|
ec73a53101 | ||
|
|
3fb1d086b7 | ||
|
|
5d4a12c127 | ||
|
|
d2a79214b5 | ||
|
|
db57111188 | ||
|
|
b22276bcd5 | ||
|
|
6e2d85f2d2 | ||
|
|
efb72652ce | ||
|
|
3615c3e8e5 | ||
|
|
8186ee0e3f | ||
|
|
74aa14c011 | ||
|
|
aa68369f95 | ||
|
|
7837364b86 | ||
|
|
31f0e39617 | ||
|
|
a48151920d | ||
|
|
8199653ddd | ||
|
|
9d650519c7 | ||
|
|
a545eaca97 | ||
|
|
9da9de2d2d | ||
|
|
c9ddd9d513 | ||
|
|
415cfd57e7 | ||
|
|
b1ede93cdb | ||
|
|
32df465c8a | ||
|
|
2f1c5df11d | ||
|
|
f85f6a7fdc | ||
|
|
93cc80a4b3 | ||
|
|
0f3e401d42 | ||
|
|
a9f2c33053 | ||
|
|
c5ef8c3fec | ||
|
|
ad696bd60b | ||
|
|
9da7462f3e | ||
|
|
0ecdf354c6 | ||
|
|
67f10632fb | ||
|
|
044b910357 | ||
|
|
3de943e50f | ||
|
|
1a2272249d | ||
|
|
0dfeec6cae | ||
|
|
8d193021ab | ||
|
|
f45226a680 | ||
|
|
40d55805ff | ||
|
|
7d6d0b012b | ||
|
|
d7d2c6bf01 | ||
|
|
ce1c6096d3 | ||
|
|
8c4ea6180a | ||
|
|
95649ba758 | ||
|
|
2b61f2a9e8 | ||
|
|
47242aa66c | ||
|
|
31a8abe615 | ||
|
|
4dae3c34b4 | ||
|
|
65aa8919b9 | ||
|
|
acf8eac0d4 | ||
|
|
e8aa29d3c2 | ||
|
|
a36194afe2 | ||
|
|
8554898e45 | ||
|
|
55b29f645b | ||
|
|
52cc91ba3e | ||
|
|
c9e3a9f8fe | ||
|
|
c3de788956 | ||
|
|
53e3c36f7e | ||
|
|
20908107a0 | ||
|
|
cbe1a0d6f8 | ||
|
|
7d8c79ec57 | ||
|
|
9bb89c728f | ||
|
|
be2e28cc53 | ||
|
|
57be57971d | ||
|
|
64eed47f04 | ||
|
|
7659105fda | ||
|
|
18c9376992 | ||
|
|
3a4aca7fbd | ||
|
|
85ff8cdd4a | ||
|
|
b1048cb84a | ||
|
|
ff9cf9abf6 | ||
|
|
157c84663d | ||
|
|
908e4fb5a4 | ||
|
|
fdca7d58e5 | ||
|
|
2eb26d512b | ||
|
|
b7fdf7d60f | ||
|
|
2a1e66468d | ||
|
|
cd7faf751e | ||
|
|
d79d806cda | ||
|
|
cae389c04c | ||
|
|
4e6850a60c | ||
|
|
a71f8cf358 | ||
|
|
fe6ff9b2b6 | ||
|
|
5a0f3e474d | ||
|
|
cd9c4239b1 | ||
|
|
0cf1d87b12 | ||
|
|
99d91c1e07 | ||
|
|
457f80bc9b | ||
|
|
968e3fc23b | ||
|
|
b1e338579d | ||
|
|
7e4361bb5e | ||
|
|
dd35fa3d91 | ||
|
|
affb911d97 | ||
|
|
785f9cd1b6 | ||
|
|
419056f098 | ||
|
|
396db18de2 | ||
|
|
f124721cd9 | ||
|
|
52e49b49fb | ||
|
|
10e03777e9 | ||
|
|
27538b72e9 | ||
|
|
19ace37a75 | ||
|
|
90c5377ef0 | ||
|
|
f0e3d3e474 | ||
|
|
26064ebf8c | ||
|
|
1d92b74131 | ||
|
|
5f83c137d2 | ||
|
|
fa57db8e83 | ||
|
|
9d3c2e19f5 | ||
|
|
46a9600d55 | ||
|
|
de40701cf6 | ||
|
|
307536c6b6 | ||
|
|
203be8bd35 | ||
|
|
1e644587b3 | ||
|
|
66a60f09e3 | ||
|
|
69b6f16ec1 | ||
|
|
10b4274a3d | ||
|
|
28c9d94457 | ||
|
|
d74fd1a560 | ||
|
|
624b1b06c5 | ||
|
|
95376c2ccc | ||
|
|
1eb622165b | ||
|
|
9d6ba324fb | ||
|
|
d462852ab9 | ||
|
|
eeec81898b | ||
|
|
ab0c8b8170 | ||
|
|
91d295e0bb | ||
|
|
125974dd7a | ||
|
|
92895a7d00 | ||
|
|
d9308440e6 | ||
|
|
5340142c06 | ||
|
|
012d30ee9f | ||
|
|
031d69d996 | ||
|
|
8b63c18f65 | ||
|
|
dc2f11eb2b | ||
|
|
13e64a9487 | ||
|
|
6a3c88ae5b | ||
|
|
08a7e324aa | ||
|
|
9202a4525f | ||
|
|
bfc756fb35 | ||
|
|
2a735ff1cc | ||
|
|
551092a1b4 | ||
|
|
ddd12688aa | ||
|
|
71767c4172 | ||
|
|
ee405fbff6 | ||
|
|
c87c6fbabb | ||
|
|
096b36b89b | ||
|
|
c3db90d2e3 | ||
|
|
b7df10aa45 | ||
|
|
fea5e02e7d | ||
|
|
cb94261727 | ||
|
|
efac7e4860 | ||
|
|
0f6fd3adee | ||
|
|
e5e0405401 | ||
|
|
bb35e9ce15 | ||
|
|
5881db4e55 | ||
|
|
89f7167b08 | ||
|
|
3b39fcff84 | ||
|
|
0daf45a465 | ||
|
|
c0d7b96e79 | ||
|
|
a247e40865 | ||
|
|
c3e7a99bca | ||
|
|
9080d8be6a | ||
|
|
b7ee83f6b6 | ||
|
|
1e24bde9a9 | ||
|
|
ba82df1152 | ||
|
|
8980fe7838 | ||
|
|
ef34ed7c20 | ||
|
|
17d60b79ca | ||
|
|
ab0a32c339 | ||
|
|
697778df8d | ||
|
|
55caf84b94 | ||
|
|
335c012f1b | ||
|
|
3c4c538de8 | ||
|
|
c344a3ee55 | ||
|
|
d2695947ed | ||
|
|
21beeef920 | ||
|
|
a4630a53fa | ||
|
|
f5aef8d0c3 | ||
|
|
e044e66030 | ||
|
|
88bfacd386 | ||
|
|
c61c73088f | ||
|
|
2887263f26 | ||
|
|
72b90bdf5c | ||
|
|
163b02f023 | ||
|
|
1a96899336 | ||
|
|
554c086aba | ||
|
|
1fad9bf7db | ||
|
|
22d922e451 |
@@ -110,7 +110,7 @@
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/quotient-im/libQuotient.git",
|
||||
"branch": "dev",
|
||||
"branch": "0.8.x",
|
||||
"disable-submodules": true
|
||||
}
|
||||
],
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,3 +12,4 @@ kate.project.ctags.*
|
||||
.idea/
|
||||
cmake-build-*
|
||||
src/res.generated.qrc
|
||||
.qmlls.ini
|
||||
|
||||
@@ -8,7 +8,7 @@ include:
|
||||
- /gitlab-templates/android-qt6.yml
|
||||
- /gitlab-templates/linux-qt6.yml
|
||||
- /gitlab-templates/windows-qt6.yml
|
||||
- /gitlab-templates/freebsd-qt6.yml
|
||||
# - /gitlab-templates/freebsd-qt6.yml
|
||||
- /gitlab-templates/flatpak.yml
|
||||
- /gitlab-templates/craft-android-qt6-apks.yml
|
||||
- /gitlab-templates/craft-appimage-qt6.yml
|
||||
|
||||
@@ -28,6 +28,7 @@ Dependencies:
|
||||
'frameworks/kio': '@latest-kf6'
|
||||
'frameworks/kwindowsystem': '@latest-kf6'
|
||||
'frameworks/kstatusnotifieritem': '@latest-kf6'
|
||||
'frameworks/kcrash': '@latest-kf6'
|
||||
- 'on': ['Linux', 'FreeBSD']
|
||||
'require':
|
||||
'frameworks/kdbusaddons': '@latest-kf6'
|
||||
@@ -39,4 +40,4 @@ Dependencies:
|
||||
|
||||
Options:
|
||||
per-test-timeout: 90
|
||||
require-passing-tests-on: [ '@all' ]
|
||||
require-passing-tests-on: [ 'Linux', 'Android', 'FreeBSD' ]
|
||||
|
||||
@@ -8,8 +8,8 @@ cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# KDE Applications version, managed by release script.
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "24")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "04")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "08")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "2")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||
@@ -38,6 +38,7 @@ include(KDEGitCommitHooks)
|
||||
include(ECMCheckOutboundLicense)
|
||||
include(ECMQtDeclareLoggingCategory)
|
||||
include(ECMAddAndroidApk)
|
||||
include(ECMQmlModule)
|
||||
if (NOT ANDROID)
|
||||
include(KDEClangFormat)
|
||||
endif()
|
||||
@@ -59,7 +60,9 @@ set_package_properties(Qt6 PROPERTIES
|
||||
PURPOSE "Basic application components"
|
||||
)
|
||||
|
||||
qt_policy(SET QTP0001 NEW)
|
||||
if (QT_KNOWN_POLICY_QTP0004)
|
||||
qt_policy(SET QTP0004 NEW)
|
||||
endif ()
|
||||
|
||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels ColorScheme)
|
||||
set_package_properties(KF6 PROPERTIES
|
||||
@@ -84,7 +87,7 @@ if(ANDROID)
|
||||
)
|
||||
else()
|
||||
find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Widgets)
|
||||
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem)
|
||||
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem Crash)
|
||||
find_package(KF6SyntaxHighlighting ${KF_MIN_VERSION} REQUIRED)
|
||||
set_package_properties(KF6QQC2DesktopStyle PROPERTIES
|
||||
TYPE RUNTIME
|
||||
@@ -102,7 +105,7 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(QuotientQt6 0.7)
|
||||
find_package(QuotientQt6 0.8.2)
|
||||
set_package_properties(QuotientQt6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
DESCRIPTION "Qt wrapper around Matrix API"
|
||||
|
||||
48
README.md
48
README.md
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carlschwan@kde.org>
|
||||
SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
||||
SPDX-FileCopyrightText: 2020-2024 Tobias Fella <tobias.fella@kde.org>
|
||||
SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
-->
|
||||
@@ -16,19 +16,18 @@ A Qt/QML based Matrix client.
|
||||
## Introduction
|
||||
|
||||
NeoChat is a client for [Matrix](https://matrix.org), the decentralized communication protocol for instant
|
||||
messaging. It is a fork of Spectral, using KDE frameworks, most notably [Kirigami](https://invent.kde.org/frameworks/kirigami)
|
||||
to provide a convergent experience across multiple platforms.
|
||||
messaging.
|
||||
|
||||
NeoChat also make use of other KDE Frameworks as well as [libQuotient](https://github.com/quotient-im/libQuotient), a
|
||||
NeoChat is based on KDE frameworks and as [libQuotient](https://github.com/quotient-im/libQuotient), a
|
||||
Qt-based SDK for the [Matrix Protocol](https://spec.matrix.org/).
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
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.
|
||||
NeoChat aims to be a fully featured application for the Matrix specification. As such most parts of the current specification are supported, with the notable exceptions
|
||||
of VoIP, threads, and some aspects of End-to-End Encryption. 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.
|
||||
|
||||
Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:
|
||||
- Polls - MSC3381
|
||||
@@ -39,26 +38,9 @@ Due to the nature of the Matrix specification development NeoChat also supports
|
||||
|
||||
Details where to find stable releases for NeoChat can be found on its [homepage](https://apps.kde.org/neochat).
|
||||
|
||||
In addition to the stable builds, unstable nightly builds are available for all platforms. These can be downloaded
|
||||
from the [binary factory](https://binary-factory.kde.org/). There are unstable versions for the following platforms
|
||||
in addition to stable ones:
|
||||
- Android
|
||||
- MacOS
|
||||
- Windows
|
||||
|
||||
Additionally the nightly Flatpak version can be obtained from the nightly Flatpak repo using the following commands in your terminal:
|
||||
|
||||
```
|
||||
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
flatpak remote-add --if-not-exists kdeapps --from https://distribute.kde.org/kdeapps.flatpakrepo
|
||||
flatpak install kdeapps org.kde.neochat
|
||||
```
|
||||
|
||||
The unstable Android version can also be obtained from the [KDE nightly F-Droid repo](https://community.kde.org/Android/FDroid).
|
||||
|
||||
## Running
|
||||
|
||||
Just start the executable in your preferred way - either from the build directory or from the installed location.
|
||||
Nightly builds for Linux and Windows can be downloaded from [cdn.kde.org](https://cdn.kde.org/ci-builds/network/neochat/).
|
||||
Nightly builds for Android are available from [KDE's nightly F-Droid repository](https://community.kde.org/Android/F-Droid).
|
||||
Nightly Flatpaks are available from [KDE's nightly Flatpak repository](https://userbase.kde.org/Tutorials/Flatpak).
|
||||
|
||||
## Building NeoChat
|
||||
|
||||
@@ -69,14 +51,18 @@ is primarily aimed at Linux development.
|
||||
For Windows and Android [Craft](https://invent.kde.org/packaging/craft) is the primary choice. There are guides for setting up
|
||||
development environments for [Windows](https://community.kde.org/Get_Involved/development/Windows) and [Android](https://develop.kde.org/docs/packaging/android/building_applications/).
|
||||
|
||||
## Running
|
||||
|
||||
Just start the executable in your preferred way - either from the build directory or from the installed location.
|
||||
|
||||
## Tests
|
||||
|
||||
Tests are in the repository under [autotests](autotests) and should all pass for any contribution.
|
||||
Tests are in the repository under [autotests](autotests) and [appiumtests](appiumtests).
|
||||
|
||||
The project has CI setup to test new commits to the repository. All tests are expected to pass for a merge request to
|
||||
be complete.
|
||||
|
||||
Current build status
|
||||
## Current build status
|
||||
|
||||

|
||||
|
||||
@@ -100,9 +86,9 @@ The best place to reach the maintainers is on the KDE Matrix instance in the Neo
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
This program utilizes [libQuotient](https://github.com/quotient-im/libQuotient/) as its Matrix SDK.
|
||||
NeoChat utilizes [libQuotient](https://github.com/quotient-im/libQuotient/) as its Matrix SDK.
|
||||
|
||||
This program is a fork of [Spectral](https://gitlab.com/spectral-im/spectral/).
|
||||
NeoChat is a fork of [Spectral](https://gitlab.com/spectral-im/spectral/).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
56
appiumtests/createroomtest.py
Executable file
56
appiumtests/createroomtest.py
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# SPDX-License-Identifier: MIT
|
||||
# SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
import time
|
||||
|
||||
from appium import webdriver
|
||||
from appium.options.common.base import AppiumOptions
|
||||
from appium.webdriver.common.appiumby import AppiumBy
|
||||
|
||||
|
||||
class CreateRoomTest(unittest.TestCase):
|
||||
|
||||
mockServerProcess: subprocess.Popen
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.mockServerProcess = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), "login-server.py")])
|
||||
options = AppiumOptions()
|
||||
options.set_capability("app", "neochat --ignore-ssl-errors --test")
|
||||
cls.driver = webdriver.Remote(command_executor='http://127.0.0.1:4723', options=options)
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
if not self._outcome.result.wasSuccessful():
|
||||
self.driver.get_screenshot_as_file("failed_test_shot_{}.png".format(self.id()))
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(self):
|
||||
self.mockServerProcess.terminate()
|
||||
self.driver.quit()
|
||||
|
||||
def test_create_room(self):
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="@user:localhost:1234").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Show Menu").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Create a Room").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Name:").send_keys("Super awesome room name")#
|
||||
time.sleep(0.1) # without this, the second half of the text is sent to the topic field?!
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Topic:").send_keys("There are not enough raccoons here")
|
||||
time.sleep(0.1)
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Create Room").click()
|
||||
time.sleep(0.1)
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Super awesome room name").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Show Room Information").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="There are not enough raccoons here")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
78
appiumtests/data/sync_response_new_room.json
Normal file
78
appiumtests/data/sync_response_new_room.json
Normal file
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"next_batch": "batch1234",
|
||||
"rooms": {
|
||||
"join": {
|
||||
"!newroom123321:localhost:1234": {
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"type": "m.room.member",
|
||||
"state_key": "@user:localhost:1234",
|
||||
"sender": "@user:localhost:1234",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"event_id": "$event_id_1234_0:localhost:1234",
|
||||
"room_id": "!newroom123321:localhost:1234",
|
||||
"content": {
|
||||
"avatar_url": "",
|
||||
"displayname": "A Display Name",
|
||||
"membership": "join",
|
||||
"reason": "Nothing"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "m.room.name",
|
||||
"state_key": "",
|
||||
"sender": "@user:localhost:1234",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"event_id": "$event_id_1234_1:localhost:1234",
|
||||
"room_id": "!newroom123321:localhost:1234",
|
||||
"content": {
|
||||
"name": "Super awesome room name"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "m.room.topic",
|
||||
"state_key": "",
|
||||
"sender": "@user:localhost:1234",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"event_id": "$event_id_1234_2:localhost:1234",
|
||||
"room_id": "!newroom123321:localhost:1234",
|
||||
"content": {
|
||||
"topic": "There are not enough raccoons here"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"type": "m.room.message",
|
||||
"sender": "@user:localhost:1234",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"event_id": "$event_id_1234_1:localhost:1234",
|
||||
"room_id": "!newroom123321:localhost:1234",
|
||||
"content": {
|
||||
"body": "This is a message",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<a href=\"https://matrix.to/#/@user:localhost:1234\">User</a>:",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ from flask import Flask, request, abort
|
||||
import os
|
||||
app = Flask(__name__)
|
||||
|
||||
next_sync_payload = ""
|
||||
|
||||
|
||||
@app.route("/_matrix/client/v3/login", methods=["GET"])
|
||||
def login_get():
|
||||
@@ -42,8 +44,13 @@ def load_json(name):
|
||||
|
||||
@app.route("/_matrix/client/r0/sync")
|
||||
def sync():
|
||||
|
||||
result = load_json("sync_response_no_rooms") if ("login" in request.headers.get("Authorization")) else load_json("sync_response_rooms")
|
||||
global next_sync_payload
|
||||
result = dict()
|
||||
if len(next_sync_payload) > 0:
|
||||
result = load_json(next_sync_payload)
|
||||
next_sync_payload = ""
|
||||
else:
|
||||
result = load_json("sync_response_no_rooms") if ("login" in request.headers.get("Authorization")) else load_json("sync_response_rooms")
|
||||
return result
|
||||
|
||||
@app.route("/.well-known/matrix/client")
|
||||
@@ -65,6 +72,18 @@ def upload_keys():
|
||||
reply = dict()
|
||||
return reply
|
||||
|
||||
@app.route("/_matrix/client/v3/createRoom", methods=["POST"])
|
||||
def create_room():
|
||||
global next_sync_payload
|
||||
data = request.get_json()
|
||||
if data["name"] != "Super awesome room name" or data["topic"] != "There are not enough raccoons here":
|
||||
return dict(), 400
|
||||
response = dict()
|
||||
response["room_id"] = "!newroom123321:localhost:1234"
|
||||
next_sync_payload = "sync_response_new_room"
|
||||
return response
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(ssl_context='adhoc', port=1234)
|
||||
|
||||
@@ -39,6 +39,10 @@ class OpenUserDetailsTest(unittest.TestCase):
|
||||
|
||||
def test_open_sheet(self):
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="@user:localhost:1234").click()
|
||||
try:
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Expand Normal").click()
|
||||
except:
|
||||
pass
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Empty room (!room_id_1234:localhost:1234)").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="A Display Name").click()
|
||||
self.driver.find_element(by=AppiumBy.NAME, value="Account Details")
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
|
||||
#include <Quotient/roommember.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
#include <qtestcase.h>
|
||||
|
||||
@@ -50,7 +51,7 @@ void ChatBarCacheTest::empty()
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(nullptr));
|
||||
QCOMPARE(chatBarCache->relationUser(), room->member(QString()));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
}
|
||||
@@ -64,7 +65,7 @@ void ChatBarCacheTest::noRoom()
|
||||
// ChatBarCache has no parent.
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationUser(), QVariantMap());
|
||||
QCOMPARE(chatBarCache->relationUser(), Quotient::RoomMember());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
@@ -80,7 +81,7 @@ void ChatBarCacheTest::badParent()
|
||||
// ChatBarCache has no parent.
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationUser(), QVariantMap());
|
||||
QCOMPARE(chatBarCache->relationUser(), Quotient::RoomMember());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.");
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
@@ -98,7 +99,7 @@ void ChatBarCacheTest::reply()
|
||||
QCOMPARE(chatBarCache->replyId(), QLatin1String("$153456789:example.org"));
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(room->user(QLatin1String("@example:example.org"))));
|
||||
QCOMPARE(chatBarCache->relationUser(), room->member(QLatin1String("@example:example.org")));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QLatin1String("This is an example\ntext message"));
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
}
|
||||
@@ -115,7 +116,7 @@ void ChatBarCacheTest::edit()
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), true);
|
||||
QCOMPARE(chatBarCache->editId(), QLatin1String("$153456789:example.org"));
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(room->user(QLatin1String("@example:example.org"))));
|
||||
QCOMPARE(chatBarCache->relationUser(), room->member(QLatin1String("@example:example.org")));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QLatin1String("This is an example\ntext message"));
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QString());
|
||||
}
|
||||
@@ -132,7 +133,7 @@ void ChatBarCacheTest::attachment()
|
||||
QCOMPARE(chatBarCache->replyId(), QString());
|
||||
QCOMPARE(chatBarCache->isEditing(), false);
|
||||
QCOMPARE(chatBarCache->editId(), QString());
|
||||
QCOMPARE(chatBarCache->relationUser(), room->getUser(nullptr));
|
||||
QCOMPARE(chatBarCache->relationUser(), room->member(QString()));
|
||||
QCOMPARE(chatBarCache->relationMessage(), QString());
|
||||
QCOMPARE(chatBarCache->attachmentPath(), QLatin1String("some/path"));
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"content": {
|
||||
"user_ids": [
|
||||
"@alice:matrix.org",
|
||||
"@bob:example.com"
|
||||
"@bob:kde.org"
|
||||
]
|
||||
},
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
@@ -35,7 +35,7 @@
|
||||
"content": {
|
||||
"$153456789:example.org": {
|
||||
"m.read": {
|
||||
"@alice:matrix.org": {
|
||||
"@alice:example.org": {
|
||||
"ts": 1436451550453
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@bob:example.com": {
|
||||
"@bob:kde.org": {
|
||||
"ts": 1436451550453
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,18 @@
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
"m.read": {
|
||||
"@tim2:example.com": {
|
||||
"ts": 1436451550454
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "m.receipt"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"$1532735824654:example.org": {
|
||||
@@ -136,6 +148,22 @@
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Bob",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "bob:kde.org",
|
||||
"state_key": "@bob:kde.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"displayname": "Look\nat\nme\nI\nput\nnewlines\nin\nmy\ndisplay name",
|
||||
@@ -156,7 +184,7 @@
|
||||
"summary": {
|
||||
"m.heroes": [
|
||||
"@alice:example.com",
|
||||
"@bob:example.com"
|
||||
"@bob:kde.org"
|
||||
],
|
||||
"m.invited_member_count": 0,
|
||||
"m.joined_member_count": 2
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "https://matrix.to/#/@alice:example.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "testhttps://kde.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
@@ -37,16 +37,14 @@
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Alice Margatroid",
|
||||
"membership": "join",
|
||||
"reason": "Looking for support"
|
||||
"displayname": "Example",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"state_key": "@example:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
|
||||
@@ -130,7 +130,23 @@
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@alice:example.org",
|
||||
"state_key": "@alice:matrix.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Bob",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "bob:example.org",
|
||||
"state_key": "@bob:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
|
||||
@@ -51,6 +51,21 @@
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"displayname": "Example",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "@example:example.org",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "https://kde.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "www.example.org",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"content": {
|
||||
"body": "[Rich Link](https://kde.org)",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<a href=\"https://kde.org\">Rich Link</a>",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$validlink1:example.org",
|
||||
"origin_server_ts": 1432735824654,
|
||||
"room_id": "!test:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 1234
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,7 @@ void DelegateSizeHelperTest::equalBreakpoint_data()
|
||||
}
|
||||
|
||||
/**
|
||||
* We expect a default return except in the case where the the two percentages are
|
||||
* We expect a default return except in the case where the two percentages are
|
||||
* equal as that case can be calculated without dividing by zero.
|
||||
*/
|
||||
void DelegateSizeHelperTest::equalBreakpoint()
|
||||
|
||||
@@ -36,8 +36,6 @@ private Q_SLOTS:
|
||||
|
||||
void eventId();
|
||||
void nullEventId();
|
||||
void author();
|
||||
void nullAuthor();
|
||||
void authorDisplayName();
|
||||
void nullAuthorDisplayName();
|
||||
void singleLineSidplayName();
|
||||
@@ -56,6 +54,7 @@ private Q_SLOTS:
|
||||
void genericBody();
|
||||
void nullGenericBody();
|
||||
void markdownBody();
|
||||
void markdownBodyReply();
|
||||
void subtitle();
|
||||
void nullSubtitle();
|
||||
void mediaInfo();
|
||||
@@ -74,8 +73,6 @@ private Q_SLOTS:
|
||||
void nullThread();
|
||||
void location();
|
||||
void nullLocation();
|
||||
void readMarkers();
|
||||
void nullReadMarkers();
|
||||
};
|
||||
|
||||
void EventHandlerTest::initTestCase()
|
||||
@@ -97,33 +94,6 @@ void EventHandlerTest::nullEventId()
|
||||
QCOMPARE(noEventHandler.getId(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::author()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
auto author = room->user(event->senderId());
|
||||
EventHandler eventHandler(room, event);
|
||||
|
||||
auto eventHandlerAuthor = eventHandler.getAuthor();
|
||||
|
||||
QCOMPARE(eventHandlerAuthor["isLocalUser"_ls], author->id() == room->localUser()->id());
|
||||
QCOMPARE(eventHandlerAuthor["id"_ls], author->id());
|
||||
QCOMPARE(eventHandlerAuthor["displayName"_ls], author->displayname(room));
|
||||
QCOMPARE(eventHandlerAuthor["avatarSource"_ls], room->avatarForMember(author));
|
||||
QCOMPARE(eventHandlerAuthor["avatarMediaId"_ls], author->avatarMediaId(room));
|
||||
QCOMPARE(eventHandlerAuthor["color"_ls], Utils::getUserColor(author->hueF()));
|
||||
QCOMPARE(eventHandlerAuthor["object"_ls], QVariant::fromValue(author));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullAuthor()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getAuthor(), QVariantMap());
|
||||
|
||||
EventHandler noEventHandler(room, nullptr);
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_event set to nullptr. Returning empty user.");
|
||||
QCOMPARE(noEventHandler.getAuthor(), room->getUser(nullptr));
|
||||
}
|
||||
|
||||
void EventHandlerTest::authorDisplayName()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(1).get());
|
||||
@@ -193,6 +163,7 @@ void EventHandlerTest::timeString()
|
||||
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().time(), QLocale::LongFormat));
|
||||
QCOMPARE(eventHandler.getTimeString(true, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
||||
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::LongFormat));
|
||||
QCOMPARE(eventHandler.getTimeString(QStringLiteral("hh:mm")), QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toString(QStringLiteral("hh:mm")));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullTimeString()
|
||||
@@ -301,6 +272,13 @@ void EventHandlerTest::markdownBody()
|
||||
QCOMPARE(eventHandler.getMarkdownBody(), QStringLiteral("This is an example\ntext message"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::markdownBodyReply()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(5).get());
|
||||
|
||||
QCOMPARE(eventHandler.getMarkdownBody(), QStringLiteral("reply"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::subtitle()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||
@@ -385,31 +363,30 @@ void EventHandlerTest::nullReplyId()
|
||||
void EventHandlerTest::replyAuthor()
|
||||
{
|
||||
auto replyEvent = room->messageEvents().at(0).get();
|
||||
auto replyAuthor = room->user(replyEvent->senderId());
|
||||
auto replyAuthor = room->member(replyEvent->senderId());
|
||||
EventHandler eventHandler(room, room->messageEvents().at(5).get());
|
||||
|
||||
auto eventHandlerReplyAuthor = eventHandler.getReplyAuthor();
|
||||
|
||||
QCOMPARE(eventHandlerReplyAuthor["isLocalUser"_ls], replyAuthor->id() == room->localUser()->id());
|
||||
QCOMPARE(eventHandlerReplyAuthor["id"_ls], replyAuthor->id());
|
||||
QCOMPARE(eventHandlerReplyAuthor["displayName"_ls], replyAuthor->displayname(room));
|
||||
QCOMPARE(eventHandlerReplyAuthor["avatarSource"_ls], room->avatarForMember(replyAuthor));
|
||||
QCOMPARE(eventHandlerReplyAuthor["avatarMediaId"_ls], replyAuthor->avatarMediaId(room));
|
||||
QCOMPARE(eventHandlerReplyAuthor["color"_ls], Utils::getUserColor(replyAuthor->hueF()));
|
||||
QCOMPARE(eventHandlerReplyAuthor["object"_ls], QVariant::fromValue(replyAuthor));
|
||||
QCOMPARE(eventHandlerReplyAuthor.isLocalMember(), replyAuthor.id() == room->localMember().id());
|
||||
QCOMPARE(eventHandlerReplyAuthor.id(), replyAuthor.id());
|
||||
QCOMPARE(eventHandlerReplyAuthor.displayName(), replyAuthor.displayName());
|
||||
QCOMPARE(eventHandlerReplyAuthor.avatarUrl(), replyAuthor.avatarUrl());
|
||||
QCOMPARE(eventHandlerReplyAuthor.avatarMediaId(), replyAuthor.avatarMediaId());
|
||||
QCOMPARE(eventHandlerReplyAuthor.color(), replyAuthor.color());
|
||||
|
||||
EventHandler eventHandlerNoAuthor(room, room->messageEvents().at(0).get());
|
||||
QCOMPARE(eventHandlerNoAuthor.getReplyAuthor(), room->getUser(nullptr));
|
||||
QCOMPARE(eventHandlerNoAuthor.getReplyAuthor(), RoomMember());
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReplyAuthor()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyAuthor called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReplyAuthor(), QVariantMap());
|
||||
QCOMPARE(emptyHandler.getReplyAuthor(), RoomMember());
|
||||
|
||||
EventHandler noEventHandler(room, nullptr);
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReplyAuthor called with m_event set to nullptr. Returning empty user.");
|
||||
QCOMPARE(noEventHandler.getReplyAuthor(), room->getUser(nullptr));
|
||||
QCOMPARE(noEventHandler.getReplyAuthor(), RoomMember());
|
||||
}
|
||||
|
||||
void EventHandlerTest::replyBody()
|
||||
@@ -515,59 +492,5 @@ void EventHandlerTest::nullLocation()
|
||||
QCOMPARE(emptyHandler.getLocationAssetType(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::readMarkers()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||
QCOMPARE(eventHandler.hasReadMarkers(), true);
|
||||
|
||||
auto readMarkers = eventHandler.getReadMarkers();
|
||||
|
||||
QCOMPARE(readMarkers.size(), 1);
|
||||
QCOMPARE(readMarkers[0].toMap()["id"_ls], QStringLiteral("@alice:matrix.org"));
|
||||
|
||||
QCOMPARE(eventHandler.getNumberExcessReadMarkers(), QString());
|
||||
QCOMPARE(eventHandler.getReadMarkersString(), QStringLiteral("1 user: @alice:matrix.org"));
|
||||
|
||||
EventHandler eventHandler2(room, room->messageEvents().at(2).get());
|
||||
QCOMPARE(eventHandler2.hasReadMarkers(), true);
|
||||
|
||||
readMarkers = eventHandler2.getReadMarkers();
|
||||
|
||||
QCOMPARE(readMarkers.size(), 5);
|
||||
|
||||
QCOMPARE(eventHandler2.getNumberExcessReadMarkers(), QStringLiteral("+ 1"));
|
||||
// There are no guarantees on the order of the users it will be different every time so don't match the whole string.
|
||||
QCOMPARE(eventHandler2.getReadMarkersString().startsWith(QStringLiteral("6 users:")), true);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReadMarkers()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "hasReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.hasReadMarkers(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReadMarkers(), QVariantList());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getNumberExcessReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getNumberExcessReadMarkers(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkersString called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReadMarkersString(), QString());
|
||||
|
||||
EventHandler noEventHandler(room, nullptr);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "hasReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.hasReadMarkers(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReadMarkers(), QVariantList());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getNumberExcessReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getNumberExcessReadMarkers(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkersString called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReadMarkersString(), QString());
|
||||
}
|
||||
|
||||
QTEST_MAIN(EventHandlerTest)
|
||||
#include "eventhandlertest.moc"
|
||||
|
||||
@@ -6,12 +6,11 @@
|
||||
|
||||
#include "linkpreviewer.h"
|
||||
|
||||
#include "utils.h"
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
@@ -30,10 +29,11 @@ private Q_SLOTS:
|
||||
void linkPreviewsMatch_data();
|
||||
void linkPreviewsMatch();
|
||||
|
||||
void multipleLinkPreviewsMatch_data();
|
||||
void multipleLinkPreviewsMatch();
|
||||
|
||||
void linkPreviewsReject_data();
|
||||
void linkPreviewsReject();
|
||||
|
||||
void editedLink();
|
||||
};
|
||||
|
||||
void LinkPreviewerTest::initTestCase()
|
||||
@@ -44,60 +44,59 @@ void LinkPreviewerTest::initTestCase()
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsMatch_data()
|
||||
{
|
||||
QTest::addColumn<QString>("eventSource");
|
||||
QTest::addColumn<QString>("inputString");
|
||||
QTest::addColumn<QUrl>("testOutputLink");
|
||||
|
||||
QTest::newRow("plainHttps") << QStringLiteral("test-validplainlink-event.json") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("richHttps") << QStringLiteral("test-validrichlink-event.json") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("plainWww") << QStringLiteral("test-validplainwwwlink-event.json") << QUrl("www.example.org"_ls);
|
||||
QTest::newRow("multipleHttps") << QStringLiteral("test-multiplelink-event.json") << QUrl("www.example.org"_ls);
|
||||
QTest::newRow("plainHttps") << QStringLiteral("https://kde.org") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("richHttps") << QStringLiteral("<a href=\"https://kde.org\">Rich Link</a>") << QUrl("https://kde.org"_ls);
|
||||
QTest::newRow("richHttpsLinkDescription") << QStringLiteral("<a href=\"https://kde.org\">https://kde.org</a>") << QUrl("https://kde.org"_ls);
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsMatch()
|
||||
{
|
||||
QFETCH(QString, eventSource);
|
||||
QFETCH(QString, inputString);
|
||||
QFETCH(QUrl, testOutputLink);
|
||||
|
||||
auto event = TestUtils::loadEventFromFile<RoomMessageEvent>(eventSource);
|
||||
auto linkPreviewer = LinkPreviewer(room, event.get());
|
||||
auto link = LinkPreviewer::linkPreviews(inputString)[0];
|
||||
|
||||
QCOMPARE(linkPreviewer.empty(), false);
|
||||
QCOMPARE(linkPreviewer.url(), testOutputLink);
|
||||
QCOMPARE(link, testOutputLink);
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::multipleLinkPreviewsMatch_data()
|
||||
{
|
||||
QTest::addColumn<QString>("inputString");
|
||||
QTest::addColumn<QList<QUrl>>("testOutputLinks");
|
||||
|
||||
QTest::newRow("multipleHttps") << QStringLiteral("www.example.org https://kde.org") << QList{QUrl("www.example.org"_ls), QUrl("https://kde.org"_ls)};
|
||||
QTest::newRow("multipleHttps1Invalid") << QStringLiteral("www.example.org mxc://example.org/SEsfnsuifSDFSSEF") << QList{QUrl("www.example.org"_ls)};
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::multipleLinkPreviewsMatch()
|
||||
{
|
||||
QFETCH(QString, inputString);
|
||||
QFETCH(QList<QUrl>, testOutputLinks);
|
||||
|
||||
auto links = LinkPreviewer::linkPreviews(inputString);
|
||||
|
||||
QCOMPARE(links, testOutputLinks);
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsReject_data()
|
||||
{
|
||||
QTest::addColumn<QString>("eventSource");
|
||||
QTest::addColumn<QString>("inputString");
|
||||
|
||||
QTest::newRow("mxc") << QStringLiteral("test-invalidmxclink-event.json");
|
||||
QTest::newRow("matrixTo") << QStringLiteral("test-invalidmatrixtolink-event.json");
|
||||
QTest::newRow("noSpace") << QStringLiteral("test-invalidnospacelink-event.json");
|
||||
QTest::newRow("mxc") << QStringLiteral("mxc://example.org/SEsfnsuifSDFSSEF");
|
||||
QTest::newRow("matrixTo") << QStringLiteral("https://matrix.to/#/@alice:example.org");
|
||||
QTest::newRow("noSpace") << QStringLiteral("testhttps://kde.org");
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::linkPreviewsReject()
|
||||
{
|
||||
QFETCH(QString, eventSource);
|
||||
QFETCH(QString, inputString);
|
||||
|
||||
auto event = TestUtils::loadEventFromFile<RoomMessageEvent>(eventSource);
|
||||
auto linkPreviewer = LinkPreviewer(room, event.get());
|
||||
auto links = LinkPreviewer::linkPreviews(inputString);
|
||||
|
||||
QCOMPARE(linkPreviewer.empty(), true);
|
||||
QCOMPARE(linkPreviewer.url(), QUrl());
|
||||
}
|
||||
|
||||
void LinkPreviewerTest::editedLink()
|
||||
{
|
||||
room->syncNewEvents(QStringLiteral("test-linkpreviewerintial-sync.json"));
|
||||
auto event = eventCast<const RoomMessageEvent>(room->messageEvents().at(0).get());
|
||||
auto linkPreviewer = LinkPreviewer(room, event);
|
||||
|
||||
QCOMPARE(linkPreviewer.empty(), false);
|
||||
QCOMPARE(linkPreviewer.url(), QUrl("https://kde.org"_ls));
|
||||
|
||||
room->syncNewEvents(QStringLiteral("test-linkpreviewerreplace-sync.json"));
|
||||
|
||||
QCOMPARE(linkPreviewer.empty(), true);
|
||||
QCOMPARE(linkPreviewer.url(), QUrl());
|
||||
QCOMPARE(links.empty(), true);
|
||||
}
|
||||
|
||||
QTEST_MAIN(LinkPreviewerTest)
|
||||
|
||||
@@ -52,10 +52,8 @@ void ReactionModelTest::basicReaction()
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::TextContentRole), QStringLiteral("<span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::ReactionRole), QStringLiteral("👍"));
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::ToolTipRole),
|
||||
QStringLiteral("@alice:matrix.org reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
auto authorList = QVariantList{room->getUser(room->user(QStringLiteral("@alice:matrix.org")))};
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::AuthorsRole), authorList);
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::HasLocalUser), false);
|
||||
QStringLiteral("Alice Margatroid reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::HasLocalMember), false);
|
||||
}
|
||||
|
||||
void ReactionModelTest::newReaction()
|
||||
@@ -65,7 +63,7 @@ void ReactionModelTest::newReaction()
|
||||
|
||||
QCOMPARE(model->rowCount(), 1);
|
||||
QCOMPARE(model->data(model->index(0), ReactionModel::ToolTipRole),
|
||||
QStringLiteral("@alice:matrix.org reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
QStringLiteral("Alice Margatroid reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
|
||||
QSignalSpy spy(model, SIGNAL(modelReset()));
|
||||
|
||||
@@ -74,7 +72,7 @@ void ReactionModelTest::newReaction()
|
||||
QCOMPARE(spy.count(), 2); // Once for each of the 2 new reactions.
|
||||
QCOMPARE(model->data(model->index(1), ReactionModel::ReactionRole), QStringLiteral("😆"));
|
||||
QCOMPARE(model->data(model->index(0), ReactionModel::ToolTipRole),
|
||||
QStringLiteral("@alice:matrix.org and @bob:example.org reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
QStringLiteral("Alice Margatroid and Bob reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
|
||||
|
||||
delete model;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<name xml:lang="fi">NeoChat</name>
|
||||
<name xml:lang="fr">NeoChat</name>
|
||||
<name xml:lang="gl">NeoChat</name>
|
||||
<name xml:lang="he">NeoChat</name>
|
||||
<name xml:lang="hu">NeoChat</name>
|
||||
<name xml:lang="ia">Neochat</name>
|
||||
<name xml:lang="id">NeoChat</name>
|
||||
@@ -54,12 +55,15 @@
|
||||
<summary xml:lang="ca">Xategeu amb els vostres amics a Matrix</summary>
|
||||
<summary xml:lang="ca-valencia">Xategeu amb els vostres amics a Matrix</summary>
|
||||
<summary xml:lang="cs">Mluvte se svými přáteli na Matrixu</summary>
|
||||
<summary xml:lang="de">Mit den Freunden auf Matrix unterhalten</summary>
|
||||
<summary xml:lang="en-GB">Chat with your friends on matrix</summary>
|
||||
<summary xml:lang="eo">Babilu kun viaj amikoj sur matrix</summary>
|
||||
<summary xml:lang="es">Charle con sus amigos en matrix</summary>
|
||||
<summary xml:lang="eu">Berriketan jardun zure lagunekin «Matrix»en</summary>
|
||||
<summary xml:lang="fi">Keskustelu ystäviesi kanssa Matrixissa</summary>
|
||||
<summary xml:lang="fr">Discuter avec vos ami(e)s sur le réseau Matrix</summary>
|
||||
<summary xml:lang="gl">Charle coas súas amizades en Matrix.</summary>
|
||||
<summary xml:lang="he">התכתבות עם החברים שלך ב־matrix</summary>
|
||||
<summary xml:lang="hu">Csevegjen barátaival a matrixon</summary>
|
||||
<summary xml:lang="ia">Starta Conversation con tu amicos sur matrix</summary>
|
||||
<summary xml:lang="it">Conversa con i tuoi contatti su matrix</summary>
|
||||
@@ -79,20 +83,28 @@
|
||||
<summary xml:lang="zh-TW">在 Matrix 上與您的朋友聊天</summary>
|
||||
<description>
|
||||
<p>NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
|
||||
<p xml:lang="ar">نيوتشات هو تطبيق دردشة يتيح لك الاستفادة الكاملة من شبكة Matrix. فهو يوفر لك طريقة آمنة لإرسال الرسائل النصية ومقاطع الفيديو والملفات الصوتية إلى عائلتك وزملائك وأصدقائك.</p>
|
||||
<p xml:lang="ca">El NeoChat és una aplicació de xat que us permet aprofitar plenament la xarxa Matrix. Proporciona una manera segura d'enviar missatges de text, vídeos i arxius d'àudio a la vostra família, companys i amics.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat és una aplicació de xat que us permet aprofitar plenament la xarxa Matrix. Proporciona una manera segura d'enviar missatges de text, vídeos i arxius d'àudio a la vostra família, companys i amics.</p>
|
||||
<p xml:lang="de">NeoChat ist eine Anwendung für Unterhaltungen mit allen Vorteilen des Matrix-Netzwerkes. Sie bietet eine sichere Möglichkeit zum Versenden von Nachrichten, Videos und Audiodateien and die Familienmitglieder.</p>
|
||||
<p xml:lang="en-GB">NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
|
||||
<p xml:lang="eo">NeoChat estas babilej-apo, kiu ebligas al vi plene profiti de la Matrix-reto. Ĝi provizas al vi sekuran manieron sendi tekstmesaĝojn, filmetojn kaj sondosierojn al via familio, kolegoj kaj amikoj.</p>
|
||||
<p xml:lang="es">NeoChat es una aplicación de chat que le permite aprovechar al máximo la red Matrix. Le proporciona un modo seguro de enviar mensajes de texto, vídeos y archivos de sonido a su familia, colegas y amigos.</p>
|
||||
<p xml:lang="eu">NeoChat, Matrix sarearen abantaila guztiei probetsua ateratzeko aukera ematen dizun berriketa aplikaizo bat da. Zure familiari, kideei eta lagunei testu mezuak, bideoak eta audio fitxategiak era seguruan bidaltzeko aukera ematen dizu.</p>
|
||||
<p xml:lang="fr">NeoChat est une application de discussions vous permettant de profiter pleinement du réseau Matrix. Elle vous offre un moyen sécurisé d’envoyer des messages de texte, des vidéos et des fichiers audio à votre famille, vos collègues et vos ami(e)s.</p>
|
||||
<p xml:lang="fi">NeoChat on keskustelusovellus, jolla Matrix-verkosta saa täyden hyödyn. Se tarjoaa salatun kanavan lähettää perheelle, työkavereille ja ystäville tekstiviestejä sekä video- ja äänitiedostoja.</p>
|
||||
<p xml:lang="fr">NeoChat est une application de discussions vous permettant de profiter pleinement du réseau Matrix. Elle vous offre un moyen sécurisé d'envoyer des messages de texte, des vidéos et des fichiers audio à votre famille, vos collègues et vos ami(e)s.</p>
|
||||
<p xml:lang="gl">NeoChat é unha aplicación de conversa que lle permite usar todas as funcionalidades da rede Matrix. Fornece unha forma segura de enviar mensaxes de texto e ficheiros de vídeo e son a familiares, amizades ou no traballo.</p>
|
||||
<p xml:lang="he">NeoChat הוא יישום התכתבות שמאפשר לך לנצל את רשת Matrix במלואה. הוא מספק דרך מאובטחת לשליחת הודעות כתובות, סרטונים וקובצי שמע למשפחה, לעמיתים לעבודה ולחברים.</p>
|
||||
<p xml:lang="hu">A NeoChat egy olyan csevegőalkalmazás, amellyel teljes mértékben kihasználhatja a Matrix hálózatot. Biztonságos módot biztosít szöveges üzenetek, videók és hangfájlok küldéséhez családtagjainak, kollégáinak és barátainak.</p>
|
||||
<p xml:lang="ia">NeoChat es un app de conversation que te permitte prender avantage plen del rete Matrix. Il te forni un modo secur de inviar messages de texto, videos e files audio a tui familia, collegas e amicos.</p>
|
||||
<p xml:lang="it">NeoChat è un'applicazione di chat che ti consente di sfruttare appieno la rete Matrix. Ti fornisce un modo sicuro per inviare messaggi di testo, video e file audio a familiari, colleghi e amici.</p>
|
||||
<p xml:lang="ka">NeoChat ჩატის აპია, რომელიც საშუალება გაძლევთ, Matrix-ის ქსელის საშუალებები ბოლომდე გამოიყენოთ. ის გაძლევთ უსაფრთხო გზას, გააგზავნოთ ტექსტური შეტყობინებები, ვიდეოებ და აუდიოფაილები თქვენს ოჯახთან, კოლეგებთან და მეგობრებთან.</p>
|
||||
<p xml:lang="lv">„NeoChat“ ir tērzēšanas programma, kas ļauj pilnvērtīgi izmantot „Matrix“ tīklu. Tā sniedz drošu veidu teksta ziņu, video un audio sūtīšanai ģimenes locekļiem, kolēģiem un draugiem.</p>
|
||||
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
|
||||
<p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p>
|
||||
<p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p>
|
||||
<p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p>
|
||||
<p xml:lang="sv">NeoChat är ett chattprogram som låter dig dra full nytta av Matrix-nätverket. Det ger dig ett säkert sätt att skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner.</p>
|
||||
<p xml:lang="tr">NeoChat, Matrix ağının tüm özelliklerini kullanan bir sohbet uygulamasıdır. Ailenize, arkadaşlarınıza ve iş arkadaşlarınıza metin iletileri, ses ve video dosyaları göndermenin kolay bir yolunu sunar.</p>
|
||||
<p xml:lang="uk">NeoChat є програмою для спілкування, за допомогою якої ви можете скористатися усіма перевагами мережі Matrix. За її допомогою ви можете безпечно надсилати текстові повідомлення, відео та звукові файли вашим родичам, колегам та друзям.</p>
|
||||
<p xml:lang="x-test">xxNeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.xx</p>
|
||||
@@ -101,6 +113,7 @@
|
||||
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. على هذا النحو يتم دعم كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP والخيوط وبعض جوانب التشفير من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار ، ولكن يبقى الهدف توفير الدعم النهائي للمواصفات بأكملها.</p>
|
||||
<p xml:lang="ca">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptatge d'extrem a extrem. Hi ha algunes altres omissions més petites a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu segueix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="de">NeoChat versucht eine vollumfängliche Anwendung für die Spezifikation von Matrix zu sein. Damit wird alles der aktuellen stabilen Spezifikation mit den erwähnenswerten Ausnahmen von VoIP, Diskussionsfäden und ein paar Teilen der Ende-zu-Ende-Verschlüsselung unterstützt. Zudem sind andere kleinere Auslassungen vorhanden, da sich die Matrixspezifikation ständig weiterentwickelt. Nichtsdestotrotz soll letztendlich die gesamte Spezifikation unterstützt werden.</p>
|
||||
<p xml:lang="en-GB">NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
|
||||
<p xml:lang="eo">NeoChat celas esti plene kapabla aplikaĵo por la Matrix-specifo. Kiel tia, ĉio en la nuna stabila specifo kun la rimarkindaj esceptoj de VoIP, fadenoj kaj kelkaj aspektoj de Fin-al-Fina Ĉifrado estas subtenataj. Estas kelkaj aliaj pli malgrandaj preterlasoj pro la fakto, ke la Matrix-speco konstante evoluas, sed la celo restas provizi finfine subtenon por la tuta specifaĵo.</p>
|
||||
<p xml:lang="es">NeoChat pretende ser una aplicación con todas las funciones para la especificación de Matrix. Como tal, admite todo en la especificación estable actual, con las notables excepciones de VoIP, subprocesos y algunas funciones de cifrado de extremo a extremo. Existen algunas omisiones menos importantes debido al hecho de que la especificación de Matrix está en constante evolución, pero el objetivo sigue siendo brindar compatibilidad final con toda la especificación.</p>
|
||||
@@ -108,8 +121,9 @@
|
||||
<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="he">NeoChat מתיימר להיות יישום עתיר יכולות לפי מפרט Matrix. כיוון שזה ייעודו, כל מה שבמפרט היציב עם חריגות משמעותיות כגון VoIP, שרשורים ועוד מגוון היבטים של הצפנה מקצה לקצה נתמכים גם הם. יש מספר השמטות קטן עקב העובדה שהמפרט של Matrix ממשיך להתפתח אך המטרה היא להמשיך לספק תמיכה בסופו של דבר לכל המפרט.</p>
|
||||
<p xml:lang="hu">A NeoChat célja, hogy a Matrix specifikációnak megfelelő teljes funkcionalitású alkalmazás legyen. Mint ilyen, a jelenlegi stabil specifikáció támogatott a VoIP, a szálak és a végpontok közötti titkosítás egyes elemeinek kivételével. Van még néhány kisebb hiányosság annak köszönhetően, hogy a Matrix specifikáció folyamatosan fejlődik, de végső cél a teljes specifikáció megvalósítása.</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="ia">NeoChat aspira a esser un application plenmente 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="ko">NeoChat은 Matrix 표준을 따르는 프로그램을 목표로 합니다. 현재 안정 버전의 표준에서 제공하는 기능의 대부분을 지원하며, VoIP, 스레드, 일부 종단간 암호화와 같은 기능은 아직 지원하지 않습니다. Matrix 표준은 계속하여 진화 중이기 때문에 일부 기능이 빠져 있을 수도 있지만 장기적으로는 전체 표준을 지원하는 것이 목표입니다.</p>
|
||||
@@ -118,6 +132,7 @@
|
||||
<p xml:lang="nn">NeoChat har som mål å støtta all funksjonalitet i Matrix-spesifikasjonen. Førebels er alt i den gjeldande stabile spesifikasjonen støtta, med unntak av VoIP, trådar og nokre delar av ende-til-kryptering. Det finst òg andre småting som ikkje er støtta, sidan Matrix-spesifikasjon er i stadig endring, men målet er altså støtte for alt.</p>
|
||||
<p xml:lang="pl">NeoChat w zamyśle ma być pełnowartościową aplikacją wg wytycznych Matriksa. Z tego powodu, wszystko, co jest obecnie w stabilnych wytycznych z pominięciem VoIP, wątków i niektórych części szyfrowania Użytkownik-do-Użytkownika są obecnie obsługiwane. Pominięto też kilka mniejszych rzeczy ze względu na ciągły rozwój wytycznych Matriksa, lecz celem nadal jest zapewnienie obsługi wszystkich wytycznych.</p>
|
||||
<p xml:lang="pt">O NeoChat pretende ser uma aplicação completa para a especificação do Matrix. Como tal, tudo o que existe na especificação estável actual, com as notáveis excepções do VoIP, tópicos e alguns aspectos da Encriptação Ponto-a-Ponto, são suportados. Existem mais algumas omissões, devido ao facto que a norma do Matrix está em constante evolução, mas o objectivo continua a ser oferecer o suporte eventual para a norma por inteiro.</p>
|
||||
<p xml:lang="ru">Целью создания NeoChat является полноценная реализация программы для спецификации Matrix. Как следствие, реализовано всё в текущей стабильной спецификации (за исключением голосовой интернет-связи, потоков и некоторых аспектов сквозного шифрования). Есть также несколько других незначительных пробелов, обусловленных постоянными изменениями спецификации Matrix. Тем не менее, стоит задача в итоге предоставить полную поддержку спецификации.</p>
|
||||
<p xml:lang="sl">Neochat cilja, da bi bila popolna aplikacija po specifikaciji Matrixa. Kot takšna vsebuje vse v trenutni stabilni specifikaciji z pomembnimi izjemami pri VoIP, nitih in nekaterih vidikov šifriranja od konca do konca. Obstaja nekaj drugih manjših opustitev zaradi dejstva, da se specifikacija Matrix nenehno razvija, vendar cilj ostaja zagotoviti morebitno podporo celotni specifikaciji.</p>
|
||||
<p xml:lang="sv">NeoChat har som mål att vara ett fullständigt program enligt Matrix-specifikationen. Som sådant stöds allt i den nuvarande stabila specifikationen, med de nämnvärda undantagen VoIP, trådar och några aspekter av kryptering hela vägen. Det finns några ytterligare utelämnanden på grund av att Matrix-specifikationen hela tiden utvecklas, men målet förblir att till slut erbjuda stöd för hela specifikationen.</p>
|
||||
<p xml:lang="tr">NeoChat, Matrix belirtimi için tam özellikli bir uygulama olmayı hedefler. Bu nedenle; VoIP, ileti zincirleri ve Uçtan Uca Şifreleme’nin bazı yönleri gibi dikkate değer istisnalar dışında var olan kararlı belirtimdeki her şey desteklenir. Matrix belirtiminin sürekli gelişmesi nedeniyle birkaç küçük eksiklik daha var; ancak amaç tüm belirtim için nihai destek sağlamak olmayı sürdürüyor.</p>
|
||||
@@ -128,6 +143,7 @@
|
||||
<p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يدعم نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p>
|
||||
<p xml:lang="ca">A causa de la naturalesa del desenvolupament de l'especificació de Matrix, el NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
|
||||
<p xml:lang="ca-valencia">A causa de la naturalea del desenvolupament de l'especificació de Matrix, NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
|
||||
<p xml:lang="de">Durch die Weiterentwicklung der Matrix-Spezifikation unterstützt auch NeoChat einige als noch instabil gekennzeichnete Funktionen. Derzeit sind das:</p>
|
||||
<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="eo">Pro la naturo de la Matrix-specifevoluo NeoChat ankaŭ subtenas multajn malstabilajn funkciojn. Nuntempe ĉi tiuj estas:</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>
|
||||
@@ -135,6 +151,7 @@
|
||||
<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="he">מטבע הדברים, הפיתוח של NeoChat תומך במגוון יכולות מפוקפקות כתלות בהתפתחות המפרט הטכני של Matrix. היכולות האלה הן:</p>
|
||||
<p xml:lang="hu">A Matrix specifikáció fejlesztésének jellegéből adódóan a NeoChat számos instabil funkciót is támogat. Jelenleg a következőket:</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>
|
||||
@@ -145,6 +162,7 @@
|
||||
<p xml:lang="nn">På grunn av måten Matrix-spesifikasjonen vert utvikla på, støttar NeoChat òg nokre uferdige funksjonar:</p>
|
||||
<p xml:lang="pl">Ze względu na sposób rozwoju Matriksa, NeoChat obsługuje także kilka niestabilnych możliwości. Obecnie są to:</p>
|
||||
<p xml:lang="pt">Devido à natureza do desenvolvimento da especificação do Matrix, o NeoChat também suporta diversas funcionalidades instáveis. De momento são:</p>
|
||||
<p xml:lang="ru">В силу природы разработки спецификации Matrix в NeoChat тоже предусмотрена поддержка многочисленных нестабильных возможностей. В текущей версии это следующие возможности:</p>
|
||||
<p xml:lang="sl">Zaradi narave razvoja specifikacije Matrixa NeoChat podpira tudi številne nestabilne zmožnosti. Trenutno so to:</p>
|
||||
<p xml:lang="sv">På grund av sättet Matrix-specifikationens utvecklas, stöder NeoChat också ett stor antal instabila funktioner. För närvarande är de:</p>
|
||||
<p xml:lang="ta">மேட்ரிக்ஸு நெறிமுறை வரையறுக்கப்படும் விதத்தின் காரணமாக, பல நிலையற்ற அம்சங்களையும் நியோச்சாட் ஆதரிக்கிறது. தற்போது ஆதரிக்கப்படுபவை:</p>
|
||||
@@ -164,6 +182,7 @@
|
||||
<li xml:lang="fi">Kyselyt – MSC3381</li>
|
||||
<li xml:lang="fr">Sondages - MSC3381</li>
|
||||
<li xml:lang="gl">Enquisas — MSC3381</li>
|
||||
<li xml:lang="he">סקרים - MSC3381</li>
|
||||
<li xml:lang="hu">Szavazások - MSC3381</li>
|
||||
<li xml:lang="ia">Inquestas - MSC3381</li>
|
||||
<li xml:lang="it">Sondaggi - MSC3381</li>
|
||||
@@ -192,6 +211,7 @@
|
||||
<li xml:lang="fi">Tarrapakkaukset – MSC2545</li>
|
||||
<li xml:lang="fr">Paquets d'auto-collants - MSC2545</li>
|
||||
<li xml:lang="gl">Paquetes de adhesivos — MSC2545</li>
|
||||
<li xml:lang="he">חבילות מדבקות - MSC2545</li>
|
||||
<li xml:lang="hu">Matricacsomagok - MSC2545</li>
|
||||
<li xml:lang="ia">Etiquetta gummate (sticker) -MSC2545</li>
|
||||
<li xml:lang="it">Pacchetti di adesivi - MSC2545</li>
|
||||
@@ -202,10 +222,11 @@
|
||||
<li xml:lang="nn">Klistremerke-pakkar – MSC2545</li>
|
||||
<li xml:lang="pl">Paczki naklejek - MSC2545</li>
|
||||
<li xml:lang="pt">Pacotes de Autocolantes - MSC2545</li>
|
||||
<li xml:lang="ru">Наборы стикеров — MSC2545</li>
|
||||
<li xml:lang="sl">Sticker Packs - MSC2545</li>
|
||||
<li xml:lang="sv">Sticker Packs - MSC2545</li>
|
||||
<li xml:lang="ta">ஒட்டி தொகுப்புகள் - MSC2545</li>
|
||||
<li xml:lang="tr">Yapışkan Paketleri — MSC2545</li>
|
||||
<li xml:lang="tr">Çıkartma Paketleri — MSC2545</li>
|
||||
<li xml:lang="uk">Пакунки наліпок - MSC2545</li>
|
||||
<li xml:lang="x-test">xxSticker Packs - MSC2545xx</li>
|
||||
<li xml:lang="zh-TW">貼圖包 - MSC2545</li>
|
||||
@@ -220,6 +241,7 @@
|
||||
<li xml:lang="fi">Sijaintitapahtumat – MSC3488</li>
|
||||
<li xml:lang="fr">Événements de lieu - MSC3488</li>
|
||||
<li xml:lang="gl">Localización de eventos — MSC3488</li>
|
||||
<li xml:lang="he">אירועי מקום - MSC3488</li>
|
||||
<li xml:lang="hu">Események helyadatai - MSC3488</li>
|
||||
<li xml:lang="ia">Eventos de Location - MSC3488</li>
|
||||
<li xml:lang="it">Località eventi - MSC3488</li>
|
||||
@@ -230,6 +252,7 @@
|
||||
<li xml:lang="nn">Posisjonshendingar – MSC3488</li>
|
||||
<li xml:lang="pl">Wydarzenia w miejscach - MSC3488</li>
|
||||
<li xml:lang="pt">Eventos com Localizações - MSC3488</li>
|
||||
<li xml:lang="ru">События местоположения — MSC3488</li>
|
||||
<li xml:lang="sl">Location Events - MSC3488</li>
|
||||
<li xml:lang="sv">Location Events - MSC3488</li>
|
||||
<li xml:lang="ta">இட நிகழ்வுகள் - MSC3488</li>
|
||||
@@ -275,12 +298,15 @@
|
||||
<caption xml:lang="ar">العرض الرئيسة مع قائمة الغرف والدردشات و معلومات الغرفة</caption>
|
||||
<caption xml:lang="ca">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||
<caption xml:lang="ca-valencia">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||
<caption xml:lang="de">Hauptansicht mit Raumliste, Unterhaltung und Raum-Informationen</caption>
|
||||
<caption xml:lang="en-GB">Main view with room list, chat, and room information</caption>
|
||||
<caption xml:lang="eo">Ĉefa vido kun ĉambra listo, babilejo kaj ĉambra informo</caption>
|
||||
<caption xml:lang="es">Vista principal con la lista de salas, chat e información de la sala</caption>
|
||||
<caption xml:lang="eu">Ikuspegi nagusia gela-zerrenda, berriketa, eta gelako informazioarekin</caption>
|
||||
<caption xml:lang="fi">Päänäkymä, jossa huoneluettelo, keskustelu ja huoneen tiedot</caption>
|
||||
<caption xml:lang="fr">Vue principale avec la liste des salons ainsi que des informations sur les salons et forums de discussions</caption>
|
||||
<caption xml:lang="gl">Vista principal coa lista de salas, a charla, e información da sala.</caption>
|
||||
<caption xml:lang="he">תצוגה ראשית עם רשימת חדרים, צ׳אט ופרטי חדר</caption>
|
||||
<caption xml:lang="hu">A fő nézet a szobalistával, csevegéssel és szobainformációkkal</caption>
|
||||
<caption xml:lang="ia">Vista principal con lista de sala, chat e information de sala</caption>
|
||||
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
|
||||
@@ -291,6 +317,7 @@
|
||||
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
|
||||
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
|
||||
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
|
||||
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
|
||||
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
|
||||
<caption xml:lang="sv">Huvudvy med rumslista, chatt, och rumsinformation</caption>
|
||||
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
|
||||
@@ -302,20 +329,28 @@
|
||||
<screenshot type="default">
|
||||
<image>https://cdn.kde.org/screenshots/neochat/spaces.png</image>
|
||||
<caption>Discover new communities with Matrix Spaces</caption>
|
||||
<caption xml:lang="ar">اكتشف مجتمعات جديدة مع فضاءات ماتركس</caption>
|
||||
<caption xml:lang="ca">Descobriu comunitats noves amb els espais de Matrix</caption>
|
||||
<caption xml:lang="ca-valencia">Descobriu comunitats noves amb els espais de Matrix</caption>
|
||||
<caption xml:lang="de">Neue Gemeinschaften mit den Umgebungen von Matrix erkunden</caption>
|
||||
<caption xml:lang="en-GB">Discover new communities with Matrix Spaces</caption>
|
||||
<caption xml:lang="eo">Malkovru novajn komunumojn per Matrix Spaces</caption>
|
||||
<caption xml:lang="es">Descubra nuevas comunidades con los espacios de Matrix</caption>
|
||||
<caption xml:lang="eu">Ezagutu komunitate berriak Matrixeko Tokiak erabiliz</caption>
|
||||
<caption xml:lang="fi">Löydä uusia yhteisöjä Matrix Spacesillä</caption>
|
||||
<caption xml:lang="fr">Découvrez de nouvelles communautés avec les espaces sous Matrix</caption>
|
||||
<caption xml:lang="gl">Descubra novas comunidades dos espazos de Matrix.</caption>
|
||||
<caption xml:lang="he">אפשר להיחשף לקהילות חדשות דרך Matrix Spaces</caption>
|
||||
<caption xml:lang="hu">Fedezzen fel új közösségeket a Matrix Terek segítségével</caption>
|
||||
<caption xml:lang="ia">Discoperi nove communitate con Matrix Spaces (Spatios de Matrix)</caption>
|
||||
<caption xml:lang="it">Scopri nuove comunità con Matrix Spaces</caption>
|
||||
<caption xml:lang="ka">აღმოაჩინეთ ახალი საზოგადოებები Matrix Spaces-თან ერთად</caption>
|
||||
<caption xml:lang="lv">Atklājiet jaunas kopienas ar „Matrix“ telpām</caption>
|
||||
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
|
||||
<caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption>
|
||||
<caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption>
|
||||
<caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption>
|
||||
<caption xml:lang="sv">Upptäck nya gemenskaper med Matrix Spaces</caption>
|
||||
<caption xml:lang="tr">Matrix Alanlar ile yeni topluluklar keşfedin</caption>
|
||||
<caption xml:lang="uk">Пошук нових спільнот за допомогою Matrix Spaces</caption>
|
||||
<caption xml:lang="x-test">xxDiscover new communities with Matrix Spacesxx</caption>
|
||||
@@ -334,12 +369,15 @@
|
||||
<caption xml:lang="ar">العرض الرئيسة مع قائمة الغرف والدردشات و معلومات الغرفة</caption>
|
||||
<caption xml:lang="ca">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||
<caption xml:lang="ca-valencia">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||
<caption xml:lang="de">Hauptansicht mit Raumliste, Unterhaltung und Raum-Informationen</caption>
|
||||
<caption xml:lang="en-GB">Main view with room list, chat, and room information</caption>
|
||||
<caption xml:lang="eo">Ĉefa vido kun ĉambra listo, babilejo kaj ĉambra informo</caption>
|
||||
<caption xml:lang="es">Vista principal con la lista de salas, chat e información de la sala</caption>
|
||||
<caption xml:lang="eu">Ikuspegi nagusia gela-zerrenda, berriketa, eta gelako informazioarekin</caption>
|
||||
<caption xml:lang="fi">Päänäkymä, jossa huoneluettelo, keskustelu ja huoneen tiedot</caption>
|
||||
<caption xml:lang="fr">Vue principale avec la liste des salons ainsi que des informations sur les salons et forums de discussions</caption>
|
||||
<caption xml:lang="gl">Vista principal coa lista de salas, a charla, e información da sala.</caption>
|
||||
<caption xml:lang="he">תצוגה ראשית עם רשימת חדרים, צ׳אט ופרטי חדר</caption>
|
||||
<caption xml:lang="hu">A fő nézet a szobalistával, csevegéssel és szobainformációkkal</caption>
|
||||
<caption xml:lang="ia">Vista principal con lista de sala, chat e information de sala</caption>
|
||||
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
|
||||
@@ -350,6 +388,7 @@
|
||||
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
|
||||
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
|
||||
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
|
||||
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
|
||||
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
|
||||
<caption xml:lang="sv">Huvudvy med rumslista, chatt, och rumsinformation</caption>
|
||||
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
|
||||
@@ -365,12 +404,15 @@
|
||||
<caption xml:lang="ca">Pantalla d'inici de sessió</caption>
|
||||
<caption xml:lang="ca-valencia">Pantalla d'inici de sessió</caption>
|
||||
<caption xml:lang="cs">Přihlašovací obrazovka</caption>
|
||||
<caption xml:lang="de">Anmeldebildschirm</caption>
|
||||
<caption xml:lang="en-GB">Login screen</caption>
|
||||
<caption xml:lang="eo">Ensaluta ekrano</caption>
|
||||
<caption xml:lang="es">Pantalla de inicio de sesión</caption>
|
||||
<caption xml:lang="eu">Saio-hasteko pantaila</caption>
|
||||
<caption xml:lang="fi">Kirjautumisnäkymä</caption>
|
||||
<caption xml:lang="fr">Écran de connexion</caption>
|
||||
<caption xml:lang="gl">Pantalla de identificación.</caption>
|
||||
<caption xml:lang="he">מסך כניסה</caption>
|
||||
<caption xml:lang="hu">Bejelentkező képernyő</caption>
|
||||
<caption xml:lang="ia">Schermo de accesso</caption>
|
||||
<caption xml:lang="it">Schermata di accesso</caption>
|
||||
@@ -381,6 +423,7 @@
|
||||
<caption xml:lang="nn">Innloggingsbilete</caption>
|
||||
<caption xml:lang="pl">Ekran logowania</caption>
|
||||
<caption xml:lang="pt">Ecrã de autenticação</caption>
|
||||
<caption xml:lang="ru">Окно входа</caption>
|
||||
<caption xml:lang="sl">Prijavni zaslon</caption>
|
||||
<caption xml:lang="sv">Inloggningsfönster</caption>
|
||||
<caption xml:lang="ta">நுழைவுத் திரை</caption>
|
||||
@@ -394,6 +437,13 @@
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release version="24.08.2" date="2024-10-10"/>
|
||||
<release version="24.08.1" date="2024-09-12"/>
|
||||
<release version="24.08.0" date="2024-08-22"/>
|
||||
<release version="24.05.2" date="2024-07-04"/>
|
||||
<release version="24.05.1" date="2024-06-13"/>
|
||||
<release version="24.05.0" date="2024-05-23"/>
|
||||
<release version="24.02.2" date="2024-04-11"/>
|
||||
<release version="24.02.1" date="2024-03-21"/>
|
||||
<release version="24.02.0" date="2024-02-28">
|
||||
<url>https://kde.org/announcements/megarelease/6/#neochat</url>
|
||||
|
||||
@@ -18,6 +18,7 @@ Name[eu]=NeoChat
|
||||
Name[fi]=NeoChat
|
||||
Name[fr]=NeoChat
|
||||
Name[gl]=NeoChat
|
||||
Name[he]=NeoChat
|
||||
Name[hu]=NeoChat
|
||||
Name[ia]=Neochat
|
||||
Name[id]=NeoChat
|
||||
@@ -59,6 +60,7 @@ GenericName[eu]=Matrix bezeroa
|
||||
GenericName[fi]=Matrix-asiakas
|
||||
GenericName[fr]=Client « Matrix »
|
||||
GenericName[gl]=Cliente de Matrix
|
||||
GenericName[he]=לקוח Matrix
|
||||
GenericName[hu]=Matrix kliens
|
||||
GenericName[ia]=Cliente de Matrice
|
||||
GenericName[id]=Klien Matrix
|
||||
@@ -99,6 +101,7 @@ 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[he]=לקוח לפרוטוקול Matrix
|
||||
Comment[hu]=Kliens a Matrix protokollhoz
|
||||
Comment[ia]=Cliente per le protocollo de Matrix
|
||||
Comment[id]=Klien untuk protokol Matrix
|
||||
|
||||
3865
po/ar/neochat.po
3865
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
2694
po/ast/neochat.po
2694
po/ast/neochat.po
File diff suppressed because it is too large
Load Diff
3194
po/az/neochat.po
3194
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -77,7 +77,7 @@ SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
></term>
|
||||
<listitem>
|
||||
<para
|
||||
>L'URI de Matrix per a un usuari o una sala. P. ex. matrix:u/usuari:exemple.org o matrix:r/root:exemple.org. Això farà que el NeoChat intenti obrir la sala o conversa indicada. </para>
|
||||
>L'URI de Matrix per a un usuari o una sala. P. ex. matrix:u/usuari:example.org o matrix:r/root:example.org. Això farà que el NeoChat intenti obrir la sala o conversa indicada. </para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
2875
po/ca/neochat.po
2875
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2901
po/cs/neochat.po
2901
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
2932
po/da/neochat.po
2932
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
3889
po/de/neochat.po
3889
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
3184
po/el/neochat.po
3184
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
3936
po/en_GB/neochat.po
3936
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
2883
po/eo/neochat.po
2883
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
2960
po/es/neochat.po
2960
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
3026
po/eu/neochat.po
3026
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
3253
po/fi/neochat.po
3253
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
2963
po/fr/neochat.po
2963
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
5478
po/gl/neochat.po
Normal file
5478
po/gl/neochat.po
Normal file
File diff suppressed because it is too large
Load Diff
3113
po/hu/neochat.po
3113
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
2968
po/ia/neochat.po
2968
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
3175
po/id/neochat.po
3175
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
3051
po/ie/neochat.po
3051
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
2964
po/it/neochat.po
2964
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
2692
po/ja/neochat.po
2692
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
2962
po/ka/neochat.po
2962
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
3117
po/ko/neochat.po
3117
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
2698
po/lt/neochat.po
2698
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
3102
po/lv/neochat.po
3102
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
2963
po/nl/neochat.po
2963
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
3656
po/nn/neochat.po
3656
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
3094
po/pa/neochat.po
3094
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
2990
po/pl/neochat.po
2990
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
3161
po/pt/neochat.po
3161
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
3237
po/pt_BR/neochat.po
3237
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
3221
po/ru/neochat.po
3221
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
3115
po/sk/neochat.po
3115
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
2938
po/sl/neochat.po
2938
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
3708
po/sv/neochat.po
3708
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
2978
po/ta/neochat.po
2978
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
2896
po/tok/neochat.po
2896
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
2953
po/tr/neochat.po
2953
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
2980
po/uk/neochat.po
2980
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
3016
po/zh_CN/neochat.po
3016
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
2985
po/zh_TW/neochat.po
2985
po/zh_TW/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -20,8 +20,6 @@ add_library(neochat STATIC
|
||||
models/customemojimodel.h
|
||||
clipboard.cpp
|
||||
clipboard.h
|
||||
matriximageprovider.cpp
|
||||
matriximageprovider.h
|
||||
models/messageeventmodel.cpp
|
||||
models/messageeventmodel.h
|
||||
models/messagefiltermodel.cpp
|
||||
@@ -136,6 +134,8 @@ add_library(neochat STATIC
|
||||
jobs/neochatdeletedevicejob.h
|
||||
jobs/neochatchangepasswordjob.cpp
|
||||
jobs/neochatchangepasswordjob.h
|
||||
jobs/neochatgetcommonroomsjob.cpp
|
||||
jobs/neochatgetcommonroomsjob.h
|
||||
mediasizehelper.cpp
|
||||
mediasizehelper.h
|
||||
eventhandler.cpp
|
||||
@@ -158,7 +158,6 @@ add_library(neochat STATIC
|
||||
models/linemodel.cpp
|
||||
models/linemodel.h
|
||||
events/locationbeaconevent.h
|
||||
events/serveraclevent.h
|
||||
events/widgetevent.h
|
||||
enums/messagecomponenttype.h
|
||||
models/messagecontentmodel.cpp
|
||||
@@ -174,16 +173,35 @@ add_library(neochat STATIC
|
||||
sharehandler.h
|
||||
models/roomtreeitem.cpp
|
||||
models/roomtreeitem.h
|
||||
foreigntypes.h
|
||||
models/threepidmodel.cpp
|
||||
models/threepidmodel.h
|
||||
threepidaddhelper.cpp
|
||||
threepidaddhelper.h
|
||||
jobs/neochatadd3pidjob.cpp
|
||||
jobs/neochatadd3pidjob.h
|
||||
identityserverhelper.cpp
|
||||
identityserverhelper.h
|
||||
enums/powerlevel.cpp
|
||||
enums/powerlevel.h
|
||||
models/permissionsmodel.cpp
|
||||
models/permissionsmodel.h
|
||||
threepidbindhelper.cpp
|
||||
threepidbindhelper.h
|
||||
models/readmarkermodel.cpp
|
||||
models/readmarkermodel.h
|
||||
neochatroommember.cpp
|
||||
neochatroommember.h
|
||||
)
|
||||
|
||||
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||
QT_QML_SINGLETON_TYPE TRUE
|
||||
)
|
||||
|
||||
qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat
|
||||
QML_FILES
|
||||
qml/main.qml
|
||||
qml/Main.qml
|
||||
qml/AccountMenu.qml
|
||||
qml/ExploreComponent.qml
|
||||
qml/ExploreComponentMobile.qml
|
||||
@@ -200,35 +218,14 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/ExplorerDelegate.qml
|
||||
qml/InviteUserPage.qml
|
||||
qml/ImageEditorPage.qml
|
||||
qml/WelcomePage.qml
|
||||
qml/NeochatMaximizeComponent.qml
|
||||
qml/FancyEffectsContainer.qml
|
||||
qml/TypingPane.qml
|
||||
qml/QuickSwitcher.qml
|
||||
qml/HoverActions.qml
|
||||
qml/ChatBar.qml
|
||||
qml/AttachmentPane.qml
|
||||
qml/ReplyPane.qml
|
||||
qml/CompletionMenu.qml
|
||||
qml/PieProgressBar.qml
|
||||
qml/QuickFormatBar.qml
|
||||
qml/EmojiPicker.qml
|
||||
qml/LoginStep.qml
|
||||
qml/Login.qml
|
||||
qml/Homeserver.qml
|
||||
qml/Username.qml
|
||||
qml/RegisterPassword.qml
|
||||
qml/Captcha.qml
|
||||
qml/Terms.qml
|
||||
qml/Email.qml
|
||||
qml/Password.qml
|
||||
qml/LoginRegister.qml
|
||||
qml/Loading.qml
|
||||
qml/LoginMethod.qml
|
||||
qml/Sso.qml
|
||||
qml/UserDetailDialog.qml
|
||||
qml/CreateRoomDialog.qml
|
||||
qml/EmojiDialog.qml
|
||||
qml/OpenFileDialog.qml
|
||||
qml/KeyVerificationDialog.qml
|
||||
qml/ConfirmLogoutDialog.qml
|
||||
@@ -248,9 +245,6 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/ConfirmEncryptionDialog.qml
|
||||
qml/RemoveSheet.qml
|
||||
qml/BanSheet.qml
|
||||
qml/EmojiTonesPicker.qml
|
||||
qml/EmojiDelegate.qml
|
||||
qml/EmojiGrid.qml
|
||||
qml/RoomSearchPage.qml
|
||||
qml/LocationChooser.qml
|
||||
qml/TimelineView.qml
|
||||
@@ -274,7 +268,6 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/SelectParentDialog.qml
|
||||
qml/QrCodeMaximizeComponent.qml
|
||||
qml/SelectSpacesDialog.qml
|
||||
qml/AttachDialog.qml
|
||||
qml/NotificationsView.qml
|
||||
qml/SearchPage.qml
|
||||
qml/ServerComboBox.qml
|
||||
@@ -291,14 +284,26 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/AccountSwitchDialog.qml
|
||||
qml/ConfirmLeaveDialog.qml
|
||||
qml/CodeMaximizeComponent.qml
|
||||
RESOURCES
|
||||
qml/confetti.png
|
||||
qml/glowdot.png
|
||||
qml/EditStateDialog.qml
|
||||
qml/ConsentDialog.qml
|
||||
qml/AskDirectChatConfirmation.qml
|
||||
qml/HoverLinkIndicator.qml
|
||||
DEPENDENCIES
|
||||
QtCore
|
||||
QtQuick
|
||||
IMPORTS
|
||||
org.kde.neochat.timeline
|
||||
org.kde.neochat.settings
|
||||
org.kde.neochat.devtools
|
||||
org.kde.neochat.login
|
||||
org.kde.neochat.chatbar
|
||||
)
|
||||
|
||||
add_subdirectory(settings)
|
||||
add_subdirectory(timeline)
|
||||
add_subdirectory(devtools)
|
||||
add_subdirectory(login)
|
||||
add_subdirectory(chatbar)
|
||||
|
||||
if(UNIX)
|
||||
qt_target_qml_sources(neochat QML_FILES qml/ShareAction.qml)
|
||||
@@ -381,16 +386,18 @@ endif()
|
||||
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_RUNNER)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_X11)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_X11=1)
|
||||
target_sources(neochat PRIVATE runner.cpp)
|
||||
|
||||
if (TARGET KUnifiedPush)
|
||||
target_sources(neochat PRIVATE fakerunner.cpp)
|
||||
endif()
|
||||
else()
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_X11=0)
|
||||
endif()
|
||||
|
||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
|
||||
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin devtoolsplugin)
|
||||
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin devtoolsplugin loginplugin chatbarplugin)
|
||||
target_link_libraries(neochat PUBLIC
|
||||
Qt::Core
|
||||
Qt::Quick
|
||||
@@ -414,6 +421,10 @@ target_link_libraries(neochat PUBLIC
|
||||
QCoro::Network
|
||||
)
|
||||
|
||||
if (TARGET KF6::Crash)
|
||||
target_link_libraries(neochat PUBLIC KF6::Crash)
|
||||
endif()
|
||||
|
||||
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||
|
||||
if(NEOCHAT_FLATPAK)
|
||||
|
||||
@@ -91,7 +91,7 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, Cha
|
||||
|
||||
for (auto it = m_room->messageEvents().crbegin(); it != m_room->messageEvents().crend(); it++) {
|
||||
if (const auto event = eventCast<const RoomMessageEvent>(&**it)) {
|
||||
if (event->senderId() == m_room->localUser()->id() && event->hasTextContent()) {
|
||||
if (event->senderId() == m_room->localMember().id() && event->hasTextContent()) {
|
||||
QString originalString;
|
||||
if (event->content()) {
|
||||
originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content())->body;
|
||||
|
||||
@@ -13,13 +13,15 @@ import org.kde.neochat
|
||||
QQC2.Popup {
|
||||
id: root
|
||||
|
||||
padding: 16
|
||||
padding: Kirigami.Units.largeSpacing
|
||||
|
||||
signal chosen(string path)
|
||||
|
||||
contentItem: RowLayout {
|
||||
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
QQC2.ToolButton {
|
||||
Layout.preferredWidth: 160
|
||||
Layout.fillHeight: true
|
||||
|
||||
icon.name: 'mail-attachment'
|
||||
@@ -28,7 +30,7 @@ QQC2.Popup {
|
||||
|
||||
onClicked: {
|
||||
root.close();
|
||||
var fileDialog = openFileDialog.createObject(QQC2.ApplicationWindow.overlay);
|
||||
var fileDialog = openFileDialog.createObject(QQC2.Overlay.overlay);
|
||||
fileDialog.chosen.connect(path => root.chosen(path));
|
||||
fileDialog.open();
|
||||
}
|
||||
@@ -37,11 +39,8 @@ QQC2.Popup {
|
||||
Kirigami.Separator {}
|
||||
|
||||
QQC2.ToolButton {
|
||||
Layout.preferredWidth: 160
|
||||
Layout.fillHeight: true
|
||||
|
||||
padding: 16
|
||||
|
||||
icon.name: 'insert-image'
|
||||
text: i18n("Clipboard image")
|
||||
onClicked: {
|
||||
19
src/chatbar/CMakeLists.txt
Normal file
19
src/chatbar/CMakeLists.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
# SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
qt_add_library(chatbar STATIC)
|
||||
ecm_add_qml_module(chatbar GENERATE_PLUGIN_SOURCE
|
||||
URI org.kde.neochat.chatbar
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/chatbar
|
||||
QML_FILES
|
||||
AttachDialog.qml
|
||||
ChatBar.qml
|
||||
CompletionMenu.qml
|
||||
EmojiDelegate.qml
|
||||
EmojiGrid.qml
|
||||
ReplyPane.qml
|
||||
PieProgressBar.qml
|
||||
EmojiPicker.qml
|
||||
EmojiDialog.qml
|
||||
EmojiTonesPicker.qml
|
||||
)
|
||||
@@ -9,7 +9,6 @@ import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.config
|
||||
|
||||
/**
|
||||
* @brief A component for typing and sending chat messages.
|
||||
@@ -116,7 +115,7 @@ QQC2.Control {
|
||||
displayHint: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onTriggered: {
|
||||
locationChooser.createObject(QQC2.ApplicationWindow.overlay, {
|
||||
locationChooser.createObject(QQC2.Overlay.overlay, {
|
||||
room: root.currentRoom
|
||||
}).open();
|
||||
}
|
||||
@@ -279,6 +278,8 @@ QQC2.Control {
|
||||
Keys.onTabPressed: {
|
||||
if (completionMenu.visible) {
|
||||
completionMenu.complete();
|
||||
} else {
|
||||
contextDrawer.handle.children[0].forceActiveFocus()
|
||||
}
|
||||
}
|
||||
Keys.onPressed: event => {
|
||||
@@ -363,7 +364,7 @@ QQC2.Control {
|
||||
ReplyPane {
|
||||
userName: _private.chatBarCache.relationUser.displayName
|
||||
userColor: _private.chatBarCache.relationUser.color
|
||||
userAvatar: _private.chatBarCache.relationUser.avatarSource
|
||||
userAvatar: _private.chatBarCache.relationUser.avatarUrl
|
||||
text: _private.chatBarCache.relationMessage
|
||||
|
||||
onCancel: {
|
||||
@@ -24,7 +24,7 @@ QQC2.ItemDelegate {
|
||||
contentItem: Item {
|
||||
Kirigami.Heading {
|
||||
anchors.fill: parent
|
||||
visible: !root.emoji.startsWith("image") && !root.isImage
|
||||
visible: !root.emoji.startsWith("mxc") && !root.isImage
|
||||
text: root.emoji
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
@@ -41,17 +41,17 @@ QQC2.ItemDelegate {
|
||||
}
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
visible: root.emoji.startsWith("image") || root.isImage
|
||||
visible: root.emoji.startsWith("mxc") || root.isImage
|
||||
source: visible ? root.emoji : ""
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: root.checked ? Kirigami.Theme.highlightColor : Kirigami.Theme.backgroundColor
|
||||
radius: Kirigami.Units.smallSpacing
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
|
||||
Rectangle {
|
||||
radius: Kirigami.Units.smallSpacing
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
anchors.fill: parent
|
||||
color: Kirigami.Theme.highlightColor
|
||||
opacity: root.hovered && !root.pressed ? 0.2 : 0
|
||||
@@ -38,18 +38,22 @@ QQC2.Popup {
|
||||
}
|
||||
|
||||
background: Kirigami.ShadowedRectangle {
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
radius: Kirigami.Units.mediumSpacing
|
||||
shadow {
|
||||
size: Kirigami.Units.largeSpacing
|
||||
color: Qt.rgba(0.0, 0.0, 0.0, 0.3)
|
||||
yOffset: 2
|
||||
}
|
||||
|
||||
border {
|
||||
color: Kirigami.ColorUtils.tintWithAlpha(color, Kirigami.Theme.textColor, 0.15)
|
||||
width: 2
|
||||
width: 1
|
||||
color: Kirigami.ColorUtils.linearInterpolation(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, Kirigami.Theme.frameContrast)
|
||||
}
|
||||
|
||||
shadow {
|
||||
size: Kirigami.Units.gridUnit
|
||||
yOffset: 0
|
||||
color: Qt.rgba(0, 0, 0, 0.2)
|
||||
}
|
||||
|
||||
Kirigami.Theme.inherit: false
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
}
|
||||
|
||||
modal: true
|
||||
@@ -30,7 +30,7 @@ QQC2.Popup {
|
||||
onOpened: x = Math.min(parent.mapFromGlobal(QQC2.Overlay.overlay.width - root.width, 0).x, -(width - parent.width) / 2)
|
||||
background: Kirigami.ShadowedRectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
radius: Kirigami.Units.mediumSpacing
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
shadow {
|
||||
size: Kirigami.Units.largeSpacing
|
||||
color: Qt.rgba(0.0, 0.0, 0.0, 0.3)
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "chatbarcache.h"
|
||||
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
#include "chatdocumenthandler.h"
|
||||
#include "eventhandler.h"
|
||||
#include "neochatroom.h"
|
||||
@@ -84,7 +86,7 @@ void ChatBarCache::setEditId(const QString &editId)
|
||||
Q_EMIT attachmentPathChanged();
|
||||
}
|
||||
|
||||
QVariantMap ChatBarCache::relationUser() const
|
||||
Quotient::RoomMember ChatBarCache::relationUser() const
|
||||
{
|
||||
if (parent() == nullptr) {
|
||||
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
@@ -96,9 +98,9 @@ QVariantMap ChatBarCache::relationUser() const
|
||||
return {};
|
||||
}
|
||||
if (m_relationId.isEmpty()) {
|
||||
return room->getUser(nullptr);
|
||||
return room->member(QString());
|
||||
}
|
||||
return room->getUser(room->user((*room->findInTimeline(m_relationId))->senderId()));
|
||||
return room->member((*room->findInTimeline(m_relationId))->senderId());
|
||||
}
|
||||
|
||||
QString ChatBarCache::relationMessage() const
|
||||
@@ -118,7 +120,11 @@ QString ChatBarCache::relationMessage() const
|
||||
|
||||
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
|
||||
EventHandler eventhandler(room, &**event);
|
||||
return eventhandler.getMarkdownBody();
|
||||
if (isEditing()) {
|
||||
return eventhandler.getMarkdownBody();
|
||||
} else {
|
||||
return eventhandler.getMarkdownBody().toHtmlEscaped();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
|
||||
class ChatDocumentHandler;
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class RoomMember;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines a user mention in the current chat or edit text.
|
||||
*/
|
||||
@@ -88,26 +94,13 @@ class ChatBarCache : public QObject
|
||||
Q_PROPERTY(QString editId READ editId WRITE setEditId NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Get the user for the message being replied to.
|
||||
* @brief Get the RoomMember object for the message being replied to.
|
||||
*
|
||||
* This is different to getting a Quotient::User object
|
||||
* as neither of those can provide details like the displayName or avatarMediaId
|
||||
* without the room context as these can vary from room to room.
|
||||
* Returns an empty RoomMember if not replying to a message.
|
||||
*
|
||||
* Returns an empty user if not replying to a message.
|
||||
*
|
||||
* The user QVariantMap has the following properties:
|
||||
* - isLocalUser - Whether the user is the local user.
|
||||
* - id - The matrix ID of the user.
|
||||
* - displayName - Display name in the context of this room.
|
||||
* - avatarSource - The mxc URL for the user's avatar in the current room.
|
||||
* - avatarMediaId - Avatar id in the context of this room.
|
||||
* - color - Color for the user.
|
||||
* - object - The Quotient::User object for the user.
|
||||
*
|
||||
* @sa getUser, Quotient::User
|
||||
* @sa Quotient::RoomMember
|
||||
*/
|
||||
Q_PROPERTY(QVariantMap relationUser READ relationUser NOTIFY relationIdChanged)
|
||||
Q_PROPERTY(Quotient::RoomMember relationUser READ relationUser NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The content of the related message.
|
||||
@@ -161,7 +154,7 @@ public:
|
||||
QString editId() const;
|
||||
void setEditId(const QString &editId);
|
||||
|
||||
QVariantMap relationUser() const;
|
||||
Quotient::RoomMember relationUser() const;
|
||||
|
||||
QString relationMessage() const;
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ class ChatDocumentHandler : public QObject
|
||||
Q_PROPERTY(CompletionModel *completionModel READ completionModel CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The current room that the the text document is being handled for.
|
||||
* @brief The current room that the text document is being handled for.
|
||||
*/
|
||||
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||
|
||||
|
||||
@@ -9,28 +9,20 @@
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QNetworkProxy>
|
||||
#include <QQuickTextDocument>
|
||||
#include <QQuickWindow>
|
||||
#include <QStandardPaths>
|
||||
#include <QStringBuilder>
|
||||
#include <QTimer>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/csapi/logout.h>
|
||||
#include <Quotient/csapi/notifications.h>
|
||||
#include <Quotient/eventstats.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
#include <Quotient/settings.h>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "notificationsmanager.h"
|
||||
#include "proxycontroller.h"
|
||||
#include "roommanager.h"
|
||||
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
#include "trayicon.h"
|
||||
@@ -46,6 +38,10 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
#include <kunifiedpush/connector.h>
|
||||
#endif
|
||||
|
||||
bool testMode = false;
|
||||
|
||||
using namespace Quotient;
|
||||
@@ -68,7 +64,11 @@ Controller::Controller(QObject *parent)
|
||||
});
|
||||
} else {
|
||||
auto c = new NeoChatConnection(this);
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
c->assumeIdentity(QStringLiteral("@user:localhost:1234"), QStringLiteral("device_1234"), QStringLiteral("token_1234"));
|
||||
#else
|
||||
c->assumeIdentity(QStringLiteral("@user:localhost:1234"), QStringLiteral("token_1234"));
|
||||
#endif
|
||||
connect(c, &Connection::connected, this, [c, this]() {
|
||||
m_accountRegistry.add(c);
|
||||
c->syncLoop();
|
||||
@@ -107,14 +107,16 @@ Controller::Controller(QObject *parent)
|
||||
connect(&m_accountRegistry, &AccountRegistry::accountCountChanged, this, [this]() {
|
||||
if (m_accountRegistry.size() > oldAccountCount) {
|
||||
auto connection = dynamic_cast<NeoChatConnection *>(m_accountRegistry.accounts()[m_accountRegistry.size() - 1]);
|
||||
connect(connection, &NeoChatConnection::syncDone, this, [connection]() {
|
||||
NotificationsManager::instance().handleNotifications(connection);
|
||||
});
|
||||
connectSingleShot(connection, &NeoChatConnection::syncDone, this, [this, connection] {
|
||||
if (!m_endpoint.isEmpty()) {
|
||||
connection->setupPushNotifications(m_endpoint);
|
||||
}
|
||||
});
|
||||
connect(
|
||||
connection,
|
||||
&NeoChatConnection::syncDone,
|
||||
this,
|
||||
[this, connection] {
|
||||
if (!m_endpoint.isEmpty()) {
|
||||
connection->setupPushNotifications(m_endpoint);
|
||||
}
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
}
|
||||
oldAccountCount = m_accountRegistry.size();
|
||||
});
|
||||
@@ -157,7 +159,7 @@ void Controller::addConnection(NeoChatConnection *c)
|
||||
});
|
||||
connect(c, &NeoChatConnection::loggedOut, this, [this, c] {
|
||||
if (accounts().count() > 1) {
|
||||
// Only set the connection if the the account being logged out is currently active
|
||||
// Only set the connection if the account being logged out is currently active
|
||||
if (c == activeConnection()) {
|
||||
setActiveConnection(dynamic_cast<NeoChatConnection *>(accounts().accounts()[0]));
|
||||
}
|
||||
@@ -190,7 +192,7 @@ void Controller::invokeLogin()
|
||||
m_accountsLoading += accountId;
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
if (!account.homeserver().isEmpty()) {
|
||||
auto accessTokenLoadingJob = loadAccessTokenFromKeyChain(account);
|
||||
auto accessTokenLoadingJob = loadAccessTokenFromKeyChain(account.userId());
|
||||
connect(accessTokenLoadingJob, &QKeychain::Job::finished, this, [accountId, this, accessTokenLoadingJob](QKeychain::Job *) {
|
||||
AccountSettings account{accountId};
|
||||
QString accessToken;
|
||||
@@ -204,25 +206,43 @@ void Controller::invokeLogin()
|
||||
m_connectionsLoading[accountId] = connection;
|
||||
connect(connection, &NeoChatConnection::connected, this, [this, connection, accountId] {
|
||||
connection->loadState();
|
||||
addConnection(connection);
|
||||
m_accountsLoading.removeAll(connection->userId());
|
||||
m_connectionsLoading.remove(accountId);
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
if (connection->allRooms().size() == 0 || connection->allRooms()[0]->currentState().get<RoomCreateEvent>()) {
|
||||
addConnection(connection);
|
||||
m_accountsLoading.removeAll(connection->userId());
|
||||
m_connectionsLoading.remove(accountId);
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
} else {
|
||||
connect(
|
||||
connection->allRooms()[0],
|
||||
&Room::baseStateLoaded,
|
||||
this,
|
||||
[this, connection, accountId]() {
|
||||
addConnection(connection);
|
||||
m_accountsLoading.removeAll(connection->userId());
|
||||
m_connectionsLoading.remove(accountId);
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
}
|
||||
});
|
||||
connect(connection, &NeoChatConnection::networkError, this, [this](const QString &error, const QString &, int, int) {
|
||||
Q_EMIT errorOccured(i18n("Network Error: %1", error), {});
|
||||
});
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
connection->assumeIdentity(account.userId(), account.deviceId(), accessToken);
|
||||
#else
|
||||
connection->assumeIdentity(account.userId(), accessToken);
|
||||
#endif
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const AccountSettings &account)
|
||||
QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const QString &userId)
|
||||
{
|
||||
qDebug() << "Reading access token from the keychain for" << account.userId();
|
||||
qDebug() << "Reading access token from the keychain for" << userId;
|
||||
auto job = new QKeychain::ReadPasswordJob(qAppName(), this);
|
||||
job->setKey(account.userId());
|
||||
job->setKey(userId);
|
||||
|
||||
// Handling of errors
|
||||
connect(job, &QKeychain::Job::finished, this, [this, job]() {
|
||||
@@ -253,23 +273,19 @@ QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const Accoun
|
||||
return job;
|
||||
}
|
||||
|
||||
bool Controller::saveAccessTokenToKeyChain(const AccountSettings &account, const QByteArray &accessToken)
|
||||
void Controller::saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken)
|
||||
{
|
||||
qDebug() << "Save the access token to the keychain for " << account.userId();
|
||||
QKeychain::WritePasswordJob job(qAppName());
|
||||
job.setAutoDelete(false);
|
||||
job.setKey(account.userId());
|
||||
job.setBinaryData(accessToken);
|
||||
QEventLoop loop;
|
||||
QKeychain::WritePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
|
||||
job.start();
|
||||
loop.exec();
|
||||
|
||||
if (job.error()) {
|
||||
qWarning() << "Could not save access token to the keychain: " << qPrintable(job.errorString());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
qDebug() << "Save the access token to the keychain for " << userId;
|
||||
auto job = new QKeychain::WritePasswordJob(qAppName());
|
||||
job->setAutoDelete(true);
|
||||
job->setKey(userId);
|
||||
job->setBinaryData(accessToken);
|
||||
connect(job, &QKeychain::WritePasswordJob::finished, this, [job]() {
|
||||
if (job->error()) {
|
||||
qWarning() << "Could not save access token to the keychain: " << qPrintable(job->errorString());
|
||||
}
|
||||
});
|
||||
job->start();
|
||||
}
|
||||
|
||||
bool Controller::supportSystemTray() const
|
||||
@@ -394,8 +410,6 @@ QString Controller::loadFileContent(const QString &path) const
|
||||
return QString::fromLatin1(file.readAll());
|
||||
}
|
||||
|
||||
#include "moc_controller.cpp"
|
||||
|
||||
void Controller::setTestMode(bool test)
|
||||
{
|
||||
testMode = test;
|
||||
@@ -411,11 +425,13 @@ void Controller::removeConnection(const QString &userId)
|
||||
}
|
||||
}
|
||||
|
||||
bool Controller::ssssSupported() const
|
||||
bool Controller::csSupported() const
|
||||
{
|
||||
#if __has_include("Quotient/e2ee/sssshandler.h")
|
||||
#if Quotient_VERSION_MINOR > 9
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#include "moc_controller.cpp"
|
||||
|
||||
@@ -5,15 +5,9 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickItem>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/settings.h>
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
#include <kunifiedpush/connector.h>
|
||||
#endif
|
||||
|
||||
class TrayIcon;
|
||||
class QQuickTextDocument;
|
||||
@@ -56,9 +50,19 @@ class Controller : public QObject
|
||||
|
||||
Q_PROPERTY(QStringList accountsLoading MEMBER m_accountsLoading NOTIFY accountsLoadingChanged)
|
||||
|
||||
Q_PROPERTY(bool ssssSupported READ ssssSupported CONSTANT)
|
||||
Q_PROPERTY(bool csSupported READ csSupported CONSTANT)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Define the types on inline messages that can be shown.
|
||||
*/
|
||||
enum MessageType {
|
||||
Positive, /**< Positive message, typically green. */
|
||||
Info, /**< Info message, typically highlight color. */
|
||||
Error, /**< Error message, typically red. */
|
||||
};
|
||||
Q_ENUM(MessageType)
|
||||
|
||||
static Controller &instance();
|
||||
static Controller *create(QQmlEngine *engine, QJSEngine *)
|
||||
{
|
||||
@@ -82,7 +86,7 @@ public:
|
||||
/**
|
||||
* @brief Save an access token to the keychain for the given account.
|
||||
*/
|
||||
bool saveAccessTokenToKeyChain(const Quotient::AccountSettings &account, const QByteArray &accessToken);
|
||||
void saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken);
|
||||
|
||||
[[nodiscard]] bool supportSystemTray() const;
|
||||
|
||||
@@ -102,7 +106,7 @@ public:
|
||||
|
||||
Q_INVOKABLE void removeConnection(const QString &userId);
|
||||
|
||||
bool ssssSupported() const;
|
||||
bool csSupported() const;
|
||||
|
||||
private:
|
||||
explicit Controller(QObject *parent = nullptr);
|
||||
@@ -110,14 +114,14 @@ private:
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
TrayIcon *m_trayIcon = nullptr;
|
||||
|
||||
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const Quotient::AccountSettings &account);
|
||||
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const QString &account);
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings() const;
|
||||
|
||||
Quotient::AccountRegistry m_accountRegistry;
|
||||
QStringList m_accountsLoading;
|
||||
QMap<QString, QPointer<Quotient::Connection>> m_connectionsLoading;
|
||||
QMap<QString, QPointer<NeoChatConnection>> m_connectionsLoading;
|
||||
QString m_endpoint;
|
||||
|
||||
private Q_SLOTS:
|
||||
@@ -131,4 +135,5 @@ Q_SIGNALS:
|
||||
void connectionDropped(NeoChatConnection *connection);
|
||||
void activeConnectionChanged(NeoChatConnection *connection);
|
||||
void accountsLoadingChanged();
|
||||
void showMessage(MessageType messageType, const QString &message);
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ ColumnLayout {
|
||||
model: root.connection.accountDataEventTypes
|
||||
delegate: FormCard.FormButtonDelegate {
|
||||
text: modelData
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
sourceText: root.connection.accountDataJsonString(modelData)
|
||||
}, {
|
||||
title: i18nc("@title:window", "Event Source"),
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
qt_add_library(devtools STATIC)
|
||||
qt_add_qml_module(devtools
|
||||
ecm_add_qml_module(devtools GENERATE_PLUGIN_SOURCE
|
||||
URI org.kde.neochat.devtools
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/devtools
|
||||
QML_FILES
|
||||
DevtoolsPage.qml
|
||||
AccountData.qml
|
||||
DebugOptions.qml
|
||||
FeatureFlagPage.qml
|
||||
RoomData.qml
|
||||
ServerData.qml
|
||||
|
||||
42
src/devtools/DebugOptions.qml
Normal file
42
src/devtools/DebugOptions.qml
Normal file
@@ -0,0 +1,42 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
FormCard.FormCard {
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check", "Show hidden events in the timeline")
|
||||
checked: Config.showAllEvents
|
||||
|
||||
onToggled: Config.showAllEvents = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
id: roomAccountDataVisibleCheck
|
||||
text: i18nc("@option:check Enable the matrix 'threads' feature", "Always allow device verification")
|
||||
description: i18n("Allow the user to start a verification session with devices that were already verified")
|
||||
checked: Config.alwaysVerifyDevice
|
||||
|
||||
onToggled: Config.alwaysVerifyDevice = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check", "Show focus in window header")
|
||||
checked: Config.windowTitleFocus
|
||||
|
||||
onToggled: {
|
||||
Config.windowTitleFocus = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,12 +26,17 @@ FormCard.FormCardPage {
|
||||
readonly property real tabWidth: tabBar.width / tabBar.count
|
||||
|
||||
QQC2.TabButton {
|
||||
text: qsTr("Room Data")
|
||||
text: i18nc("@title:tab", "Debug Options")
|
||||
|
||||
implicitWidth: tabBar.tabWidth
|
||||
}
|
||||
QQC2.TabButton {
|
||||
text: qsTr("Server Info")
|
||||
text: i18nc("@title:tab", "Room Data")
|
||||
|
||||
implicitWidth: tabBar.tabWidth
|
||||
}
|
||||
QQC2.TabButton {
|
||||
text: i18nc("@title:tab", "Server Info")
|
||||
|
||||
implicitWidth: tabBar.tabWidth
|
||||
}
|
||||
@@ -52,6 +57,7 @@ FormCard.FormCardPage {
|
||||
|
||||
currentIndex: tabBar.currentIndex
|
||||
|
||||
DebugOptions {}
|
||||
RoomData {
|
||||
room: root.room
|
||||
connection: root.connection
|
||||
|
||||
@@ -7,7 +7,7 @@ import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat.config
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
@@ -28,5 +28,14 @@ FormCard.FormCardPage {
|
||||
|
||||
onToggled: Config.secretBackup = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check Enable the matrix feature to add a phone number as a third party ID", "Add phone numbers as 3PIDs")
|
||||
checked: Config.phone3PId
|
||||
|
||||
onToggled: {
|
||||
Config.phone3PId = checked
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,10 @@ ColumnLayout {
|
||||
text: i18n("Room")
|
||||
textRole: "escapedDisplayName"
|
||||
valueRole: "roomId"
|
||||
displayText: RoomManager.roomListModel.data(RoomManager.roomListModel.index(currentIndex, 0), RoomListModel.DisplayNameRole)
|
||||
displayText: RoomManager.roomListModel.data(RoomManager.roomListModel.index(currentIndex, 0), RoomListModel.EscapedDisplayNameRole)
|
||||
model: RoomManager.roomListModel
|
||||
currentIndex: 0
|
||||
displayMode: FormCard.FormComboBoxDelegate.Page
|
||||
Component.onCompleted: currentIndex = RoomManager.roomListModel.rowForRoom(root.room)
|
||||
onCurrentValueChanged: root.room = RoomManager.roomListModel.roomByAliasOrId(roomComboBox.currentValue)
|
||||
}
|
||||
@@ -46,7 +47,7 @@ ColumnLayout {
|
||||
model: root.room.accountDataEventTypes
|
||||
delegate: FormCard.FormButtonDelegate {
|
||||
text: modelData
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
sourceText: root.room.roomAcountDataJson(text)
|
||||
}, {
|
||||
title: i18n("Event Source"),
|
||||
@@ -74,14 +75,9 @@ ColumnLayout {
|
||||
description: i18ncp("'Event' being some JSON data, not something physically happening.", "%1 event of this type", "%1 events of this type", model.eventCount)
|
||||
onClicked: {
|
||||
if (model.eventCount === 1) {
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
|
||||
sourceText: stateModel.stateEventJson(stateModel.index(model.index, 0))
|
||||
}, {
|
||||
title: i18n("Event Source"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
})
|
||||
openEventSource(model.type, model.stateKey);
|
||||
} else {
|
||||
pageStack.pushDialogLayer(stateKeysComponent, {
|
||||
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'StateKeys'), {
|
||||
room: root.room,
|
||||
eventType: model.type
|
||||
}, {
|
||||
@@ -91,9 +87,17 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: stateKeysComponent
|
||||
StateKeys {}
|
||||
}
|
||||
}
|
||||
function openEventSource(type: string, stateKey: string): void {
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
model: stateModel,
|
||||
allowEdit: true,
|
||||
room: root.room,
|
||||
type: type,
|
||||
stateKey: stateKey,
|
||||
}, {
|
||||
title: i18n("Event Source"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,13 +31,21 @@ FormCard.FormCardPage {
|
||||
|
||||
delegate: FormCard.FormButtonDelegate {
|
||||
text: model.stateKey
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
|
||||
sourceText: stateKeysModel.stateEventJson(stateKeysModel.index(model.index, 0))
|
||||
}, {
|
||||
title: i18nc("@title:window", "Event Source"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
})
|
||||
onClicked: openEventSource(model.stateKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function openEventSource(stateKey: string): void {
|
||||
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
model: stateKeysModel,
|
||||
allowEdit: true,
|
||||
room: root.room,
|
||||
type: root.eventType,
|
||||
stateKey: stateKey
|
||||
}, {
|
||||
title: i18nc("@title:window", "Event Source"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ public:
|
||||
* a room message.
|
||||
*/
|
||||
enum Type {
|
||||
Author, /**< The message sender and time. */
|
||||
Text, /**< A text message. */
|
||||
Image, /**< A message that is an image. */
|
||||
Audio, /**< A message that is an audio recording. */
|
||||
@@ -47,11 +48,11 @@ public:
|
||||
LiveLocation, /**< The initial event of a shared live location (i.e., the place where this is supposed to be shown in the timeline). */
|
||||
Encrypted, /**< An encrypted message that cannot be decrypted. */
|
||||
Reply, /**< A component to show a replied-to message. */
|
||||
ReplyLoad, /**< A loading dialog for a reply. */
|
||||
LinkPreview, /**< A preview of a URL in the message. */
|
||||
LinkPreviewLoad, /**< A loading dialog for a link preview. */
|
||||
Edit, /**< A text edit for editing a message. */
|
||||
Verification, /**< A user verification session start message. */
|
||||
Loading, /**< The component is loading. */
|
||||
Other, /**< Anything that cannot be classified as another type. */
|
||||
};
|
||||
Q_ENUM(Type);
|
||||
|
||||
@@ -21,7 +21,6 @@ public:
|
||||
* @brief Defines the room list categories a room can be assigned.
|
||||
*/
|
||||
enum Types {
|
||||
Search = 0, /**< So we can show a search delegate if needed, e.g. collapsed mode. */
|
||||
Invited, /**< The user has been invited to the room. */
|
||||
Favorite, /**< The room is set as a favourite. */
|
||||
Direct, /**< The room is a direct chat. */
|
||||
@@ -68,8 +67,6 @@ public:
|
||||
return i18n("Low priority");
|
||||
case NeoChatRoomType::Space:
|
||||
return i18n("Spaces");
|
||||
case NeoChatRoomType::Search:
|
||||
return i18n("Search");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
@@ -89,8 +86,6 @@ public:
|
||||
return QStringLiteral("object-order-lower");
|
||||
case NeoChatRoomType::Space:
|
||||
return QStringLiteral("group");
|
||||
case NeoChatRoomType::Search:
|
||||
return QStringLiteral("search");
|
||||
default:
|
||||
return QStringLiteral("tools-report-bug");
|
||||
}
|
||||
|
||||
109
src/enums/powerlevel.cpp
Normal file
109
src/enums/powerlevel.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
// SPDX-FileCopyrightText: 2024 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 "powerlevel.h"
|
||||
|
||||
QString PowerLevel::nameForLevel(Level level)
|
||||
{
|
||||
switch (level) {
|
||||
case PowerLevel::Member:
|
||||
return i18n("Member");
|
||||
case PowerLevel::Moderator:
|
||||
return i18n("Moderator");
|
||||
case PowerLevel::Admin:
|
||||
return i18n("Admin");
|
||||
case PowerLevel::Mute:
|
||||
return i18n("Mute");
|
||||
case PowerLevel::Custom:
|
||||
return i18n("Custom");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
int PowerLevel::valueForLevel(Level level)
|
||||
{
|
||||
switch (level) {
|
||||
case PowerLevel::Member:
|
||||
return 0;
|
||||
case PowerLevel::Moderator:
|
||||
return 50;
|
||||
case PowerLevel::Admin:
|
||||
return 100;
|
||||
case PowerLevel::Mute:
|
||||
return -1;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
PowerLevel::Level PowerLevel::levelForValue(int value)
|
||||
{
|
||||
switch (value) {
|
||||
case 0:
|
||||
return PowerLevel::Member;
|
||||
case 50:
|
||||
return PowerLevel::Moderator;
|
||||
case 100:
|
||||
return PowerLevel::Admin;
|
||||
case -1:
|
||||
return PowerLevel::Mute;
|
||||
default:
|
||||
return PowerLevel::Custom;
|
||||
}
|
||||
}
|
||||
|
||||
PowerLevelModel::PowerLevelModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool PowerLevelModel::showMute() const
|
||||
{
|
||||
return m_showMute;
|
||||
}
|
||||
|
||||
void PowerLevelModel::setShowMute(bool showMute)
|
||||
{
|
||||
if (showMute == m_showMute) {
|
||||
return;
|
||||
}
|
||||
m_showMute = showMute;
|
||||
Q_EMIT showMuteChanged();
|
||||
}
|
||||
|
||||
QVariant PowerLevelModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return {};
|
||||
}
|
||||
if (index.row() >= rowCount()) {
|
||||
qDebug() << "PowerLevelModel, something's wrong: index.row() >= m_rules.count()";
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto level = static_cast<PowerLevel::Level>(index.row());
|
||||
if (role == NameRole) {
|
||||
return i18nc("%1 is the name of the power level, e.g. admin and %2 is the value that represents.",
|
||||
"%1 (%2)",
|
||||
PowerLevel::nameForLevel(level),
|
||||
PowerLevel::valueForLevel(level));
|
||||
}
|
||||
if (role == ValueRole) {
|
||||
return PowerLevel::valueForLevel(level);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
int PowerLevelModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return PowerLevel::NUMLevels - (m_showMute ? 0 : 1);
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> PowerLevelModel::roleNames() const
|
||||
{
|
||||
return {{NameRole, "name"}, {ValueRole, "value"}};
|
||||
}
|
||||
|
||||
#include "moc_powerlevel.cpp"
|
||||
110
src/enums/powerlevel.h
Normal file
110
src/enums/powerlevel.h
Normal file
@@ -0,0 +1,110 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
/**
|
||||
* @class PowerLevel
|
||||
*
|
||||
* This class is designed to define the PowerLevel enumeration.
|
||||
*/
|
||||
class PowerLevel : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief The type of delegate that is needed for the event.
|
||||
*
|
||||
* @note While similar this is not the matrix event or message type. This is
|
||||
* to tell a QML ListView what delegate to show for each event. So while
|
||||
* similar to the spec it is not the same.
|
||||
*/
|
||||
enum Level {
|
||||
Member, /**< A basic member. */
|
||||
Moderator, /**< A moderator with enhanced powers. */
|
||||
Admin, /**< The highest power level in the room. */
|
||||
Mute, /**< The level to remove posting privileges. */
|
||||
NUMLevels,
|
||||
Custom, /**< A non-standard value. Intentionally after NUMLevels so it doesn't appear in the model. */
|
||||
};
|
||||
Q_ENUM(Level);
|
||||
|
||||
/**
|
||||
* @brief Return a string representation of the enum value.
|
||||
*/
|
||||
static QString nameForLevel(Level level);
|
||||
|
||||
/**
|
||||
* @brief Return the integer representation of the enum value.
|
||||
*/
|
||||
static int valueForLevel(Level level);
|
||||
|
||||
/**
|
||||
* @brief Return the enum value for the given integer power level.
|
||||
*/
|
||||
static Level levelForValue(int value);
|
||||
};
|
||||
|
||||
/**
|
||||
* @class PowerLevelModel
|
||||
*
|
||||
* A model visualize the allowed power levels.
|
||||
*/
|
||||
class PowerLevelModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
Q_PROPERTY(bool showMute READ showMute WRITE setShowMute NOTIFY showMuteChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*/
|
||||
enum Roles {
|
||||
NameRole = Qt::DisplayRole, /**< The power level name. */
|
||||
ValueRole, /**< The power level value. */
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
explicit PowerLevelModel(QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] bool showMute() const;
|
||||
void setShowMute(bool showMute);
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
*
|
||||
* @sa QAbstractItemModel::data
|
||||
*/
|
||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
/**
|
||||
* @brief Number of rows in the model.
|
||||
*
|
||||
* @sa QAbstractItemModel::rowCount
|
||||
*/
|
||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
/**
|
||||
* @brief Returns a mapping from Role enum values to role names.
|
||||
*
|
||||
* @sa Roles, QAbstractItemModel::roleNames()
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void showMuteChanged();
|
||||
|
||||
private:
|
||||
bool m_showMute = true;
|
||||
};
|
||||
@@ -19,11 +19,11 @@
|
||||
#include <Quotient/events/simplestateevents.h>
|
||||
#include <Quotient/events/stickerevent.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
#include "eventhandler_logging.h"
|
||||
#include "events/locationbeaconevent.h"
|
||||
#include "events/pollevent.h"
|
||||
#include "events/serveraclevent.h"
|
||||
#include "events/widgetevent.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "messagecomponenttype.h"
|
||||
@@ -61,22 +61,6 @@ MessageComponentType::Type EventHandler::messageComponentType() const
|
||||
return MessageComponentType::typeForEvent(*m_event);
|
||||
}
|
||||
|
||||
QVariantMap EventHandler::getAuthor(bool isPending) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getAuthor called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
// If we have a room we can return an empty user by handing nullptr to m_room->getUser.
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getAuthor called with m_event set to nullptr. Returning empty user.";
|
||||
return m_room->getUser(nullptr);
|
||||
}
|
||||
|
||||
const auto author = isPending ? m_room->localUser() : m_room->user(m_event->senderId());
|
||||
return m_room->getUser(author);
|
||||
}
|
||||
|
||||
QString EventHandler::getAuthorDisplayName(bool isPending) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
@@ -96,8 +80,8 @@ QString EventHandler::getAuthorDisplayName(bool isPending) const
|
||||
}
|
||||
return previousDisplayName;
|
||||
} else {
|
||||
const auto author = isPending ? m_room->localUser() : m_room->user(m_event->senderId());
|
||||
return m_room->htmlSafeMemberName(author->id());
|
||||
const auto author = isPending ? m_room->localMember() : m_room->member(m_event->senderId());
|
||||
return author.htmlSafeDisplayName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,8 +96,8 @@ QString EventHandler::singleLineAuthorDisplayname(bool isPending) const
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto author = isPending ? m_room->localUser() : m_room->user(m_event->senderId());
|
||||
auto displayName = m_room->safeMemberName(author->id());
|
||||
const auto author = isPending ? m_room->localMember() : m_room->member(m_event->senderId());
|
||||
auto displayName = author.displayName();
|
||||
displayName.replace(QStringLiteral("<br>\n"), QStringLiteral(" "));
|
||||
displayName.replace(QStringLiteral("<br>"), QStringLiteral(" "));
|
||||
displayName.replace(QStringLiteral("<br />\n"), QStringLiteral(" "));
|
||||
@@ -159,6 +143,11 @@ QString EventHandler::getTimeString(bool relative, QLocale::FormatType format, b
|
||||
return {};
|
||||
}
|
||||
|
||||
QString EventHandler::getTimeString(const QString &format, bool isPending, const QDateTime &lastUpdated)
|
||||
{
|
||||
return getTime(isPending, lastUpdated).toLocalTime().toString(format);
|
||||
}
|
||||
|
||||
bool EventHandler::isHighlighted()
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
@@ -220,7 +209,7 @@ bool EventHandler::isHidden()
|
||||
}
|
||||
}
|
||||
|
||||
if (m_room->connection()->isIgnored(m_room->user(m_event->senderId()))) {
|
||||
if (m_room->connection()->isIgnored(m_event->senderId())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -243,19 +232,21 @@ Qt::TextFormat EventHandler::messageBodyInputFormat(const Quotient::RoomMessageE
|
||||
|
||||
QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
|
||||
{
|
||||
QString body;
|
||||
|
||||
if (event.hasFileContent()) {
|
||||
auto fileCaption = event.content()->fileInfo()->originalName;
|
||||
if (fileCaption.isEmpty()) {
|
||||
fileCaption = event.plainBody();
|
||||
} else if (event.content()->fileInfo()->originalName != event.plainBody()) {
|
||||
fileCaption = event.plainBody() + " | "_ls + fileCaption;
|
||||
// if filename is given or body is equal to filename,
|
||||
// then body is a caption
|
||||
QString filename = event.content()->fileInfo()->originalName;
|
||||
QString body = event.plainBody();
|
||||
if (filename.isEmpty() || filename == body) {
|
||||
return QString();
|
||||
}
|
||||
return fileCaption;
|
||||
return body;
|
||||
}
|
||||
|
||||
QString body;
|
||||
if (event.hasTextContent() && event.content()) {
|
||||
body = static_cast<const MessageEventContent::TextContent *>(event.content())->body;
|
||||
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
||||
} else {
|
||||
body = event.plainBody();
|
||||
}
|
||||
@@ -293,14 +284,17 @@ QString EventHandler::getMarkdownBody() const
|
||||
}
|
||||
|
||||
const auto roomMessageEvent = eventCast<const RoomMessageEvent>(m_event);
|
||||
return roomMessageEvent->plainBody();
|
||||
|
||||
QString plainBody = roomMessageEvent->plainBody();
|
||||
plainBody.remove(TextRegex::removeReply);
|
||||
return plainBody;
|
||||
}
|
||||
|
||||
QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat format, bool stripNewlines) const
|
||||
{
|
||||
if (event->isRedacted()) {
|
||||
auto reason = event->redactedBecause()->reason();
|
||||
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>") : i18n("<i>[This message was deleted: %1]</i>", reason);
|
||||
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>") : i18n("<i>[This message was deleted: %1]</i>", reason.toHtmlEscaped());
|
||||
}
|
||||
|
||||
const bool prettyPrint = (format == Qt::RichText);
|
||||
@@ -315,15 +309,19 @@ QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat f
|
||||
},
|
||||
[this, prettyPrint](const RoomMemberEvent &e) {
|
||||
// FIXME: Rewind to the name that was at the time of this event
|
||||
auto subjectName = m_room->htmlSafeMemberName(e.userId());
|
||||
auto subjectName = m_room->member(e.userId()).htmlSafeDisplayName();
|
||||
if (e.membership() == Membership::Leave) {
|
||||
if (e.prevContent() && e.prevContent()->displayName) {
|
||||
subjectName = sanitized(*e.prevContent()->displayName).toHtmlEscaped();
|
||||
subjectName = sanitized(*e.prevContent()->displayName);
|
||||
if (prettyPrint) {
|
||||
subjectName = subjectName.toHtmlEscaped();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prettyPrint) {
|
||||
subjectName = QStringLiteral("<a href=\"https://matrix.to/#/%1\">%2</a>").arg(e.userId(), subjectName);
|
||||
subjectName = QStringLiteral("<a href=\"https://matrix.to/#/%1\" style=\"color: %2\">%3</a>")
|
||||
.arg(e.userId(), m_room->member(e.userId()).color().name(), subjectName);
|
||||
}
|
||||
|
||||
// The below code assumes senderName output in AuthorRole
|
||||
@@ -437,7 +435,7 @@ QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat f
|
||||
[](const LocationBeaconEvent &e) {
|
||||
return e.contentJson()["description"_ls].toString();
|
||||
},
|
||||
[](const ServerAclEvent &) {
|
||||
[](const RoomServerAclEvent &) {
|
||||
return i18n("changed the server access control lists for this room");
|
||||
},
|
||||
[](const WidgetEvent &e) {
|
||||
@@ -476,7 +474,7 @@ QString EventHandler::getMessageBody(const RoomMessageEvent &event, Qt::TextForm
|
||||
|
||||
QString body;
|
||||
if (event.hasTextContent() && event.content()) {
|
||||
body = static_cast<const MessageEventContent::TextContent *>(event.content())->body;
|
||||
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
||||
} else {
|
||||
body = event.plainBody();
|
||||
}
|
||||
@@ -606,7 +604,7 @@ QString EventHandler::getGenericBody() const
|
||||
[](const LocationBeaconEvent &) {
|
||||
return i18n("sent a live location beacon");
|
||||
},
|
||||
[](const ServerAclEvent &) {
|
||||
[](const RoomServerAclEvent &) {
|
||||
return i18n("changed the server access control lists for this room");
|
||||
},
|
||||
[](const WidgetEvent &e) {
|
||||
@@ -655,23 +653,30 @@ QVariantMap EventHandler::getMediaInfoForEvent(const Quotient::RoomEvent *event)
|
||||
QString eventId = event->id();
|
||||
|
||||
// Get the file info for the event.
|
||||
const EventContent::FileInfo *fileInfo;
|
||||
bool isSticker = false;
|
||||
if (event->is<RoomMessageEvent>()) {
|
||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event);
|
||||
if (!roomMessageEvent->hasFileContent()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const EventContent::FileInfo *fileInfo;
|
||||
fileInfo = roomMessageEvent->content()->fileInfo();
|
||||
QVariantMap mediaInfo = getMediaInfoFromFileInfo(fileInfo, eventId, false, false);
|
||||
// if filename isn't specifically given, it is in body
|
||||
// https://spec.matrix.org/latest/client-server-api/#mfile
|
||||
mediaInfo["filename"_ls] = (fileInfo->originalName.isEmpty()) ? roomMessageEvent->plainBody() : fileInfo->originalName;
|
||||
|
||||
return mediaInfo;
|
||||
} else if (event->is<StickerEvent>()) {
|
||||
const EventContent::FileInfo *fileInfo;
|
||||
|
||||
auto stickerEvent = eventCast<const StickerEvent>(event);
|
||||
fileInfo = &stickerEvent->image();
|
||||
isSticker = true;
|
||||
|
||||
return getMediaInfoFromFileInfo(fileInfo, eventId, false, true);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
return getMediaInfoFromFileInfo(fileInfo, eventId, false, isSticker);
|
||||
}
|
||||
|
||||
QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail, bool isSticker) const
|
||||
@@ -797,25 +802,21 @@ MessageComponentType::Type EventHandler::replyMessageComponentType() const
|
||||
return MessageComponentType::typeForEvent(*replyEvent);
|
||||
}
|
||||
|
||||
QVariantMap EventHandler::getReplyAuthor() const
|
||||
Quotient::RoomMember EventHandler::getReplyAuthor() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReplyAuthor called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
// If we have a room we can return an empty user by handing nullptr to m_room->getUser.
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReplyAuthor called with m_event set to nullptr. Returning empty user.";
|
||||
return m_room->getUser(nullptr);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto replyPtr = m_room->getReplyForEvent(*m_event);
|
||||
|
||||
if (replyPtr) {
|
||||
auto replyUser = m_room->user(replyPtr->senderId());
|
||||
return m_room->getUser(replyUser);
|
||||
if (auto replyPtr = m_room->getReplyForEvent(*m_event)) {
|
||||
return m_room->member(replyPtr->senderId());
|
||||
} else {
|
||||
return m_room->getUser(nullptr);
|
||||
return m_room->member(QString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -951,97 +952,4 @@ QString EventHandler::getLocationAssetType() const
|
||||
return assetType;
|
||||
}
|
||||
|
||||
bool EventHandler::hasReadMarkers() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "hasReadMarkers called with m_room set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "hasReadMarkers called with m_event set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
return userIds.size() > 0;
|
||||
}
|
||||
|
||||
QVariantList EventHandler::getReadMarkers(int maxMarkers) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkers called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkers called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds_temp = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds_temp.remove(m_room->localUser()->id());
|
||||
|
||||
auto userIds = userIds_temp.values();
|
||||
if (userIds.count() > maxMarkers) {
|
||||
userIds = userIds.mid(0, maxMarkers);
|
||||
}
|
||||
|
||||
QVariantList users;
|
||||
users.reserve(userIds.size());
|
||||
for (const auto &userId : userIds) {
|
||||
auto user = m_room->user(userId);
|
||||
users += m_room->getUser(user);
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
QString EventHandler::getNumberExcessReadMarkers(int maxMarkers) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getNumberExcessReadMarkers called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getNumberExcessReadMarkers called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
|
||||
if (userIds.count() > maxMarkers) {
|
||||
return QStringLiteral("+ ") + QString::number(userIds.count() - maxMarkers);
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
QString EventHandler::getReadMarkersString() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkersString called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkersString called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localUser()->id());
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
auto user = m_room->user(userId);
|
||||
readMarkersString += user->displayname(m_room) + i18nc("list separator", ", ");
|
||||
}
|
||||
readMarkersString.chop(2);
|
||||
return readMarkersString;
|
||||
}
|
||||
|
||||
#include "moc_eventhandler.cpp"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user