Compare commits
245 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2839d44ea8 | ||
|
|
5f32ae79c1 | ||
|
|
0fc94310c3 | ||
|
|
7f26651edf | ||
|
|
36880d001c | ||
|
|
e0e289d424 | ||
|
|
b79956871f | ||
|
|
917f77152d | ||
|
|
e28d1918f5 | ||
|
|
149b11ba6f | ||
|
|
c007961ef6 | ||
|
|
ef40f5a747 | ||
|
|
37780c2e3b | ||
|
|
53b9f42399 | ||
|
|
d2ed1cfb2e | ||
|
|
c7d4b1a529 | ||
|
|
a8045f2134 | ||
|
|
b8262fef92 | ||
|
|
3071901a47 | ||
|
|
396260549a | ||
|
|
50c2de8d52 | ||
|
|
d4e067d57c | ||
|
|
3797b0129a | ||
|
|
59b4146c63 | ||
|
|
c76524d540 | ||
|
|
10030efd08 | ||
|
|
918bd5439c | ||
|
|
e3ff50bbe8 | ||
|
|
76edc858aa | ||
|
|
2a2c117ac1 | ||
|
|
d3e4640af9 | ||
|
|
7e1f0f4ea7 | ||
|
|
80bf279321 | ||
|
|
6eb4b8c9d2 | ||
|
|
4bba505da6 | ||
|
|
c2fc4e44a7 | ||
|
|
274bf824e3 | ||
|
|
d70a8a652a | ||
|
|
8c436075d8 | ||
|
|
828032ba06 | ||
|
|
c30abfefa9 | ||
|
|
5d4efad2f8 | ||
|
|
3a391aafe8 | ||
|
|
7b5b8197bc | ||
|
|
55659e488f | ||
|
|
c2c235eff1 | ||
|
|
f6f4edec86 | ||
|
|
55847cb9cc | ||
|
|
736c4b02ed | ||
|
|
4cf5b516d0 | ||
|
|
c379a7fa27 | ||
|
|
2318fb95d9 | ||
|
|
14e57e7833 | ||
|
|
8c9ea72a9b | ||
|
|
efd3f2e8d0 | ||
|
|
ea70782771 | ||
|
|
6cca3dbe3a | ||
|
|
696b34e094 | ||
|
|
6ce74bbd0c | ||
|
|
3c7e85fbbf | ||
|
|
6c6e408497 | ||
|
|
1ac79273b6 | ||
|
|
d4d99284cc | ||
|
|
25226aa61f | ||
|
|
6748a2d21d | ||
|
|
fdb424e65e | ||
|
|
8cd0b12c4a | ||
|
|
d133c4fab7 | ||
|
|
2f8303348b | ||
|
|
7dfac8a9f7 | ||
|
|
3c98a8fac4 | ||
|
|
57b1bb659c | ||
|
|
da8c8c48bc | ||
|
|
07c5cd8016 | ||
|
|
a23ef130ca | ||
|
|
2f4116796a | ||
|
|
f004f9e3c8 | ||
|
|
9a3726f9ff | ||
|
|
91d1f6ffeb | ||
|
|
cd895f1b06 | ||
|
|
fead1a69b3 | ||
|
|
63af4cae77 | ||
|
|
89090690b4 | ||
|
|
6a16334eab | ||
|
|
5b05622058 | ||
|
|
1eb705c17f | ||
|
|
f9ad0e8426 | ||
|
|
ef1ff04ef2 | ||
|
|
9b54aff3d5 | ||
|
|
52a093d449 | ||
|
|
619369e148 | ||
|
|
e63a9a9be1 | ||
|
|
5c97e67404 | ||
|
|
84a265b7f9 | ||
|
|
61201a7097 | ||
|
|
b9630ad2f2 | ||
|
|
179a201113 | ||
|
|
6eef58e57d | ||
|
|
94f325609a | ||
|
|
a40ba493b6 | ||
|
|
916e7465f1 | ||
|
|
8257a9d65e | ||
|
|
a75048761b | ||
|
|
f21822aba7 | ||
|
|
75eb5a51af | ||
|
|
7e37c31011 | ||
|
|
f80039a5c4 | ||
|
|
400d86a1e9 | ||
|
|
b20b1c10d0 | ||
|
|
a37fd0713f | ||
|
|
b1581a54d1 | ||
|
|
ded60b906b | ||
|
|
6957dd0fa2 | ||
|
|
7de4014b28 | ||
|
|
63e7ec1bd7 | ||
|
|
f24428fab3 | ||
|
|
c3a5a767c2 | ||
|
|
5fb311b509 | ||
|
|
f0d832f756 | ||
|
|
a7c137ca39 | ||
|
|
a96e8958c9 | ||
|
|
7dc951d2cd | ||
|
|
c3ee277ede | ||
|
|
78d62e9376 | ||
|
|
11e9eaf3e9 | ||
|
|
4337d0d5d8 | ||
|
|
a07537367f | ||
|
|
51efecaa25 | ||
|
|
830a47c5ff | ||
|
|
24748d42d8 | ||
|
|
19fe439e95 | ||
|
|
2bcd7118f4 | ||
|
|
27e660178e | ||
|
|
e0df553a72 | ||
|
|
53f040cb28 | ||
|
|
28cc7cf616 | ||
|
|
d224df8aa2 | ||
|
|
1dff2b8273 | ||
|
|
722aa422e7 | ||
|
|
70de0dc624 | ||
|
|
9d804e6ea7 | ||
|
|
52d552650d | ||
|
|
0c5007fd56 | ||
|
|
af19829225 | ||
|
|
0be8828dd4 | ||
|
|
729b6bd354 | ||
|
|
ef10042179 | ||
|
|
846d430947 | ||
|
|
cce4a3ebdf | ||
|
|
5b848961bc | ||
|
|
51574f5125 | ||
|
|
a779907500 | ||
|
|
f195db323d | ||
|
|
da47d76a7f | ||
|
|
6dc8c4976c | ||
|
|
fec7680068 | ||
|
|
b84264891d | ||
|
|
465a981033 | ||
|
|
06b4c40b33 | ||
|
|
efae510fda | ||
|
|
f9fc8c5c0b | ||
|
|
49c9c63bf5 | ||
|
|
90cee0f437 | ||
|
|
5bc9362fde | ||
|
|
10922aeb52 | ||
|
|
f9a96ccdab | ||
|
|
04056d9ed1 | ||
|
|
ecf373e317 | ||
|
|
9c2e0669f6 | ||
|
|
2b8aa9f975 | ||
|
|
2f61090413 | ||
|
|
aa9daad704 | ||
|
|
0e79d3506d | ||
|
|
72994d0349 | ||
|
|
0ee5ba76c9 | ||
|
|
d1398f6726 | ||
|
|
9084817450 | ||
|
|
083a2f9772 | ||
|
|
b44e81c849 | ||
|
|
014826bd09 | ||
|
|
2858fcfad2 | ||
|
|
b6db36a9f2 | ||
|
|
ede860c99f | ||
|
|
f8951fc760 | ||
|
|
525015fe78 | ||
|
|
b834510be0 | ||
|
|
8700611235 | ||
|
|
15ddcef115 | ||
|
|
7216da8b6f | ||
|
|
7bd4aac692 | ||
|
|
10e17d9f0f | ||
|
|
db5e328869 | ||
|
|
921667565e | ||
|
|
9cd8a380ed | ||
|
|
29816730e4 | ||
|
|
7214936eaa | ||
|
|
5a7c3295dc | ||
|
|
dce4a409c7 | ||
|
|
bd27904f17 | ||
|
|
731b234dda | ||
|
|
ce0fc637c4 | ||
|
|
070fe45a2d | ||
|
|
3a8d078e6c | ||
|
|
fb9183e5c3 | ||
|
|
853113df3f | ||
|
|
e62288e6f1 | ||
|
|
4f978a950b | ||
|
|
36b2868933 | ||
|
|
1763dc13c5 | ||
|
|
b7e4c2c6a2 | ||
|
|
5969612ead | ||
|
|
0d00d4200c | ||
|
|
35f30c293b | ||
|
|
77e20ec446 | ||
|
|
101b57c581 | ||
|
|
b994907be4 | ||
|
|
97ce81daca | ||
|
|
4e61c5e53c | ||
|
|
6871ed051c | ||
|
|
10da870ab3 | ||
|
|
6b5f76296a | ||
|
|
93a4930301 | ||
|
|
7b393f2681 | ||
|
|
fb6266fa15 | ||
|
|
334b245669 | ||
|
|
3a969189b8 | ||
|
|
cef5d11130 | ||
|
|
216c751d81 | ||
|
|
154109dde1 | ||
|
|
312db10439 | ||
|
|
f4f540e805 | ||
|
|
b3ca71580f | ||
|
|
2fc2ac113e | ||
|
|
2ea95ea080 | ||
|
|
22168dcef9 | ||
|
|
e25ffd0c41 | ||
|
|
5595d8f896 | ||
|
|
abed37518d | ||
|
|
57493e87ee | ||
|
|
1bcff6503f | ||
|
|
98571cb37d | ||
|
|
b64cd3c1b8 | ||
|
|
69ced8406b | ||
|
|
1f551b5f59 | ||
|
|
48a2a793c8 |
157
.flatpak-manifest.json
Normal file
157
.flatpak-manifest.json
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
{
|
||||||
|
"id": "org.kde.neochat",
|
||||||
|
"branch": "master",
|
||||||
|
"runtime": "org.kde.Platform",
|
||||||
|
"runtime-version": "5.15-21.08",
|
||||||
|
"sdk": "org.kde.Sdk",
|
||||||
|
"command": "neochat",
|
||||||
|
"tags": [
|
||||||
|
"nightly"
|
||||||
|
],
|
||||||
|
"desktop-file-name-suffix": " (Nightly)",
|
||||||
|
"finish-args": [
|
||||||
|
"--share=network",
|
||||||
|
"--share=ipc",
|
||||||
|
"--socket=x11",
|
||||||
|
"--socket=wayland",
|
||||||
|
"--device=dri",
|
||||||
|
"--filesystem=xdg-download",
|
||||||
|
"--talk-name=org.freedesktop.Notifications",
|
||||||
|
"--talk-name=org.kde.kwalletd5",
|
||||||
|
"--talk-name=org.kde.StatusNotifierWatcher",
|
||||||
|
"--own-name=org.kde.StatusNotifierItem-2-2"
|
||||||
|
],
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"name": "kquickimageeditor",
|
||||||
|
"buildsystem": "cmake-ninja",
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://invent.kde.org/libraries/kquickimageeditor"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "olm",
|
||||||
|
"buildsystem": "cmake-ninja",
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://gitlab.matrix.org/matrix-org/olm.git",
|
||||||
|
"tag": "3.2.10",
|
||||||
|
"x-checker-data": {
|
||||||
|
"type": "git",
|
||||||
|
"tag-pattern": "^([\\d.]+)$"
|
||||||
|
},
|
||||||
|
"commit": "9908862979147a71dc6abaecd521be526ae77be1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "libsecret",
|
||||||
|
"buildsystem": "meson",
|
||||||
|
"config-opts": [
|
||||||
|
"-Dmanpage=false",
|
||||||
|
"-Dvapi=false",
|
||||||
|
"-Dgtk_doc=false",
|
||||||
|
"-Dintrospection=false",
|
||||||
|
"-Dgcrypt=false"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"type": "archive",
|
||||||
|
"url": "https://download.gnome.org/sources/libsecret/0.20/libsecret-0.20.5.tar.xz",
|
||||||
|
"sha256": "3fb3ce340fcd7db54d87c893e69bfc2b1f6e4d4b279065ffe66dac9f0fd12b4d",
|
||||||
|
"x-checker-data": {
|
||||||
|
"type": "gnome",
|
||||||
|
"name": "libsecret",
|
||||||
|
"stable-only": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "qtkeychain",
|
||||||
|
"buildsystem": "cmake-ninja",
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"type": "archive",
|
||||||
|
"url": "https://github.com/frankosterfeld/qtkeychain/archive/v0.13.2.tar.gz",
|
||||||
|
"sha256": "20beeb32de7c4eb0af9039b21e18370faf847ac8697ab3045906076afbc4caa5",
|
||||||
|
"x-checker-data": {
|
||||||
|
"type": "anitya",
|
||||||
|
"project-id": 4138,
|
||||||
|
"stable-only": true,
|
||||||
|
"url-template": "https://github.com/frankosterfeld/qtkeychain/archive/v$version.tar.gz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"config-opts": [
|
||||||
|
"-DCMAKE_INSTALL_LIBDIR=/app/lib",
|
||||||
|
"-DLIB_INSTALL_DIR=/app/lib",
|
||||||
|
"-DBUILD_TRANSLATIONS=NO"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "libQuotient",
|
||||||
|
"buildsystem": "cmake-ninja",
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/quotient-im/libQuotient.git",
|
||||||
|
"branch": "dev"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"config-opts": [
|
||||||
|
"-DQuotient_ENABLE_E2EE=ON"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cmark",
|
||||||
|
"buildsystem": "cmake-ninja",
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/commonmark/cmark.git"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"config-opts": [
|
||||||
|
"-DCMARK_TESTS=OFF",
|
||||||
|
"-DCMAKE_BUILD_TYPE=Release",
|
||||||
|
"-DCMAKE_INSTALL_PREFIX=/app"
|
||||||
|
],
|
||||||
|
"builddir": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "qcoro",
|
||||||
|
"buildsystem": "cmake-ninja",
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"type": "archive",
|
||||||
|
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.4.0.tar.gz",
|
||||||
|
"sha256": "0e68b3f0ce7bf521ffbdd731464d2d60d8d7a39a749b551ed26855a1707d86d1",
|
||||||
|
"x-checker-data": {
|
||||||
|
"type": "anitya",
|
||||||
|
"project-id": 236236,
|
||||||
|
"stable-only": true,
|
||||||
|
"url-template": "https://github.com/danvratil/qcoro/archive/refs/tags/v$version.tar.gz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "neochat",
|
||||||
|
"buildsystem": "cmake-ninja",
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"type": "dir",
|
||||||
|
"path": "."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"config-opts": [
|
||||||
|
"-DNEOCHAT_FLATPAK=ON"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -2,7 +2,11 @@
|
|||||||
# SPDX-License-Identifier: CC0-1.0
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- https://invent.kde.org/sysadmin/ci-tooling/raw/master/invent/ci-reuse.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/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/windows.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/flatpak.yml
|
||||||
|
|||||||
@@ -31,8 +31,7 @@ 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>
|
Copyright: 2020-2021 Carl Schwan <carlschwan@kde.org>, Tobias Fella <fella@posteo.de>
|
||||||
Copyright: 2020-2021 Tobias Fella <fella@posteo.de>
|
|
||||||
License: BSD-2-Clause
|
License: BSD-2-Clause
|
||||||
|
|
||||||
Files: src/neochat.notifyrc
|
Files: src/neochat.notifyrc
|
||||||
@@ -42,3 +41,7 @@ License: BSD-2-Clause
|
|||||||
Files: imports/NeoChat/Component/confetti.png imports/NeoChat/Component/glowdot.png
|
Files: imports/NeoChat/Component/confetti.png imports/NeoChat/Component/glowdot.png
|
||||||
Copyright: 2021 Alexey Andreyev <aa13q@ya.ru>
|
Copyright: 2021 Alexey Andreyev <aa13q@ya.ru>
|
||||||
License: CC0-1.0
|
License: CC0-1.0
|
||||||
|
|
||||||
|
Files: .flatpak-manifest.json
|
||||||
|
Copyright: 2020-2022 Tobias Fella <fella@posteo.de>
|
||||||
|
License: BSD-2-Clause
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
project(NeoChat)
|
project(NeoChat)
|
||||||
set(PROJECT_VERSION "22.04")
|
set(PROJECT_VERSION "22.09")
|
||||||
|
|
||||||
set(KF5_MIN_VERSION "5.88.0")
|
set(KF5_MIN_VERSION "5.91.0")
|
||||||
set(QT_MIN_VERSION "5.15.2")
|
set(QT_MIN_VERSION "5.15.2")
|
||||||
|
|
||||||
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
|
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
|
||||||
@@ -25,12 +25,14 @@ include(FeatureSummary)
|
|||||||
include(ECMSetupVersion)
|
include(ECMSetupVersion)
|
||||||
include(KDEInstallDirs)
|
include(KDEInstallDirs)
|
||||||
include(ECMFindQmlModule)
|
include(ECMFindQmlModule)
|
||||||
include(KDEClangFormat)
|
|
||||||
include(KDECMakeSettings)
|
include(KDECMakeSettings)
|
||||||
include(KDECompilerSettings NO_POLICY_SCOPE)
|
include(KDECompilerSettings NO_POLICY_SCOPE)
|
||||||
include(ECMAddAppIcon)
|
include(ECMAddAppIcon)
|
||||||
include(KDEGitCommitHooks)
|
include(KDEGitCommitHooks)
|
||||||
include(ECMCheckOutboundLicense)
|
include(ECMCheckOutboundLicense)
|
||||||
|
if (NOT ANDROID)
|
||||||
|
include(KDEClangFormat)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NEOCHAT_FLATPAK)
|
if(NEOCHAT_FLATPAK)
|
||||||
include(cmake/Flatpak.cmake)
|
include(cmake/Flatpak.cmake)
|
||||||
@@ -41,8 +43,8 @@ ecm_setup_version(${PROJECT_VERSION}
|
|||||||
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
|
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(Qt5 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg)
|
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg)
|
||||||
set_package_properties(Qt5 PROPERTIES
|
set_package_properties(Qt${QT_MAJOR_VERSION} PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
PURPOSE "Basic application components"
|
PURPOSE "Basic application components"
|
||||||
)
|
)
|
||||||
@@ -56,8 +58,8 @@ set_package_properties(KF5Kirigami2 PROPERTIES
|
|||||||
PURPOSE "Kirigami application UI framework"
|
PURPOSE "Kirigami application UI framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(Qt5Keychain)
|
find_package(Qt${QT_MAJOR_VERSION}Keychain)
|
||||||
set_package_properties(Qt5Keychain PROPERTIES
|
set_package_properties(Qt${QT_MAJOR_VERSION}Keychain PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
PURPOSE "Secure storage of account secrets"
|
PURPOSE "Secure storage of account secrets"
|
||||||
)
|
)
|
||||||
@@ -69,11 +71,12 @@ if(ANDROID)
|
|||||||
PURPOSE "Encrypted communications"
|
PURPOSE "Encrypted communications"
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
find_package(Qt5 ${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 Sonnet)
|
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle ConfigWidgets KIO WindowSystem)
|
||||||
set_package_properties(KF5QQC2DesktopStyle PROPERTIES
|
set_package_properties(KF5QQC2DesktopStyle PROPERTIES
|
||||||
TYPE RUNTIME
|
TYPE RUNTIME
|
||||||
)
|
)
|
||||||
|
ecm_find_qmlmodule(org.kde.sonnet 1.0)
|
||||||
ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0)
|
ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -84,7 +87,7 @@ endif()
|
|||||||
find_package(Quotient 0.6)
|
find_package(Quotient 0.6)
|
||||||
set_package_properties(Quotient PROPERTIES
|
set_package_properties(Quotient PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
DESCRIPTION "Qt wrapper arround Matrix API"
|
DESCRIPTION "Qt wrapper around Matrix API"
|
||||||
URL "https://github.com/quotient-im/libQuotient/"
|
URL "https://github.com/quotient-im/libQuotient/"
|
||||||
PURPOSE "Talk with matrix server"
|
PURPOSE "Talk with matrix server"
|
||||||
)
|
)
|
||||||
@@ -108,8 +111,8 @@ set_package_properties(KQuickImageEditor PROPERTIES
|
|||||||
PURPOSE "Add image editing capability to image attachments"
|
PURPOSE "Add image editing capability to image attachments"
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(QCoro5 COMPONENTS Core QUIET)
|
find_package(QCoro${QT_MAJOR_VERSION} COMPONENTS Core QUIET)
|
||||||
if(NOT QCoro5_FOUND)
|
if(NOT QCoro${QT_MAJOR_VERSION}_FOUND)
|
||||||
find_package(QCoro REQUIRED)
|
find_package(QCoro REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -136,11 +139,12 @@ add_subdirectory(src)
|
|||||||
|
|
||||||
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||||
|
|
||||||
file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES src/*.cpp src/*.h)
|
if (NOT ANDROID)
|
||||||
kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
|
file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES src/*.cpp src/*.h)
|
||||||
|
kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
|
||||||
kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
|
|
||||||
|
|
||||||
|
kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
|
||||||
|
endif()
|
||||||
file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.h *.qml)
|
file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.h *.qml)
|
||||||
# CI installs dependency headers to _install and _build, which break the reuse check
|
# CI installs dependency headers to _install and _build, which break the reuse check
|
||||||
# Fixes the test by excluding this directory
|
# Fixes the test by excluding this directory
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ and can also directly be downloaded from the [binary factory](https://binary-fac
|
|||||||
|
|
||||||
Nightly builds for [Windows](https://binary-factory.kde.org/job/NeoChat_Nightly_win64/), [MacOS](https://binary-factory.kde.org/job/NeoChat_Nightly_macos/) and [AppImages](https://binary-factory.kde.org/job/NeoChat_Nightly_appimage/) can also be downloaded from the [binary factory](https://binary-factory.kde.org/search/?q=neochat).
|
Nightly builds for [Windows](https://binary-factory.kde.org/job/NeoChat_Nightly_win64/), [MacOS](https://binary-factory.kde.org/job/NeoChat_Nightly_macos/) and [AppImages](https://binary-factory.kde.org/job/NeoChat_Nightly_appimage/) can also be downloaded from the [binary factory](https://binary-factory.kde.org/search/?q=neochat).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ We welcome contributions in this direction.
|
|||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
|
|
||||||
You can reach the maintainers at #neochat:kde.org, if you are already on Matrix.
|
You can reach the maintainers at [#neochat:kde.org](https://matrix.to/#/#neochat:kde.org), if you are already on Matrix.
|
||||||
Development happens in http://invent.kde.org/network/neochat (not in GitHub).
|
Development happens in http://invent.kde.org/network/neochat (not in GitHub).
|
||||||
|
|
||||||
## Acknowledgement
|
## Acknowledgement
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data android:name="android.app.lib_name" android:value="neochat"/>
|
<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.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"/>
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ install(
|
|||||||
FILES
|
FILES
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Flatpak/99-noto-mono-color-emoji.conf
|
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Flatpak/99-noto-mono-color-emoji.conf
|
||||||
DESTINATION
|
DESTINATION
|
||||||
${CMAKE_INSTALL_SYSCONFDIR}/fonts/conf.d/
|
${CMAKE_INSTALL_SYSCONFDIR}/fonts/local.conf
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -14,14 +14,14 @@ import NeoChat.Page 1.0
|
|||||||
Loader {
|
Loader {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property var attachmentMimetype: FileType.mimeTypeForUrl(ChatBoxHelper.attachmentPath)
|
property var attachmentMimetype: FileType.mimeTypeForUrl(chatBoxHelper.attachmentPath)
|
||||||
readonly property bool hasImage: attachmentMimetype.valid && FileType.supportedImageFormats.includes(attachmentMimetype.preferredSuffix)
|
readonly property bool hasImage: attachmentMimetype.valid && FileType.supportedImageFormats.includes(attachmentMimetype.preferredSuffix)
|
||||||
|
|
||||||
active: visible
|
active: visible
|
||||||
sourceComponent: Component {
|
sourceComponent: Component {
|
||||||
Pane {
|
Pane {
|
||||||
id: attachmentPane
|
id: attachmentPane
|
||||||
property string baseFileName: ChatBoxHelper.attachmentPath.toString().substring(ChatBoxHelper.attachmentPath.toString().lastIndexOf('/') + 1, ChatBoxHelper.attachmentPath.length)
|
property string baseFileName: chatBoxHelper.attachmentPath.toString().substring(chatBoxHelper.attachmentPath.toString().lastIndexOf('/') + 1, chatBoxHelper.attachmentPath.length)
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
@@ -46,7 +46,7 @@ Loader {
|
|||||||
asynchronous: true
|
asynchronous: true
|
||||||
cache: false // Cache is not needed. Images will rarely be shown repeatedly.
|
cache: false // Cache is not needed. Images will rarely be shown repeatedly.
|
||||||
smooth: height == preferredHeight && parent.height == parent.implicitHeight // Don't smooth until height animation stops
|
smooth: height == preferredHeight && parent.height == parent.implicitHeight // Don't smooth until height animation stops
|
||||||
source: hasImage ? ChatBoxHelper.attachmentPath : ""
|
source: hasImage ? chatBoxHelper.attachmentPath : ""
|
||||||
visible: hasImage
|
visible: hasImage
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
@@ -162,14 +162,14 @@ Loader {
|
|||||||
Component {
|
Component {
|
||||||
id: imageEditorPage
|
id: imageEditorPage
|
||||||
ImageEditorPage {
|
ImageEditorPage {
|
||||||
imagePath: ChatBoxHelper.attachmentPath
|
imagePath: chatBoxHelper.attachmentPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let imageEditor = applicationWindow().pageStack.layers.push(imageEditorPage);
|
let imageEditor = applicationWindow().pageStack.layers.push(imageEditorPage);
|
||||||
imageEditor.newPathChanged.connect(function(newPath) {
|
imageEditor.newPathChanged.connect(function(newPath) {
|
||||||
applicationWindow().pageStack.layers.pop();
|
applicationWindow().pageStack.layers.pop();
|
||||||
ChatBoxHelper.attachmentPath = newPath;
|
chatBoxHelper.attachmentPath = newPath;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ToolTip.text: text
|
ToolTip.text: text
|
||||||
@@ -180,7 +180,7 @@ Loader {
|
|||||||
icon.name: "dialog-cancel"
|
icon.name: "dialog-cancel"
|
||||||
text: i18n("Cancel")
|
text: i18n("Cancel")
|
||||||
display: AbstractButton.IconOnly
|
display: AbstractButton.IconOnly
|
||||||
onClicked: ChatBoxHelper.clearAttachment();
|
onClicked: chatBoxHelper.clearAttachment();
|
||||||
ToolTip.text: text
|
ToolTip.text: text
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ ToolBar {
|
|||||||
Layout.maximumHeight: fontMetrics.lineSpacing * 8 - fontMetrics.leading
|
Layout.maximumHeight: fontMetrics.lineSpacing * 8 - fontMetrics.leading
|
||||||
+ inputField.topPadding + inputField.bottomPadding
|
+ inputField.topPadding + inputField.bottomPadding
|
||||||
|
|
||||||
|
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
FontMetrics {
|
FontMetrics {
|
||||||
id: fontMetrics
|
id: fontMetrics
|
||||||
font: inputField.font
|
font: inputField.font
|
||||||
@@ -94,11 +97,11 @@ ToolBar {
|
|||||||
//property int lineHeight: contentHeight / lineCount
|
//property int lineHeight: contentHeight / lineCount
|
||||||
|
|
||||||
text: inputFieldText
|
text: inputFieldText
|
||||||
placeholderText: currentRoom.usesEncryption ? i18n("This room is encrypted. Sending encrypted messages is not yet supported.") : editEventId.length > 0 ? i18n("Edit Message") : i18n("Write your message...")
|
placeholderText: readOnly ? i18n("This room is encrypted. Sending encrypted messages is not yet supported.") : editEventId.length > 0 ? i18n("Edit Message") : currentRoom.usesEncryption ? i18n("Send an encrypted message…") : i18n("Send a message…")
|
||||||
verticalAlignment: TextEdit.AlignVCenter
|
verticalAlignment: TextEdit.AlignVCenter
|
||||||
horizontalAlignment: TextEdit.AlignLeft
|
horizontalAlignment: TextEdit.AlignLeft
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
readOnly: currentRoom.usesEncryption
|
readOnly: currentRoom.usesEncryption && !Controller.encryptionSupported
|
||||||
|
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||||
Kirigami.Theme.inherit: false
|
Kirigami.Theme.inherit: false
|
||||||
@@ -145,11 +148,7 @@ ToolBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if (event.key === Qt.Key_PageDown) {
|
if (event.key === Qt.Key_V && event.modifiers & Qt.ControlModifier) {
|
||||||
switchRoomDown();
|
|
||||||
} else if (event.key === Qt.Key_PageUp) {
|
|
||||||
switchRoomUp();
|
|
||||||
} else if (event.key === Qt.Key_V && event.modifiers & Qt.ControlModifier) {
|
|
||||||
chatBar.pasteImage();
|
chatBar.pasteImage();
|
||||||
} else if (event.key === Qt.Key_Up && event.modifiers & Qt.ControlModifier) {
|
} else if (event.key === Qt.Key_Up && event.modifiers & Qt.ControlModifier) {
|
||||||
replyPreviousUserMessage();
|
replyPreviousUserMessage();
|
||||||
@@ -229,7 +228,7 @@ ToolBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
if (!repeatTimer.running) {
|
if (!repeatTimer.running && Config.typingNotifications) {
|
||||||
currentRoom.sendTypingNotification(true)
|
currentRoom.sendTypingNotification(true)
|
||||||
}
|
}
|
||||||
repeatTimer.start()
|
repeatTimer.start()
|
||||||
@@ -278,14 +277,14 @@ ToolBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
visible: !ChatBoxHelper.isReplying && (!ChatBoxHelper.hasAttachment || uploadingBusySpinner.running)
|
visible: !chatBoxHelper.isReplying && (!chatBoxHelper.hasAttachment || uploadingBusySpinner.running)
|
||||||
implicitWidth: uploadButton.implicitWidth
|
implicitWidth: uploadButton.implicitWidth
|
||||||
implicitHeight: uploadButton.implicitHeight
|
implicitHeight: uploadButton.implicitHeight
|
||||||
ToolButton {
|
ToolButton {
|
||||||
id: uploadButton
|
id: uploadButton
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
// Matrix does not allow sending attachments in replies
|
// Matrix does not allow sending attachments in replies
|
||||||
visible: !ChatBoxHelper.isReplying && !ChatBoxHelper.hasAttachment && !uploadingBusySpinner.running
|
visible: !chatBoxHelper.isReplying && !chatBoxHelper.hasAttachment && !uploadingBusySpinner.running
|
||||||
icon.name: "mail-attachment"
|
icon.name: "mail-attachment"
|
||||||
text: i18n("Attach an image or file")
|
text: i18n("Attach an image or file")
|
||||||
display: AbstractButton.IconOnly
|
display: AbstractButton.IconOnly
|
||||||
@@ -297,7 +296,7 @@ ToolBar {
|
|||||||
var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay)
|
var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay)
|
||||||
fileDialog.chosen.connect((path) => {
|
fileDialog.chosen.connect((path) => {
|
||||||
if (!path) { return }
|
if (!path) { return }
|
||||||
ChatBoxHelper.attachmentPath = path;
|
chatBoxHelper.attachmentPath = path;
|
||||||
})
|
})
|
||||||
fileDialog.open()
|
fileDialog.open()
|
||||||
}
|
}
|
||||||
@@ -380,16 +379,16 @@ ToolBar {
|
|||||||
if (!Clipboard.saveImage(localPath)) {
|
if (!Clipboard.saveImage(localPath)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ChatBoxHelper.attachmentPath = localPath;
|
chatBoxHelper.attachmentPath = localPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
function postMessage() {
|
function postMessage() {
|
||||||
checkForFancyEffectsReason();
|
checkForFancyEffectsReason();
|
||||||
|
|
||||||
if (ChatBoxHelper.hasAttachment) {
|
if (chatBoxHelper.hasAttachment) {
|
||||||
// send attachment but don't reset the text
|
// send attachment but don't reset the text
|
||||||
actionsHandler.postMessage("", ChatBoxHelper.attachmentPath,
|
actionsHandler.postMessage("", chatBoxHelper.attachmentPath,
|
||||||
ChatBoxHelper.replyEventId, ChatBoxHelper.editEventId, {}, this.customEmojiModel);
|
chatBoxHelper.replyEventId, chatBoxHelper.editEventId, {}, this.customEmojiModel);
|
||||||
currentRoom.markAllMessagesAsRead();
|
currentRoom.markAllMessagesAsRead();
|
||||||
messageSent();
|
messageSent();
|
||||||
return;
|
return;
|
||||||
@@ -401,8 +400,8 @@ ToolBar {
|
|||||||
actionsHandler.postEdit(inputField.text);
|
actionsHandler.postEdit(inputField.text);
|
||||||
} else {
|
} else {
|
||||||
// send normal message
|
// send normal message
|
||||||
actionsHandler.postMessage(inputField.text.trim(), ChatBoxHelper.attachmentPath,
|
actionsHandler.postMessage(inputField.text.trim(), chatBoxHelper.attachmentPath,
|
||||||
ChatBoxHelper.replyEventId, ChatBoxHelper.editEventId, userAutocompleted, this.customEmojiModel);
|
chatBoxHelper.replyEventId, chatBoxHelper.editEventId, userAutocompleted, this.customEmojiModel);
|
||||||
}
|
}
|
||||||
currentRoom.markAllMessagesAsRead();
|
currentRoom.markAllMessagesAsRead();
|
||||||
inputField.clear();
|
inputField.clear();
|
||||||
|
|||||||
@@ -127,8 +127,8 @@ Item {
|
|||||||
|
|
||||||
ReplyPane {
|
ReplyPane {
|
||||||
id: replyPane
|
id: replyPane
|
||||||
visible: ChatBoxHelper.isReplying || ChatBoxHelper.isEditing
|
visible: chatBoxHelper.isReplying || chatBoxHelper.isEditing
|
||||||
user: ChatBoxHelper.replyUser
|
user: chatBoxHelper.replyUser
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: visible ? implicitHeight : 0
|
height: visible ? implicitHeight : 0
|
||||||
anchors.bottom: attachmentSeparator.top
|
anchors.bottom: attachmentSeparator.top
|
||||||
@@ -154,7 +154,7 @@ Item {
|
|||||||
|
|
||||||
AttachmentPane {
|
AttachmentPane {
|
||||||
id: attachmentPane
|
id: attachmentPane
|
||||||
visible: ChatBoxHelper.hasAttachment
|
visible: chatBoxHelper.hasAttachment
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: visible ? implicitHeight : 0
|
height: visible ? implicitHeight : 0
|
||||||
anchors.bottom: chatBarSeparator.top
|
anchors.bottom: chatBarSeparator.top
|
||||||
@@ -248,7 +248,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: ChatBoxHelper
|
target: chatBoxHelper
|
||||||
|
|
||||||
function onShouldClearText() {
|
function onShouldClearText() {
|
||||||
root.inputFieldText = "";
|
root.inputFieldText = "";
|
||||||
@@ -277,7 +277,7 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function closeAll() {
|
function closeAll() {
|
||||||
ChatBoxHelper.clear();
|
chatBoxHelper.clear();
|
||||||
chatBar.emojiPaneOpened = false;
|
chatBar.emojiPaneOpened = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ Popup {
|
|||||||
implicitHeight: Math.min(completionListView.contentHeight, Kirigami.Units.gridUnit * 10)
|
implicitHeight: Math.min(completionListView.contentHeight, Kirigami.Units.gridUnit * 10)
|
||||||
|
|
||||||
contentItem: ScrollView {
|
contentItem: ScrollView {
|
||||||
|
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
ListView {
|
ListView {
|
||||||
id: completionListView
|
id: completionListView
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import org.kde.neochat 1.0
|
|||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: root
|
id: root
|
||||||
readonly property bool isEdit: ChatBoxHelper.isEditing
|
readonly property bool isEdit: chatBoxHelper.isEditing
|
||||||
property var user: null
|
property var user: null
|
||||||
property string avatarMediaUrl: user ? "image://mxc/" + user.avatarMediaId : ""
|
property string avatarMediaUrl: user ? "image://mxc/" + user.avatarMediaId : ""
|
||||||
|
|
||||||
@@ -71,6 +71,10 @@ Loader {
|
|||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumHeight: fontMetrics.lineSpacing * 8 - fontMetrics.leading
|
Layout.maximumHeight: fontMetrics.lineSpacing * 8 - fontMetrics.leading
|
||||||
|
|
||||||
|
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
TextArea {
|
TextArea {
|
||||||
id: textArea
|
id: textArea
|
||||||
leftPadding: 0
|
leftPadding: 0
|
||||||
@@ -79,7 +83,7 @@ Loader {
|
|||||||
bottomPadding: 0
|
bottomPadding: 0
|
||||||
text: {
|
text: {
|
||||||
const stylesheet = "<style> a{color:"+Kirigami.Theme.linkColor+";}.user-pill{}</style>";
|
const stylesheet = "<style> a{color:"+Kirigami.Theme.linkColor+";}.user-pill{}</style>";
|
||||||
const content = ChatBoxHelper.isReplying ? ChatBoxHelper.replyEventContent : ChatBoxHelper.editContent;
|
const content = chatBoxHelper.isReplying ? chatBoxHelper.replyEventContent : chatBoxHelper.editContent;
|
||||||
return stylesheet + content;
|
return stylesheet + content;
|
||||||
}
|
}
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
@@ -102,7 +106,7 @@ Loader {
|
|||||||
text: i18n("Cancel")
|
text: i18n("Cancel")
|
||||||
display: AbstractButton.IconOnly
|
display: AbstractButton.IconOnly
|
||||||
onClicked: {
|
onClicked: {
|
||||||
ChatBoxHelper.clearEditReply();
|
chatBoxHelper.clear();
|
||||||
root.replyCancelled();
|
root.replyCancelled();
|
||||||
}
|
}
|
||||||
ToolTip.text: text
|
ToolTip.text: text
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ ApplicationWindow {
|
|||||||
property string blurhash: ""
|
property string blurhash: ""
|
||||||
property int imageWidth: -1
|
property int imageWidth: -1
|
||||||
property int imageHeight: -1
|
property int imageHeight: -1
|
||||||
|
property var modelData
|
||||||
|
|
||||||
flags: Qt.FramelessWindowHint | Qt.WA_TranslucentBackground
|
flags: Qt.FramelessWindowHint | Qt.WA_TranslucentBackground
|
||||||
|
|
||||||
@@ -52,6 +53,24 @@ ApplicationWindow {
|
|||||||
source: root.blurhash !== "" ? ("image://blurhash/" + root.blurhash) : ""
|
source: root.blurhash !== "" ? ("image://blurhash/" + root.blurhash) : ""
|
||||||
visible: root.blurhash !== "" && parent.status !== Image.Ready
|
visible: root.blurhash !== "" && parent.status !== Image.Ready
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
onTapped: {
|
||||||
|
const contextMenu = fileDelegateContextMenu.createObject(parent, {
|
||||||
|
author: modelData.author,
|
||||||
|
message: modelData.message,
|
||||||
|
eventId: modelData.eventId,
|
||||||
|
source: modelData.source,
|
||||||
|
file: root.parent,
|
||||||
|
mimeType: modelData.mimeType,
|
||||||
|
progressInfo: modelData.progressInfo,
|
||||||
|
plainMessage: modelData.message,
|
||||||
|
});
|
||||||
|
contextMenu.closeFullscreen.connect(root.destroy)
|
||||||
|
contextMenu.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ LoginStep {
|
|||||||
Connections {
|
Connections {
|
||||||
target: LoginHelper
|
target: LoginHelper
|
||||||
function onSsoUrlChanged() {
|
function onSsoUrlChanged() {
|
||||||
Qt.openUrlExternally(LoginHelper.ssoUrl)
|
UrlHelper.openUrl(LoginHelper.ssoUrl)
|
||||||
}
|
}
|
||||||
function onConnected() {
|
function onConnected() {
|
||||||
processed("qrc:/imports/NeoChat/Component/Login/Loading.qml")
|
processed("qrc:/imports/NeoChat/Component/Login/Loading.qml")
|
||||||
|
|||||||
@@ -24,10 +24,12 @@ QQC2.Popup {
|
|||||||
quickSearch.forceActiveFocus()
|
quickSearch.forceActiveFocus()
|
||||||
quickSearch.text = ""
|
quickSearch.text = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
anchors.centerIn: QQC2.Overlay.overlay
|
anchors.centerIn: QQC2.Overlay.overlay
|
||||||
background: Kirigami.Card {}
|
background: Kirigami.Card {}
|
||||||
height: 2 * Math.round(implicitHeight / 2)
|
height: 2 * Math.round(implicitHeight / 2)
|
||||||
padding: Kirigami.Units.largeSpacing * 2
|
padding: Kirigami.Units.largeSpacing * 2
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
spacing: Kirigami.Units.largeSpacing * 2
|
spacing: Kirigami.Units.largeSpacing * 2
|
||||||
|
|
||||||
@@ -77,11 +79,30 @@ QQC2.Popup {
|
|||||||
|
|
||||||
required property string avatar
|
required property string avatar
|
||||||
required property var currentRoom
|
required property var currentRoom
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
// When an item is hovered set the currentIndex of listview to it so that it is highlighted
|
||||||
|
onHoveredChanged: {
|
||||||
|
if (!hovered) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cView.currentIndex = index
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.main: Kirigami.Action {
|
||||||
|
id: enterRoomAction
|
||||||
|
onTriggered: {
|
||||||
|
RoomManager.enterRoom(currentRoom);
|
||||||
|
|
||||||
|
_popup.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
source: avatar != "" ? "image://mxc/" + avatar : ""
|
source: avatar != "" ? "image://mxc/" + avatar : ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
modal: true
|
modal: true
|
||||||
focus: true
|
focus: true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,71 +1,116 @@
|
|||||||
// SPDX-FileCopyrightText: 2019-2020 Black Hat <bhat@encom.eu.org>
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
import QtGraphicalEffects 1.15
|
|
||||||
import Qt.labs.platform 1.1 as Platform
|
|
||||||
import QtMultimedia 5.15
|
import QtMultimedia 5.15
|
||||||
|
|
||||||
import org.kde.kirigami 2.15 as Kirigami
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
import NeoChat.Component 1.0
|
|
||||||
import NeoChat.Dialog 1.0
|
|
||||||
import NeoChat.Menu.Timeline 1.0
|
|
||||||
|
|
||||||
TimelineContainer {
|
TimelineContainer {
|
||||||
id: audioDelegate
|
id: audioDelegate
|
||||||
|
|
||||||
width: ListView.view.width
|
|
||||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||||
|
|
||||||
|
onOpenContextMenu: openFileContext(model, audioDelegate)
|
||||||
|
|
||||||
|
readonly property bool downloaded: model.progressInfo && model.progressInfo.completed
|
||||||
|
onDownloadedChanged: audio.play()
|
||||||
|
|
||||||
hoverComponent: hoverActions
|
hoverComponent: hoverActions
|
||||||
innerObject: Control {
|
innerObject: Control {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumWidth: audioDelegate.bubbleMaxWidth
|
Layout.maximumWidth: audioDelegate.contentMaxWidth
|
||||||
|
|
||||||
Audio {
|
Audio {
|
||||||
id: audio
|
id: audio
|
||||||
source: currentRoom.urlToMxcUrl(content.url)
|
source: model.progressInfo.localPath
|
||||||
autoLoad: false
|
autoLoad: false
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
states: [
|
||||||
acceptedButtons: Qt.RightButton
|
State {
|
||||||
onTapped: openFileContext(model, parent)
|
name: "notDownloaded"
|
||||||
}
|
when: !model.progressInfo.completed && !model.progressInfo.active
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.LeftButton
|
PropertyChanges {
|
||||||
onLongPressed: openFileContext(model, parent)
|
target: playButton
|
||||||
}
|
icon.name: "media-playback-start"
|
||||||
|
onClicked: currentRoom.downloadFile(model.eventId)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "downloading"
|
||||||
|
when: model.progressInfo.active && !model.progressInfo.completed
|
||||||
|
PropertyChanges {
|
||||||
|
target: downloadBar
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: playButton
|
||||||
|
icon.name: "media-playback-stop"
|
||||||
|
onClicked: {
|
||||||
|
currentRoom.cancelFileTransfer(model.eventId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "paused"
|
||||||
|
when: model.progressInfo.completed && (audio.playbackState === Audio.StoppedState || audio.playbackState === Audio.PausedState)
|
||||||
|
PropertyChanges {
|
||||||
|
target: playButton
|
||||||
|
icon.name: "media-playback-start"
|
||||||
|
onClicked: {
|
||||||
|
audio.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "playing"
|
||||||
|
when: model.progressInfo.completed && audio.playbackState === Audio.PlayingState
|
||||||
|
|
||||||
|
PropertyChanges {
|
||||||
|
target: playButton
|
||||||
|
|
||||||
|
icon.name: "media-playback-pause"
|
||||||
|
|
||||||
|
onClicked: audio.pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
RowLayout {
|
RowLayout {
|
||||||
ToolButton {
|
ToolButton {
|
||||||
icon.name: audio.playbackState == Audio.PlayingState ? "media-playback-pause" : "media-playback-start"
|
id: playButton
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
if (audio.playbackState == Audio.PlayingState) {
|
|
||||||
audio.pause()
|
|
||||||
} else {
|
|
||||||
audio.play()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
text: model.display
|
text: model.display
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ProgressBar {
|
||||||
|
id: downloadBar
|
||||||
|
visible: false
|
||||||
|
Layout.fillWidth: true
|
||||||
|
from: 0
|
||||||
|
to: model.content.info.size
|
||||||
|
value: model.progressInfo.progress
|
||||||
|
}
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: audio.hasAudio
|
visible: audio.hasAudio
|
||||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||||
// Server doesn't support seeking, so use ProgressBar instead of Slider :(
|
|
||||||
ProgressBar {
|
Slider {
|
||||||
from: 0
|
from: 0
|
||||||
to: audio.duration
|
to: audio.duration
|
||||||
value: audio.position
|
value: audio.position
|
||||||
|
onMoved: audio.seek(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
|||||||
@@ -10,17 +10,18 @@ import org.kde.neochat 1.0
|
|||||||
|
|
||||||
TimelineContainer {
|
TimelineContainer {
|
||||||
id: encryptedDelegate
|
id: encryptedDelegate
|
||||||
width: ListView.view.width
|
|
||||||
|
|
||||||
innerObject: TextEdit {
|
innerObject: TextEdit {
|
||||||
text: i18n("This message is encrypted and the sender has not shared the key with this device.")
|
text: i18n("This message is encrypted and the sender has not shared the key with this device.")
|
||||||
color: Kirigami.Theme.disabledTextColor
|
color: Kirigami.Theme.disabledTextColor
|
||||||
|
selectedTextColor: Kirigami.Theme.highlightedTextColor
|
||||||
|
selectionColor: Kirigami.Theme.highlightColor
|
||||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize
|
||||||
selectByMouse: !Kirigami.Settings.isMobile
|
selectByMouse: !Kirigami.Settings.isMobile
|
||||||
readOnly: true
|
readOnly: true
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
Layout.maximumWidth: encryptedDelegate.bubbleMaxWidth
|
Layout.maximumWidth: encryptedDelegate.contentMaxWidth
|
||||||
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,9 +42,7 @@ DelegateChooser {
|
|||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: "sticker"
|
roleValue: "sticker"
|
||||||
delegate: ImageDelegate {
|
delegate: ImageDelegate {}
|
||||||
cardBackground: false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15 as QQC2
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
import QtGraphicalEffects 1.15
|
|
||||||
import Qt.labs.platform 1.1
|
import Qt.labs.platform 1.1
|
||||||
|
|
||||||
import org.kde.kirigami 2.15 as Kirigami
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
@@ -16,11 +15,12 @@ import NeoChat.Menu.Timeline 1.0
|
|||||||
|
|
||||||
TimelineContainer {
|
TimelineContainer {
|
||||||
id: fileDelegate
|
id: fileDelegate
|
||||||
width: ListView.view.width
|
|
||||||
|
|
||||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||||
hoverComponent: hoverActions
|
hoverComponent: hoverActions
|
||||||
|
|
||||||
|
onOpenContextMenu: openFileContext(model, fileDelegate)
|
||||||
|
|
||||||
readonly property bool downloaded: progressInfo && progressInfo.completed
|
readonly property bool downloaded: progressInfo && progressInfo.completed
|
||||||
|
|
||||||
function saveFileAs() {
|
function saveFileAs() {
|
||||||
@@ -30,14 +30,14 @@ TimelineContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openSavedFile() {
|
function openSavedFile() {
|
||||||
if (Qt.openUrlExternally(progressInfo.localPath)) return;
|
if (UrlHelper.openUrl(progressInfo.localPath)) return;
|
||||||
if (Qt.openUrlExternally(progressInfo.localDir)) return;
|
if (UrlHelper.openUrl(progressInfo.localDir)) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
innerObject: RowLayout {
|
innerObject: RowLayout {
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumWidth: fileDelegate.bubbleMaxWidth
|
Layout.maximumWidth: fileDelegate.contentMaxWidth
|
||||||
Layout.margins: Kirigami.Units.largeSpacing
|
Layout.margins: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
spacing: Kirigami.Units.largeSpacing
|
spacing: Kirigami.Units.largeSpacing
|
||||||
@@ -53,6 +53,7 @@ TimelineContainer {
|
|||||||
icon.name: "document-open"
|
icon.name: "document-open"
|
||||||
|
|
||||||
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; offers ability to open its downloaded file with an appropriate application", "Open File")
|
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; offers ability to open its downloaded file with an appropriate application", "Open File")
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
|
||||||
onClicked: openSavedFile()
|
onClicked: openSavedFile()
|
||||||
}
|
}
|
||||||
@@ -70,6 +71,7 @@ TimelineContainer {
|
|||||||
icon.name: "media-playback-stop"
|
icon.name: "media-playback-stop"
|
||||||
|
|
||||||
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; stops downloading the message's file", "Stop Download")
|
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; stops downloading the message's file", "Stop Download")
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
onClicked: currentRoom.cancelFileTransfer(eventId)
|
onClicked: currentRoom.cancelFileTransfer(eventId)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -131,14 +133,5 @@ TimelineContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.RightButton
|
|
||||||
onTapped: openFileContext(model, parent)
|
|
||||||
}
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
onLongPressed: openFileContext(model, parent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
import QtGraphicalEffects 1.15
|
|
||||||
import Qt.labs.platform 1.1
|
import Qt.labs.platform 1.1
|
||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
@@ -16,11 +15,11 @@ import NeoChat.Menu.Timeline 1.0
|
|||||||
TimelineContainer {
|
TimelineContainer {
|
||||||
id: imageDelegate
|
id: imageDelegate
|
||||||
|
|
||||||
width: ListView.view.width
|
|
||||||
|
|
||||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||||
hoverComponent: hoverActions
|
hoverComponent: hoverActions
|
||||||
|
|
||||||
|
onOpenContextMenu: openFileContext(model, imageDelegate)
|
||||||
|
|
||||||
property var content: model.content
|
property var content: model.content
|
||||||
readonly property bool isAnimated: contentType === "image/gif"
|
readonly property bool isAnimated: contentType === "image/gif"
|
||||||
|
|
||||||
@@ -35,8 +34,8 @@ TimelineContainer {
|
|||||||
innerObject: Image {
|
innerObject: Image {
|
||||||
id: img
|
id: img
|
||||||
|
|
||||||
Layout.maximumWidth: imageDelegate.bubbleMaxWidth
|
Layout.maximumWidth: imageDelegate.contentMaxWidth
|
||||||
Layout.maximumHeight: imageDelegate.bubbleMaxWidth / imageDelegate.info.w * imageDelegate.info.h
|
Layout.maximumHeight: imageDelegate.contentMaxWidth / imageDelegate.info.w * imageDelegate.info.h
|
||||||
Layout.preferredWidth: imageDelegate.info.w
|
Layout.preferredWidth: imageDelegate.info.w
|
||||||
Layout.preferredHeight: imageDelegate.info.h
|
Layout.preferredHeight: imageDelegate.info.h
|
||||||
source: model.mediaUrl
|
source: model.mediaUrl
|
||||||
@@ -86,21 +85,17 @@ TimelineContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.RightButton
|
|
||||||
onTapped: openFileContext(model, parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
onLongPressed: openFileContext(model, parent)
|
onLongPressed: openFileContext(model, parent)
|
||||||
onTapped: {
|
onTapped: {
|
||||||
fullScreenImage.createObject(parent, {
|
fullScreenImage.createObject(parent, {
|
||||||
filename: eventId,
|
filename: eventId,
|
||||||
source: model.mediaUrl,
|
source: mediaUrl,
|
||||||
blurhash: model.content.info["xyz.amorgan.blurhash"],
|
blurhash: model.content.info["xyz.amorgan.blurhash"],
|
||||||
imageWidth: content.info.w,
|
imageWidth: content.info.w,
|
||||||
imageHeight: content.info.h
|
imageHeight: content.info.h,
|
||||||
|
modelData: model
|
||||||
}).showFullScreen();
|
}).showFullScreen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,8 +110,8 @@ TimelineContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openSavedFile() {
|
function openSavedFile() {
|
||||||
if (Qt.openUrlExternally(progressInfo.localPath)) return;
|
if (UrlHelper.openUrl(progressInfo.localPath)) return;
|
||||||
if (Qt.openUrlExternally(progressInfo.localDir)) return;
|
if (UrlHelper.openUrl(progressInfo.localDir)) return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
64
imports/NeoChat/Component/Timeline/LinkPreviewDelegate.qml
Normal file
64
imports/NeoChat/Component/Timeline/LinkPreviewDelegate.qml
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Bharadwaj Raju <bharadwaj.raju777@protonmail.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-or-later OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: row
|
||||||
|
property var links: model.display.match(/(\bhttps?:\/\/[^\s\<\>\"\']*[^\s\<\>\"\'])/g)
|
||||||
|
// don't show previews for room links or user mentions
|
||||||
|
.filter(link => !link.includes("https://matrix.to"))
|
||||||
|
// remove ending fullstops and commas
|
||||||
|
.map(link => (link.length && [".", ","].includes(link[link.length-1])) ? link.substring(0, link.length-1) : link)
|
||||||
|
LinkPreviewer {
|
||||||
|
id: lp
|
||||||
|
url: links[0]
|
||||||
|
}
|
||||||
|
visible: lp.loaded && lp.title
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
width: Kirigami.Units.smallSpacing
|
||||||
|
visible: lp.loaded && lp.title
|
||||||
|
color: Kirigami.Theme.highlightColor
|
||||||
|
}
|
||||||
|
Image {
|
||||||
|
visible: lp.imageSource
|
||||||
|
Layout.maximumHeight: Kirigami.Units.gridUnit * 5
|
||||||
|
Layout.maximumWidth: Kirigami.Units.gridUnit * 5
|
||||||
|
source: lp.imageSource.replace("mxc://", "image://mxc/")
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
ColumnLayout {
|
||||||
|
id: column
|
||||||
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
Kirigami.Heading {
|
||||||
|
Layout.maximumWidth: messageDelegate.bubbleMaxWidth
|
||||||
|
Layout.fillWidth: true
|
||||||
|
level: 4
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
textFormat: Text.RichText
|
||||||
|
text: "<style>
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<a href=\"" + links[0] + "\">" + lp.title.replace("–", "—") + "</a>"
|
||||||
|
visible: lp.loaded
|
||||||
|
onLinkActivated: RoomManager.openResource(link)
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: lp.description
|
||||||
|
Layout.maximumWidth: messageDelegate.bubbleMaxWidth
|
||||||
|
Layout.fillWidth: true
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
visible: lp.loaded && lp.description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -13,24 +13,28 @@ import org.kde.neochat 1.0
|
|||||||
TimelineContainer {
|
TimelineContainer {
|
||||||
id: messageDelegate
|
id: messageDelegate
|
||||||
|
|
||||||
width: ListView.view.width
|
|
||||||
property bool isEmote: false
|
property bool isEmote: false
|
||||||
|
onOpenContextMenu: openMessageContext(model, label.selectedText, Controller.plainText(label.textDocument))
|
||||||
|
|
||||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||||
hoverComponent: hoverActions
|
hoverComponent: hoverActions
|
||||||
|
|
||||||
innerObject: RichLabel {
|
innerObject: ColumnLayout {
|
||||||
isEmote: messageDelegate.isEmote
|
RichLabel {
|
||||||
Layout.maximumWidth: messageDelegate.bubbleMaxWidth
|
id: label
|
||||||
|
isEmote: messageDelegate.isEmote
|
||||||
TapHandler {
|
Layout.maximumWidth: messageDelegate.bubbleMaxWidth
|
||||||
acceptedButtons: Qt.RightButton
|
|
||||||
onTapped: openMessageContext(model, parent.selectedText)
|
|
||||||
}
|
}
|
||||||
|
Loader {
|
||||||
TapHandler {
|
id: linkPreviewLoader
|
||||||
acceptedButtons: Qt.LeftButton
|
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||||
onLongPressed: openMessageContext(model, parent.selectedText)
|
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||||
|
height: active ? item.implicitHeight : 0
|
||||||
|
active: !currentRoom.usesEncryption && model.display && model.display.includes("http")
|
||||||
|
visible: active
|
||||||
|
sourceComponent: LinkPreviewDelegate {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,49 @@ import org.kde.kirigami 2.15 as Kirigami
|
|||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
QQC2.ItemDelegate {
|
QQC2.ItemDelegate {
|
||||||
|
id: readMarkerDelegate
|
||||||
padding: Kirigami.Units.largeSpacing
|
padding: Kirigami.Units.largeSpacing
|
||||||
topInset: Kirigami.Units.largeSpacing
|
topInset: Kirigami.Units.largeSpacing
|
||||||
topPadding: Kirigami.Units.largeSpacing * 2
|
topPadding: Kirigami.Units.largeSpacing * 2
|
||||||
width: ListView.view.width - Kirigami.Units.gridUnit
|
|
||||||
x: Kirigami.Units.gridUnit / 2
|
// extraWidth defines how the delegate can grow after the listView gets very wide
|
||||||
|
readonly property int extraWidth: messageListView.width >= Kirigami.Units.gridUnit * 46 ? Math.min((messageListView.width - Kirigami.Units.gridUnit * 46), Kirigami.Units.gridUnit * 20) : 0
|
||||||
|
readonly property int delegateMaxWidth: Config.compactLayout ? messageListView.width - Kirigami.Units.largeSpacing * 2 : Math.min(messageListView.width - Kirigami.Units.largeSpacing * 2, Kirigami.Units.gridUnit * 40 + extraWidth)
|
||||||
|
|
||||||
|
width: delegateMaxWidth
|
||||||
|
anchors.leftMargin: Kirigami.Units.largeSpacing
|
||||||
|
anchors.rightMargin: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
state: Config.compactLayout ? "alignLeft" : "alignCenter"
|
||||||
|
// Align left when in compact mode and center when using bubbles
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "alignLeft"
|
||||||
|
AnchorChanges {
|
||||||
|
target: readMarkerDelegate
|
||||||
|
anchors.horizontalCenter: undefined
|
||||||
|
anchors.left: parent ? parent.left : undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "alignCenter"
|
||||||
|
AnchorChanges {
|
||||||
|
target: readMarkerDelegate
|
||||||
|
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
|
||||||
|
anchors.left: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
AnchorAnimation {
|
||||||
|
duration: Kirigami.Units.longDuration
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
contentItem: QQC2.Label {
|
contentItem: QQC2.Label {
|
||||||
text: i18nc("Relative time since the room was last read", "Last read: %1", time)
|
text: i18nc("Relative time since the room was last read", "Last read: %1", time)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ MouseArea {
|
|||||||
id: replyButton
|
id: replyButton
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
implicitHeight: replyName.implicitHeight + (loader.item ? loader.item.height : 0) + Kirigami.Units.largeSpacing
|
implicitHeight: replyName.implicitHeight + (loader.item ? loader.item.height : 0) + Kirigami.Units.largeSpacing
|
||||||
implicitWidth: Math.min(bubbleMaxWidth, Math.max((loader.item ? loader.item.width + Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing : 0), replyName.implicitWidth)) + Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 3
|
implicitWidth: Math.min(contentMaxWidth, Math.max((loader.item ? loader.item.width + Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing : 0), replyName.implicitWidth)) + Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 3
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
parent.Layout.fillWidth = true;
|
parent.Layout.fillWidth = true;
|
||||||
parent.Layout.preferredWidth = Qt.binding(function() { return implicitWidth; })
|
parent.Layout.preferredWidth = Qt.binding(function() { return implicitWidth; })
|
||||||
parent.Layout.maximumWidth = Qt.binding(function() { return bubbleMaxWidth + Kirigami.Units.largeSpacing * 2; })
|
parent.Layout.maximumWidth = Qt.binding(function() { return contentMaxWidth + Kirigami.Units.largeSpacing * 2; })
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: replyLeftBorder
|
id: replyLeftBorder
|
||||||
@@ -79,7 +79,7 @@ MouseArea {
|
|||||||
id: replyText
|
id: replyText
|
||||||
textMessage: reply.display
|
textMessage: reply.display
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
width: Math.min(implicitWidth, bubbleMaxWidth - Kirigami.Units.largeSpacing * 3)
|
width: Math.min(implicitWidth, contentMaxWidth - Kirigami.Units.largeSpacing * 3)
|
||||||
x: Kirigami.Units.smallSpacing * 3 + replyAvatar.width
|
x: Kirigami.Units.smallSpacing * 3 + replyAvatar.width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@ MouseArea {
|
|||||||
readonly property string mediaId: isThumbnail ? content.thumbnailMediaId : content.mediaId
|
readonly property string mediaId: isThumbnail ? content.thumbnailMediaId : content.mediaId
|
||||||
source: "image://mxc/" + mediaId
|
source: "image://mxc/" + mediaId
|
||||||
|
|
||||||
width: bubbleMaxWidth * 0.75 - Kirigami.Units.smallSpacing * 5 - replyAvatar.width
|
width: contentMaxWidth * 0.75 - Kirigami.Units.smallSpacing * 5 - replyAvatar.width
|
||||||
height: reply.content.info.h / reply.content.info.w * width
|
height: reply.content.info.h / reply.content.info.w * width
|
||||||
x: Kirigami.Units.smallSpacing * 3 + replyAvatar.width
|
x: Kirigami.Units.smallSpacing * 3 + replyAvatar.width
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ TextEdit {
|
|||||||
|
|
||||||
Layout.fillWidth: Config.compactLayout
|
Layout.fillWidth: Config.compactLayout
|
||||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||||
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
persistentSelection: true
|
||||||
|
|
||||||
text: "<style>
|
text: "<style>
|
||||||
table {
|
table {
|
||||||
@@ -44,6 +46,10 @@ a{
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
" + (!spoilerRevealed ? "
|
" + (!spoilerRevealed ? "
|
||||||
|
[data-mx-spoiler] a {
|
||||||
|
color: transparent;
|
||||||
|
background: " + Kirigami.Theme.textColor + ";
|
||||||
|
}
|
||||||
[data-mx-spoiler] {
|
[data-mx-spoiler] {
|
||||||
color: transparent;
|
color: transparent;
|
||||||
background: " + Kirigami.Theme.textColor + ";
|
background: " + Kirigami.Theme.textColor + ";
|
||||||
@@ -52,14 +58,19 @@ a{
|
|||||||
</style>" + (isEmote ? "* <a href='https://matrix.to/#/" + author.id + "' style='color: " + author.color + "'>" + author.displayName + "</a> " : "") + textMessage + (isEdited ? (" <span style=\"color: " + Kirigami.Theme.disabledTextColor + "\">" + "<span style='font-size: " + Kirigami.Theme.defaultFont.pixelSize +"px'>" + i18n(" (edited)") + "</span>") : "")
|
</style>" + (isEmote ? "* <a href='https://matrix.to/#/" + author.id + "' style='color: " + author.color + "'>" + author.displayName + "</a> " : "") + textMessage + (isEdited ? (" <span style=\"color: " + Kirigami.Theme.disabledTextColor + "\">" + "<span style='font-size: " + Kirigami.Theme.defaultFont.pixelSize +"px'>" + i18n(" (edited)") + "</span>") : "")
|
||||||
|
|
||||||
color: Kirigami.Theme.textColor
|
color: Kirigami.Theme.textColor
|
||||||
|
selectedTextColor: Kirigami.Theme.highlightedTextColor
|
||||||
|
selectionColor: Kirigami.Theme.highlightColor
|
||||||
font.pointSize: model.reply === undefined && isEmoji.test(model.display) ? Kirigami.Theme.defaultFont.pointSize * 4 : Kirigami.Theme.defaultFont.pointSize
|
font.pointSize: model.reply === undefined && isEmoji.test(model.display) ? Kirigami.Theme.defaultFont.pointSize * 4 : Kirigami.Theme.defaultFont.pointSize
|
||||||
selectByMouse: !Kirigami.Settings.isMobile
|
selectByMouse: !Kirigami.Settings.isMobile
|
||||||
readOnly: true
|
readOnly: true
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
|
|
||||||
onLinkActivated: RoomManager.openResource(link)
|
onLinkActivated: {
|
||||||
onHoveredLinkChanged: if (hoveredLink.length > 0) {
|
spoilerRevealed = true
|
||||||
|
RoomManager.openResource(link)
|
||||||
|
}
|
||||||
|
onHoveredLinkChanged: if (hoveredLink.length > 0 && hoveredLink !== "1") {
|
||||||
applicationWindow().hoverLinkIndicator.text = hoveredLink;
|
applicationWindow().hoverLinkIndicator.text = hoveredLink;
|
||||||
} else {
|
} else {
|
||||||
applicationWindow().hoverLinkIndicator.text = "";
|
applicationWindow().hoverLinkIndicator.text = "";
|
||||||
|
|||||||
@@ -7,19 +7,53 @@ import QtQuick.Layouts 1.15
|
|||||||
|
|
||||||
import org.kde.kirigami 2.15 as Kirigami
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat 1.0
|
||||||
import NeoChat.Component 1.0
|
import NeoChat.Component 1.0
|
||||||
import NeoChat.Dialog 1.0
|
import NeoChat.Dialog 1.0
|
||||||
|
|
||||||
Control {
|
Control {
|
||||||
x: Kirigami.Units.gridUnit * 1.5 + Kirigami.Units.smallSpacing
|
id: stateDelegate
|
||||||
width: ListView.view.width - Kirigami.Units.largeSpacing - x
|
// extraWidth defines how the delegate can grow after the listView gets very wide
|
||||||
|
readonly property int extraWidth: messageListView.width >= Kirigami.Units.gridUnit * 46 ? Math.min((messageListView.width - Kirigami.Units.gridUnit * 46), Kirigami.Units.gridUnit * 20) : 0
|
||||||
|
readonly property int delegateMaxWidth: Config.compactLayout ? messageListView.width: Math.min(messageListView.width, Kirigami.Units.gridUnit * 40 + extraWidth)
|
||||||
|
|
||||||
|
width: delegateMaxWidth
|
||||||
|
// anchors.leftMargin: Kirigami.Units.largeSpacing
|
||||||
|
// anchors.rightMargin: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
state: Config.compactLayout ? "alignLeft" : "alignCenter"
|
||||||
|
// Align left when in compact mode and center when using bubbles
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "alignLeft"
|
||||||
|
AnchorChanges {
|
||||||
|
target: stateDelegate
|
||||||
|
anchors.horizontalCenter: undefined
|
||||||
|
anchors.left: parent ? parent.left : undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "alignCenter"
|
||||||
|
AnchorChanges {
|
||||||
|
target: stateDelegate
|
||||||
|
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
|
||||||
|
anchors.left: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
AnchorAnimation{duration: Kirigami.Units.longDuration; easing.type: Easing.OutCubic}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
height: sectionDelegate.height + rowLayout.height
|
height: sectionDelegate.height + rowLayout.height
|
||||||
SectionDelegate {
|
SectionDelegate {
|
||||||
id: sectionDelegate
|
id: sectionDelegate
|
||||||
width: parent.width
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.leftMargin: Kirigami.Units.smallSpacing
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
visible: model.showSection
|
visible: model.showSection
|
||||||
height: visible ? implicitHeight : 0
|
height: visible ? implicitHeight : 0
|
||||||
}
|
}
|
||||||
@@ -27,8 +61,11 @@ Control {
|
|||||||
RowLayout {
|
RowLayout {
|
||||||
id: rowLayout
|
id: rowLayout
|
||||||
height: label.contentHeight
|
height: label.contentHeight
|
||||||
width: parent.width
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.leftMargin: Kirigami.Units.gridUnit * 1.5 + Kirigami.Units.smallSpacing + (Config.compactLayout ? Kirigami.Units.largeSpacing * 1.25 : 0)
|
||||||
|
anchors.rightMargin: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
Kirigami.Avatar {
|
Kirigami.Avatar {
|
||||||
id: icon
|
id: icon
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15 as QQC2
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
import QtGraphicalEffects 1.12
|
|
||||||
|
|
||||||
import org.kde.kirigami 2.15 as Kirigami
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
|
|
||||||
@@ -13,19 +12,24 @@ import NeoChat.Component 1.0
|
|||||||
import NeoChat.Dialog 1.0
|
import NeoChat.Dialog 1.0
|
||||||
|
|
||||||
QQC2.ItemDelegate {
|
QQC2.ItemDelegate {
|
||||||
id: messageDelegate
|
id: timelineContainer
|
||||||
default property alias innerObject : column.children
|
default property alias innerObject : column.children
|
||||||
// readonly property bool failed: marks == EventStatus.SendingFailed
|
// readonly property bool failed: marks == EventStatus.SendingFailed
|
||||||
|
|
||||||
property bool isEmote: false
|
property bool isEmote: false
|
||||||
property bool cardBackground: true
|
property bool cardBackground: true
|
||||||
|
|
||||||
readonly property int bubbleMaxWidth: Config.compactLayout && !Config.showAvatarInTimeline ? width : (Config.compactLayout ? width - Kirigami.Units.gridUnit * 2 - Kirigami.Units.largeSpacing * 4 : Math.min(width - Kirigami.Units.gridUnit * 2 - Kirigami.Units.largeSpacing * 6, Kirigami.Units.gridUnit * 20))
|
signal openContextMenu
|
||||||
|
|
||||||
|
// The bubble and delegate widths are allowed to grow once the ListView gets beyond a certain size
|
||||||
|
// extraWidth defines this as the excess after a certain ListView width, capped to a max value
|
||||||
|
readonly property int extraWidth: messageListView.width >= Kirigami.Units.gridUnit * 46 ? Math.min((messageListView.width - Kirigami.Units.gridUnit * 46), Kirigami.Units.gridUnit * 20) : 0
|
||||||
|
readonly property int bubbleMaxWidth: Kirigami.Units.gridUnit * 20 + extraWidth * 0.5
|
||||||
|
readonly property int delegateMaxWidth: Config.compactLayout ? messageListView.width : Math.min(messageListView.width, Kirigami.Units.gridUnit * 40 + extraWidth)
|
||||||
|
readonly property int contentMaxWidth: Config.compactLayout ? width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) - Kirigami.Units.largeSpacing * 4 : Math.min(width - Kirigami.Units.gridUnit * 2 - Kirigami.Units.largeSpacing * 6, bubbleMaxWidth)
|
||||||
|
|
||||||
property bool showUserMessageOnRight: Config.showLocalMessagesOnRight &&
|
property bool showUserMessageOnRight: Config.showLocalMessagesOnRight &&
|
||||||
model.author.isLocalUser &&
|
model.author.isLocalUser && !Config.compactLayout
|
||||||
!applicationWindow().wideScreen &&
|
|
||||||
!Config.compactLayout
|
|
||||||
|
|
||||||
signal openExternally()
|
signal openExternally()
|
||||||
signal replyClicked(string eventID)
|
signal replyClicked(string eventID)
|
||||||
@@ -38,7 +42,10 @@ QQC2.ItemDelegate {
|
|||||||
|
|
||||||
topPadding: 0
|
topPadding: 0
|
||||||
bottomPadding: 0
|
bottomPadding: 0
|
||||||
|
width: delegateMaxWidth
|
||||||
|
height: sectionDelegate.height + Math.max(model.showAuthor ? avatar.height : 0, bubble.implicitHeight) + loader.height + (showAuthor ? Kirigami.Units.largeSpacing : 0)
|
||||||
background: null
|
background: null
|
||||||
|
|
||||||
property Item hoverComponent
|
property Item hoverComponent
|
||||||
|
|
||||||
// show hover actions
|
// show hover actions
|
||||||
@@ -51,13 +58,39 @@ QQC2.ItemDelegate {
|
|||||||
// updates the global hover component to point to this delegate, and update its position
|
// updates the global hover component to point to this delegate, and update its position
|
||||||
function updateHoverComponent() {
|
function updateHoverComponent() {
|
||||||
if (hoverComponent) {
|
if (hoverComponent) {
|
||||||
|
hoverComponent.delegate = timelineContainer
|
||||||
hoverComponent.bubble = bubble
|
hoverComponent.bubble = bubble
|
||||||
hoverComponent.updateFunction = updateHoverComponent;
|
hoverComponent.updateFunction = updateHoverComponent;
|
||||||
hoverComponent.event = model
|
hoverComponent.event = model
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
height: sectionDelegate.height + Math.max(model.showAuthor ? avatar.height : 0, bubble.implicitHeight) + loader.height + (showAuthor ? Kirigami.Units.largeSpacing : 0)
|
state: Config.compactLayout ? "alignLeft" : "alignCenter"
|
||||||
|
// Align left when in compact mode and center when using bubbles
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: "alignLeft"
|
||||||
|
AnchorChanges {
|
||||||
|
target: timelineContainer
|
||||||
|
anchors.horizontalCenter: undefined
|
||||||
|
anchors.left: parent ? parent.left : undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "alignCenter"
|
||||||
|
AnchorChanges {
|
||||||
|
target: timelineContainer
|
||||||
|
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
|
||||||
|
anchors.left: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
transitions: [
|
||||||
|
Transition {
|
||||||
|
AnchorAnimation{duration: Kirigami.Units.longDuration; easing.type: Easing.OutCubic}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
SectionDelegate {
|
SectionDelegate {
|
||||||
id: sectionDelegate
|
id: sectionDelegate
|
||||||
@@ -105,24 +138,21 @@ QQC2.ItemDelegate {
|
|||||||
|
|
||||||
QQC2.Control {
|
QQC2.Control {
|
||||||
id: bubble
|
id: bubble
|
||||||
topPadding: !Config.compactLayout ? Kirigami.Units.largeSpacing : 0
|
topPadding: Config.compactLayout ? Kirigami.Units.smallSpacing / 2 : Kirigami.Units.largeSpacing
|
||||||
bottomPadding: !Config.compactLayout ? Kirigami.Units.largeSpacing : 0
|
bottomPadding: Config.compactLayout ? Kirigami.Units.mediumSpacing / 2 : Kirigami.Units.largeSpacing
|
||||||
leftPadding: Kirigami.Units.smallSpacing
|
leftPadding: Kirigami.Units.smallSpacing
|
||||||
rightPadding: Config.compactLayout ? Kirigami.Units.largeSpacing : Kirigami.Units.smallSpacing
|
rightPadding: Config.compactLayout ? Kirigami.Units.largeSpacing : Kirigami.Units.smallSpacing
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|
||||||
// state: Config.compactLayout ? "compactLayout" : "default"
|
|
||||||
state: showUserMessageOnRight ? "userMessageOnRight" : "userMessageOnLeft"
|
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
top: avatar.top
|
top: avatar.top
|
||||||
leftMargin: Kirigami.Units.largeSpacing
|
leftMargin: Kirigami.Units.largeSpacing
|
||||||
rightMargin: showUserMessageOnRight ? Kirigami.Units.smallSpacing : Kirigami.Units.largeSpacing
|
rightMargin: showUserMessageOnRight ? Kirigami.Units.smallSpacing : Kirigami.Units.largeSpacing
|
||||||
}
|
}
|
||||||
// HACK: anchoring didn't reset anchors.right when switching from parent.right to undefined reliably
|
// HACK: anchoring didn't reset anchors.right when switching from parent.right to undefined reliably
|
||||||
width: Config.compactLayout ? messageDelegate.width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) + Kirigami.Units.largeSpacing * 2 : implicitWidth
|
width: Config.compactLayout ? timelineContainer.width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) + Kirigami.Units.largeSpacing * 2 : implicitWidth
|
||||||
|
|
||||||
|
|
||||||
|
state: showUserMessageOnRight ? "userMessageOnRight" : "userMessageOnLeft"
|
||||||
// states for anchor animations on window resize
|
// states for anchor animations on window resize
|
||||||
// as setting anchors to undefined did not work reliably
|
// as setting anchors to undefined did not work reliably
|
||||||
states: [
|
states: [
|
||||||
@@ -160,7 +190,7 @@ QQC2.ItemDelegate {
|
|||||||
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
||||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||||
Layout.preferredWidth: nameLabel.implicitWidth + timeLabel.implicitWidth + Kirigami.Units.largeSpacing * 2
|
Layout.preferredWidth: nameLabel.implicitWidth + timeLabel.implicitWidth + Kirigami.Units.largeSpacing * 2
|
||||||
Layout.maximumWidth: bubbleMaxWidth
|
Layout.maximumWidth: contentMaxWidth
|
||||||
implicitHeight: visible ? nameLabel.implicitHeight : 0
|
implicitHeight: visible ? nameLabel.implicitHeight : 0
|
||||||
|
|
||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
@@ -168,15 +198,13 @@ QQC2.ItemDelegate {
|
|||||||
topInset: 0
|
topInset: 0
|
||||||
|
|
||||||
visible: model.showAuthor && !isEmote
|
visible: model.showAuthor && !isEmote
|
||||||
anchors.left: rowLayout.left
|
width: Math.min(contentMaxWidth - timeLabel.width, implicitWidth)
|
||||||
anchors.right: timeLabel.left
|
|
||||||
anchors.rightMargin: Kirigami.Units.smallSpacing
|
|
||||||
|
|
||||||
text: visible ? author.displayName : ""
|
text: visible ? author.displayName : ""
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
font.weight: Font.Bold
|
font.weight: Font.Bold
|
||||||
color: author.color
|
color: author.color
|
||||||
wrapMode: Text.Wrap
|
elide: Text.ElideRight
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
@@ -193,10 +221,19 @@ QQC2.ItemDelegate {
|
|||||||
}
|
}
|
||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
id: timeLabel
|
id: timeLabel
|
||||||
anchors.right: rowLayout.right
|
leftPadding: Kirigami.Units.largeSpacing
|
||||||
|
rightPadding: Kirigami.Units.largeSpacing
|
||||||
|
anchors.left: nameLabel.right
|
||||||
visible: model.showAuthor && !isEmote
|
visible: model.showAuthor && !isEmote
|
||||||
text: visible ? time.toLocaleTimeString(Locale.ShortFormat) : ""
|
text: visible ? time.toLocaleTimeString(Qt.locale(), Locale.ShortFormat) : ""
|
||||||
color: Kirigami.Theme.disabledTextColor
|
color: Kirigami.Theme.disabledTextColor
|
||||||
|
QQC2.ToolTip.visible: hoverHandler.hovered
|
||||||
|
QQC2.ToolTip.text: time.toLocaleString(Qt.locale(), Locale.LongFormat)
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: hoverHandler
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loader {
|
Loader {
|
||||||
@@ -206,6 +243,7 @@ QQC2.ItemDelegate {
|
|||||||
visible: active
|
visible: active
|
||||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||||
Layout.bottomMargin: Config.compactLayout ? 0 : Kirigami.Units.smallSpacing
|
Layout.bottomMargin: Config.compactLayout ? 0 : Kirigami.Units.smallSpacing
|
||||||
|
Layout.leftMargin: Config.compactLayout ? 0 : Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: replyLoader.item
|
target: replyLoader.item
|
||||||
@@ -218,7 +256,7 @@ QQC2.ItemDelegate {
|
|||||||
|
|
||||||
background: Item {
|
background: Item {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: messageDelegate.hovered
|
visible: timelineContainer.hovered
|
||||||
color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
|
color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
|
||||||
radius: Kirigami.Units.smallSpacing
|
radius: Kirigami.Units.smallSpacing
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -250,7 +288,7 @@ QQC2.ItemDelegate {
|
|||||||
left: bubble.left
|
left: bubble.left
|
||||||
right: parent.right
|
right: parent.right
|
||||||
top: bubble.bottom
|
top: bubble.bottom
|
||||||
topMargin: active && !Config.compactLayout ? Kirigami.Units.smallSpacing : 0
|
topMargin: active && Config.compactLayout ? 0 : Kirigami.Units.smallSpacing
|
||||||
}
|
}
|
||||||
height: active ? item.implicitHeight : 0
|
height: active ? item.implicitHeight : 0
|
||||||
//Layout.bottomMargin: readMarker ? Kirigami.Units.smallSpacing : 0
|
//Layout.bottomMargin: readMarker ? Kirigami.Units.smallSpacing : 0
|
||||||
@@ -258,4 +296,14 @@ QQC2.ItemDelegate {
|
|||||||
visible: active
|
visible: active
|
||||||
sourceComponent: ReactionDelegate { }
|
sourceComponent: ReactionDelegate { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
onTapped: timelineContainer.openContextMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
onLongPressed: timelineContainer.openContextMenu()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
import QtGraphicalEffects 1.15
|
|
||||||
import QtMultimedia 5.15
|
import QtMultimedia 5.15
|
||||||
import Qt.labs.platform 1.1 as Platform
|
import Qt.labs.platform 1.1 as Platform
|
||||||
|
|
||||||
@@ -18,8 +17,6 @@ import NeoChat.Menu.Timeline 1.0
|
|||||||
TimelineContainer {
|
TimelineContainer {
|
||||||
id: videoDelegate
|
id: videoDelegate
|
||||||
|
|
||||||
width: ListView.view.width
|
|
||||||
|
|
||||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||||
hoverComponent: hoverActions
|
hoverComponent: hoverActions
|
||||||
|
|
||||||
@@ -29,6 +26,8 @@ TimelineContainer {
|
|||||||
property bool supportStreaming: true
|
property bool supportStreaming: true
|
||||||
readonly property int maxWidth: 1000 // TODO messageListView.width
|
readonly property int maxWidth: 1000 // TODO messageListView.width
|
||||||
|
|
||||||
|
onOpenContextMenu: openFileContext(model, vid)
|
||||||
|
|
||||||
onDownloadedChanged: {
|
onDownloadedChanged: {
|
||||||
if (downloaded) {
|
if (downloaded) {
|
||||||
vid.source = progressInfo.localPath
|
vid.source = progressInfo.localPath
|
||||||
@@ -43,7 +42,7 @@ TimelineContainer {
|
|||||||
innerObject: Video {
|
innerObject: Video {
|
||||||
id: vid
|
id: vid
|
||||||
|
|
||||||
Layout.maximumWidth: videoDelegate.bubbleMaxWidth
|
Layout.maximumWidth: videoDelegate.contentMaxWidth
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumHeight: Kirigami.Units.gridUnit * 15
|
Layout.maximumHeight: Kirigami.Units.gridUnit * 15
|
||||||
Layout.minimumHeight: Kirigami.Units.gridUnit * 5
|
Layout.minimumHeight: Kirigami.Units.gridUnit * 5
|
||||||
@@ -124,16 +123,6 @@ TimelineContainer {
|
|||||||
videoDelegate.downloadAndPlay()
|
videoDelegate.downloadAndPlay()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.RightButton
|
|
||||||
onTapped: openFileContext(model, parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
onLongPressed: openFileContext(model, parent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadAndPlay() {
|
function downloadAndPlay() {
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ EncryptedDelegate 1.0 EncryptedDelegate.qml
|
|||||||
EventDelegate 1.0 EventDelegate.qml
|
EventDelegate 1.0 EventDelegate.qml
|
||||||
MessageDelegate 1.0 MessageDelegate.qml
|
MessageDelegate 1.0 MessageDelegate.qml
|
||||||
ReadMarkerDelegate 1.0 ReadMarkerDelegate.qml
|
ReadMarkerDelegate 1.0 ReadMarkerDelegate.qml
|
||||||
|
LinkPreviewDelegate 1.0 LinkPreviewDelegate.qml
|
||||||
|
|||||||
39
imports/NeoChat/Dialog/KeyVerification/EmojiItem.qml
Normal file
39
imports/NeoChat/Dialog/KeyVerification/EmojiItem.qml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
import QtQml 2.15
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.19 as Kirigami
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: emojiItem
|
||||||
|
|
||||||
|
property string emoji
|
||||||
|
property string description
|
||||||
|
|
||||||
|
QQC2.Label {
|
||||||
|
id: emojiLabel
|
||||||
|
x: 0
|
||||||
|
y: 0
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height * 0.75
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
text: emojiItem.emoji
|
||||||
|
font.family: "emoji"
|
||||||
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 4
|
||||||
|
}
|
||||||
|
QQC2.Label {
|
||||||
|
x: 0
|
||||||
|
y: parent.height * 0.75
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height * 0.25
|
||||||
|
text: emojiItem.description
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
25
imports/NeoChat/Dialog/KeyVerification/EmojiRow.qml
Normal file
25
imports/NeoChat/Dialog/KeyVerification/EmojiRow.qml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
import QtQml 2.15
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.19 as Kirigami
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: emojiRow
|
||||||
|
|
||||||
|
property alias model: repeater.model
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
Repeater {
|
||||||
|
id: repeater
|
||||||
|
delegate: EmojiItem {
|
||||||
|
emoji: modelData.emoji
|
||||||
|
description: modelData.description
|
||||||
|
width: emojiRow.height
|
||||||
|
height: width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
imports/NeoChat/Dialog/KeyVerification/EmojiSas.qml
Normal file
50
imports/NeoChat/Dialog/KeyVerification/EmojiSas.qml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
import QtQml 2.15
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.19 as Kirigami
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: emojiSas
|
||||||
|
|
||||||
|
required property var model
|
||||||
|
|
||||||
|
signal accept()
|
||||||
|
signal reject()
|
||||||
|
|
||||||
|
visible: dialog.session.state === KeyVerificationSession.WAITINGFORVERIFICATION
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Kirigami.Units.largeSpacing
|
||||||
|
QQC2.Label {
|
||||||
|
text: i18n("Confirm the emoji below are displayed on both devices, in the same order.")
|
||||||
|
}
|
||||||
|
EmojiRow {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
height: Kirigami.Units.gridUnit * 4
|
||||||
|
model: emojiSas.model.slice(0, 4)
|
||||||
|
}
|
||||||
|
EmojiRow {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
height: Kirigami.Units.gridUnit * 4
|
||||||
|
model: emojiSas.model.slice(4, 7)
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
QQC2.Button {
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
text: i18n("They match")
|
||||||
|
icon.name: "dialog-ok"
|
||||||
|
onClicked: emojiSas.accept()
|
||||||
|
}
|
||||||
|
QQC2.Button {
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
text: i18n("They don't match")
|
||||||
|
icon.name: "dialog-cancel"
|
||||||
|
onClicked: emojiSas.reject()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtQml 2.15
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.19 as Kirigami
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
Kirigami.Page {
|
||||||
|
id: dialog
|
||||||
|
title: i18n("Session Verification")
|
||||||
|
|
||||||
|
required property var session
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.fill: parent
|
||||||
|
VerificationCanceled {
|
||||||
|
visible: dialog.session.state === KeyVerificationSession.CANCELED
|
||||||
|
anchors.centerIn: parent
|
||||||
|
reason: dialog.session.error
|
||||||
|
}
|
||||||
|
EmojiSas {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: dialog.session.state === KeyVerificationSession.WAITINGFORVERIFICATION
|
||||||
|
model: dialog.session.sasEmojis
|
||||||
|
onReject: dialog.session.cancelVerification(KeyVerificationSession.MISMATCHED_SAS)
|
||||||
|
onAccept: dialog.session.sendMac()
|
||||||
|
}
|
||||||
|
Message {
|
||||||
|
visible: dialog.session.state === KeyVerificationSession.WAITINGFORREADY
|
||||||
|
anchors.centerIn: parent
|
||||||
|
icon: "security-medium-symbolic"
|
||||||
|
text: i18n("Waiting for device to accept verification.")
|
||||||
|
}
|
||||||
|
Message {
|
||||||
|
visible: dialog.session.state === KeyVerificationSession.INCOMING
|
||||||
|
anchors.centerIn: parent
|
||||||
|
icon: "security-medium-symbolic"
|
||||||
|
text: i18n("Incoming key verification request from device **%1**", dialog.session.remoteDeviceId)
|
||||||
|
}
|
||||||
|
Message {
|
||||||
|
visible: dialog.session.state === KeyVerificationSession.WAITINGFORMAC
|
||||||
|
anchors.centerIn: parent
|
||||||
|
icon: "security-medium-symbolic"
|
||||||
|
text: i18n("Waiting for other party to verify.")
|
||||||
|
}
|
||||||
|
Kirigami.BasicListItem {
|
||||||
|
id: emojiVerification
|
||||||
|
text: "Emoji Verification"
|
||||||
|
visible: dialog.session.state === KeyVerificationSession.READY
|
||||||
|
subtitle: i18n("Compare a set of emoji on both devices")
|
||||||
|
onClicked: {
|
||||||
|
dialog.session.sendStartSas()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message {
|
||||||
|
visible: dialog.session.state === KeyVerificationSession.DONE
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: i18n("Successfully verified device **%1**", dialog.session.remoteDeviceId)
|
||||||
|
icon: "security-high"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: QQC2.ToolBar {
|
||||||
|
visible: dialog.session.state === KeyVerificationSession.INCOMING
|
||||||
|
QQC2.DialogButtonBox {
|
||||||
|
anchors.fill: parent
|
||||||
|
Item { Layout.fillWidth: true }
|
||||||
|
QQC2.Button {
|
||||||
|
text: i18n("Accept")
|
||||||
|
icon.name: "dialog-ok"
|
||||||
|
onClicked: dialog.session.sendReady()
|
||||||
|
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
|
||||||
|
}
|
||||||
|
QQC2.Button {
|
||||||
|
text: i18n("Decline")
|
||||||
|
icon.name: "dialog-cancel"
|
||||||
|
onClicked: dialog.session.cancelVerification("m.user", "Declined")
|
||||||
|
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.CancelRole
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
imports/NeoChat/Dialog/KeyVerification/Message.qml
Normal file
27
imports/NeoChat/Dialog/KeyVerification/Message.qml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
import QtQml 2.15
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.19 as Kirigami
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: message
|
||||||
|
required property string icon
|
||||||
|
required property string text
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
Kirigami.Icon {
|
||||||
|
width: Kirigami.Units.iconSizes.enormous
|
||||||
|
height: width
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
source: message.icon
|
||||||
|
}
|
||||||
|
QQC2.Label {
|
||||||
|
text: message.text
|
||||||
|
textFormat: Text.MarkdownText
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
import QtQml 2.15
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.19 as Kirigami
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
Message {
|
||||||
|
id: verificationCanceled
|
||||||
|
|
||||||
|
required property int reason
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
icon: "security-low"
|
||||||
|
text: {
|
||||||
|
switch(verificationCanceled.reason) {
|
||||||
|
case KeyVerificationSession.NONE:
|
||||||
|
return i18n("The session verification was canceled for unknown reason.");
|
||||||
|
case KeyVerificationSession.TIMEOUT:
|
||||||
|
return i18n("The session verification timed out.");
|
||||||
|
case KeyVerificationSession.REMOTE_TIMEOUT:
|
||||||
|
return i18n("The session verification timed out for remote party.");
|
||||||
|
case KeyVerificationSession.USER:
|
||||||
|
return i18n("You canceled the session verification.");
|
||||||
|
case KeyVerificationSession.REMOTE_USER:
|
||||||
|
return i18n("The remote party canceled the session verification.");
|
||||||
|
case KeyVerificationSession.UNEXPECTED_MESSAGE:
|
||||||
|
return i18n("The session verification was canceled because we received an unexpected message.");
|
||||||
|
case KeyVerificationSession.REMOTE_UNEXPECTED_MESSAGE:
|
||||||
|
return i18n("The remote party canceled the session verification because it received an unexpected message.");
|
||||||
|
case KeyVerificationSession.UNKNOWN_TRANSACTION:
|
||||||
|
return i18n("The session verification was canceled because it received a message for an unknown session.");
|
||||||
|
case KeyVerificationSession.REMOTE_UNKNOWN_TRANSACTION:
|
||||||
|
return i18n("The remote party canceled the session verification because it received a message for an unknown session.");
|
||||||
|
case KeyVerificationSession.UNKNOWN_METHOD:
|
||||||
|
return i18n("The session verification was canceled because NeoChat is unable to handle this verification method.");
|
||||||
|
case KeyVerificationSession.REMOTE_UNKNOWN_METHOD:
|
||||||
|
return i18n("The remote party canceled the session verification because it is unable to handle this verification method.");
|
||||||
|
case KeyVerificationSession.KEY_MISMATCH:
|
||||||
|
return i18n("The session verification was canceled because the keys are incorrect.");
|
||||||
|
case KeyVerificationSession.REMOTE_KEY_MISMATCH:
|
||||||
|
return i18n("The remote party canceled the session verification because the keys are incorrect.");
|
||||||
|
case KeyVerificationSession.USER_MISMATCH:
|
||||||
|
return i18n("The session verification was canceled because it verifies an unexpected user.");
|
||||||
|
case KeyVerificationSession.REMOTE_USER_MISMATCH:
|
||||||
|
return i18n("The remote party canceled the session verification because it verifies an unexpected user.");
|
||||||
|
case KeyVerificationSession.INVALID_MESSAGE:
|
||||||
|
return i18n("The session verification was canceled because we received an invalid message.");
|
||||||
|
case KeyVerificationSession.REMOTE_INVALID_MESSAGE:
|
||||||
|
return i18n("The remote party canceled the session verification because it received an invalid message.");
|
||||||
|
case KeyVerificationSession.SESSION_ACCEPTED:
|
||||||
|
return i18n("The session was accepted on a different device"); //TODO this should not be visible
|
||||||
|
case KeyVerificationSession.REMOTE_SESSION_ACCEPTED:
|
||||||
|
return i18n("The session was accepted on a different device"); //TODO neither should this
|
||||||
|
case KeyVerificationSession.MISMATCHED_COMMITMENT:
|
||||||
|
return i18n("The session verification was canceled because of a mismatched key.");
|
||||||
|
case KeyVerificationSession.REMOTE_MISMATCHED_COMMITMENT:
|
||||||
|
return i18n("The remote party canceled the session verification because of a mismatched key.");
|
||||||
|
case KeyVerificationSession.MISMATCHED_SAS:
|
||||||
|
return i18n("The session verification was canceled because the keys do not match.");
|
||||||
|
case KeyVerificationSession.REMOTE_MISMATCHED_SAS:
|
||||||
|
return i18n("The remote party canceled the session verification because the keys do not match.");
|
||||||
|
default:
|
||||||
|
return i18n("The session verification was canceled due to an unknown error.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
imports/NeoChat/Dialog/KeyVerification/qmldir
Normal file
7
imports/NeoChat/Dialog/KeyVerification/qmldir
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
module NeoChat.Dialog.KeyVerification
|
||||||
|
KeyVerificationDialog 1.0 KeyVerificationDialog.qml
|
||||||
|
Message 1.0 Message.qml
|
||||||
|
VerificationCanceled 1.0 VerificationCanceled.qml
|
||||||
|
EmojiItem 1.0 EmojiItem.qml
|
||||||
|
EmojiRow 1.0 EmojiRow.qml
|
||||||
|
EmojiSas 1.0 EmojiSas.qml
|
||||||
@@ -14,6 +14,8 @@ import NeoChat.Component 1.0
|
|||||||
Kirigami.OverlaySheet {
|
Kirigami.OverlaySheet {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
signal closed()
|
||||||
|
|
||||||
property var room
|
property var room
|
||||||
property var user
|
property var user
|
||||||
|
|
||||||
@@ -52,7 +54,7 @@ Kirigami.OverlaySheet {
|
|||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (avatarMediaId) {
|
if (avatarMediaId) {
|
||||||
fullScreenImage.createObject(parent, {"filename": displayName, "source": room.urlToMxcUrl(avatarUrl)}).showFullScreen()
|
fullScreenImage.createObject(parent, {filename: displayName, source: room.urlToMxcUrl(avatarUrl)}).showFullScreen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,5 +166,11 @@ Kirigami.OverlaySheet {
|
|||||||
FullScreenImage {}
|
FullScreenImage {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSheetOpenChanged: {
|
||||||
|
if (!sheetOpen) {
|
||||||
|
closed()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ OpenFileDialog 1.0 OpenFileDialog.qml
|
|||||||
ImageClipboardDialog 1.0 ImageClipboardDialog.qml
|
ImageClipboardDialog 1.0 ImageClipboardDialog.qml
|
||||||
StartChatDialog 1.0 StartChatDialog.qml
|
StartChatDialog 1.0 StartChatDialog.qml
|
||||||
EmojiDialog 1.0 EmojiDialog.qml
|
EmojiDialog 1.0 EmojiDialog.qml
|
||||||
|
KeyVerificationDialog 1.0 KeyVerificationDialog.qml
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ Labs.MenuBar {
|
|||||||
Labs.MenuItem {
|
Labs.MenuItem {
|
||||||
text: i18nc("menu", "New Private Chat…")
|
text: i18nc("menu", "New Private Chat…")
|
||||||
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0
|
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0
|
||||||
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/StartChatPage.qml", {"connection": Controller.activeConnection})
|
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/StartChatPage.qml", {connection: Controller.activeConnection})
|
||||||
}
|
}
|
||||||
Labs.MenuItem {
|
Labs.MenuItem {
|
||||||
text: i18nc("menu", "New Group…")
|
text: i18nc("menu", "New Group…")
|
||||||
@@ -58,7 +58,7 @@ Labs.MenuBar {
|
|||||||
}
|
}
|
||||||
Labs.MenuItem {
|
Labs.MenuItem {
|
||||||
text: i18nc("menu", "Browse Chats…")
|
text: i18nc("menu", "Browse Chats…")
|
||||||
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/JoinRoomPage.qml", {"connection": Controller.activeConnection})
|
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/JoinRoomPage.qml", {connection: Controller.activeConnection})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EditMenu {
|
EditMenu {
|
||||||
@@ -92,7 +92,7 @@ Labs.MenuBar {
|
|||||||
|
|
||||||
Labs.MenuItem {
|
Labs.MenuItem {
|
||||||
text: i18nc("menu", "Matrix FAQ")
|
text: i18nc("menu", "Matrix FAQ")
|
||||||
onTriggered: Qt.openUrlExternally("https://matrix.org/faq/")
|
onTriggered: UrlHelper.openUrl("https://matrix.org/faq/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ Loader {
|
|||||||
Menu {
|
Menu {
|
||||||
MenuItem {
|
MenuItem {
|
||||||
id: newWindow
|
id: newWindow
|
||||||
text: i18n("Open in new window")
|
text: i18n("Open in New Window")
|
||||||
onTriggered: RoomManager.openWindow(room);
|
onTriggered: RoomManager.openWindow(room);
|
||||||
visible: !Kirigami.Settings.isMobile
|
visible: !Kirigami.Settings.isMobile
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ Loader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: i18nc("@action:inmenu", "Copy address to clipboard")
|
text: i18nc("@action:inmenu", "Copy Address to Clipboard")
|
||||||
onTriggered: if (room.canonicalAlias.length === 0) {
|
onTriggered: if (room.canonicalAlias.length === 0) {
|
||||||
Clipboard.saveText(room.id)
|
Clipboard.saveText(room.id)
|
||||||
} else {
|
} else {
|
||||||
@@ -57,6 +57,56 @@ Loader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Menu {
|
||||||
|
title: i18n("Notification State")
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: i18n("Follow Global Setting")
|
||||||
|
checkable: true
|
||||||
|
autoExclusive: true
|
||||||
|
checked: room.pushNotificationState === PushNotificationState.Default
|
||||||
|
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||||
|
onTriggered: {
|
||||||
|
room.pushNotificationState = PushNotificationState.Default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: i18nc("As in 'notify for all messages'","All")
|
||||||
|
checkable: true
|
||||||
|
autoExclusive: true
|
||||||
|
checked: room.pushNotificationState === PushNotificationState.All
|
||||||
|
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||||
|
onTriggered: {
|
||||||
|
room.pushNotificationState = PushNotificationState.All
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: i18nc("As in 'notify when the user is mentioned or the message contains a set keyword'","@Mentions and Keywords")
|
||||||
|
checkable: true
|
||||||
|
autoExclusive: true
|
||||||
|
checked: room.pushNotificationState === PushNotificationState.MentionKeyword
|
||||||
|
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||||
|
onTriggered: {
|
||||||
|
room.pushNotificationState = PushNotificationState.MentionKeyword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: i18nc("As in 'do not notify for any messages'","Off")
|
||||||
|
checkable: true
|
||||||
|
autoExclusive: true
|
||||||
|
checked: room.pushNotificationState === PushNotificationState.Mute
|
||||||
|
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||||
|
onTriggered: {
|
||||||
|
room.pushNotificationState = PushNotificationState.Mute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: i18n("Room Settings")
|
||||||
|
onTriggered: ApplicationWindow.window.pageStack.pushDialogLayer('qrc:/imports/NeoChat/RoomSettings/Categories.qml', {room: room})
|
||||||
|
}
|
||||||
|
|
||||||
MenuSeparator {}
|
MenuSeparator {}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import NeoChat.Menu 1.0
|
|||||||
MessageDelegateContextMenu {
|
MessageDelegateContextMenu {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
signal closeFullscreen
|
||||||
|
|
||||||
required property var file
|
required property var file
|
||||||
required property var progressInfo
|
required property var progressInfo
|
||||||
required property string mimeType
|
required property string mimeType
|
||||||
@@ -24,13 +26,13 @@ MessageDelegateContextMenu {
|
|||||||
icon.name: "document-open"
|
icon.name: "document-open"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (file.downloaded) {
|
if (file.downloaded) {
|
||||||
if (!Qt.openUrlExternally(progressInfo.localPath)) {
|
if (!UrlHelper.openUrl(progressInfo.localPath)) {
|
||||||
Qt.openUrlExternally(progressInfo.localDir);
|
UrlHelper.openUrl(progressInfo.localDir);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
file.onDownloadedChanged.connect(function() {
|
file.onDownloadedChanged.connect(function() {
|
||||||
if (!Qt.openUrlExternally(progressInfo.localPath)) {
|
if (!UrlHelper.openUrl(progressInfo.localPath)) {
|
||||||
Qt.openUrlExternally(progressInfo.localDir);
|
UrlHelper.openUrl(progressInfo.localDir);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
currentRoom.downloadFile(eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId))
|
currentRoom.downloadFile(eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId))
|
||||||
@@ -50,7 +52,8 @@ MessageDelegateContextMenu {
|
|||||||
text: i18n("Reply")
|
text: i18n("Reply")
|
||||||
icon.name: "mail-replied-symbolic"
|
icon.name: "mail-replied-symbolic"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
ChatBoxHelper.replyToMessage(eventId, message, author);
|
chatBoxHelper.replyToMessage(eventId, message, author);
|
||||||
|
root.closeFullscreen()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
@@ -60,8 +63,18 @@ MessageDelegateContextMenu {
|
|||||||
icon.color: "red"
|
icon.color: "red"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
currentRoom.redactEvent(eventId);
|
currentRoom.redactEvent(eventId);
|
||||||
|
root.closeFullscreen()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report")
|
||||||
|
icon.name: "dialog-warning-symbolic"
|
||||||
|
visible: author.id !== currentRoom.localUser.id
|
||||||
|
onTriggered: applicationWindow().pageStack.pushDialogLayer("qrc:/imports/NeoChat/Menu/Timeline/ReportSheet.qml", {room: currentRoom, eventId: eventId}, {
|
||||||
|
title: i18nc("@title", "Report Message"),
|
||||||
|
width: Kirigami.Units.gridUnit * 25
|
||||||
|
})
|
||||||
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("View Source")
|
text: i18n("View Source")
|
||||||
icon.name: "code-context"
|
icon.name: "code-context"
|
||||||
@@ -72,6 +85,7 @@ MessageDelegateContextMenu {
|
|||||||
title: i18n("Message Source"),
|
title: i18n("Message Source"),
|
||||||
width: Kirigami.Units.gridUnit * 25
|
width: Kirigami.Units.gridUnit * 25
|
||||||
});
|
});
|
||||||
|
root.closeFullscreen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ Loader {
|
|||||||
property string formattedBody: ""
|
property string formattedBody: ""
|
||||||
required property string source
|
required property string source
|
||||||
property string selectedText: ""
|
property string selectedText: ""
|
||||||
|
required property string plainMessage
|
||||||
|
|
||||||
property list<Kirigami.Action> nestedActions
|
property list<Kirigami.Action> nestedActions
|
||||||
|
|
||||||
@@ -27,13 +28,13 @@ Loader {
|
|||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Edit")
|
text: i18n("Edit")
|
||||||
icon.name: "document-edit"
|
icon.name: "document-edit"
|
||||||
onTriggered: ChatBoxHelper.edit(message, formattedBody, eventId);
|
onTriggered: chatBoxHelper.edit(message, formattedBody, eventId);
|
||||||
visible: eventType.length > 0 && author.id === Controller.activeConnection.localUserId && (eventType === "emote" || eventType === "message")
|
visible: eventType.length > 0 && author.id === Controller.activeConnection.localUserId && (eventType === "emote" || eventType === "message")
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Reply")
|
text: i18n("Reply")
|
||||||
icon.name: "mail-replied-symbolic"
|
icon.name: "mail-replied-symbolic"
|
||||||
onTriggered: ChatBoxHelper.replyToMessage(eventId, message, author);
|
onTriggered: chatBoxHelper.replyToMessage(eventId, message, author);
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
visible: author.id === currentRoom.localUser.id || currentRoom.canSendState("redact")
|
visible: author.id === currentRoom.localUser.id || currentRoom.canSendState("redact")
|
||||||
@@ -45,7 +46,16 @@ Loader {
|
|||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Copy")
|
text: i18n("Copy")
|
||||||
icon.name: "edit-copy"
|
icon.name: "edit-copy"
|
||||||
onTriggered: Clipboard.saveText(loadRoot.selectedText === "" ? loadRoot.message : loadRoot.selectedText)
|
onTriggered: Clipboard.saveText(loadRoot.selectedText === "" ? loadRoot.plainMessage : loadRoot.selectedText)
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report")
|
||||||
|
icon.name: "dialog-warning-symbolic"
|
||||||
|
visible: author.id !== currentRoom.localUser.id
|
||||||
|
onTriggered: applicationWindow().pageStack.pushDialogLayer("qrc:/imports/NeoChat/Menu/Timeline/ReportSheet.qml", {room: currentRoom, eventId: eventId}, {
|
||||||
|
title: i18nc("@title", "Report Message"),
|
||||||
|
width: Kirigami.Units.gridUnit * 25
|
||||||
|
})
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("View Source")
|
text: i18n("View Source")
|
||||||
@@ -111,7 +121,7 @@ Loader {
|
|||||||
Instantiator {
|
Instantiator {
|
||||||
model: WebShortcutModel {
|
model: WebShortcutModel {
|
||||||
id: webshortcutmodel
|
id: webshortcutmodel
|
||||||
selectedText: loadRoot.selectedText ? loadRoot.selectedText : loadRoot.message
|
selectedText: loadRoot.selectedText ? loadRoot.selectedText : loadRoot.plainMessage
|
||||||
onOpenUrl: RoomManager.visitNonMatrix(url)
|
onOpenUrl: RoomManager.visitNonMatrix(url)
|
||||||
}
|
}
|
||||||
delegate: QQC2.MenuItem {
|
delegate: QQC2.MenuItem {
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ Kirigami.Page {
|
|||||||
ScrollView {
|
ScrollView {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
contentWidth: availableWidth
|
contentWidth: availableWidth
|
||||||
|
|
||||||
|
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
TextArea {
|
TextArea {
|
||||||
id: sourceTextArea
|
id: sourceTextArea
|
||||||
text: sourceText
|
text: sourceText
|
||||||
|
|||||||
47
imports/NeoChat/Menu/Timeline/ReportSheet.qml
Normal file
47
imports/NeoChat/Menu/Timeline/ReportSheet.qml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.20 as Kirigami
|
||||||
|
|
||||||
|
Kirigami.Page {
|
||||||
|
id: reportSheet
|
||||||
|
|
||||||
|
property var room
|
||||||
|
property string eventId
|
||||||
|
|
||||||
|
title: i18n("Report Message")
|
||||||
|
|
||||||
|
QQC2.TextArea {
|
||||||
|
id: reason
|
||||||
|
placeholderText: i18n("Reason for reporting this message")
|
||||||
|
anchors.fill: parent
|
||||||
|
wrapMode: TextEdit.Wrap
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: QQC2.ToolBar {
|
||||||
|
QQC2.DialogButtonBox {
|
||||||
|
anchors.fill: parent
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
QQC2.Button {
|
||||||
|
text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report")
|
||||||
|
icon.name: "dialog-warning-symbolic"
|
||||||
|
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
|
||||||
|
onClicked: {
|
||||||
|
reportSheet.room.reportEvent(eventId, reason.text)
|
||||||
|
reportSheet.closeDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QQC2.Button {
|
||||||
|
text: i18nc("@action", "Cancel")
|
||||||
|
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.RejectRole
|
||||||
|
onClicked: reportSheet.closeDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,3 +2,4 @@ module NeoChat.Menu.Timeline
|
|||||||
MessageDelegateContextMenu 1.0 MessageDelegateContextMenu.qml
|
MessageDelegateContextMenu 1.0 MessageDelegateContextMenu.qml
|
||||||
FileDelegateContextMenu 1.0 FileDelegateContextMenu.qml
|
FileDelegateContextMenu 1.0 FileDelegateContextMenu.qml
|
||||||
MessageSourceSheet 1.0 MessageSourceSheet.qml
|
MessageSourceSheet 1.0 MessageSourceSheet.qml
|
||||||
|
ReportSheet 1.0 ReportSheet.qml
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15 as QQC2
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
import QtGraphicalEffects 1.15
|
|
||||||
import Qt.labs.platform 1.1 as Platform
|
import Qt.labs.platform 1.1 as Platform
|
||||||
|
|
||||||
import org.kde.kirigami 2.15 as Kirigami
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
|
|||||||
@@ -3,18 +3,11 @@
|
|||||||
|
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
import QtQuick.Controls 2.12 as QQC2
|
import QtQuick.Controls 2.12 as QQC2
|
||||||
import org.kde.kirigami 2.12 as Kirigami
|
import org.kde.kirigami 2.19 as Kirigami
|
||||||
|
|
||||||
Kirigami.Page {
|
Kirigami.Page {
|
||||||
title: i18n("Loading…")
|
Kirigami.LoadingPlaceholder {
|
||||||
|
|
||||||
Kirigami.PlaceholderMessage {
|
|
||||||
id: loadingIndicator
|
id: loadingIndicator
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: i18n("Loading…")
|
|
||||||
QQC2.BusyIndicator {
|
|
||||||
running: false
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,95 @@ import NeoChat.Component 1.0
|
|||||||
import NeoChat.Menu 1.0
|
import NeoChat.Menu 1.0
|
||||||
|
|
||||||
Kirigami.ScrollablePage {
|
Kirigami.ScrollablePage {
|
||||||
|
|
||||||
|
header: ColumnLayout {
|
||||||
|
visible: !page.collapsedMode
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: spaceList
|
||||||
|
property string activeSpaceId: ''
|
||||||
|
|
||||||
|
orientation: Qt.Horizontal
|
||||||
|
spacing: Kirigami.Units.largeSpacing
|
||||||
|
clip:true
|
||||||
|
visible: spaceList.count > 0
|
||||||
|
|
||||||
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 3
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
model: SortFilterSpaceListModel {
|
||||||
|
id: sortFilterSpaceListModel
|
||||||
|
sourceModel: RoomListModel {
|
||||||
|
id: spaceListModel
|
||||||
|
connection: Controller.activeConnection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: SpaceHierarchyCache
|
||||||
|
function onSpaceHierarchyChanged() {
|
||||||
|
if (spaceList.activeSpaceId !== '') {
|
||||||
|
sortFilterRoomListModel.activeSpaceRooms = SpaceHierarchyCache.getRoomListForSpace(spaceList.activeSpaceId, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header: QQC2.Control {
|
||||||
|
contentItem: QQC2.RoundButton {
|
||||||
|
id: homeButton
|
||||||
|
flat: true
|
||||||
|
padding: Kirigami.Units.gridUnit / 2
|
||||||
|
icon.name: "home"
|
||||||
|
text: i18nc("@action:button", "Show All Rooms")
|
||||||
|
display: QQC2.AbstractButton.IconOnly
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
sortFilterRoomListModel.activeSpaceRooms = [];
|
||||||
|
spaceList.activeSpaceId = '';
|
||||||
|
listView.positionViewAtIndex(0, ListView.Beginning);
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.ToolTip {
|
||||||
|
text: homeButton.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: QQC2.Control {
|
||||||
|
required property string avatar
|
||||||
|
required property var currentRoom
|
||||||
|
required property int index
|
||||||
|
required property string id
|
||||||
|
implicitWidth: ListView.view.headerItem.implicitWidth
|
||||||
|
implicitHeight: ListView.view.headerItem.implicitHeight
|
||||||
|
|
||||||
|
contentItem: Kirigami.Avatar {
|
||||||
|
id: del
|
||||||
|
|
||||||
|
actions.main: Kirigami.Action {
|
||||||
|
id: enterSpaceAction
|
||||||
|
onTriggered: {
|
||||||
|
spaceList.activeSpaceId = id;
|
||||||
|
sortFilterRoomListModel.activeSpaceRooms = SpaceHierarchyCache.getRoomListForSpace(id, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.ToolTip {
|
||||||
|
text: currentRoom.displayName
|
||||||
|
}
|
||||||
|
|
||||||
|
source: avatar !== "" ? "image://mxc/" + avatar : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Kirigami.Separator {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
id: page
|
id: page
|
||||||
|
|
||||||
title: i18n("Rooms")
|
title: i18n("Rooms")
|
||||||
@@ -22,21 +111,10 @@ Kirigami.ScrollablePage {
|
|||||||
property var enteredRoom
|
property var enteredRoom
|
||||||
property bool collapsedMode: Config.roomListPageWidth === applicationWindow().collapsedPageWidth && applicationWindow().shouldUseSidebars
|
property bool collapsedMode: Config.roomListPageWidth === applicationWindow().collapsedPageWidth && applicationWindow().shouldUseSidebars
|
||||||
|
|
||||||
|
verticalScrollBarPolicy: collapsedMode ? QQC2.ScrollBar.AlwaysOff : QQC2.ScrollBar.AsNeeded
|
||||||
|
|
||||||
onCollapsedModeChanged: if (collapsedMode) {
|
onCollapsedModeChanged: if (collapsedMode) {
|
||||||
sortFilterRoomListModel.filterText = "";
|
sortFilterRoomListModel.filterText = "";
|
||||||
if (page.contentItem && page.contentItem.flickableItem && page.contentItem.flickableItem.QQC2.ScrollBar.vertical) {
|
|
||||||
page.contentItem.flickableItem.QQC2.ScrollBar.vertical.visible = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
page.contentItem.flickableItem.QQC2.ScrollBar.vertical.visible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// HACK: the scrollbar is created with a 0 timer, so we need to set the visible flag
|
|
||||||
// after it has been created
|
|
||||||
Timer {
|
|
||||||
running: true
|
|
||||||
interval: 200
|
|
||||||
onTriggered: page.contentItem.flickableItem.QQC2.ScrollBar.vertical.visible = !collapsedMode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -79,38 +157,39 @@ Kirigami.ScrollablePage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
header: QQC2.ItemDelegate {
|
|
||||||
visible: page.collapsedMode
|
|
||||||
action: Kirigami.Action {
|
|
||||||
id: enterRoomAction
|
|
||||||
onTriggered: quickView.item.open();
|
|
||||||
}
|
|
||||||
topPadding: Kirigami.Units.largeSpacing
|
|
||||||
leftPadding: Kirigami.Units.largeSpacing
|
|
||||||
rightPadding: Kirigami.Units.largeSpacing
|
|
||||||
bottomPadding: Kirigami.Units.largeSpacing
|
|
||||||
width: visible ? page.width : 0
|
|
||||||
height: visible ? Kirigami.Units.gridUnit * 2 : 0
|
|
||||||
|
|
||||||
Kirigami.Icon {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: 22
|
|
||||||
height: 22
|
|
||||||
source: "search"
|
|
||||||
}
|
|
||||||
Kirigami.Separator {
|
|
||||||
width: parent.width
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: listView
|
id: listView
|
||||||
|
|
||||||
activeFocusOnTab: true
|
activeFocusOnTab: true
|
||||||
clip: accountList.count > 1
|
clip: accountList.count > 1
|
||||||
|
|
||||||
|
header: QQC2.ItemDelegate {
|
||||||
|
visible: page.collapsedMode
|
||||||
|
action: Kirigami.Action {
|
||||||
|
id: enterRoomAction
|
||||||
|
onTriggered: quickView.item.open();
|
||||||
|
}
|
||||||
|
topPadding: Kirigami.Units.largeSpacing
|
||||||
|
leftPadding: Kirigami.Units.largeSpacing
|
||||||
|
rightPadding: Kirigami.Units.largeSpacing
|
||||||
|
bottomPadding: Kirigami.Units.largeSpacing
|
||||||
|
width: visible ? page.width : 0
|
||||||
|
height: visible ? Kirigami.Units.gridUnit * 2 : 0
|
||||||
|
|
||||||
|
Kirigami.Icon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 22
|
||||||
|
height: 22
|
||||||
|
source: "search"
|
||||||
|
}
|
||||||
|
Kirigami.Separator {
|
||||||
|
width: parent.width
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
Kirigami.PlaceholderMessage {
|
Kirigami.PlaceholderMessage {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: parent.width - (Kirigami.Units.largeSpacing * 4)
|
width: parent.width - (Kirigami.Units.largeSpacing * 4)
|
||||||
@@ -240,14 +319,12 @@ Kirigami.ScrollablePage {
|
|||||||
subtitle: subtitleText
|
subtitle: subtitleText
|
||||||
subtitleItem.textFormat: Text.PlainText
|
subtitleItem.textFormat: Text.PlainText
|
||||||
onPressAndHold: {
|
onPressAndHold: {
|
||||||
const menu = roomListContextMenu.createObject(page, {"room": currentRoom})
|
createRoomListContextMenu()
|
||||||
configButton.visible = true
|
}
|
||||||
configButton.down = true
|
TapHandler {
|
||||||
menu.closed.connect(function() {
|
acceptedButtons: Qt.RightButton
|
||||||
configButton.down = undefined
|
acceptedDevices: PointerDevice.Mouse
|
||||||
configButton.visible = Qt.binding(function() { return roomListItem.hovered || Kirigami.Settings.isMobile })
|
onTapped: createRoomListContextMenu()
|
||||||
})
|
|
||||||
menu.open()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
leading: Kirigami.Avatar {
|
leading: Kirigami.Avatar {
|
||||||
@@ -261,15 +338,17 @@ Kirigami.ScrollablePage {
|
|||||||
|
|
||||||
trailing: RowLayout {
|
trailing: RowLayout {
|
||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
text: notificationCount
|
text: notificationCount > 0 ? notificationCount : "●"
|
||||||
visible: notificationCount > 0
|
visible: unreadCount > 0
|
||||||
padding: Kirigami.Units.smallSpacing
|
padding: Kirigami.Units.smallSpacing
|
||||||
color: highlightCount > 0 ? "white" : Kirigami.Theme.textColor
|
color: Kirigami.Theme.textColor
|
||||||
Layout.minimumWidth: height
|
Layout.minimumWidth: height
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
|
visible: notificationCount > 0
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
||||||
color: highlightCount > 0 ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
|
color: highlightCount > 0 ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.disabledTextColor
|
||||||
|
opacity: highlightCount > 0 ? 1 : 0.3
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,18 +361,22 @@ Kirigami.ScrollablePage {
|
|||||||
id: optionAction
|
id: optionAction
|
||||||
icon.name: "configure"
|
icon.name: "configure"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
const menu = roomListContextMenu.createObject(page, {"room": currentRoom})
|
createRoomListContextMenu()
|
||||||
configButton.visible = true
|
|
||||||
configButton.down = true
|
|
||||||
menu.closed.connect(function() {
|
|
||||||
configButton.down = undefined
|
|
||||||
configButton.visible = Qt.binding(function() { return roomListItem.hovered || Kirigami.Settings.isMobile })
|
|
||||||
})
|
|
||||||
menu.open()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createRoomListContextMenu() {
|
||||||
|
const menu = roomListContextMenu.createObject(page, {room: currentRoom})
|
||||||
|
configButton.visible = true
|
||||||
|
configButton.down = true
|
||||||
|
menu.closed.connect(function() {
|
||||||
|
configButton.down = undefined
|
||||||
|
configButton.visible = Qt.binding(function() { return roomListItem.hovered || Kirigami.Settings.isMobile })
|
||||||
|
})
|
||||||
|
menu.open()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import QtQuick.Layouts 1.15
|
|||||||
import Qt.labs.platform 1.1 as Platform
|
import Qt.labs.platform 1.1 as Platform
|
||||||
import Qt.labs.qmlmodels 1.0
|
import Qt.labs.qmlmodels 1.0
|
||||||
|
|
||||||
import org.kde.kirigami 2.15 as Kirigami
|
import org.kde.kirigami 2.19 as Kirigami
|
||||||
import org.kde.kitemmodels 1.0
|
import org.kde.kitemmodels 1.0
|
||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
@@ -23,6 +23,7 @@ Kirigami.ScrollablePage {
|
|||||||
|
|
||||||
/// It's not readonly because of the seperate window view.
|
/// It's not readonly because of the seperate window view.
|
||||||
property var currentRoom: RoomManager.currentRoom
|
property var currentRoom: RoomManager.currentRoom
|
||||||
|
property bool loading: page.currentRoom === null || (messageListView.count === 0 && !page.currentRoom.allHistoryLoaded && !page.currentRoom.isInvite)
|
||||||
/// Used to determine if scrolling to the bottom should mark the message as unread
|
/// Used to determine if scrolling to the bottom should mark the message as unread
|
||||||
property bool hasScrolledUpBefore: false;
|
property bool hasScrolledUpBefore: false;
|
||||||
|
|
||||||
@@ -52,7 +53,26 @@ Kirigami.ScrollablePage {
|
|||||||
|
|
||||||
onCurrentRoomChanged: {
|
onCurrentRoomChanged: {
|
||||||
hasScrolledUpBefore = false;
|
hasScrolledUpBefore = false;
|
||||||
ChatBoxHelper.clearEditReply()
|
chatBoxHelper.clearEditReply()
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: messageEventModel
|
||||||
|
function onRowsInserted() {
|
||||||
|
markReadIfVisibleTimer.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: markReadIfVisibleTimer
|
||||||
|
interval: 1000
|
||||||
|
onTriggered: {
|
||||||
|
if (loading || !currentRoom.readMarkerLoaded || !applicationWindow().active) {
|
||||||
|
restart()
|
||||||
|
} else {
|
||||||
|
markReadIfVisible()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionsHandler {
|
ActionsHandler {
|
||||||
@@ -61,6 +81,10 @@ Kirigami.ScrollablePage {
|
|||||||
connection: Controller.activeConnection
|
connection: Controller.activeConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChatBoxHelper {
|
||||||
|
id: chatBoxHelper
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
onActivated: applicationWindow().pageStack.get(0).forceActiveFocus()
|
onActivated: applicationWindow().pageStack.get(0).forceActiveFocus()
|
||||||
@@ -81,17 +105,17 @@ Kirigami.ScrollablePage {
|
|||||||
function onShowMessage(messageType, message) {
|
function onShowMessage(messageType, message) {
|
||||||
page.header.contentItem.text = message;
|
page.header.contentItem.text = message;
|
||||||
page.header.contentItem.type = messageType === ActionsHandler.Error ? Kirigami.MessageType.Error : Kirigami.MessageType.Information;
|
page.header.contentItem.type = messageType === ActionsHandler.Error ? Kirigami.MessageType.Error : Kirigami.MessageType.Information;
|
||||||
page.header.contentItem.visible = true;
|
page.header.visible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
header: QQC2.Control {
|
header: QQC2.Control {
|
||||||
height: visible ? implicitHeight : 0
|
height: visible ? implicitHeight : 0
|
||||||
visible: contentItem.visible
|
visible: false
|
||||||
padding: Kirigami.Units.smallSpacing
|
padding: Kirigami.Units.smallSpacing
|
||||||
contentItem: Kirigami.InlineMessage {
|
contentItem: Kirigami.InlineMessage {
|
||||||
showCloseButton: true
|
showCloseButton: true
|
||||||
visible: false
|
visible: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,15 +144,10 @@ Kirigami.ScrollablePage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Kirigami.PlaceholderMessage {
|
Kirigami.LoadingPlaceholder {
|
||||||
id: loadingIndicator
|
id: loadingIndicator
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: page.currentRoom === null || (messageListView.count === 0 && !page.currentRoom.allHistoryLoaded && !page.currentRoom.isInvite)
|
visible: loading
|
||||||
text: i18n("Loading…")
|
|
||||||
QQC2.BusyIndicator {
|
|
||||||
running: loadingIndicator.visible
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
focus: true
|
focus: true
|
||||||
@@ -147,9 +166,9 @@ Kirigami.ScrollablePage {
|
|||||||
|
|
||||||
Keys.onPressed: {
|
Keys.onPressed: {
|
||||||
if (event.key === Qt.Key_PageDown && (event.modifiers & Qt.ControlModifier)) {
|
if (event.key === Qt.Key_PageDown && (event.modifiers & Qt.ControlModifier)) {
|
||||||
switchRoomUp();
|
|
||||||
} else if (event.key === Qt.Key_PageUp && (event.modifiers & Qt.ControlModifier)) {
|
|
||||||
switchRoomDown();
|
switchRoomDown();
|
||||||
|
} else if (event.key === Qt.Key_PageUp && (event.modifiers & Qt.ControlModifier)) {
|
||||||
|
switchRoomUp();
|
||||||
} else if (!(event.modifiers & Qt.ControlModifier) && event.key < Qt.Key_Escape) {
|
} else if (!(event.modifiers & Qt.ControlModifier) && event.key < Qt.Key_Escape) {
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
chatBox.addText(event.text);
|
chatBox.addText(event.text);
|
||||||
@@ -158,6 +177,15 @@ Kirigami.ScrollablePage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: currentRoom
|
||||||
|
function onPositiveMessage(message) {
|
||||||
|
page.header.contentItem.text = message;
|
||||||
|
page.header.contentItem.type = Kirigami.MessageType.Positive;
|
||||||
|
page.header.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// hover actions on a delegate, activated in TimelineContainer.qml
|
// hover actions on a delegate, activated in TimelineContainer.qml
|
||||||
Connections {
|
Connections {
|
||||||
target: page.flickable
|
target: page.flickable
|
||||||
@@ -167,80 +195,6 @@ Kirigami.ScrollablePage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
|
||||||
id: hoverActions
|
|
||||||
property var event: null
|
|
||||||
property bool showEdit: event && (event.author.id === Controller.activeConnection.localUserId && (event.eventType === "emote" || event.eventType === "message"))
|
|
||||||
property var bubble: null
|
|
||||||
property var hovered: bubble && bubble.hovered
|
|
||||||
property var visibleDelayed: (hovered || hoverHandler.hovered) && !Kirigami.Settings.isMobile
|
|
||||||
onVisibleDelayedChanged: if (visibleDelayed) {
|
|
||||||
visible = true;
|
|
||||||
} else {
|
|
||||||
// HACK: delay disapearing by 200ms, otherwise this can create some glitches
|
|
||||||
// See https://invent.kde.org/network/neochat/-/issues/333
|
|
||||||
hoverActionsTimer.restart();
|
|
||||||
}
|
|
||||||
Timer {
|
|
||||||
id: hoverActionsTimer
|
|
||||||
interval: 200
|
|
||||||
onTriggered: hoverActions.visible = hoverActions.visibleDelayed;
|
|
||||||
}
|
|
||||||
x: bubble ? (bubble.x + Kirigami.Units.largeSpacing + Math.max(bubble.width - childWidth, 0) - (Config.compactLayout ? Kirigami.Units.gridUnit * 3 : 0)) : 0
|
|
||||||
y: bubble ? bubble.mapToItem(parent, 0, 0).y - hoverActions.childHeight + Kirigami.Units.smallSpacing: 0;
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
property var updateFunction
|
|
||||||
|
|
||||||
property alias childWidth: hoverActionsRow.width
|
|
||||||
property alias childHeight: hoverActionsRow.height
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: hoverActionsRow
|
|
||||||
z: 4
|
|
||||||
spacing: 0
|
|
||||||
HoverHandler {
|
|
||||||
id: hoverHandler
|
|
||||||
margin: Kirigami.Units.smallSpacing
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.Button {
|
|
||||||
QQC2.ToolTip.text: i18n("React")
|
|
||||||
QQC2.ToolTip.visible: hovered
|
|
||||||
icon.name: "preferences-desktop-emoticons"
|
|
||||||
onClicked: emojiDialog.open();
|
|
||||||
EmojiDialog {
|
|
||||||
id: emojiDialog
|
|
||||||
onReact: {
|
|
||||||
page.currentRoom.toggleReaction(hoverActions.event.eventId, emoji);
|
|
||||||
chatBox.focusInputField();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QQC2.Button {
|
|
||||||
QQC2.ToolTip.text: i18n("Edit")
|
|
||||||
QQC2.ToolTip.visible: hovered
|
|
||||||
visible: hoverActions.showEdit
|
|
||||||
icon.name: "document-edit"
|
|
||||||
onClicked: {
|
|
||||||
if (hoverActions.showEdit) {
|
|
||||||
ChatBoxHelper.edit(hoverActions.event.message, hoverActions.event.formattedBody, hoverActions.event.eventId)
|
|
||||||
}
|
|
||||||
chatBox.focusInputField();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QQC2.Button {
|
|
||||||
QQC2.ToolTip.text: i18n("Reply")
|
|
||||||
QQC2.ToolTip.visible: hovered
|
|
||||||
icon.name: "mail-replied-symbolic"
|
|
||||||
onClicked: {
|
|
||||||
ChatBoxHelper.replyToMessage(hoverActions.event.eventId, hoverActions.event.message, hoverActions.event.author);
|
|
||||||
chatBox.focusInputField();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CollapseStateProxyModel {
|
CollapseStateProxyModel {
|
||||||
id: collapseStateProxyModel
|
id: collapseStateProxyModel
|
||||||
sourceModel: sortedMessageEventModel
|
sourceModel: sortedMessageEventModel
|
||||||
@@ -316,7 +270,7 @@ Kirigami.ScrollablePage {
|
|||||||
fileDialog.chosen.connect(function(path) {
|
fileDialog.chosen.connect(function(path) {
|
||||||
if (!path) return
|
if (!path) return
|
||||||
|
|
||||||
ChatBoxHelper.attachmentPath = path;
|
chatBoxHelper.attachmentPath = path;
|
||||||
})
|
})
|
||||||
|
|
||||||
fileDialog.open()
|
fileDialog.open()
|
||||||
@@ -338,7 +292,7 @@ Kirigami.ScrollablePage {
|
|||||||
if (!Clipboard.saveImage(localPath)) {
|
if (!Clipboard.saveImage(localPath)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ChatBoxHelper.attachmentPath = localPath;
|
chatBoxHelper.attachmentPath = localPath;
|
||||||
attachDialog.close();
|
attachDialog.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -413,7 +367,7 @@ Kirigami.ScrollablePage {
|
|||||||
DropArea {
|
DropArea {
|
||||||
id: dropAreaFile
|
id: dropAreaFile
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onDropped: ChatBoxHelper.attachmentPath = drop.urls[0]
|
onDropped: chatBoxHelper.attachmentPath = drop.urls[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.Pane {
|
QQC2.Pane {
|
||||||
@@ -462,7 +416,7 @@ Kirigami.ScrollablePage {
|
|||||||
currentRoom.usersTyping.length,
|
currentRoom.usersTyping.length,
|
||||||
currentRoom.usersTyping.map(user => user.displayName).join(", ")
|
currentRoom.usersTyping.map(user => user.displayName).join(", ")
|
||||||
) : ""
|
) : ""
|
||||||
anchors.right: parent.right
|
anchors.left: parent.left
|
||||||
height: visible ? implicitHeight : 0
|
height: visible ? implicitHeight : 0
|
||||||
Behavior on height {
|
Behavior on height {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
@@ -478,6 +432,100 @@ Kirigami.ScrollablePage {
|
|||||||
function goToEvent(eventID) {
|
function goToEvent(eventID) {
|
||||||
messageListView.positionViewAtIndex(eventToIndex(eventID), ListView.Contain)
|
messageListView.positionViewAtIndex(eventToIndex(eventID), ListView.Contain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: hoverActions
|
||||||
|
property var event: null
|
||||||
|
property bool userMsg: event && event.author.id === Controller.activeConnection.localUserId
|
||||||
|
property bool showEdit: event && (userMsg && (event.eventType === "emote" || event.eventType === "message"))
|
||||||
|
property var delegate: null
|
||||||
|
property var bubble: null
|
||||||
|
property var hovered: bubble && bubble.hovered
|
||||||
|
property var visibleDelayed: (hovered || hoverHandler.hovered) && !Kirigami.Settings.isMobile
|
||||||
|
onVisibleDelayedChanged: if (visibleDelayed) {
|
||||||
|
visible = true;
|
||||||
|
} else {
|
||||||
|
// HACK: delay disapearing by 200ms, otherwise this can create some glitches
|
||||||
|
// See https://invent.kde.org/network/neochat/-/issues/333
|
||||||
|
hoverActionsTimer.restart();
|
||||||
|
}
|
||||||
|
Timer {
|
||||||
|
id: hoverActionsTimer
|
||||||
|
interval: 200
|
||||||
|
onTriggered: hoverActions.visible = hoverActions.visibleDelayed;
|
||||||
|
}
|
||||||
|
|
||||||
|
property int childOffset: userMsg && Config.showLocalMessagesOnRight && !Config.compactLayout ? (bubble ? bubble.width : 0) - childWidth : Math.max((bubble ? bubble.width : 0) - childWidth, 0)
|
||||||
|
x: delegate && bubble ? (delegate.x + bubble.x + Kirigami.Units.largeSpacing + childOffset - (Config.compactLayout ? Kirigami.Units.gridUnit * 3 : 0)) : 0
|
||||||
|
y: bubble ? bubble.mapToItem(parent, 0, 0).y - hoverActions.childHeight + Kirigami.Units.smallSpacing: 0;
|
||||||
|
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
property var updateFunction
|
||||||
|
|
||||||
|
property alias childWidth: hoverActionsRow.width
|
||||||
|
property alias childHeight: hoverActionsRow.height
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: hoverActionsRow
|
||||||
|
z: 4
|
||||||
|
spacing: 0
|
||||||
|
HoverHandler {
|
||||||
|
id: hoverHandler
|
||||||
|
margin: Kirigami.Units.smallSpacing
|
||||||
|
}
|
||||||
|
Kirigami.Icon {
|
||||||
|
source: "security-high"
|
||||||
|
width: height
|
||||||
|
height: parent.height
|
||||||
|
visible: hoverActions.event.verified
|
||||||
|
HoverHandler {
|
||||||
|
id: hover
|
||||||
|
}
|
||||||
|
QQC2.ToolTip.text: i18n("This message was sent from a verified device")
|
||||||
|
QQC2.ToolTip.visible: hover.hovered
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.Button {
|
||||||
|
QQC2.ToolTip.text: i18n("React")
|
||||||
|
QQC2.ToolTip.visible: hovered
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
icon.name: "preferences-desktop-emoticons"
|
||||||
|
onClicked: emojiDialog.open();
|
||||||
|
EmojiDialog {
|
||||||
|
id: emojiDialog
|
||||||
|
onReact: {
|
||||||
|
page.currentRoom.toggleReaction(hoverActions.event.eventId, emoji);
|
||||||
|
chatBox.focusInputField();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QQC2.Button {
|
||||||
|
QQC2.ToolTip.text: i18n("Edit")
|
||||||
|
QQC2.ToolTip.visible: hovered
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
visible: hoverActions.showEdit
|
||||||
|
icon.name: "document-edit"
|
||||||
|
onClicked: {
|
||||||
|
if (hoverActions.showEdit) {
|
||||||
|
chatBoxHelper.edit(hoverActions.event.message, hoverActions.event.formattedBody, hoverActions.event.eventId)
|
||||||
|
}
|
||||||
|
chatBox.focusInputField();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QQC2.Button {
|
||||||
|
QQC2.ToolTip.text: i18n("Reply")
|
||||||
|
QQC2.ToolTip.visible: hovered
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
icon.name: "mail-replied-symbolic"
|
||||||
|
onClicked: {
|
||||||
|
chatBoxHelper.replyToMessage(hoverActions.event.eventId, hoverActions.event.message, hoverActions.event.author);
|
||||||
|
chatBox.focusInputField();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -492,14 +540,14 @@ Kirigami.ScrollablePage {
|
|||||||
onEditLastUserMessage: {
|
onEditLastUserMessage: {
|
||||||
const targetMessage = messageEventModel.getLastLocalUserMessageEventId();
|
const targetMessage = messageEventModel.getLastLocalUserMessageEventId();
|
||||||
if (targetMessage) {
|
if (targetMessage) {
|
||||||
ChatBoxHelper.edit(targetMessage["body"], targetMessage["body"], targetMessage["event_id"]);
|
chatBoxHelper.edit(targetMessage["message"], targetMessage["formattedBody"], targetMessage["event_id"]);
|
||||||
chatBox.focusInputField();
|
chatBox.focusInputField();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onReplyPreviousUserMessage: {
|
onReplyPreviousUserMessage: {
|
||||||
const replyResponse = messageEventModel.getLatestMessageFromIndex(0);
|
const replyResponse = messageEventModel.getLatestMessageFromIndex(0);
|
||||||
if (replyResponse && replyResponse["event_id"]) {
|
if (replyResponse && replyResponse["event_id"]) {
|
||||||
ChatBoxHelper.replyToMessage(replyResponse["event_id"], replyResponse["event"], replyResponse["sender_id"]);
|
chatBoxHelper.replyToMessage(replyResponse["event_id"], replyResponse["message"], replyResponse["sender_id"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -543,7 +591,7 @@ Kirigami.ScrollablePage {
|
|||||||
function warning(title, message) {
|
function warning(title, message) {
|
||||||
page.header.contentItem.text = `${title}<br />${message}`;
|
page.header.contentItem.text = `${title}<br />${message}`;
|
||||||
page.header.contentItem.type = Kirigami.MessageType.Warning;
|
page.header.contentItem.type = Kirigami.MessageType.Warning;
|
||||||
page.header.contentItem.visible = true;
|
page.header.visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function showUserDetail(user) {
|
function showUserDetail(user) {
|
||||||
@@ -588,6 +636,14 @@ Kirigami.ScrollablePage {
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark all messages as read if all unread messages are visible to the user
|
||||||
|
function markReadIfVisible() {
|
||||||
|
let readMarkerRow = eventToIndex(currentRoom.readMarkerEventId)
|
||||||
|
if (readMarkerRow > 0 && readMarkerRow < firstVisibleIndex()) {
|
||||||
|
currentRoom.markAllMessagesAsRead()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Open message context dialog for file and videos
|
/// Open message context dialog for file and videos
|
||||||
function openFileContext(event, file) {
|
function openFileContext(event, file) {
|
||||||
const contextMenu = fileDelegateContextMenu.createObject(page, {
|
const contextMenu = fileDelegateContextMenu.createObject(page, {
|
||||||
@@ -598,12 +654,13 @@ Kirigami.ScrollablePage {
|
|||||||
file: file,
|
file: file,
|
||||||
mimeType: event.mimeType,
|
mimeType: event.mimeType,
|
||||||
progressInfo: event.progressInfo,
|
progressInfo: event.progressInfo,
|
||||||
|
plainMessage: event.message,
|
||||||
});
|
});
|
||||||
contextMenu.open();
|
contextMenu.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open context menu for normal message
|
/// Open context menu for normal message
|
||||||
function openMessageContext(event, selectedText) {
|
function openMessageContext(event, selectedText, plainMessage) {
|
||||||
const contextMenu = messageDelegateContextMenu.createObject(page, {
|
const contextMenu = messageDelegateContextMenu.createObject(page, {
|
||||||
selectedText: selectedText,
|
selectedText: selectedText,
|
||||||
author: event.author,
|
author: event.author,
|
||||||
@@ -611,7 +668,8 @@ Kirigami.ScrollablePage {
|
|||||||
eventId: event.eventId,
|
eventId: event.eventId,
|
||||||
formattedBody: event.formattedBody,
|
formattedBody: event.formattedBody,
|
||||||
source: event.source,
|
source: event.source,
|
||||||
eventType: event.eventType
|
eventType: event.eventType,
|
||||||
|
plainMessage: plainMessage,
|
||||||
});
|
});
|
||||||
contextMenu.open();
|
contextMenu.open();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ Kirigami.OverlayDrawer {
|
|||||||
id: roomDrawer
|
id: roomDrawer
|
||||||
readonly property var room: RoomManager.currentRoom
|
readonly property var room: RoomManager.currentRoom
|
||||||
|
|
||||||
width: modal ? undefined : actualWidth
|
width: actualWidth
|
||||||
|
|
||||||
readonly property int minWidth: Kirigami.Units.gridUnit * 15
|
readonly property int minWidth: Kirigami.Units.gridUnit * 15
|
||||||
readonly property int maxWidth: Kirigami.Units.gridUnit * 25
|
readonly property int maxWidth: Kirigami.Units.gridUnit * 25
|
||||||
readonly property int defaultWidth: Kirigami.Units.gridUnit * 20
|
readonly property int defaultWidth: Kirigami.Units.gridUnit * 20
|
||||||
@@ -62,56 +63,84 @@ Kirigami.OverlayDrawer {
|
|||||||
|
|
||||||
edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge
|
edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge
|
||||||
|
|
||||||
|
// If modal has been changed and the drawer is closed automatically then dim on popup open will have been switched off in main.qml so switch it back on after the animation completes.
|
||||||
|
// This is to avoid dim being active for a split second when the drawer is switched to modal which looks terrible.
|
||||||
|
onAnimatingChanged: if (dim === false) dim = undefined
|
||||||
|
|
||||||
topPadding: 0
|
topPadding: 0
|
||||||
leftPadding: 0
|
leftPadding: 0
|
||||||
rightPadding: 0
|
rightPadding: 0
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||||
contentItem: Loader {
|
contentItem: Loader {
|
||||||
|
id: loader
|
||||||
active: roomDrawer.drawerOpen
|
active: roomDrawer.drawerOpen
|
||||||
sourceComponent: ColumnLayout {
|
sourceComponent: ColumnLayout {
|
||||||
id: columnLayout
|
id: columnLayout
|
||||||
|
property alias userSearchText: userListSearchField.text
|
||||||
|
property alias highlightedUser: userListView.currentIndex
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Kirigami.AbstractApplicationHeader {
|
Kirigami.AbstractApplicationHeader {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
topPadding: Kirigami.Units.smallSpacing / 2;
|
topPadding: Kirigami.Units.smallSpacing / 2;
|
||||||
bottomPadding: Kirigami.Units.smallSpacing / 2;
|
bottomPadding: Kirigami.Units.smallSpacing / 2;
|
||||||
rightPadding: Kirigami.Units.smallSpacing
|
rightPadding: Kirigami.Units.largeSpacing
|
||||||
leftPadding: Kirigami.Units.smallSpacing
|
leftPadding: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
|
Kirigami.Heading {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: i18n("Room information")
|
||||||
|
level: 1
|
||||||
|
}
|
||||||
ToolButton {
|
ToolButton {
|
||||||
|
id: inviteButton
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
icon.name: "list-add-user"
|
icon.name: "list-add-user"
|
||||||
text: i18n("Invite")
|
text: i18n("Invite user to room")
|
||||||
|
display: AbstractButton.IconOnly
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
applicationWindow().pageStack.layers.push("qrc:/imports/NeoChat/Page/InviteUserPage.qml", {room: room})
|
applicationWindow().pageStack.layers.push("qrc:/imports/NeoChat/Page/InviteUserPage.qml", {room: room})
|
||||||
roomDrawer.close();
|
roomDrawer.close();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Item {
|
|
||||||
// HACK otherwise rating item is not right aligned
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
ToolTip {
|
||||||
|
text: inviteButton.text
|
||||||
|
}
|
||||||
|
}
|
||||||
ToolButton {
|
ToolButton {
|
||||||
|
id: favouriteButton
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
icon.name: room && room.isFavourite ? "rating" : "rating-unrated"
|
icon.name: room && room.isFavourite ? "rating" : "rating-unrated"
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: room && room.isFavourite
|
checked: room && room.isFavourite
|
||||||
|
text: room && room.isFavourite ? i18n("Remove room from favorites") : i18n("Make room favorite")
|
||||||
|
display: AbstractButton.IconOnly
|
||||||
|
|
||||||
onClicked: room.isFavourite ? room.removeTag("m.favourite") : room.addTag("m.favourite", 1.0)
|
onClicked: room.isFavourite ? room.removeTag("m.favourite") : room.addTag("m.favourite", 1.0)
|
||||||
|
|
||||||
ToolTip {
|
ToolTip {
|
||||||
text: room && room.isFavourite ? i18n("Remove room from favorites") : i18n("Make room favorite")
|
text: favouriteButton.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToolButton {
|
ToolButton {
|
||||||
|
id: settingsButton
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
icon.name: 'settings-configure'
|
icon.name: 'settings-configure'
|
||||||
|
text: i18n("Room settings")
|
||||||
|
display: AbstractButton.IconOnly
|
||||||
|
|
||||||
onClicked: ApplicationWindow.window.pageStack.pushDialogLayer('qrc:/imports/NeoChat/RoomSettings/Categories.qml', {room: room})
|
onClicked: ApplicationWindow.window.pageStack.pushDialogLayer('qrc:/imports/NeoChat/RoomSettings/Categories.qml', {room: room})
|
||||||
|
|
||||||
ToolTip {
|
ToolTip {
|
||||||
text: i18n("Room settings")
|
text: settingsButton.text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,14 +149,11 @@ Kirigami.OverlayDrawer {
|
|||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.margins: Kirigami.Units.largeSpacing
|
Layout.margins: Kirigami.Units.largeSpacing
|
||||||
Kirigami.Heading {
|
spacing: Kirigami.Units.largeSpacing
|
||||||
text: i18n("Room information")
|
|
||||||
level: 3
|
|
||||||
}
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.margins: Kirigami.Units.largeSpacing
|
Layout.leftMargin: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
spacing: Kirigami.Units.largeSpacing
|
spacing: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
Kirigami.Avatar {
|
Kirigami.Avatar {
|
||||||
@@ -144,10 +170,9 @@ Kirigami.OverlayDrawer {
|
|||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Kirigami.Heading {
|
Kirigami.Heading {
|
||||||
Layout.maximumWidth: Kirigami.Units.gridUnit * 9
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
level: 1
|
level: 1
|
||||||
font.bold: true
|
type: Kirigami.Heading.Type.Primary
|
||||||
wrapMode: Label.Wrap
|
wrapMode: Label.Wrap
|
||||||
text: room ? room.displayName : i18n("No name")
|
text: room ? room.displayName : i18n("No name")
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
@@ -158,6 +183,8 @@ Kirigami.OverlayDrawer {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
color: Kirigami.Theme.textColor
|
color: Kirigami.Theme.textColor
|
||||||
|
selectedTextColor: Kirigami.Theme.highlightedTextColor
|
||||||
|
selectionColor: Kirigami.Theme.highlightColor
|
||||||
readOnly: true
|
readOnly: true
|
||||||
text: room && room.canonicalAlias ? room.canonicalAlias : i18n("No Canonical Alias")
|
text: room && room.canonicalAlias ? room.canonicalAlias : i18n("No Canonical Alias")
|
||||||
}
|
}
|
||||||
@@ -172,7 +199,9 @@ Kirigami.OverlayDrawer {
|
|||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
color: Kirigami.Theme.textColor
|
color: Kirigami.Theme.textColor
|
||||||
onLinkActivated: Qt.openUrlExternally(link)
|
selectedTextColor: Kirigami.Theme.highlightedTextColor
|
||||||
|
selectionColor: Kirigami.Theme.highlightColor
|
||||||
|
onLinkActivated: UrlHelper.openUrl(link)
|
||||||
readOnly: true
|
readOnly: true
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -185,16 +214,23 @@ Kirigami.OverlayDrawer {
|
|||||||
Kirigami.ListSectionHeader {
|
Kirigami.ListSectionHeader {
|
||||||
label: i18n("Members")
|
label: i18n("Members")
|
||||||
activeFocusOnTab: false
|
activeFocusOnTab: false
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
text: room ? i18np("%1 Member", "%1 Members", room.joinedCount) : i18n("No Member Count")
|
text: room ? i18np("%1 Member", "%1 Members", room.joinedCount) : i18n("No Member Count")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pane {
|
Control {
|
||||||
padding: Kirigami.Units.smallSpacing
|
Layout.fillWidth: true
|
||||||
implicitWidth: parent.width
|
|
||||||
z: 2
|
// Note need to set padding individually to guarantee it will always work
|
||||||
|
// see note - https://doc.qt.io/qt-6/qml-qtquick-controls2-control.html#padding-prop
|
||||||
|
topPadding: Kirigami.Units.smallSpacing
|
||||||
|
bottomPadding: Kirigami.Units.smallSpacing
|
||||||
|
rightPadding: Kirigami.Units.largeSpacing
|
||||||
|
leftPadding: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Kirigami.Theme.backgroundColor
|
color: Kirigami.Theme.backgroundColor
|
||||||
Kirigami.Theme.inherit: false
|
Kirigami.Theme.inherit: false
|
||||||
@@ -202,6 +238,7 @@ Kirigami.OverlayDrawer {
|
|||||||
}
|
}
|
||||||
contentItem: Kirigami.SearchField {
|
contentItem: Kirigami.SearchField {
|
||||||
id: userListSearchField
|
id: userListSearchField
|
||||||
|
|
||||||
onAccepted: sortedMessageEventModel.filterString = text;
|
onAccepted: sortedMessageEventModel.filterString = text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,11 +247,12 @@ Kirigami.OverlayDrawer {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: userListView
|
id: userListView
|
||||||
clip: true
|
clip: true
|
||||||
headerPositioning: ListView.OverlayHeader
|
|
||||||
boundsBehavior: Flickable.DragOverBounds
|
|
||||||
activeFocusOnTab: true
|
activeFocusOnTab: true
|
||||||
|
|
||||||
model: KSortFilterProxyModel {
|
model: KSortFilterProxyModel {
|
||||||
@@ -229,58 +267,50 @@ Kirigami.OverlayDrawer {
|
|||||||
filterCaseSensitivity: Qt.CaseInsensitive
|
filterCaseSensitivity: Qt.CaseInsensitive
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Kirigami.AbstractListItem {
|
delegate: Kirigami.BasicListItem {
|
||||||
width: userListView.width
|
id: userListItem
|
||||||
|
|
||||||
implicitHeight: Kirigami.Units.gridUnit * 2
|
implicitHeight: Kirigami.Units.gridUnit * 2
|
||||||
z: 1
|
leftPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
contentItem: RowLayout {
|
label: name
|
||||||
Kirigami.Avatar {
|
|
||||||
Layout.preferredWidth: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
|
||||||
Layout.preferredHeight: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
|
||||||
visible: Config.showAvatarInRoomDrawer
|
|
||||||
sourceSize.height: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
|
||||||
sourceSize.width: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
|
||||||
source: avatar ? ("image://mxc/" + avatar) : ""
|
|
||||||
name: name
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
onClicked: {
|
||||||
Layout.fillWidth: true
|
const popup = userDetailDialog.createObject(ApplicationWindow.overlay, {room: room, user: user, displayName: name, avatarMediaId: avatar})
|
||||||
|
popup.closed.connect(function() {
|
||||||
text: name
|
userListItem.highlighted = false
|
||||||
textFormat: Text.PlainText
|
})
|
||||||
elide: Text.ElideRight
|
popup.open()
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
}
|
|
||||||
|
|
||||||
Label {
|
|
||||||
visible: perm != UserType.Member
|
|
||||||
|
|
||||||
text: {
|
|
||||||
if (perm == UserType.Owner) {
|
|
||||||
return i18n("Owner")
|
|
||||||
}
|
|
||||||
if (perm == UserType.Admin) {
|
|
||||||
return i18n("Admin")
|
|
||||||
}
|
|
||||||
if (perm == UserType.Moderator) {
|
|
||||||
return i18n("Mod")
|
|
||||||
}
|
|
||||||
if (perm == UserType.Muted) {
|
|
||||||
return i18n("Muted")
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
color: Kirigami.Theme.disabledTextColor
|
|
||||||
font.pixelSize: 12
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
action: Kirigami.Action {
|
leading: Kirigami.Avatar {
|
||||||
onTriggered: userDetailDialog.createObject(ApplicationWindow.overlay, {"room": room, "user": user, "displayName": name, "avatarMediaId": avatar}).open()
|
implicitWidth: height
|
||||||
|
sourceSize.height: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||||
|
sourceSize.width: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||||
|
source: avatar ? ("image://mxc/" + avatar) : ""
|
||||||
|
name: name
|
||||||
|
}
|
||||||
|
|
||||||
|
trailing: Label {
|
||||||
|
visible: perm != UserType.Member
|
||||||
|
|
||||||
|
text: {
|
||||||
|
switch (perm) {
|
||||||
|
case UserType.Owner:
|
||||||
|
return i18n("Owner");
|
||||||
|
case UserType.Admin:
|
||||||
|
return i18n("Admin");
|
||||||
|
case UserType.Moderator:
|
||||||
|
return i18n("Mod");
|
||||||
|
case UserType.Muted:
|
||||||
|
return i18n("Muted");
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
color: Kirigami.Theme.disabledTextColor
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,6 +319,10 @@ Kirigami.OverlayDrawer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onRoomChanged: {
|
onRoomChanged: {
|
||||||
|
if (loader.active) {
|
||||||
|
loader.item.userSearchText = ""
|
||||||
|
loader.item.highlightedUser = -1
|
||||||
|
}
|
||||||
if (room == null) {
|
if (room == null) {
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import org.kde.kirigami 2.18 as Kirigami
|
import org.kde.kirigami 2.18 as Kirigami
|
||||||
import QtQuick.Controls 2.15 as Controls
|
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
Kirigami.CategorizedSettings {
|
Kirigami.CategorizedSettings {
|
||||||
id: root
|
id: root
|
||||||
property var room
|
property var room
|
||||||
|
|
||||||
objectName: "settingsPage"
|
objectName: "settingsPage"
|
||||||
actions: [
|
actions: [
|
||||||
Kirigami.SettingAction {
|
Kirigami.SettingAction {
|
||||||
@@ -30,6 +30,16 @@ Kirigami.CategorizedSettings {
|
|||||||
room: root.room
|
room: root.room
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Kirigami.SettingAction {
|
||||||
|
text: i18n("Notifications")
|
||||||
|
icon.name: "notifications"
|
||||||
|
page: Qt.resolvedUrl("PushNotification.qml")
|
||||||
|
initialProperties: {
|
||||||
|
return {
|
||||||
|
room: root.room
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ Kirigami.ScrollablePage {
|
|||||||
readonly property bool canChangeTopic: room.canSendState("m.room.topic")
|
readonly property bool canChangeTopic: room.canSendState("m.room.topic")
|
||||||
readonly property bool canChangeCanonicalAlias: room.canSendState("m.room.canonical_alias")
|
readonly property bool canChangeCanonicalAlias: room.canSendState("m.room.canonical_alias")
|
||||||
|
|
||||||
title: i18n('General')
|
title: i18n("General")
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Kirigami.FormLayout {
|
Kirigami.FormLayout {
|
||||||
|
|||||||
57
imports/NeoChat/RoomSettings/PushNotification.qml
Normal file
57
imports/NeoChat/RoomSettings/PushNotification.qml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
Kirigami.ScrollablePage {
|
||||||
|
|
||||||
|
property var room
|
||||||
|
|
||||||
|
title: i18nc('@title:window', 'Notifications')
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Kirigami.FormLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
QQC2.RadioButton {
|
||||||
|
text: i18n("Follow global setting")
|
||||||
|
Kirigami.FormData.label: i18n("Room notifications setting:")
|
||||||
|
checked: room.pushNotificationState === PushNotificationState.Default
|
||||||
|
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||||
|
onToggled: {
|
||||||
|
room.pushNotificationState = PushNotificationState.Default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QQC2.RadioButton {
|
||||||
|
text: i18nc("As in 'notify for all messages'","All")
|
||||||
|
checked: room.pushNotificationState === PushNotificationState.All
|
||||||
|
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||||
|
onToggled: {
|
||||||
|
room.pushNotificationState = PushNotificationState.All
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QQC2.RadioButton {
|
||||||
|
text: i18nc("As in 'notify when the user is mentioned or the message contains a set keyword'","@Mentions and Keywords")
|
||||||
|
checked: room.pushNotificationState === PushNotificationState.MentionKeyword
|
||||||
|
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||||
|
onToggled: {
|
||||||
|
room.pushNotificationState = PushNotificationState.MentionKeyword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QQC2.RadioButton {
|
||||||
|
text: i18nc("As in 'do not notify for any messages'","Off")
|
||||||
|
checked: room.pushNotificationState === PushNotificationState.Mute
|
||||||
|
enabled: room.pushNotificationState != PushNotificationState.Unknown
|
||||||
|
onToggled: {
|
||||||
|
room.pushNotificationState = PushNotificationState.Mute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,13 +16,13 @@ Kirigami.ScrollablePage {
|
|||||||
|
|
||||||
property var room
|
property var room
|
||||||
|
|
||||||
title: i18n('Security')
|
title: i18n("Security")
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Kirigami.FormLayout {
|
Kirigami.FormLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
CheckBox {
|
RadioButton {
|
||||||
text: i18nc("@option:check", "Private (invite only)")
|
text: i18nc("@option:check", "Private (invite only)")
|
||||||
Kirigami.FormData.label: i18nc("@option:check", "Access:")
|
Kirigami.FormData.label: i18nc("@option:check", "Access:")
|
||||||
checked: room.joinRule === "invite"
|
checked: room.joinRule === "invite"
|
||||||
@@ -32,7 +32,7 @@ Kirigami.ScrollablePage {
|
|||||||
text: i18n("Only invited people can join.")
|
text: i18n("Only invited people can join.")
|
||||||
font: Kirigami.Theme.smallFont
|
font: Kirigami.Theme.smallFont
|
||||||
}
|
}
|
||||||
CheckBox {
|
RadioButton {
|
||||||
text: i18nc("@option:check", "Space members")
|
text: i18nc("@option:check", "Space members")
|
||||||
checked: room.joinRule === "restricted"
|
checked: room.joinRule === "restricted"
|
||||||
enabled: false
|
enabled: false
|
||||||
@@ -41,7 +41,7 @@ Kirigami.ScrollablePage {
|
|||||||
text: i18n("Anyone in a space can find and join.")
|
text: i18n("Anyone in a space can find and join.")
|
||||||
font: Kirigami.Theme.smallFont
|
font: Kirigami.Theme.smallFont
|
||||||
}
|
}
|
||||||
CheckBox {
|
RadioButton {
|
||||||
text: i18nc("@option:check", "Public")
|
text: i18nc("@option:check", "Public")
|
||||||
checked: room.joinRule === "public"
|
checked: room.joinRule === "public"
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ import org.kde.kirigami 2.15 as Kirigami
|
|||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
Kirigami.AboutPage {
|
Kirigami.AboutPage {
|
||||||
title: i18nc('@title:window', 'About NeoChat')
|
title: i18nc("@title:window", "About NeoChat")
|
||||||
aboutData: Controller.aboutData
|
aboutData: Controller.aboutData
|
||||||
}
|
}
|
||||||
|
|||||||
130
imports/NeoChat/Settings/AccountEditorPage.qml
Normal file
130
imports/NeoChat/Settings/AccountEditorPage.qml
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
|
||||||
|
// SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as Controls
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import Qt.labs.platform 1.1
|
||||||
|
|
||||||
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
import NeoChat.Dialog 1.0
|
||||||
|
|
||||||
|
Kirigami.ScrollablePage {
|
||||||
|
id: root
|
||||||
|
title: i18n("Edit Account")
|
||||||
|
property var connection
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Kirigami.FormLayout {
|
||||||
|
RowLayout {
|
||||||
|
Kirigami.Avatar {
|
||||||
|
id: avatar
|
||||||
|
source: root.connection && root.connection.localUser.avatarMediaId ? ("image://mxc/" + root.connection.localUser.avatarMediaId) : ""
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
property var fileDialog: null;
|
||||||
|
onClicked: {
|
||||||
|
if (fileDialog != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileDialog = openFileDialog.createObject(Controls.ApplicationWindow.Overlay)
|
||||||
|
|
||||||
|
fileDialog.chosen.connect(function(receivedSource) {
|
||||||
|
mouseArea.fileDialog = null;
|
||||||
|
if (!receivedSource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parent.source = receivedSource;
|
||||||
|
});
|
||||||
|
fileDialog.onRejected.connect(function() {
|
||||||
|
mouseArea.fileDialog = null;
|
||||||
|
});
|
||||||
|
fileDialog.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Controls.Button {
|
||||||
|
visible: avatar.source.toString().length !== 0
|
||||||
|
icon.name: "edit-clear"
|
||||||
|
|
||||||
|
onClicked: avatar.source = ""
|
||||||
|
}
|
||||||
|
Kirigami.FormData.label: i18n("Avatar:")
|
||||||
|
}
|
||||||
|
Controls.TextField {
|
||||||
|
id: name
|
||||||
|
text: root.connection ? root.connection.localUser.displayName : ""
|
||||||
|
Kirigami.FormData.label: i18n("Name:")
|
||||||
|
}
|
||||||
|
Controls.TextField {
|
||||||
|
id: accountLabel
|
||||||
|
text: root.connection ? root.connection.localUser.accountLabel : ""
|
||||||
|
Kirigami.FormData.label: i18n("Label:")
|
||||||
|
}
|
||||||
|
Controls.TextField {
|
||||||
|
id: currentPassword
|
||||||
|
Kirigami.FormData.label: i18n("Current Password:")
|
||||||
|
enabled: roto.connection !== undefined && root.connection.canChangePassword !== false
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
}
|
||||||
|
Controls.TextField {
|
||||||
|
id: newPassword
|
||||||
|
Kirigami.FormData.label: i18n("New Password:")
|
||||||
|
enabled: root.connection !== undefined && root.connection.canChangePassword !== false
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
|
||||||
|
}
|
||||||
|
Controls.TextField {
|
||||||
|
id: confirmPassword
|
||||||
|
Kirigami.FormData.label: i18n("Confirm new Password:")
|
||||||
|
enabled: root.connection !== undefined && root.connection.canChangePassword !== false
|
||||||
|
echoMode: TextInput.Password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: RowLayout {
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Controls.Button {
|
||||||
|
text: i18n("Save")
|
||||||
|
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||||
|
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||||
|
onClicked: {
|
||||||
|
if (!Controller.setAvatar(root.connection, avatar.source)) {
|
||||||
|
showPassiveNotification("The Avatar could not be set");
|
||||||
|
}
|
||||||
|
if (root.connection.localUser.displayName !== name.text) {
|
||||||
|
root.connection.localUser.rename(name.text);
|
||||||
|
}
|
||||||
|
if (root.connection.localUser.accountLabel !== accountLabel.text) {
|
||||||
|
root.connection.localUser.setAccountLabel(accountLabel.text);
|
||||||
|
}
|
||||||
|
if(currentPassword.text !== "" && newPassword.text !== "" && confirmPassword.text !== "") {
|
||||||
|
if(newPassword.text === confirmPassword.text) {
|
||||||
|
Controller.changePassword(root.connection, currentPassword.text, newPassword.text);
|
||||||
|
} else {
|
||||||
|
showPassiveNotification(i18n("Passwords do not match"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.closeDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Controls.Button {
|
||||||
|
text: i18n("Cancel")
|
||||||
|
Layout.rightMargin: Kirigami.Units.smallSpacing
|
||||||
|
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||||
|
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||||
|
onClicked: root.closeDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,42 +23,51 @@ Kirigami.ScrollablePage {
|
|||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
model: AccountRegistry
|
model: AccountRegistry
|
||||||
delegate: Kirigami.SwipeListItem {
|
anchors.fill: parent
|
||||||
leftPadding: 0
|
delegate: Kirigami.BasicListItem {
|
||||||
rightPadding: 0
|
text: model.connection.localUser.displayName
|
||||||
Kirigami.BasicListItem {
|
labelItem.textFormat: Text.PlainText
|
||||||
anchors.top: parent.top
|
subtitle: model.connection.localUserId
|
||||||
anchors.bottom: parent.bottom
|
icon: model.connection.localUser.avatarMediaId ? ("image://mxc/" + model.connection.localUser.avatarMediaId) : "im-user"
|
||||||
|
|
||||||
text: model.connection.localUser.displayName
|
onClicked: {
|
||||||
labelItem.textFormat: Text.PlainText
|
Controller.activeConnection = model.connection;
|
||||||
subtitle: model.connection.localUserId
|
pageStack.layers.pop();
|
||||||
icon: model.connection.localUser.avatarMediaId ? ("image://mxc/" + model.connection.localUser.avatarMediaId) : "im-user"
|
}
|
||||||
|
|
||||||
onClicked: {
|
trailing: RowLayout {
|
||||||
Controller.activeConnection = model.connection
|
Controls.ToolButton {
|
||||||
pageStack.layers.pop()
|
display: Controls.AbstractButton.IconOnly
|
||||||
|
Controls.ToolTip {
|
||||||
|
text: parent.action.text
|
||||||
|
}
|
||||||
|
action: Kirigami.Action {
|
||||||
|
text: i18n("Edit this account")
|
||||||
|
iconName: "document-edit"
|
||||||
|
onTriggered: pageSettingStack.pushDialogLayer(Qt.resolvedUrl('./AccountEditorPage.qml'), {
|
||||||
|
connection: model.connection
|
||||||
|
}, {
|
||||||
|
title: i18n("Account editor")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Controls.ToolButton {
|
||||||
|
display: Controls.AbstractButton.IconOnly
|
||||||
|
Controls.ToolTip {
|
||||||
|
text: parent.action.text
|
||||||
|
}
|
||||||
|
action: Kirigami.Action {
|
||||||
|
text: i18n("Logout")
|
||||||
|
iconName: "im-kick-user"
|
||||||
|
onTriggered: {
|
||||||
|
Controller.logout(model.connection, true);
|
||||||
|
if (Controller.accountCount === 1) {
|
||||||
|
pageStack.layers.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actions: [
|
|
||||||
Kirigami.Action {
|
|
||||||
text: i18n("Edit this account")
|
|
||||||
iconName: "document-edit"
|
|
||||||
onTriggered: {
|
|
||||||
userEditSheet.connection = model.connection
|
|
||||||
userEditSheet.open()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Kirigami.Action {
|
|
||||||
text: i18n("Logout")
|
|
||||||
iconName: "im-kick-user"
|
|
||||||
onTriggered: {
|
|
||||||
Controller.logout(model.connection, true)
|
|
||||||
if(Controller.accountCount === 1)
|
|
||||||
pageStack.layers.pop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,6 +85,7 @@ Kirigami.ScrollablePage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Controller
|
target: Controller
|
||||||
function onConnectionAdded() {
|
function onConnectionAdded() {
|
||||||
@@ -83,134 +93,21 @@ Kirigami.ScrollablePage {
|
|||||||
pageStack.layers.pop()
|
pageStack.layers.pop()
|
||||||
}
|
}
|
||||||
function onPasswordStatus(status) {
|
function onPasswordStatus(status) {
|
||||||
if(status == Controller.Success)
|
if (status === Controller.Success) {
|
||||||
showPassiveNotification(i18n("Password changed successfully"))
|
showPassiveNotification(i18n("Password changed successfully"));
|
||||||
else if(status == Controller.Wrong)
|
} else if (status === Controller.Wrong) {
|
||||||
showPassiveNotification(i18n("Wrong password entered"))
|
showPassiveNotification(i18n("Wrong password entered"));
|
||||||
else
|
} else {
|
||||||
showPassiveNotification(i18n("Unknown problem while trying to change password"))
|
showPassiveNotification(i18n("Unknown problem while trying to change password"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
property Component openFileDialog: Component {
|
||||||
id: openFileDialog
|
id: openFileDialog
|
||||||
|
|
||||||
OpenFileDialog {
|
OpenFileDialog {
|
||||||
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
|
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Kirigami.OverlaySheet {
|
|
||||||
id: userEditSheet
|
|
||||||
|
|
||||||
property var connection
|
|
||||||
|
|
||||||
title: i18n("Edit Account")
|
|
||||||
|
|
||||||
Kirigami.FormLayout {
|
|
||||||
RowLayout {
|
|
||||||
Kirigami.Avatar {
|
|
||||||
id: avatar
|
|
||||||
source: userEditSheet.connection && userEditSheet.connection.localUser.avatarMediaId ? ("image://mxc/" + userEditSheet.connection.localUser.avatarMediaId) : ""
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: mouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
property var fileDialog: null;
|
|
||||||
onClicked: {
|
|
||||||
if (fileDialog != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileDialog = openFileDialog.createObject(Controls.ApplicationWindow.Overlay)
|
|
||||||
|
|
||||||
fileDialog.chosen.connect(function(receivedSource) {
|
|
||||||
mouseArea.fileDialog = null;
|
|
||||||
if (!receivedSource) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
parent.source = receivedSource;
|
|
||||||
});
|
|
||||||
fileDialog.onRejected.connect(function() {
|
|
||||||
mouseArea.fileDialog = null;
|
|
||||||
});
|
|
||||||
fileDialog.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Controls.Button {
|
|
||||||
visible: avatar.source.toString().length !== 0
|
|
||||||
icon.name: "edit-clear"
|
|
||||||
|
|
||||||
onClicked: avatar.source = ""
|
|
||||||
}
|
|
||||||
Kirigami.FormData.label: i18n("Avatar:")
|
|
||||||
}
|
|
||||||
Controls.TextField {
|
|
||||||
id: name
|
|
||||||
text: userEditSheet.connection ? userEditSheet.connection.localUser.displayName : ""
|
|
||||||
Kirigami.FormData.label: i18n("Name:")
|
|
||||||
}
|
|
||||||
Controls.TextField {
|
|
||||||
id: accountLabel
|
|
||||||
text: userEditSheet.connection ? userEditSheet.connection.localUser.accountLabel : ""
|
|
||||||
Kirigami.FormData.label: i18n("Label:")
|
|
||||||
}
|
|
||||||
Controls.TextField {
|
|
||||||
id: currentPassword
|
|
||||||
Kirigami.FormData.label: i18n("Current Password:")
|
|
||||||
enabled: userEditSheet.connection !== undefined && userEditSheet.connection.canChangePassword !== false
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
}
|
|
||||||
Controls.TextField {
|
|
||||||
id: newPassword
|
|
||||||
Kirigami.FormData.label: i18n("New Password:")
|
|
||||||
enabled: userEditSheet.connection !== undefined && userEditSheet.connection.canChangePassword !== false
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
|
|
||||||
}
|
|
||||||
Controls.TextField {
|
|
||||||
id: confirmPassword
|
|
||||||
Kirigami.FormData.label: i18n("Confirm new Password:")
|
|
||||||
enabled: userEditSheet.connection !== undefined && userEditSheet.connection.canChangePassword !== false
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Controls.Button {
|
|
||||||
text: i18n("Save")
|
|
||||||
onClicked: {
|
|
||||||
if(!Controller.setAvatar(userEditSheet.connection, avatar.source))
|
|
||||||
showPassiveNotification("The Avatar could not be set")
|
|
||||||
if(userEditSheet.connection.localUser.displayName !== name.text)
|
|
||||||
userEditSheet.connection.localUser.rename(name.text)
|
|
||||||
if(userEditSheet.connection.localUser.accountLabel !== accountLabel.text)
|
|
||||||
userEditSheet.connection.localUser.setAccountLabel(accountLabel.text)
|
|
||||||
if(currentPassword.text !== "" && newPassword.text !== "" && confirmPassword.text !== "") {
|
|
||||||
if(newPassword.text === confirmPassword.text) {
|
|
||||||
Controller.changePassword(userEditSheet.connection, currentPassword.text, newPassword.text)
|
|
||||||
} else {
|
|
||||||
showPassiveNotification(i18n("Passwords do not match"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
userEditSheet.close()
|
|
||||||
currentPassword.text = ""
|
|
||||||
newPassword.text = ""
|
|
||||||
confirmPassword.text = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Controls.Button {
|
|
||||||
text: i18n("Cancel")
|
|
||||||
onClicked: {
|
|
||||||
userEditSheet.close()
|
|
||||||
avatar.source = userEditSheet.connection.localUser.avatarMediaId ? ("image://mxc/" + userEditSheet.connection.localUser.avatarMediaId) : ""
|
|
||||||
currentPassword.text = ""
|
|
||||||
newPassword.text = ""
|
|
||||||
confirmPassword.text = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import org.kde.neochat 1.0
|
|||||||
import NeoChat.Settings 1.0
|
import NeoChat.Settings 1.0
|
||||||
|
|
||||||
Kirigami.ScrollablePage {
|
Kirigami.ScrollablePage {
|
||||||
title: i18nc('@title:window', 'Appearance')
|
title: i18nc("@title:window", "Appearance")
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.alignment: Qt.AlignCenter
|
Layout.alignment: Qt.AlignCenter
|
||||||
@@ -177,7 +177,7 @@ Kirigami.ScrollablePage {
|
|||||||
Kirigami.FormLayout {
|
Kirigami.FormLayout {
|
||||||
Layout.maximumWidth: parent.width
|
Layout.maximumWidth: parent.width
|
||||||
QQC2.CheckBox {
|
QQC2.CheckBox {
|
||||||
Kirigami.FormData.label: "Show Avatar:"
|
Kirigami.FormData.label: i18n("Show Avatar:")
|
||||||
text: i18n("In Chat")
|
text: i18n("In Chat")
|
||||||
checked: Config.showAvatarInTimeline
|
checked: Config.showAvatarInTimeline
|
||||||
onToggled: {
|
onToggled: {
|
||||||
@@ -237,6 +237,7 @@ Kirigami.ScrollablePage {
|
|||||||
HoverHandler { id: sliderHover }
|
HoverHandler { id: sliderHover }
|
||||||
QQC2.ToolTip.visible: sliderHover.hovered && !enabled
|
QQC2.ToolTip.visible: sliderHover.hovered && !enabled
|
||||||
QQC2.ToolTip.text: i18n("Only enabled if the transparent chat page is enabled.")
|
QQC2.ToolTip.text: i18n("Only enabled if the transparent chat page is enabled.")
|
||||||
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
}
|
}
|
||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
text: Math.round(Config.transparency * 100) + "%"
|
text: Math.round(Config.transparency * 100) + "%"
|
||||||
@@ -245,7 +246,7 @@ Kirigami.ScrollablePage {
|
|||||||
QQC2.CheckBox {
|
QQC2.CheckBox {
|
||||||
text: i18n("Show your messages on the right")
|
text: i18n("Show your messages on the right")
|
||||||
checked: Config.showLocalMessagesOnRight
|
checked: Config.showLocalMessagesOnRight
|
||||||
enabled: !Config.isShowLocalMessagesOnRightImmutable
|
enabled: !Config.isShowLocalMessagesOnRightImmutable && !Config.compactLayout
|
||||||
onToggled: {
|
onToggled: {
|
||||||
Config.showLocalMessagesOnRight = checked
|
Config.showLocalMessagesOnRight = checked
|
||||||
Config.save()
|
Config.save()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import QtQuick 2.15
|
|||||||
import QtQuick.Controls 2.15 as Controls
|
import QtQuick.Controls 2.15 as Controls
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
import org.kde.kirigami 2.15 as Kirigami
|
import org.kde.kirigami 2.19 as Kirigami
|
||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
@@ -17,45 +17,53 @@ Kirigami.ScrollablePage {
|
|||||||
id: devices
|
id: devices
|
||||||
}
|
}
|
||||||
|
|
||||||
Kirigami.PlaceholderMessage {
|
anchors.fill: parent
|
||||||
visible: parent.model.count === 0 // We can assume 0 means loading since there is at least one device
|
|
||||||
|
Kirigami.LoadingPlaceholder {
|
||||||
|
visible: parent.count === 0 // We can assume 0 means loading since there is at least one device
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
text: i18n("Loading…")
|
|
||||||
Controls.BusyIndicator {
|
|
||||||
running: parent.visible
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Kirigami.SwipeListItem {
|
delegate: Kirigami.BasicListItem {
|
||||||
leftPadding: 0
|
text: model.displayName
|
||||||
rightPadding: 0
|
subtitle: model.id
|
||||||
Kirigami.BasicListItem {
|
icon: "network-connect"
|
||||||
anchors.top: parent.top
|
trailing: RowLayout {
|
||||||
anchors.bottom: parent.bottom
|
Controls.ToolButton {
|
||||||
|
display: Controls.AbstractButton.IconOnly
|
||||||
text: model.displayName
|
action: Kirigami.Action {
|
||||||
subtitle: model.id
|
text: i18n("Edit device name")
|
||||||
icon: "network-connect"
|
iconName: "document-edit"
|
||||||
}
|
onTriggered: {
|
||||||
actions: [
|
renameSheet.index = model.index
|
||||||
Kirigami.Action {
|
renameSheet.name = model.displayName
|
||||||
text: i18n("Edit device name")
|
renameSheet.open()
|
||||||
iconName: "document-edit"
|
}
|
||||||
onTriggered: {
|
|
||||||
renameSheet.index = model.index
|
|
||||||
renameSheet.name = model.displayName
|
|
||||||
renameSheet.open()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Kirigami.Action {
|
|
||||||
text: i18n("Logout device")
|
|
||||||
iconName: "edit-delete-remove"
|
|
||||||
onTriggered: {
|
|
||||||
passwordSheet.index = index
|
|
||||||
passwordSheet.open()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
Controls.ToolButton {
|
||||||
|
display: Controls.AbstractButton.IconOnly
|
||||||
|
visible: Controller.encryptionSupported
|
||||||
|
action: Kirigami.Action {
|
||||||
|
text: i18n("Verify device")
|
||||||
|
iconName: "security-low-symbolic"
|
||||||
|
onTriggered: {
|
||||||
|
devices.connection.startKeyVerificationSession(model.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Controls.ToolButton {
|
||||||
|
display: Controls.AbstractButton.IconOnly
|
||||||
|
action: Kirigami.Action {
|
||||||
|
text: i18n("Logout device")
|
||||||
|
iconName: "edit-delete-remove"
|
||||||
|
onTriggered: {
|
||||||
|
passwordSheet.index = index
|
||||||
|
passwordSheet.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import QtQuick 2.15
|
|||||||
import QtQuick.Controls 2.15 as QQC2
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
import Qt.labs.platform 1.1
|
||||||
|
|
||||||
import org.kde.kirigami 2.15 as Kirigami
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
@@ -14,12 +16,13 @@ import NeoChat.Component 1.0 as Components
|
|||||||
import NeoChat.Dialog 1.0
|
import NeoChat.Dialog 1.0
|
||||||
|
|
||||||
Kirigami.ScrollablePage {
|
Kirigami.ScrollablePage {
|
||||||
title: i18nc('@title:window', 'Custom Emojis')
|
title: i18nc("@title:window", "Custom Emojis")
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
model: CustomEmojiModel {
|
model: CustomEmojiModel {
|
||||||
id: emojiModel
|
id: emojiModel
|
||||||
|
|
||||||
connection: Controller.activeConnection
|
connection: Controller.activeConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,11 +99,10 @@ Kirigami.ScrollablePage {
|
|||||||
this.fileDialog = openFileDialog.createObject(QQC2.Overlay.overlay)
|
this.fileDialog = openFileDialog.createObject(QQC2.Overlay.overlay)
|
||||||
|
|
||||||
this.fileDialog.chosen.connect((url) => {
|
this.fileDialog.chosen.connect((url) => {
|
||||||
emojiModel.addEmoji(emojiField.text, url)
|
emojiModel.addEmoji(emojiCreator.name, url)
|
||||||
this.fileDialog = null
|
this.fileDialog = null
|
||||||
})
|
})
|
||||||
this.fileDialog.onRejected.connect(() => {
|
this.fileDialog.onRejected.connect(() => {
|
||||||
rej()
|
|
||||||
this.fileDialog = null
|
this.fileDialog = null
|
||||||
})
|
})
|
||||||
this.fileDialog.open()
|
this.fileDialog.open()
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ import org.kde.kirigami 2.15 as Kirigami
|
|||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
Kirigami.ScrollablePage {
|
Kirigami.ScrollablePage {
|
||||||
title: i18nc('@title:window', 'General')
|
title: i18nc("@title:window", "General")
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Kirigami.FormLayout {
|
Kirigami.FormLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
QQC2.CheckBox {
|
QQC2.CheckBox {
|
||||||
Kirigami.FormData.label: i18n("General settings:")
|
Kirigami.FormData.label: i18n("General settings:")
|
||||||
text: i18n("Close to system tray")
|
text: i18n("Close to system tray")
|
||||||
@@ -45,6 +46,7 @@ Kirigami.ScrollablePage {
|
|||||||
onToggled: {
|
onToggled: {
|
||||||
Config.showNotifications = checked
|
Config.showNotifications = checked
|
||||||
Config.save()
|
Config.save()
|
||||||
|
NotificationsManager.globalNotificationsEnabled = checked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QQC2.CheckBox {
|
QQC2.CheckBox {
|
||||||
@@ -96,19 +98,38 @@ Kirigami.ScrollablePage {
|
|||||||
QQC2.CheckBox {
|
QQC2.CheckBox {
|
||||||
id: quickEditCheckbox
|
id: quickEditCheckbox
|
||||||
Layout.maximumWidth: parent.width
|
Layout.maximumWidth: parent.width
|
||||||
contentItem: QQC2.Label {
|
text: i18n("Use s/text/replacement syntax to edit your last message")
|
||||||
text: i18n("Use s/text/replacement syntax to edit your last message")
|
|
||||||
horizontalAlignment: Text.AlignLeft
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
leftPadding: quickEditCheckbox.indicator.width + quickEditCheckbox.spacing
|
|
||||||
wrapMode: QQC2.Label.Wrap
|
|
||||||
}
|
|
||||||
checked: Config.allowQuickEdit
|
checked: Config.allowQuickEdit
|
||||||
enabled: !Config.isAllowQuickEditImmutable
|
enabled: !Config.isAllowQuickEditImmutable
|
||||||
onToggled: {
|
onToggled: {
|
||||||
Config.allowQuickEdit = checked
|
Config.allowQuickEdit = checked
|
||||||
Config.save()
|
Config.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO KF5.97 remove this line
|
||||||
|
Component.onCompleted: this.contentItem.wrap = QQC2.Label.Wrap
|
||||||
|
}
|
||||||
|
QQC2.CheckBox {
|
||||||
|
text: i18n("Send Typing Notifications")
|
||||||
|
checked: Config.typingNotifications
|
||||||
|
enabled: !Config.isTypingNotificationsImmutable
|
||||||
|
onToggled: {
|
||||||
|
Config.typingNotifications = checked
|
||||||
|
Config.save()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QQC2.CheckBox {
|
||||||
|
text: i18n("Automatically hide/unhide the room information when resizing the window")
|
||||||
|
Layout.maximumWidth: parent.width
|
||||||
|
checked: Config.autoRoomInfoDrawer
|
||||||
|
enabled: !Config.isAutoRoomInfoDrawerImmutable
|
||||||
|
onToggled: {
|
||||||
|
Config.autoRoomInfoDrawer = checked
|
||||||
|
Config.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO KF5.97 remove this line
|
||||||
|
Component.onCompleted: this.contentItem.wrap = QQC2.Label.Wrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import org.kde.kirigami 2.18 as Kirigami
|
import org.kde.kirigami 2.18 as Kirigami
|
||||||
import QtQuick.Controls 2.15 as Controls
|
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
Kirigami.CategorizedSettings {
|
Kirigami.CategorizedSettings {
|
||||||
@@ -25,7 +24,7 @@ Kirigami.CategorizedSettings {
|
|||||||
page: Qt.resolvedUrl("AccountsPage.qml")
|
page: Qt.resolvedUrl("AccountsPage.qml")
|
||||||
},
|
},
|
||||||
Kirigami.SettingAction {
|
Kirigami.SettingAction {
|
||||||
text: i18n("Custom Emoji")
|
text: i18n("Custom Emojis")
|
||||||
icon.name: "preferences-desktop-emoticons"
|
icon.name: "preferences-desktop-emoticons"
|
||||||
page: Qt.resolvedUrl("Emoticons.qml")
|
page: Qt.resolvedUrl("Emoticons.qml")
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ Kirigami.Page {
|
|||||||
dialog.close();
|
dialog.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
title: i18nc('@window:title', 'Spellchecking')
|
title: i18nc("@window:title", "Spellchecking")
|
||||||
|
|
||||||
QQC2.Dialog {
|
QQC2.Dialog {
|
||||||
id: applyDialog
|
id: applyDialog
|
||||||
@@ -190,6 +190,10 @@ Kirigami.Page {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
enabled: autodetectLanguageCheckbox.checked
|
enabled: autodetectLanguageCheckbox.checked
|
||||||
Component.onCompleted: background.visible = wideMode
|
Component.onCompleted: background.visible = wideMode
|
||||||
|
|
||||||
|
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||||
|
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
clip: true
|
clip: true
|
||||||
model: settings.dictionaryModel
|
model: settings.dictionaryModel
|
||||||
@@ -254,6 +258,10 @@ Kirigami.Page {
|
|||||||
}
|
}
|
||||||
QQC2.ScrollView {
|
QQC2.ScrollView {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
|
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||||
|
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
model: settings.currentIgnoreList
|
model: settings.currentIgnoreList
|
||||||
delegate: Kirigami.BasicListItem {
|
delegate: Kirigami.BasicListItem {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
<name xml:lang="sl">NeoChat</name>
|
<name xml:lang="sl">NeoChat</name>
|
||||||
<name xml:lang="sv">NeoChat</name>
|
<name xml:lang="sv">NeoChat</name>
|
||||||
<name xml:lang="ta">நியோச்சாட்</name>
|
<name xml:lang="ta">நியோச்சாட்</name>
|
||||||
|
<name xml:lang="tr">NeoChat</name>
|
||||||
<name xml:lang="uk">NeoChat</name>
|
<name xml:lang="uk">NeoChat</name>
|
||||||
<name xml:lang="x-test">xxNeoChatxx</name>
|
<name xml:lang="x-test">xxNeoChatxx</name>
|
||||||
<name xml:lang="zh-CN">NeoChat</name>
|
<name xml:lang="zh-CN">NeoChat</name>
|
||||||
@@ -43,7 +44,7 @@
|
|||||||
<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 per al Matrix, el protocol de comunicacions descentralitzat</summary>
|
||||||
<summary xml:lang="ca-valencia">Un client per al Matrix, el protocol de comunicacions descentralitzat</summary>
|
<summary xml:lang="ca-valencia">Un client per a Matrix, el protocol de comunicacions descentralitzat</summary>
|
||||||
<summary xml:lang="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="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>
|
||||||
@@ -65,6 +66,7 @@
|
|||||||
<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="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>
|
||||||
@@ -73,7 +75,7 @@
|
|||||||
<p xml:lang="ar">نيوتشات هو عميل ماتركس Matrix. يتيح لك إرسال رسائل نصية ومقاطع فيديو وملفات صوتية إلى عائلتك وزملائك وأصدقائك باستخدام بروتوكول ماتركس</p>
|
<p xml:lang="ar">نيوتشات هو عميل ماتركس Matrix. يتيح لك إرسال رسائل نصية ومقاطع فيديو وملفات صوتية إلى عائلتك وزملائك وأصدقائك باستخدام بروتوكول ماتركس</p>
|
||||||
<p xml:lang="az">NeoChat Mtrix müştərisidir. O, Matrix protokolundan istifadə edərək, ailənizə, dostlarınıza, iş yoldaşlarınıza mətn, səsli və görüntülü ismarıclar göndərməyə imkan verir.</p>
|
<p xml:lang="az">NeoChat Mtrix müştərisidir. O, Matrix protokolundan istifadə edərək, ailənizə, dostlarınıza, iş yoldaşlarınıza mətn, səsli və görüntülü ismarıclar göndərməyə imkan verir.</p>
|
||||||
<p xml:lang="ca">El NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics usant el protocol Matrix.</p>
|
<p xml:lang="ca">El NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics usant el protocol Matrix.</p>
|
||||||
<p xml:lang="ca-valencia">NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics usant el protocol Matrix.</p>
|
<p xml:lang="ca-valencia">NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics utilitzant el protocol Matrix.</p>
|
||||||
<p xml:lang="de">NeoChat ist ein Matrix-Client. Er ermöglicht Ihnen das Senden von Textnachrichten, Videos und Audiodateien an Ihre Familie, Kollegen und Freunde unter Verwendung des Matrix-Protokolls.</p>
|
<p xml:lang="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 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="en-GB">NeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.</p>
|
||||||
<p xml:lang="es">NeoChat es un cliente para Matrix. Le permite enviar mensajes de texto, vídeos y archivos de sonido a su familia, compañeros de trabajo y amigos usando el protocolo Matrix.</p>
|
<p xml:lang="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>
|
||||||
@@ -92,6 +94,7 @@
|
|||||||
<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="sk">NeoChat je Matrix klient. Umožňuje vám posielať textové správy, videá a zvukové súbory rodine, kolegom a priateľom pomocou protokolu Matrix.</p>
|
||||||
<p xml:lang="sl">NeoChat je odjemalec Matrixa. Dovoljuje vam pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, kolegom in prijateljem z uporabo protokola Matrix.</p>
|
<p xml:lang="sl">NeoChat je odjemalec Matrixa. Dovoljuje vam pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, kolegom in prijateljem z uporabo protokola Matrix.</p>
|
||||||
<p xml:lang="sv">NeoChat är en Matrix-klient. Den låter dig skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner med användning av Matrix-protokollet.</p>
|
<p xml:lang="sv">NeoChat är en Matrix-klient. Den låter dig skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner med användning av Matrix-protokollet.</p>
|
||||||
|
<p xml:lang="tr">NeoChat, bir Matrix istemcisidir. Matrix protokolünü kullanarak ailenize, iş arkadaşlarınıza, arkadaşlarınıza ve başkalarına metin iletileri, video ve ses dosyaların göndermenize olanak verir.</p>
|
||||||
<p xml:lang="uk">NeoChat — клієнт мережі обміну повідомленнями Matrix. За допомогою цієї програми ви зможете надсилати текстові повідомлення, відео та звукові файли вашій родині, колегам та друзям за допомогою протоколу Matrix.</p>
|
<p xml:lang="uk">NeoChat — клієнт мережі обміну повідомленнями Matrix. За допомогою цієї програми ви зможете надсилати текстові повідомлення, відео та звукові файли вашій родині, колегам та друзям за допомогою протоколу Matrix.</p>
|
||||||
<p xml:lang="x-test">xxNeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.xx</p>
|
<p xml:lang="x-test">xxNeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.xx</p>
|
||||||
<p xml:lang="zh-CN">NeoChat 是一个 Matrix 客户端。 它允许您使用 Matrix 协议向您的家人、同事和朋友发送文本消息、视频和音频文件。</p>
|
<p xml:lang="zh-CN">NeoChat 是一个 Matrix 客户端。 它允许您使用 Matrix 协议向您的家人、同事和朋友发送文本消息、视频和音频文件。</p>
|
||||||
@@ -118,6 +121,7 @@
|
|||||||
<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="sk">Matrix je decentralizovaný komunikačný protokol, ktorý používateľovi vracia kontrolu. V súčasnosti NeoChat implementuje veľkú časť protokolu s výnimkou šifrovaných chatov a videohovorov.</p>
|
||||||
<p xml:lang="sl">Matrix je decentraliziran komunikacijski protokol, kjer ima uporabnik uporabnik kontrolo rabe. Trenutno ima NeoChat izveden velik del protokola z izjemo šifriranih klepetov in video klepetov.</p>
|
<p xml:lang="sl">Matrix je decentraliziran komunikacijski protokol, kjer ima uporabnik uporabnik kontrolo rabe. Trenutno ima NeoChat izveden velik del protokola z izjemo šifriranih klepetov in video klepetov.</p>
|
||||||
<p xml:lang="sv">Matrix är ett decentraliserat kommunikationsprotokoll, som ger tillbaka kontrollen till användaren. För närvarande implementerar NeoChat en stor del av protokollet, med undantag för krypterad chatt och videochatt.</p>
|
<p xml:lang="sv">Matrix är ett decentraliserat kommunikationsprotokoll, som ger tillbaka kontrollen till användaren. För närvarande implementerar NeoChat en stor del av protokollet, med undantag för krypterad chatt och videochatt.</p>
|
||||||
|
<p xml:lang="tr">Matrix; tam denetimi kullanıcıya bırakan, merkezi olmayan bir iletişim protokolüdür. Şu anda NeoChat, uçtan uca şifrelenmiş metin ve video sohbetleri dışında protokolün büyük bir bölümünü bünyesinde bulundurur.</p>
|
||||||
<p xml:lang="uk">Matrix — протокол децентралізованого спілкування, який передає контроль над даними користувачеві. У поточній версії NeoChat реалізовано більшу частину протоколу, окрім зашифрованого спілкування та відеоспілкування.</p>
|
<p xml:lang="uk">Matrix — протокол децентралізованого спілкування, який передає контроль над даними користувачеві. У поточній версії NeoChat реалізовано більшу частину протоколу, окрім зашифрованого спілкування та відеоспілкування.</p>
|
||||||
<p xml:lang="x-test">xxMatrix is a decentralized communication protocol, putting the user back in control. Currently NeoChat implements large part of the protocol with the exception of encrypted chats and video chat.xx</p>
|
<p xml:lang="x-test">xxMatrix is a decentralized communication protocol, putting the user back in control. Currently NeoChat implements large part of the protocol with the exception of encrypted chats and video chat.xx</p>
|
||||||
<p xml:lang="zh-CN">Matrix 是一个分布式通讯协议,使用户重新得到控制权。 目前,NeoChat 实现了协议的大部分,除了加密聊天和视频聊天。</p>
|
<p xml:lang="zh-CN">Matrix 是一个分布式通讯协议,使用户重新得到控制权。 目前,NeoChat 实现了协议的大部分,除了加密聊天和视频聊天。</p>
|
||||||
@@ -125,7 +129,7 @@
|
|||||||
<p xml:lang="ar">يعمل نيوتشات على كل من الأجهزة المحمولة وسطح المكتب مع توفير تجربة مستخدم متسقة.</p>
|
<p xml:lang="ar">يعمل نيوتشات على كل من الأجهزة المحمولة وسطح المكتب مع توفير تجربة مستخدم متسقة.</p>
|
||||||
<p xml:lang="az">Vahid istifadəçi interfeysi ilə təmin olunan NeoChat, həm mobil telefonda həm də kompyuterlərdə işləyir.</p>
|
<p xml:lang="az">Vahid istifadəçi interfeysi ilə təmin olunan NeoChat, həm mobil telefonda həm də kompyuterlərdə işləyir.</p>
|
||||||
<p xml:lang="ca">El NeoChat funciona en els mòbils i a l'escriptori, proporcionant una experiència d'usuari coherent.</p>
|
<p xml:lang="ca">El NeoChat funciona en els mòbils i a l'escriptori, proporcionant una experiència d'usuari coherent.</p>
|
||||||
<p xml:lang="ca-valencia">NeoChat funciona en els mòbils i a l'escriptori, proporcionant una experiència d'usuari coherent.</p>
|
<p xml:lang="ca-valencia">NeoChat funciona en els mòbils i en l'escriptori, proporcionant una experiència d'usuari coherent.</p>
|
||||||
<p xml:lang="de">NeoChat funktioniert sowohl auf dem Mobiltelefon als auch auf dem Arbeitsfläche und bietet ein einheitliches Benutzererlebnis. </p>
|
<p xml:lang="de">NeoChat funktioniert sowohl auf dem Mobiltelefon als auch auf dem Arbeitsfläche und bietet ein einheitliches Benutzererlebnis. </p>
|
||||||
<p xml:lang="en-GB">NeoChat works both on mobile and desktop while providing a consistent user experience.</p>
|
<p xml:lang="en-GB">NeoChat works both on mobile and desktop while providing a consistent user experience.</p>
|
||||||
<p xml:lang="es">NeoChat funciona en móviles y en el escritorio a la vez que proporciona una experiencia de usuario consistente.</p>
|
<p xml:lang="es">NeoChat funciona en móviles y en el escritorio a la vez que proporciona una experiencia de usuario consistente.</p>
|
||||||
@@ -144,12 +148,13 @@
|
|||||||
<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="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="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="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="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="x-test">xxNeoChat works both on mobile and desktop while providing a consistent user experience.xx</p>
|
||||||
<p xml:lang="zh-CN">NeoChat 在移动设备和桌面上均可用,并提供一致的用户体验。</p>
|
<p 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://invent.kde.org/network/neochat/-/issues</url>
|
<url type="bugtracker">https://bugs.kde.org/buglist.cgi?component=General&product=NeoChat</url>
|
||||||
<categories>
|
<categories>
|
||||||
<category>Network</category>
|
<category>Network</category>
|
||||||
</categories>
|
</categories>
|
||||||
@@ -179,12 +184,16 @@
|
|||||||
<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="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>
|
||||||
<developer_name xml:lang="zh-CN">KDE 社区</developer_name>
|
<developer_name xml:lang="zh-CN">KDE 社区</developer_name>
|
||||||
<metadata_license>CC0-1.0</metadata_license>
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
<project_license>GPL-3.0</project_license>
|
<project_license>GPL-3.0</project_license>
|
||||||
<value key="KDE::matrix">#neochat:kde.org</value>
|
<custom>
|
||||||
|
<value key="KDE::matrix">#neochat:kde.org</value>
|
||||||
|
</custom>
|
||||||
|
<launchable type="desktop-id">org.kde.neochat.desktop</launchable>
|
||||||
<screenshots>
|
<screenshots>
|
||||||
<screenshot type="default">
|
<screenshot type="default">
|
||||||
<image>https://cdn.kde.org/screenshots/neochat/application-mobile.png</image>
|
<image>https://cdn.kde.org/screenshots/neochat/application-mobile.png</image>
|
||||||
@@ -197,6 +206,26 @@
|
|||||||
<content_attribute id="social-chat">intense</content_attribute>
|
<content_attribute id="social-chat">intense</content_attribute>
|
||||||
</content_rating>
|
</content_rating>
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="22.09" date="2022-09-27">
|
||||||
|
<url>https://www.plasma-mobile.org/2022/09/27/plasma-mobile-gear-22-09/</url>
|
||||||
|
</release>
|
||||||
|
<release version="22.06" date="2022-06-24">
|
||||||
|
<url>https://www.plasma-mobile.org/2022/06/28/plasma-mobile-gear-22-06/</url>
|
||||||
|
<description>
|
||||||
|
<p>This release brings you various small bugfixes and improvements:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Sending of typing notifications can now be disabled.</li>
|
||||||
|
<li>In the room list, the scrollbar will now disappear correctly when it is not needed.</li>
|
||||||
|
<li>On wayland, NeoChat will now raise correctly when clicking on a notification.</li>
|
||||||
|
<li>Several bugs have been fixed that would sometimes cause messages containing markdown and/or HTML elements to be sent incorrectly.</li>
|
||||||
|
<li>The quick switcher can now be controlled using the mouse.</li>
|
||||||
|
<li>There is now an option to disable automatic room sidebar opening when resizing the window.</li>
|
||||||
|
<li>Creation of custom emojis has been fixed.</li>
|
||||||
|
<li>Editing or replying to the last message using the keyboard shortcuts now works correctly.</li>
|
||||||
|
<li>When switching between rooms using the keyboard, the switching direction is now correct.</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release version="22.04" date="2022-04-26">
|
<release version="22.04" date="2022-04-26">
|
||||||
<url>https://www.plasma-mobile.org/2022/04/26/plasma-mobile-gear-22-04/</url>
|
<url>https://www.plasma-mobile.org/2022/04/26/plasma-mobile-gear-22-04/</url>
|
||||||
<description>
|
<description>
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ Name[sk]=NeoChat
|
|||||||
Name[sl]=NeoChat
|
Name[sl]=NeoChat
|
||||||
Name[sv]=NeoChat
|
Name[sv]=NeoChat
|
||||||
Name[ta]=நியோச்சாட்
|
Name[ta]=நியோச்சாட்
|
||||||
|
Name[tr]=NeoChat
|
||||||
Name[uk]=NeoChat
|
Name[uk]=NeoChat
|
||||||
Name[x-test]=xxNeoChatxx
|
Name[x-test]=xxNeoChatxx
|
||||||
Name[zh_CN]=NeoChat
|
Name[zh_CN]=NeoChat
|
||||||
@@ -63,6 +64,7 @@ GenericName[sk]=Matrix Client
|
|||||||
GenericName[sl]=Odjemalec Matrix
|
GenericName[sl]=Odjemalec Matrix
|
||||||
GenericName[sv]=Matrix-klient
|
GenericName[sv]=Matrix-klient
|
||||||
GenericName[ta]=Matrix வாங்கி
|
GenericName[ta]=Matrix வாங்கி
|
||||||
|
GenericName[tr]=Matrix İstemcisi
|
||||||
GenericName[uk]=Клієнт Matrix
|
GenericName[uk]=Клієнт Matrix
|
||||||
GenericName[x-test]=xxMatrix Clientxx
|
GenericName[x-test]=xxMatrix Clientxx
|
||||||
GenericName[zh_CN]=Matrix 客户端
|
GenericName[zh_CN]=Matrix 客户端
|
||||||
@@ -94,6 +96,7 @@ 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
|
||||||
Comment[ta]=Matrix நெறிமுறைக்கான வாங்கி
|
Comment[ta]=Matrix நெறிமுறைக்கான வாங்கி
|
||||||
|
Comment[tr]=Matrix protokolü için istemci
|
||||||
Comment[uk]=Клієнт протоколу Matrix
|
Comment[uk]=Клієнт протоколу Matrix
|
||||||
Comment[x-test]=xxClient for the Matrix protocolxx
|
Comment[x-test]=xxClient for the Matrix protocolxx
|
||||||
Comment[zh_CN]=为 Matrix 协议打造的客户端
|
Comment[zh_CN]=为 Matrix 协议打造的客户端
|
||||||
|
|||||||
1034
po/ar/neochat.po
1034
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
1066
po/az/neochat.po
1066
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
1240
po/ca/neochat.po
1240
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1009
po/cs/neochat.po
1009
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
1026
po/da/neochat.po
1026
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
1219
po/de/neochat.po
1219
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
1043
po/en_GB/neochat.po
1043
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
1071
po/es/neochat.po
1071
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
1251
po/eu/neochat.po
1251
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
1061
po/fi/neochat.po
1061
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
1067
po/fr/neochat.po
1067
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
1061
po/hu/neochat.po
1061
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
1060
po/ia/neochat.po
1060
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
1086
po/id/neochat.po
1086
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
1039
po/it/neochat.po
1039
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
1013
po/ja/neochat.po
1013
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
1060
po/ko/neochat.po
1060
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
1069
po/nl/neochat.po
1069
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
961
po/nn/neochat.po
961
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
1063
po/pa/neochat.po
1063
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
1093
po/pl/neochat.po
1093
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
1060
po/pt/neochat.po
1060
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
1062
po/pt_BR/neochat.po
1062
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
2373
po/ru/neochat.po
Normal file
2373
po/ru/neochat.po
Normal file
File diff suppressed because it is too large
Load Diff
1057
po/sk/neochat.po
1057
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
1058
po/sl/neochat.po
1058
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
1059
po/sv/neochat.po
1059
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
1211
po/ta/neochat.po
1211
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
1055
po/tok/neochat.po
1055
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
1868
po/tr/neochat.po
1868
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
1058
po/uk/neochat.po
1058
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
1020
po/zh_CN/neochat.po
1020
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
54
qml/main.qml
54
qml/main.qml
@@ -13,6 +13,7 @@ import NeoChat.Component 1.0
|
|||||||
import NeoChat.Dialog 1.0
|
import NeoChat.Dialog 1.0
|
||||||
import NeoChat.Page 1.0
|
import NeoChat.Page 1.0
|
||||||
import NeoChat.Panel 1.0
|
import NeoChat.Panel 1.0
|
||||||
|
import NeoChat.Dialog.KeyVerification 1.0
|
||||||
|
|
||||||
Kirigami.ApplicationWindow {
|
Kirigami.ApplicationWindow {
|
||||||
id: root
|
id: root
|
||||||
@@ -51,7 +52,7 @@ Kirigami.ApplicationWindow {
|
|||||||
Timer {
|
Timer {
|
||||||
id: saveWindowGeometryTimer
|
id: saveWindowGeometryTimer
|
||||||
interval: 1000
|
interval: 1000
|
||||||
onTriggered: Controller.saveWindowGeometry(root)
|
onTriggered: Controller.saveWindowGeometry()
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
@@ -59,7 +60,7 @@ Kirigami.ApplicationWindow {
|
|||||||
enabled: false // Disable on startup to avoid writing wrong values if the window is hidden
|
enabled: false // Disable on startup to avoid writing wrong values if the window is hidden
|
||||||
target: root
|
target: root
|
||||||
|
|
||||||
function onClosing() { Controller.saveWindowGeometry(root); }
|
function onClosing() { Controller.saveWindowGeometry(); }
|
||||||
function onWidthChanged() { saveWindowGeometryTimer.restart(); }
|
function onWidthChanged() { saveWindowGeometryTimer.restart(); }
|
||||||
function onHeightChanged() { saveWindowGeometryTimer.restart(); }
|
function onHeightChanged() { saveWindowGeometryTimer.restart(); }
|
||||||
function onXChanged() { saveWindowGeometryTimer.restart(); }
|
function onXChanged() { saveWindowGeometryTimer.restart(); }
|
||||||
@@ -106,7 +107,7 @@ Kirigami.ApplicationWindow {
|
|||||||
|
|
||||||
function onOpenRoomInNewWindow(room) {
|
function onOpenRoomInNewWindow(room) {
|
||||||
const secondayWindow = roomWindow.createObject(applicationWindow(), {currentRoom: room});
|
const secondayWindow = roomWindow.createObject(applicationWindow(), {currentRoom: room});
|
||||||
secondayWindow.width = root.width - pageStack.get(0).width;
|
secondayWiroomWindowndow.width = root.width - pageStack.get(0).width;
|
||||||
secondayWindow.show();
|
secondayWindow.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,19 +140,16 @@ Kirigami.ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showWindow() {
|
|
||||||
root.show()
|
|
||||||
root.raise()
|
|
||||||
root.requestActivate()
|
|
||||||
Controller.raiseWindow(root)
|
|
||||||
}
|
|
||||||
|
|
||||||
contextDrawer: RoomDrawer {
|
contextDrawer: RoomDrawer {
|
||||||
id: contextDrawer
|
id: contextDrawer
|
||||||
edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge
|
|
||||||
modal: !root.wideScreen || !enabled
|
modal: !root.wideScreen || !enabled
|
||||||
onEnabledChanged: drawerOpen = enabled && !modal
|
onEnabledChanged: drawerOpen = enabled && !modal
|
||||||
onModalChanged: drawerOpen = !modal
|
onModalChanged: {
|
||||||
|
if (Config.autoRoomInfoDrawer) {
|
||||||
|
drawerOpen = !modal
|
||||||
|
dim = false
|
||||||
|
}
|
||||||
|
}
|
||||||
enabled: RoomManager.hasOpenRoom && pageStack.layers.depth < 2 && pageStack.depth < 3
|
enabled: RoomManager.hasOpenRoom && pageStack.layers.depth < 2 && pageStack.depth < 3
|
||||||
handleVisible: enabled && pageStack.layers.depth < 2 && pageStack.depth < 3 && (root.wideScreen || pageStack.currentIndex > 0)
|
handleVisible: enabled && pageStack.layers.depth < 2 && pageStack.depth < 3 && (root.wideScreen || pageStack.currentIndex > 0)
|
||||||
}
|
}
|
||||||
@@ -231,13 +229,13 @@ Kirigami.ApplicationWindow {
|
|||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Explore rooms")
|
text: i18n("Explore rooms")
|
||||||
icon.name: "compass"
|
icon.name: "compass"
|
||||||
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/JoinRoomPage.qml", {"connection": Controller.activeConnection})
|
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/JoinRoomPage.qml", {connection: Controller.activeConnection})
|
||||||
enabled: pageStack.layers.currentItem.title !== i18n("Explore Rooms") && Controller.accountCount > 0
|
enabled: pageStack.layers.currentItem.title !== i18n("Explore Rooms") && Controller.accountCount > 0
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Start a Chat")
|
text: i18n("Start a Chat")
|
||||||
icon.name: "irc-join-channel"
|
icon.name: "irc-join-channel"
|
||||||
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/StartChatPage.qml", {"connection": Controller.activeConnection})
|
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/StartChatPage.qml", {connection: Controller.activeConnection})
|
||||||
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0
|
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
@@ -309,10 +307,10 @@ Kirigami.ApplicationWindow {
|
|||||||
Connections {
|
Connections {
|
||||||
target: root.roomPage
|
target: root.roomPage
|
||||||
function onSwitchRoomUp() {
|
function onSwitchRoomUp() {
|
||||||
roomList.goToNextRoom();
|
roomList.goToPreviousRoom();
|
||||||
}
|
}
|
||||||
function onSwitchRoomDown() {
|
function onSwitchRoomDown() {
|
||||||
roomList.goToPreviousRoom();
|
roomList.goToNextRoom();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,16 +347,6 @@ Kirigami.ApplicationWindow {
|
|||||||
showPassiveNotification(i18n("%1: %2", error, detail));
|
showPassiveNotification(i18n("%1: %2", error, detail));
|
||||||
}
|
}
|
||||||
|
|
||||||
function onShowWindow(token = null) {
|
|
||||||
root.showWindow()
|
|
||||||
if (token && KWindowSystem) {
|
|
||||||
KWindowSystem.setCurrentXdgActivationToken(basicNotification.xdgActivationToken)
|
|
||||||
KWindowSystem.activateWindow(root)
|
|
||||||
} else {
|
|
||||||
root.raise()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onUserConsentRequired(url) {
|
function onUserConsentRequired(url) {
|
||||||
consentSheet.url = url
|
consentSheet.url = url
|
||||||
consentSheet.open()
|
consentSheet.open()
|
||||||
@@ -380,11 +368,23 @@ Kirigami.ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: keyVerificationDialogComponent
|
||||||
|
KeyVerificationDialog { }
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Controller.activeConnection
|
target: Controller.activeConnection
|
||||||
function onDirectChatAvailable(directChat) {
|
function onDirectChatAvailable(directChat) {
|
||||||
RoomManager.enterRoom(Controller.activeConnection.room(directChat.id));
|
RoomManager.enterRoom(Controller.activeConnection.room(directChat.id));
|
||||||
}
|
}
|
||||||
|
function onNewKeyVerificationSession(session) {
|
||||||
|
applicationWindow().pageStack.pushDialogLayer(keyVerificationDialogComponent, {
|
||||||
|
session: session,
|
||||||
|
}, {
|
||||||
|
title: i18nc("@title:window", "Session Verification")
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Kirigami.OverlaySheet {
|
Kirigami.OverlaySheet {
|
||||||
@@ -403,7 +403,7 @@ Kirigami.ApplicationWindow {
|
|||||||
}
|
}
|
||||||
footer: QQC2.Button {
|
footer: QQC2.Button {
|
||||||
text: i18n("Open")
|
text: i18n("Open")
|
||||||
onClicked: Qt.openUrlExternally(consentSheet.url)
|
onClicked: UrlHelper.openUrl(consentSheet.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user