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
|
||||
|
||||
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/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/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
|
||||
|
||||
Files: src/neochatconfig.kcfg
|
||||
Copyright: 2020-2021 Carl Schwan <carlschwan@kde.org>
|
||||
Copyright: 2020-2021 Tobias Fella <fella@posteo.de>
|
||||
Copyright: 2020-2021 Carl Schwan <carlschwan@kde.org>, Tobias Fella <fella@posteo.de>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: src/neochat.notifyrc
|
||||
@@ -42,3 +41,7 @@ License: BSD-2-Clause
|
||||
Files: imports/NeoChat/Component/confetti.png imports/NeoChat/Component/glowdot.png
|
||||
Copyright: 2021 Alexey Andreyev <aa13q@ya.ru>
|
||||
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)
|
||||
|
||||
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")
|
||||
|
||||
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
|
||||
@@ -25,12 +25,14 @@ include(FeatureSummary)
|
||||
include(ECMSetupVersion)
|
||||
include(KDEInstallDirs)
|
||||
include(ECMFindQmlModule)
|
||||
include(KDEClangFormat)
|
||||
include(KDECMakeSettings)
|
||||
include(KDECompilerSettings NO_POLICY_SCOPE)
|
||||
include(ECMAddAppIcon)
|
||||
include(KDEGitCommitHooks)
|
||||
include(ECMCheckOutboundLicense)
|
||||
if (NOT ANDROID)
|
||||
include(KDEClangFormat)
|
||||
endif()
|
||||
|
||||
if(NEOCHAT_FLATPAK)
|
||||
include(cmake/Flatpak.cmake)
|
||||
@@ -41,8 +43,8 @@ ecm_setup_version(${PROJECT_VERSION}
|
||||
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
|
||||
)
|
||||
|
||||
find_package(Qt5 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg)
|
||||
set_package_properties(Qt5 PROPERTIES
|
||||
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg)
|
||||
set_package_properties(Qt${QT_MAJOR_VERSION} PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
)
|
||||
@@ -56,8 +58,8 @@ set_package_properties(KF5Kirigami2 PROPERTIES
|
||||
PURPOSE "Kirigami application UI framework"
|
||||
)
|
||||
|
||||
find_package(Qt5Keychain)
|
||||
set_package_properties(Qt5Keychain PROPERTIES
|
||||
find_package(Qt${QT_MAJOR_VERSION}Keychain)
|
||||
set_package_properties(Qt${QT_MAJOR_VERSION}Keychain PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Secure storage of account secrets"
|
||||
)
|
||||
@@ -69,11 +71,12 @@ if(ANDROID)
|
||||
PURPOSE "Encrypted communications"
|
||||
)
|
||||
else()
|
||||
find_package(Qt5 ${QT_MIN_VERSION} COMPONENTS Widgets)
|
||||
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle ConfigWidgets KIO WindowSystem Sonnet)
|
||||
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} COMPONENTS Widgets)
|
||||
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle ConfigWidgets KIO WindowSystem)
|
||||
set_package_properties(KF5QQC2DesktopStyle PROPERTIES
|
||||
TYPE RUNTIME
|
||||
)
|
||||
ecm_find_qmlmodule(org.kde.sonnet 1.0)
|
||||
ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0)
|
||||
endif()
|
||||
|
||||
@@ -84,7 +87,7 @@ endif()
|
||||
find_package(Quotient 0.6)
|
||||
set_package_properties(Quotient PROPERTIES
|
||||
TYPE REQUIRED
|
||||
DESCRIPTION "Qt wrapper arround Matrix API"
|
||||
DESCRIPTION "Qt wrapper around Matrix API"
|
||||
URL "https://github.com/quotient-im/libQuotient/"
|
||||
PURPOSE "Talk with matrix server"
|
||||
)
|
||||
@@ -108,8 +111,8 @@ set_package_properties(KQuickImageEditor PROPERTIES
|
||||
PURPOSE "Add image editing capability to image attachments"
|
||||
)
|
||||
|
||||
find_package(QCoro5 COMPONENTS Core QUIET)
|
||||
if(NOT QCoro5_FOUND)
|
||||
find_package(QCoro${QT_MAJOR_VERSION} COMPONENTS Core QUIET)
|
||||
if(NOT QCoro${QT_MAJOR_VERSION}_FOUND)
|
||||
find_package(QCoro REQUIRED)
|
||||
endif()
|
||||
|
||||
@@ -136,11 +139,12 @@ add_subdirectory(src)
|
||||
|
||||
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
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)
|
||||
if (NOT ANDROID)
|
||||
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)
|
||||
endif()
|
||||
file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.h *.qml)
|
||||
# CI installs dependency headers to _install and _build, which break the reuse check
|
||||
# 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).
|
||||
|
||||

|
||||

