Compare commits
382 Commits
work/inval
...
v23.04.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2c37a93d0 | ||
|
|
d6b575f017 | ||
|
|
063d91c25b | ||
|
|
54cf9ad495 | ||
|
|
aa569e127b | ||
|
|
7e7b9728af | ||
|
|
c72cf64369 | ||
|
|
7aa68ce896 | ||
|
|
c204b36aa9 | ||
|
|
38584bc735 | ||
|
|
2e873fc49b | ||
|
|
5e898d68c2 | ||
|
|
ed9d2e1d02 | ||
|
|
07fb6bd90a | ||
|
|
11003775a1 | ||
|
|
79d8f35dca | ||
|
|
a1d5727a10 | ||
|
|
4e9c4f9752 | ||
|
|
d0e71114de | ||
|
|
a6db2aac70 | ||
|
|
a829b6c991 | ||
|
|
05a6d055b4 | ||
|
|
64f8df4dd2 | ||
|
|
8dcc231bdc | ||
|
|
5efe5cbb88 | ||
|
|
d2f8392ea2 | ||
|
|
57d79bf3fa | ||
|
|
13a573c7f6 | ||
|
|
64a1de8729 | ||
|
|
669ad9f6e2 | ||
|
|
269e29244a | ||
|
|
0b6da7b799 | ||
|
|
6eee09a560 | ||
|
|
2ad5a593b4 | ||
|
|
970ff95fd0 | ||
|
|
5a1a3fbd06 | ||
|
|
9493530b9c | ||
|
|
ec76df5e47 | ||
|
|
095c1fa4c3 | ||
|
|
41874701e6 | ||
|
|
58de466ad5 | ||
|
|
5800180836 | ||
|
|
2b2a864041 | ||
|
|
90399930ec | ||
|
|
8c4ac46a69 | ||
|
|
31951db498 | ||
|
|
fd593bb483 | ||
|
|
1243aef243 | ||
|
|
41ce874f77 | ||
|
|
4d69b70e02 | ||
|
|
b5a46b35f8 | ||
|
|
33ab3fd921 | ||
|
|
357d418cb4 | ||
|
|
76d3484a1e | ||
|
|
aef86017b2 | ||
|
|
40358cba85 | ||
|
|
3ee6877b15 | ||
|
|
e7a0d90746 | ||
|
|
484188c4e1 | ||
|
|
3e967e53bf | ||
|
|
c2f1e0de53 | ||
|
|
69a1d5477e | ||
|
|
a8f29de87a | ||
|
|
275a4f9222 | ||
|
|
95f4c5b9e1 | ||
|
|
d7412c965c | ||
|
|
a94dfde7f7 | ||
|
|
6c7e12c471 | ||
|
|
9949ba8f6d | ||
|
|
2c3ce1e88b | ||
|
|
33533a75bd | ||
|
|
8c6671265c | ||
|
|
bfa643557d | ||
|
|
2b0f6a1d4d | ||
|
|
0e8deee005 | ||
|
|
fe60d11ba3 | ||
|
|
e9d3862537 | ||
|
|
e03b2e9adf | ||
|
|
566743bc19 | ||
|
|
a3b4f05e2c | ||
|
|
977e9bbf99 | ||
|
|
ad11a509aa | ||
|
|
abbe68f9ab | ||
|
|
b7b0749cd8 | ||
|
|
f8ae06f8a7 | ||
|
|
9946ba008b | ||
|
|
376916212d | ||
|
|
1ea6ffe416 | ||
|
|
2d99ae0404 | ||
|
|
88eeed74da | ||
|
|
0032d417ac | ||
|
|
bd9fb097e3 | ||
|
|
ecd7a5edff | ||
|
|
d3f0902835 | ||
|
|
6eb258f8a3 | ||
|
|
dff908edd2 | ||
|
|
7e06606cbd | ||
|
|
f78f92cd0f | ||
|
|
c4fdfa22d9 | ||
|
|
3e5d1429f7 | ||
|
|
76b5463dac | ||
|
|
8b26a9f45f | ||
|
|
da1c664f94 | ||
|
|
498cfedfea | ||
|
|
8983129520 | ||
|
|
1c8acb2acd | ||
|
|
53fa4be4d8 | ||
|
|
bf30152e33 | ||
|
|
d86c61ac8a | ||
|
|
280d90e191 | ||
|
|
b4b24430a1 | ||
|
|
b7b7f1817a | ||
|
|
3d67a682f4 | ||
|
|
da6df18367 | ||
|
|
0b1b1b8c8c | ||
|
|
2493a00ba4 | ||
|
|
d6ebb1308c | ||
|
|
332c311023 | ||
|
|
27d33d121a | ||
|
|
e954fc204f | ||
|
|
d1f7e7091e | ||
|
|
bd4eeb405b | ||
|
|
e6a060c192 | ||
|
|
f53a7a27f6 | ||
|
|
34384dced4 | ||
|
|
b68cfafab2 | ||
|
|
58b836fd1e | ||
|
|
90cad05bae | ||
|
|
072f7cec37 | ||
|
|
39388e204e | ||
|
|
6ccb201110 | ||
|
|
178b516c7c | ||
|
|
ac88e13e58 | ||
|
|
8d3e145e0b | ||
|
|
f40d1b9f4e | ||
|
|
e9cd165457 | ||
|
|
c0c86c67b6 | ||
|
|
12afa43d23 | ||
|
|
386f637b94 | ||
|
|
d7bd9f4609 | ||
|
|
33c9edc9a3 | ||
|
|
3b2dbc731e | ||
|
|
51a29ac528 | ||
|
|
cc8bf79a9b | ||
|
|
8d47e58861 | ||
|
|
47ce8a4846 | ||
|
|
71c9537c61 | ||
|
|
8825e6ec83 | ||
|
|
44ec93f0a0 | ||
|
|
ed7688e66f | ||
|
|
6b49854b12 | ||
|
|
e8484ebc7a | ||
|
|
17d01c68c4 | ||
|
|
dae2cbab90 | ||
|
|
b8e8fa3ee5 | ||
|
|
9c4a925171 | ||
|
|
8996806b05 | ||
|
|
2bd4579c10 | ||
|
|
0891f32c08 | ||
|
|
5287c2d529 | ||
|
|
084b89f3dc | ||
|
|
2dd3197beb | ||
|
|
6d9dca7da8 | ||
|
|
e0f16054fc | ||
|
|
fa70679439 | ||
|
|
949bd20873 | ||
|
|
79a9eb0de0 | ||
|
|
e1b9bc7d0e | ||
|
|
a1abf22174 | ||
|
|
fa27d993e2 | ||
|
|
d4b750433e | ||
|
|
9df534c72c | ||
|
|
f785e4d5b0 | ||
|
|
f186be7314 | ||
|
|
3cbcc2b597 | ||
|
|
61f3d6c9ae | ||
|
|
d8b2436f0d | ||
|
|
ea76edce74 | ||
|
|
5482aad7ba | ||
|
|
aaa26571d1 | ||
|
|
554e3576fd | ||
|
|
8d2c7ff8c2 | ||
|
|
92261eb94f | ||
|
|
4dfb7fc3e2 | ||
|
|
2728446692 | ||
|
|
dfb32fe687 | ||
|
|
55507112a9 | ||
|
|
d9aa77a9f4 | ||
|
|
c0ea52c331 | ||
|
|
f1b9d2329f | ||
|
|
0707fd8189 | ||
|
|
6bef2205db | ||
|
|
ace62b4df1 | ||
|
|
d76e512785 | ||
|
|
db2692c411 | ||
|
|
4c9bdd03ce | ||
|
|
efa2c012a7 | ||
|
|
072dc1b6a3 | ||
|
|
4066427168 | ||
|
|
b901c1bdf2 | ||
|
|
532310d739 | ||
|
|
4f7d32df2b | ||
|
|
833d2159e7 | ||
|
|
ccf8701395 | ||
|
|
5d696aabc4 | ||
|
|
ebd521e2ee | ||
|
|
9d5303113e | ||
|
|
3850fe15a5 | ||
|
|
5392481d06 | ||
|
|
0ce048517b | ||
|
|
c51e16e16c | ||
|
|
765a95050d | ||
|
|
38dcefffcb | ||
|
|
391b61dcb4 | ||
|
|
d76c9fcbcf | ||
|
|
f7b8ae2af2 | ||
|
|
ddbf9b60d4 | ||
|
|
8948ff5faa | ||
|
|
d270e202a8 | ||
|
|
61e1dd14ba | ||
|
|
eee93e0f1f | ||
|
|
826760a55c | ||
|
|
749398df8f | ||
|
|
150968d226 | ||
|
|
d81df24e7c | ||
|
|
594a5cf6ca | ||
|
|
0af420b824 | ||
|
|
31fed8362a | ||
|
|
dbcf8c6327 | ||
|
|
8012392400 | ||
|
|
3e48578b44 | ||
|
|
d7e656e57f | ||
|
|
8e83b923d9 | ||
|
|
a739f0f09f | ||
|
|
bc74737b0f | ||
|
|
e92fccf904 | ||
|
|
20cbedfff5 | ||
|
|
e0d508d3dd | ||
|
|
af318a2bae | ||
|
|
6b4e81c763 | ||
|
|
dcac63aa04 | ||
|
|
7818747e45 | ||
|
|
7cb1f856ca | ||
|
|
5955c8e7dc | ||
|
|
dee3c279e8 | ||
|
|
94427280d8 | ||
|
|
8b245b1cc9 | ||
|
|
0513cd10c4 | ||
|
|
28b5631d06 | ||
|
|
5f2cd92da7 | ||
|
|
4535125c54 | ||
|
|
8831da956a | ||
|
|
f2ec6e1d4c | ||
|
|
93f7def532 | ||
|
|
782f5517d3 | ||
|
|
f238c18ce8 | ||
|
|
4fe0ea373f | ||
|
|
982d21dd58 | ||
|
|
7002132bde | ||
|
|
1ef931f7e7 | ||
|
|
8797015c6a | ||
|
|
85a562d469 | ||
|
|
f50c62ba12 | ||
|
|
13f05a0995 | ||
|
|
1adddcc0d9 | ||
|
|
f03cd3f4c6 | ||
|
|
29a2e4eb99 | ||
|
|
7137a5808f | ||
|
|
fa6f451e11 | ||
|
|
666f247185 | ||
|
|
93dd25f954 | ||
|
|
8e2ba9552f | ||
|
|
1f02c5ea5e | ||
|
|
112b26c39d | ||
|
|
5e243ed01b | ||
|
|
706809d12a | ||
|
|
3fc06c2a74 | ||
|
|
4bc2c42982 | ||
|
|
6651fa4fa3 | ||
|
|
07d65a0046 | ||
|
|
4c9f062a70 | ||
|
|
893ee4a763 | ||
|
|
fa67d174d2 | ||
|
|
bb542521fb | ||
|
|
85d3cf2d77 | ||
|
|
ebf4cfb825 | ||
|
|
d7daa697df | ||
|
|
884484922d | ||
|
|
83ab751d4a | ||
|
|
fcee5bfa92 | ||
|
|
eb610ffe81 | ||
|
|
647cc25e57 | ||
|
|
3912b8e096 | ||
|
|
67f88416f1 | ||
|
|
8e3398df34 | ||
|
|
1a09405829 | ||
|
|
304054a4bb | ||
|
|
3c33bea7db | ||
|
|
15a7cc6e08 | ||
|
|
19b530d34b | ||
|
|
7fe8bc9f3b | ||
|
|
422fca4dc9 | ||
|
|
142dbe2c2c | ||
|
|
dced8ace1d | ||
|
|
de1fa71d8c | ||
|
|
158c99daef | ||
|
|
51e0023384 | ||
|
|
7f27056a34 | ||
|
|
cdbf5ea8e7 | ||
|
|
8bbd5e5a88 | ||
|
|
2e3c2c2424 | ||
|
|
80faa4bd4f | ||
|
|
de6f93b200 | ||
|
|
c46bfe05c1 | ||
|
|
0d50af6285 | ||
|
|
efb9ca5ac8 | ||
|
|
3ad5b62e27 | ||
|
|
8985aadcf1 | ||
|
|
907d52d693 | ||
|
|
eb5523a69c | ||
|
|
f475965cf7 | ||
|
|
1176cf029b | ||
|
|
d68fb81bcf | ||
|
|
81f7afe730 | ||
|
|
60e43a2794 | ||
|
|
76f686b580 | ||
|
|
cba4fdc397 | ||
|
|
62ea4bc67d | ||
|
|
25c7b7b780 | ||
|
|
6b3f44e923 | ||
|
|
9334585e0f | ||
|
|
1190511b54 | ||
|
|
b40d51841e | ||
|
|
f2ddee09c0 | ||
|
|
698cbceda3 | ||
|
|
66b1499fad | ||
|
|
e8748ce733 | ||
|
|
4bfa9c783c | ||
|
|
5cdfa086b2 | ||
|
|
e8824edfd4 | ||
|
|
507bd44bbf | ||
|
|
bfa08d178f | ||
|
|
9e01c96476 | ||
|
|
fe855f16f8 | ||
|
|
0fbc1b2121 | ||
|
|
b5d8acf9de | ||
|
|
1ab5bdb600 | ||
|
|
9060de1d60 | ||
|
|
1f83ab4450 | ||
|
|
e5680da5ce | ||
|
|
66bfcd6239 | ||
|
|
8f19c73908 | ||
|
|
716616210f | ||
|
|
cc414f71f4 | ||
|
|
d107dfcab1 | ||
|
|
875c03a0f6 | ||
|
|
4145987c65 | ||
|
|
232f3f624b | ||
|
|
2a2791c37f | ||
|
|
f1c9f5902a | ||
|
|
3f8d2a11d0 | ||
|
|
eb38741486 | ||
|
|
f207f57bd5 | ||
|
|
74b9f5fa4f | ||
|
|
6347c02d8b | ||
|
|
8dbfc093fa | ||
|
|
6acb847843 | ||
|
|
e270a46a36 | ||
|
|
e010116232 | ||
|
|
9bcbdb78fd | ||
|
|
85b0ec1e96 | ||
|
|
e4f42e2c2b | ||
|
|
8b5910773c | ||
|
|
1366158b45 | ||
|
|
d0dd86e6e8 | ||
|
|
af40860315 | ||
|
|
f3d7fbc483 | ||
|
|
d07066e540 | ||
|
|
dfb569c0f6 | ||
|
|
fda433706b | ||
|
|
a734be5f9e | ||
|
|
7ebd82d441 |
@@ -2,7 +2,7 @@
|
|||||||
"id": "org.kde.neochat",
|
"id": "org.kde.neochat",
|
||||||
"branch": "master",
|
"branch": "master",
|
||||||
"runtime": "org.kde.Platform",
|
"runtime": "org.kde.Platform",
|
||||||
"runtime-version": "5.15-21.08",
|
"runtime-version": "5.15-22.08",
|
||||||
"sdk": "org.kde.Sdk",
|
"sdk": "org.kde.Sdk",
|
||||||
"command": "neochat",
|
"command": "neochat",
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -136,8 +136,8 @@
|
|||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "archive",
|
"type": "archive",
|
||||||
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.4.0.tar.gz",
|
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.7.0.tar.gz",
|
||||||
"sha256": "0e68b3f0ce7bf521ffbdd731464d2d60d8d7a39a749b551ed26855a1707d86d1",
|
"sha256": "23ef0217926e67c8d2eb861cf91617da2f7d8d5a9ae6c62321b21448b1669210",
|
||||||
"x-checker-data": {
|
"x-checker-data": {
|
||||||
"type": "anitya",
|
"type": "anitya",
|
||||||
"project-id": 236236,
|
"project-id": 236236,
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ compile_commands.json
|
|||||||
kate.project.ctags.*
|
kate.project.ctags.*
|
||||||
*.user
|
*.user
|
||||||
.flatpak-builder/
|
.flatpak-builder/
|
||||||
|
.idea/
|
||||||
|
|||||||
@@ -4,10 +4,11 @@
|
|||||||
include:
|
include:
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/reuse-lint.yml
|
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/reuse-lint.yml
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android.yml
|
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android.yml
|
||||||
|
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android-qt6.yml
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml
|
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml
|
||||||
# TODO enable once we can have qt6 libQuotient on the CI
|
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml
|
||||||
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml
|
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows.yml
|
||||||
# TODO re-enable once cmark is in the CI again
|
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows-qt6.yml
|
||||||
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows.yml
|
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd.yml
|
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd.yml
|
||||||
|
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd-qt6.yml
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/flatpak.yml
|
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/flatpak.yml
|
||||||
|
|||||||
42
.kde-ci.yml
42
.kde-ci.yml
@@ -1,8 +1,8 @@
|
|||||||
# SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
|
# SPDX-FileCopyrightText: 2021 Tobias Fella <tobias.fella@kde.org>
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
Dependencies:
|
Dependencies:
|
||||||
- 'on': ['@all']
|
- 'on': ['Linux/Qt5', 'Android/Qt5', 'FreeBSD/Qt5', 'Windows/Qt5']
|
||||||
'require':
|
'require':
|
||||||
'frameworks/extra-cmake-modules': '@stable'
|
'frameworks/extra-cmake-modules': '@stable'
|
||||||
'frameworks/kcoreaddons': '@stable'
|
'frameworks/kcoreaddons': '@stable'
|
||||||
@@ -14,15 +14,47 @@ Dependencies:
|
|||||||
'frameworks/knotifications': '@stable'
|
'frameworks/knotifications': '@stable'
|
||||||
'libraries/kquickimageeditor': '@stable'
|
'libraries/kquickimageeditor': '@stable'
|
||||||
'frameworks/sonnet': '@stable'
|
'frameworks/sonnet': '@stable'
|
||||||
- 'on': ['Windows', 'Linux', 'FreeBSD']
|
'libraries/kirigami-addons': '@latest'
|
||||||
|
'third-party/libquotient': '@latest'
|
||||||
|
'third-party/qtkeychain': '@latest'
|
||||||
|
'third-party/cmark': '@latest'
|
||||||
|
'third-party/qcoro': '@latest'
|
||||||
|
- 'on': ['Windows/Qt5', 'Linux/Qt5', 'FreeBSD/Qt5']
|
||||||
'require':
|
'require':
|
||||||
'frameworks/qqc2-desktop-style': '@stable'
|
'frameworks/qqc2-desktop-style': '@stable'
|
||||||
'frameworks/kio': '@stable'
|
'frameworks/kio': '@stable'
|
||||||
'frameworks/kwindowsystem': '@stable'
|
'frameworks/kwindowsystem': '@stable'
|
||||||
'frameworks/kconfigwidgets': '@stable'
|
'frameworks/kconfigwidgets': '@stable'
|
||||||
- 'on': ['Linux', 'FreeBSD']
|
- 'on': ['Linux/Qt5', 'FreeBSD/Qt5']
|
||||||
'require':
|
'require':
|
||||||
'frameworks/kdbusaddons': '@stable'
|
'frameworks/kdbusaddons': '@stable'
|
||||||
|
|
||||||
|
- 'on': ['Linux/Qt6', 'Android/Qt6', 'FreeBSD/Qt6', 'Windows/Qt6']
|
||||||
|
'require':
|
||||||
|
'frameworks/extra-cmake-modules': '@latest-kf6'
|
||||||
|
'frameworks/kcoreaddons': '@latest-kf6'
|
||||||
|
'frameworks/kirigami': '@latest-kf6'
|
||||||
|
'frameworks/ki18n': '@latest-kf6'
|
||||||
|
'frameworks/kconfig': '@latest-kf6'
|
||||||
|
'frameworks/syntax-highlighting': '@latest-kf6'
|
||||||
|
'frameworks/kitemmodels': '@latest-kf6'
|
||||||
|
'frameworks/knotifications': '@latest-kf6'
|
||||||
|
'libraries/kquickimageeditor': '@latest-kf6'
|
||||||
|
'frameworks/sonnet': '@latest-kf6'
|
||||||
|
'libraries/kirigami-addons': '@latest-kf6'
|
||||||
|
'third-party/libquotient': '@latest'
|
||||||
|
'third-party/qtkeychain': '@latest'
|
||||||
|
'third-party/cmark': '@latest'
|
||||||
|
'third-party/qcoro': '@latest'
|
||||||
|
- 'on': ['Windows/Qt6', 'Linux/Qt6', 'FreeBSD/Qt6']
|
||||||
|
'require':
|
||||||
|
'frameworks/qqc2-desktop-style': '@latest-kf6'
|
||||||
|
'frameworks/kio': '@latest-kf6'
|
||||||
|
'frameworks/kwindowsystem': '@latest-kf6'
|
||||||
|
'frameworks/kconfigwidgets': '@latest-kf6'
|
||||||
|
- 'on': ['Linux/Qt6', 'FreeBSD/Qt6']
|
||||||
|
'require':
|
||||||
|
'frameworks/kdbusaddons': '@latest-kf6'
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
require-passing-tests-on: [ 'Linux', 'FreeBSD', 'Windows' ]
|
require-passing-tests-on: [ 'Linux/Qt5', 'FreeBSD', 'Windows/Qt5' ]
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Copyright: 2020 Carson Black <uhhadd@gmail.com>
|
|||||||
License: LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
License: LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||||
|
|
||||||
Files: android/res/drawable/splash.xml
|
Files: android/res/drawable/splash.xml
|
||||||
Copyright: 2020 Tobias Fella <fella@posteo.de>
|
Copyright: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||||
License: BSD-2-Clause
|
License: BSD-2-Clause
|
||||||
|
|
||||||
Files: .gitignore
|
Files: .gitignore
|
||||||
@@ -27,11 +27,11 @@ Copyright: 2021 Carl Schwan <carlschwan@kde.org>
|
|||||||
License: BSD-2-Clause
|
License: BSD-2-Clause
|
||||||
|
|
||||||
Files: src/neochatconfig.kcfg
|
Files: src/neochatconfig.kcfg
|
||||||
Copyright: 2020-2021 Carl Schwan <carlschwan@kde.org>, Tobias Fella <fella@posteo.de>
|
Copyright: 2020-2021 Carl Schwan <carlschwan@kde.org>, Tobias Fella <tobias.fella@kde.org>
|
||||||
License: BSD-2-Clause
|
License: BSD-2-Clause
|
||||||
|
|
||||||
Files: src/neochat.notifyrc
|
Files: src/neochat.notifyrc
|
||||||
Copyright: 2020 Tobias Fella <fella@posteo.de>
|
Copyright: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||||
License: BSD-2-Clause
|
License: BSD-2-Clause
|
||||||
|
|
||||||
Files: src/qml/Component/confetti.png src/qml/Component/glowdot.png
|
Files: src/qml/Component/confetti.png src/qml/Component/glowdot.png
|
||||||
@@ -39,5 +39,5 @@ Copyright: 2021 Alexey Andreyev <aa13q@ya.ru>
|
|||||||
License: CC0-1.0
|
License: CC0-1.0
|
||||||
|
|
||||||
Files: .flatpak-manifest.json
|
Files: .flatpak-manifest.json
|
||||||
Copyright: 2020-2022 Tobias Fella <fella@posteo.de>
|
Copyright: 2020-2022 Tobias Fella <tobias.fella@kde.org>
|
||||||
License: BSD-2-Clause
|
License: BSD-2-Clause
|
||||||
|
|||||||
@@ -1,18 +1,26 @@
|
|||||||
# SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carl@carlschwan.eu>
|
# SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carl@carlschwan.eu>
|
||||||
# SPDX-FileCopyrightText: 2020-2021 Nicolas Fella <nicolas.fella@gmx.de>
|
# SPDX-FileCopyrightText: 2020-2021 Nicolas Fella <nicolas.fella@gmx.de>
|
||||||
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <fella@posteo.de>
|
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
||||||
# SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
|
# SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
project(NeoChat)
|
# KDE Applications version, managed by release script.
|
||||||
set(PROJECT_VERSION "22.09")
|
set(RELEASE_SERVICE_VERSION_MAJOR "23")
|
||||||
|
set(RELEASE_SERVICE_VERSION_MINOR "04")
|
||||||
|
set(RELEASE_SERVICE_VERSION_MICRO "3")
|
||||||
|
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||||
|
|
||||||
set(KF5_MIN_VERSION "5.91.0")
|
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||||
|
|
||||||
|
set(KF_MIN_VERSION "5.91.0")
|
||||||
set(QT_MIN_VERSION "5.15.2")
|
set(QT_MIN_VERSION "5.15.2")
|
||||||
|
if (ANDROID)
|
||||||
|
set(QT_MIN_VERSION "5.15.8")
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
|
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
|
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
|
||||||
|
|
||||||
@@ -49,15 +57,16 @@ set_package_properties(Qt${QT_MAJOR_VERSION} PROPERTIES
|
|||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
PURPOSE "Basic application components"
|
PURPOSE "Basic application components"
|
||||||
)
|
)
|
||||||
find_package(KF5 ${KF5_MIN_VERSION} COMPONENTS Kirigami2 I18n Notifications Config CoreAddons Sonnet ItemModels)
|
find_package(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} COMPONENTS Kirigami2 I18n Notifications Config CoreAddons Sonnet ItemModels)
|
||||||
set_package_properties(KF5 PROPERTIES
|
set_package_properties(KF${QT_MAJOR_VERSION} PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
PURPOSE "Basic application components"
|
PURPOSE "Basic application components"
|
||||||
)
|
)
|
||||||
set_package_properties(KF5Kirigami2 PROPERTIES
|
set_package_properties(KF${QT_MAJOR_VERSION}Kirigami2 PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
PURPOSE "Kirigami application UI framework"
|
PURPOSE "Kirigami application UI framework"
|
||||||
)
|
)
|
||||||
|
find_package(KF${QT_MAJOR_VERSION}KirigamiAddons 0.7.2 REQUIRED)
|
||||||
|
|
||||||
find_package(Qt${QT_MAJOR_VERSION}Keychain)
|
find_package(Qt${QT_MAJOR_VERSION}Keychain)
|
||||||
set_package_properties(Qt${QT_MAJOR_VERSION}Keychain PROPERTIES
|
set_package_properties(Qt${QT_MAJOR_VERSION}Keychain PROPERTIES
|
||||||
@@ -73,17 +82,15 @@ if(ANDROID)
|
|||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} COMPONENTS Widgets)
|
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} COMPONENTS Widgets)
|
||||||
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle ConfigWidgets KIO WindowSystem)
|
find_package(KF${QT_MAJOR_VERSION} ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle ConfigWidgets KIO WindowSystem)
|
||||||
set_package_properties(KF5QQC2DesktopStyle PROPERTIES
|
set_package_properties(KF${QT_MAJOR_VERSION}QQC2DesktopStyle PROPERTIES
|
||||||
TYPE RUNTIME
|
TYPE RUNTIME
|
||||||
)
|
)
|
||||||
ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0)
|
ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ecm_find_qmlmodule(org.kde.kirigamiaddons.labs.mobileform 0.1)
|
|
||||||
|
|
||||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||||
find_package(KF5DBusAddons ${KF5_MIN_VERSION} REQUIRED)
|
find_package(KF${QT_MAJOR_VERSION}DBusAddons ${KF_MIN_VERSION} REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Quotient 0.6)
|
find_package(Quotient 0.6)
|
||||||
@@ -113,19 +120,18 @@ set_package_properties(KQuickImageEditor PROPERTIES
|
|||||||
PURPOSE "Add image editing capability to image attachments"
|
PURPOSE "Add image editing capability to image attachments"
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(QCoro${QT_MAJOR_VERSION} COMPONENTS Core QUIET)
|
find_package(QCoro${QT_MAJOR_VERSION} 0.4 COMPONENTS Core REQUIRED)
|
||||||
if(NOT QCoro${QT_MAJOR_VERSION}_FOUND)
|
|
||||||
find_package(QCoro REQUIRED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
qcoro_enable_coroutines()
|
qcoro_enable_coroutines()
|
||||||
|
|
||||||
find_package(KF5DocTools ${KF5_MIN_VERSION})
|
find_package(KF${QT_MAJOR_VERSION}DocTools ${KF_MIN_VERSION})
|
||||||
set_package_properties(KF5DocTools PROPERTIES DESCRIPTION
|
set_package_properties(KF${QT_MAJOR_VERSION}DocTools PROPERTIES DESCRIPTION
|
||||||
"Tools to generate documentation"
|
"Tools to generate documentation"
|
||||||
TYPE OPTIONAL
|
TYPE OPTIONAL
|
||||||
)
|
)
|
||||||
|
|
||||||
|
find_package(Sqlite3)
|
||||||
|
|
||||||
if(NOT Quotient_VERSION_MINOR GREATER 6)
|
if(NOT Quotient_VERSION_MINOR GREATER 6)
|
||||||
cmake_policy(SET CMP0063 OLD)
|
cmake_policy(SET CMP0063 OLD)
|
||||||
endif()
|
endif()
|
||||||
@@ -145,11 +151,11 @@ add_definitions(-DQT_NO_FOREACH)
|
|||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
if (BUILD_TESTING AND Quotient_VERSION_MINOR GREATER 6)
|
if (BUILD_TESTING AND Quotient_VERSION_MINOR GREATER 6)
|
||||||
find_package(Qt5 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test)
|
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test)
|
||||||
add_subdirectory(autotests)
|
add_subdirectory(autotests)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(KF5DocTools_FOUND)
|
if(KF${QT_MAJOR_VERSION}DocTools_FOUND)
|
||||||
kdoctools_install(po)
|
kdoctools_install(po)
|
||||||
add_subdirectory(doc)
|
add_subdirectory(doc)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
0
LICENSES/MIT.txt
Executable file → Normal file
0
LICENSES/MIT.txt
Executable file → Normal file
@@ -1,6 +1,6 @@
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carlschwan@kde.org>
|
SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carlschwan@kde.org>
|
||||||
SPDX-FileCopyrightText: 2020-2021 Tobias Fella <fella@posteo.de>
|
SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
||||||
SPDX-License-Identifier: CC0-1.0
|
SPDX-License-Identifier: CC0-1.0
|
||||||
-->
|
-->
|
||||||
# NeoChat
|
# NeoChat
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
android:name="org.qtproject.qt5.android.bindings.QtActivity"
|
android:name="org.qtproject.qt5.android.bindings.QtActivity"
|
||||||
android:label="NeoChat"
|
android:label="NeoChat"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:launchMode="singleTop">
|
android:launchMode="singleTop"
|
||||||
|
android:exported="true">
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
@@ -22,7 +23,6 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data android:name="android.app.lib_name" android:value="neochat-app"/>
|
<meta-data android:name="android.app.lib_name" android:value="neochat-app"/>
|
||||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
|
||||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||||
@@ -38,8 +38,6 @@
|
|||||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
||||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
||||||
<!-- Messages maps -->
|
<!-- Messages maps -->
|
||||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
|
||||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
|
||||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
||||||
|
|
||||||
<!-- Splash screen -->
|
<!-- Splash screen -->
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.6.4'
|
classpath 'com.android.tools.build:gradle:7.0.2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +73,10 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion qtMinSdkVersion
|
minSdkVersion qtMinSdkVersion
|
||||||
targetSdkVersion qtTargetSdkVersion
|
targetSdkVersion qtTargetSdkVersion
|
||||||
|
applicationId "org.kde.neochat"
|
||||||
|
namespace "org.kde.neochat"
|
||||||
|
versionCode timestamp
|
||||||
|
versionName projectVersionFull
|
||||||
manifestPlaceholders = [versionName: projectVersionFull, versionCode: timestamp]
|
manifestPlaceholders = [versionName: projectVersionFull, versionCode: timestamp]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,3 +8,9 @@ ecm_add_test(
|
|||||||
LINK_LIBRARIES neochat Qt::Test Quotient
|
LINK_LIBRARIES neochat Qt::Test Quotient
|
||||||
TEST_NAME neochatroomtest
|
TEST_NAME neochatroomtest
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ecm_add_test(
|
||||||
|
texthandlertest.cpp
|
||||||
|
LINK_LIBRARIES neochat Qt::Test
|
||||||
|
TEST_NAME texthandlertest
|
||||||
|
)
|
||||||
|
|||||||
@@ -5,9 +5,7 @@
|
|||||||
#include <QSignalSpy>
|
#include <QSignalSpy>
|
||||||
#include <QTest>
|
#include <QTest>
|
||||||
|
|
||||||
#define protected public // please don't hate me
|
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
#undef protected
|
|
||||||
|
|
||||||
#include <connection.h>
|
#include <connection.h>
|
||||||
#include <quotient_common.h>
|
#include <quotient_common.h>
|
||||||
@@ -15,12 +13,23 @@
|
|||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
|
class TestRoom : public NeoChatRoom
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using NeoChatRoom::NeoChatRoom;
|
||||||
|
|
||||||
|
void update(SyncRoomData &&data, bool fromCache = false)
|
||||||
|
{
|
||||||
|
Room::updateData(std::move(data), fromCache);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class NeoChatRoomTest : public QObject {
|
class NeoChatRoomTest : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Connection *connection = nullptr;
|
Connection *connection = nullptr;
|
||||||
NeoChatRoom *room = nullptr;
|
TestRoom *room = nullptr;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
@@ -31,7 +40,7 @@ private Q_SLOTS:
|
|||||||
void NeoChatRoomTest::initTestCase()
|
void NeoChatRoomTest::initTestCase()
|
||||||
{
|
{
|
||||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||||
room = new NeoChatRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||||
|
|
||||||
auto json = QJsonDocument::fromJson(R"EVENT({
|
auto json = QJsonDocument::fromJson(R"EVENT({
|
||||||
"account_data": {
|
"account_data": {
|
||||||
@@ -121,13 +130,13 @@ void NeoChatRoomTest::initTestCase()
|
|||||||
}
|
}
|
||||||
})EVENT");
|
})EVENT");
|
||||||
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object());
|
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object());
|
||||||
room->updateData(std::move(roomData));
|
room->update(std::move(roomData));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeoChatRoomTest::subtitleTextTest()
|
void NeoChatRoomTest::subtitleTextTest()
|
||||||
{
|
{
|
||||||
QCOMPARE(room->timelineSize(), 1);
|
QCOMPARE(room->timelineSize(), 1);
|
||||||
QCOMPARE(room->subtitleText(), QStringLiteral("@example:example.org: This is an example text message"));
|
QCOMPARE(room->lastEventToString(), QStringLiteral("@example:example.org: This is an example text message"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeoChatRoomTest::eventTest()
|
void NeoChatRoomTest::eventTest()
|
||||||
|
|||||||
512
autotests/texthandlertest.cpp
Normal file
512
autotests/texthandlertest.cpp
Normal file
@@ -0,0 +1,512 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
#include "texthandler.h"
|
||||||
|
|
||||||
|
#include <connection.h>
|
||||||
|
#include <quotient_common.h>
|
||||||
|
#include <syncdata.h>
|
||||||
|
|
||||||
|
using namespace Quotient;
|
||||||
|
|
||||||
|
class TestRoom : public NeoChatRoom
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using NeoChatRoom::NeoChatRoom;
|
||||||
|
|
||||||
|
void update(SyncRoomData &&data, bool fromCache = false)
|
||||||
|
{
|
||||||
|
Room::updateData(std::move(data), fromCache);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TextHandlerTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
Connection *connection = nullptr;
|
||||||
|
TestRoom *room = nullptr;
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void initTestCase();
|
||||||
|
|
||||||
|
void allowedAttributes();
|
||||||
|
void stripDisallowedTags();
|
||||||
|
void stripDisallowedAttributes();
|
||||||
|
void emptyCodeTags();
|
||||||
|
|
||||||
|
void sendSimpleStringCase();
|
||||||
|
void sendSingleParaMarkup();
|
||||||
|
void sendMultipleSectionMarkup();
|
||||||
|
void sendBadLinks();
|
||||||
|
void sendEscapeCode();
|
||||||
|
void sendCodeClass();
|
||||||
|
|
||||||
|
void receiveStripReply();
|
||||||
|
void receivePlainTextIn();
|
||||||
|
|
||||||
|
void recieveRichInPlainOut();
|
||||||
|
void receivePlainStripHtml();
|
||||||
|
void receivePlainStripMarkup();
|
||||||
|
void receiveStripNewlines();
|
||||||
|
|
||||||
|
void receiveRichUserPill();
|
||||||
|
void receiveRichStrikethrough();
|
||||||
|
void receiveRichtextIn();
|
||||||
|
void receiveRichMxcUrl();
|
||||||
|
void receiveRichPlainUrl();
|
||||||
|
|
||||||
|
void receiveLineSeparator();
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef QUOTIENT_07
|
||||||
|
void TextHandlerTest::initTestCase()
|
||||||
|
{
|
||||||
|
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||||
|
room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
|
||||||
|
|
||||||
|
const auto json = QJsonDocument::fromJson(R"EVENT({
|
||||||
|
"account_data": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"tags": {
|
||||||
|
"u.work": {
|
||||||
|
"order": 0.9
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "m.tag"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"custom_config_key": "custom_config_value"
|
||||||
|
},
|
||||||
|
"type": "org.example.custom.room.config"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ephemeral": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"user_ids": [
|
||||||
|
"@alice:matrix.org",
|
||||||
|
"@bob:example.com"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"type": "m.typing"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||||
|
"displayname": "Alice Margatroid",
|
||||||
|
"membership": "join",
|
||||||
|
"reason": "Looking for support"
|
||||||
|
},
|
||||||
|
"event_id": "$143273582443PhrSn:example.org",
|
||||||
|
"origin_server_ts": 1432735824653,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"sender": "@example:example.org",
|
||||||
|
"state_key": "@alice:example.org",
|
||||||
|
"type": "m.room.member",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 1234
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"summary": {
|
||||||
|
"m.heroes": [
|
||||||
|
"@alice:example.com",
|
||||||
|
"@bob:example.com"
|
||||||
|
],
|
||||||
|
"m.invited_member_count": 0,
|
||||||
|
"m.joined_member_count": 2
|
||||||
|
},
|
||||||
|
"timeline": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"body": "This is an **example** text message",
|
||||||
|
"format": "org.matrix.custom.html",
|
||||||
|
"formatted_body": "<b>This is an example text message</b>",
|
||||||
|
"msgtype": "m.text"
|
||||||
|
},
|
||||||
|
"event_id": "$143273582443PhrSn:example.org",
|
||||||
|
"origin_server_ts": 1432735824654,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||||
|
"sender": "@example:example.org",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 1235
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"limited": true,
|
||||||
|
"prev_batch": "t34-23535_0_0"
|
||||||
|
}
|
||||||
|
})EVENT");
|
||||||
|
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object());
|
||||||
|
room->update(std::move(roomData));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void TextHandlerTest::allowedAttributes()
|
||||||
|
{
|
||||||
|
const QString testInputString1 = QStringLiteral("<p><span data-mx-spoiler><font color=#FFFFFF>Test</font><span></p>");
|
||||||
|
const QString testOutputString1 = QStringLiteral("<p><span data-mx-spoiler><font color=#FFFFFF>Test</font><span></p>");
|
||||||
|
// Handle urls where the href has either single (') or double (") quotes.
|
||||||
|
const QString testInputString2 = QStringLiteral("<p><a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a></p>");
|
||||||
|
const QString testOutputString2 = QStringLiteral("<p><a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a></p>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString1);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleSendText(), testOutputString1);
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString1);
|
||||||
|
|
||||||
|
testTextHandler.setData(testInputString2);
|
||||||
|
QCOMPARE(testTextHandler.handleSendText(), testOutputString2);
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::stripDisallowedTags()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral("<p>Allowed</p> <span>Allowed</span> <body>Disallowed</body>");
|
||||||
|
const QString testOutputString = QStringLiteral("<p>Allowed</p> <span>Allowed</span> Disallowed");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::stripDisallowedAttributes()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral("<p style=\"font-size:50px;\" color=#FFFFFF>Test</p>");
|
||||||
|
const QString testOutputString = QStringLiteral("<p>Test</p>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure that empty code tags are handled.
|
||||||
|
* (this was a bug during development hence the test)
|
||||||
|
*/
|
||||||
|
void TextHandlerTest::emptyCodeTags()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral("<pre><code></code></pre>");
|
||||||
|
const QString testOutputString = QStringLiteral("<pre><code></code></pre>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::sendSimpleStringCase()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral("This data should just be put in a paragraph.");
|
||||||
|
const QString testOutputString = QStringLiteral("<p>This data should just be put in a paragraph.</p>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::sendSingleParaMarkup()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral(
|
||||||
|
"Text para with **bold**, *italic*, [link](https://kde.org), , `inline code`.");
|
||||||
|
const QString testOutputString = QStringLiteral(
|
||||||
|
"<p>Text para with <strong>bold</strong>, <em>italic</em>, <a href=\"https://kde.org\">link</a>, <img "
|
||||||
|
"src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e\" alt=\"image\">, <code>inline code</code>.</p>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::sendMultipleSectionMarkup()
|
||||||
|
{
|
||||||
|
const QString testInputString =
|
||||||
|
QStringLiteral("Text para\n> blockquote\n* List 1\n* List 2\n1. one\n2. two\n# Heading 1\n## Heading 2\nhorizontal rule\n\n---\n```\ncodeblock\n```");
|
||||||
|
const QString testOutputString = QStringLiteral(
|
||||||
|
"<p>Text para</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>\n<ul>\n<li>List 1</li>\n<li>List "
|
||||||
|
"2</li>\n</ul>\n<ol>\n<li>one</li>\n<li>two</li>\n</ol>\n<h1>Heading 1</h1>\n<h2>Heading 2</h2>\n<p>horizontal "
|
||||||
|
"rule</p>\n<hr>\n<pre><code>codeblock\n</code></pre>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::sendBadLinks()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral("[link](kde.org), ");
|
||||||
|
const QString testOutputString = QStringLiteral("<p><a>link</a>, <img alt=\"image\"></p>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All text between code tags is treated as plain so it should get escaped.
|
||||||
|
*/
|
||||||
|
void TextHandlerTest::sendEscapeCode()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral("```\n<p>Test <span style=\"font-size:50px;\">some</span> code</p>\n```");
|
||||||
|
const QString testOutputString =
|
||||||
|
QStringLiteral("<pre><code><p>Test <span style="font-size:50px;">some</span> code</p>\n</code></pre>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::sendCodeClass()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral("```html\nsome code\n```\n<pre><code class=\"code-underline\">some more code</code></pre>");
|
||||||
|
const QString testOutputString = QStringLiteral("<pre><code class=\"language-html\">some code\n</code></pre>\n<pre><code>some more code</code></pre>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::receiveStripReply()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral(
|
||||||
|
"<mx-reply><blockquote><a href=\"https://matrix.to/#/!somewhere:example.org/$event:example.org\">In reply to</a><a "
|
||||||
|
"href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a><br />Message replied to.</blockquote></mx-reply>Reply message.");
|
||||||
|
const QString testOutputString = QStringLiteral("Reply message.");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||||
|
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::recieveRichInPlainOut()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral("a & b");
|
||||||
|
const QString testOutputString = QStringLiteral("a & b");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::receivePlainTextIn()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral("<plain text in tag bracket>\nTest link https://kde.org.");
|
||||||
|
const QString testOutputStringRich = QStringLiteral("<plain text in tag bracket><br>Test link <a href=\"https://kde.org\">https://kde.org</a>.");
|
||||||
|
QString testOutputStringPlain = QStringLiteral("<plain text in tag bracket>\nTest link https://kde.org.");
|
||||||
|
|
||||||
|
// Make sure quotes are maintained in a plain string.
|
||||||
|
const QString testInputString2 = QStringLiteral("last line is \"Time to switch to a new topic.\"");
|
||||||
|
const QString testOutputString2 = QStringLiteral("last line is \"Time to switch to a new topic.\"");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::PlainText), testOutputStringRich);
|
||||||
|
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputStringPlain);
|
||||||
|
|
||||||
|
testTextHandler.setData(testInputString2);
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::PlainText), testOutputString2);
|
||||||
|
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::receiveStripNewlines()
|
||||||
|
{
|
||||||
|
const QString testInputStringPlain = QStringLiteral("Test\nmany\nnew\nlines.");
|
||||||
|
const QString testInputStringRich = QStringLiteral("Test<br>many<br />new<br>lines.");
|
||||||
|
const QString testOutputString = QStringLiteral("Test many new lines.");
|
||||||
|
|
||||||
|
const QString testInputStringPlain2 = QStringLiteral("* List\n* Items");
|
||||||
|
const QString testOutputString2 = QStringLiteral("List Items");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputStringPlain);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::PlainText, true), testOutputString);
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::PlainText, nullptr, nullptr, true), testOutputString);
|
||||||
|
|
||||||
|
testTextHandler.setData(testInputStringRich);
|
||||||
|
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText, true), testOutputString);
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, nullptr, nullptr, true), testOutputString);
|
||||||
|
|
||||||
|
testTextHandler.setData(testInputStringPlain2);
|
||||||
|
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText, true), testOutputString2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For a plain text output of a received string all html is stripped except for
|
||||||
|
* code which is unescaped if it's html.
|
||||||
|
*/
|
||||||
|
void TextHandlerTest::receivePlainStripHtml()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral("<p>Test</p> <pre><code>Some code <strong>with tags</strong></code></pre>");
|
||||||
|
const QString testOutputString = QStringLiteral("Test Some code <strong>with tags</strong>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::receivePlainStripMarkup()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral("**bold** `<p>inline code</p>` *italic*");
|
||||||
|
const QString testOutputString = QStringLiteral("bold <p>inline code</p> italic");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::receiveRichUserPill()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral("<p><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></p>");
|
||||||
|
const QString testOutputString = QStringLiteral("<p><b><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></b></p>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::receiveRichStrikethrough()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral("<p><del>Test</del></p>");
|
||||||
|
const QString testOutputString = QStringLiteral("<p><s>Test</s></p>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::receiveRichtextIn()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral("<p>Test</p> <pre><code>Some code <strong>with tags</strong></code></pre>");
|
||||||
|
const QString testOutputString = QStringLiteral("<p>Test</p> <pre><code>Some code <strong>with tags</strong></code></pre>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef QUOTIENT_07
|
||||||
|
void TextHandlerTest::receiveRichMxcUrl()
|
||||||
|
{
|
||||||
|
const QString testInputString = QStringLiteral(
|
||||||
|
"<img src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e\" alt=\"image\"><img src=\"mxc://kde.org/34c3464b3a1bd7f55af2d559e07d2c773c430e73\" "
|
||||||
|
"alt=\"image\">");
|
||||||
|
const QString testOutputString = QStringLiteral(
|
||||||
|
"<img "
|
||||||
|
"src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e?user_id=@bob:kde.org&room_id=%23myroom:kde.org&event_id=$143273582443PhrSn:example.org\" "
|
||||||
|
"alt=\"image\"><img "
|
||||||
|
"src=\"mxc://kde.org/34c3464b3a1bd7f55af2d559e07d2c773c430e73?user_id=@bob:kde.org&room_id=%23myroom:kde.org&event_id=$143273582443PhrSn:example.org\" "
|
||||||
|
"alt=\"image\">");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputString);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, room, room->messageEvents().back().get()), testOutputString);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For when your rich input string has a plain text url left in.
|
||||||
|
*
|
||||||
|
* This test is to show that a url that is already rich will be left alone but a
|
||||||
|
* plain one will be linkified.
|
||||||
|
*/
|
||||||
|
void TextHandlerTest::receiveRichPlainUrl()
|
||||||
|
{
|
||||||
|
// This is an actual link that caused trouble which is why it's so long. Keeping
|
||||||
|
// so we can confirm consistent behaviour for complex urls.
|
||||||
|
const QString testInputStringLink1 = QStringLiteral(
|
||||||
|
"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im "
|
||||||
|
"<a "
|
||||||
|
"href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/"
|
||||||
|
"$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im\">Link already rich</a>");
|
||||||
|
const QString testOutputStringLink1 = QStringLiteral(
|
||||||
|
"<a "
|
||||||
|
"href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/"
|
||||||
|
"$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im\">https://matrix.to/#/"
|
||||||
|
"!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im</a> <a "
|
||||||
|
"href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/"
|
||||||
|
"$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im\">Link already rich</a>");
|
||||||
|
|
||||||
|
// Another real case. The linkification wasn't handling it when a single link
|
||||||
|
// contains what looks like and email. It was been broken into 3 but needs to
|
||||||
|
// be just single link.
|
||||||
|
const QString testInputStringLink2 = QStringLiteral("https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/");
|
||||||
|
const QString testOutputStringLink2 = QStringLiteral(
|
||||||
|
"<a "
|
||||||
|
"href=\"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/\">https://lore.kernel.org/lkml/"
|
||||||
|
"CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/</a>");
|
||||||
|
|
||||||
|
QString testInputStringEmail = QStringLiteral(R"(email@example.com <a href="mailto:email@example.com">Link already rich</a>)");
|
||||||
|
QString testOutputStringEmail =
|
||||||
|
QStringLiteral(R"(<a href="mailto:email@example.com">email@example.com</a> <a href="mailto:email@example.com">Link already rich</a>)");
|
||||||
|
|
||||||
|
QString testInputStringMxId = QStringLiteral("@user:kde.org <a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a>");
|
||||||
|
QString testOutputStringMxId = QStringLiteral(
|
||||||
|
"<b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a></b>");
|
||||||
|
|
||||||
|
TextHandler testTextHandler;
|
||||||
|
testTextHandler.setData(testInputStringLink1);
|
||||||
|
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringLink1);
|
||||||
|
|
||||||
|
testTextHandler.setData(testInputStringLink2);
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringLink2);
|
||||||
|
|
||||||
|
testTextHandler.setData(testInputStringEmail);
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringEmail);
|
||||||
|
|
||||||
|
testTextHandler.setData(testInputStringMxId);
|
||||||
|
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringMxId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextHandlerTest::receiveLineSeparator()
|
||||||
|
{
|
||||||
|
auto text = QStringLiteral("foo\u2028bar");
|
||||||
|
TextHandler textHandler;
|
||||||
|
textHandler.setData(text);
|
||||||
|
QCOMPARE(textHandler.handleRecievePlainText(Qt::PlainText, true), QStringLiteral("foo bar"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(TextHandlerTest)
|
||||||
|
#include "texthandlertest.moc"
|
||||||
BIN
icons/150-apps-neochat.png
Normal file
BIN
icons/150-apps-neochat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
BIN
icons/44-apps-neochat.png
Normal file
BIN
icons/44-apps-neochat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
@@ -2,7 +2,7 @@
|
|||||||
<!--
|
<!--
|
||||||
- SPDX-License-Identifier: CC0-1.0
|
- SPDX-License-Identifier: CC0-1.0
|
||||||
- SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carlschwan@kde.org>
|
- SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carlschwan@kde.org>
|
||||||
- SPDX-FileCopyrightText: 2020-2021 Tobias Fella <fella@posteo.de>
|
- SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
||||||
-->
|
-->
|
||||||
<component type="desktop">
|
<component type="desktop">
|
||||||
<id>org.kde.neochat</id>
|
<id>org.kde.neochat</id>
|
||||||
@@ -16,11 +16,13 @@
|
|||||||
<name xml:lang="ca-valencia">NeoChat</name>
|
<name xml:lang="ca-valencia">NeoChat</name>
|
||||||
<name xml:lang="cs">NeoChat</name>
|
<name xml:lang="cs">NeoChat</name>
|
||||||
<name xml:lang="de">NeoChat</name>
|
<name xml:lang="de">NeoChat</name>
|
||||||
|
<name xml:lang="el">NeoChat</name>
|
||||||
<name xml:lang="en-GB">NeoChat</name>
|
<name xml:lang="en-GB">NeoChat</name>
|
||||||
<name xml:lang="es">NeoChat</name>
|
<name xml:lang="es">NeoChat</name>
|
||||||
<name xml:lang="eu">NeoChat</name>
|
<name xml:lang="eu">NeoChat</name>
|
||||||
<name xml:lang="fi">NeoChat</name>
|
<name xml:lang="fi">NeoChat</name>
|
||||||
<name xml:lang="fr">NeoChat</name>
|
<name xml:lang="fr">NeoChat</name>
|
||||||
|
<name xml:lang="gl">NeoChat</name>
|
||||||
<name xml:lang="hu">NeoChat</name>
|
<name xml:lang="hu">NeoChat</name>
|
||||||
<name xml:lang="ia">Neochat</name>
|
<name xml:lang="ia">Neochat</name>
|
||||||
<name xml:lang="id">NeoChat</name>
|
<name xml:lang="id">NeoChat</name>
|
||||||
@@ -34,6 +36,7 @@
|
|||||||
<name xml:lang="pl">NeoChat</name>
|
<name xml:lang="pl">NeoChat</name>
|
||||||
<name xml:lang="pt">NeoChat</name>
|
<name xml:lang="pt">NeoChat</name>
|
||||||
<name xml:lang="pt-BR">NeoChat</name>
|
<name xml:lang="pt-BR">NeoChat</name>
|
||||||
|
<name xml:lang="ru">NeoChat</name>
|
||||||
<name xml:lang="sk">NeoChat</name>
|
<name xml:lang="sk">NeoChat</name>
|
||||||
<name xml:lang="sl">NeoChat</name>
|
<name xml:lang="sl">NeoChat</name>
|
||||||
<name xml:lang="sv">NeoChat</name>
|
<name xml:lang="sv">NeoChat</name>
|
||||||
@@ -45,15 +48,17 @@
|
|||||||
<summary>A client for matrix, the decentralized communication protocol</summary>
|
<summary>A client for matrix, the decentralized communication protocol</summary>
|
||||||
<summary xml:lang="ar">عميل لماتركس، ميفاق الاتصال اللامركزي</summary>
|
<summary xml:lang="ar">عميل لماتركس، ميفاق الاتصال اللامركزي</summary>
|
||||||
<summary xml:lang="az">Matrix üçün müştəri, mərkəzləşməmiş kommunikasiya protokolu</summary>
|
<summary xml:lang="az">Matrix üçün müştəri, mərkəzləşməmiş kommunikasiya protokolu</summary>
|
||||||
<summary xml:lang="ca">Un client per al Matrix, el protocol de comunicacions descentralitzat</summary>
|
<summary xml:lang="ca">Un client de Matrix, el protocol de comunicacions descentralitzat</summary>
|
||||||
<summary xml:lang="ca-valencia">Un client per a Matrix, el protocol de comunicacions descentralitzat</summary>
|
<summary xml:lang="ca-valencia">Un client de Matrix, el protocol de comunicacions descentralitzat</summary>
|
||||||
<summary xml:lang="cs">Klient pro decentralizovaný komunikační protokol matrix</summary>
|
<summary xml:lang="cs">Klient pro decentralizovaný komunikační protokol matrix</summary>
|
||||||
<summary xml:lang="de">Ein Programm für Matrix, das dezentrale Kommunikationsprotokoll</summary>
|
<summary xml:lang="de">Ein Programm für Matrix, das dezentrale Kommunikationsprotokoll</summary>
|
||||||
|
<summary xml:lang="el">Ένας πελάτης για το Matrix, το αποκεντρωμένο πρωτόκολλο επικοινωνίας</summary>
|
||||||
<summary xml:lang="en-GB">A client for matrix, the decentralised communication protocol</summary>
|
<summary xml:lang="en-GB">A client for matrix, the decentralised communication protocol</summary>
|
||||||
<summary xml:lang="es">Un cliente para Matrix, el protocolo de comunicaciones descentralizado</summary>
|
<summary xml:lang="es">Un cliente para Matrix, el protocolo de comunicaciones descentralizado</summary>
|
||||||
<summary xml:lang="eu">Matrix, deszentralizatutako komunikazio protokolorako bezero bat</summary>
|
<summary xml:lang="eu">Matrix, deszentralizatutako komunikazio protokolorako bezero bat</summary>
|
||||||
<summary xml:lang="fi">Asiakas Matrixille, hajautetulle viestintäyhteyskäytännölle</summary>
|
<summary xml:lang="fi">Asiakas Matrixille, hajautetulle viestintäyhteyskäytännölle</summary>
|
||||||
<summary xml:lang="fr">Un client pour « Matrix », le protocole décentralisé de communications.</summary>
|
<summary xml:lang="fr">Un client pour « Matrix », le protocole décentralisé de communications.</summary>
|
||||||
|
<summary xml:lang="gl">Un cliente para Matrix, o protocolo de comunicación descentralizada</summary>
|
||||||
<summary xml:lang="hu">Kliens a matrixhoz, a decentralizált kommunikációs protokollhoz</summary>
|
<summary xml:lang="hu">Kliens a matrixhoz, a decentralizált kommunikációs protokollhoz</summary>
|
||||||
<summary xml:lang="ia">Un cliente per Matrix, le protocollo de communication decentralisate</summary>
|
<summary xml:lang="ia">Un cliente per Matrix, le protocollo de communication decentralisate</summary>
|
||||||
<summary xml:lang="id">Klien untuk matrix, protokol komunikasi terdesentralisasi</summary>
|
<summary xml:lang="id">Klien untuk matrix, protokol komunikasi terdesentralisasi</summary>
|
||||||
@@ -62,106 +67,99 @@
|
|||||||
<summary xml:lang="ka">კლიენტი Matrix-სთვის, დეცენტრალიზებული კომუნიკაციის პროტოკოლისთვის</summary>
|
<summary xml:lang="ka">კლიენტი Matrix-სთვის, დეცენტრალიზებული კომუნიკაციის პროტოკოლისთვის</summary>
|
||||||
<summary xml:lang="ko">Matrix, 분산 대화 프로토콜 클라이언트</summary>
|
<summary xml:lang="ko">Matrix, 분산 대화 프로토콜 클라이언트</summary>
|
||||||
<summary xml:lang="nl">Een client voor matrix, het gedecentraliseerde communicatieprotocol</summary>
|
<summary xml:lang="nl">Een client voor matrix, het gedecentraliseerde communicatieprotocol</summary>
|
||||||
<summary xml:lang="nn">Ein klient for Matrix, den desentraliserte lynmeldingsprotokollen</summary>
|
<summary xml:lang="nn">Ein klient for Matrix – protokollen for desentralisert kommunikasjon</summary>
|
||||||
<summary xml:lang="pa">ਮੈਟਰਿਕਸ, ਸਰਬ-ਸਾਂਝੇ ਸੰਚਾਰ ਪਰੋਟੋਕਾਲ, ਲਈ ਕਲਾਈਂਟ ਹੈ</summary>
|
<summary xml:lang="pa">ਮੈਟਰਿਕਸ, ਸਰਬ-ਸਾਂਝੇ ਸੰਚਾਰ ਪਰੋਟੋਕਾਲ, ਲਈ ਕਲਾਈਂਟ ਹੈ</summary>
|
||||||
<summary xml:lang="pl">Program do obsługi matriksa, rozproszonego protokołu porozumiewania się</summary>
|
<summary xml:lang="pl">Program do obsługi matriksa, rozproszonego protokołu porozumiewania się</summary>
|
||||||
<summary xml:lang="pt">Um cliente para o Matrix, o protocolo de comunicação descentralizado</summary>
|
<summary xml:lang="pt">Um cliente para o Matrix, o protocolo de comunicação descentralizado</summary>
|
||||||
<summary xml:lang="pt-BR">Um cliente do Matrix, o protocolo de comunicação descentralizado</summary>
|
<summary xml:lang="pt-BR">Um cliente do Matrix, o protocolo de comunicação descentralizado</summary>
|
||||||
|
<summary xml:lang="ru">Клиент для Matrix — децентрализованного коммуникационного протокола</summary>
|
||||||
<summary xml:lang="sk">Klient pre matrix, decentralizovaný komunikačný protokol</summary>
|
<summary xml:lang="sk">Klient pre matrix, decentralizovaný komunikačný protokol</summary>
|
||||||
<summary xml:lang="sl">Odjemalec za matrix, decentralizirani komunikacijski protokol</summary>
|
<summary xml:lang="sl">Odjemalec za matrix, decentralizirani komunikacijski protokol</summary>
|
||||||
<summary xml:lang="sv">En klient för Matrix, det decentraliserade kommunikationsprotokollet</summary>
|
<summary xml:lang="sv">En klient för Matrix, det decentraliserade kommunikationsprotokollet</summary>
|
||||||
|
<summary xml:lang="ta">மையமில்லா தகவல் பரிமாற்ற நெறிமுறையான மேட்ரிக்ஸுக்கான செயலி</summary>
|
||||||
<summary xml:lang="tr">Merkezi olmayan iletişim protokolü Matrix için bir istemci</summary>
|
<summary xml:lang="tr">Merkezi olmayan iletişim protokolü Matrix için bir istemci</summary>
|
||||||
<summary xml:lang="uk">Клієнт matrix, децентралізованого протоколу обміну даними</summary>
|
<summary xml:lang="uk">Клієнт matrix, децентралізованого протоколу обміну даними</summary>
|
||||||
<summary xml:lang="x-test">xxA client for matrix, the decentralized communication protocolxx</summary>
|
<summary xml:lang="x-test">xxA client for matrix, the decentralized communication protocolxx</summary>
|
||||||
<summary xml:lang="zh-CN">分布式通讯协议 Matrix 的客户端</summary>
|
<summary xml:lang="zh-CN">分布式通讯协议 Matrix 的客户端</summary>
|
||||||
<description>
|
<description>
|
||||||
<p>NeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.</p>
|
<p>NeoChat is a client for Matrix, the decentralized communication protocol for instant messaging. It allows you to send text messages, videos and audio files to your family, colleagues and friends. It uses KDE frameworks and most notably Kirigami
|
||||||
<p xml:lang="ar">نيوتشات هو عميل ماتركس Matrix. يتيح لك إرسال رسائل نصية ومقاطع فيديو وملفات صوتية إلى عائلتك وزملائك وأصدقائك باستخدام بروتوكول ماتركس</p>
|
to provide a convergent experience across multiple platforms.</p>
|
||||||
<p xml:lang="az">NeoChat Mtrix müştərisidir. O, Matrix protokolundan istifadə edərək, ailənizə, dostlarınıza, iş yoldaşlarınıza mətn, səsli və görüntülü ismarıclar göndərməyə imkan verir.</p>
|
<p xml:lang="ar">نيوتشات هو عميل ماتركس Matrix، (ميفاق الاتصال اللامركزي للمراسلة الفورية). يتيح لك نيوتشات إرسال رسائل نصية ومقاطع فيديو وملفات صوتية إلى عائلتك وزملائك وأصدقائك. يستخدم أطر عمل كيدي وأبرزها Kirigami لتوفير تجربة متقاربة عبر منصات متعددة.</p>
|
||||||
<p xml:lang="ca">El NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics usant el protocol Matrix.</p>
|
<p xml:lang="ca">El NeoChat és un client de Matrix, el protocol descentralitzat de comunicacions de missatgeria instantània. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics. Fa servir els Frameworks de KDE i, sobretot, el Kirigami per a proporcionar una experiència convergent a través de diverses plataformes.</p>
|
||||||
<p xml:lang="ca-valencia">NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics utilitzant el protocol Matrix.</p>
|
<p xml:lang="ca-valencia">NeoChat és un client de Matrix, el protocol descentralitzat de comunicacions de missatgeria instantània. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics. Utilitza els Frameworks de KDE i, sobretot, Kirigami per a proporcionar una experiència convergent a través de diverses plataformes.</p>
|
||||||
<p xml:lang="de">NeoChat ist ein Matrix-Client. Er ermöglicht Ihnen das Senden von Textnachrichten, Videos und Audiodateien an Ihre Familie, Kollegen und Freunde unter Verwendung des Matrix-Protokolls.</p>
|
<p xml:lang="en-GB">NeoChat is a client for Matrix, the decentralised communication protocol for instant messaging. It allows you to send text messages, videos and audio files to your family, colleagues and friends. It uses KDE frameworks and most notably Kirigami to provide a convergent experience across multiple platforms.</p>
|
||||||
<p xml:lang="en-GB">NeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.</p>
|
<p xml:lang="es">NeoChat es un cliente para Matrix, el protocolo de comunicaciones descentralizado para mensajería instantánea. Le permite enviar mensajes de texto, vídeos y archivos de sonido a su familia, compañeros de trabajo y amigos. Usa la infraestructura de KDE y, en particular, Kirigami para proporcionar una experiencia convergente en muchas plataformas.</p>
|
||||||
<p xml:lang="es">NeoChat es un cliente para Matrix. Le permite enviar mensajes de texto, vídeos y archivos de sonido a su familia, compañeros de trabajo y amigos usando el protocolo Matrix.</p>
|
<p xml:lang="fi">NeoChat on asiakassovellus Matrixille, hajautetulle pikaviestinyhteyskäytännölle. Sillä voi lähettää teksti-, video- ja ääniviestejä perheelle, tutuille ja ystäville. Se käyttää KDE-kehystä ja erityisesti Kirigamia tuottaakseen mukautuvan monialustaisen käyttökokemuksen.</p>
|
||||||
<p xml:lang="eu">NeoChat Matrix bezero bat da. Familiari, lankideei eta lagunei testu-mezuak, bideoak eta audio-fitxategiak bidaltzeko aukera ematen du, Matrix protokoloa erabiliz.</p>
|
<p xml:lang="fr">NeoChat est un client pour le protocole Matrix, un protocole décentralisé de communications pour messagerie instantané. Il vous permet d'envoyer des messages de texte, des vidéos et des fichiers audio à votre famille, vos collègues et vos amis. Il utilise les environnements de développement et plus précisément Kirigami pour fournir une expérience convergente sur plusieurs plate-formes. </p>
|
||||||
<p xml:lang="fi">NeoChat on Matrix-asiakas. Sillä voi lähettää perheelle, tuttaville ja kavereille tekstiviestejä sekä video- ja äänitiedostoja Matrix-yhteyskäytännöllä.</p>
|
<p xml:lang="gl">NeoChat é un cliente para Matrix, o protocolo de comunicación descentralizada para mensaxería instantánea. Podes enviar mensaxes de texto, vídeos e ficheiros de son á túa familia, colegas e amizades. Usas infraestruturas de KDE e principalmente Kirigami para proporcionar unha experiencia de uso converxente para varias plataformas.</p>
|
||||||
<p xml:lang="fr">NeoChat est un client Matrix. Il vous permet d'envoyer des messages de texte, des vidéos et des fichiers audio à votre famille, vos collègues et vos amis en utilisant le protocole Matrix.</p>
|
<p xml:lang="ia">NeoChat es un cliente per Matrix, le protocollo de communication decentralisate per messager instantanee. Illo te permitte inviar messager de texto, files de video e audio a tu familia, collegas e amicos usante. Illo usa KDE frameworks e super toto Kirigamii forni un experientia convergente trans platteforme multiple.</p>
|
||||||
<p xml:lang="hu">A NeoChat egy Matrix kliens. Szöveges üzeneteket, videókat ésaudio fájlokat küldhet családjának, kollégáinak és barátainak a Matrix protokoll használatával.</p>
|
<p xml:lang="it">NeoChat è un client per Matrix, il protocollo di comunicazione decentralizzato per la messaggistica istantanea. Ti consente di inviare messaggi di testo, video e file audio a familiari, colleghi e amici. Utilizza i framework KDE e in particolare Kirigami per fornire un'esperienza convergente su più piattaforme.</p>
|
||||||
<p xml:lang="ia">NeoChat es un cliente de Matrix. Illo te permitte inviar messager de texto, files de video e audio a tu familia, collegas e amicos usante le protocollo de Matrix.</p>
|
<p xml:lang="ka">NeoChat არის Matrix კლიენტი. ის საშუალებას გაძლევთ გაგზავნოთ ტექსტური შეტყობინებები, ვიდეოები და აუდიო ფაილები თქვენს ოჯახს, კოლეგებსა და მეგობრებს მატრიქსის პროტოკოლის გამოყენებით. ის იყენებს KDE-ის შესაძლებლობებს და განსაკუთრებით კი Kirigami-ის, რათა პლატფორმებს შორის თავსებადობა უზრუნველყოს.</p>
|
||||||
<p xml:lang="id">NeoChat adalah sebuah klien Matrix. Memungkinkan Anda untuk mengirim pesan teks, file video dan audio ke keluarga, kolega dan teman Anda menggunakan protokol Matrix.</p>
|
<p xml:lang="nl">NeoChat is een client voor Matrix, het gedecentraliseerde communicatieprotocol voor instant messages. Het biedt u het verzenden van tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden. Het gebruik KDE frameworks en het meest opmerkelijk Kirigami om een convergente ervaring te leveren op meerdere platforms.</p>
|
||||||
<p xml:lang="ie">NeoChat es un cliente de Matrix. On posse usar it por inviar missages textual, video- e audiofiles a su familie, colegos e amics med li protocol Matrix.</p>
|
<p xml:lang="nn">NeoChat er ein klient for Matrix, ein protokoll for desentralisert kommunikasjon. Du kan utveksla tekst, lyd og videoar med kollegaar, vennar og familie. Programmet brukar KDE Frameworks og Kirigami for å gje ei brukarflate tilpassa ulike plattformer.</p>
|
||||||
<p xml:lang="it">NeoChat è un client Matrix. Ti consente di inviare messaggi di testo, file video e audio a familiari, colleghi e amici utilizzando il protocollo Matrix.</p>
|
<p xml:lang="pl">NeoChat jest programem do Matriksa, protokołu rozproszonego porozumiewania się w czasie rzeczywistym. Umożliwia wysyłanie wiadomości tekstowych, filmów oraz dźwięku do twojej rodziny, znajomych oraz przyjaciół. Używa szkieletów KDE i głównie Kirigami, aby zapewnić spójne wrażenia na wielu platformach</p>
|
||||||
<p xml:lang="ka">NeoChat არის Matrix კლიენტი. ის საშუალებას გაძლევთ გაგზავნოთ ტექსტური შეტყობინებები, ვიდეოები და აუდიო ფაილები თქვენს ოჯახს, კოლეგებსა და მეგობრებს მატრიქსის პროტოკოლის გამოყენებით.</p>
|
<p xml:lang="sl">Neochat je odjemalec za Matrix, decentralizirani komunikacijski protokol za takojšnje sporočanje. Omogoča vam pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek svoji družini, sodelavcem in prijateljem. Uporablja okvire KDE in predvsem Kirigami, da zagotavlja konvergentno izkušnjo na več platformah.</p>
|
||||||
<p xml:lang="ko">NeoChat은 Matrix 클라이언트입니다. Matrix 프로토콜을 사용하여 가족, 동료, 친구에게 텍스트 메시지, 동영상, 오디오 파일을 전송할 수 있습니다.</p>
|
<p xml:lang="tr">NeoChat, anlık iletileşme için merkezi olmayan iletişim protokolü olan Matrix için bir istemcidir. Ailenize, iş arkadaşlarınıza ve arkadaşlarınıza metin iletiler, videolar ve ses dosyaları göndermenize olanak tanır. Birden çok platformda yakınsak bir deneyim sağlamak için KDE Frameworks ve en önemlilerinden Kirigami'yi kullanır.</p>
|
||||||
<p xml:lang="nl">NeoChat is een Matrix-client. Het biedt u het verzenden van tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden met het Matrix-protocol.</p>
|
<p xml:lang="uk">NeoChat — клієнт Matrix, децентралізованого протоколу спілкування для миттєвого обміну повідомленнями. За його допомогою ви можете надсилати текстові повідомлення, відео та звукові файли вашій родин, колегами та друзям. У програмі використано бібліотеки KDE, зокрема Kirigami, для надання однорідного середовища на декількох програмних та апаратних платформах.</p>
|
||||||
<p xml:lang="pl">NeoChat jest programem do Matrisa. Umożliwia wysyłanie wiadomości tekstowych, filmów oraz dźwięku do twojej rodziny, znajomych oraz przyjaciół poprzez protokół Matriksa.</p>
|
<p xml:lang="x-test">xxNeoChat is a client for Matrix, the decentralized communication protocol for instant messaging. It allows you to send text messages, videos and audio files to your family, colleagues and friends. It uses KDE frameworks and most notably Kirigami to provide a convergent experience across multiple platforms.xx</p>
|
||||||
<p xml:lang="pt">O NeoChat é um cliente do Matrix. O mesmo permite-lhe enviar mensagens de texto, ficheiros de vídeo e áudio para a sua família, colegas e amigos com o protocolo Matrix.</p>
|
<p>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="pt-BR">O NeoChat é um cliente Matrix. Ele permite a você enviar mensagens de texto, arquivos de vídeo e áudio para seus familiares, colegas e amigos usando o protocolo Matrix.</p>
|
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. على هذا النحو يتم دعم كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP والخيوط وبعض جوانب التشفير من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار ، ولكن يبقى الهدف توفير الدعم النهائي للمواصفات بأكملها.</p>
|
||||||
<p xml:lang="sk">NeoChat je Matrix klient. Umožňuje vám posielať textové správy, videá a zvukové súbory rodine, kolegom a priateľom pomocou protokolu Matrix.</p>
|
<p xml:lang="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="sl">NeoChat je odjemalec Matrixa. Dovoljuje vam pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, kolegom in prijateljem z uporabo protokola Matrix.</p>
|
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||||
<p xml:lang="sv">NeoChat är en Matrix-klient. Den låter dig skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner med användning av Matrix-protokollet.</p>
|
<p xml:lang="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="tr">NeoChat, bir Matrix istemcisidir. Matrix protokolünü kullanarak ailenize, iş arkadaşlarınıza, arkadaşlarınıza ve başkalarına metin iletileri, video ve ses dosyaların göndermenize olanak verir.</p>
|
<p xml:lang="es">NeoChat pretende ser una aplicación con todas las funciones para la especificación de Matrix. Como tal, admite todo en la especificación estable actual, con las notables excepciones de VoIP, subprocesos y algunas funciones de cifrado de extremo a extremo. Existen algunas omisiones menos importantes debido al hecho de que la especificación de Matrix está en constante evolución, pero el objetivo sigue siendo brindar compatibilidad final con toda la especificación.</p>
|
||||||
<p xml:lang="uk">NeoChat — клієнт мережі обміну повідомленнями Matrix. За допомогою цієї програми ви зможете надсилати текстові повідомлення, відео та звукові файли вашій родині, колегам та друзям за допомогою протоколу Matrix.</p>
|
<p xml:lang="fi">NeoChat pyrkii olemaan Matrix-määritelmän täysominaisuuksinen sovellus, joten se tukee kaikkea nykyisessä vakaassa määritelmässä muutamaa huomattavaa poikkeusta lukuun ottamatta (VoIP, säikeet ja jotkin piirteet päästä päähän -salauksessa). Joitakin pienempiäkin puutteita on Matrix-määritelmän jatkuvan kehityksen vuoksi, mutta lopputavoitteena on tarjota määritelmän täysi tuki.</p>
|
||||||
<p xml:lang="x-test">xxNeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.xx</p>
|
<p xml:lang="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="zh-CN">NeoChat 是一个 Matrix 客户端。 它允许您使用 Matrix 协议向您的家人、同事和朋友发送文本消息、视频和音频文件。</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>Matrix is a decentralized communication protocol, putting the user back in control. Currently NeoChat implements large part of the protocol with the exception of encrypted chats and video chat.</p>
|
<p xml:lang="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="ar">ماتريكس هو بروتوكول اتصال لامركزي ، يعيد المستخدم إلى السيطرة. يطبق نيوتشات حاليًا جزءًا كبيرًا من الميفاق باستثناء الدردشات المشفرة ودردشة الفيديو.</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="az">Matrix, istifadəçini nəzarətdə saxlayan, mərkəzləşməmişi rabitə protokoludur. NeoChat, söhbətin və video əlaqəsinin şifrələnməsindən başqa bir çox protokolları həyata keçirə bilir.</p>
|
<p xml:lang="ka">NeoChat-ი მიზნად ისახავს Matrix სპეციფიკაციის სრული განხორციელება ჰქონდეს. როგორც ასეთი, ყველაფერი მიმდინარე სპეციფიკაციიდან, VoIP-ის, ძაფებისა და გამჭოლი დაშიფვრის ზოგიერთი ასპექტის გარდა, მხარდაჭერილია. შეძლება ასევე იყოს მცირე ლაფსუსებიც იმის გამო, რომ Matrix-ის სპეციფიკაცია მუდმივად ვითარგდება, მაგრამ ჩვენი მიზანი მისი სრული მხარდაჭერაა.</p>
|
||||||
<p xml:lang="ca">Matrix és un protocol de comunicacions descentralitzat, que retorna el control a l'usuari. Actualment el NeoChat implementa una gran part del protocol amb l'excepció dels xats encriptats i els xats de vídeo.</p>
|
<p xml:lang="nl">NeoChat richt zich op het volledig bieden van alle mogelijkheden van de Matrix-specificatie. Alles in de huidige stabiele specificatie met merkbare uitzondering van VoIP, gekoppelde discussies en sommige aspecten van eind-tot-eind versleuteling worden ondersteund. Er zijn een paar andere kleinere omissies vanwege het feit dat de Matrix specificatie constant evolueert maar het doel blijft het eventueel bieden van ondersteuning van de gehele specificatie.</p>
|
||||||
<p xml:lang="ca-valencia">Matrix és un protocol de comunicacions descentralitzat, que retorna el control a l'usuari. Actualment NeoChat implementa una gran part del protocol amb l'excepció dels xats encriptats i els xats de vídeo.</p>
|
<p xml:lang="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="de">Matrix ist ein dezentralisiertes Kommunikationsprotokoll, das dem Benutzer wieder die Kontrolle zurückgibt. Derzeit implementiert NeoChat einen großen Teil des Protokolls mit der Ausnahme von verschlüsselten Chats und Video-Chat.</p>
|
<p xml:lang="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="en-GB">Matrix is a decentralised communication protocol, putting the user back in control. Currently NeoChat implements large part of the protocol with the exception of encrypted chats and video chat.</p>
|
<p xml:lang="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="es">Matrix es un protocolo de comunicaciones descentralizado, que devuelve el control al usuario. En la actualidad, NeoChat implementa gran parte del protocolo con la excepción de chats cifrados y chats de vídeo.</p>
|
<p xml:lang="tr">NeoChat, Matrix belirtimi için tam özellikli bir uygulama olmayı hedefler. Bu nedenle; VoIP, ileti zincirleri ve Uçtan Uca Şifreleme'nin bazı yönleri gibi dikkate değer istisnalar dışında var olan kararlı belirtimdeki her şey desteklenir. Matrix belirtiminin sürekli gelişmesi nedeniyle birkaç küçük eksiklik daha var; ancak amaç tüm belirtim için nihai destek sağlamak olmayı sürdürüyor.</p>
|
||||||
<p xml:lang="eu">Matrix komunikazio-protokolo deszentralizatu bat da, erabiltzaileari kontrola itzultzen diona. Gaur egun, NeoChat-ek protokoloaren zati handi bat inplementatzen du, berriketa zifratuak eta bideo berriketak izan ezik.</p>
|
<p xml:lang="uk">Метою створення NeoChat є повноцінна реалізація програми для специфікації Matrix. Як наслідок, реалізовано усе у поточній стабільній специфікації, окрім голосового інтернет-зв'язку, потоків та деяких аспектів міжвузлового шифрування. Є також декілька інших незначних прогалин через те, що специфікація Matrix постійно змінюється, але метою лишається повна підтримка специфікації.</p>
|
||||||
<p xml:lang="fi">Matrix on hajautettu viestintäyhteyskäytäntö, joka antaa hallinnan takaisin käyttäjille. NeoChat tarjoaa nykyisellään valtaosan yhteyskäytännöstä salattuja keskustelu- ja videokeskusteluja lukuun ottamatta.</p>
|
<p xml:lang="x-test">xxNeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.xx</p>
|
||||||
<p xml:lang="fr">Matrix est un protocole de communication décentralisé, donnant le contrôle à l'utilisateur. Actuellement, NeoChat met en œuvre une grande partie du protocole, à l'exception des discussions chiffrées et du chat vidéo.</p>
|
<p>Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:</p>
|
||||||
<p xml:lang="hu">A Matrix egy decentralizált kommunikációs protokoll, amely a felhasználók kezébe adja az irányítást.</p>
|
<p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يدعم نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p>
|
||||||
<p xml:lang="ia">Matrix es un protocollo de communication decentrate, ponente le usator in le controlo. Currentemente NeoChat implementa un grande parte del protocollo con le exception de conversationes cryptate e conversationes video.</p>
|
<p xml:lang="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="id">Matrix adalah protokol komunikasi terdesentralisasi, menempatkan pengguna kembali dalam kendali. Saat ini NeoChat mengimplementasikan sebagian besar protokol dengan pengecualian obrolan terenkripsi dan obrolan video.</p>
|
<p xml:lang="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="ie">Matrix es un decentralisat protocol de communication quel retroda li control a un usator. Actualmen, NeoChat implementa un grand parte del protocol except ciffrat conversationes e videotelefonada.</p>
|
<p xml:lang="en-GB">Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:</p>
|
||||||
<p xml:lang="it">Matrix è un protocollo di comunicazione decentralizzato, che restituisce all'utente il controllo. Attualmente NeoChat implementa gran parte del protocollo ad eccezione delle chat cifrate e delle chat video.</p>
|
<p xml:lang="es">Debido a la naturaleza del desarrollo de la especificación de Matrix, NeoChat también permite numerosas funciones no estables, como:</p>
|
||||||
<p xml:lang="ka">Matrix -ი დეცენტრალიზებული კომუნიკაციის პროტოკოლია, რომელიც მომხმარებელს კონტროლის უფლებას უბრუნებს. ამჟამად NeoChat-ს პროტოკოლის ძირითადი ნაწილის მხარდაჭერა გააჩნია, დაშიფვრისა და ვიდეოჩატის გამოკლებით.</p>
|
<p xml:lang="fi">Matrix-määritelmän kehittyessä NeoChat tukee myös monia epävakaita ominaisuuksia. Tällä hetkellä näitä ovat:</p>
|
||||||
<p xml:lang="ko">Matrix는 사용자에게 제어권을 돌려 주는 분산 통신 프로토콜입니다. NeoChat은 암호화된 대화 및 영상 통화를 제외한 프로토콜의 대부분 기능을 구현합니다.</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="nl">Matrix is een gedecentraliseerd communicatieprotocol, dat de gebruiker de controle teruggeeft. Op dit moment implementeert NeoChat grote delen van het protocol met de uitzondering van versleutelde chats en video-chat.</p>
|
<p xml:lang="gl">Debido á natureza do desenvolvemento da especificación de Matrix, NeoChat tamén inclúe varias funcionalidades non estábeis:</p>
|
||||||
<p xml:lang="pl">Matrix jest protokołem rozproszonego porozumiewania się oddający użytkownikowi jego władzę. Obecnie NeoChat obsługuje dużą część protokołu poza szyfrowanymi rozmowami tekstowymi i z obrazem.</p>
|
<p xml:lang="ia">Debite al natura del disveloppamento de specification de Matrix NeoChat tamben supporta numerose characteristicas instabile. Currentemente istes es:</p>
|
||||||
<p xml:lang="pt">O Matrix é um protocolo de comunicações descentralizado, colocando de novo o utilizador no poder. De momento, o NeoChat implementa uma boa parte do protocolo, com a excepção das conversas encriptadas e as conversas de vídeo.</p>
|
<p xml:lang="it">A causa della natura dello sviluppo delle specifiche Matrix, NeoChat supporta anche numerose funzionalità instabili. Attualmente queste sono:</p>
|
||||||
<p xml:lang="pt-BR">O Matrix é um protocolo de comunicação descentralizado, colocando o usuário de volta no controle. Atualmente o NeoChat implementa grande parte do protocolo com a exceção de bate-papos criptografados e bate-papo por vídeo.</p>
|
<p xml:lang="ka">Matrix-ის სპეციფიკაციის განვითარების ბუნების გამო NeoChat-ს ასევე აქვს უამრავი არასტაბილური ფუნქციაც. ახლა ისინია:</p>
|
||||||
<p xml:lang="sk">Matrix je decentralizovaný komunikačný protokol, ktorý používateľovi vracia kontrolu. V súčasnosti NeoChat implementuje veľkú časť protokolu s výnimkou šifrovaných chatov a videohovorov.</p>
|
<p xml:lang="nl">Vanwege de aard van de ontwikkeling van de Matrix specificatie ondersteunt NeoChat ook talloze onstabiele mogelijkheden. Dit zijn nu:</p>
|
||||||
<p xml:lang="sl">Matrix je decentraliziran komunikacijski protokol, kjer ima uporabnik uporabnik kontrolo rabe. Trenutno ima NeoChat izveden velik del protokola z izjemo šifriranih klepetov in video klepetov.</p>
|
<p xml:lang="nn">På grunn av måten Matrix-spesifikasjonen vert utvikla på, støttar NeoChat òg nokre uferdige funksjonar:</p>
|
||||||
<p xml:lang="sv">Matrix är ett decentraliserat kommunikationsprotokoll, som ger tillbaka kontrollen till användaren. För närvarande implementerar NeoChat en stor del av protokollet, med undantag för krypterad chatt och videochatt.</p>
|
<p xml:lang="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="tr">Matrix; tam denetimi kullanıcıya bırakan, merkezi olmayan bir iletişim protokolüdür. Şu anda NeoChat, uçtan uca şifrelenmiş metin ve video sohbetleri dışında protokolün büyük bir bölümünü bünyesinde bulundurur.</p>
|
<p xml:lang="sl">Zaradi narave razvoja specifikacije Matrixa NeoChat podpira tudi številne nestabilne zmožnosti. Trenutno so to:</p>
|
||||||
<p xml:lang="uk">Matrix — протокол децентралізованого спілкування, який передає контроль над даними користувачеві. У поточній версії NeoChat реалізовано більшу частину протоколу, окрім зашифрованого спілкування та відеоспілкування.</p>
|
<p xml:lang="tr">NeoChat, Matrix belirtimi geliştirmesinin doğası gereği çok sayıda kararsız özelliği de destekler. Şu anda bunlar:</p>
|
||||||
<p xml:lang="x-test">xxMatrix is a decentralized communication protocol, putting the user back in control. Currently NeoChat implements large part of the protocol with the exception of encrypted chats and video chat.xx</p>
|
<p xml:lang="uk">Через природу розробки специфікації Matrix, у NeoChat також передбачено підтримку численних нестабільних можливостей. У поточній версії цими можливостями є:</p>
|
||||||
<p xml:lang="zh-CN">Matrix 是一个分布式通讯协议,使用户重新得到控制权。 目前,NeoChat 实现了协议的大部分,除了加密聊天和视频聊天。</p>
|
<p xml:lang="x-test">xxDue to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:xx</p>
|
||||||
<p>NeoChat works both on mobile and desktop while providing a consistent user experience.</p>
|
<ul>>
|
||||||
<p xml:lang="ar">يعمل نيوتشات على كل من الأجهزة المحمولة وسطح المكتب مع توفير تجربة مستخدم متسقة.</p>
|
</ul>
|
||||||
<p xml:lang="az">Vahid istifadəçi interfeysi ilə təmin olunan NeoChat, həm mobil telefonda həm də kompyuterlərdə işləyir.</p>
|
<ul xml:lang="ca">></ul>
|
||||||
<p xml:lang="ca">El NeoChat funciona en els mòbils i a l'escriptori, proporcionant una experiència d'usuari coherent.</p>
|
<ul xml:lang="ca-valencia">></ul>
|
||||||
<p xml:lang="ca-valencia">NeoChat funciona en els mòbils i en l'escriptori, proporcionant una experiència d'usuari coherent.</p>
|
<ul xml:lang="cs">></ul>
|
||||||
<p xml:lang="de">NeoChat funktioniert sowohl auf dem Mobiltelefon als auch auf dem Arbeitsfläche und bietet ein einheitliches Benutzererlebnis. </p>
|
<ul xml:lang="en-GB">></ul>
|
||||||
<p xml:lang="en-GB">NeoChat works both on mobile and desktop while providing a consistent user experience.</p>
|
<ul xml:lang="es">></ul>
|
||||||
<p xml:lang="es">NeoChat funciona en móviles y en el escritorio a la vez que proporciona una experiencia de usuario consistente.</p>
|
<ul xml:lang="fi">></ul>
|
||||||
<p xml:lang="eu">NeoChat mugikorretan eta mahaigainean dabil, erabiltzaile esperientzia koherentea eskainiz.</p>
|
<ul xml:lang="fr">></ul>
|
||||||
<p xml:lang="fi">NeoChat toimii sekä mobiili- että työpöytäalustoilla tarjoten yhdenmukaisen käyttökokemuksen.</p>
|
<ul xml:lang="gl">></ul>
|
||||||
<p xml:lang="fr">NeoChat fonctionne aussi bien sur les mobiles que sur les ordinateurs de bureau, tout en offrant une expérience utilisateur cohérente.</p>
|
<ul xml:lang="ia">></ul>
|
||||||
<p xml:lang="hu">A NeoChat mobilon és asztali számítógépen is működik, egységes felhasználói élményt nyújtva.</p>
|
<ul xml:lang="it">></ul>
|
||||||
<p xml:lang="ia">NeoChat functiona sia sur mobile que ur scriptorio durante que forni un experientia de usator consistente.</p>
|
<ul xml:lang="ka">></ul>
|
||||||
<p xml:lang="id">NeoChat berfungsi baik di ponsel dan desktop sambil memberikan pengalaman pengguna yang konsisten.</p>
|
<ul xml:lang="nl">></ul>
|
||||||
<p xml:lang="ie">NeoChat supporta amb computatores e aparates mobil, ancor providente un consistent experientie de usator.</p>
|
<ul xml:lang="nn">></ul>
|
||||||
<p xml:lang="it">NeoChat funziona sia su dispositivi mobili che desktop, fornendo un'esperienza utente coerente.</p>
|
<ul xml:lang="pl">></ul>
|
||||||
<p xml:lang="ka">NeoChat მუშაობს როგორც მობილურ, ისე ჩვეულებრივ კომპიუტერებზე იმ დროს, როცა მომხმარებლის ინტერფეისი არ იცვლება.</p>
|
<ul xml:lang="pt-BR">></ul>
|
||||||
<p xml:lang="ko">NeoChat은 모바일과 데스크톱 모두에서 일관된 사용자 경험을 제공합니다.</p>
|
<ul xml:lang="sl">></ul>
|
||||||
<p xml:lang="nl">NeoChat werkt zowel op de mobiel en het bureaublad met het leveren van een consistente gebruikerservaring.</p>
|
<ul xml:lang="uk">></ul>
|
||||||
<p xml:lang="pl">NeoChat działa zarówno na urządzeniach przenośnych jak i biurkowych, zapewniając spójne wrażenia użytkownika</p>
|
<ul xml:lang="x-test">xx>xx</ul>
|
||||||
<p xml:lang="pt">O NeoChat funciona tanto em dispositivos móveis como no computador, fornecendo uma experiência de utilizador consistente.</p>
|
|
||||||
<p xml:lang="pt-BR">O NeoChat funciona tanto no celular como no computador enquanto fornece uma experiência consistente ao usuário.</p>
|
|
||||||
<p xml:lang="sk">NeoChat funguje na mobilných aj stolových počítačoch a poskytuje konzistentný používateľský zážitok.</p>
|
|
||||||
<p xml:lang="sl">NeoChat deluje tako na mobilnih kot na namiznih platformah z zagotavljanjem konsistentne uporabniške izkušnje.</p>
|
|
||||||
<p xml:lang="sv">NeoChat fungerar både på mobil och skrivbord och tillhandahåller en konsekvent användarupplevelse.</p>
|
|
||||||
<p xml:lang="tr">NeoChat, hem masaüstü hem de taşınabilir ortamlarda çalışarak tutarlı bir kullanıcı deneyimi sunar.</p>
|
|
||||||
<p xml:lang="uk">NeoChat працює на мобільних пристроях та звичайних комп'ютерах, маючи однорідний інтерфейс на усіх підтримуваних пристроях.</p>
|
|
||||||
<p xml:lang="x-test">xxNeoChat works both on mobile and desktop while providing a consistent user experience.xx</p>
|
|
||||||
<p xml:lang="zh-CN">NeoChat 在移动设备和桌面上均可用,并提供一致的用户体验。</p>
|
|
||||||
</description>
|
</description>
|
||||||
<url type="homepage">https://apps.kde.org/neochat/</url>
|
<url type="homepage">https://apps.kde.org/neochat/</url>
|
||||||
<url type="bugtracker">https://bugs.kde.org/buglist.cgi?component=General&product=NeoChat</url>
|
<url type="bugtracker">https://bugs.kde.org/buglist.cgi?component=General&product=NeoChat</url>
|
||||||
@@ -169,17 +167,19 @@
|
|||||||
<category>Network</category>
|
<category>Network</category>
|
||||||
</categories>
|
</categories>
|
||||||
<developer_name>The KDE Community</developer_name>
|
<developer_name>The KDE Community</developer_name>
|
||||||
<developer_name xml:lang="ar">مجتمع كدي</developer_name>
|
<developer_name xml:lang="ar">مجتمع كِيدِي</developer_name>
|
||||||
<developer_name xml:lang="az">KDE Cəmiyyəti</developer_name>
|
<developer_name xml:lang="az">KDE Cəmiyyəti</developer_name>
|
||||||
<developer_name xml:lang="ca">La comunitat KDE</developer_name>
|
<developer_name xml:lang="ca">La comunitat KDE</developer_name>
|
||||||
<developer_name xml:lang="ca-valencia">La comunitat KDE</developer_name>
|
<developer_name xml:lang="ca-valencia">La comunitat KDE</developer_name>
|
||||||
<developer_name xml:lang="cs">Komunita KDE</developer_name>
|
<developer_name xml:lang="cs">Komunita KDE</developer_name>
|
||||||
<developer_name xml:lang="de">Die KDE-Gemeinschaft</developer_name>
|
<developer_name xml:lang="de">Die KDE-Gemeinschaft</developer_name>
|
||||||
|
<developer_name xml:lang="el">Η Κοινότητα του KDE</developer_name>
|
||||||
<developer_name xml:lang="en-GB">The KDE Community</developer_name>
|
<developer_name xml:lang="en-GB">The KDE Community</developer_name>
|
||||||
<developer_name xml:lang="es">La comunidad KDE</developer_name>
|
<developer_name xml:lang="es">La comunidad KDE</developer_name>
|
||||||
<developer_name xml:lang="eu">KDE komunitatea</developer_name>
|
<developer_name xml:lang="eu">KDE komunitatea</developer_name>
|
||||||
<developer_name xml:lang="fi">KDE-yhteisö</developer_name>
|
<developer_name xml:lang="fi">KDE-yhteisö</developer_name>
|
||||||
<developer_name xml:lang="fr">La communauté de KDE</developer_name>
|
<developer_name xml:lang="fr">La communauté de KDE</developer_name>
|
||||||
|
<developer_name xml:lang="gl">A comunidade KDE</developer_name>
|
||||||
<developer_name xml:lang="hu">A KDE Közösség</developer_name>
|
<developer_name xml:lang="hu">A KDE Közösség</developer_name>
|
||||||
<developer_name xml:lang="ia">Le communitate de KDE</developer_name>
|
<developer_name xml:lang="ia">Le communitate de KDE</developer_name>
|
||||||
<developer_name xml:lang="id">Komunitas KDE</developer_name>
|
<developer_name xml:lang="id">Komunitas KDE</developer_name>
|
||||||
@@ -193,9 +193,11 @@
|
|||||||
<developer_name xml:lang="pl">Społeczność KDE</developer_name>
|
<developer_name xml:lang="pl">Społeczność KDE</developer_name>
|
||||||
<developer_name xml:lang="pt">A Comunidade do KDE</developer_name>
|
<developer_name xml:lang="pt">A Comunidade do KDE</developer_name>
|
||||||
<developer_name xml:lang="pt-BR">A comunidade KDE</developer_name>
|
<developer_name xml:lang="pt-BR">A comunidade KDE</developer_name>
|
||||||
|
<developer_name xml:lang="ru">Сообщество KDE</developer_name>
|
||||||
<developer_name xml:lang="sk">KDE Komunita</developer_name>
|
<developer_name xml:lang="sk">KDE Komunita</developer_name>
|
||||||
<developer_name xml:lang="sl">Skupnost KDE</developer_name>
|
<developer_name xml:lang="sl">Skupnost KDE</developer_name>
|
||||||
<developer_name xml:lang="sv">KDE-gemenskapen</developer_name>
|
<developer_name xml:lang="sv">KDE-gemenskapen</developer_name>
|
||||||
|
<developer_name xml:lang="ta">கே.டீ.யீ. சமூகம்</developer_name>
|
||||||
<developer_name xml:lang="tr">KDE Topluluğu</developer_name>
|
<developer_name xml:lang="tr">KDE Topluluğu</developer_name>
|
||||||
<developer_name xml:lang="uk">Спільнота KDE</developer_name>
|
<developer_name xml:lang="uk">Спільнота KDE</developer_name>
|
||||||
<developer_name xml:lang="x-test">xxThe KDE Communityxx</developer_name>
|
<developer_name xml:lang="x-test">xxThe KDE Communityxx</developer_name>
|
||||||
@@ -204,6 +206,7 @@
|
|||||||
<project_license>GPL-3.0</project_license>
|
<project_license>GPL-3.0</project_license>
|
||||||
<custom>
|
<custom>
|
||||||
<value key="KDE::matrix">#neochat:kde.org</value>
|
<value key="KDE::matrix">#neochat:kde.org</value>
|
||||||
|
<value key="KDE::windows_store">https://www.microsoft.com/store/apps/9PNXWVNRC29H</value>
|
||||||
</custom>
|
</custom>
|
||||||
<launchable type="desktop-id">org.kde.neochat.desktop</launchable>
|
<launchable type="desktop-id">org.kde.neochat.desktop</launchable>
|
||||||
<screenshots>
|
<screenshots>
|
||||||
@@ -218,6 +221,27 @@
|
|||||||
<content_attribute id="social-chat">intense</content_attribute>
|
<content_attribute id="social-chat">intense</content_attribute>
|
||||||
</content_rating>
|
</content_rating>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="23.04.3" date="2023-07-06"/>
|
||||||
|
<release version="23.04.2" date="2023-06-08"/>
|
||||||
|
<release version="23.04.1" date="2023-05-11"/>
|
||||||
|
<release version="23.04.0" date="2023-04-20"/>
|
||||||
|
<release version="23.01" date="2023-01-30">
|
||||||
|
<url>https://plasma-mobile.org/2023/01/30/january-blog-post/</url>
|
||||||
|
<description>
|
||||||
|
<p>New features and bugfixes:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Notifications will now be shown for all accounts, not just the active one</li>
|
||||||
|
<li>There is a new "compact" mode for the room list</li>
|
||||||
|
<li>You can now search in the room history</li>
|
||||||
|
<li>Emojis and Reactions have been significantly improved</li>
|
||||||
|
<li>Fixed several crashes around user invitations</li>
|
||||||
|
<li>Room permission settings can now be configured</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
|
<release version="22.11" date="2022-11-30">
|
||||||
|
<url>https://plasma-mobile.org/2022/11/30/plasma-mobile-gear-22-11/</url>
|
||||||
|
</release>
|
||||||
<release version="22.09" date="2022-09-27">
|
<release version="22.09" date="2022-09-27">
|
||||||
<url>https://www.plasma-mobile.org/2022/09/27/plasma-mobile-gear-22-09/</url>
|
<url>https://www.plasma-mobile.org/2022/09/27/plasma-mobile-gear-22-09/</url>
|
||||||
</release>
|
</release>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# SPDX-License-Identifier: CC0-1.0
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
# SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
|
# SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Version=1.5
|
Version=1.5
|
||||||
Name=NeoChat
|
Name=NeoChat
|
||||||
@@ -9,11 +9,13 @@ Name[ca]=NeoChat
|
|||||||
Name[ca@valencia]=NeoChat
|
Name[ca@valencia]=NeoChat
|
||||||
Name[cs]=NeoChat
|
Name[cs]=NeoChat
|
||||||
Name[de]=NeoChat
|
Name[de]=NeoChat
|
||||||
|
Name[el]=NeoChat
|
||||||
Name[en_GB]=NeoChat
|
Name[en_GB]=NeoChat
|
||||||
Name[es]=NeoChat
|
Name[es]=NeoChat
|
||||||
Name[eu]=NeoChat
|
Name[eu]=NeoChat
|
||||||
Name[fi]=NeoChat
|
Name[fi]=NeoChat
|
||||||
Name[fr]=NeoChat
|
Name[fr]=NeoChat
|
||||||
|
Name[gl]=NeoChat
|
||||||
Name[hu]=NeoChat
|
Name[hu]=NeoChat
|
||||||
Name[ia]=Neochat
|
Name[ia]=Neochat
|
||||||
Name[id]=NeoChat
|
Name[id]=NeoChat
|
||||||
@@ -29,6 +31,7 @@ Name[pl]=NeoChat
|
|||||||
Name[pt]=NeoChat
|
Name[pt]=NeoChat
|
||||||
Name[pt_BR]=NeoChat
|
Name[pt_BR]=NeoChat
|
||||||
Name[ro]=NeoChat
|
Name[ro]=NeoChat
|
||||||
|
Name[ru]=NeoChat
|
||||||
Name[sk]=NeoChat
|
Name[sk]=NeoChat
|
||||||
Name[sl]=NeoChat
|
Name[sl]=NeoChat
|
||||||
Name[sv]=NeoChat
|
Name[sv]=NeoChat
|
||||||
@@ -44,11 +47,13 @@ GenericName[ca]=Client de Matrix
|
|||||||
GenericName[ca@valencia]=Client de Matrix
|
GenericName[ca@valencia]=Client de Matrix
|
||||||
GenericName[cs]=Klient protokolu Matrix
|
GenericName[cs]=Klient protokolu Matrix
|
||||||
GenericName[de]=Matrix-Programm
|
GenericName[de]=Matrix-Programm
|
||||||
|
GenericName[el]=Εφαρμογή του Matrix
|
||||||
GenericName[en_GB]=Matrix Client
|
GenericName[en_GB]=Matrix Client
|
||||||
GenericName[es]=Cliente para Matrix
|
GenericName[es]=Cliente para Matrix
|
||||||
GenericName[eu]=Matrix bezeroa
|
GenericName[eu]=Matrix bezeroa
|
||||||
GenericName[fi]=Matrix-asiakas
|
GenericName[fi]=Matrix-asiakas
|
||||||
GenericName[fr]=Client « Matrix »
|
GenericName[fr]=Client « Matrix »
|
||||||
|
GenericName[gl]=Cliente de Matrix
|
||||||
GenericName[hu]=Matrix kliens
|
GenericName[hu]=Matrix kliens
|
||||||
GenericName[ia]=Cliente de Matrice
|
GenericName[ia]=Cliente de Matrice
|
||||||
GenericName[id]=Klien Matrix
|
GenericName[id]=Klien Matrix
|
||||||
@@ -64,6 +69,7 @@ GenericName[pl]=Program Matriksa
|
|||||||
GenericName[pt]=Cliente de Matrix
|
GenericName[pt]=Cliente de Matrix
|
||||||
GenericName[pt_BR]=Cliente Matrix
|
GenericName[pt_BR]=Cliente Matrix
|
||||||
GenericName[ro]=Client Matrix
|
GenericName[ro]=Client Matrix
|
||||||
|
GenericName[ru]=Клиент Matrix
|
||||||
GenericName[sk]=Matrix Client
|
GenericName[sk]=Matrix Client
|
||||||
GenericName[sl]=Odjemalec Matrix
|
GenericName[sl]=Odjemalec Matrix
|
||||||
GenericName[sv]=Matrix-klient
|
GenericName[sv]=Matrix-klient
|
||||||
@@ -78,11 +84,13 @@ Comment[az]=Matrix protokolu üçün müştəri
|
|||||||
Comment[ca]=Client per al protocol Matrix
|
Comment[ca]=Client per al protocol Matrix
|
||||||
Comment[ca@valencia]=Client per al protocol Matrix
|
Comment[ca@valencia]=Client per al protocol Matrix
|
||||||
Comment[de]=Programm für das Matrix-Protokoll
|
Comment[de]=Programm für das Matrix-Protokoll
|
||||||
|
Comment[el]=Πελάτης για το πρωτόκολλο Matrix
|
||||||
Comment[en_GB]=Client for the Matrix protocol
|
Comment[en_GB]=Client for the Matrix protocol
|
||||||
Comment[es]=Cliente para el protocolo Matrix
|
Comment[es]=Cliente para el protocolo Matrix
|
||||||
Comment[eu]=Matrix protokolorako bezeroa
|
Comment[eu]=Matrix protokolorako bezeroa
|
||||||
Comment[fi]=Asiakas Matrix-yhteyskäytännölle
|
Comment[fi]=Asiakas Matrix-yhteyskäytännölle
|
||||||
Comment[fr]=Client pour le protocole « Matrix »
|
Comment[fr]=Client pour le protocole « Matrix »
|
||||||
|
Comment[gl]=Cliente para o protocolo Matrix
|
||||||
Comment[hu]=Kliens a Matrix protokollhoz
|
Comment[hu]=Kliens a Matrix protokollhoz
|
||||||
Comment[ia]=Cliente per le protocollo de Matrix
|
Comment[ia]=Cliente per le protocollo de Matrix
|
||||||
Comment[id]=Klien untuk protokol Matrix
|
Comment[id]=Klien untuk protokol Matrix
|
||||||
@@ -92,12 +100,13 @@ Comment[ka]=კლიენტი Matrix-ის პროტოკოლის
|
|||||||
Comment[ko]=Matrix 프로토콜용 클라이언트
|
Comment[ko]=Matrix 프로토콜용 클라이언트
|
||||||
Comment[lt]=Matrix protokolo kliento programa
|
Comment[lt]=Matrix protokolo kliento programa
|
||||||
Comment[nl]=Client voor het Matrix-protocol
|
Comment[nl]=Client voor het Matrix-protocol
|
||||||
Comment[nn]=Lynmeldingsklient for Matrix-protokollen
|
Comment[nn]=Klient for Matrix-protokollen
|
||||||
Comment[pa]=ਮੈਟਰਿਕਸ ਪਰੋਟੋਕਾਲ ਲਈ ਕਲਾਈਂਟ ਹੈ
|
Comment[pa]=ਮੈਟਰਿਕਸ ਪਰੋਟੋਕਾਲ ਲਈ ਕਲਾਈਂਟ ਹੈ
|
||||||
Comment[pl]=Program obsługi protokołu Matriksa
|
Comment[pl]=Program obsługi protokołu Matriksa
|
||||||
Comment[pt]=Cliente para o protocolo Matrix
|
Comment[pt]=Cliente para o protocolo Matrix
|
||||||
Comment[pt_BR]=Cliente para o protocolo Matrix
|
Comment[pt_BR]=Cliente para o protocolo Matrix
|
||||||
Comment[ro]=Client pentru protocolul Matrix
|
Comment[ro]=Client pentru protocolul Matrix
|
||||||
|
Comment[ru]=Клиент для протокола Matrix
|
||||||
Comment[sk]=Klient protokolu Matrix
|
Comment[sk]=Klient protokolu Matrix
|
||||||
Comment[sl]=Odjemalec za protokol Matrix
|
Comment[sl]=Odjemalec za protokol Matrix
|
||||||
Comment[sv]=Klient för protokollet Matrix
|
Comment[sv]=Klient för protokollet Matrix
|
||||||
|
|||||||
2386
po/ar/neochat.po
2386
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
2577
po/az/neochat.po
2577
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
2438
po/ca/neochat.po
2438
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2726
po/cs/neochat.po
2726
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
2350
po/da/neochat.po
2350
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
3091
po/de/neochat.po
3091
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
3921
po/el/neochat.po
Normal file
3921
po/el/neochat.po
Normal file
File diff suppressed because it is too large
Load Diff
2965
po/en_GB/neochat.po
2965
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
2406
po/es/neochat.po
2406
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
2400
po/eu/neochat.po
2400
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
3018
po/fi/neochat.po
3018
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
2552
po/fr/neochat.po
2552
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
2574
po/hu/neochat.po
2574
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
2933
po/ia/neochat.po
2933
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
2521
po/id/neochat.po
2521
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
2434
po/ie/neochat.po
2434
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
122
po/it/docs/neochat/man-neochat.1.docbook
Normal file
122
po/it/docs/neochat/man-neochat.1.docbook
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
<!DOCTYPE refentry PUBLIC "-//KDE//DTD DocBook XML V4.5-Based Variant V1.1//EN" "dtd/kdedbx45.dtd" [
|
||||||
|
<!ENTITY % Italian "INCLUDE">
|
||||||
|
]>
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
<refentry lang="&language;">
|
||||||
|
<refentryinfo>
|
||||||
|
<title
|
||||||
|
>Manuale utente di NeoChat</title>
|
||||||
|
<author
|
||||||
|
><firstname
|
||||||
|
>Carl</firstname
|
||||||
|
><surname
|
||||||
|
>Schwan</surname
|
||||||
|
> <contrib
|
||||||
|
>Pagina man di NeoChat.</contrib
|
||||||
|
> <email
|
||||||
|
>carl@carlschwan.eu</email
|
||||||
|
></author>
|
||||||
|
<date
|
||||||
|
>1/11/2022</date>
|
||||||
|
<releaseinfo
|
||||||
|
>22.09</releaseinfo>
|
||||||
|
<productname
|
||||||
|
>NeoChat</productname>
|
||||||
|
</refentryinfo>
|
||||||
|
|
||||||
|
<refmeta>
|
||||||
|
<refentrytitle>
|
||||||
|
<command
|
||||||
|
>neochat</command>
|
||||||
|
</refentrytitle>
|
||||||
|
<manvolnum
|
||||||
|
>1</manvolnum>
|
||||||
|
</refmeta>
|
||||||
|
|
||||||
|
<refnamediv>
|
||||||
|
<refname
|
||||||
|
>neochat</refname>
|
||||||
|
<refpurpose
|
||||||
|
>Client per l'interazione con il protocollo di messaggistica matrix</refpurpose>
|
||||||
|
</refnamediv>
|
||||||
|
<!-- body begins here -->
|
||||||
|
<refsynopsisdiv id='synopsis'>
|
||||||
|
<cmdsynopsis
|
||||||
|
><command
|
||||||
|
>neochat</command
|
||||||
|
> <arg choice="opt"
|
||||||
|
><replaceable
|
||||||
|
>URI</replaceable
|
||||||
|
></arg
|
||||||
|
> </cmdsynopsis>
|
||||||
|
</refsynopsisdiv>
|
||||||
|
|
||||||
|
|
||||||
|
<refsect1 id="description">
|
||||||
|
<title
|
||||||
|
>Descrizione</title>
|
||||||
|
<para
|
||||||
|
><command
|
||||||
|
>neochat</command
|
||||||
|
> è un'applicazione di chat per il protocollo Matrix che funziona sia su desktop che su dispositivi mobili. </para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1 id="options"
|
||||||
|
><title
|
||||||
|
>Opzioni</title>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term
|
||||||
|
><option
|
||||||
|
>URI</option
|
||||||
|
></term>
|
||||||
|
<listitem>
|
||||||
|
<para
|
||||||
|
>L'URI matrix per un utente o una stanza. ad esempio matrix:u/utente:esempio.org e matrix:r/stanza:esempio.org. Questo farà in modo che NeoChat provi ad aprire la stanza o la conversazione specificata. </para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1 id="bug">
|
||||||
|
<title
|
||||||
|
>Segnalazione bug</title>
|
||||||
|
<para
|
||||||
|
>Puoi segnalare bug e richiedere funzionalità su <ulink url="https://bugs.kde.org/enter_bug.cgi?product=NeoChat&component=General"
|
||||||
|
>https://bugs.kde.org/enter_bug.cgi? product=NeoChat&component=General</ulink
|
||||||
|
></para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title
|
||||||
|
>Vedi anche</title>
|
||||||
|
<simplelist>
|
||||||
|
<member
|
||||||
|
>Un elenco di domande frequenti su Matrix <ulink url="https://matrix.org/faq/"
|
||||||
|
>https://matrix.org/faq/</ulink
|
||||||
|
> </member>
|
||||||
|
<member
|
||||||
|
>kf5options(7)</member>
|
||||||
|
<member
|
||||||
|
>qt5options(7)</member>
|
||||||
|
</simplelist>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1 id="copyright"
|
||||||
|
><title
|
||||||
|
>Copyright</title>
|
||||||
|
<para
|
||||||
|
>Copyright © 2020-2022 Tobias Fella </para>
|
||||||
|
<para
|
||||||
|
>Copyright © 2020-2022 Carl Schwan </para>
|
||||||
|
<para
|
||||||
|
>Licenza: GNU General Public Version 3 o successiva <<ulink url="https://www.gnu.org/licenses/gpl-3.0.html"
|
||||||
|
>https://www.gnu.org/licenses/gpl-3.0.html</ulink
|
||||||
|
>></para>
|
||||||
|
</refsect1>
|
||||||
|
</refentry>
|
||||||
2752
po/it/neochat.po
2752
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
2311
po/ja/neochat.po
2311
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
2544
po/ka/neochat.po
2544
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
2550
po/ko/neochat.po
2550
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
3789
po/lt/neochat.po
Normal file
3789
po/lt/neochat.po
Normal file
File diff suppressed because it is too large
Load Diff
2390
po/nl/neochat.po
2390
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
2975
po/nn/neochat.po
2975
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
2536
po/pa/neochat.po
2536
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
2927
po/pl/neochat.po
2927
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
2503
po/pt/neochat.po
2503
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
2606
po/pt_BR/neochat.po
2606
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
3561
po/ru/neochat.po
3561
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
2581
po/sk/neochat.po
2581
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
2434
po/sl/neochat.po
2434
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
2559
po/sv/neochat.po
2559
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
2537
po/ta/neochat.po
2537
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
2409
po/tok/neochat.po
2409
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
5
po/tr/docs/neochat/CMakeLists.txt
Normal file
5
po/tr/docs/neochat/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
kdoctools_create_manpage(man-neochat.1.docbook 1 INSTALL_DESTINATION ${MAN_INSTALL_DIR})
|
||||||
122
po/tr/docs/neochat/man-neochat.1.docbook
Normal file
122
po/tr/docs/neochat/man-neochat.1.docbook
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
<!DOCTYPE refentry PUBLIC "-//KDE//DTD DocBook XML V4.5-Based Variant V1.1//EN" "dtd/kdedbx45.dtd" [
|
||||||
|
<!ENTITY % Turkish "INCLUDE">
|
||||||
|
]>
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
<refentry lang="&language;">
|
||||||
|
<refentryinfo>
|
||||||
|
<title
|
||||||
|
>NeoChat Kullanıcı Kılavuzu</title>
|
||||||
|
<author
|
||||||
|
><firstname
|
||||||
|
>Carl</firstname
|
||||||
|
><surname
|
||||||
|
>Schwan</surname
|
||||||
|
> <contrib
|
||||||
|
>NeoChat man page.</contrib
|
||||||
|
> <email
|
||||||
|
>carl@carlschwan.eu</email
|
||||||
|
></author>
|
||||||
|
<date
|
||||||
|
>2022-11-01</date>
|
||||||
|
<releaseinfo
|
||||||
|
>22.09</releaseinfo>
|
||||||
|
<productname
|
||||||
|
>NeoChat</productname>
|
||||||
|
</refentryinfo>
|
||||||
|
|
||||||
|
<refmeta>
|
||||||
|
<refentrytitle>
|
||||||
|
<command
|
||||||
|
>neochat</command>
|
||||||
|
</refentrytitle>
|
||||||
|
<manvolnum
|
||||||
|
>1</manvolnum>
|
||||||
|
</refmeta>
|
||||||
|
|
||||||
|
<refnamediv>
|
||||||
|
<refname
|
||||||
|
>neochat</refname>
|
||||||
|
<refpurpose
|
||||||
|
>Matrix iletileşme protokolü ile etkileşim için istemci</refpurpose>
|
||||||
|
</refnamediv>
|
||||||
|
<!-- body begins here -->
|
||||||
|
<refsynopsisdiv id='synopsis'>
|
||||||
|
<cmdsynopsis
|
||||||
|
><command
|
||||||
|
>neochat</command
|
||||||
|
> <arg choice="opt"
|
||||||
|
><replaceable
|
||||||
|
>URI</replaceable
|
||||||
|
></arg
|
||||||
|
> </cmdsynopsis>
|
||||||
|
</refsynopsisdiv>
|
||||||
|
|
||||||
|
|
||||||
|
<refsect1 id="description">
|
||||||
|
<title
|
||||||
|
>Açıklama</title>
|
||||||
|
<para
|
||||||
|
><command
|
||||||
|
>neochat</command
|
||||||
|
>, Matrix protokolü için hem masaüstünde hem de taşınabilir aygıtlarda çalışan bir sohbet uygulamasıdır. </para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1 id="options"
|
||||||
|
><title
|
||||||
|
>Seçenekler</title>
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term
|
||||||
|
><option
|
||||||
|
>URI</option
|
||||||
|
></term>
|
||||||
|
<listitem>
|
||||||
|
<para
|
||||||
|
>Bir kullanıcı veya oda için matrix URI'si; örneğin, matrix:u/kullanıcı:örnek.org ve matrix:r/kök:örnek.org. Bu, NeoChat'in verilen odayı veya konuşmayı açmayı denemesini sağlar. </para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1 id="bug">
|
||||||
|
<title
|
||||||
|
>Hata Bildirme</title>
|
||||||
|
<para
|
||||||
|
>Hataları veya özellik isteklerini <ulink url="https://bugs.kde.org/enter_bug.cgi?product=NeoChat&component=General"
|
||||||
|
>https://bugs.kde.org/enter_bug.cgi?product=NeoChat&component=General</ulink
|
||||||
|
> bağlantısından bildirebilirsiniz</para>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1>
|
||||||
|
<title
|
||||||
|
>Ayrıca</title>
|
||||||
|
<simplelist>
|
||||||
|
<member
|
||||||
|
>Matrix üzerine sıkça sorulan soruların bir listesi: <ulink url="https://matrix.org/faq/"
|
||||||
|
>https://matrix.org/faq/</ulink
|
||||||
|
> </member>
|
||||||
|
<member
|
||||||
|
>kf5options(7)</member>
|
||||||
|
<member
|
||||||
|
>qt5options(7)</member>
|
||||||
|
</simplelist>
|
||||||
|
</refsect1>
|
||||||
|
|
||||||
|
<refsect1 id="copyright"
|
||||||
|
><title
|
||||||
|
>Telif Hakkı</title>
|
||||||
|
<para
|
||||||
|
>Telif hakkı © 2020-2022 Tobias Fella </para>
|
||||||
|
<para
|
||||||
|
>Telif hakkı © 2020-2022 Carl Schwan </para>
|
||||||
|
<para
|
||||||
|
>Lisans: GNU Genel Kamu Lisansa, 3. sürüm veya sonrası <<ulink url="https://www.gnu.org/licenses/gpl-3.0.html"
|
||||||
|
>https://www.gnu.org/licenses/gpl-3.0.html</ulink
|
||||||
|
>></para>
|
||||||
|
</refsect1>
|
||||||
|
</refentry>
|
||||||
2403
po/tr/neochat.po
2403
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
2395
po/uk/neochat.po
2395
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
2738
po/zh_CN/neochat.po
2738
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
2320
po/zh_TW/neochat.po
2320
po/zh_TW/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -1,49 +1,52 @@
|
|||||||
# SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carl@carlschwan.eu>
|
# SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carl@carlschwan.eu>
|
||||||
# SPDX-FileCopyrightText: 2020-2021 Nicolas Fella <nicolas.fella@gmx.de>
|
# SPDX-FileCopyrightText: 2020-2021 Nicolas Fella <nicolas.fella@gmx.de>
|
||||||
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <fella@posteo.de>
|
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
||||||
# SPDX-License-Identifier: BSD-2-Clause
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
add_library(neochat STATIC
|
add_library(neochat STATIC
|
||||||
controller.cpp
|
controller.cpp
|
||||||
actionshandler.cpp
|
actionshandler.cpp
|
||||||
emojimodel.cpp
|
models/emojimodel.cpp
|
||||||
customemojimodel.cpp
|
emojitones.cpp
|
||||||
|
models/customemojimodel.cpp
|
||||||
clipboard.cpp
|
clipboard.cpp
|
||||||
matriximageprovider.cpp
|
matriximageprovider.cpp
|
||||||
messageeventmodel.cpp
|
models/messageeventmodel.cpp
|
||||||
messagefiltermodel.cpp
|
models/messagefiltermodel.cpp
|
||||||
roomlistmodel.cpp
|
models/roomlistmodel.cpp
|
||||||
sortfilterspacelistmodel.cpp
|
models/sortfilterspacelistmodel.cpp
|
||||||
spacehierarchycache.cpp
|
spacehierarchycache.cpp
|
||||||
roommanager.cpp
|
roommanager.cpp
|
||||||
neochatroom.cpp
|
neochatroom.cpp
|
||||||
neochatuser.cpp
|
neochatuser.cpp
|
||||||
userlistmodel.cpp
|
models/userlistmodel.cpp
|
||||||
publicroomlistmodel.cpp
|
models/userfiltermodel.cpp
|
||||||
userdirectorylistmodel.cpp
|
models/publicroomlistmodel.cpp
|
||||||
keywordnotificationrulemodel.cpp
|
models/userdirectorylistmodel.cpp
|
||||||
utils.cpp
|
models/keywordnotificationrulemodel.cpp
|
||||||
notificationsmanager.cpp
|
notificationsmanager.cpp
|
||||||
sortfilterroomlistmodel.cpp
|
models/sortfilterroomlistmodel.cpp
|
||||||
chatdocumenthandler.cpp
|
chatdocumenthandler.cpp
|
||||||
devicesmodel.cpp
|
models/devicesmodel.cpp
|
||||||
filetypesingleton.cpp
|
filetypesingleton.cpp
|
||||||
login.cpp
|
login.cpp
|
||||||
stickerevent.cpp
|
stickerevent.cpp
|
||||||
webshortcutmodel.cpp
|
models/webshortcutmodel.cpp
|
||||||
blurhash.cpp
|
blurhash.cpp
|
||||||
blurhashimageprovider.cpp
|
blurhashimageprovider.cpp
|
||||||
joinrulesevent.cpp
|
joinrulesevent.cpp
|
||||||
collapsestateproxymodel.cpp
|
models/collapsestateproxymodel.cpp
|
||||||
urlhelper.cpp
|
urlhelper.cpp
|
||||||
windowcontroller.cpp
|
windowcontroller.cpp
|
||||||
linkpreviewer.cpp
|
linkpreviewer.cpp
|
||||||
completionmodel.cpp
|
models/completionmodel.cpp
|
||||||
completionproxymodel.cpp
|
models/completionproxymodel.cpp
|
||||||
actionsmodel.cpp
|
models/actionsmodel.cpp
|
||||||
serverlistmodel.cpp
|
models/serverlistmodel.cpp
|
||||||
statemodel.cpp
|
models/statemodel.cpp
|
||||||
filetransferpseudojob.cpp
|
filetransferpseudojob.cpp
|
||||||
|
models/searchmodel.cpp
|
||||||
|
texthandler.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(neochat-app
|
add_executable(neochat-app
|
||||||
@@ -67,7 +70,7 @@ endif()
|
|||||||
|
|
||||||
ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
|
ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
|
||||||
|
|
||||||
target_sources(neochat PRIVATE ${NEOCHAT_ICON})
|
target_sources(neochat-app PRIVATE ${NEOCHAT_ICON})
|
||||||
|
|
||||||
if(NOT ANDROID)
|
if(NOT ANDROID)
|
||||||
target_sources(neochat PRIVATE colorschemer.cpp)
|
target_sources(neochat PRIVATE colorschemer.cpp)
|
||||||
@@ -76,7 +79,7 @@ if(NOT ANDROID)
|
|||||||
else()
|
else()
|
||||||
target_sources(neochat PRIVATE trayicon.cpp)
|
target_sources(neochat PRIVATE trayicon.cpp)
|
||||||
endif()
|
endif()
|
||||||
target_link_libraries(neochat PUBLIC KF5::ConfigWidgets KF5::WindowSystem)
|
target_link_libraries(neochat PUBLIC KF${QT_MAJOR_VERSION}::ConfigWidgets KF${QT_MAJOR_VERSION}::WindowSystem)
|
||||||
target_compile_definitions(neochat PUBLIC -DHAVE_COLORSCHEME)
|
target_compile_definitions(neochat PUBLIC -DHAVE_COLORSCHEME)
|
||||||
target_compile_definitions(neochat PUBLIC -DHAVE_WINDOWSYSTEM)
|
target_compile_definitions(neochat PUBLIC -DHAVE_WINDOWSYSTEM)
|
||||||
endif()
|
endif()
|
||||||
@@ -84,18 +87,14 @@ endif()
|
|||||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||||
target_sources(neochat-app PRIVATE res_desktop.qrc)
|
target_sources(neochat-app PRIVATE res_desktop.qrc)
|
||||||
target_compile_definitions(neochat PUBLIC -DHAVE_RUNNER)
|
target_compile_definitions(neochat PUBLIC -DHAVE_RUNNER)
|
||||||
|
target_compile_definitions(neochat PUBLIC -DHAVE_X11)
|
||||||
target_sources(neochat PRIVATE runner.cpp)
|
target_sources(neochat PRIVATE runner.cpp)
|
||||||
else()
|
else()
|
||||||
target_sources(neochat-app PRIVATE res_android.qrc)
|
target_sources(neochat-app PRIVATE res_android.qrc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR})
|
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR})
|
||||||
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF5::I18n KF5::Kirigami2 KF5::Notifications KF5::ConfigCore KF5::ConfigGui KF5::CoreAddons KF5::SonnetCore KF5::ItemModels Quotient cmark::cmark ${QTKEYCHAIN_LIBRARIES})
|
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF${QT_MAJOR_VERSION}::I18n KF${QT_MAJOR_VERSION}::Kirigami2 KF${QT_MAJOR_VERSION}::Notifications KF${QT_MAJOR_VERSION}::ConfigCore KF${QT_MAJOR_VERSION}::ConfigGui KF${QT_MAJOR_VERSION}::CoreAddons KF${QT_MAJOR_VERSION}::SonnetCore KF${QT_MAJOR_VERSION}::ItemModels Quotient cmark::cmark ${QTKEYCHAIN_LIBRARIES} QCoro::Core)
|
||||||
if(TARGET QCoro5::Coro)
|
|
||||||
target_link_libraries(neochat PUBLIC QCoro5::Coro)
|
|
||||||
else()
|
|
||||||
target_link_libraries(neochat PUBLIC QCoro::QCoro)
|
|
||||||
endif()
|
|
||||||
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||||
|
|
||||||
if(NEOCHAT_FLATPAK)
|
if(NEOCHAT_FLATPAK)
|
||||||
@@ -105,6 +104,9 @@ endif()
|
|||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
target_sources(neochat PRIVATE notifyrc.qrc)
|
target_sources(neochat PRIVATE notifyrc.qrc)
|
||||||
target_link_libraries(neochat PRIVATE Qt::Svg OpenSSL::SSL)
|
target_link_libraries(neochat PRIVATE Qt::Svg OpenSSL::SSL)
|
||||||
|
if(SQLite3_FOUND)
|
||||||
|
target_link_libraries(neochat-app PRIVATE SQLite::SQLite3)
|
||||||
|
endif()
|
||||||
target_sources(neochat-app PRIVATE notifyrc.qrc)
|
target_sources(neochat-app PRIVATE notifyrc.qrc)
|
||||||
target_link_libraries(neochat PUBLIC Qt::Svg OpenSSL::SSL)
|
target_link_libraries(neochat PUBLIC Qt::Svg OpenSSL::SSL)
|
||||||
kirigami_package_breeze_icons(ICONS
|
kirigami_package_breeze_icons(ICONS
|
||||||
@@ -161,13 +163,21 @@ if(ANDROID)
|
|||||||
"zoom-out"
|
"zoom-out"
|
||||||
"image-rotate-left-symbolic"
|
"image-rotate-left-symbolic"
|
||||||
"image-rotate-right-symbolic"
|
"image-rotate-right-symbolic"
|
||||||
"channel-insecure-symbolic"
|
"channel-secure-symbolic"
|
||||||
"download"
|
"download"
|
||||||
"smiley"
|
"smiley"
|
||||||
"tools-check-spelling"
|
"tools-check-spelling"
|
||||||
|
"username-copy"
|
||||||
|
"system-switch-user"
|
||||||
|
"bookmark-new"
|
||||||
|
"bookmark-remove"
|
||||||
|
"favorite"
|
||||||
|
"window-new"
|
||||||
|
"globe"
|
||||||
|
"visibility"
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(neochat PUBLIC Qt::Widgets KF5::KIOWidgets)
|
target_link_libraries(neochat PUBLIC Qt::Widgets KF${QT_MAJOR_VERSION}::KIOWidgets)
|
||||||
install(FILES neochat.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
|
install(FILES neochat.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -175,12 +185,12 @@ if(NOT ANDROID)
|
|||||||
set_target_properties(neochat-app PROPERTIES OUTPUT_NAME "neochat")
|
set_target_properties(neochat-app PROPERTIES OUTPUT_NAME "neochat")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(TARGET KF5::DBusAddons)
|
if(TARGET KF${QT_MAJOR_VERSION}::DBusAddons)
|
||||||
target_link_libraries(neochat PUBLIC KF5::DBusAddons)
|
target_link_libraries(neochat PUBLIC KF${QT_MAJOR_VERSION}::DBusAddons)
|
||||||
target_compile_definitions(neochat PUBLIC -DHAVE_KDBUSADDONS)
|
target_compile_definitions(neochat PUBLIC -DHAVE_KDBUSADDONS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (TARGET KF5::KIOWidgets)
|
if (TARGET KF${QT_MAJOR_VERSION}::KIOWidgets)
|
||||||
target_compile_definitions(neochat PUBLIC -DHAVE_KIO)
|
target_compile_definitions(neochat PUBLIC -DHAVE_KIO)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -13,32 +13,17 @@
|
|||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
#include <QStringBuilder>
|
#include <QStringBuilder>
|
||||||
|
|
||||||
#include "actionsmodel.h"
|
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "customemojimodel.h"
|
#include "models/actionsmodel.h"
|
||||||
|
#include "models/customemojimodel.h"
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
#include "roommanager.h"
|
|
||||||
#include "neochatuser.h"
|
#include "neochatuser.h"
|
||||||
|
#include "roommanager.h"
|
||||||
|
#include "texthandler.h"
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
QString markdownToHTML(const QString &markdown)
|
|
||||||
{
|
|
||||||
const auto str = markdown.toUtf8();
|
|
||||||
char *tmp_buf = cmark_markdown_to_html(str.constData(), str.size(), CMARK_OPT_HARDBREAKS | CMARK_OPT_UNSAFE);
|
|
||||||
|
|
||||||
const std::string html(tmp_buf);
|
|
||||||
|
|
||||||
free(tmp_buf);
|
|
||||||
|
|
||||||
auto result = QString::fromStdString(html).trimmed();
|
|
||||||
|
|
||||||
result.replace("<!-- raw HTML omitted -->", "");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ActionsHandler::ActionsHandler(QObject *parent)
|
ActionsHandler::ActionsHandler(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
@@ -59,9 +44,9 @@ void ActionsHandler::setRoom(NeoChatRoom *room)
|
|||||||
Q_EMIT roomChanged();
|
Q_EMIT roomChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionsHandler::handleMessage()
|
void ActionsHandler::handleNewMessage()
|
||||||
{
|
{
|
||||||
checkEffects();
|
checkEffects(m_room->chatBoxText());
|
||||||
if (!m_room->chatBoxAttachmentPath().isEmpty()) {
|
if (!m_room->chatBoxAttachmentPath().isEmpty()) {
|
||||||
QUrl url(m_room->chatBoxAttachmentPath());
|
QUrl url(m_room->chatBoxAttachmentPath());
|
||||||
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
|
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
|
||||||
@@ -70,13 +55,39 @@ void ActionsHandler::handleMessage()
|
|||||||
m_room->setChatBoxText({});
|
m_room->setChatBoxText({});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QString handledText = m_room->chatBoxText();
|
|
||||||
|
|
||||||
std::sort(m_room->mentions()->begin(), m_room->mentions()->end(), [](const auto &a, const auto &b) -> bool {
|
QString handledText = m_room->chatBoxText();
|
||||||
|
handledText = handleMentions(handledText);
|
||||||
|
handleMessage(m_room->chatBoxText(), handledText);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsHandler::handleEdit()
|
||||||
|
{
|
||||||
|
checkEffects(m_room->editText());
|
||||||
|
|
||||||
|
QString handledText = m_room->editText();
|
||||||
|
handledText = handleMentions(handledText, true);
|
||||||
|
handleMessage(m_room->editText(), handledText, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ActionsHandler::handleMentions(QString handledText, const bool &isEdit)
|
||||||
|
{
|
||||||
|
if (!m_room) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<Mention> *mentions;
|
||||||
|
if (isEdit) {
|
||||||
|
mentions = m_room->editMentions();
|
||||||
|
} else {
|
||||||
|
mentions = m_room->mentions();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(mentions->begin(), mentions->end(), [](const auto &a, const auto &b) -> bool {
|
||||||
return a.cursor.anchor() > b.cursor.anchor();
|
return a.cursor.anchor() > b.cursor.anchor();
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const auto &mention : *m_room->mentions()) {
|
for (const auto &mention : *mentions) {
|
||||||
if (mention.text.isEmpty() || mention.id.isEmpty()) {
|
if (mention.text.isEmpty() || mention.id.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -84,11 +95,16 @@ void ActionsHandler::handleMessage()
|
|||||||
mention.cursor.position() - mention.cursor.anchor(),
|
mention.cursor.position() - mention.cursor.anchor(),
|
||||||
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text, mention.id));
|
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text, mention.id));
|
||||||
}
|
}
|
||||||
m_room->mentions()->clear();
|
mentions->clear();
|
||||||
|
|
||||||
|
return handledText;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsHandler::handleMessage(const QString &text, QString handledText, const bool &isEdit)
|
||||||
|
{
|
||||||
if (NeoChatConfig::allowQuickEdit()) {
|
if (NeoChatConfig::allowQuickEdit()) {
|
||||||
QRegularExpression sed("^s/([^/]*)/([^/]*)(/g)?$");
|
QRegularExpression sed("^s/([^/]*)/([^/]*)(/g)?$");
|
||||||
auto match = sed.match(m_room->chatBoxText());
|
auto match = sed.match(text);
|
||||||
if (match.hasMatch()) {
|
if (match.hasMatch()) {
|
||||||
const QString regex = match.captured(1);
|
const QString regex = match.captured(1);
|
||||||
const QString replacement = match.captured(2).toHtmlEscaped();
|
const QString replacement = match.captured(2).toHtmlEscaped();
|
||||||
@@ -138,18 +154,25 @@ void ActionsHandler::handleMessage()
|
|||||||
}
|
}
|
||||||
|
|
||||||
handledText = CustomEmojiModel::instance().preprocessText(handledText);
|
handledText = CustomEmojiModel::instance().preprocessText(handledText);
|
||||||
handledText = markdownToHTML(handledText);
|
TextHandler textHandler;
|
||||||
|
textHandler.setData(handledText);
|
||||||
|
handledText = textHandler.handleSendText();
|
||||||
|
|
||||||
|
if (handledText.count("<p>") == 1 && handledText.count("</p>") == 1) {
|
||||||
|
handledText.remove("<p>");
|
||||||
|
handledText.remove("</p>");
|
||||||
|
}
|
||||||
|
|
||||||
if (handledText.length() == 0) {
|
if (handledText.length() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_room->postMessage(m_room->chatBoxText(), handledText, messageType, m_room->chatBoxReplyId(), m_room->chatBoxEditId());
|
|
||||||
|
m_room->postMessage(text, handledText, messageType, m_room->chatBoxReplyId(), isEdit ? m_room->chatBoxEditId() : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionsHandler::checkEffects()
|
void ActionsHandler::checkEffects(const QString &text)
|
||||||
{
|
{
|
||||||
std::optional<QString> effect = std::nullopt;
|
std::optional<QString> effect = std::nullopt;
|
||||||
const auto &text = m_room->chatBoxText();
|
|
||||||
if (text.contains("\u2744")) {
|
if (text.contains("\u2744")) {
|
||||||
effect = QLatin1String("snowflake");
|
effect = QLatin1String("snowflake");
|
||||||
} else if (text.contains("\u1F386")) {
|
} else if (text.contains("\u1F386")) {
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
|
|
||||||
#include <events/roommessageevent.h>
|
#include <events/roommessageevent.h>
|
||||||
|
|
||||||
class NeoChatRoom;
|
#include "neochatroom.h"
|
||||||
|
|
||||||
class CustomEmojiModel;
|
class CustomEmojiModel;
|
||||||
class NeoChatRoom;
|
class NeoChatRoom;
|
||||||
|
|
||||||
@@ -32,14 +33,20 @@ Q_SIGNALS:
|
|||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
|
|
||||||
/// \brief Post a message.
|
/**
|
||||||
///
|
* @brief Pre-process text and send message.
|
||||||
/// This also interprets commands if any.
|
*/
|
||||||
void handleMessage();
|
void handleNewMessage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pre-process text and send edit.
|
||||||
|
*/
|
||||||
|
void handleEdit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NeoChatRoom *m_room = nullptr;
|
NeoChatRoom *m_room = nullptr;
|
||||||
void checkEffects();
|
void checkEffects(const QString &text);
|
||||||
};
|
|
||||||
|
|
||||||
QString markdownToHTML(const QString &markdown);
|
QString handleMentions(QString handledText, const bool &isEdit = false);
|
||||||
|
void handleMessage(const QString &text, QString handledText, const bool &isEdit = false);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: 2021 Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
#include "blurhashimageprovider.h"
|
#include "blurhashimageprovider.h"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: 2021 Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#include <QQmlFile>
|
#include <QQmlFile>
|
||||||
#include <QQmlFileSelector>
|
#include <QQmlFileSelector>
|
||||||
#include <QQuickTextDocument>
|
|
||||||
#include <QStringBuilder>
|
#include <QStringBuilder>
|
||||||
#include <QSyntaxHighlighter>
|
#include <QSyntaxHighlighter>
|
||||||
#include <QTextBlock>
|
#include <QTextBlock>
|
||||||
@@ -15,10 +14,9 @@
|
|||||||
#include <Sonnet/BackgroundChecker>
|
#include <Sonnet/BackgroundChecker>
|
||||||
#include <Sonnet/Settings>
|
#include <Sonnet/Settings>
|
||||||
|
|
||||||
#include "actionsmodel.h"
|
#include "models/actionsmodel.h"
|
||||||
#include "completionmodel.h"
|
#include "models/roomlistmodel.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
#include "roomlistmodel.h"
|
|
||||||
|
|
||||||
class SyntaxHighlighter : public QSyntaxHighlighter
|
class SyntaxHighlighter : public QSyntaxHighlighter
|
||||||
{
|
{
|
||||||
@@ -107,14 +105,19 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
|||||||
{
|
{
|
||||||
connect(this, &ChatDocumentHandler::roomChanged, this, [this]() {
|
connect(this, &ChatDocumentHandler::roomChanged, this, [this]() {
|
||||||
m_completionModel->setRoom(m_room);
|
m_completionModel->setRoom(m_room);
|
||||||
static NeoChatRoom *previousRoom = nullptr;
|
static QPointer<NeoChatRoom> previousRoom = nullptr;
|
||||||
if (previousRoom) {
|
if (previousRoom) {
|
||||||
disconnect(previousRoom, &NeoChatRoom::chatBoxTextChanged, this, nullptr);
|
disconnect(previousRoom, &NeoChatRoom::chatBoxTextChanged, this, nullptr);
|
||||||
|
disconnect(previousRoom, &NeoChatRoom::editTextChanged, this, nullptr);
|
||||||
}
|
}
|
||||||
previousRoom = m_room;
|
previousRoom = m_room;
|
||||||
connect(m_room, &NeoChatRoom::chatBoxTextChanged, this, [this]() {
|
connect(m_room, &NeoChatRoom::chatBoxTextChanged, this, [this]() {
|
||||||
int start = completionStartIndex();
|
int start = completionStartIndex();
|
||||||
m_completionModel->setText(m_room->chatBoxText().mid(start, cursorPosition() - start), m_room->chatBoxText().mid(start));
|
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||||
|
});
|
||||||
|
connect(m_room, &NeoChatRoom::editTextChanged, this, [this]() {
|
||||||
|
int start = completionStartIndex();
|
||||||
|
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
connect(this, &ChatDocumentHandler::documentChanged, this, [this]() {
|
connect(this, &ChatDocumentHandler::documentChanged, this, [this]() {
|
||||||
@@ -125,7 +128,7 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int start = completionStartIndex();
|
int start = completionStartIndex();
|
||||||
m_completionModel->setText(m_room->chatBoxText().mid(start, cursorPosition() - start), m_room->chatBoxText().mid(start));
|
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,8 +138,12 @@ int ChatDocumentHandler::completionStartIndex() const
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &cursor = cursorPosition();
|
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
|
||||||
const auto &text = m_room->chatBoxText();
|
const long long cursor = cursorPosition();
|
||||||
|
#else
|
||||||
|
const auto cursor = cursorPosition();
|
||||||
|
#endif
|
||||||
|
const auto &text = getText();
|
||||||
auto start = std::min(cursor, text.size()) - 1;
|
auto start = std::min(cursor, text.size()) - 1;
|
||||||
while (start > -1) {
|
while (start > -1) {
|
||||||
if (text.at(start) == QLatin1Char(' ')) {
|
if (text.at(start) == QLatin1Char(' ')) {
|
||||||
@@ -148,6 +155,20 @@ int ChatDocumentHandler::completionStartIndex() const
|
|||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ChatDocumentHandler::isEdit() const
|
||||||
|
{
|
||||||
|
return m_isEdit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatDocumentHandler::setIsEdit(bool edit)
|
||||||
|
{
|
||||||
|
if (edit == m_isEdit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_isEdit = edit;
|
||||||
|
Q_EMIT isEditChanged();
|
||||||
|
}
|
||||||
|
|
||||||
QQuickTextDocument *ChatDocumentHandler::document() const
|
QQuickTextDocument *ChatDocumentHandler::document() const
|
||||||
{
|
{
|
||||||
return m_document;
|
return m_document;
|
||||||
@@ -199,10 +220,10 @@ void ChatDocumentHandler::setRoom(NeoChatRoom *room)
|
|||||||
|
|
||||||
void ChatDocumentHandler::complete(int index)
|
void ChatDocumentHandler::complete(int index)
|
||||||
{
|
{
|
||||||
if (m_completionModel->autoCompletionType() == ChatDocumentHandler::User) {
|
if (m_completionModel->autoCompletionType() == CompletionModel::User) {
|
||||||
auto name = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::Text).toString();
|
auto name = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::Text).toString();
|
||||||
auto id = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::Subtitle).toString();
|
auto id = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::Subtitle).toString();
|
||||||
auto text = m_room->chatBoxText();
|
auto text = getText();
|
||||||
auto at = text.lastIndexOf(QLatin1Char('@'), cursorPosition() - 1);
|
auto at = text.lastIndexOf(QLatin1Char('@'), cursorPosition() - 1);
|
||||||
QTextCursor cursor(document()->textDocument());
|
QTextCursor cursor(document()->textDocument());
|
||||||
cursor.setPosition(at);
|
cursor.setPosition(at);
|
||||||
@@ -211,19 +232,19 @@ void ChatDocumentHandler::complete(int index)
|
|||||||
cursor.setPosition(at);
|
cursor.setPosition(at);
|
||||||
cursor.setPosition(cursor.position() + name.size(), QTextCursor::KeepAnchor);
|
cursor.setPosition(cursor.position() + name.size(), QTextCursor::KeepAnchor);
|
||||||
cursor.setKeepPositionOnInsert(true);
|
cursor.setKeepPositionOnInsert(true);
|
||||||
m_room->mentions()->push_back({cursor, name, 0, 0, id});
|
pushMention({cursor, name, 0, 0, id});
|
||||||
m_highlighter->rehighlight();
|
m_highlighter->rehighlight();
|
||||||
} else if (m_completionModel->autoCompletionType() == ChatDocumentHandler::Command) {
|
} else if (m_completionModel->autoCompletionType() == CompletionModel::Command) {
|
||||||
auto command = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedText).toString();
|
auto command = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedText).toString();
|
||||||
auto text = m_room->chatBoxText();
|
auto text = getText();
|
||||||
auto at = text.lastIndexOf(QLatin1Char('/'));
|
auto at = text.lastIndexOf(QLatin1Char('/'));
|
||||||
QTextCursor cursor(document()->textDocument());
|
QTextCursor cursor(document()->textDocument());
|
||||||
cursor.setPosition(at);
|
cursor.setPosition(at);
|
||||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||||
cursor.insertText(QStringLiteral("/%1 ").arg(command));
|
cursor.insertText(QStringLiteral("/%1 ").arg(command));
|
||||||
} else if (m_completionModel->autoCompletionType() == ChatDocumentHandler::Room) {
|
} else if (m_completionModel->autoCompletionType() == CompletionModel::Room) {
|
||||||
auto alias = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::Subtitle).toString();
|
auto alias = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::Subtitle).toString();
|
||||||
auto text = m_room->chatBoxText();
|
auto text = getText();
|
||||||
auto at = text.lastIndexOf(QLatin1Char('#'), cursorPosition() - 1);
|
auto at = text.lastIndexOf(QLatin1Char('#'), cursorPosition() - 1);
|
||||||
QTextCursor cursor(document()->textDocument());
|
QTextCursor cursor(document()->textDocument());
|
||||||
cursor.setPosition(at);
|
cursor.setPosition(at);
|
||||||
@@ -232,11 +253,11 @@ void ChatDocumentHandler::complete(int index)
|
|||||||
cursor.setPosition(at);
|
cursor.setPosition(at);
|
||||||
cursor.setPosition(cursor.position() + alias.size(), QTextCursor::KeepAnchor);
|
cursor.setPosition(cursor.position() + alias.size(), QTextCursor::KeepAnchor);
|
||||||
cursor.setKeepPositionOnInsert(true);
|
cursor.setKeepPositionOnInsert(true);
|
||||||
m_room->mentions()->push_back({cursor, alias, 0, 0, alias});
|
pushMention({cursor, alias, 0, 0, alias});
|
||||||
m_highlighter->rehighlight();
|
m_highlighter->rehighlight();
|
||||||
} else if (m_completionModel->autoCompletionType() == ChatDocumentHandler::Emoji) {
|
} else if (m_completionModel->autoCompletionType() == CompletionModel::Emoji) {
|
||||||
auto shortcode = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedText).toString();
|
auto shortcode = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedText).toString();
|
||||||
auto text = m_room->chatBoxText();
|
auto text = getText();
|
||||||
auto at = text.lastIndexOf(QLatin1Char(':'));
|
auto at = text.lastIndexOf(QLatin1Char(':'));
|
||||||
QTextCursor cursor(document()->textDocument());
|
QTextCursor cursor(document()->textDocument());
|
||||||
cursor.setPosition(at);
|
cursor.setPosition(at);
|
||||||
@@ -279,3 +300,27 @@ void ChatDocumentHandler::setSelectionEnd(int position)
|
|||||||
m_selectionEnd = position;
|
m_selectionEnd = position;
|
||||||
Q_EMIT selectionEndChanged();
|
Q_EMIT selectionEndChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ChatDocumentHandler::getText() const
|
||||||
|
{
|
||||||
|
if (!m_room) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
if (m_isEdit) {
|
||||||
|
return m_room->editText();
|
||||||
|
} else {
|
||||||
|
return m_room->chatBoxText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatDocumentHandler::pushMention(const Mention mention) const
|
||||||
|
{
|
||||||
|
if (!m_room) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_isEdit) {
|
||||||
|
m_room->editMentions()->push_back(mention);
|
||||||
|
} else {
|
||||||
|
m_room->mentions()->push_back(mention);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,20 +4,28 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QQuickTextDocument>
|
||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
|
|
||||||
#include "userlistmodel.h"
|
#include "models/completionmodel.h"
|
||||||
|
#include "models/userlistmodel.h"
|
||||||
|
#include "neochatroom.h"
|
||||||
|
|
||||||
class QTextDocument;
|
class QTextDocument;
|
||||||
class QQuickTextDocument;
|
|
||||||
class NeoChatRoom;
|
class NeoChatRoom;
|
||||||
class SyntaxHighlighter;
|
class SyntaxHighlighter;
|
||||||
class CompletionModel;
|
|
||||||
|
|
||||||
class ChatDocumentHandler : public QObject
|
class ChatDocumentHandler : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is the instance being used to handle an edit message.
|
||||||
|
*
|
||||||
|
* This is needed to ensure that the text and mentions are saved and retrieved
|
||||||
|
* from the correct parameters in the assigned room.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool isEdit READ isEdit WRITE setIsEdit NOTIFY isEditChanged)
|
||||||
Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged)
|
Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged)
|
||||||
Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
|
Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
|
||||||
Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
|
Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
|
||||||
@@ -25,20 +33,14 @@ class ChatDocumentHandler : public QObject
|
|||||||
|
|
||||||
Q_PROPERTY(CompletionModel *completionModel READ completionModel NOTIFY completionModelChanged)
|
Q_PROPERTY(CompletionModel *completionModel READ completionModel NOTIFY completionModelChanged)
|
||||||
|
|
||||||
Q_PROPERTY(NeoChatRoom *room READ room NOTIFY roomChanged)
|
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum AutoCompletionType {
|
|
||||||
User,
|
|
||||||
Room,
|
|
||||||
Emoji,
|
|
||||||
Command,
|
|
||||||
None,
|
|
||||||
};
|
|
||||||
Q_ENUM(AutoCompletionType)
|
|
||||||
|
|
||||||
explicit ChatDocumentHandler(QObject *parent = nullptr);
|
explicit ChatDocumentHandler(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
[[nodiscard]] bool isEdit() const;
|
||||||
|
void setIsEdit(bool edit);
|
||||||
|
|
||||||
[[nodiscard]] QQuickTextDocument *document() const;
|
[[nodiscard]] QQuickTextDocument *document() const;
|
||||||
void setDocument(QQuickTextDocument *document);
|
void setDocument(QQuickTextDocument *document);
|
||||||
|
|
||||||
@@ -59,6 +61,7 @@ public:
|
|||||||
void updateCompletions();
|
void updateCompletions();
|
||||||
CompletionModel *completionModel() const;
|
CompletionModel *completionModel() const;
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
void isEditChanged();
|
||||||
void documentChanged();
|
void documentChanged();
|
||||||
void cursorPositionChanged();
|
void cursorPositionChanged();
|
||||||
void roomChanged();
|
void roomChanged();
|
||||||
@@ -69,20 +72,23 @@ Q_SIGNALS:
|
|||||||
private:
|
private:
|
||||||
int completionStartIndex() const;
|
int completionStartIndex() const;
|
||||||
|
|
||||||
|
bool m_isEdit;
|
||||||
|
|
||||||
QQuickTextDocument *m_document;
|
QQuickTextDocument *m_document;
|
||||||
|
|
||||||
NeoChatRoom *m_room = nullptr;
|
QPointer<NeoChatRoom> m_room;
|
||||||
bool completionVisible = false;
|
bool completionVisible = false;
|
||||||
|
|
||||||
int m_cursorPosition;
|
int m_cursorPosition;
|
||||||
int m_selectionStart;
|
int m_selectionStart;
|
||||||
int m_selectionEnd;
|
int m_selectionEnd;
|
||||||
|
|
||||||
|
QString getText() const;
|
||||||
|
void pushMention(const Mention mention) const;
|
||||||
|
|
||||||
SyntaxHighlighter *m_highlighter = nullptr;
|
SyntaxHighlighter *m_highlighter = nullptr;
|
||||||
|
|
||||||
AutoCompletionType m_completionType = None;
|
CompletionModel::AutoCompletionType m_completionType = CompletionModel::None;
|
||||||
|
|
||||||
CompletionModel *m_completionModel = nullptr;
|
CompletionModel *m_completionModel = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(ChatDocumentHandler::AutoCompletionType);
|
|
||||||
|
|||||||
@@ -33,13 +33,14 @@ QImage Clipboard::image() const
|
|||||||
|
|
||||||
QString Clipboard::saveImage(QString localPath) const
|
QString Clipboard::saveImage(QString localPath) const
|
||||||
{
|
{
|
||||||
if (!QDir().exists(QStringLiteral("%1/screenshots").arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)))) {
|
QString imageDir(QStringLiteral("%1/screenshots").arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)));
|
||||||
QDir().mkdir(QStringLiteral("%1/screenshots").arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)));
|
|
||||||
|
if (!QDir().exists(imageDir)) {
|
||||||
|
QDir().mkdir(imageDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localPath.isEmpty()) {
|
if (localPath.isEmpty()) {
|
||||||
localPath = QStringLiteral("file://%1/screenshots/%2.png")
|
localPath = QStringLiteral("file://%1/%2.png").arg(imageDir, QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd-hh-mm-ss")));
|
||||||
.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation),
|
|
||||||
QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd-hh-mm-ss")));
|
|
||||||
}
|
}
|
||||||
QUrl url(localPath);
|
QUrl url(localPath);
|
||||||
if (!url.isLocalFile()) {
|
if (!url.isLocalFile()) {
|
||||||
@@ -51,21 +52,30 @@ QString Clipboard::saveImage(QString localPath) const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QDir dir;
|
if (image.save(url.toLocalFile())) {
|
||||||
if (!dir.exists(localPath)) {
|
return localPath;
|
||||||
dir.mkpath(localPath);
|
} else {
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
image.save(url.toLocalFile());
|
|
||||||
|
|
||||||
return localPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Clipboard::saveText(QString message)
|
void Clipboard::saveText(QString message)
|
||||||
{
|
{
|
||||||
QRegularExpression re("<[^>]*>");
|
static QRegularExpression re(QStringLiteral("<[^>]*>"));
|
||||||
auto *mineData = new QMimeData; // ownership is transferred to clipboard
|
auto *mineData = new QMimeData; // ownership is transferred to clipboard
|
||||||
mineData->setHtml(message);
|
mineData->setHtml(message);
|
||||||
mineData->setText(message.replace(re, ""));
|
mineData->setText(message.replace(re, QString()));
|
||||||
m_clipboard->setMimeData(mineData);
|
m_clipboard->setMimeData(mineData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Clipboard::setImage(const QUrl &url)
|
||||||
|
{
|
||||||
|
if (url.isLocalFile()) {
|
||||||
|
QImage img(url.path());
|
||||||
|
auto *mimeData = new QMimeData;
|
||||||
|
mimeData->setImageData(img);
|
||||||
|
if (!img.isNull()) {
|
||||||
|
m_clipboard->setMimeData(mimeData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public:
|
|||||||
Q_INVOKABLE QString saveImage(QString localPath = {}) const;
|
Q_INVOKABLE QString saveImage(QString localPath = {}) const;
|
||||||
|
|
||||||
Q_INVOKABLE void saveText(QString message);
|
Q_INVOKABLE void saveText(QString message);
|
||||||
|
Q_INVOKABLE void setImage(const QUrl &image);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QClipboard *m_clipboard;
|
QClipboard *m_clipboard;
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
|
||||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
|
||||||
|
|
||||||
#include "collapsestateproxymodel.h"
|
|
||||||
#include "messageeventmodel.h"
|
|
||||||
|
|
||||||
#include <KLocalizedString>
|
|
||||||
|
|
||||||
bool CollapseStateProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(source_parent);
|
|
||||||
return sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::EventTypeRole)
|
|
||||||
!= MessageEventModel::DelegateType::State // If this is not a state, show it
|
|
||||||
|| sourceModel()->data(sourceModel()->index(source_row + 1, 0), MessageEventModel::EventTypeRole)
|
|
||||||
!= MessageEventModel::DelegateType::State // If this is the first state in a block, show it. TODO hidden events?
|
|
||||||
|| sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::ShowSectionRole).toBool() // If it's a new day, show it
|
|
||||||
|| sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::EventResolvedTypeRole)
|
|
||||||
!= sourceModel()->data(sourceModel()->index(source_row + 1, 0),
|
|
||||||
MessageEventModel::EventResolvedTypeRole) // Also show it if it's of a different type than the one before TODO improve in
|
|
||||||
|| sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::AuthorIdRole)
|
|
||||||
!= sourceModel()->data(sourceModel()->index(source_row + 1, 0), MessageEventModel::AuthorIdRole); // Also show it if it's a different author
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant CollapseStateProxyModel::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
if (role == AggregateDisplayRole) {
|
|
||||||
return aggregateEventToString(mapToSource(index).row());
|
|
||||||
}
|
|
||||||
return sourceModel()->data(mapToSource(index), role);
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> CollapseStateProxyModel::roleNames() const
|
|
||||||
{
|
|
||||||
auto roles = sourceModel()->roleNames();
|
|
||||||
roles[AggregateDisplayRole] = "aggregateDisplay";
|
|
||||||
return roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString CollapseStateProxyModel::aggregateEventToString(int sourceRow) const
|
|
||||||
{
|
|
||||||
QStringList parts;
|
|
||||||
for (int i = sourceRow; i >= 0; i--) {
|
|
||||||
parts += sourceModel()->data(sourceModel()->index(i, 0), Qt::DisplayRole).toString();
|
|
||||||
if (sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::EventTypeRole)
|
|
||||||
!= MessageEventModel::DelegateType::State // If it's not a state event
|
|
||||||
|| (i > 0
|
|
||||||
&& sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::EventResolvedTypeRole)
|
|
||||||
!= sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::EventResolvedTypeRole)) // or of a different type
|
|
||||||
|| (i > 0
|
|
||||||
&& sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::AuthorIdRole)
|
|
||||||
!= sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::AuthorIdRole)) // or by a different author
|
|
||||||
|| sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::ShowSectionRole).toBool() // or the section needs to be visible
|
|
||||||
) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!parts.isEmpty()) {
|
|
||||||
QStringList chunks;
|
|
||||||
while (!parts.isEmpty()) {
|
|
||||||
chunks += QString();
|
|
||||||
int count = 1;
|
|
||||||
auto part = parts.takeFirst();
|
|
||||||
chunks.last() += part;
|
|
||||||
while (!parts.isEmpty() && parts.first() == part) {
|
|
||||||
parts.removeFirst();
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
if (count > 1) {
|
|
||||||
chunks.last() += i18ncp("[user did something] n times", " %1 time", " %1 times", count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QString text = chunks.takeFirst();
|
|
||||||
|
|
||||||
if (chunks.size() > 0) {
|
|
||||||
while (chunks.size() > 1) {
|
|
||||||
text += i18nc("[action 1], [action 2 and action 3]", ", ");
|
|
||||||
text += chunks.takeFirst();
|
|
||||||
}
|
|
||||||
text += i18nc("[action 1, action 2] and [action 3]", " and ");
|
|
||||||
text += chunks.takeFirst();
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
|
||||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "messageeventmodel.h"
|
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
|
|
||||||
class CollapseStateProxyModel : public QSortFilterProxyModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
enum Roles {
|
|
||||||
AggregateDisplayRole = MessageEventModel::LastRole + 1,
|
|
||||||
};
|
|
||||||
[[nodiscard]] bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
|
||||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
|
||||||
[[nodiscard]] QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
|
||||||
|
|
||||||
[[nodiscard]] QString aggregateEventToString(int row) const;
|
|
||||||
};
|
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
// SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
|
// SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
|
||||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
#include <qt5keychain/keychain.h>
|
#include <qt5keychain/keychain.h>
|
||||||
|
#else
|
||||||
|
#include <qt6keychain/keychain.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <KConfig>
|
#include <KConfig>
|
||||||
#include <KConfigGroup>
|
#include <KConfigGroup>
|
||||||
@@ -19,7 +23,6 @@
|
|||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QImageReader>
|
#include <QImageReader>
|
||||||
#include <QNetworkProxy>
|
#include <QNetworkProxy>
|
||||||
#include <QQuickItem>
|
|
||||||
#include <QQuickTextDocument>
|
#include <QQuickTextDocument>
|
||||||
#include <QQuickWindow>
|
#include <QQuickWindow>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
@@ -38,6 +41,7 @@
|
|||||||
#include <csapi/content-repo.h>
|
#include <csapi/content-repo.h>
|
||||||
#include <csapi/logout.h>
|
#include <csapi/logout.h>
|
||||||
#include <csapi/profile.h>
|
#include <csapi/profile.h>
|
||||||
|
#include <jobs/downloadfilejob.h>
|
||||||
#include <qt_connection_util.h>
|
#include <qt_connection_util.h>
|
||||||
|
|
||||||
#ifdef QUOTIENT_07
|
#ifdef QUOTIENT_07
|
||||||
@@ -129,16 +133,7 @@ Controller::Controller(QObject *parent)
|
|||||||
if (AccountRegistry::instance().size() > oldAccountCount) {
|
if (AccountRegistry::instance().size() > oldAccountCount) {
|
||||||
auto connection = AccountRegistry::instance().accounts()[AccountRegistry::instance().size() - 1];
|
auto connection = AccountRegistry::instance().accounts()[AccountRegistry::instance().size() - 1];
|
||||||
connect(connection, &Connection::syncDone, this, [=]() {
|
connect(connection, &Connection::syncDone, this, [=]() {
|
||||||
bool changes = false;
|
handleNotifications(connection);
|
||||||
for (const auto &room : connection->allRooms()) {
|
|
||||||
if (m_notificationCounts[room] != room->unreadStats().notableCount) {
|
|
||||||
m_notificationCounts[room] = room->unreadStats().notableCount;
|
|
||||||
changes = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (changes) {
|
|
||||||
handleNotifications();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
oldAccountCount = AccountRegistry::instance().size();
|
oldAccountCount = AccountRegistry::instance().size();
|
||||||
@@ -147,19 +142,16 @@ Controller::Controller(QObject *parent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef QUOTIENT_07
|
#ifdef QUOTIENT_07
|
||||||
void Controller::handleNotifications()
|
void Controller::handleNotifications(QPointer<Quotient::Connection> connection)
|
||||||
{
|
{
|
||||||
static bool initial = true;
|
static QStringList initial;
|
||||||
static QStringList oldNotifications;
|
static QStringList oldNotifications;
|
||||||
if (!m_connection) {
|
auto job = connection->callApi<GetNotificationsJob>();
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto job = m_connection->callApi<GetNotificationsJob>();
|
|
||||||
|
|
||||||
connect(job, &BaseJob::success, this, [this, job]() {
|
connect(job, &BaseJob::success, this, [job, connection]() {
|
||||||
const auto notifications = job->jsonData()["notifications"].toArray();
|
const auto notifications = job->jsonData()["notifications"].toArray();
|
||||||
if (initial) {
|
if (!initial.contains(connection->user()->id())) {
|
||||||
initial = false;
|
initial.append(connection->user()->id());
|
||||||
for (const auto &n : notifications) {
|
for (const auto &n : notifications) {
|
||||||
oldNotifications += n.toObject()["event"].toObject()["event_id"].toString();
|
oldNotifications += n.toObject()["event"].toObject()["event_id"].toString();
|
||||||
}
|
}
|
||||||
@@ -175,10 +167,12 @@ void Controller::handleNotifications()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
oldNotifications += notification["event"].toObject()["event_id"].toString();
|
oldNotifications += notification["event"].toObject()["event_id"].toString();
|
||||||
auto room = m_connection->room(notification["room_id"].toString());
|
auto room = connection->room(notification["room_id"].toString());
|
||||||
|
|
||||||
// If room exists, room is NOT active OR the application is NOT active, show notification
|
// If room exists, room is NOT active OR the application is NOT active, show notification
|
||||||
if (room && !(room->id() == RoomManager::instance().currentRoom()->id() && QGuiApplication::applicationState() == Qt::ApplicationActive)) {
|
if (room
|
||||||
|
&& !(RoomManager::instance().currentRoom() && room->id() == RoomManager::instance().currentRoom()->id()
|
||||||
|
&& QGuiApplication::applicationState() == Qt::ApplicationActive)) {
|
||||||
// The room might have been deleted (for example rejected invitation).
|
// The room might have been deleted (for example rejected invitation).
|
||||||
auto sender = room->user(notification["event"].toObject()["sender"].toString());
|
auto sender = room->user(notification["event"].toObject()["sender"].toString());
|
||||||
|
|
||||||
@@ -197,7 +191,7 @@ void Controller::handleNotifications()
|
|||||||
|
|
||||||
if (notification["event"]["type"] == "m.room.encrypted") {
|
if (notification["event"]["type"] == "m.room.encrypted") {
|
||||||
#ifdef Quotient_E2EE_ENABLED
|
#ifdef Quotient_E2EE_ENABLED
|
||||||
auto decrypted = m_connection->decryptNotification(notification);
|
auto decrypted = connection->decryptNotification(notification);
|
||||||
body = decrypted["content"].toObject()["body"].toString();
|
body = decrypted["content"].toObject()["body"].toString();
|
||||||
#endif
|
#endif
|
||||||
if (body.isEmpty()) {
|
if (body.isEmpty()) {
|
||||||
@@ -234,13 +228,6 @@ void Controller::showWindow()
|
|||||||
WindowController::instance().showAndRaiseWindow(QString());
|
WindowController::instance().showAndRaiseWindow(QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QString accessTokenFileName(const AccountSettings &account)
|
|
||||||
{
|
|
||||||
QString fileName = account.userId();
|
|
||||||
fileName.replace(':', '_');
|
|
||||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + '/' + fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Controller::loginWithAccessToken(const QString &serverAddr, const QString &user, const QString &token, const QString &deviceName)
|
void Controller::loginWithAccessToken(const QString &serverAddr, const QString &user, const QString &token, const QString &deviceName)
|
||||||
{
|
{
|
||||||
if (user.isEmpty() || token.isEmpty()) {
|
if (user.isEmpty() || token.isEmpty()) {
|
||||||
@@ -281,7 +268,6 @@ void Controller::logout(Connection *conn, bool serverSideLogout)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SettingsGroup("Accounts").remove(conn->userId());
|
SettingsGroup("Accounts").remove(conn->userId());
|
||||||
QFile(accessTokenFileName(AccountSettings(conn->userId()))).remove();
|
|
||||||
|
|
||||||
QKeychain::DeletePasswordJob job(qAppName());
|
QKeychain::DeletePasswordJob job(qAppName());
|
||||||
job.setAutoDelete(true);
|
job.setAutoDelete(true);
|
||||||
@@ -374,15 +360,7 @@ void Controller::invokeLogin()
|
|||||||
if (accessTokenLoadingJob->error() == QKeychain::Error::NoError) {
|
if (accessTokenLoadingJob->error() == QKeychain::Error::NoError) {
|
||||||
accessToken = accessTokenLoadingJob->binaryData();
|
accessToken = accessTokenLoadingJob->binaryData();
|
||||||
} else {
|
} else {
|
||||||
// No access token from the keychain, try token file
|
return;
|
||||||
// TODO FIXME this code is racy since the file might have
|
|
||||||
// already been removed. But since the other code do a blocking
|
|
||||||
// dbus call, the probability are not high that it will happen.
|
|
||||||
// loadAccessTokenFromFile is also mostly legacy nowadays
|
|
||||||
accessToken = loadAccessTokenFromFile(account);
|
|
||||||
if (accessToken.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto connection = new Connection(account.homeserver());
|
auto connection = new Connection(account.homeserver());
|
||||||
@@ -398,6 +376,11 @@ void Controller::invokeLogin()
|
|||||||
if (error == "Unrecognised access token") {
|
if (error == "Unrecognised access token") {
|
||||||
Q_EMIT errorOccured(i18n("Login Failed: Access Token invalid or revoked"));
|
Q_EMIT errorOccured(i18n("Login Failed: Access Token invalid or revoked"));
|
||||||
logout(connection, false);
|
logout(connection, false);
|
||||||
|
} else if (error == "Connection closed") {
|
||||||
|
Q_EMIT errorOccured(i18n("Login Failed: %1", error));
|
||||||
|
// Failed due to network connection issue. This might happen when the homeserver is
|
||||||
|
// temporary down, or the user trying to re-launch NeoChat in a network that cannot
|
||||||
|
// connect to the homeserver. In this case, we don't want to do logout().
|
||||||
} else {
|
} else {
|
||||||
Q_EMIT errorOccured(i18n("Login Failed: %1", error));
|
Q_EMIT errorOccured(i18n("Login Failed: %1", error));
|
||||||
logout(connection, true);
|
logout(connection, true);
|
||||||
@@ -416,21 +399,6 @@ void Controller::invokeLogin()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray Controller::loadAccessTokenFromFile(const AccountSettings &account)
|
|
||||||
{
|
|
||||||
QFile accountTokenFile{accessTokenFileName(account)};
|
|
||||||
if (accountTokenFile.open(QFile::ReadOnly)) {
|
|
||||||
if (accountTokenFile.size() < 1024) {
|
|
||||||
return accountTokenFile.readAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
qWarning() << "File" << accountTokenFile.fileName() << "is" << accountTokenFile.size() << "bytes long - too long for a token, ignoring it.";
|
|
||||||
}
|
|
||||||
qWarning() << "Could not open access token file" << accountTokenFile.fileName();
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const AccountSettings &account)
|
QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const AccountSettings &account)
|
||||||
{
|
{
|
||||||
qDebug() << "Reading access token from the keychain for" << account.userId();
|
qDebug() << "Reading access token from the keychain for" << account.userId();
|
||||||
@@ -442,24 +410,6 @@ QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const Accoun
|
|||||||
if (job->error() == QKeychain::Error::NoError) {
|
if (job->error() == QKeychain::Error::NoError) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (job->error() == QKeychain::Error::EntryNotFound) {
|
|
||||||
// no access token from the keychain, try token file
|
|
||||||
auto accessToken = loadAccessTokenFromFile(account);
|
|
||||||
if (!accessToken.isEmpty()) {
|
|
||||||
qDebug() << "Migrating the access token from file to the keychain for " << account.userId();
|
|
||||||
bool removed = false;
|
|
||||||
bool saved = saveAccessTokenToKeyChain(account, accessToken);
|
|
||||||
if (saved) {
|
|
||||||
QFile accountTokenFile{accessTokenFileName(account)};
|
|
||||||
removed = accountTokenFile.remove();
|
|
||||||
}
|
|
||||||
if (!(saved && removed)) {
|
|
||||||
qDebug() << "Migrating the access token from the file to the keychain "
|
|
||||||
"failed";
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (job->error()) {
|
switch (job->error()) {
|
||||||
case QKeychain::EntryNotFound:
|
case QKeychain::EntryNotFound:
|
||||||
@@ -484,22 +434,6 @@ QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const Accoun
|
|||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::saveAccessTokenToFile(const AccountSettings &account, const QByteArray &accessToken)
|
|
||||||
{
|
|
||||||
// (Re-)Make a dedicated file for access_token.
|
|
||||||
QFile accountTokenFile{accessTokenFileName(account)};
|
|
||||||
accountTokenFile.remove(); // Just in case
|
|
||||||
|
|
||||||
auto fileDir = QFileInfo(accountTokenFile).dir();
|
|
||||||
if (!((fileDir.exists() || fileDir.mkpath(".")) && accountTokenFile.open(QFile::WriteOnly))) {
|
|
||||||
Q_EMIT errorOccured("I/O Denied: Cannot save access token.");
|
|
||||||
} else {
|
|
||||||
accountTokenFile.write(accessToken);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Controller::saveAccessTokenToKeyChain(const AccountSettings &account, const QByteArray &accessToken)
|
bool Controller::saveAccessTokenToKeyChain(const AccountSettings &account, const QByteArray &accessToken)
|
||||||
{
|
{
|
||||||
qDebug() << "Save the access token to the keychain for " << account.userId();
|
qDebug() << "Save the access token to the keychain for " << account.userId();
|
||||||
@@ -514,7 +448,7 @@ bool Controller::saveAccessTokenToKeyChain(const AccountSettings &account, const
|
|||||||
|
|
||||||
if (job.error()) {
|
if (job.error()) {
|
||||||
qWarning() << "Could not save access token to the keychain: " << qPrintable(job.errorString());
|
qWarning() << "Could not save access token to the keychain: " << qPrintable(job.errorString());
|
||||||
return saveAccessTokenToFile(account, accessToken);
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -677,6 +611,11 @@ void Controller::setActiveConnection(Connection *connection)
|
|||||||
m_isOnline = true;
|
m_isOnline = true;
|
||||||
Q_EMIT isOnlineChanged(true);
|
Q_EMIT isOnlineChanged(true);
|
||||||
});
|
});
|
||||||
|
connect(connection, &Connection::requestFailed, this, [=](BaseJob *job) {
|
||||||
|
if (dynamic_cast<DownloadFileJob *>(job) && job->jsonData()["errcode"].toString() == "M_TOO_LARGE"_ls) {
|
||||||
|
RoomManager::instance().warning(i18n("File too large to download."), i18n("Contact your matrix server administrator for support."));
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
NeoChatConfig::self()->setActiveConnection(QString());
|
NeoChatConfig::self()->setActiveConnection(QString());
|
||||||
}
|
}
|
||||||
@@ -705,9 +644,17 @@ NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const Om
|
|||||||
void Controller::createRoom(const QString &name, const QString &topic)
|
void Controller::createRoom(const QString &name, const QString &topic)
|
||||||
{
|
{
|
||||||
auto createRoomJob = m_connection->createRoom(Connection::PublishRoom, "", name, topic, QStringList());
|
auto createRoomJob = m_connection->createRoom(Connection::PublishRoom, "", name, topic, QStringList());
|
||||||
Quotient::CreateRoomJob::connect(createRoomJob, &CreateRoomJob::failure, [this, createRoomJob] {
|
connect(createRoomJob, &CreateRoomJob::failure, this, [this, createRoomJob] {
|
||||||
Q_EMIT errorOccured(i18n("Room creation failed: \"%1\"", createRoomJob->errorString()));
|
Q_EMIT errorOccured(i18n("Room creation failed: \"%1\"", createRoomJob->errorString()));
|
||||||
});
|
});
|
||||||
|
connectSingleShot(
|
||||||
|
this,
|
||||||
|
&Controller::roomAdded,
|
||||||
|
this,
|
||||||
|
[this](NeoChatRoom *room) {
|
||||||
|
RoomManager::instance().enterRoom(room);
|
||||||
|
},
|
||||||
|
Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::isOnline() const
|
bool Controller::isOnline() const
|
||||||
@@ -808,11 +755,15 @@ void Controller::setApplicationProxy()
|
|||||||
proxy.setType(QNetworkProxy::HttpProxy);
|
proxy.setType(QNetworkProxy::HttpProxy);
|
||||||
proxy.setHostName(cfg->proxyHost());
|
proxy.setHostName(cfg->proxyHost());
|
||||||
proxy.setPort(cfg->proxyPort());
|
proxy.setPort(cfg->proxyPort());
|
||||||
|
proxy.setUser(cfg->proxyUser());
|
||||||
|
proxy.setPassword(cfg->proxyPassword());
|
||||||
break;
|
break;
|
||||||
case 2: // SOCKS 5
|
case 2: // SOCKS 5
|
||||||
proxy.setType(QNetworkProxy::Socks5Proxy);
|
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||||||
proxy.setHostName(cfg->proxyHost());
|
proxy.setHostName(cfg->proxyHost());
|
||||||
proxy.setPort(cfg->proxyPort());
|
proxy.setPort(cfg->proxyPort());
|
||||||
|
proxy.setUser(cfg->proxyUser());
|
||||||
|
proxy.setPassword(cfg->proxyPassword());
|
||||||
break;
|
break;
|
||||||
case 0: // System Default
|
case 0: // System Default
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] bool supportSystemTray() const;
|
[[nodiscard]] bool supportSystemTray() const;
|
||||||
|
|
||||||
bool saveAccessTokenToFile(const Quotient::AccountSettings &account, const QByteArray &accessToken);
|
|
||||||
bool saveAccessTokenToKeyChain(const Quotient::AccountSettings &account, const QByteArray &accessToken);
|
bool saveAccessTokenToKeyChain(const Quotient::AccountSettings &account, const QByteArray &accessToken);
|
||||||
|
|
||||||
int activeConnectionIndex() const;
|
int activeConnectionIndex() const;
|
||||||
@@ -111,7 +110,6 @@ private:
|
|||||||
bool m_busy = false;
|
bool m_busy = false;
|
||||||
TrayIcon *m_trayIcon = nullptr;
|
TrayIcon *m_trayIcon = nullptr;
|
||||||
|
|
||||||
static QByteArray loadAccessTokenFromFile(const Quotient::AccountSettings &account);
|
|
||||||
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const Quotient::AccountSettings &account);
|
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const Quotient::AccountSettings &account);
|
||||||
|
|
||||||
void loadSettings();
|
void loadSettings();
|
||||||
@@ -121,7 +119,7 @@ private:
|
|||||||
|
|
||||||
bool hasWindowSystem() const;
|
bool hasWindowSystem() const;
|
||||||
#ifdef QUOTIENT_07
|
#ifdef QUOTIENT_07
|
||||||
void handleNotifications();
|
void handleNotifications(QPointer<Quotient::Connection> connection);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
@@ -153,6 +151,7 @@ Q_SIGNALS:
|
|||||||
void keyVerificationAccept(const QString &commitment);
|
void keyVerificationAccept(const QString &commitment);
|
||||||
void keyVerificationKey(const QString &sas);
|
void keyVerificationKey(const QString &sas);
|
||||||
void activeConnectionIndexChanged();
|
void activeConnectionIndexChanged();
|
||||||
|
void roomAdded(NeoChatRoom *room);
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void logout(Quotient::Connection *conn, bool serverSideLogout);
|
void logout(Quotient::Connection *conn, bool serverSideLogout);
|
||||||
|
|||||||
3706
src/emojis.h
3706
src/emojis.h
File diff suppressed because it is too large
Load Diff
9
src/emojitones.cpp
Normal file
9
src/emojitones.cpp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// SPDX-FileCopyrightText: None
|
||||||
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "emojitones.h"
|
||||||
|
#include "models/emojimodel.h"
|
||||||
|
|
||||||
|
QMultiHash<QString, QVariant> EmojiTones::_tones = {
|
||||||
|
#include "emojitones_data.h"
|
||||||
|
};
|
||||||
1794
src/emojitones.h
1794
src/emojitones.h
File diff suppressed because it is too large
Load Diff
1784
src/emojitones_data.h
Normal file
1784
src/emojitones_data.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "filetransferpseudojob.h"
|
#include "filetransferpseudojob.h"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "login.h"
|
#include "login.h"
|
||||||
@@ -14,8 +14,6 @@
|
|||||||
|
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
|
|
||||||
#include <QUrl>
|
|
||||||
|
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
@@ -158,8 +156,8 @@ void Login::login()
|
|||||||
|
|
||||||
// Some servers do not have a .well_known file. So we login via the username part from the mxid,
|
// Some servers do not have a .well_known file. So we login via the username part from the mxid,
|
||||||
// rather than with the full mxid, as that would lead to an invalid user.
|
// rather than with the full mxid, as that would lead to an invalid user.
|
||||||
QStringRef username(&m_matrixId, 1, m_matrixId.indexOf(":") - 1);
|
auto username = m_matrixId.mid(1, m_matrixId.indexOf(":") - 1);
|
||||||
m_connection->loginWithPassword(username.toString(), m_password, m_deviceName, QString());
|
m_connection->loginWithPassword(username, m_password, m_deviceName, QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Login::supportsPassword() const
|
bool Login::supportsPassword() const
|
||||||
@@ -183,8 +181,8 @@ void Login::loginWithSso()
|
|||||||
connectSingleShot(m_connection, &Connection::loginFlowsChanged, this, [this]() {
|
connectSingleShot(m_connection, &Connection::loginFlowsChanged, this, [this]() {
|
||||||
SsoSession *session = m_connection->prepareForSso(m_deviceName);
|
SsoSession *session = m_connection->prepareForSso(m_deviceName);
|
||||||
m_ssoUrl = session->ssoUrl();
|
m_ssoUrl = session->ssoUrl();
|
||||||
|
Q_EMIT ssoUrlChanged();
|
||||||
});
|
});
|
||||||
Q_EMIT ssoUrlChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Login::testing() const
|
bool Login::testing() const
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: 2020 Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|||||||
95
src/main.cpp
95
src/main.cpp
@@ -42,19 +42,29 @@
|
|||||||
#include "blurhashimageprovider.h"
|
#include "blurhashimageprovider.h"
|
||||||
#include "chatdocumenthandler.h"
|
#include "chatdocumenthandler.h"
|
||||||
#include "clipboard.h"
|
#include "clipboard.h"
|
||||||
#include "collapsestateproxymodel.h"
|
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "customemojimodel.h"
|
|
||||||
#include "devicesmodel.h"
|
|
||||||
#include "emojimodel.h"
|
|
||||||
#include "filetypesingleton.h"
|
#include "filetypesingleton.h"
|
||||||
#include "joinrulesevent.h"
|
#include "joinrulesevent.h"
|
||||||
#include "linkpreviewer.h"
|
#include "linkpreviewer.h"
|
||||||
#include "keywordnotificationrulemodel.h"
|
|
||||||
#include "login.h"
|
#include "login.h"
|
||||||
#include "matriximageprovider.h"
|
#include "matriximageprovider.h"
|
||||||
#include "messageeventmodel.h"
|
#include "models/collapsestateproxymodel.h"
|
||||||
#include "messagefiltermodel.h"
|
#include "models/customemojimodel.h"
|
||||||
|
#include "models/devicesmodel.h"
|
||||||
|
#include "models/emojimodel.h"
|
||||||
|
#include "models/keywordnotificationrulemodel.h"
|
||||||
|
#include "models/messageeventmodel.h"
|
||||||
|
#include "models/messagefiltermodel.h"
|
||||||
|
#include "models/publicroomlistmodel.h"
|
||||||
|
#include "models/roomlistmodel.h"
|
||||||
|
#include "models/searchmodel.h"
|
||||||
|
#include "models/serverlistmodel.h"
|
||||||
|
#include "models/sortfilterroomlistmodel.h"
|
||||||
|
#include "models/sortfilterspacelistmodel.h"
|
||||||
|
#include "models/userdirectorylistmodel.h"
|
||||||
|
#include "models/userfiltermodel.h"
|
||||||
|
#include "models/userlistmodel.h"
|
||||||
|
#include "models/webshortcutmodel.h"
|
||||||
#include "neochatconfig.h"
|
#include "neochatconfig.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
#include "neochatuser.h"
|
#include "neochatuser.h"
|
||||||
@@ -62,17 +72,9 @@
|
|||||||
#ifdef QUOTIENT_07
|
#ifdef QUOTIENT_07
|
||||||
#include "pollhandler.h"
|
#include "pollhandler.h"
|
||||||
#endif
|
#endif
|
||||||
#include "publicroomlistmodel.h"
|
|
||||||
#include "roomlistmodel.h"
|
|
||||||
#include "roommanager.h"
|
#include "roommanager.h"
|
||||||
#include "serverlistmodel.h"
|
|
||||||
#include "sortfilterroomlistmodel.h"
|
|
||||||
#include "sortfilterspacelistmodel.h"
|
|
||||||
#include "spacehierarchycache.h"
|
#include "spacehierarchycache.h"
|
||||||
#include "urlhelper.h"
|
#include "urlhelper.h"
|
||||||
#include "userdirectorylistmodel.h"
|
|
||||||
#include "userlistmodel.h"
|
|
||||||
#include "webshortcutmodel.h"
|
|
||||||
#include "windowcontroller.h"
|
#include "windowcontroller.h"
|
||||||
#ifdef QUOTIENT_07
|
#ifdef QUOTIENT_07
|
||||||
#include <keyverificationsession.h>
|
#include <keyverificationsession.h>
|
||||||
@@ -80,15 +82,19 @@
|
|||||||
#ifdef HAVE_COLORSCHEME
|
#ifdef HAVE_COLORSCHEME
|
||||||
#include "colorschemer.h"
|
#include "colorschemer.h"
|
||||||
#endif
|
#endif
|
||||||
#include "completionmodel.h"
|
#include "models/completionmodel.h"
|
||||||
|
#include "models/statemodel.h"
|
||||||
#include "neochatuser.h"
|
#include "neochatuser.h"
|
||||||
#include "statemodel.h"
|
|
||||||
|
|
||||||
#ifdef HAVE_RUNNER
|
#ifdef HAVE_RUNNER
|
||||||
#include "runner.h"
|
#include "runner.h"
|
||||||
#include <QDBusConnection>
|
#include <QDBusConnection>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_WINDOWS
|
||||||
|
#include <Windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
|
class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
|
||||||
@@ -151,7 +157,7 @@ int main(int argc, char *argv[])
|
|||||||
KAboutLicense::GPL_V3,
|
KAboutLicense::GPL_V3,
|
||||||
i18n("© 2018-2020 Black Hat, 2020-2022 KDE Community"));
|
i18n("© 2018-2020 Black Hat, 2020-2022 KDE Community"));
|
||||||
about.addAuthor(i18n("Carl Schwan"), i18n("Maintainer"), QStringLiteral("carl@carlschwan.eu"), QStringLiteral("https://carlschwan.eu"));
|
about.addAuthor(i18n("Carl Schwan"), i18n("Maintainer"), QStringLiteral("carl@carlschwan.eu"), QStringLiteral("https://carlschwan.eu"));
|
||||||
about.addAuthor(i18n("Tobias Fella"), i18n("Maintainer"), QStringLiteral("fella@posteo.de"), QStringLiteral("https://tobiasfella.de"));
|
about.addAuthor(i18n("Tobias Fella"), i18n("Maintainer"), QStringLiteral("tobias.fella@kde.org"), QStringLiteral("https://tobiasfella.de"));
|
||||||
about.addAuthor(i18n("James Graham"), i18n("Maintainer"), QStringLiteral("james.h.graham@protonmail.com"));
|
about.addAuthor(i18n("James Graham"), i18n("Maintainer"), QStringLiteral("james.h.graham@protonmail.com"));
|
||||||
about.addCredit(i18n("Black Hat"), i18n("Original author of Spectral"), QStringLiteral("bhat@encom.eu.org"));
|
about.addCredit(i18n("Black Hat"), i18n("Original author of Spectral"), QStringLiteral("bhat@encom.eu.org"));
|
||||||
about.addCredit(i18n("Alexey Rusakov"), i18n("Maintainer of Quotient"), QStringLiteral("Kitsune-Ral@users.sf.net"));
|
about.addCredit(i18n("Alexey Rusakov"), i18n("Maintainer of Quotient"), QStringLiteral("Kitsune-Ral@users.sf.net"));
|
||||||
@@ -219,6 +225,7 @@ int main(int argc, char *argv[])
|
|||||||
qmlRegisterType<MessageEventModel>("org.kde.neochat", 1, 0, "MessageEventModel");
|
qmlRegisterType<MessageEventModel>("org.kde.neochat", 1, 0, "MessageEventModel");
|
||||||
qmlRegisterType<CollapseStateProxyModel>("org.kde.neochat", 1, 0, "CollapseStateProxyModel");
|
qmlRegisterType<CollapseStateProxyModel>("org.kde.neochat", 1, 0, "CollapseStateProxyModel");
|
||||||
qmlRegisterType<MessageFilterModel>("org.kde.neochat", 1, 0, "MessageFilterModel");
|
qmlRegisterType<MessageFilterModel>("org.kde.neochat", 1, 0, "MessageFilterModel");
|
||||||
|
qmlRegisterType<UserFilterModel>("org.kde.neochat", 1, 0, "UserFilterModel");
|
||||||
qmlRegisterType<PublicRoomListModel>("org.kde.neochat", 1, 0, "PublicRoomListModel");
|
qmlRegisterType<PublicRoomListModel>("org.kde.neochat", 1, 0, "PublicRoomListModel");
|
||||||
qmlRegisterType<UserDirectoryListModel>("org.kde.neochat", 1, 0, "UserDirectoryListModel");
|
qmlRegisterType<UserDirectoryListModel>("org.kde.neochat", 1, 0, "UserDirectoryListModel");
|
||||||
qmlRegisterType<ServerListModel>("org.kde.neochat", 1, 0, "ServerListModel");
|
qmlRegisterType<ServerListModel>("org.kde.neochat", 1, 0, "ServerListModel");
|
||||||
@@ -228,6 +235,7 @@ int main(int argc, char *argv[])
|
|||||||
qmlRegisterType<LinkPreviewer>("org.kde.neochat", 1, 0, "LinkPreviewer");
|
qmlRegisterType<LinkPreviewer>("org.kde.neochat", 1, 0, "LinkPreviewer");
|
||||||
qmlRegisterType<CompletionModel>("org.kde.neochat", 1, 0, "CompletionModel");
|
qmlRegisterType<CompletionModel>("org.kde.neochat", 1, 0, "CompletionModel");
|
||||||
qmlRegisterType<StateModel>("org.kde.neochat", 1, 0, "StateModel");
|
qmlRegisterType<StateModel>("org.kde.neochat", 1, 0, "StateModel");
|
||||||
|
qmlRegisterType<SearchModel>("org.kde.neochat", 1, 0, "SearchModel");
|
||||||
#ifdef QUOTIENT_07
|
#ifdef QUOTIENT_07
|
||||||
qmlRegisterType<PollHandler>("org.kde.neochat", 1, 0, "PollHandler");
|
qmlRegisterType<PollHandler>("org.kde.neochat", 1, 0, "PollHandler");
|
||||||
#endif
|
#endif
|
||||||
@@ -265,6 +273,32 @@ int main(int argc, char *argv[])
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
|
|
||||||
|
#ifdef HAVE_KDBUSADDONS
|
||||||
|
KDBusService service(KDBusService::Unique);
|
||||||
|
service.connect(&service,
|
||||||
|
&KDBusService::activateRequested,
|
||||||
|
&RoomManager::instance(),
|
||||||
|
[&engine](const QStringList &arguments, const QString &workingDirectory) {
|
||||||
|
Q_UNUSED(workingDirectory);
|
||||||
|
|
||||||
|
QWindow *window = windowFromEngine(&engine);
|
||||||
|
KWindowSystem::updateStartupId(window);
|
||||||
|
|
||||||
|
WindowController::instance().showAndRaiseWindow(QString());
|
||||||
|
|
||||||
|
// Open matrix uri
|
||||||
|
if (arguments.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto args = arguments;
|
||||||
|
args.removeFirst();
|
||||||
|
for (const auto &arg : args) {
|
||||||
|
RoomManager::instance().openResource(arg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
|
||||||
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
|
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
|
||||||
QObject::connect(&engine, &QQmlApplicationEngine::quit, &app, &QCoreApplication::quit);
|
QObject::connect(&engine, &QQmlApplicationEngine::quit, &app, &QCoreApplication::quit);
|
||||||
engine.setNetworkAccessManagerFactory(new NetworkAccessManagerFactory());
|
engine.setNetworkAccessManagerFactory(new NetworkAccessManagerFactory());
|
||||||
@@ -294,31 +328,6 @@ int main(int argc, char *argv[])
|
|||||||
QDBusConnection::sessionBus().registerObject("/RoomRunner", &runner, QDBusConnection::ExportScriptableContents);
|
QDBusConnection::sessionBus().registerObject("/RoomRunner", &runner, QDBusConnection::ExportScriptableContents);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_KDBUSADDONS
|
|
||||||
KDBusService service(KDBusService::Unique);
|
|
||||||
service.connect(&service,
|
|
||||||
&KDBusService::activateRequested,
|
|
||||||
&RoomManager::instance(),
|
|
||||||
[&engine](const QStringList &arguments, const QString &workingDirectory) {
|
|
||||||
Q_UNUSED(workingDirectory);
|
|
||||||
|
|
||||||
QWindow *window = windowFromEngine(&engine);
|
|
||||||
KWindowSystem::updateStartupId(window);
|
|
||||||
|
|
||||||
WindowController::instance().showAndRaiseWindow(QString());
|
|
||||||
|
|
||||||
// Open matrix uri
|
|
||||||
if (arguments.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto args = arguments;
|
|
||||||
args.removeFirst();
|
|
||||||
for (const auto &arg : args) {
|
|
||||||
RoomManager::instance().openResource(arg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QWindow *window = windowFromEngine(&engine);
|
QWindow *window = windowFromEngine(&engine);
|
||||||
|
|
||||||
WindowController::instance().setWindow(window);
|
WindowController::instance().setWindow(window);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
#include "actionsmodel.h"
|
#include "actionsmodel.h"
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
#include "neochatuser.h"
|
#include "neochatuser.h"
|
||||||
|
#include "roommanager.h"
|
||||||
#include <events/roommemberevent.h>
|
#include <events/roommemberevent.h>
|
||||||
#include <events/roompowerlevelsevent.h>
|
#include <events/roompowerlevelsevent.h>
|
||||||
|
|
||||||
@@ -114,7 +115,7 @@ QVector<ActionsModel::Action> actions{
|
|||||||
QStringLiteral("spoiler"),
|
QStringLiteral("spoiler"),
|
||||||
[](const QString &text, NeoChatRoom *room) {
|
[](const QString &text, NeoChatRoom *room) {
|
||||||
// Ideally, we would just return rainbowText and let that do the rest, but the colors don't survive markdownToHTML.
|
// Ideally, we would just return rainbowText and let that do the rest, but the colors don't survive markdownToHTML.
|
||||||
room->postMessage(QStringLiteral("/rainbow %1").arg(text),
|
room->postMessage(QStringLiteral("/spoiler %1").arg(text),
|
||||||
QStringLiteral("<span data-mx-spoiler>%1</span>").arg(text),
|
QStringLiteral("<span data-mx-spoiler>%1</span>").arg(text),
|
||||||
RoomMessageEvent::MsgType::Text,
|
RoomMessageEvent::MsgType::Text,
|
||||||
room->chatBoxReplyId(),
|
room->chatBoxReplyId(),
|
||||||
@@ -157,11 +158,12 @@ QVector<ActionsModel::Action> actions{
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
#ifdef QUOTIENT_07
|
#ifdef QUOTIENT_07
|
||||||
if (room->currentState().get<RoomMemberEvent>(text)->membership() == Membership::Invite) {
|
const RoomMemberEvent *roomMemberEvent = room->currentState().get<RoomMemberEvent>(text);
|
||||||
|
if (roomMemberEvent && roomMemberEvent->membership() == Membership::Invite) {
|
||||||
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is already invited to this room.", "%1 is already invited to this room.", text));
|
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is already invited to this room.", "%1 is already invited to this room.", text));
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
if (room->currentState().get<RoomMemberEvent>(text)->membership() == Membership::Ban) {
|
if (roomMemberEvent && roomMemberEvent->membership() == Membership::Ban) {
|
||||||
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is banned from this room.", "%1 is banned from this room.", text));
|
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is banned from this room.", "%1 is banned from this room.", text));
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
@@ -193,8 +195,9 @@ QVector<ActionsModel::Action> actions{
|
|||||||
i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
|
i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
if (Controller::instance().activeConnection()->room(text) || Controller::instance().activeConnection()->roomByAlias(text)) {
|
auto targetRoom = text.startsWith(QLatin1Char('!')) ? room->connection()->room(text) : room->connection()->roomByAlias(text);
|
||||||
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("You are already in room <roomname>.", "You are already in room %1.", text));
|
if (targetRoom) {
|
||||||
|
RoomManager::instance().enterRoom(dynamic_cast<NeoChatRoom *>(targetRoom));
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Joining room <roomname>.", "Joining room %1.", text));
|
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Joining room <roomname>.", "Joining room %1.", text));
|
||||||
@@ -206,6 +209,40 @@ QVector<ActionsModel::Action> actions{
|
|||||||
kli18n("<room alias or id>"),
|
kli18n("<room alias or id>"),
|
||||||
kli18n("Joins the given room"),
|
kli18n("Joins the given room"),
|
||||||
},
|
},
|
||||||
|
#ifdef QUOTIENT_07
|
||||||
|
Action{
|
||||||
|
QStringLiteral("knock"),
|
||||||
|
[](const QString &text, NeoChatRoom *room) {
|
||||||
|
auto parts = text.split(QLatin1String(" "));
|
||||||
|
QString roomName = parts[0];
|
||||||
|
QRegularExpression roomRegex(QStringLiteral(R"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"));
|
||||||
|
auto regexMatch = roomRegex.match(roomName);
|
||||||
|
if (!regexMatch.hasMatch()) {
|
||||||
|
Q_EMIT room->showMessage(NeoChatRoom::Error,
|
||||||
|
i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
auto targetRoom = text.startsWith(QLatin1Char('!')) ? room->connection()->room(text) : room->connection()->roomByAlias(text);
|
||||||
|
if (targetRoom) {
|
||||||
|
RoomManager::instance().enterRoom(dynamic_cast<NeoChatRoom *>(targetRoom));
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Knocking room <roomname>.", "Knocking room %1.", text));
|
||||||
|
auto connection = Controller::instance().activeConnection();
|
||||||
|
const auto knownServer = roomName.mid(roomName.indexOf(":") + 1);
|
||||||
|
if (parts.length() >= 2) {
|
||||||
|
RoomManager::instance().knockRoom(connection, roomName, parts[1], QStringList{knownServer});
|
||||||
|
} else {
|
||||||
|
RoomManager::instance().knockRoom(connection, roomName, QString(), QStringList{knownServer});
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
std::nullopt,
|
||||||
|
kli18n("<room alias or id> [<reason>]"),
|
||||||
|
kli18n("Requests to join the given room"),
|
||||||
|
},
|
||||||
|
#endif
|
||||||
Action{
|
Action{
|
||||||
QStringLiteral("j"),
|
QStringLiteral("j"),
|
||||||
[](const QString &text, NeoChatRoom *room) {
|
[](const QString &text, NeoChatRoom *room) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
134
src/models/collapsestateproxymodel.cpp
Normal file
134
src/models/collapsestateproxymodel.cpp
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "collapsestateproxymodel.h"
|
||||||
|
#include "messageeventmodel.h"
|
||||||
|
|
||||||
|
#include <KLocalizedString>
|
||||||
|
|
||||||
|
bool CollapseStateProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(source_parent);
|
||||||
|
return sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::EventTypeRole)
|
||||||
|
!= MessageEventModel::DelegateType::State // If this is not a state, show it
|
||||||
|
|| sourceModel()->data(sourceModel()->index(source_row + 1, 0), MessageEventModel::EventTypeRole)
|
||||||
|
!= MessageEventModel::DelegateType::State // If this is the first state in a block, show it. TODO hidden events?
|
||||||
|
|| sourceModel()->data(sourceModel()->index(source_row, 0), MessageEventModel::ShowSectionRole).toBool(); // If it's a new day, show it
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant CollapseStateProxyModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (role == AggregateDisplayRole) {
|
||||||
|
return aggregateEventToString(mapToSource(index).row());
|
||||||
|
} else if (role == StateEventsRole) {
|
||||||
|
return stateEventsList(mapToSource(index).row());
|
||||||
|
} else if (role == AuthorListRole) {
|
||||||
|
return authorList(mapToSource(index).row());
|
||||||
|
}
|
||||||
|
return sourceModel()->data(mapToSource(index), role);
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> CollapseStateProxyModel::roleNames() const
|
||||||
|
{
|
||||||
|
auto roles = sourceModel()->roleNames();
|
||||||
|
roles[AggregateDisplayRole] = "aggregateDisplay";
|
||||||
|
roles[StateEventsRole] = "stateEvents";
|
||||||
|
roles[AuthorListRole] = "authorList";
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CollapseStateProxyModel::aggregateEventToString(int sourceRow) const
|
||||||
|
{
|
||||||
|
QStringList parts;
|
||||||
|
QVariantList uniqueAuthors;
|
||||||
|
for (int i = sourceRow; i >= 0; i--) {
|
||||||
|
parts += sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::GenericDisplayRole).toString();
|
||||||
|
QVariant nextAuthor = sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::AuthorRole);
|
||||||
|
if (!uniqueAuthors.contains(nextAuthor)) {
|
||||||
|
uniqueAuthors.append(nextAuthor);
|
||||||
|
}
|
||||||
|
if (sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::EventTypeRole)
|
||||||
|
!= MessageEventModel::DelegateType::State // If it's not a state event
|
||||||
|
|| sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::ShowSectionRole).toBool() // or the section needs to be visible
|
||||||
|
) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parts.sort(); // Sort them so that all identical events can be collected.
|
||||||
|
if (!parts.isEmpty()) {
|
||||||
|
QStringList chunks;
|
||||||
|
while (!parts.isEmpty()) {
|
||||||
|
chunks += QString();
|
||||||
|
int count = 1;
|
||||||
|
auto part = parts.takeFirst();
|
||||||
|
chunks.last() += part;
|
||||||
|
while (!parts.isEmpty() && parts.first() == part) {
|
||||||
|
parts.removeFirst();
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (count > 1 && uniqueAuthors.length() == 1) {
|
||||||
|
chunks.last() += i18ncp("n times", " %1 time ", " %1 times ", count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chunks.removeDuplicates();
|
||||||
|
QString text = "<style>a {text-decoration: none;}</style>"; // There can be links in the event text so make sure all are styled.
|
||||||
|
// The author text is either "n users" if > 1 user or the matrix.to link to a single user.
|
||||||
|
QString userText = uniqueAuthors.length() > 1 ? i18ncp("n users", " %1 user ", " %1 users ", uniqueAuthors.length())
|
||||||
|
: QStringLiteral("<a href=\"https://matrix.to/#/%1\" style=\"color: %2\">%3</a> ")
|
||||||
|
.arg(uniqueAuthors[0].toMap()["id"].toString(),
|
||||||
|
uniqueAuthors[0].toMap()["color"].toString(),
|
||||||
|
uniqueAuthors[0].toMap()["displayName"].toString());
|
||||||
|
text += userText;
|
||||||
|
text += chunks.takeFirst();
|
||||||
|
|
||||||
|
if (chunks.size() > 0) {
|
||||||
|
while (chunks.size() > 1) {
|
||||||
|
text += i18nc("[action 1], [action 2 and/or action 3]", ", ");
|
||||||
|
text += chunks.takeFirst();
|
||||||
|
}
|
||||||
|
text += uniqueAuthors.length() > 1 ? i18nc("[action 1, action 2] or [action 3]", " or ") : i18nc("[action 1, action 2] and [action 3]", " and ");
|
||||||
|
text += chunks.takeFirst();
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList CollapseStateProxyModel::stateEventsList(int sourceRow) const
|
||||||
|
{
|
||||||
|
QVariantList stateEvents;
|
||||||
|
for (int i = sourceRow; i >= 0; i--) {
|
||||||
|
auto nextState = QVariantMap{
|
||||||
|
{"author", sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::AuthorRole)},
|
||||||
|
{"authorDisplayName", sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::AuthorDisplayNameRole).toString()},
|
||||||
|
{"text", sourceModel()->data(sourceModel()->index(i, 0), Qt::DisplayRole).toString()},
|
||||||
|
};
|
||||||
|
stateEvents.append(nextState);
|
||||||
|
if (sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::EventTypeRole)
|
||||||
|
!= MessageEventModel::DelegateType::State // If it's not a state event
|
||||||
|
|| sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::ShowSectionRole).toBool() // or the section needs to be visible
|
||||||
|
) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stateEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList CollapseStateProxyModel::authorList(int sourceRow) const
|
||||||
|
{
|
||||||
|
QVariantList uniqueAuthors;
|
||||||
|
for (int i = sourceRow; i >= 0; i--) {
|
||||||
|
QVariant nextAvatar = sourceModel()->data(sourceModel()->index(i, 0), MessageEventModel::AuthorRole);
|
||||||
|
if (!uniqueAuthors.contains(nextAvatar)) {
|
||||||
|
uniqueAuthors.append(nextAvatar);
|
||||||
|
}
|
||||||
|
if (sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::EventTypeRole)
|
||||||
|
!= MessageEventModel::DelegateType::State // If it's not a state event
|
||||||
|
|| sourceModel()->data(sourceModel()->index(i - 1, 0), MessageEventModel::ShowSectionRole).toBool() // or the section needs to be visible
|
||||||
|
) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uniqueAuthors;
|
||||||
|
}
|
||||||
40
src/models/collapsestateproxymodel.h
Normal file
40
src/models/collapsestateproxymodel.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "messageeventmodel.h"
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
class CollapseStateProxyModel : public QSortFilterProxyModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum Roles {
|
||||||
|
AggregateDisplayRole = MessageEventModel::LastRole + 1,
|
||||||
|
StateEventsRole,
|
||||||
|
AuthorListRole,
|
||||||
|
};
|
||||||
|
[[nodiscard]] bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
||||||
|
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||||
|
[[nodiscard]] QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief QString aggregating the text of consecutive state events starting at row.
|
||||||
|
*
|
||||||
|
* If state events happen on different days they will be split into two aggregate
|
||||||
|
* events.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QString aggregateEventToString(int row) const;
|
||||||
|
/**
|
||||||
|
* @brief Return a list of consecutive state events starting at row.
|
||||||
|
*
|
||||||
|
* If state events happen on different days they will be split into two aggregate
|
||||||
|
* events.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QVariantList stateEventsList(int row) const;
|
||||||
|
/**
|
||||||
|
* @brief List of unique authors for the aggregate state events starting at row.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QVariantList authorList(int row) const;
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
#include "completionmodel.h"
|
#include "completionmodel.h"
|
||||||
@@ -10,14 +10,13 @@
|
|||||||
#include "customemojimodel.h"
|
#include "customemojimodel.h"
|
||||||
#include "emojimodel.h"
|
#include "emojimodel.h"
|
||||||
#include "neochatroom.h"
|
#include "neochatroom.h"
|
||||||
#include "roomlistmodel.h"
|
|
||||||
#include "userlistmodel.h"
|
#include "userlistmodel.h"
|
||||||
|
|
||||||
CompletionModel::CompletionModel(QObject *parent)
|
CompletionModel::CompletionModel(QObject *parent)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
, m_filterModel(new CompletionProxyModel())
|
, m_filterModel(new CompletionProxyModel())
|
||||||
, m_userListModel(new UserListModel(this))
|
, m_userListModel(new UserListModel(this))
|
||||||
, m_emojiModel(new KConcatenateRowsProxyModel(this))
|
, m_emojiModel(new QConcatenateTablesProxyModel(this))
|
||||||
{
|
{
|
||||||
connect(this, &CompletionModel::textChanged, this, &CompletionModel::updateCompletion);
|
connect(this, &CompletionModel::textChanged, this, &CompletionModel::updateCompletion);
|
||||||
connect(this, &CompletionModel::roomChanged, this, [this]() {
|
connect(this, &CompletionModel::roomChanged, this, [this]() {
|
||||||
@@ -42,7 +41,7 @@ void CompletionModel::setText(const QString &text, const QString &fullText)
|
|||||||
int CompletionModel::rowCount(const QModelIndex &parent) const
|
int CompletionModel::rowCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
Q_UNUSED(parent);
|
Q_UNUSED(parent);
|
||||||
if (m_autoCompletionType == ChatDocumentHandler::None) {
|
if (m_autoCompletionType == None) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return m_filterModel->rowCount();
|
return m_filterModel->rowCount();
|
||||||
@@ -54,7 +53,7 @@ QVariant CompletionModel::data(const QModelIndex &index, int role) const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
auto filterIndex = m_filterModel->index(index.row(), 0);
|
auto filterIndex = m_filterModel->index(index.row(), 0);
|
||||||
if (m_autoCompletionType == ChatDocumentHandler::User) {
|
if (m_autoCompletionType == User) {
|
||||||
if (role == Text) {
|
if (role == Text) {
|
||||||
return m_filterModel->data(filterIndex, UserListModel::NameRole);
|
return m_filterModel->data(filterIndex, UserListModel::NameRole);
|
||||||
}
|
}
|
||||||
@@ -66,7 +65,7 @@ QVariant CompletionModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_autoCompletionType == ChatDocumentHandler::Command) {
|
if (m_autoCompletionType == Command) {
|
||||||
if (role == Text) {
|
if (role == Text) {
|
||||||
return m_filterModel->data(filterIndex, ActionsModel::Prefix).toString() + QStringLiteral(" ")
|
return m_filterModel->data(filterIndex, ActionsModel::Prefix).toString() + QStringLiteral(" ")
|
||||||
+ m_filterModel->data(filterIndex, ActionsModel::Parameters).toString();
|
+ m_filterModel->data(filterIndex, ActionsModel::Parameters).toString();
|
||||||
@@ -81,7 +80,7 @@ QVariant CompletionModel::data(const QModelIndex &index, int role) const
|
|||||||
return m_filterModel->data(filterIndex, ActionsModel::Prefix);
|
return m_filterModel->data(filterIndex, ActionsModel::Prefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_autoCompletionType == ChatDocumentHandler::Room) {
|
if (m_autoCompletionType == Room) {
|
||||||
if (role == Text) {
|
if (role == Text) {
|
||||||
return m_filterModel->data(filterIndex, RoomListModel::DisplayNameRole);
|
return m_filterModel->data(filterIndex, RoomListModel::DisplayNameRole);
|
||||||
}
|
}
|
||||||
@@ -92,7 +91,7 @@ QVariant CompletionModel::data(const QModelIndex &index, int role) const
|
|||||||
return m_filterModel->data(filterIndex, RoomListModel::AvatarRole);
|
return m_filterModel->data(filterIndex, RoomListModel::AvatarRole);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_autoCompletionType == ChatDocumentHandler::Emoji) {
|
if (m_autoCompletionType == Emoji) {
|
||||||
if (role == Text) {
|
if (role == Text) {
|
||||||
return m_filterModel->data(filterIndex, CustomEmojiModel::DisplayRole);
|
return m_filterModel->data(filterIndex, CustomEmojiModel::DisplayRole);
|
||||||
}
|
}
|
||||||
@@ -102,6 +101,9 @@ QVariant CompletionModel::data(const QModelIndex &index, int role) const
|
|||||||
if (role == ReplacedText) {
|
if (role == ReplacedText) {
|
||||||
return m_filterModel->data(filterIndex, CustomEmojiModel::ReplacedTextRole);
|
return m_filterModel->data(filterIndex, CustomEmojiModel::ReplacedTextRole);
|
||||||
}
|
}
|
||||||
|
if (role == Subtitle) {
|
||||||
|
return m_filterModel->data(filterIndex, EmojiModel::DescriptionRole);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@@ -125,7 +127,7 @@ void CompletionModel::updateCompletion()
|
|||||||
m_filterModel->setSecondaryFilterRole(UserListModel::NameRole);
|
m_filterModel->setSecondaryFilterRole(UserListModel::NameRole);
|
||||||
m_filterModel->setFullText(m_fullText);
|
m_filterModel->setFullText(m_fullText);
|
||||||
m_filterModel->setFilterText(m_text);
|
m_filterModel->setFilterText(m_text);
|
||||||
m_autoCompletionType = ChatDocumentHandler::User;
|
m_autoCompletionType = User;
|
||||||
m_filterModel->invalidate();
|
m_filterModel->invalidate();
|
||||||
} else if (text().startsWith(QLatin1Char('/'))) {
|
} else if (text().startsWith(QLatin1Char('/'))) {
|
||||||
m_filterModel->setSourceModel(&ActionsModel::instance());
|
m_filterModel->setSourceModel(&ActionsModel::instance());
|
||||||
@@ -133,28 +135,28 @@ void CompletionModel::updateCompletion()
|
|||||||
m_filterModel->setSecondaryFilterRole(-1);
|
m_filterModel->setSecondaryFilterRole(-1);
|
||||||
m_filterModel->setFullText(m_fullText);
|
m_filterModel->setFullText(m_fullText);
|
||||||
m_filterModel->setFilterText(m_text.mid(1));
|
m_filterModel->setFilterText(m_text.mid(1));
|
||||||
m_autoCompletionType = ChatDocumentHandler::Command;
|
m_autoCompletionType = Command;
|
||||||
m_filterModel->invalidate();
|
m_filterModel->invalidate();
|
||||||
} else if (text().startsWith(QLatin1Char('#'))) {
|
} else if (text().startsWith(QLatin1Char('#'))) {
|
||||||
m_autoCompletionType = ChatDocumentHandler::Room;
|
m_autoCompletionType = Room;
|
||||||
m_filterModel->setSourceModel(m_roomListModel);
|
m_filterModel->setSourceModel(m_roomListModel);
|
||||||
m_filterModel->setFilterRole(RoomListModel::CanonicalAliasRole);
|
m_filterModel->setFilterRole(RoomListModel::CanonicalAliasRole);
|
||||||
m_filterModel->setSecondaryFilterRole(RoomListModel::DisplayNameRole);
|
m_filterModel->setSecondaryFilterRole(RoomListModel::DisplayNameRole);
|
||||||
m_filterModel->setFullText(m_fullText);
|
m_filterModel->setFullText(m_fullText);
|
||||||
m_filterModel->setFilterText(m_text);
|
m_filterModel->setFilterText(m_text);
|
||||||
m_filterModel->invalidate();
|
m_filterModel->invalidate();
|
||||||
} else if (text().startsWith(QLatin1Char(':'))
|
} else if (text().startsWith(QLatin1Char(':')) && !text()[1].isUpper()
|
||||||
&& (m_fullText.indexOf(QLatin1Char(':'), 1) == -1
|
&& (m_fullText.indexOf(QLatin1Char(':'), 1) == -1
|
||||||
|| (m_fullText.indexOf(QLatin1Char(' ')) != -1 && m_fullText.indexOf(QLatin1Char(':'), 1) > m_fullText.indexOf(QLatin1Char(' '), 1)))) {
|
|| (m_fullText.indexOf(QLatin1Char(' ')) != -1 && m_fullText.indexOf(QLatin1Char(':'), 1) > m_fullText.indexOf(QLatin1Char(' '), 1)))) {
|
||||||
m_autoCompletionType = ChatDocumentHandler::Emoji;
|
|
||||||
m_filterModel->setSourceModel(m_emojiModel);
|
m_filterModel->setSourceModel(m_emojiModel);
|
||||||
|
m_autoCompletionType = Emoji;
|
||||||
m_filterModel->setFilterRole(CustomEmojiModel::Name);
|
m_filterModel->setFilterRole(CustomEmojiModel::Name);
|
||||||
m_filterModel->setSecondaryFilterRole(-1);
|
m_filterModel->setSecondaryFilterRole(EmojiModel::DescriptionRole);
|
||||||
m_filterModel->setFullText(m_fullText);
|
m_filterModel->setFullText(m_fullText);
|
||||||
m_filterModel->setFilterText(m_text);
|
m_filterModel->setFilterText(m_text);
|
||||||
m_filterModel->invalidate();
|
m_filterModel->invalidate();
|
||||||
} else {
|
} else {
|
||||||
m_autoCompletionType = ChatDocumentHandler::None;
|
m_autoCompletionType = None;
|
||||||
}
|
}
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
@@ -171,12 +173,12 @@ void CompletionModel::setRoom(NeoChatRoom *room)
|
|||||||
Q_EMIT roomChanged();
|
Q_EMIT roomChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatDocumentHandler::AutoCompletionType CompletionModel::autoCompletionType() const
|
CompletionModel::AutoCompletionType CompletionModel::autoCompletionType() const
|
||||||
{
|
{
|
||||||
return m_autoCompletionType;
|
return m_autoCompletionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompletionModel::setAutoCompletionType(ChatDocumentHandler::AutoCompletionType autoCompletionType)
|
void CompletionModel::setAutoCompletionType(AutoCompletionType autoCompletionType)
|
||||||
{
|
{
|
||||||
m_autoCompletionType = autoCompletionType;
|
m_autoCompletionType = autoCompletionType;
|
||||||
Q_EMIT autoCompletionTypeChanged();
|
Q_EMIT autoCompletionTypeChanged();
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QConcatenateTablesProxyModel>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
#include <KConcatenateRowsProxyModel>
|
#include "roomlistmodel.h"
|
||||||
|
|
||||||
#include "chatdocumenthandler.h"
|
|
||||||
|
|
||||||
class CompletionProxyModel;
|
class CompletionProxyModel;
|
||||||
class UserListModel;
|
class UserListModel;
|
||||||
@@ -19,10 +18,19 @@ class CompletionModel : public QAbstractListModel
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(QString text READ text NOTIFY textChanged)
|
Q_PROPERTY(QString text READ text NOTIFY textChanged)
|
||||||
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||||
Q_PROPERTY(ChatDocumentHandler::AutoCompletionType autoCompletionType READ autoCompletionType NOTIFY autoCompletionTypeChanged);
|
Q_PROPERTY(AutoCompletionType autoCompletionType READ autoCompletionType NOTIFY autoCompletionTypeChanged);
|
||||||
Q_PROPERTY(RoomListModel *roomListModel READ roomListModel WRITE setRoomListModel NOTIFY roomListModelChanged);
|
Q_PROPERTY(RoomListModel *roomListModel READ roomListModel WRITE setRoomListModel NOTIFY roomListModelChanged);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum AutoCompletionType {
|
||||||
|
User,
|
||||||
|
Room,
|
||||||
|
Emoji,
|
||||||
|
Command,
|
||||||
|
None,
|
||||||
|
};
|
||||||
|
Q_ENUM(AutoCompletionType)
|
||||||
|
|
||||||
enum Roles {
|
enum Roles {
|
||||||
Text = Qt::DisplayRole,
|
Text = Qt::DisplayRole,
|
||||||
Subtitle,
|
Subtitle,
|
||||||
@@ -47,7 +55,7 @@ public:
|
|||||||
RoomListModel *roomListModel() const;
|
RoomListModel *roomListModel() const;
|
||||||
void setRoomListModel(RoomListModel *roomListModel);
|
void setRoomListModel(RoomListModel *roomListModel);
|
||||||
|
|
||||||
ChatDocumentHandler::AutoCompletionType autoCompletionType() const;
|
AutoCompletionType autoCompletionType() const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void textChanged();
|
void textChanged();
|
||||||
@@ -60,11 +68,12 @@ private:
|
|||||||
QString m_fullText;
|
QString m_fullText;
|
||||||
CompletionProxyModel *m_filterModel;
|
CompletionProxyModel *m_filterModel;
|
||||||
NeoChatRoom *m_room = nullptr;
|
NeoChatRoom *m_room = nullptr;
|
||||||
ChatDocumentHandler::AutoCompletionType m_autoCompletionType = ChatDocumentHandler::None;
|
AutoCompletionType m_autoCompletionType = None;
|
||||||
|
|
||||||
void setAutoCompletionType(ChatDocumentHandler::AutoCompletionType autoCompletionType);
|
void setAutoCompletionType(AutoCompletionType autoCompletionType);
|
||||||
|
|
||||||
UserListModel *m_userListModel;
|
UserListModel *m_userListModel;
|
||||||
RoomListModel *m_roomListModel;
|
RoomListModel *m_roomListModel;
|
||||||
KConcatenateRowsProxyModel *m_emojiModel;
|
QConcatenateTablesProxyModel *m_emojiModel;
|
||||||
};
|
};
|
||||||
|
Q_DECLARE_METATYPE(CompletionModel::AutoCompletionType);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
#include "completionproxymodel.h"
|
#include "completionproxymodel.h"
|
||||||
@@ -18,7 +18,22 @@ bool CompletionProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &so
|
|||||||
&& sourceModel()
|
&& sourceModel()
|
||||||
->data(sourceModel()->index(sourceRow, 0), secondaryFilterRole())
|
->data(sourceModel()->index(sourceRow, 0), secondaryFilterRole())
|
||||||
.toString()
|
.toString()
|
||||||
|
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
|
||||||
|
.startsWith(QStringView(m_filterText).sliced(1), Qt::CaseInsensitive));
|
||||||
|
#else
|
||||||
.startsWith(m_filterText.midRef(1), Qt::CaseInsensitive));
|
.startsWith(m_filterText.midRef(1), Qt::CaseInsensitive));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CompletionProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
|
||||||
|
{
|
||||||
|
if (m_secondaryFilterRole == -1)
|
||||||
|
return QSortFilterProxyModel::lessThan(source_left, source_right);
|
||||||
|
bool left_primary = sourceModel()->data(source_left, filterRole()).toString().startsWith(m_filterText, Qt::CaseInsensitive);
|
||||||
|
bool right_primary = sourceModel()->data(source_right, filterRole()).toString().startsWith(m_filterText, Qt::CaseInsensitive);
|
||||||
|
if (left_primary != right_primary)
|
||||||
|
return left_primary;
|
||||||
|
return QSortFilterProxyModel::lessThan(source_left, source_right);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CompletionProxyModel::secondaryFilterRole() const
|
int CompletionProxyModel::secondaryFilterRole() const
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -13,6 +13,7 @@ class CompletionProxyModel : public QSortFilterProxyModel
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||||
|
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
|
||||||
|
|
||||||
int secondaryFilterRole() const;
|
int secondaryFilterRole() const;
|
||||||
void setSecondaryFilterRole(int role);
|
void setSecondaryFilterRole(int role);
|
||||||
@@ -4,13 +4,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <memory>
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
struct CustomEmoji {
|
struct CustomEmoji {
|
||||||
QString name; // with :semicolons:
|
QString name; // with :semicolons:
|
||||||
QString url; // mxc://
|
QString url; // mxc://
|
||||||
QRegularExpression regexp;
|
QRegularExpression regexp;
|
||||||
|
|
||||||
|
Q_GADGET
|
||||||
|
Q_PROPERTY(QString unicode MEMBER url)
|
||||||
|
Q_PROPERTY(QString name MEMBER name)
|
||||||
};
|
};
|
||||||
|
|
||||||
class CustomEmojiModel : public QAbstractListModel
|
class CustomEmojiModel : public QAbstractListModel
|
||||||
@@ -25,6 +29,7 @@ public:
|
|||||||
MxcUrl = 50,
|
MxcUrl = 50,
|
||||||
DisplayRole = 51,
|
DisplayRole = 51,
|
||||||
ReplacedTextRole = 52,
|
ReplacedTextRole = 52,
|
||||||
|
DescriptionRole = 53, // also invalid, reserved
|
||||||
};
|
};
|
||||||
Q_ENUM(Roles);
|
Q_ENUM(Roles);
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "devicesmodel.h"
|
#include "devicesmodel.h"
|
||||||
@@ -70,7 +70,12 @@ void DevicesModel::logout(int index, const QString &password)
|
|||||||
auto job = Controller::instance().activeConnection()->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId);
|
auto job = Controller::instance().activeConnection()->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId);
|
||||||
|
|
||||||
connect(job, &BaseJob::result, this, [this, job, password, index] {
|
connect(job, &BaseJob::result, this, [this, job, password, index] {
|
||||||
if (job->error() != 0) {
|
auto onSuccess = [this, index]() {
|
||||||
|
beginRemoveRows(QModelIndex(), index, index);
|
||||||
|
m_devices.remove(index);
|
||||||
|
endRemoveRows();
|
||||||
|
};
|
||||||
|
if (job->error() != BaseJob::Success) {
|
||||||
QJsonObject replyData = job->jsonData();
|
QJsonObject replyData = job->jsonData();
|
||||||
QJsonObject authData;
|
QJsonObject authData;
|
||||||
authData["session"] = replyData["session"];
|
authData["session"] = replyData["session"];
|
||||||
@@ -79,11 +84,9 @@ void DevicesModel::logout(int index, const QString &password)
|
|||||||
QJsonObject identifier = {{"type", "m.id.user"}, {"user", Controller::instance().activeConnection()->user()->id()}};
|
QJsonObject identifier = {{"type", "m.id.user"}, {"user", Controller::instance().activeConnection()->user()->id()}};
|
||||||
authData["identifier"] = identifier;
|
authData["identifier"] = identifier;
|
||||||
auto *innerJob = Controller::instance().activeConnection()->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId, authData);
|
auto *innerJob = Controller::instance().activeConnection()->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId, authData);
|
||||||
connect(innerJob, &BaseJob::success, this, [this, index]() {
|
connect(innerJob, &BaseJob::success, this, onSuccess);
|
||||||
beginRemoveRows(QModelIndex(), index, index);
|
} else {
|
||||||
m_devices.remove(index);
|
onSuccess();
|
||||||
endRemoveRows();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Tobias Fella <fella@posteo.de>
|
// SPDX-FileCopyrightText: Tobias Fella <tobias.fella@kde.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -4,12 +4,13 @@
|
|||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
#include "emojimodel.h"
|
#include "emojimodel.h"
|
||||||
|
#include "emojitones.h"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "customemojimodel.h"
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
#include <qnamespace.h>
|
|
||||||
|
|
||||||
EmojiModel::EmojiModel(QObject *parent)
|
EmojiModel::EmojiModel(QObject *parent)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
@@ -48,6 +49,8 @@ QVariant EmojiModel::data(const QModelIndex &index, int role) const
|
|||||||
return QStringLiteral("invalid");
|
return QStringLiteral("invalid");
|
||||||
case DisplayRole:
|
case DisplayRole:
|
||||||
return QStringLiteral("%2 :%1:").arg(emoji.shortName, emoji.unicode);
|
return QStringLiteral("%2 :%1:").arg(emoji.shortName, emoji.unicode);
|
||||||
|
case DescriptionRole:
|
||||||
|
return emoji.description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
@@ -58,16 +61,19 @@ QHash<int, QByteArray> EmojiModel::roleNames() const
|
|||||||
return {{ShortNameRole, "shortName"}, {UnicodeRole, "unicode"}};
|
return {{ShortNameRole, "shortName"}, {UnicodeRole, "unicode"}};
|
||||||
}
|
}
|
||||||
|
|
||||||
QMultiHash<QString, QVariant> EmojiModel::_tones = {
|
|
||||||
#include "emojitones.h"
|
|
||||||
};
|
|
||||||
|
|
||||||
QVariantList EmojiModel::history() const
|
QVariantList EmojiModel::history() const
|
||||||
{
|
{
|
||||||
return m_settings.value("Editor/emojis", QVariantList()).toList();
|
return m_settings.value("Editor/emojis", QVariantList()).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantList EmojiModel::filterModel(const QString &filter, bool limit)
|
QVariantList EmojiModel::filterModel(const QString &filter, bool limit)
|
||||||
|
{
|
||||||
|
auto emojis = CustomEmojiModel::instance().filterModel(filter);
|
||||||
|
emojis += filterModelNoCustom(filter, limit);
|
||||||
|
return emojis;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList EmojiModel::filterModelNoCustom(const QString &filter, bool limit)
|
||||||
{
|
{
|
||||||
QVariantList result;
|
QVariantList result;
|
||||||
|
|
||||||
@@ -82,7 +88,6 @@ QVariantList EmojiModel::filterModel(const QString &filter, bool limit)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,12 +115,28 @@ QVariantList EmojiModel::emojis(Category category) const
|
|||||||
if (category == History) {
|
if (category == History) {
|
||||||
return history();
|
return history();
|
||||||
}
|
}
|
||||||
|
if (category == HistoryNoCustom) {
|
||||||
|
QVariantList list;
|
||||||
|
for (const auto &e : history()) {
|
||||||
|
auto emoji = qvariant_cast<Emoji>(e);
|
||||||
|
if (!emoji.isCustom) {
|
||||||
|
list.append(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
if (category == Custom) {
|
||||||
|
return CustomEmojiModel::instance().filterModel({});
|
||||||
|
}
|
||||||
return _emojis[category];
|
return _emojis[category];
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantList EmojiModel::tones(const QString &baseEmoji) const
|
QVariantList EmojiModel::tones(const QString &baseEmoji) const
|
||||||
{
|
{
|
||||||
return _tones.values(baseEmoji);
|
if (baseEmoji.endsWith("tone")) {
|
||||||
|
return EmojiTones::_tones.values(baseEmoji.split(":")[0]);
|
||||||
|
}
|
||||||
|
return EmojiTones::_tones.values(baseEmoji);
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<EmojiModel::Category, QVariantList> EmojiModel::_emojis;
|
QHash<EmojiModel::Category, QVariantList> EmojiModel::_emojis;
|
||||||
@@ -123,21 +144,11 @@ QHash<EmojiModel::Category, QVariantList> EmojiModel::_emojis;
|
|||||||
QVariantList EmojiModel::categories() const
|
QVariantList EmojiModel::categories() const
|
||||||
{
|
{
|
||||||
return QVariantList{
|
return QVariantList{
|
||||||
// {QVariantMap{
|
|
||||||
// {"category", EmojiModel::Search},
|
|
||||||
// {"name", i18nc("Search for emojis", "Search")},
|
|
||||||
// {"emoji", QStringLiteral("🔎")},
|
|
||||||
// }},
|
|
||||||
{QVariantMap{
|
{QVariantMap{
|
||||||
{"category", EmojiModel::History},
|
{"category", EmojiModel::HistoryNoCustom},
|
||||||
{"name", i18nc("Previously used emojis", "History")},
|
{"name", i18nc("Previously used emojis", "History")},
|
||||||
{"emoji", QStringLiteral("⌛️")},
|
{"emoji", QStringLiteral("⌛️")},
|
||||||
}},
|
}},
|
||||||
{QVariantMap{
|
|
||||||
{"category", EmojiModel::Custom},
|
|
||||||
{"name", i18nc("'Custom' is a category of emoji", "Custom")},
|
|
||||||
{"emoji", QStringLiteral("😏")},
|
|
||||||
}},
|
|
||||||
{QVariantMap{
|
{QVariantMap{
|
||||||
{"category", EmojiModel::Smileys},
|
{"category", EmojiModel::Smileys},
|
||||||
{"name", i18nc("'Smileys' is a category of emoji", "Smileys")},
|
{"name", i18nc("'Smileys' is a category of emoji", "Smileys")},
|
||||||
@@ -185,3 +196,23 @@ QVariantList EmojiModel::categories() const
|
|||||||
}},
|
}},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariantList EmojiModel::categoriesWithCustom() const
|
||||||
|
{
|
||||||
|
auto cats = categories();
|
||||||
|
cats.removeAt(0);
|
||||||
|
cats.insert(0,
|
||||||
|
QVariantMap{
|
||||||
|
{"category", EmojiModel::History},
|
||||||
|
{"name", i18nc("Previously used emojis", "History")},
|
||||||
|
{"emoji", QStringLiteral("⌛️")},
|
||||||
|
});
|
||||||
|
cats.insert(1,
|
||||||
|
QVariantMap{
|
||||||
|
{"category", EmojiModel::Custom},
|
||||||
|
{"name", i18nc("'Custom' is a category of emoji", "Custom")},
|
||||||
|
{"emoji", QStringLiteral("🖼️")},
|
||||||
|
});
|
||||||
|
;
|
||||||
|
return cats;
|
||||||
|
}
|
||||||
@@ -8,12 +8,18 @@
|
|||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
|
||||||
struct Emoji {
|
struct Emoji {
|
||||||
Emoji(QString u, QString s, bool isCustom = false)
|
Emoji(QString unicode, QString shortname, bool isCustom = false)
|
||||||
: unicode(std::move(std::move(u)))
|
: unicode(std::move(unicode))
|
||||||
, shortName(std::move(std::move(s)))
|
, shortName(std::move(shortname))
|
||||||
, isCustom(isCustom)
|
, isCustom(isCustom)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
Emoji(QString unicode, QString shortname, QString description)
|
||||||
|
: unicode(std::move(unicode))
|
||||||
|
, shortName(std::move(shortname))
|
||||||
|
, description(std::move(description))
|
||||||
|
{
|
||||||
|
}
|
||||||
Emoji() = default;
|
Emoji() = default;
|
||||||
|
|
||||||
friend QDataStream &operator<<(QDataStream &arch, const Emoji &object)
|
friend QDataStream &operator<<(QDataStream &arch, const Emoji &object)
|
||||||
@@ -33,11 +39,13 @@ struct Emoji {
|
|||||||
|
|
||||||
QString unicode;
|
QString unicode;
|
||||||
QString shortName;
|
QString shortName;
|
||||||
|
QString description;
|
||||||
bool isCustom = false;
|
bool isCustom = false;
|
||||||
|
|
||||||
Q_GADGET
|
Q_GADGET
|
||||||
Q_PROPERTY(QString unicode MEMBER unicode)
|
Q_PROPERTY(QString unicode MEMBER unicode)
|
||||||
Q_PROPERTY(QString shortName MEMBER shortName)
|
Q_PROPERTY(QString shortName MEMBER shortName)
|
||||||
|
Q_PROPERTY(QString description MEMBER description)
|
||||||
Q_PROPERTY(bool isCustom MEMBER isCustom)
|
Q_PROPERTY(bool isCustom MEMBER isCustom)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -49,6 +57,7 @@ class EmojiModel : public QAbstractListModel
|
|||||||
|
|
||||||
Q_PROPERTY(QVariantList history READ history NOTIFY historyChanged)
|
Q_PROPERTY(QVariantList history READ history NOTIFY historyChanged)
|
||||||
Q_PROPERTY(QVariantList categories READ categories CONSTANT)
|
Q_PROPERTY(QVariantList categories READ categories CONSTANT)
|
||||||
|
Q_PROPERTY(QVariantList categoriesWithCustom READ categoriesWithCustom CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static EmojiModel &instance()
|
static EmojiModel &instance()
|
||||||
@@ -63,13 +72,16 @@ public:
|
|||||||
InvalidRole = 50,
|
InvalidRole = 50,
|
||||||
DisplayRole = 51,
|
DisplayRole = 51,
|
||||||
ReplacedTextRole = 52,
|
ReplacedTextRole = 52,
|
||||||
|
DescriptionRole = 53,
|
||||||
};
|
};
|
||||||
Q_ENUM(RoleNames);
|
Q_ENUM(RoleNames);
|
||||||
|
|
||||||
enum Category {
|
enum Category {
|
||||||
Custom,
|
Custom,
|
||||||
Search,
|
Search,
|
||||||
|
SearchNoCustom,
|
||||||
History,
|
History,
|
||||||
|
HistoryNoCustom,
|
||||||
Smileys,
|
Smileys,
|
||||||
People,
|
People,
|
||||||
Nature,
|
Nature,
|
||||||
@@ -89,11 +101,14 @@ public:
|
|||||||
|
|
||||||
Q_INVOKABLE QVariantList history() const;
|
Q_INVOKABLE QVariantList history() const;
|
||||||
Q_INVOKABLE static QVariantList filterModel(const QString &filter, bool limit = true);
|
Q_INVOKABLE static QVariantList filterModel(const QString &filter, bool limit = true);
|
||||||
|
Q_INVOKABLE static QVariantList filterModelNoCustom(const QString &filter, bool limit = true);
|
||||||
|
|
||||||
Q_INVOKABLE QVariantList emojis(Category category) const;
|
Q_INVOKABLE QVariantList emojis(Category category) const;
|
||||||
|
|
||||||
Q_INVOKABLE QVariantList tones(const QString &baseEmoji) const;
|
Q_INVOKABLE QVariantList tones(const QString &baseEmoji) const;
|
||||||
|
|
||||||
QVariantList categories() const;
|
QVariantList categories() const;
|
||||||
|
QVariantList categoriesWithCustom() const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void historyChanged();
|
void historyChanged();
|
||||||
@@ -103,7 +118,6 @@ public Q_SLOTS:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static QHash<Category, QVariantList> _emojis;
|
static QHash<Category, QVariantList> _emojis;
|
||||||
static QMultiHash<QString, QVariant> _tones;
|
|
||||||
|
|
||||||
// TODO: Port away from QSettings
|
// TODO: Port away from QSettings
|
||||||
QSettings m_settings;
|
QSettings m_settings;
|
||||||
@@ -15,7 +15,9 @@
|
|||||||
KeywordNotificationRuleModel::KeywordNotificationRuleModel(QObject *parent)
|
KeywordNotificationRuleModel::KeywordNotificationRuleModel(QObject *parent)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
{
|
{
|
||||||
controllerConnectionChanged();
|
if (Controller::instance().activeConnection()) {
|
||||||
|
controllerConnectionChanged();
|
||||||
|
}
|
||||||
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, &KeywordNotificationRuleModel::controllerConnectionChanged);
|
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, &KeywordNotificationRuleModel::controllerConnectionChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +27,6 @@
|
|||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
|
|
||||||
#include "neochatuser.h"
|
#include "neochatuser.h"
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
@@ -54,6 +53,9 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
|
|||||||
roles[UserMarkerRole] = "userMarker";
|
roles[UserMarkerRole] = "userMarker";
|
||||||
roles[ShowAuthorRole] = "showAuthor";
|
roles[ShowAuthorRole] = "showAuthor";
|
||||||
roles[ShowSectionRole] = "showSection";
|
roles[ShowSectionRole] = "showSection";
|
||||||
|
roles[ReadMarkersRole] = "readMarkers";
|
||||||
|
roles[ReadMarkersStringRole] = "readMarkersString";
|
||||||
|
roles[ShowReadMarkersRole] = "showReadMarkers";
|
||||||
roles[ReactionRole] = "reaction";
|
roles[ReactionRole] = "reaction";
|
||||||
roles[IsEditedRole] = "isEdited";
|
roles[IsEditedRole] = "isEdited";
|
||||||
roles[SourceRole] = "source";
|
roles[SourceRole] = "source";
|
||||||
@@ -64,8 +66,9 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
|
|||||||
roles[VerifiedRole] = "verified";
|
roles[VerifiedRole] = "verified";
|
||||||
roles[DisplayNameForInitialsRole] = "displayNameForInitials";
|
roles[DisplayNameForInitialsRole] = "displayNameForInitials";
|
||||||
roles[AuthorDisplayNameRole] = "authorDisplayName";
|
roles[AuthorDisplayNameRole] = "authorDisplayName";
|
||||||
roles[IsNameChangeRole] = "isNameChange";
|
roles[IsRedactedRole] = "isRedacted";
|
||||||
roles[IsAvatarChangeRole] = "isAvatarChange";
|
roles[GenericDisplayRole] = "genericDisplay";
|
||||||
|
roles[IsPendingRole] = "isPending";
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,6 +177,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
|||||||
});
|
});
|
||||||
connect(m_currentRoom, &Room::pendingEventAdded, this, &MessageEventModel::endInsertRows);
|
connect(m_currentRoom, &Room::pendingEventAdded, this, &MessageEventModel::endInsertRows);
|
||||||
connect(m_currentRoom, &Room::pendingEventAboutToMerge, this, [this](RoomEvent *, int i) {
|
connect(m_currentRoom, &Room::pendingEventAboutToMerge, this, [this](RoomEvent *, int i) {
|
||||||
|
Q_EMIT dataChanged(index(i, 0), index(i, 0), {IsPendingRole});
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
return; // No need to move anything, just refresh
|
return; // No need to move anything, just refresh
|
||||||
}
|
}
|
||||||
@@ -212,6 +216,12 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
|||||||
}
|
}
|
||||||
refreshEventRoles(eventId, {ReactionRole, Qt::DisplayRole});
|
refreshEventRoles(eventId, {ReactionRole, Qt::DisplayRole});
|
||||||
});
|
});
|
||||||
|
connect(m_currentRoom, &Room::changed, this, [this]() {
|
||||||
|
for (auto it = m_currentRoom->messageEvents().rbegin(); it != m_currentRoom->messageEvents().rend(); ++it) {
|
||||||
|
auto event = it->event();
|
||||||
|
refreshEventRoles(event->id(), {ReadMarkersRole, ReadMarkersStringRole});
|
||||||
|
}
|
||||||
|
});
|
||||||
connect(m_currentRoom, &Room::newFileTransfer, this, &MessageEventModel::refreshEvent);
|
connect(m_currentRoom, &Room::newFileTransfer, this, &MessageEventModel::refreshEvent);
|
||||||
connect(m_currentRoom, &Room::fileTransferProgress, this, &MessageEventModel::refreshEvent);
|
connect(m_currentRoom, &Room::fileTransferProgress, this, &MessageEventModel::refreshEvent);
|
||||||
connect(m_currentRoom, &Room::fileTransferCompleted, this, &MessageEventModel::refreshEvent);
|
connect(m_currentRoom, &Room::fileTransferCompleted, this, &MessageEventModel::refreshEvent);
|
||||||
@@ -438,7 +448,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
case EventTypeRole:
|
case EventTypeRole:
|
||||||
return DelegateType::ReadMarker;
|
return DelegateType::ReadMarker;
|
||||||
case TimeRole: {
|
case TimeRole: {
|
||||||
const QDateTime eventDate = data(index(m_lastReadEventIndex.row() + 1, 0), TimeRole).toDateTime();
|
const QDateTime eventDate = data(index(m_lastReadEventIndex.row() + 1, 0), TimeRole).toDateTime().toLocalTime();
|
||||||
const KFormat format;
|
const KFormat format;
|
||||||
return format.formatRelativeDateTime(eventDate, QLocale::ShortFormat);
|
return format.formatRelativeDateTime(eventDate, QLocale::ShortFormat);
|
||||||
}
|
}
|
||||||
@@ -461,6 +471,14 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
return m_currentRoom->eventToString(evt, Qt::RichText);
|
return m_currentRoom->eventToString(evt, Qt::RichText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (role == GenericDisplayRole) {
|
||||||
|
if (evt.isRedacted()) {
|
||||||
|
return i18n("<i>[This message was deleted]</i>");
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_currentRoom->eventToGenericString(evt);
|
||||||
|
}
|
||||||
|
|
||||||
if (role == FormattedBodyRole) {
|
if (role == FormattedBodyRole) {
|
||||||
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
||||||
if (e->hasTextContent() && e->mimeType().name() != "text/plain") {
|
if (e->hasTextContent() && e->mimeType().name() != "text/plain") {
|
||||||
@@ -559,7 +577,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (role == HighlightRole) {
|
if (role == HighlightRole) {
|
||||||
return m_currentRoom->isEventHighlighted(&evt);
|
return !m_currentRoom->isDirectChat() && m_currentRoom->isEventHighlighted(&evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == FileMimetypeIcon) {
|
if (role == FileMimetypeIcon) {
|
||||||
@@ -587,12 +605,25 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
|
|
||||||
if (role == SpecialMarksRole) {
|
if (role == SpecialMarksRole) {
|
||||||
if (isPending) {
|
if (isPending) {
|
||||||
|
// A pending event with an m.new_content key will be merged into the
|
||||||
|
// original event so don't show.
|
||||||
|
if (evt.contentJson().contains("m.new_content")) {
|
||||||
|
return EventStatus::Hidden;
|
||||||
|
}
|
||||||
return pendingIt->deliveryStatus();
|
return pendingIt->deliveryStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *memberEvent = timelineIt->viewAs<RoomMemberEvent>();
|
if (evt.isStateEvent() && !NeoChatConfig::self()->showStateEvent()) {
|
||||||
if (memberEvent) {
|
return EventStatus::Hidden;
|
||||||
if ((memberEvent->isJoin() || memberEvent->isLeave()) && !NeoChatConfig::self()->showLeaveJoinEvent()) {
|
}
|
||||||
|
|
||||||
|
if (auto roomMemberEvent = eventCast<const RoomMemberEvent>(&evt)) {
|
||||||
|
if ((roomMemberEvent->isJoin() || roomMemberEvent->isLeave()) && !NeoChatConfig::self()->showLeaveJoinEvent()) {
|
||||||
|
return EventStatus::Hidden;
|
||||||
|
} else if (roomMemberEvent->isRename() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave() && !NeoChatConfig::self()->showRename()) {
|
||||||
|
return EventStatus::Hidden;
|
||||||
|
} else if (roomMemberEvent->isAvatarUpdate() && !roomMemberEvent->isJoin() && !roomMemberEvent->isLeave()
|
||||||
|
&& !NeoChatConfig::self()->showAvatarUpdate()) {
|
||||||
return EventStatus::Hidden;
|
return EventStatus::Hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -779,6 +810,65 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (role == ReadMarkersRole) {
|
||||||
|
#ifdef QUOTIENT_07
|
||||||
|
auto userIds = room()->userIdsAtEvent(evt.id());
|
||||||
|
userIds.remove(m_currentRoom->localUser()->id());
|
||||||
|
#else
|
||||||
|
auto userIds = room()->usersAtEventId(evt.id());
|
||||||
|
userIds.removeAll(m_currentRoom->localUser());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QVariantList users;
|
||||||
|
users.reserve(userIds.size());
|
||||||
|
for (const auto &userId : userIds) {
|
||||||
|
#ifdef QUOTIENT_07
|
||||||
|
auto user = static_cast<NeoChatUser *>(m_currentRoom->user(userId));
|
||||||
|
#else
|
||||||
|
auto user = static_cast<NeoChatUser *>(userId);
|
||||||
|
#endif
|
||||||
|
users += userAtEvent(user, m_currentRoom, evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == ReadMarkersStringRole) {
|
||||||
|
#ifdef QUOTIENT_07
|
||||||
|
auto userIds = room()->userIdsAtEvent(evt.id());
|
||||||
|
userIds.remove(m_currentRoom->localUser()->id());
|
||||||
|
#else
|
||||||
|
auto userIds = room()->usersAtEventId(evt.id());
|
||||||
|
userIds.removeAll(m_currentRoom->localUser());
|
||||||
|
#endif
|
||||||
|
/**
|
||||||
|
* The string ends up in the form
|
||||||
|
* "x users: user1DisplayName, user2DisplayName, etc."
|
||||||
|
*/
|
||||||
|
QString readMarkersString = i18np("1 user: ", "%1 users: ", userIds.size());
|
||||||
|
for (const auto &userId : userIds) {
|
||||||
|
#ifdef QUOTIENT_07
|
||||||
|
auto user = static_cast<NeoChatUser *>(m_currentRoom->user(userId));
|
||||||
|
#else
|
||||||
|
auto user = static_cast<NeoChatUser *>(userId);
|
||||||
|
#endif
|
||||||
|
readMarkersString += user->displayname(m_currentRoom) + i18nc("list separator", ", ");
|
||||||
|
}
|
||||||
|
readMarkersString.chop(2);
|
||||||
|
return readMarkersString;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == ShowReadMarkersRole) {
|
||||||
|
#ifdef QUOTIENT_07
|
||||||
|
auto userIds = room()->userIdsAtEvent(evt.id());
|
||||||
|
userIds.remove(m_currentRoom->localUser()->id());
|
||||||
|
#else
|
||||||
|
auto userIds = room()->usersAtEventId(evt.id());
|
||||||
|
userIds.removeAll(m_currentRoom->localUser());
|
||||||
|
#endif
|
||||||
|
return userIds.size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (role == ReactionRole) {
|
if (role == ReactionRole) {
|
||||||
const auto &annotations = m_currentRoom->relatedEvents(evt, EventRelation::Annotation());
|
const auto &annotations = m_currentRoom->relatedEvents(evt, EventRelation::Annotation());
|
||||||
if (annotations.isEmpty()) {
|
if (annotations.isEmpty()) {
|
||||||
@@ -879,20 +969,12 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == IsNameChangeRole) {
|
if (role == IsRedactedRole) {
|
||||||
auto roomMemberEvent = eventCast<const RoomMemberEvent>(&evt);
|
return evt.isRedacted();
|
||||||
if (roomMemberEvent) {
|
|
||||||
return roomMemberEvent->isRename();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == IsAvatarChangeRole) {
|
if (role == IsPendingRole) {
|
||||||
auto roomMemberEvent = eventCast<const RoomMemberEvent>(&evt);
|
return row < m_currentRoom->pendingEvents().size();
|
||||||
if (roomMemberEvent) {
|
|
||||||
return roomMemberEvent->isAvatarUpdate();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@@ -962,6 +1044,9 @@ QVariant MessageEventModel::getLatestMessageFromIndex(const int baseline)
|
|||||||
for (auto it = timelineBottom; it != limit; ++it) {
|
for (auto it = timelineBottom; it != limit; ++it) {
|
||||||
auto evt = it->event();
|
auto evt = it->event();
|
||||||
auto e = eventCast<const RoomMessageEvent>(evt);
|
auto e = eventCast<const RoomMessageEvent>(evt);
|
||||||
|
if (!e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto content = (*it)->contentJson();
|
auto content = (*it)->contentJson();
|
||||||
|
|
||||||
@@ -45,6 +45,7 @@ public:
|
|||||||
AnnotationRole,
|
AnnotationRole,
|
||||||
UserMarkerRole,
|
UserMarkerRole,
|
||||||
FormattedBodyRole,
|
FormattedBodyRole,
|
||||||
|
GenericDisplayRole,
|
||||||
|
|
||||||
MimeTypeRole,
|
MimeTypeRole,
|
||||||
FileMimetypeIcon,
|
FileMimetypeIcon,
|
||||||
@@ -56,6 +57,9 @@ public:
|
|||||||
ShowAuthorRole,
|
ShowAuthorRole,
|
||||||
ShowSectionRole,
|
ShowSectionRole,
|
||||||
|
|
||||||
|
ReadMarkersRole, /**< QVariantList of users at the event for read marker tracking. */
|
||||||
|
ReadMarkersStringRole, /**< QString with the display name and mxID of the users at the event. */
|
||||||
|
ShowReadMarkersRole, /**< bool with whether there are any other user read markers to be shown. */
|
||||||
ReactionRole,
|
ReactionRole,
|
||||||
|
|
||||||
IsEditedRole,
|
IsEditedRole,
|
||||||
@@ -69,8 +73,8 @@ public:
|
|||||||
DisplayNameForInitialsRole,
|
DisplayNameForInitialsRole,
|
||||||
// The displayname for the event's sender; for name change events, the old displayname
|
// The displayname for the event's sender; for name change events, the old displayname
|
||||||
AuthorDisplayNameRole,
|
AuthorDisplayNameRole,
|
||||||
IsNameChangeRole,
|
IsRedactedRole,
|
||||||
IsAvatarChangeRole,
|
IsPendingRole,
|
||||||
LastRole, // Keep this last
|
LastRole, // Keep this last
|
||||||
};
|
};
|
||||||
Q_ENUM(EventRoles)
|
Q_ENUM(EventRoles)
|
||||||
@@ -11,19 +11,20 @@ using namespace Quotient;
|
|||||||
MessageFilterModel::MessageFilterModel(QObject *parent)
|
MessageFilterModel::MessageFilterModel(QObject *parent)
|
||||||
: QSortFilterProxyModel(parent)
|
: QSortFilterProxyModel(parent)
|
||||||
{
|
{
|
||||||
|
connect(NeoChatConfig::self(), &NeoChatConfig::ShowStateEventChanged, this, [this] {
|
||||||
|
invalidateFilter();
|
||||||
|
});
|
||||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLeaveJoinEventChanged, this, [this] {
|
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLeaveJoinEventChanged, this, [this] {
|
||||||
beginResetModel();
|
invalidateFilter();
|
||||||
endResetModel();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowRenameChanged, this, [this] {
|
connect(NeoChatConfig::self(), &NeoChatConfig::ShowRenameChanged, this, [this] {
|
||||||
beginResetModel();
|
invalidateFilter();
|
||||||
endResetModel();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowAvatarUpdateChanged, this, [this] {
|
connect(NeoChatConfig::self(), &NeoChatConfig::ShowAvatarUpdateChanged, this, [this] {
|
||||||
beginResetModel();
|
invalidateFilter();
|
||||||
endResetModel();
|
});
|
||||||
|
connect(NeoChatConfig::self(), &NeoChatConfig::ShowDeletedMessagesChanged, this, [this] {
|
||||||
|
invalidateFilter();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,28 +32,20 @@ bool MessageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
|
|||||||
{
|
{
|
||||||
const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||||
|
|
||||||
|
if (index.data(MessageEventModel::IsRedactedRole).toBool() && !NeoChatConfig::self()->showDeletedMessages()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const int specialMarks = index.data(MessageEventModel::SpecialMarksRole).toInt();
|
const int specialMarks = index.data(MessageEventModel::SpecialMarksRole).toInt();
|
||||||
if (index.data(MessageEventModel::IsNameChangeRole).toBool() && !NeoChatConfig::self()->showRename()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index.data(MessageEventModel::IsAvatarChangeRole).toBool() && !NeoChatConfig::self()->showAvatarUpdate()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (specialMarks == EventStatus::Hidden || specialMarks == EventStatus::Replaced) {
|
if (specialMarks == EventStatus::Hidden || specialMarks == EventStatus::Replaced) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString eventType = index.data(MessageEventModel::EventTypeRole).toString();
|
const auto eventType = index.data(MessageEventModel::EventTypeRole).toInt();
|
||||||
|
|
||||||
if (eventType == MessageEventModel::Other) {
|
if (eventType == MessageEventModel::Other) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NeoChatConfig::self()->showLeaveJoinEvent() && eventType == MessageEventModel::State) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user