|
||||
|
||||
## Features
|
||||
|
||||
@@ -55,7 +55,7 @@ We welcome contributions in this direction.
|
||||
|
||||
## 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).
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</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.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
|
||||
@@ -9,6 +9,6 @@ install(
|
||||
FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Flatpak/99-noto-mono-color-emoji.conf
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_SYSCONFDIR}/fonts/conf.d/
|
||||
${CMAKE_INSTALL_SYSCONFDIR}/fonts/local.conf
|
||||
)
|
||||
|
||||
|
||||
@@ -14,14 +14,14 @@ import NeoChat.Page 1.0
|
||||
Loader {
|
||||
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)
|
||||
|
||||
active: visible
|
||||
sourceComponent: Component {
|
||||
Pane {
|
||||
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
|
||||
|
||||
contentItem: Item {
|
||||
@@ -46,7 +46,7 @@ Loader {
|
||||
asynchronous: true
|
||||
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
|
||||
source: hasImage ? ChatBoxHelper.attachmentPath : ""
|
||||
source: hasImage ? chatBoxHelper.attachmentPath : ""
|
||||
visible: hasImage
|
||||
fillMode: Image.PreserveAspectFit
|
||||
|
||||
@@ -162,14 +162,14 @@ Loader {
|
||||
Component {
|
||||
id: imageEditorPage
|
||||
ImageEditorPage {
|
||||
imagePath: ChatBoxHelper.attachmentPath
|
||||
imagePath: chatBoxHelper.attachmentPath
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
let imageEditor = applicationWindow().pageStack.layers.push(imageEditorPage);
|
||||
imageEditor.newPathChanged.connect(function(newPath) {
|
||||
applicationWindow().pageStack.layers.pop();
|
||||
ChatBoxHelper.attachmentPath = newPath;
|
||||
chatBoxHelper.attachmentPath = newPath;
|
||||
});
|
||||
}
|
||||
ToolTip.text: text
|
||||
@@ -180,7 +180,7 @@ Loader {
|
||||
icon.name: "dialog-cancel"
|
||||
text: i18n("Cancel")
|
||||
display: AbstractButton.IconOnly
|
||||
onClicked: ChatBoxHelper.clearAttachment();
|
||||
onClicked: chatBoxHelper.clearAttachment();
|
||||
ToolTip.text: text
|
||||
ToolTip.visible: hovered
|
||||
}
|
||||
|
||||
@@ -63,6 +63,9 @@ ToolBar {
|
||||
Layout.maximumHeight: fontMetrics.lineSpacing * 8 - fontMetrics.leading
|
||||
+ inputField.topPadding + inputField.bottomPadding
|
||||
|
||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
FontMetrics {
|
||||
id: fontMetrics
|
||||
font: inputField.font
|
||||
@@ -94,11 +97,11 @@ ToolBar {
|
||||
//property int lineHeight: contentHeight / lineCount
|
||||
|
||||
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
|
||||
horizontalAlignment: TextEdit.AlignLeft
|
||||
wrapMode: Text.Wrap
|
||||
readOnly: currentRoom.usesEncryption
|
||||
readOnly: currentRoom.usesEncryption && !Controller.encryptionSupported
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
@@ -145,11 +148,7 @@ ToolBar {
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key === Qt.Key_PageDown) {
|
||||
switchRoomDown();
|
||||
} else if (event.key === Qt.Key_PageUp) {
|
||||
switchRoomUp();
|
||||
} else if (event.key === Qt.Key_V && event.modifiers & Qt.ControlModifier) {
|
||||
if (event.key === Qt.Key_V && event.modifiers & Qt.ControlModifier) {
|
||||
chatBar.pasteImage();
|
||||
} else if (event.key === Qt.Key_Up && event.modifiers & Qt.ControlModifier) {
|
||||
replyPreviousUserMessage();
|
||||
@@ -229,7 +228,7 @@ ToolBar {
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
if (!repeatTimer.running) {
|
||||
if (!repeatTimer.running && Config.typingNotifications) {
|
||||
currentRoom.sendTypingNotification(true)
|
||||
}
|
||||
repeatTimer.start()
|
||||
@@ -278,14 +277,14 @@ ToolBar {
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: !ChatBoxHelper.isReplying && (!ChatBoxHelper.hasAttachment || uploadingBusySpinner.running)
|
||||
visible: !chatBoxHelper.isReplying && (!chatBoxHelper.hasAttachment || uploadingBusySpinner.running)
|
||||
implicitWidth: uploadButton.implicitWidth
|
||||
implicitHeight: uploadButton.implicitHeight
|
||||
ToolButton {
|
||||
id: uploadButton
|
||||
anchors.fill: parent
|
||||
// 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"
|
||||
text: i18n("Attach an image or file")
|
||||
display: AbstractButton.IconOnly
|
||||
@@ -297,7 +296,7 @@ ToolBar {
|
||||
var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay)
|
||||
fileDialog.chosen.connect((path) => {
|
||||
if (!path) { return }
|
||||
ChatBoxHelper.attachmentPath = path;
|
||||
chatBoxHelper.attachmentPath = path;
|
||||
})
|
||||
fileDialog.open()
|
||||
}
|
||||
@@ -380,16 +379,16 @@ ToolBar {
|
||||
if (!Clipboard.saveImage(localPath)) {
|
||||
return;
|
||||
}
|
||||
ChatBoxHelper.attachmentPath = localPath;
|
||||
chatBoxHelper.attachmentPath = localPath;
|
||||
}
|
||||
|
||||
function postMessage() {
|
||||
checkForFancyEffectsReason();
|
||||
|
||||
if (ChatBoxHelper.hasAttachment) {
|
||||
if (chatBoxHelper.hasAttachment) {
|
||||
// send attachment but don't reset the text
|
||||
actionsHandler.postMessage("", ChatBoxHelper.attachmentPath,
|
||||
ChatBoxHelper.replyEventId, ChatBoxHelper.editEventId, {}, this.customEmojiModel);
|
||||
actionsHandler.postMessage("", chatBoxHelper.attachmentPath,
|
||||
chatBoxHelper.replyEventId, chatBoxHelper.editEventId, {}, this.customEmojiModel);
|
||||
currentRoom.markAllMessagesAsRead();
|
||||
messageSent();
|
||||
return;
|
||||
@@ -401,8 +400,8 @@ ToolBar {
|
||||
actionsHandler.postEdit(inputField.text);
|
||||
} else {
|
||||
// send normal message
|
||||
actionsHandler.postMessage(inputField.text.trim(), ChatBoxHelper.attachmentPath,
|
||||
ChatBoxHelper.replyEventId, ChatBoxHelper.editEventId, userAutocompleted, this.customEmojiModel);
|
||||
actionsHandler.postMessage(inputField.text.trim(), chatBoxHelper.attachmentPath,
|
||||
chatBoxHelper.replyEventId, chatBoxHelper.editEventId, userAutocompleted, this.customEmojiModel);
|
||||
}
|
||||
currentRoom.markAllMessagesAsRead();
|
||||
inputField.clear();
|
||||
|
||||
@@ -127,8 +127,8 @@ Item {
|
||||
|
||||
ReplyPane {
|
||||
id: replyPane
|
||||
visible: ChatBoxHelper.isReplying || ChatBoxHelper.isEditing
|
||||
user: ChatBoxHelper.replyUser
|
||||
visible: chatBoxHelper.isReplying || chatBoxHelper.isEditing
|
||||
user: chatBoxHelper.replyUser
|
||||
width: parent.width
|
||||
height: visible ? implicitHeight : 0
|
||||
anchors.bottom: attachmentSeparator.top
|
||||
@@ -154,7 +154,7 @@ Item {
|
||||
|
||||
AttachmentPane {
|
||||
id: attachmentPane
|
||||
visible: ChatBoxHelper.hasAttachment
|
||||
visible: chatBoxHelper.hasAttachment
|
||||
width: parent.width
|
||||
height: visible ? implicitHeight : 0
|
||||
anchors.bottom: chatBarSeparator.top
|
||||
@@ -248,7 +248,7 @@ Item {
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ChatBoxHelper
|
||||
target: chatBoxHelper
|
||||
|
||||
function onShouldClearText() {
|
||||
root.inputFieldText = "";
|
||||
@@ -277,7 +277,7 @@ Item {
|
||||
}
|
||||
|
||||
function closeAll() {
|
||||
ChatBoxHelper.clear();
|
||||
chatBoxHelper.clear();
|
||||
chatBar.emojiPaneOpened = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ Popup {
|
||||
implicitHeight: Math.min(completionListView.contentHeight, Kirigami.Units.gridUnit * 10)
|
||||
|
||||
contentItem: ScrollView {
|
||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ListView {
|
||||
id: completionListView
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.kde.neochat 1.0
|
||||
|
||||
Loader {
|
||||
id: root
|
||||
readonly property bool isEdit: ChatBoxHelper.isEditing
|
||||
readonly property bool isEdit: chatBoxHelper.isEditing
|
||||
property var user: null
|
||||
property string avatarMediaUrl: user ? "image://mxc/" + user.avatarMediaId : ""
|
||||
|
||||
@@ -71,6 +71,10 @@ Loader {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
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 {
|
||||
id: textArea
|
||||
leftPadding: 0
|
||||
@@ -79,7 +83,7 @@ Loader {
|
||||
bottomPadding: 0
|
||||
text: {
|
||||
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;
|
||||
}
|
||||
selectByMouse: true
|
||||
@@ -102,7 +106,7 @@ Loader {
|
||||
text: i18n("Cancel")
|
||||
display: AbstractButton.IconOnly
|
||||
onClicked: {
|
||||
ChatBoxHelper.clearEditReply();
|
||||
chatBoxHelper.clear();
|
||||
root.replyCancelled();
|
||||
}
|
||||
ToolTip.text: text
|
||||
|
||||
@@ -14,6 +14,7 @@ ApplicationWindow {
|
||||
property string blurhash: ""
|
||||
property int imageWidth: -1
|
||||
property int imageHeight: -1
|
||||
property var modelData
|
||||
|
||||
flags: Qt.FramelessWindowHint | Qt.WA_TranslucentBackground
|
||||
|
||||
@@ -52,6 +53,24 @@ ApplicationWindow {
|
||||
source: root.blurhash !== "" ? ("image://blurhash/" + root.blurhash) : ""
|
||||
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 {
|
||||
|
||||
@@ -20,7 +20,7 @@ LoginStep {
|
||||
Connections {
|
||||
target: LoginHelper
|
||||
function onSsoUrlChanged() {
|
||||
Qt.openUrlExternally(LoginHelper.ssoUrl)
|
||||
UrlHelper.openUrl(LoginHelper.ssoUrl)
|
||||
}
|
||||
function onConnected() {
|
||||
processed("qrc:/imports/NeoChat/Component/Login/Loading.qml")
|
||||
|
||||
@@ -24,10 +24,12 @@ QQC2.Popup {
|
||||
quickSearch.forceActiveFocus()
|
||||
quickSearch.text = ""
|
||||
}
|
||||
|
||||
anchors.centerIn: QQC2.Overlay.overlay
|
||||
background: Kirigami.Card {}
|
||||
height: 2 * Math.round(implicitHeight / 2)
|
||||
padding: Kirigami.Units.largeSpacing * 2
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: Kirigami.Units.largeSpacing * 2
|
||||
|
||||
@@ -77,11 +79,30 @@ QQC2.Popup {
|
||||
|
||||
required property string avatar
|
||||
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 : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modal: true
|
||||
focus: true
|
||||
}
|
||||
|
||||
@@ -1,71 +1,116 @@
|
||||
// SPDX-FileCopyrightText: 2019-2020 Black Hat <bhat@encom.eu.org>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
// 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
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtGraphicalEffects 1.15
|
||||
import Qt.labs.platform 1.1 as Platform
|
||||
import QtMultimedia 5.15
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
|
||||
import org.kde.neochat 1.0
|
||||
import NeoChat.Component 1.0
|
||||
import NeoChat.Dialog 1.0
|
||||
import NeoChat.Menu.Timeline 1.0
|
||||
|
||||
TimelineContainer {
|
||||
id: audioDelegate
|
||||
|
||||
width: ListView.view.width
|
||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||
|
||||
onOpenContextMenu: openFileContext(model, audioDelegate)
|
||||
|
||||
readonly property bool downloaded: model.progressInfo && model.progressInfo.completed
|
||||
onDownloadedChanged: audio.play()
|
||||
|
||||
hoverComponent: hoverActions
|
||||
innerObject: Control {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: audioDelegate.bubbleMaxWidth
|
||||
Layout.maximumWidth: audioDelegate.contentMaxWidth
|
||||
|
||||
Audio {
|
||||
id: audio
|
||||
source: currentRoom.urlToMxcUrl(content.url)
|
||||
source: model.progressInfo.localPath
|
||||
autoLoad: false
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: openFileContext(model, parent)
|
||||
}
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onLongPressed: openFileContext(model, parent)
|
||||
}
|
||||
states: [
|
||||
State {
|
||||
name: "notDownloaded"
|
||||
when: !model.progressInfo.completed && !model.progressInfo.active
|
||||
|
||||
PropertyChanges {
|
||||
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 {
|
||||
RowLayout {
|
||||
ToolButton {
|
||||
icon.name: audio.playbackState == Audio.PlayingState ? "media-playback-pause" : "media-playback-start"
|
||||
|
||||
onClicked: {
|
||||
if (audio.playbackState == Audio.PlayingState) {
|
||||
audio.pause()
|
||||
} else {
|
||||
audio.play()
|
||||
}
|
||||
}
|
||||
id: playButton
|
||||
}
|
||||
Label {
|
||||
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 {
|
||||
visible: audio.hasAudio
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
// Server doesn't support seeking, so use ProgressBar instead of Slider :(
|
||||
ProgressBar {
|
||||
|
||||
Slider {
|
||||
from: 0
|
||||
to: audio.duration
|
||||
value: audio.position
|
||||
onMoved: audio.seek(value)
|
||||
}
|
||||
|
||||
Label {
|
||||
|
||||
@@ -10,17 +10,18 @@ import org.kde.neochat 1.0
|
||||
|
||||
TimelineContainer {
|
||||
id: encryptedDelegate
|
||||
width: ListView.view.width
|
||||
|
||||
innerObject: TextEdit {
|
||||
text: i18n("This message is encrypted and the sender has not shared the key with this device.")
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
selectedTextColor: Kirigami.Theme.highlightedTextColor
|
||||
selectionColor: Kirigami.Theme.highlightColor
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize
|
||||
selectByMouse: !Kirigami.Settings.isMobile
|
||||
readOnly: true
|
||||
wrapMode: Text.WordWrap
|
||||
textFormat: Text.RichText
|
||||
Layout.maximumWidth: encryptedDelegate.bubbleMaxWidth
|
||||
Layout.maximumWidth: encryptedDelegate.contentMaxWidth
|
||||
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +42,7 @@ DelegateChooser {
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "sticker"
|
||||
delegate: ImageDelegate {
|
||||
cardBackground: false
|
||||
}
|
||||
delegate: ImageDelegate {}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtGraphicalEffects 1.15
|
||||
import Qt.labs.platform 1.1
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
@@ -16,11 +15,12 @@ import NeoChat.Menu.Timeline 1.0
|
||||
|
||||
TimelineContainer {
|
||||
id: fileDelegate
|
||||
width: ListView.view.width
|
||||
|
||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||
hoverComponent: hoverActions
|
||||
|
||||
onOpenContextMenu: openFileContext(model, fileDelegate)
|
||||
|
||||
readonly property bool downloaded: progressInfo && progressInfo.completed
|
||||
|
||||
function saveFileAs() {
|
||||
@@ -30,14 +30,14 @@ TimelineContainer {
|
||||
}
|
||||
|
||||
function openSavedFile() {
|
||||
if (Qt.openUrlExternally(progressInfo.localPath)) return;
|
||||
if (Qt.openUrlExternally(progressInfo.localDir)) return;
|
||||
if (UrlHelper.openUrl(progressInfo.localPath)) return;
|
||||
if (UrlHelper.openUrl(progressInfo.localDir)) return;
|
||||
}
|
||||
|
||||
innerObject: RowLayout {
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: fileDelegate.bubbleMaxWidth
|
||||
Layout.maximumWidth: fileDelegate.contentMaxWidth
|
||||
Layout.margins: Kirigami.Units.largeSpacing
|
||||
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
@@ -53,6 +53,7 @@ TimelineContainer {
|
||||
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.delay: Kirigami.Units.toolTipDelay
|
||||
|
||||
onClicked: openSavedFile()
|
||||
}
|
||||
@@ -70,6 +71,7 @@ TimelineContainer {
|
||||
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.delay: Kirigami.Units.toolTipDelay
|
||||
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.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtGraphicalEffects 1.15
|
||||
import Qt.labs.platform 1.1
|
||||
|
||||
import org.kde.neochat 1.0
|
||||
@@ -16,11 +15,11 @@ import NeoChat.Menu.Timeline 1.0
|
||||
TimelineContainer {
|
||||
id: imageDelegate
|
||||
|
||||
width: ListView.view.width
|
||||
|
||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||
hoverComponent: hoverActions
|
||||
|
||||
onOpenContextMenu: openFileContext(model, imageDelegate)
|
||||
|
||||
property var content: model.content
|
||||
readonly property bool isAnimated: contentType === "image/gif"
|
||||
|
||||
@@ -35,8 +34,8 @@ TimelineContainer {
|
||||
innerObject: Image {
|
||||
id: img
|
||||
|
||||
Layout.maximumWidth: imageDelegate.bubbleMaxWidth
|
||||
Layout.maximumHeight: imageDelegate.bubbleMaxWidth / imageDelegate.info.w * imageDelegate.info.h
|
||||
Layout.maximumWidth: imageDelegate.contentMaxWidth
|
||||
Layout.maximumHeight: imageDelegate.contentMaxWidth / imageDelegate.info.w * imageDelegate.info.h
|
||||
Layout.preferredWidth: imageDelegate.info.w
|
||||
Layout.preferredHeight: imageDelegate.info.h
|
||||
source: model.mediaUrl
|
||||
@@ -86,21 +85,17 @@ TimelineContainer {
|
||||
}
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: openFileContext(model, parent)
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onLongPressed: openFileContext(model, parent)
|
||||
onTapped: {
|
||||
fullScreenImage.createObject(parent, {
|
||||
filename: eventId,
|
||||
source: model.mediaUrl,
|
||||
source: mediaUrl,
|
||||
blurhash: model.content.info["xyz.amorgan.blurhash"],
|
||||
imageWidth: content.info.w,
|
||||
imageHeight: content.info.h
|
||||
imageHeight: content.info.h,
|
||||
modelData: model
|
||||
}).showFullScreen();
|
||||
}
|
||||
}
|
||||
@@ -115,8 +110,8 @@ TimelineContainer {
|
||||
}
|
||||
|
||||
function openSavedFile() {
|
||||
if (Qt.openUrlExternally(progressInfo.localPath)) return;
|
||||
if (Qt.openUrlExternally(progressInfo.localDir)) return;
|
||||
if (UrlHelper.openUrl(progressInfo.localPath)) 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 {
|
||||
id: messageDelegate
|
||||
|
||||
width: ListView.view.width
|
||||
property bool isEmote: false
|
||||
onOpenContextMenu: openMessageContext(model, label.selectedText, Controller.plainText(label.textDocument))
|
||||
|
||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||
hoverComponent: hoverActions
|
||||
|
||||
innerObject: RichLabel {
|
||||
isEmote: messageDelegate.isEmote
|
||||
Layout.maximumWidth: messageDelegate.bubbleMaxWidth
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: openMessageContext(model, parent.selectedText)
|
||||
innerObject: ColumnLayout {
|
||||
RichLabel {
|
||||
id: label
|
||||
isEmote: messageDelegate.isEmote
|
||||
Layout.maximumWidth: messageDelegate.bubbleMaxWidth
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onLongPressed: openMessageContext(model, parent.selectedText)
|
||||
Loader {
|
||||
id: linkPreviewLoader
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
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
|
||||
|
||||
QQC2.ItemDelegate {
|
||||
id: readMarkerDelegate
|
||||
padding: Kirigami.Units.largeSpacing
|
||||
topInset: Kirigami.Units.largeSpacing
|
||||
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 {
|
||||
text: i18nc("Relative time since the room was last read", "Last read: %1", time)
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@ MouseArea {
|
||||
id: replyButton
|
||||
Layout.fillWidth: true
|
||||
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: {
|
||||
parent.Layout.fillWidth = true;
|
||||
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 {
|
||||
id: replyLeftBorder
|
||||
@@ -79,7 +79,7 @@ MouseArea {
|
||||
id: replyText
|
||||
textMessage: reply.display
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -94,7 +94,7 @@ MouseArea {
|
||||
readonly property string mediaId: isThumbnail ? content.thumbnailMediaId : content.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
|
||||
x: Kirigami.Units.smallSpacing * 3 + replyAvatar.width
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ TextEdit {
|
||||
|
||||
Layout.fillWidth: Config.compactLayout
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
|
||||
persistentSelection: true
|
||||
|
||||
text: "<style>
|
||||
table {
|
||||
@@ -44,6 +46,10 @@ a{
|
||||
text-decoration: none;
|
||||
}
|
||||
" + (!spoilerRevealed ? "
|
||||
[data-mx-spoiler] a {
|
||||
color: transparent;
|
||||
background: " + Kirigami.Theme.textColor + ";
|
||||
}
|
||||
[data-mx-spoiler] {
|
||||
color: transparent;
|
||||
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>") : "")
|
||||
|
||||
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
|
||||
selectByMouse: !Kirigami.Settings.isMobile
|
||||
readOnly: true
|
||||
wrapMode: Text.Wrap
|
||||
textFormat: Text.RichText
|
||||
|
||||
onLinkActivated: RoomManager.openResource(link)
|
||||
onHoveredLinkChanged: if (hoveredLink.length > 0) {
|
||||
onLinkActivated: {
|
||||
spoilerRevealed = true
|
||||
RoomManager.openResource(link)
|
||||
}
|
||||
onHoveredLinkChanged: if (hoveredLink.length > 0 && hoveredLink !== "1") {
|
||||
applicationWindow().hoverLinkIndicator.text = hoveredLink;
|
||||
} else {
|
||||
applicationWindow().hoverLinkIndicator.text = "";
|
||||
|
||||
@@ -7,19 +7,53 @@ import QtQuick.Layouts 1.15
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
|
||||
import org.kde.neochat 1.0
|
||||
import NeoChat.Component 1.0
|
||||
import NeoChat.Dialog 1.0
|
||||
|
||||
Control {
|
||||
x: Kirigami.Units.gridUnit * 1.5 + Kirigami.Units.smallSpacing
|
||||
width: ListView.view.width - Kirigami.Units.largeSpacing - x
|
||||
id: stateDelegate
|
||||
// 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
|
||||
SectionDelegate {
|
||||
id: sectionDelegate
|
||||
width: parent.width
|
||||
anchors.top: parent.top
|
||||
anchors.leftMargin: Kirigami.Units.smallSpacing
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
visible: model.showSection
|
||||
height: visible ? implicitHeight : 0
|
||||
}
|
||||
@@ -27,8 +61,11 @@ Control {
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
height: label.contentHeight
|
||||
width: parent.width
|
||||
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 {
|
||||
id: icon
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtGraphicalEffects 1.12
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
|
||||
@@ -13,19 +12,24 @@ import NeoChat.Component 1.0
|
||||
import NeoChat.Dialog 1.0
|
||||
|
||||
QQC2.ItemDelegate {
|
||||
id: messageDelegate
|
||||
id: timelineContainer
|
||||
default property alias innerObject : column.children
|
||||
// readonly property bool failed: marks == EventStatus.SendingFailed
|
||||
|
||||
property bool isEmote: false
|
||||
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 &&
|
||||
model.author.isLocalUser &&
|
||||
!applicationWindow().wideScreen &&
|
||||
!Config.compactLayout
|
||||
model.author.isLocalUser && !Config.compactLayout
|
||||
|
||||
signal openExternally()
|
||||
signal replyClicked(string eventID)
|
||||
@@ -38,7 +42,10 @@ QQC2.ItemDelegate {
|
||||
|
||||
topPadding: 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
|
||||
|
||||
property Item hoverComponent
|
||||
|
||||
// show hover actions
|
||||
@@ -51,13 +58,39 @@ QQC2.ItemDelegate {
|
||||
// updates the global hover component to point to this delegate, and update its position
|
||||
function updateHoverComponent() {
|
||||
if (hoverComponent) {
|
||||
hoverComponent.delegate = timelineContainer
|
||||
hoverComponent.bubble = bubble
|
||||
hoverComponent.updateFunction = updateHoverComponent;
|
||||
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 {
|
||||
id: sectionDelegate
|
||||
@@ -105,24 +138,21 @@ QQC2.ItemDelegate {
|
||||
|
||||
QQC2.Control {
|
||||
id: bubble
|
||||
topPadding: !Config.compactLayout ? Kirigami.Units.largeSpacing : 0
|
||||
bottomPadding: !Config.compactLayout ? Kirigami.Units.largeSpacing : 0
|
||||
topPadding: Config.compactLayout ? Kirigami.Units.smallSpacing / 2 : Kirigami.Units.largeSpacing
|
||||
bottomPadding: Config.compactLayout ? Kirigami.Units.mediumSpacing / 2 : Kirigami.Units.largeSpacing
|
||||
leftPadding: Kirigami.Units.smallSpacing
|
||||
rightPadding: Config.compactLayout ? Kirigami.Units.largeSpacing : Kirigami.Units.smallSpacing
|
||||
hoverEnabled: true
|
||||
|
||||
// state: Config.compactLayout ? "compactLayout" : "default"
|
||||
state: showUserMessageOnRight ? "userMessageOnRight" : "userMessageOnLeft"
|
||||
|
||||
anchors {
|
||||
top: avatar.top
|
||||
leftMargin: 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
|
||||
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
|
||||
// as setting anchors to undefined did not work reliably
|
||||
states: [
|
||||
@@ -160,7 +190,7 @@ QQC2.ItemDelegate {
|
||||
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.preferredWidth: nameLabel.implicitWidth + timeLabel.implicitWidth + Kirigami.Units.largeSpacing * 2
|
||||
Layout.maximumWidth: bubbleMaxWidth
|
||||
Layout.maximumWidth: contentMaxWidth
|
||||
implicitHeight: visible ? nameLabel.implicitHeight : 0
|
||||
|
||||
QQC2.Label {
|
||||
@@ -168,15 +198,13 @@ QQC2.ItemDelegate {
|
||||
topInset: 0
|
||||
|
||||
visible: model.showAuthor && !isEmote
|
||||
anchors.left: rowLayout.left
|
||||
anchors.right: timeLabel.left
|
||||
anchors.rightMargin: Kirigami.Units.smallSpacing
|
||||
width: Math.min(contentMaxWidth - timeLabel.width, implicitWidth)
|
||||
|
||||
text: visible ? author.displayName : ""
|
||||
textFormat: Text.PlainText
|
||||
font.weight: Font.Bold
|
||||
color: author.color
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideRight
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
@@ -193,10 +221,19 @@ QQC2.ItemDelegate {
|
||||
}
|
||||
QQC2.Label {
|
||||
id: timeLabel
|
||||
anchors.right: rowLayout.right
|
||||
leftPadding: Kirigami.Units.largeSpacing
|
||||
rightPadding: Kirigami.Units.largeSpacing
|
||||
anchors.left: nameLabel.right
|
||||
visible: model.showAuthor && !isEmote
|
||||
text: visible ? time.toLocaleTimeString(Locale.ShortFormat) : ""
|
||||
text: visible ? time.toLocaleTimeString(Qt.locale(), Locale.ShortFormat) : ""
|
||||
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 {
|
||||
@@ -206,6 +243,7 @@ QQC2.ItemDelegate {
|
||||
visible: active
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
Layout.bottomMargin: Config.compactLayout ? 0 : Kirigami.Units.smallSpacing
|
||||
Layout.leftMargin: Config.compactLayout ? 0 : Kirigami.Units.largeSpacing
|
||||
|
||||
Connections {
|
||||
target: replyLoader.item
|
||||
@@ -218,7 +256,7 @@ QQC2.ItemDelegate {
|
||||
|
||||
background: Item {
|
||||
Rectangle {
|
||||
visible: messageDelegate.hovered
|
||||
visible: timelineContainer.hovered
|
||||
color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
|
||||
radius: Kirigami.Units.smallSpacing
|
||||
anchors.fill: parent
|
||||
@@ -250,7 +288,7 @@ QQC2.ItemDelegate {
|
||||
left: bubble.left
|
||||
right: parent.right
|
||||
top: bubble.bottom
|
||||
topMargin: active && !Config.compactLayout ? Kirigami.Units.smallSpacing : 0
|
||||
topMargin: active && Config.compactLayout ? 0 : Kirigami.Units.smallSpacing
|
||||
}
|
||||
height: active ? item.implicitHeight : 0
|
||||
//Layout.bottomMargin: readMarker ? Kirigami.Units.smallSpacing : 0
|
||||
@@ -258,4 +296,14 @@ QQC2.ItemDelegate {
|
||||
visible: active
|
||||
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.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtGraphicalEffects 1.15
|
||||
import QtMultimedia 5.15
|
||||
import Qt.labs.platform 1.1 as Platform
|
||||
|
||||
@@ -18,8 +17,6 @@ import NeoChat.Menu.Timeline 1.0
|
||||
TimelineContainer {
|
||||
id: videoDelegate
|
||||
|
||||
width: ListView.view.width
|
||||
|
||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||
hoverComponent: hoverActions
|
||||
|
||||
@@ -29,6 +26,8 @@ TimelineContainer {
|
||||
property bool supportStreaming: true
|
||||
readonly property int maxWidth: 1000 // TODO messageListView.width
|
||||
|
||||
onOpenContextMenu: openFileContext(model, vid)
|
||||
|
||||
onDownloadedChanged: {
|
||||
if (downloaded) {
|
||||
vid.source = progressInfo.localPath
|
||||
@@ -43,7 +42,7 @@ TimelineContainer {
|
||||
innerObject: Video {
|
||||
id: vid
|
||||
|
||||
Layout.maximumWidth: videoDelegate.bubbleMaxWidth
|
||||
Layout.maximumWidth: videoDelegate.contentMaxWidth
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumHeight: Kirigami.Units.gridUnit * 15
|
||||
Layout.minimumHeight: Kirigami.Units.gridUnit * 5
|
||||
@@ -124,16 +123,6 @@ TimelineContainer {
|
||||
videoDelegate.downloadAndPlay()
|
||||
}
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: openFileContext(model, parent)
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onLongPressed: openFileContext(model, parent)
|
||||
}
|
||||
}
|
||||
|
||||
function downloadAndPlay() {
|
||||
|
||||
@@ -12,3 +12,4 @@ EncryptedDelegate 1.0 EncryptedDelegate.qml
|
||||
EventDelegate 1.0 EventDelegate.qml
|
||||
MessageDelegate 1.0 MessageDelegate.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 {
|
||||
id: root
|
||||
|
||||
signal closed()
|
||||
|
||||
property var room
|
||||
property var user
|
||||
|
||||
@@ -52,7 +54,7 @@ Kirigami.OverlaySheet {
|
||||
|
||||
onClicked: {
|
||||
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 {}
|
||||
}
|
||||
}
|
||||
|
||||
onSheetOpenChanged: {
|
||||
if (!sheetOpen) {
|
||||
closed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,3 +7,4 @@ OpenFileDialog 1.0 OpenFileDialog.qml
|
||||
ImageClipboardDialog 1.0 ImageClipboardDialog.qml
|
||||
StartChatDialog 1.0 StartChatDialog.qml
|
||||
EmojiDialog 1.0 EmojiDialog.qml
|
||||
KeyVerificationDialog 1.0 KeyVerificationDialog.qml
|
||||
|
||||
@@ -45,7 +45,7 @@ Labs.MenuBar {
|
||||
Labs.MenuItem {
|
||||
text: i18nc("menu", "New Private Chat…")
|
||||
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 {
|
||||
text: i18nc("menu", "New Group…")
|
||||
@@ -58,7 +58,7 @@ Labs.MenuBar {
|
||||
}
|
||||
Labs.MenuItem {
|
||||
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 {
|
||||
@@ -92,7 +92,7 @@ Labs.MenuBar {
|
||||
|
||||
Labs.MenuItem {
|
||||
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 {
|
||||
MenuItem {
|
||||
id: newWindow
|
||||
text: i18n("Open in new window")
|
||||
text: i18n("Open in New Window")
|
||||
onTriggered: RoomManager.openWindow(room);
|
||||
visible: !Kirigami.Settings.isMobile
|
||||
}
|
||||
@@ -49,7 +49,7 @@ Loader {
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: i18nc("@action:inmenu", "Copy address to clipboard")
|
||||
text: i18nc("@action:inmenu", "Copy Address to Clipboard")
|
||||
onTriggered: if (room.canonicalAlias.length === 0) {
|
||||
Clipboard.saveText(room.id)
|
||||
} 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 {}
|
||||
|
||||
MenuItem {
|
||||
|
||||
@@ -14,6 +14,8 @@ import NeoChat.Menu 1.0
|
||||
MessageDelegateContextMenu {
|
||||
id: root
|
||||
|
||||
signal closeFullscreen
|
||||
|
||||
required property var file
|
||||
required property var progressInfo
|
||||
required property string mimeType
|
||||
@@ -24,13 +26,13 @@ MessageDelegateContextMenu {
|
||||
icon.name: "document-open"
|
||||
onTriggered: {
|
||||
if (file.downloaded) {
|
||||
if (!Qt.openUrlExternally(progressInfo.localPath)) {
|
||||
Qt.openUrlExternally(progressInfo.localDir);
|
||||
if (!UrlHelper.openUrl(progressInfo.localPath)) {
|
||||
UrlHelper.openUrl(progressInfo.localDir);
|
||||
}
|
||||
} else {
|
||||
file.onDownloadedChanged.connect(function() {
|
||||
if (!Qt.openUrlExternally(progressInfo.localPath)) {
|
||||
Qt.openUrlExternally(progressInfo.localDir);
|
||||
if (!UrlHelper.openUrl(progressInfo.localPath)) {
|
||||
UrlHelper.openUrl(progressInfo.localDir);
|
||||
}
|
||||
});
|
||||
currentRoom.downloadFile(eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId))
|
||||
@@ -50,7 +52,8 @@ MessageDelegateContextMenu {
|
||||
text: i18n("Reply")
|
||||
icon.name: "mail-replied-symbolic"
|
||||
onTriggered: {
|
||||
ChatBoxHelper.replyToMessage(eventId, message, author);
|
||||
chatBoxHelper.replyToMessage(eventId, message, author);
|
||||
root.closeFullscreen()
|
||||
}
|
||||
},
|
||||
Kirigami.Action {
|
||||
@@ -60,8 +63,18 @@ MessageDelegateContextMenu {
|
||||
icon.color: "red"
|
||||
onTriggered: {
|
||||
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 {
|
||||
text: i18n("View Source")
|
||||
icon.name: "code-context"
|
||||
@@ -72,6 +85,7 @@ MessageDelegateContextMenu {
|
||||
title: i18n("Message Source"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
});
|
||||
root.closeFullscreen()
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -20,6 +20,7 @@ Loader {
|
||||
property string formattedBody: ""
|
||||
required property string source
|
||||
property string selectedText: ""
|
||||
required property string plainMessage
|
||||
|
||||
property list<Kirigami.Action> nestedActions
|
||||
|
||||
@@ -27,13 +28,13 @@ Loader {
|
||||
Kirigami.Action {
|
||||
text: i18n("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")
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18n("Reply")
|
||||
icon.name: "mail-replied-symbolic"
|
||||
onTriggered: ChatBoxHelper.replyToMessage(eventId, message, author);
|
||||
onTriggered: chatBoxHelper.replyToMessage(eventId, message, author);
|
||||
},
|
||||
Kirigami.Action {
|
||||
visible: author.id === currentRoom.localUser.id || currentRoom.canSendState("redact")
|
||||
@@ -45,7 +46,16 @@ Loader {
|
||||
Kirigami.Action {
|
||||
text: i18n("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 {
|
||||
text: i18n("View Source")
|
||||
@@ -111,7 +121,7 @@ Loader {
|
||||
Instantiator {
|
||||
model: WebShortcutModel {
|
||||
id: webshortcutmodel
|
||||
selectedText: loadRoot.selectedText ? loadRoot.selectedText : loadRoot.message
|
||||
selectedText: loadRoot.selectedText ? loadRoot.selectedText : loadRoot.plainMessage
|
||||
onOpenUrl: RoomManager.visitNonMatrix(url)
|
||||
}
|
||||
delegate: QQC2.MenuItem {
|
||||
|
||||
@@ -22,6 +22,10 @@ Kirigami.Page {
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
contentWidth: availableWidth
|
||||
|
||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
TextArea {
|
||||
id: sourceTextArea
|
||||
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
|
||||
FileDelegateContextMenu 1.0 FileDelegateContextMenu.qml
|
||||
MessageSourceSheet 1.0 MessageSourceSheet.qml
|
||||
ReportSheet 1.0 ReportSheet.qml
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtGraphicalEffects 1.15
|
||||
import Qt.labs.platform 1.1 as Platform
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
|
||||
@@ -3,18 +3,11 @@
|
||||
|
||||
import QtQuick.Layouts 1.15
|
||||
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 {
|
||||
title: i18n("Loading…")
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
Kirigami.LoadingPlaceholder {
|
||||
id: loadingIndicator
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
title: i18n("Rooms")
|
||||
@@ -22,21 +111,10 @@ Kirigami.ScrollablePage {
|
||||
property var enteredRoom
|
||||
property bool collapsedMode: Config.roomListPageWidth === applicationWindow().collapsedPageWidth && applicationWindow().shouldUseSidebars
|
||||
|
||||
verticalScrollBarPolicy: collapsedMode ? QQC2.ScrollBar.AlwaysOff : QQC2.ScrollBar.AsNeeded
|
||||
|
||||
onCollapsedModeChanged: if (collapsedMode) {
|
||||
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 {
|
||||
@@ -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 {
|
||||
id: listView
|
||||
|
||||
activeFocusOnTab: true
|
||||
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 {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - (Kirigami.Units.largeSpacing * 4)
|
||||
@@ -240,14 +319,12 @@ Kirigami.ScrollablePage {
|
||||
subtitle: subtitleText
|
||||
subtitleItem.textFormat: Text.PlainText
|
||||
onPressAndHold: {
|
||||
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()
|
||||
createRoomListContextMenu()
|
||||
}
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
acceptedDevices: PointerDevice.Mouse
|
||||
onTapped: createRoomListContextMenu()
|
||||
}
|
||||
|
||||
leading: Kirigami.Avatar {
|
||||
@@ -261,15 +338,17 @@ Kirigami.ScrollablePage {
|
||||
|
||||
trailing: RowLayout {
|
||||
QQC2.Label {
|
||||
text: notificationCount
|
||||
visible: notificationCount > 0
|
||||
text: notificationCount > 0 ? notificationCount : "●"
|
||||
visible: unreadCount > 0
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
color: highlightCount > 0 ? "white" : Kirigami.Theme.textColor
|
||||
color: Kirigami.Theme.textColor
|
||||
Layout.minimumWidth: height
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
background: Rectangle {
|
||||
visible: notificationCount > 0
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -282,18 +361,22 @@ Kirigami.ScrollablePage {
|
||||
id: optionAction
|
||||
icon.name: "configure"
|
||||
onTriggered: {
|
||||
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()
|
||||
createRoomListContextMenu()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.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.neochat 1.0
|
||||
@@ -23,6 +23,7 @@ Kirigami.ScrollablePage {
|
||||
|
||||
/// It's not readonly because of the seperate window view.
|
||||
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
|
||||
property bool hasScrolledUpBefore: false;
|
||||
|
||||
@@ -52,7 +53,26 @@ Kirigami.ScrollablePage {
|
||||
|
||||
onCurrentRoomChanged: {
|
||||
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 {
|
||||
@@ -61,6 +81,10 @@ Kirigami.ScrollablePage {
|
||||
connection: Controller.activeConnection
|
||||
}
|
||||
|
||||
ChatBoxHelper {
|
||||
id: chatBoxHelper
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
onActivated: applicationWindow().pageStack.get(0).forceActiveFocus()
|
||||
@@ -81,17 +105,17 @@ Kirigami.ScrollablePage {
|
||||
function onShowMessage(messageType, message) {
|
||||
page.header.contentItem.text = message;
|
||||
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 {
|
||||
height: visible ? implicitHeight : 0
|
||||
visible: contentItem.visible
|
||||
visible: false
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
contentItem: Kirigami.InlineMessage {
|
||||
showCloseButton: true
|
||||
visible: false
|
||||
visible: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,15 +144,10 @@ Kirigami.ScrollablePage {
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
Kirigami.LoadingPlaceholder {
|
||||
id: loadingIndicator
|
||||
anchors.centerIn: parent
|
||||
visible: page.currentRoom === null || (messageListView.count === 0 && !page.currentRoom.allHistoryLoaded && !page.currentRoom.isInvite)
|
||||
text: i18n("Loading…")
|
||||
QQC2.BusyIndicator {
|
||||
running: loadingIndicator.visible
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
visible: loading
|
||||
}
|
||||
|
||||
focus: true
|
||||
@@ -147,9 +166,9 @@ Kirigami.ScrollablePage {
|
||||
|
||||
Keys.onPressed: {
|
||||
if (event.key === Qt.Key_PageDown && (event.modifiers & Qt.ControlModifier)) {
|
||||
switchRoomUp();
|
||||
} else if (event.key === Qt.Key_PageUp && (event.modifiers & Qt.ControlModifier)) {
|
||||
switchRoomDown();
|
||||
} else if (event.key === Qt.Key_PageUp && (event.modifiers & Qt.ControlModifier)) {
|
||||
switchRoomUp();
|
||||
} else if (!(event.modifiers & Qt.ControlModifier) && event.key < Qt.Key_Escape) {
|
||||
event.accepted = true;
|
||||
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
|
||||
Connections {
|
||||
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 {
|
||||
id: collapseStateProxyModel
|
||||
sourceModel: sortedMessageEventModel
|
||||
@@ -316,7 +270,7 @@ Kirigami.ScrollablePage {
|
||||
fileDialog.chosen.connect(function(path) {
|
||||
if (!path) return
|
||||
|
||||
ChatBoxHelper.attachmentPath = path;
|
||||
chatBoxHelper.attachmentPath = path;
|
||||
})
|
||||
|
||||
fileDialog.open()
|
||||
@@ -338,7 +292,7 @@ Kirigami.ScrollablePage {
|
||||
if (!Clipboard.saveImage(localPath)) {
|
||||
return;
|
||||
}
|
||||
ChatBoxHelper.attachmentPath = localPath;
|
||||
chatBoxHelper.attachmentPath = localPath;
|
||||
attachDialog.close();
|
||||
}
|
||||
}
|
||||
@@ -413,7 +367,7 @@ Kirigami.ScrollablePage {
|
||||
DropArea {
|
||||
id: dropAreaFile
|
||||
anchors.fill: parent
|
||||
onDropped: ChatBoxHelper.attachmentPath = drop.urls[0]
|
||||
onDropped: chatBoxHelper.attachmentPath = drop.urls[0]
|
||||
}
|
||||
|
||||
QQC2.Pane {
|
||||
@@ -462,7 +416,7 @@ Kirigami.ScrollablePage {
|
||||
currentRoom.usersTyping.length,
|
||||
currentRoom.usersTyping.map(user => user.displayName).join(", ")
|
||||
) : ""
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
height: visible ? implicitHeight : 0
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
@@ -478,6 +432,100 @@ Kirigami.ScrollablePage {
|
||||
function goToEvent(eventID) {
|
||||
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: {
|
||||
const targetMessage = messageEventModel.getLastLocalUserMessageEventId();
|
||||
if (targetMessage) {
|
||||
ChatBoxHelper.edit(targetMessage["body"], targetMessage["body"], targetMessage["event_id"]);
|
||||
chatBoxHelper.edit(targetMessage["message"], targetMessage["formattedBody"], targetMessage["event_id"]);
|
||||
chatBox.focusInputField();
|
||||
}
|
||||
}
|
||||
onReplyPreviousUserMessage: {
|
||||
const replyResponse = messageEventModel.getLatestMessageFromIndex(0);
|
||||
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) {
|
||||
page.header.contentItem.text = `${title}<br />${message}`;
|
||||
page.header.contentItem.type = Kirigami.MessageType.Warning;
|
||||
page.header.contentItem.visible = true;
|
||||
page.header.visible = true;
|
||||
}
|
||||
|
||||
function showUserDetail(user) {
|
||||
@@ -588,6 +636,14 @@ Kirigami.ScrollablePage {
|
||||
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
|
||||
function openFileContext(event, file) {
|
||||
const contextMenu = fileDelegateContextMenu.createObject(page, {
|
||||
@@ -598,12 +654,13 @@ Kirigami.ScrollablePage {
|
||||
file: file,
|
||||
mimeType: event.mimeType,
|
||||
progressInfo: event.progressInfo,
|
||||
plainMessage: event.message,
|
||||
});
|
||||
contextMenu.open();
|
||||
}
|
||||
|
||||
/// Open context menu for normal message
|
||||
function openMessageContext(event, selectedText) {
|
||||
function openMessageContext(event, selectedText, plainMessage) {
|
||||
const contextMenu = messageDelegateContextMenu.createObject(page, {
|
||||
selectedText: selectedText,
|
||||
author: event.author,
|
||||
@@ -611,7 +668,8 @@ Kirigami.ScrollablePage {
|
||||
eventId: event.eventId,
|
||||
formattedBody: event.formattedBody,
|
||||
source: event.source,
|
||||
eventType: event.eventType
|
||||
eventType: event.eventType,
|
||||
plainMessage: plainMessage,
|
||||
});
|
||||
contextMenu.open();
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@ Kirigami.OverlayDrawer {
|
||||
id: roomDrawer
|
||||
readonly property var room: RoomManager.currentRoom
|
||||
|
||||
width: modal ? undefined : actualWidth
|
||||
width: actualWidth
|
||||
|
||||
readonly property int minWidth: Kirigami.Units.gridUnit * 15
|
||||
readonly property int maxWidth: Kirigami.Units.gridUnit * 25
|
||||
readonly property int defaultWidth: Kirigami.Units.gridUnit * 20
|
||||
@@ -62,56 +63,84 @@ Kirigami.OverlayDrawer {
|
||||
|
||||
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
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
contentItem: Loader {
|
||||
id: loader
|
||||
active: roomDrawer.drawerOpen
|
||||
sourceComponent: ColumnLayout {
|
||||
id: columnLayout
|
||||
property alias userSearchText: userListSearchField.text
|
||||
property alias highlightedUser: userListView.currentIndex
|
||||
spacing: 0
|
||||
|
||||
Kirigami.AbstractApplicationHeader {
|
||||
Layout.fillWidth: true
|
||||
topPadding: Kirigami.Units.smallSpacing / 2;
|
||||
bottomPadding: Kirigami.Units.smallSpacing / 2;
|
||||
rightPadding: Kirigami.Units.smallSpacing
|
||||
leftPadding: Kirigami.Units.smallSpacing
|
||||
rightPadding: Kirigami.Units.largeSpacing
|
||||
leftPadding: Kirigami.Units.largeSpacing
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.fillWidth: true
|
||||
text: i18n("Room information")
|
||||
level: 1
|
||||
}
|
||||
ToolButton {
|
||||
id: inviteButton
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
icon.name: "list-add-user"
|
||||
text: i18n("Invite")
|
||||
text: i18n("Invite user to room")
|
||||
display: AbstractButton.IconOnly
|
||||
|
||||
onClicked: {
|
||||
applicationWindow().pageStack.layers.push("qrc:/imports/NeoChat/Page/InviteUserPage.qml", {room: room})
|
||||
roomDrawer.close();
|
||||
}
|
||||
}
|
||||
Item {
|
||||
// HACK otherwise rating item is not right aligned
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
ToolTip {
|
||||
text: inviteButton.text
|
||||
}
|
||||
}
|
||||
ToolButton {
|
||||
id: favouriteButton
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
icon.name: room && room.isFavourite ? "rating" : "rating-unrated"
|
||||
checkable: true
|
||||
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)
|
||||
|
||||
ToolTip {
|
||||
text: room && room.isFavourite ? i18n("Remove room from favorites") : i18n("Make room favorite")
|
||||
text: favouriteButton.text
|
||||
}
|
||||
}
|
||||
ToolButton {
|
||||
id: settingsButton
|
||||
|
||||
Layout.alignment: Qt.AlignRight
|
||||
icon.name: 'settings-configure'
|
||||
text: i18n("Room settings")
|
||||
display: AbstractButton.IconOnly
|
||||
|
||||
onClicked: ApplicationWindow.window.pageStack.pushDialogLayer('qrc:/imports/NeoChat/RoomSettings/Categories.qml', {room: room})
|
||||
|
||||
ToolTip {
|
||||
text: i18n("Room settings")
|
||||
text: settingsButton.text
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,14 +149,11 @@ Kirigami.OverlayDrawer {
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: Kirigami.Units.largeSpacing
|
||||
Kirigami.Heading {
|
||||
text: i18n("Room information")
|
||||
level: 3
|
||||
}
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: Kirigami.Units.largeSpacing
|
||||
|
||||
Layout.leftMargin: Kirigami.Units.smallSpacing
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
Kirigami.Avatar {
|
||||
@@ -144,10 +170,9 @@ Kirigami.OverlayDrawer {
|
||||
spacing: 0
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.maximumWidth: Kirigami.Units.gridUnit * 9
|
||||
Layout.fillWidth: true
|
||||
level: 1
|
||||
font.bold: true
|
||||
type: Kirigami.Heading.Type.Primary
|
||||
wrapMode: Label.Wrap
|
||||
text: room ? room.displayName : i18n("No name")
|
||||
textFormat: Text.PlainText
|
||||
@@ -158,6 +183,8 @@ Kirigami.OverlayDrawer {
|
||||
wrapMode: Text.WordWrap
|
||||
selectByMouse: true
|
||||
color: Kirigami.Theme.textColor
|
||||
selectedTextColor: Kirigami.Theme.highlightedTextColor
|
||||
selectionColor: Kirigami.Theme.highlightColor
|
||||
readOnly: true
|
||||
text: room && room.canonicalAlias ? room.canonicalAlias : i18n("No Canonical Alias")
|
||||
}
|
||||
@@ -172,7 +199,9 @@ Kirigami.OverlayDrawer {
|
||||
wrapMode: Text.WordWrap
|
||||
selectByMouse: true
|
||||
color: Kirigami.Theme.textColor
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
selectedTextColor: Kirigami.Theme.highlightedTextColor
|
||||
selectionColor: Kirigami.Theme.highlightColor
|
||||
onLinkActivated: UrlHelper.openUrl(link)
|
||||
readOnly: true
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
@@ -185,16 +214,23 @@ Kirigami.OverlayDrawer {
|
||||
Kirigami.ListSectionHeader {
|
||||
label: i18n("Members")
|
||||
activeFocusOnTab: false
|
||||
|
||||
Label {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: room ? i18np("%1 Member", "%1 Members", room.joinedCount) : i18n("No Member Count")
|
||||
}
|
||||
}
|
||||
|
||||
Pane {
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
implicitWidth: parent.width
|
||||
z: 2
|
||||
Control {
|
||||
Layout.fillWidth: true
|
||||
|
||||
// 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 {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
Kirigami.Theme.inherit: false
|
||||
@@ -202,6 +238,7 @@ Kirigami.OverlayDrawer {
|
||||
}
|
||||
contentItem: Kirigami.SearchField {
|
||||
id: userListSearchField
|
||||
|
||||
onAccepted: sortedMessageEventModel.filterString = text;
|
||||
}
|
||||
}
|
||||
@@ -210,11 +247,12 @@ Kirigami.OverlayDrawer {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
ListView {
|
||||
id: userListView
|
||||
clip: true
|
||||
headerPositioning: ListView.OverlayHeader
|
||||
boundsBehavior: Flickable.DragOverBounds
|
||||
activeFocusOnTab: true
|
||||
|
||||
model: KSortFilterProxyModel {
|
||||
@@ -229,58 +267,50 @@ Kirigami.OverlayDrawer {
|
||||
filterCaseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
|
||||
delegate: Kirigami.AbstractListItem {
|
||||
width: userListView.width
|
||||
delegate: Kirigami.BasicListItem {
|
||||
id: userListItem
|
||||
|
||||
implicitHeight: Kirigami.Units.gridUnit * 2
|
||||
z: 1
|
||||
leftPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
|
||||
|
||||
contentItem: RowLayout {
|
||||
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: name
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: name
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
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
|
||||
}
|
||||
onClicked: {
|
||||
const popup = userDetailDialog.createObject(ApplicationWindow.overlay, {room: room, user: user, displayName: name, avatarMediaId: avatar})
|
||||
popup.closed.connect(function() {
|
||||
userListItem.highlighted = false
|
||||
})
|
||||
popup.open()
|
||||
}
|
||||
|
||||
action: Kirigami.Action {
|
||||
onTriggered: userDetailDialog.createObject(ApplicationWindow.overlay, {"room": room, "user": user, "displayName": name, "avatarMediaId": avatar}).open()
|
||||
leading: Kirigami.Avatar {
|
||||
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: {
|
||||
if (loader.active) {
|
||||
loader.item.userSearchText = ""
|
||||
loader.item.highlightedUser = -1
|
||||
}
|
||||
if (room == null) {
|
||||
close()
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
import QtQuick 2.15
|
||||
import org.kde.kirigami 2.18 as Kirigami
|
||||
import QtQuick.Controls 2.15 as Controls
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
Kirigami.CategorizedSettings {
|
||||
id: root
|
||||
property var room
|
||||
|
||||
objectName: "settingsPage"
|
||||
actions: [
|
||||
Kirigami.SettingAction {
|
||||
@@ -30,6 +30,16 @@ Kirigami.CategorizedSettings {
|
||||
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 canChangeCanonicalAlias: room.canSendState("m.room.canonical_alias")
|
||||
|
||||
title: i18n('General')
|
||||
title: i18n("General")
|
||||
|
||||
ColumnLayout {
|
||||
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
|
||||
|
||||
title: i18n('Security')
|
||||
title: i18n("Security")
|
||||
|
||||
ColumnLayout {
|
||||
Kirigami.FormLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
CheckBox {
|
||||
RadioButton {
|
||||
text: i18nc("@option:check", "Private (invite only)")
|
||||
Kirigami.FormData.label: i18nc("@option:check", "Access:")
|
||||
checked: room.joinRule === "invite"
|
||||
@@ -32,7 +32,7 @@ Kirigami.ScrollablePage {
|
||||
text: i18n("Only invited people can join.")
|
||||
font: Kirigami.Theme.smallFont
|
||||
}
|
||||
CheckBox {
|
||||
RadioButton {
|
||||
text: i18nc("@option:check", "Space members")
|
||||
checked: room.joinRule === "restricted"
|
||||
enabled: false
|
||||
@@ -41,7 +41,7 @@ Kirigami.ScrollablePage {
|
||||
text: i18n("Anyone in a space can find and join.")
|
||||
font: Kirigami.Theme.smallFont
|
||||
}
|
||||
CheckBox {
|
||||
RadioButton {
|
||||
text: i18nc("@option:check", "Public")
|
||||
checked: room.joinRule === "public"
|
||||
enabled: false
|
||||
|
||||
@@ -7,6 +7,6 @@ import org.kde.kirigami 2.15 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
Kirigami.AboutPage {
|
||||
title: i18nc('@title:window', 'About NeoChat')
|
||||
title: i18nc("@title:window", "About NeoChat")
|
||||
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 {
|
||||
model: AccountRegistry
|
||||
delegate: Kirigami.SwipeListItem {
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
Kirigami.BasicListItem {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.fill: parent
|
||||
delegate: Kirigami.BasicListItem {
|
||||
text: model.connection.localUser.displayName
|
||||
labelItem.textFormat: Text.PlainText
|
||||
subtitle: model.connection.localUserId
|
||||
icon: model.connection.localUser.avatarMediaId ? ("image://mxc/" + model.connection.localUser.avatarMediaId) : "im-user"
|
||||
|
||||
text: model.connection.localUser.displayName
|
||||
labelItem.textFormat: Text.PlainText
|
||||
subtitle: model.connection.localUserId
|
||||
icon: model.connection.localUser.avatarMediaId ? ("image://mxc/" + model.connection.localUser.avatarMediaId) : "im-user"
|
||||
onClicked: {
|
||||
Controller.activeConnection = model.connection;
|
||||
pageStack.layers.pop();
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
Controller.activeConnection = model.connection
|
||||
pageStack.layers.pop()
|
||||
trailing: RowLayout {
|
||||
Controls.ToolButton {
|
||||
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 {
|
||||
target: Controller
|
||||
function onConnectionAdded() {
|
||||
@@ -83,134 +93,21 @@ Kirigami.ScrollablePage {
|
||||
pageStack.layers.pop()
|
||||
}
|
||||
function onPasswordStatus(status) {
|
||||
if(status == Controller.Success)
|
||||
showPassiveNotification(i18n("Password changed successfully"))
|
||||
else if(status == Controller.Wrong)
|
||||
showPassiveNotification(i18n("Wrong password entered"))
|
||||
else
|
||||
showPassiveNotification(i18n("Unknown problem while trying to change password"))
|
||||
if (status === Controller.Success) {
|
||||
showPassiveNotification(i18n("Password changed successfully"));
|
||||
} else if (status === Controller.Wrong) {
|
||||
showPassiveNotification(i18n("Wrong password entered"));
|
||||
} else {
|
||||
showPassiveNotification(i18n("Unknown problem while trying to change password"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
property Component openFileDialog: Component {
|
||||
id: openFileDialog
|
||||
|
||||
OpenFileDialog {
|
||||
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
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
title: i18nc('@title:window', 'Appearance')
|
||||
title: i18nc("@title:window", "Appearance")
|
||||
ColumnLayout {
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
@@ -177,7 +177,7 @@ Kirigami.ScrollablePage {
|
||||
Kirigami.FormLayout {
|
||||
Layout.maximumWidth: parent.width
|
||||
QQC2.CheckBox {
|
||||
Kirigami.FormData.label: "Show Avatar:"
|
||||
Kirigami.FormData.label: i18n("Show Avatar:")
|
||||
text: i18n("In Chat")
|
||||
checked: Config.showAvatarInTimeline
|
||||
onToggled: {
|
||||
@@ -237,6 +237,7 @@ Kirigami.ScrollablePage {
|
||||
HoverHandler { id: sliderHover }
|
||||
QQC2.ToolTip.visible: sliderHover.hovered && !enabled
|
||||
QQC2.ToolTip.text: i18n("Only enabled if the transparent chat page is enabled.")
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
QQC2.Label {
|
||||
text: Math.round(Config.transparency * 100) + "%"
|
||||
@@ -245,7 +246,7 @@ Kirigami.ScrollablePage {
|
||||
QQC2.CheckBox {
|
||||
text: i18n("Show your messages on the right")
|
||||
checked: Config.showLocalMessagesOnRight
|
||||
enabled: !Config.isShowLocalMessagesOnRightImmutable
|
||||
enabled: !Config.isShowLocalMessagesOnRightImmutable && !Config.compactLayout
|
||||
onToggled: {
|
||||
Config.showLocalMessagesOnRight = checked
|
||||
Config.save()
|
||||
|
||||
@@ -5,7 +5,7 @@ import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as Controls
|
||||
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
|
||||
|
||||
@@ -17,45 +17,53 @@ Kirigami.ScrollablePage {
|
||||
id: devices
|
||||
}
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
visible: parent.model.count === 0 // We can assume 0 means loading since there is at least one device
|
||||
anchors.fill: parent
|
||||
|
||||
Kirigami.LoadingPlaceholder {
|
||||
visible: parent.count === 0 // We can assume 0 means loading since there is at least one device
|
||||
anchors.centerIn: parent
|
||||
text: i18n("Loading…")
|
||||
Controls.BusyIndicator {
|
||||
running: parent.visible
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Kirigami.SwipeListItem {
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
Kirigami.BasicListItem {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
text: model.displayName
|
||||
subtitle: model.id
|
||||
icon: "network-connect"
|
||||
}
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
text: i18n("Edit device name")
|
||||
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()
|
||||
delegate: Kirigami.BasicListItem {
|
||||
text: model.displayName
|
||||
subtitle: model.id
|
||||
icon: "network-connect"
|
||||
trailing: RowLayout {
|
||||
Controls.ToolButton {
|
||||
display: Controls.AbstractButton.IconOnly
|
||||
action: Kirigami.Action {
|
||||
text: i18n("Edit device name")
|
||||
iconName: "document-edit"
|
||||
onTriggered: {
|
||||
renameSheet.index = model.index
|
||||
renameSheet.name = model.displayName
|
||||
renameSheet.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.Layouts 1.15
|
||||
|
||||
import Qt.labs.platform 1.1
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
|
||||
import org.kde.neochat 1.0
|
||||
@@ -14,12 +16,13 @@ import NeoChat.Component 1.0 as Components
|
||||
import NeoChat.Dialog 1.0
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
title: i18nc('@title:window', 'Custom Emojis')
|
||||
title: i18nc("@title:window", "Custom Emojis")
|
||||
|
||||
ListView {
|
||||
anchors.fill: parent
|
||||
|
||||
model: CustomEmojiModel {
|
||||
id: emojiModel
|
||||
|
||||
connection: Controller.activeConnection
|
||||
}
|
||||
|
||||
@@ -96,11 +99,10 @@ Kirigami.ScrollablePage {
|
||||
this.fileDialog = openFileDialog.createObject(QQC2.Overlay.overlay)
|
||||
|
||||
this.fileDialog.chosen.connect((url) => {
|
||||
emojiModel.addEmoji(emojiField.text, url)
|
||||
emojiModel.addEmoji(emojiCreator.name, url)
|
||||
this.fileDialog = null
|
||||
})
|
||||
this.fileDialog.onRejected.connect(() => {
|
||||
rej()
|
||||
this.fileDialog = null
|
||||
})
|
||||
this.fileDialog.open()
|
||||
|
||||
@@ -11,9 +11,10 @@ import org.kde.kirigami 2.15 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
Kirigami.ScrollablePage {
|
||||
title: i18nc('@title:window', 'General')
|
||||
title: i18nc("@title:window", "General")
|
||||
ColumnLayout {
|
||||
Kirigami.FormLayout {
|
||||
Layout.fillWidth: true
|
||||
QQC2.CheckBox {
|
||||
Kirigami.FormData.label: i18n("General settings:")
|
||||
text: i18n("Close to system tray")
|
||||
@@ -45,6 +46,7 @@ Kirigami.ScrollablePage {
|
||||
onToggled: {
|
||||
Config.showNotifications = checked
|
||||
Config.save()
|
||||
NotificationsManager.globalNotificationsEnabled = checked
|
||||
}
|
||||
}
|
||||
QQC2.CheckBox {
|
||||
@@ -96,19 +98,38 @@ Kirigami.ScrollablePage {
|
||||
QQC2.CheckBox {
|
||||
id: quickEditCheckbox
|
||||
Layout.maximumWidth: parent.width
|
||||
contentItem: QQC2.Label {
|
||||
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
|
||||
}
|
||||
text: i18n("Use s/text/replacement syntax to edit your last message")
|
||||
checked: Config.allowQuickEdit
|
||||
enabled: !Config.isAllowQuickEditImmutable
|
||||
onToggled: {
|
||||
Config.allowQuickEdit = checked
|
||||
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 org.kde.kirigami 2.18 as Kirigami
|
||||
import QtQuick.Controls 2.15 as Controls
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
Kirigami.CategorizedSettings {
|
||||
@@ -25,7 +24,7 @@ Kirigami.CategorizedSettings {
|
||||
page: Qt.resolvedUrl("AccountsPage.qml")
|
||||
},
|
||||
Kirigami.SettingAction {
|
||||
text: i18n("Custom Emoji")
|
||||
text: i18n("Custom Emojis")
|
||||
icon.name: "preferences-desktop-emoticons"
|
||||
page: Qt.resolvedUrl("Emoticons.qml")
|
||||
},
|
||||
|
||||
@@ -47,7 +47,7 @@ Kirigami.Page {
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
title: i18nc('@window:title', 'Spellchecking')
|
||||
title: i18nc("@window:title", "Spellchecking")
|
||||
|
||||
QQC2.Dialog {
|
||||
id: applyDialog
|
||||
@@ -190,6 +190,10 @@ Kirigami.Page {
|
||||
Layout.fillHeight: true
|
||||
enabled: autodetectLanguageCheckbox.checked
|
||||
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 {
|
||||
clip: true
|
||||
model: settings.dictionaryModel
|
||||
@@ -254,6 +258,10 @@ Kirigami.Page {
|
||||
}
|
||||
QQC2.ScrollView {
|
||||
anchors.fill: parent
|
||||
|
||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||
|
||||
ListView {
|
||||
model: settings.currentIgnoreList
|
||||
delegate: Kirigami.BasicListItem {
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<name xml:lang="sl">NeoChat</name>
|
||||
<name xml:lang="sv">NeoChat</name>
|
||||
<name xml:lang="ta">நியோச்சாட்</name>
|
||||
<name xml:lang="tr">NeoChat</name>
|
||||
<name xml:lang="uk">NeoChat</name>
|
||||
<name xml:lang="x-test">xxNeoChatxx</name>
|
||||
<name xml:lang="zh-CN">NeoChat</name>
|
||||
@@ -43,7 +44,7 @@
|
||||
<summary xml:lang="ar">عميل لماتركس، ميفاق الاتصال اللامركزي</summary>
|
||||
<summary xml:lang="az">Matrix üçün müştəri, mərkəzləşməmiş kommunikasiya protokolu</summary>
|
||||
<summary xml:lang="ca">Un client per al Matrix, el protocol de comunicacions descentralitzat</summary>
|
||||
<summary xml:lang="ca-valencia">Un client per 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="de">Ein Programm für Matrix, das dezentrale Kommunikationsprotokoll</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="sl">Odjemalec za matrix, decentralizirani komunikacijski protokol</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="x-test">xxA client for matrix, the decentralized communication protocolxx</summary>
|
||||
<summary xml:lang="zh-CN">分布式通讯协议 Matrix 的客户端</summary>
|
||||
@@ -73,7 +75,7 @@
|
||||
<p xml:lang="ar">نيوتشات هو عميل ماتركس Matrix. يتيح لك إرسال رسائل نصية ومقاطع فيديو وملفات صوتية إلى عائلتك وزملائك وأصدقائك باستخدام بروتوكول ماتركس</p>
|
||||
<p xml:lang="az">NeoChat Mtrix müştərisidir. O, Matrix protokolundan istifadə edərək, ailənizə, dostlarınıza, iş yoldaşlarınıza mətn, səsli və görüntülü ismarıclar göndərməyə imkan verir.</p>
|
||||
<p xml:lang="ca">El NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics usant el protocol Matrix.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics 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="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>
|
||||
@@ -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="sl">NeoChat je odjemalec Matrixa. Dovoljuje vam pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, kolegom in prijateljem z uporabo protokola Matrix.</p>
|
||||
<p xml:lang="sv">NeoChat är en Matrix-klient. Den låter dig skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner med användning av Matrix-protokollet.</p>
|
||||
<p xml:lang="tr">NeoChat, bir Matrix istemcisidir. Matrix protokolünü kullanarak ailenize, iş arkadaşlarınıza, arkadaşlarınıza ve başkalarına metin iletileri, video ve ses dosyaların göndermenize olanak verir.</p>
|
||||
<p xml:lang="uk">NeoChat — клієнт мережі обміну повідомленнями Matrix. За допомогою цієї програми ви зможете надсилати текстові повідомлення, відео та звукові файли вашій родині, колегам та друзям за допомогою протоколу Matrix.</p>
|
||||
<p xml:lang="x-test">xxNeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.xx</p>
|
||||
<p xml:lang="zh-CN">NeoChat 是一个 Matrix 客户端。 它允许您使用 Matrix 协议向您的家人、同事和朋友发送文本消息、视频和音频文件。</p>
|
||||
@@ -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="sl">Matrix je decentraliziran komunikacijski protokol, kjer ima uporabnik uporabnik kontrolo rabe. Trenutno ima NeoChat izveden velik del protokola z izjemo šifriranih klepetov in video klepetov.</p>
|
||||
<p xml:lang="sv">Matrix är ett decentraliserat kommunikationsprotokoll, som ger tillbaka kontrollen till användaren. För närvarande implementerar NeoChat en stor del av protokollet, med undantag för krypterad chatt och videochatt.</p>
|
||||
<p xml:lang="tr">Matrix; tam denetimi kullanıcıya bırakan, merkezi olmayan bir iletişim protokolüdür. Şu anda NeoChat, uçtan uca şifrelenmiş metin ve video sohbetleri dışında protokolün büyük bir bölümünü bünyesinde bulundurur.</p>
|
||||
<p xml:lang="uk">Matrix — протокол децентралізованого спілкування, який передає контроль над даними користувачеві. У поточній версії NeoChat реалізовано більшу частину протоколу, окрім зашифрованого спілкування та відеоспілкування.</p>
|
||||
<p xml:lang="x-test">xxMatrix is a decentralized communication protocol, putting the user back in control. Currently NeoChat implements large part of the protocol with the exception of encrypted chats and video chat.xx</p>
|
||||
<p xml:lang="zh-CN">Matrix 是一个分布式通讯协议,使用户重新得到控制权。 目前,NeoChat 实现了协议的大部分,除了加密聊天和视频聊天。</p>
|
||||
@@ -125,7 +129,7 @@
|
||||
<p xml:lang="ar">يعمل نيوتشات على كل من الأجهزة المحمولة وسطح المكتب مع توفير تجربة مستخدم متسقة.</p>
|
||||
<p xml:lang="az">Vahid istifadəçi interfeysi ilə təmin olunan NeoChat, həm mobil telefonda həm də kompyuterlərdə işləyir.</p>
|
||||
<p xml:lang="ca">El NeoChat funciona en els mòbils i a l'escriptori, proporcionant una experiència d'usuari coherent.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat funciona en els mòbils i 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="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>
|
||||
@@ -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="sl">NeoChat deluje tako na mobilnih kot na namiznih platformah z zagotavljanjem konsistentne uporabniške izkušnje.</p>
|
||||
<p xml:lang="sv">NeoChat fungerar både på mobil och skrivbord och tillhandahåller en konsekvent användarupplevelse.</p>
|
||||
<p xml:lang="tr">NeoChat, hem masaüstü hem de taşınabilir ortamlarda çalışarak tutarlı bir kullanıcı deneyimi sunar.</p>
|
||||
<p xml:lang="uk">NeoChat працює на мобільних пристроях та звичайних комп'ютерах, маючи однорідний інтерфейс на усіх підтримуваних пристроях.</p>
|
||||
<p xml:lang="x-test">xxNeoChat works both on mobile and desktop while providing a consistent user experience.xx</p>
|
||||
<p xml:lang="zh-CN">NeoChat 在移动设备和桌面上均可用,并提供一致的用户体验。</p>
|
||||
</description>
|
||||
<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>
|
||||
<category>Network</category>
|
||||
</categories>
|
||||
@@ -179,12 +184,16 @@
|
||||
<developer_name xml:lang="sk">KDE Komunita</developer_name>
|
||||
<developer_name xml:lang="sl">Skupnost KDE</developer_name>
|
||||
<developer_name xml:lang="sv">KDE-gemenskapen</developer_name>
|
||||
<developer_name xml:lang="tr">KDE Topluluğu</developer_name>
|
||||
<developer_name xml:lang="uk">Спільнота KDE</developer_name>
|
||||
<developer_name xml:lang="x-test">xxThe KDE Communityxx</developer_name>
|
||||
<developer_name xml:lang="zh-CN">KDE 社区</developer_name>
|
||||
<metadata_license>CC0-1.0</metadata_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>
|
||||
<screenshot type="default">
|
||||
<image>https://cdn.kde.org/screenshots/neochat/application-mobile.png</image>
|
||||
@@ -197,6 +206,26 @@
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<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">
|
||||
<url>https://www.plasma-mobile.org/2022/04/26/plasma-mobile-gear-22-04/</url>
|
||||
<description>
|
||||
|
||||
@@ -31,6 +31,7 @@ Name[sk]=NeoChat
|
||||
Name[sl]=NeoChat
|
||||
Name[sv]=NeoChat
|
||||
Name[ta]=நியோச்சாட்
|
||||
Name[tr]=NeoChat
|
||||
Name[uk]=NeoChat
|
||||
Name[x-test]=xxNeoChatxx
|
||||
Name[zh_CN]=NeoChat
|
||||
@@ -63,6 +64,7 @@ GenericName[sk]=Matrix Client
|
||||
GenericName[sl]=Odjemalec Matrix
|
||||
GenericName[sv]=Matrix-klient
|
||||
GenericName[ta]=Matrix வாங்கி
|
||||
GenericName[tr]=Matrix İstemcisi
|
||||
GenericName[uk]=Клієнт Matrix
|
||||
GenericName[x-test]=xxMatrix Clientxx
|
||||
GenericName[zh_CN]=Matrix 客户端
|
||||
@@ -94,6 +96,7 @@ Comment[sk]=Klient protokolu Matrix
|
||||
Comment[sl]=Odjemalec za protokol Matrix
|
||||
Comment[sv]=Klient för protokollet Matrix
|
||||
Comment[ta]=Matrix நெறிமுறைக்கான வாங்கி
|
||||
Comment[tr]=Matrix protokolü için istemci
|
||||
Comment[uk]=Клієнт протоколу Matrix
|
||||
Comment[x-test]=xxClient for the Matrix protocolxx
|
||||
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.Page 1.0
|
||||
import NeoChat.Panel 1.0
|
||||
import NeoChat.Dialog.KeyVerification 1.0
|
||||
|
||||
Kirigami.ApplicationWindow {
|
||||
id: root
|
||||
@@ -51,7 +52,7 @@ Kirigami.ApplicationWindow {
|
||||
Timer {
|
||||
id: saveWindowGeometryTimer
|
||||
interval: 1000
|
||||
onTriggered: Controller.saveWindowGeometry(root)
|
||||
onTriggered: Controller.saveWindowGeometry()
|
||||
}
|
||||
|
||||
Connections {
|
||||
@@ -59,7 +60,7 @@ Kirigami.ApplicationWindow {
|
||||
enabled: false // Disable on startup to avoid writing wrong values if the window is hidden
|
||||
target: root
|
||||
|
||||
function onClosing() { Controller.saveWindowGeometry(root); }
|
||||
function onClosing() { Controller.saveWindowGeometry(); }
|
||||
function onWidthChanged() { saveWindowGeometryTimer.restart(); }
|
||||
function onHeightChanged() { saveWindowGeometryTimer.restart(); }
|
||||
function onXChanged() { saveWindowGeometryTimer.restart(); }
|
||||
@@ -106,7 +107,7 @@ Kirigami.ApplicationWindow {
|
||||
|
||||
function onOpenRoomInNewWindow(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();
|
||||
}
|
||||
|
||||
@@ -139,19 +140,16 @@ Kirigami.ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
function showWindow() {
|
||||
root.show()
|
||||
root.raise()
|
||||
root.requestActivate()
|
||||
Controller.raiseWindow(root)
|
||||
}
|
||||
|
||||
contextDrawer: RoomDrawer {
|
||||
id: contextDrawer
|
||||
edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge
|
||||
modal: !root.wideScreen || !enabled
|
||||
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
|
||||
handleVisible: enabled && pageStack.layers.depth < 2 && pageStack.depth < 3 && (root.wideScreen || pageStack.currentIndex > 0)
|
||||
}
|
||||
@@ -231,13 +229,13 @@ Kirigami.ApplicationWindow {
|
||||
Kirigami.Action {
|
||||
text: i18n("Explore rooms")
|
||||
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
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18n("Start a Chat")
|
||||
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
|
||||
},
|
||||
Kirigami.Action {
|
||||
@@ -309,10 +307,10 @@ Kirigami.ApplicationWindow {
|
||||
Connections {
|
||||
target: root.roomPage
|
||||
function onSwitchRoomUp() {
|
||||
roomList.goToNextRoom();
|
||||
roomList.goToPreviousRoom();
|
||||
}
|
||||
function onSwitchRoomDown() {
|
||||
roomList.goToPreviousRoom();
|
||||
roomList.goToNextRoom();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -349,16 +347,6 @@ Kirigami.ApplicationWindow {
|
||||
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) {
|
||||
consentSheet.url = url
|
||||
consentSheet.open()
|
||||
@@ -380,11 +368,23 @@ Kirigami.ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: keyVerificationDialogComponent
|
||||
KeyVerificationDialog { }
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Controller.activeConnection
|
||||
function onDirectChatAvailable(directChat) {
|
||||
RoomManager.enterRoom(Controller.activeConnection.room(directChat.id));
|
||||
}
|
||||
function onNewKeyVerificationSession(session) {
|
||||
applicationWindow().pageStack.pushDialogLayer(keyVerificationDialogComponent, {
|
||||
session: session,
|
||||
}, {
|
||||
title: i18nc("@title:window", "Session Verification")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.OverlaySheet {
|
||||
@@ -403,7 +403,7 @@ Kirigami.ApplicationWindow {
|
||||
}
|
||||
footer: QQC2.Button {
|
||||
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