Compare commits
185 Commits
work/winte
...
v25.04.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e87d415ae | ||
|
|
1d1648eaef | ||
|
|
4948e8f491 | ||
|
|
0ea89ced9a | ||
|
|
101d0b8c8b | ||
|
|
f3d65bc1c4 | ||
|
|
bb0a664ddc | ||
|
|
675d135eb7 | ||
|
|
5512dda762 | ||
|
|
091475a6dc | ||
|
|
11ebe5316f | ||
|
|
9b79580c29 | ||
|
|
5327ece769 | ||
|
|
58658d1aa2 | ||
|
|
611fb87376 | ||
|
|
823700eb8e | ||
|
|
fbefaa0638 | ||
|
|
85262c70c3 | ||
|
|
c88e843a34 | ||
|
|
702d17bc4d | ||
|
|
e015cbea07 | ||
|
|
ccf3976a6b | ||
|
|
bfa1f9f053 | ||
|
|
9c4dce53b2 | ||
|
|
baaea57cd9 | ||
|
|
61bb1c5db1 | ||
|
|
9b8c7714d4 | ||
|
|
a020db2e04 | ||
|
|
457b4ee3be | ||
|
|
0237bf048b | ||
|
|
f6183a4689 | ||
|
|
2a2d469d7d | ||
|
|
2212dc4d5e | ||
|
|
6e179562f0 | ||
|
|
b75f8b527c | ||
|
|
0a7fccaa5c | ||
|
|
dd3488efbe | ||
|
|
617eb25bbb | ||
|
|
7cf20a473c | ||
|
|
749b59e5eb | ||
|
|
c8170f5a96 | ||
|
|
142adbeb00 | ||
|
|
a1c213dc46 | ||
|
|
f642b1488a | ||
|
|
3e683eac77 | ||
|
|
299d8e9c40 | ||
|
|
45f9aeb5bf | ||
|
|
6961442090 | ||
|
|
f4b31838bc | ||
|
|
8e3ffd3e9d | ||
|
|
02cfb0df9c | ||
|
|
c95778b3ac | ||
|
|
8e66f635fd | ||
|
|
355b5a33b7 | ||
|
|
e40d8230c0 | ||
|
|
a76d4496da | ||
|
|
4ad1a628b6 | ||
|
|
87ea0ef5b2 | ||
|
|
7ed43044ce | ||
|
|
074e5d4c5a | ||
|
|
12ce1e460e | ||
|
|
74a6cd28eb | ||
|
|
53307f9358 | ||
|
|
c683df2b40 | ||
|
|
3b1e19c740 | ||
|
|
4a5c4559bf | ||
|
|
7d2dc0f9ac | ||
|
|
f5a7fb0d13 | ||
|
|
3e09099958 | ||
|
|
4474efd03f | ||
|
|
8fe4cf3f89 | ||
|
|
e25060c12d | ||
|
|
c56ec01637 | ||
|
|
5e07a07f38 | ||
|
|
dcc184a45f | ||
|
|
790ab54d0f | ||
|
|
606f8a1c99 | ||
|
|
46c706e364 | ||
|
|
32bea56a6d | ||
|
|
661cf22667 | ||
|
|
ad1254fb71 | ||
|
|
7514a8a6f7 | ||
|
|
716ae11941 | ||
|
|
4ff16ff402 | ||
|
|
bd598b9c44 | ||
|
|
f1253e4ede | ||
|
|
79a3da3358 | ||
|
|
5a6bdfbbba | ||
|
|
3d663be506 | ||
|
|
0ada4cdebe | ||
|
|
d103de96aa | ||
|
|
f248b04834 | ||
|
|
9572f20682 | ||
|
|
409cec08fc | ||
|
|
51f330eae9 | ||
|
|
51750267e5 | ||
|
|
99948d5151 | ||
|
|
030726e6fb | ||
|
|
1fad54272f | ||
|
|
4af4bfd55f | ||
|
|
77cedef5bb | ||
|
|
db36f187dc | ||
|
|
2861eb9c60 | ||
|
|
9811c0d97a | ||
|
|
e9c21373ed | ||
|
|
bda23ec54a | ||
|
|
e23641375b | ||
|
|
024d54345a | ||
|
|
59fd4d3916 | ||
|
|
88d684b6c1 | ||
|
|
94fdf777cb | ||
|
|
dea70152e4 | ||
|
|
614caf5ca0 | ||
|
|
25dbae37fb | ||
|
|
e060032e6a | ||
|
|
4725410c0f | ||
|
|
20488ee400 | ||
|
|
b1c0619af5 | ||
|
|
ade730179a | ||
|
|
9264ad26d6 | ||
|
|
9020e2c7cb | ||
|
|
0f51c34b24 | ||
|
|
f6a427e865 | ||
|
|
9b95930463 | ||
|
|
cb96b4991e | ||
|
|
cde7a51cde | ||
|
|
046d611f56 | ||
|
|
d7b3748159 | ||
|
|
188c9fc726 | ||
|
|
dbc735e63b | ||
|
|
8750486f7b | ||
|
|
6dc4baeeb5 | ||
|
|
ff28828a2e | ||
|
|
e28452dfd1 | ||
|
|
5d7cb5c28f | ||
|
|
08b29f7081 | ||
|
|
c9e034b5b3 | ||
|
|
d9f0ff466f | ||
|
|
6b4b895102 | ||
|
|
0c7e02e7c9 | ||
|
|
4d1c82a623 | ||
|
|
8d33fe6221 | ||
|
|
9d27651411 | ||
|
|
268975bc3b | ||
|
|
66343ba11e | ||
|
|
684cd85a7a | ||
|
|
ef9a80e76f | ||
|
|
fbb5f02379 | ||
|
|
5f4bde96e9 | ||
|
|
f8c8a68840 | ||
|
|
bf6f4a951e | ||
|
|
58c9366548 | ||
|
|
f410ecac2b | ||
|
|
1d1a43ade2 | ||
|
|
37adb56233 | ||
|
|
aca0669bf6 | ||
|
|
b33ab76ff8 | ||
|
|
38a391b7fa | ||
|
|
82434fe87c | ||
|
|
8bf7c36249 | ||
|
|
cff3557a24 | ||
|
|
2c476c4351 | ||
|
|
82c8ab511d | ||
|
|
486ed6edd2 | ||
|
|
a4b0a9ed36 | ||
|
|
bba9c37ba5 | ||
|
|
febc7d1630 | ||
|
|
1ed071949b | ||
|
|
3878c264ef | ||
|
|
21daf0b664 | ||
|
|
5efaa72cea | ||
|
|
191cd7cbba | ||
|
|
c583e31b16 | ||
|
|
590fba7deb | ||
|
|
a8f22003cb | ||
|
|
54596e3fe6 | ||
|
|
2ee4d110a0 | ||
|
|
7a949dccbb | ||
|
|
111a45ab38 | ||
|
|
2bcf59c225 | ||
|
|
d542033125 | ||
|
|
44c72828e1 | ||
|
|
99d3ee32fa | ||
|
|
7df0ff309e | ||
|
|
856a751fcb |
@@ -25,13 +25,23 @@
|
||||
"modules": [
|
||||
{
|
||||
"name": "kirigamiaddons",
|
||||
"config-opts": [ "-DBUILD_TESTING=OFF" ],
|
||||
"config-opts": [
|
||||
"-DBUILD_TESTING=OFF"
|
||||
],
|
||||
"buildsystem": "cmake-ninja",
|
||||
"sources": [ { "type": "git", "url": "https://invent.kde.org/libraries/kirigami-addons.git", "commit": "34d311219e8b7209746a98b3a29b91ded05ff936" } ]
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://invent.kde.org/libraries/kirigami-addons.git"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "kquickimageeditor",
|
||||
"config-opts": [ "-DBUILD_WITH_QT6=ON" ],
|
||||
"config-opts": [
|
||||
"-DBUILD_WITH_QT6=ON",
|
||||
"-DBUILD_TESTING=OFF"
|
||||
],
|
||||
"buildsystem": "cmake-ninja",
|
||||
"sources": [
|
||||
{
|
||||
@@ -43,17 +53,19 @@
|
||||
{
|
||||
"name": "olm",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"config-opts": [ "-DOLM_TESTS=OFF" ],
|
||||
"config-opts": [
|
||||
"-DOLM_TESTS=OFF"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://gitlab.matrix.org/matrix-org/olm.git",
|
||||
"tag": "3.2.10",
|
||||
"tag": "3.2.16",
|
||||
"x-checker-data": {
|
||||
"type": "git",
|
||||
"tag-pattern": "^([\\d.]+)$"
|
||||
},
|
||||
"commit": "9908862979147a71dc6abaecd521be526ae77be1"
|
||||
"commit": "7e0c8277032e40308987257b711b38af8d77cc69"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -65,13 +77,13 @@
|
||||
"-Dvapi=false",
|
||||
"-Dgtk_doc=false",
|
||||
"-Dintrospection=false",
|
||||
"-Dgcrypt=false"
|
||||
"-Dcrypto=disabled"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://download.gnome.org/sources/libsecret/0.20/libsecret-0.20.5.tar.xz",
|
||||
"sha256": "3fb3ce340fcd7db54d87c893e69bfc2b1f6e4d4b279065ffe66dac9f0fd12b4d",
|
||||
"url": "https://download.gnome.org/sources/libsecret/0.21/libsecret-0.21.6.tar.xz",
|
||||
"sha256": "747b8c175be108c880d3adfb9c3537ea66e520e4ad2dccf5dce58003aeeca090",
|
||||
"x-checker-data": {
|
||||
"type": "gnome",
|
||||
"name": "libsecret",
|
||||
@@ -86,13 +98,13 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/frankosterfeld/qtkeychain/archive/0.14.2.tar.gz",
|
||||
"sha256": "cf2e972b783ba66334a79a30f6b3a1ea794a1dc574d6c3bebae5ffd2f0399571",
|
||||
"url": "https://github.com/frankosterfeld/qtkeychain/archive/refs/tags/0.15.0.tar.gz",
|
||||
"sha256": "f4254dc8f0933b06d90672d683eab08ef770acd8336e44dfa030ce041dc2ca22",
|
||||
"x-checker-data": {
|
||||
"type": "anitya",
|
||||
"project-id": 4138,
|
||||
"stable-only": true,
|
||||
"url-template": "https://github.com/frankosterfeld/qtkeychain/archive/v$version.tar.gz"
|
||||
"url-template": "https://github.com/frankosterfeld/qtkeychain/archive/refs/tags/$version.tar.gz"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -100,7 +112,8 @@
|
||||
"-DBUILD_WITH_QT6=ON",
|
||||
"-DCMAKE_INSTALL_LIBDIR=/app/lib",
|
||||
"-DLIB_INSTALL_DIR=/app/lib",
|
||||
"-DBUILD_TRANSLATIONS=NO"
|
||||
"-DBUILD_TRANSLATIONS=NO",
|
||||
"-DBUILD_TESTING=OFF"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -123,29 +136,31 @@
|
||||
{
|
||||
"name": "cmark",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"config-opts": [ "-DCMARK_TESTS=OFF" ],
|
||||
"config-opts": [
|
||||
"-DCMARK_TESTS=OFF",
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
"-DCMAKE_INSTALL_PREFIX=/app"
|
||||
],
|
||||
"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",
|
||||
"config-opts": [ "-DQCORO_BUILD_EXAMPLES=OFF", "-DBUILD_TESTING=OFF" ],
|
||||
"config-opts": [
|
||||
"-DQCORO_BUILD_EXAMPLES=OFF",
|
||||
"-DBUILD_TESTING=OFF"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.7.0.tar.gz",
|
||||
"sha256": "23ef0217926e67c8d2eb861cf91617da2f7d8d5a9ae6c62321b21448b1669210",
|
||||
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.11.0.tar.gz",
|
||||
"sha256": "9942c5b4c533192f6c5954dc6d10178b3829075e6a621b67df73f0a4b74d8297",
|
||||
"x-checker-data": {
|
||||
"type": "anitya",
|
||||
"project-id": 236236,
|
||||
@@ -158,14 +173,15 @@
|
||||
{
|
||||
"name": "neochat",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"config-opts": [
|
||||
"-DBUILD_TESTING=OFF",
|
||||
"-DNEOCHAT_FLATPAK=ON"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"config-opts": [
|
||||
"-DNEOCHAT_FLATPAK=ON"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -5,10 +5,14 @@ include:
|
||||
- project: sysadmin/ci-utilities
|
||||
file:
|
||||
- /gitlab-templates/reuse-lint.yml
|
||||
- /gitlab-templates/json-validation.yml
|
||||
- /gitlab-templates/xml-lint.yml
|
||||
- /gitlab-templates/yaml-lint.yml
|
||||
- /gitlab-templates/android-qt6.yml
|
||||
- /gitlab-templates/linux-qt6.yml
|
||||
# - /gitlab-templates/linux-qt6-next.yml
|
||||
- /gitlab-templates/windows-qt6.yml
|
||||
# - /gitlab-templates/freebsd-qt6.yml
|
||||
# - /gitlab-templates/freebsd-qt6.yml
|
||||
- /gitlab-templates/flatpak.yml
|
||||
- /gitlab-templates/snap-snapcraft-lxd.yml
|
||||
- /gitlab-templates/craft-android-qt6-apks.yml
|
||||
|
||||
71
.kde-ci.yml
71
.kde-ci.yml
@@ -2,42 +2,43 @@
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
Dependencies:
|
||||
- 'on': ['Linux', 'Android', 'FreeBSD', 'Windows']
|
||||
'require':
|
||||
'frameworks/extra-cmake-modules': '@latest-kf6'
|
||||
'frameworks/kcoreaddons': '@latest-kf6'
|
||||
'frameworks/kirigami': '@latest-kf6'
|
||||
'frameworks/ki18n': '@latest-kf6'
|
||||
'frameworks/kconfig': '@latest-kf6'
|
||||
'frameworks/syntax-highlighting': '@latest-kf6'
|
||||
'frameworks/kitemmodels': '@latest-kf6'
|
||||
'frameworks/kquickcharts': '@latest-kf6'
|
||||
'frameworks/knotifications': '@latest-kf6'
|
||||
'frameworks/kcolorscheme': '@latest-kf6'
|
||||
'libraries/kquickimageeditor': '@latest-kf6'
|
||||
'frameworks/sonnet': '@latest-kf6'
|
||||
'frameworks/prison': '@latest-kf6'
|
||||
'libraries/kirigami-addons': '@latest-kf6'
|
||||
'third-party/libquotient': '@latest'
|
||||
'third-party/qtkeychain': '@latest'
|
||||
'third-party/cmark': '@latest'
|
||||
'third-party/qcoro': '@latest'
|
||||
- 'on': ['Windows', 'Linux', 'FreeBSD']
|
||||
'require':
|
||||
'frameworks/qqc2-desktop-style': '@latest-kf6'
|
||||
'frameworks/kio': '@latest-kf6'
|
||||
'frameworks/kwindowsystem': '@latest-kf6'
|
||||
'frameworks/kstatusnotifieritem': '@latest-kf6'
|
||||
'frameworks/kcrash': '@latest-kf6'
|
||||
- 'on': ['Linux', 'FreeBSD']
|
||||
'require':
|
||||
'frameworks/kdbusaddons': '@latest-kf6'
|
||||
'frameworks/purpose': '@latest-kf6'
|
||||
- 'on': ['Linux', 'Android', 'FreeBSD', 'Windows']
|
||||
'require':
|
||||
'frameworks/extra-cmake-modules': '@latest-kf6'
|
||||
'frameworks/kcoreaddons': '@latest-kf6'
|
||||
'frameworks/kirigami': '@latest-kf6'
|
||||
'frameworks/ki18n': '@latest-kf6'
|
||||
'frameworks/kconfig': '@latest-kf6'
|
||||
'frameworks/syntax-highlighting': '@latest-kf6'
|
||||
'frameworks/kitemmodels': '@latest-kf6'
|
||||
'frameworks/kquickcharts': '@latest-kf6'
|
||||
'frameworks/knotifications': '@latest-kf6'
|
||||
'frameworks/kcolorscheme': '@latest-kf6'
|
||||
'frameworks/kiconthemes': '@latest-kf6'
|
||||
'libraries/kquickimageeditor': '@latest-kf6'
|
||||
'frameworks/sonnet': '@latest-kf6'
|
||||
'frameworks/prison': '@latest-kf6'
|
||||
'libraries/kirigami-addons': '@latest-kf6'
|
||||
'third-party/libquotient': '@latest'
|
||||
'third-party/qtkeychain': '@latest'
|
||||
'third-party/cmark': '@latest'
|
||||
'third-party/qcoro': '@latest'
|
||||
- 'on': ['Windows', 'Linux', 'FreeBSD']
|
||||
'require':
|
||||
'frameworks/qqc2-desktop-style': '@latest-kf6'
|
||||
'frameworks/kio': '@latest-kf6'
|
||||
'frameworks/kwindowsystem': '@latest-kf6'
|
||||
'frameworks/kstatusnotifieritem': '@latest-kf6'
|
||||
'frameworks/kcrash': '@latest-kf6'
|
||||
- 'on': ['Linux', 'FreeBSD']
|
||||
'require':
|
||||
'frameworks/kdbusaddons': '@latest-kf6'
|
||||
'frameworks/purpose': '@latest-kf6'
|
||||
|
||||
- 'on': ['Linux']
|
||||
'require':
|
||||
'sdk/selenium-webdriver-at-spi': '@latest-kf6'
|
||||
- 'on': ['Linux']
|
||||
'require':
|
||||
'sdk/selenium-webdriver-at-spi': '@latest-kf6'
|
||||
|
||||
Options:
|
||||
per-test-timeout: 90
|
||||
require-passing-tests-on: [ 'Linux', 'Android', 'FreeBSD', 'Windows' ]
|
||||
require-passing-tests-on: ['Linux', 'Android', 'FreeBSD', 'Windows']
|
||||
|
||||
@@ -8,8 +8,8 @@ cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# KDE Applications version, managed by release script.
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "25")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "03")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "04")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "3")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||
@@ -66,7 +66,7 @@ if (QT_KNOWN_POLICY_QTP0004)
|
||||
qt_policy(SET QTP0004 NEW)
|
||||
endif ()
|
||||
|
||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels ColorScheme)
|
||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels IconThemes ColorScheme)
|
||||
set_package_properties(KF6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
|
||||
@@ -20,11 +20,11 @@ class ReactionModelTest : public QObject
|
||||
private:
|
||||
Connection *connection = nullptr;
|
||||
TestUtils::TestRoom *room = nullptr;
|
||||
MessageContentModel *parentModel;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
|
||||
void nullModel();
|
||||
void basicReaction();
|
||||
void newReaction();
|
||||
};
|
||||
@@ -33,20 +33,13 @@ void ReactionModelTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
|
||||
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-reactionmodel-sync.json"_s);
|
||||
}
|
||||
|
||||
void ReactionModelTest::nullModel()
|
||||
{
|
||||
auto model = ReactionModel(nullptr, nullptr);
|
||||
|
||||
QCOMPARE(model.rowCount(), 0);
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::TextContentRole), QVariant());
|
||||
parentModel = new MessageContentModel(room, "123456"_L1);
|
||||
}
|
||||
|
||||
void ReactionModelTest::basicReaction()
|
||||
{
|
||||
auto event = eventCast<const RoomMessageEvent>(room->messageEvents().at(0).get());
|
||||
auto model = ReactionModel(event, room);
|
||||
auto model = ReactionModel(parentModel, event->id(), room);
|
||||
|
||||
QCOMPARE(model.rowCount(), 1);
|
||||
QCOMPARE(model.data(model.index(0), ReactionModel::TextContentRole), u"<span style=\"font-family: 'emoji';\">👍</span>"_s);
|
||||
@@ -58,7 +51,7 @@ void ReactionModelTest::basicReaction()
|
||||
void ReactionModelTest::newReaction()
|
||||
{
|
||||
auto event = eventCast<const RoomMessageEvent>(room->messageEvents().at(0).get());
|
||||
auto model = new ReactionModel(event, room);
|
||||
auto model = new ReactionModel(parentModel, event->id(), room);
|
||||
|
||||
QCOMPARE(model->rowCount(), 1);
|
||||
QCOMPARE(model->data(model->index(0), ReactionModel::ToolTipRole), u"Alice Margatroid reacted with <span style=\"font-family: 'emoji';\">👍</span>"_s);
|
||||
|
||||
@@ -530,6 +530,9 @@ void TextHandlerTest::componentOutput_data()
|
||||
QTest::newRow("quote") << u"<p>Text</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>"_s
|
||||
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, u"Text"_s, {}},
|
||||
MessageComponent{MessageComponentType::Quote, u"“blockquote”"_s, {}}};
|
||||
QTest::newRow("multiple paragraph quote") << u"<blockquote>\n<p>blockquote</p>\n<p>next paragraph</p>\n</blockquote>"_s
|
||||
<< QList<MessageComponent>{
|
||||
MessageComponent{MessageComponentType::Quote, u"<p>“blockquote</p>\n<p>next paragraph”</p>"_s, {}}};
|
||||
QTest::newRow("no tag first paragraph") << u"Text\n<p>Text</p>"_s
|
||||
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, u"Text"_s, {}},
|
||||
MessageComponent{MessageComponentType::Text, u"Text"_s, {}}};
|
||||
|
||||
@@ -137,7 +137,11 @@ void TimelineMessageModelTest::pendingEvent()
|
||||
model->setRoom(room);
|
||||
QCOMPARE(model->rowCount(), 0);
|
||||
|
||||
#if Quotient_VERSION_MINOR > 9
|
||||
auto txnId = room->postText("New plain message"_L1);
|
||||
#else
|
||||
auto txnId = room->postPlainText("New plain message"_L1);
|
||||
#endif
|
||||
QCOMPARE(model->rowCount(), 1);
|
||||
QCOMPARE(spyInsert.count(), 1);
|
||||
|
||||
@@ -145,7 +149,11 @@ void TimelineMessageModelTest::pendingEvent()
|
||||
QCOMPARE(model->rowCount(), 0);
|
||||
QCOMPARE(spyRemove.count(), 1);
|
||||
|
||||
#if Quotient_VERSION_MINOR > 9
|
||||
txnId = room->postText("New plain message"_L1);
|
||||
#else
|
||||
txnId = room->postPlainText("New plain message"_L1);
|
||||
#endif
|
||||
QCOMPARE(model->rowCount(), 1);
|
||||
QCOMPARE(spyInsert.count(), 2);
|
||||
|
||||
|
||||
@@ -70,10 +70,12 @@
|
||||
<summary xml:lang="ia">Conversation en ditecto sur Matrix</summary>
|
||||
<summary xml:lang="it">Chat su Matrix</summary>
|
||||
<summary xml:lang="ka">ისაუბრეთ Matrix-ზე</summary>
|
||||
<summary xml:lang="ko">Matrix에서 대화하기</summary>
|
||||
<summary xml:lang="lv">Tērzējiet „Matrix“ tīklā</summary>
|
||||
<summary xml:lang="nl">Chat op Matrix</summary>
|
||||
<summary xml:lang="nn">Prat med via Matrix</summary>
|
||||
<summary xml:lang="pl">Rozmawiaj na Matriksie</summary>
|
||||
<summary xml:lang="ru">Общение в Matrix</summary>
|
||||
<summary xml:lang="sa">Matrix इत्यत्र गपशपं कुर्वन्तु</summary>
|
||||
<summary xml:lang="sl">Klepet na Matrixu</summary>
|
||||
<summary xml:lang="sv">Chatta på Matrix</summary>
|
||||
@@ -102,6 +104,7 @@
|
||||
<p xml:lang="ia">NeoChat es un app de conversation que te permitte prender avantage plen del rete Matrix. Il te forni un modo secur de inviar messages de texto, videos e files audio a tui familia, collegas e amicos.</p>
|
||||
<p xml:lang="it">NeoChat è un'applicazione di chat che ti consente di sfruttare appieno la rete Matrix. Ti fornisce un modo sicuro per inviare messaggi di testo, video e file audio a familiari, colleghi e amici.</p>
|
||||
<p xml:lang="ka">NeoChat ჩატის აპია, რომელიც საშუალება გაძლევთ, Matrix-ის ქსელის საშუალებები ბოლომდე გამოიყენოთ. ის გაძლევთ უსაფრთხო გზას, გააგზავნოთ ტექსტური შეტყობინებები, ვიდეოებ და აუდიოფაილები თქვენს ოჯახთან, კოლეგებთან და მეგობრებთან.</p>
|
||||
<p xml:lang="ko">NeoChat은 Matrix 네트워크를 사용하는 채팅 앱입니다. 텍스트 메시지, 동영상, 오디오 파일을 가족, 친구, 동료와 안전하게 공유할 수 있습니다.</p>
|
||||
<p xml:lang="lv">„NeoChat“ ir tērzēšanas programma, kas ļauj pilnvērtīgi izmantot „Matrix“ tīklu. Tā sniedz drošu veidu teksta ziņu, video un audio sūtīšanai ģimenes locekļiem, kolēģiem un draugiem.</p>
|
||||
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
|
||||
<p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p>
|
||||
@@ -372,6 +375,7 @@
|
||||
<caption xml:lang="ia">Discoperi nove communitate con Matrix Spaces (Spatios de Matrix)</caption>
|
||||
<caption xml:lang="it">Scopri nuove comunità con Matrix Spaces</caption>
|
||||
<caption xml:lang="ka">აღმოაჩინეთ ახალი საზოგადოებები Matrix Spaces-თან ერთად</caption>
|
||||
<caption xml:lang="ko">Matrix 스페이스에서 새로운 커뮤니티 탐험</caption>
|
||||
<caption xml:lang="lv">Atklājiet jaunas kopienas ar „Matrix“ telpām</caption>
|
||||
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
|
||||
<caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption>
|
||||
@@ -473,6 +477,12 @@
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release version="25.04.3" date="2025-07-03"/>
|
||||
<release version="25.04.2" date="2025-06-05"/>
|
||||
<release version="25.04.1" date="2025-05-08"/>
|
||||
<release version="25.04.0" date="2025-04-17"/>
|
||||
<release version="24.12.3" date="2025-03-06"/>
|
||||
<release version="24.12.2" date="2025-02-06"/>
|
||||
<release version="24.12.1" date="2025-01-09"/>
|
||||
<release version="24.12.0" date="2024-12-12"/>
|
||||
<release version="24.08.3" date="2024-11-07"/>
|
||||
@@ -649,6 +659,9 @@
|
||||
<url>https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/</url>
|
||||
</release>
|
||||
</releases>
|
||||
<requires>
|
||||
<display_length compare="ge">360</display_length>
|
||||
</requires>
|
||||
<branding>
|
||||
<color type="primary" scheme_preference="light">#a6e4f3</color>
|
||||
<color type="primary" scheme_preference="dark">#235670</color>
|
||||
|
||||
@@ -109,9 +109,12 @@ Comment[hu]=Csevegés Matrixon
|
||||
Comment[ia]=Conversation en ditecto sur Matrix
|
||||
Comment[it]= su Matrix
|
||||
Comment[ka]=ჩატი Matrix-ზე
|
||||
Comment[ko]=Matrix에서 대화하기
|
||||
Comment[lv]=Tērzējiet „Matrix“ tīklā
|
||||
Comment[nl]=Chat op Matrix
|
||||
Comment[pl]=Rozmawiaj na Matriksie
|
||||
Comment[pt_BR]=Bate papo na Matrix
|
||||
Comment[ru]=Общение в Matrix
|
||||
Comment[sa]=Matrix इत्यत्र गपशपं कुर्वन्तु
|
||||
Comment[sl]=Klepet na Matrixu
|
||||
Comment[sv]=Chatta på Matrix
|
||||
@@ -119,6 +122,7 @@ Comment[ta]=மேட்ரிக்ஸில் உரையாட உதவு
|
||||
Comment[tr]=Matrix üzerinde sohbet edin
|
||||
Comment[uk]=Спілкування у Matrix
|
||||
Comment[x-test]=xxChat on Matrixxx
|
||||
Comment[zh_CN]=在 Matrix 上聊天
|
||||
Comment[zh_TW]=在 Matrix 上聊天
|
||||
MimeType=x-scheme-handler/matrix;
|
||||
Exec=neochat %u
|
||||
|
||||
809
po/ar/neochat.po
809
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
823
po/az/neochat.po
823
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
797
po/ca/neochat.po
797
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1127
po/cs/neochat.po
1127
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
789
po/da/neochat.po
789
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
840
po/de/neochat.po
840
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
809
po/el/neochat.po
809
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
783
po/eo/neochat.po
783
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
1578
po/es/neochat.po
1578
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
812
po/eu/neochat.po
812
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
913
po/fi/neochat.po
913
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
958
po/fr/neochat.po
958
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
819
po/gl/neochat.po
819
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
6167
po/he/neochat.po
Normal file
6167
po/he/neochat.po
Normal file
File diff suppressed because it is too large
Load Diff
844
po/hi/neochat.po
844
po/hi/neochat.po
File diff suppressed because it is too large
Load Diff
1155
po/hu/neochat.po
1155
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
808
po/ia/neochat.po
808
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
837
po/id/neochat.po
837
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
809
po/ie/neochat.po
809
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
811
po/it/neochat.po
811
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
731
po/ja/neochat.po
731
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
804
po/ka/neochat.po
804
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
2247
po/ko/neochat.po
2247
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
734
po/lt/neochat.po
734
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
1118
po/lv/neochat.po
1118
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
808
po/nl/neochat.po
808
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
798
po/nn/neochat.po
798
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
821
po/pa/neochat.po
821
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
827
po/pl/neochat.po
827
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
837
po/pt/neochat.po
837
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1654
po/ru/neochat.po
1654
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
842
po/sa/neochat.po
842
po/sa/neochat.po
File diff suppressed because it is too large
Load Diff
824
po/sk/neochat.po
824
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
809
po/sl/neochat.po
809
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
804
po/sv/neochat.po
804
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
866
po/ta/neochat.po
866
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
842
po/tr/neochat.po
842
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
803
po/uk/neochat.po
803
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
# SPDX-FileCopyrightText: 2024 Scarlett Moore <sgmoore@kde.org>
|
||||
# SPDX-FileCopyrightText: 2024-2025 Scarlett Moore <sgmoore@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
---
|
||||
name: neochat
|
||||
base: core22
|
||||
base: core24
|
||||
adopt-info: neochat
|
||||
grade: stable
|
||||
confinement: strict
|
||||
@@ -24,12 +24,15 @@ apps:
|
||||
- network-manager-observe
|
||||
- password-manager-service
|
||||
- accounts-service
|
||||
environment:
|
||||
QT_PLUGIN_PATH: "$SNAP/usr/lib/$CRAFT_ARCH_TRIPLET/plugins/snap/kf6-core24/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/plugins"
|
||||
QML_IMPORT_PATH: "$SNAP/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/qml:/snap/kf6-core24/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/qml"
|
||||
|
||||
compression: lzo
|
||||
|
||||
package-repositories:
|
||||
- type: apt
|
||||
ppa: ubuntu-toolchain-r/test
|
||||
- type: apt
|
||||
ppa: ubuntu-toolchain-r/test
|
||||
|
||||
slots:
|
||||
session-dbus-interface:
|
||||
@@ -41,11 +44,12 @@ parts:
|
||||
olm:
|
||||
source: https://gitlab.matrix.org/matrix-org/olm.git
|
||||
source-depth: 1
|
||||
source-tag: '3.2.12'
|
||||
source-tag: '3.2.16'
|
||||
plugin: cmake
|
||||
cmake-parameters:
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- -DCMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
prime:
|
||||
- -usr/include
|
||||
- -usr/lib/*/pkgconfig
|
||||
@@ -66,7 +70,7 @@ parts:
|
||||
- -Dcrypto=disabled
|
||||
- -Dgtk_doc=false
|
||||
build-packages:
|
||||
- meson
|
||||
- meson
|
||||
- libglib2.0-dev
|
||||
- libgcrypt20-dev
|
||||
prime:
|
||||
@@ -81,7 +85,7 @@ parts:
|
||||
plugin: cmake
|
||||
build-environment:
|
||||
- PATH: /snap/bin:${PATH}
|
||||
- PKG_CONFIG_PATH: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET/pkgconfig:$PKG_CONFIG_PATH
|
||||
- PKG_CONFIG_PATH: "$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET/pkgconfig:$PKG_CONFIG_PATH"
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
@@ -105,8 +109,6 @@ parts:
|
||||
build-snaps:
|
||||
- cmake
|
||||
build-packages:
|
||||
- gcc-13
|
||||
- g++-13
|
||||
- libssl-dev
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
@@ -114,9 +116,6 @@ parts:
|
||||
- -DBUILD_TESTING=OFF
|
||||
- -DQuotient_ENABLE_E2EE=ON
|
||||
- -DBUILD_WITH_QT6=ON
|
||||
override-build: |
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 100 --slave /usr/bin/g++ g++ /usr/bin/g++-13 --slave /usr/bin/gcov gcov /usr/bin/gcov-13
|
||||
craftctl default
|
||||
prime:
|
||||
- -usr/include
|
||||
- -usr/lib/*/pkgconfig
|
||||
@@ -129,6 +128,8 @@ parts:
|
||||
plugin: cmake
|
||||
build-environment:
|
||||
- PATH: /snap/bin:${PATH}
|
||||
- PYTHONPATH: ${CRAFT_STAGE}/lib/python3.12/site-packages:${CRAFT_STAGE}/usr/lib/python3/dist-packages
|
||||
- LD_LIBRARY_PATH: "/snap/mesa-2404/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/kde-qt6-core24-sdk/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libproxy:$LD_LIBRARY_PATH"
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
@@ -139,17 +140,32 @@ parts:
|
||||
- -usr/lib/*/pkgconfig
|
||||
- -usr/lib/*/cmake
|
||||
|
||||
kunifiedpush:
|
||||
source: https://invent.kde.org/libraries/kunifiedpush.git
|
||||
plugin: cmake
|
||||
build-environment:
|
||||
- PATH: /snap/bin:${PATH}
|
||||
- PYTHONPATH: ${CRAFT_STAGE}/lib/python3.12/site-packages:${CRAFT_STAGE}/usr/lib/python3/dist-packages
|
||||
- LD_LIBRARY_PATH: "/snap/mesa-2404/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/kde-qt6-core24-sdk/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libproxy:$LD_LIBRARY_PATH"
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DBUILD_TESTING=OFF
|
||||
|
||||
neochat:
|
||||
after:
|
||||
- qtkeychain
|
||||
- libquotient
|
||||
- kquickimageeditor
|
||||
- kunifiedpush
|
||||
parse-info:
|
||||
- usr/share/metainfo/org.kde.neochat.appdata.xml
|
||||
source: .
|
||||
plugin: cmake
|
||||
build-environment:
|
||||
- PATH: /snap/bin:${PATH}
|
||||
- PYTHONPATH: ${CRAFT_STAGE}/lib/python3.12/site-packages:${CRAFT_STAGE}/usr/lib/python3/dist-packages
|
||||
- LD_LIBRARY_PATH: "/snap/mesa-2404/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/kde-qt6-core24-sdk/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libproxy:$LD_LIBRARY_PATH"
|
||||
build-packages:
|
||||
- cmark
|
||||
- libcmark-dev
|
||||
@@ -173,3 +189,12 @@ parts:
|
||||
prime:
|
||||
- usr/lib/*/libcmark.so*
|
||||
|
||||
gpu-2404:
|
||||
after: [neochat]
|
||||
source: https://github.com/canonical/gpu-snap.git
|
||||
plugin: dump
|
||||
override-prime: |
|
||||
craftctl default
|
||||
${CRAFT_PART_SRC}/bin/gpu-2404-cleanup mesa-2404
|
||||
prime:
|
||||
- bin/gpu-2404-wrapper
|
||||
|
||||
@@ -107,8 +107,6 @@ add_library(neochat STATIC
|
||||
models/imagepacksmodel.h
|
||||
events/imagepackevent.cpp
|
||||
events/imagepackevent.h
|
||||
events/joinrulesevent.cpp
|
||||
events/joinrulesevent.h
|
||||
models/reactionmodel.cpp
|
||||
models/reactionmodel.h
|
||||
delegatesizehelper.cpp
|
||||
@@ -196,6 +194,8 @@ add_library(neochat STATIC
|
||||
models/messagecontentfiltermodel.h
|
||||
models/pinnedmessagemodel.cpp
|
||||
models/pinnedmessagemodel.h
|
||||
models/commonroomsmodel.cpp
|
||||
models/commonroomsmodel.h
|
||||
)
|
||||
|
||||
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||
@@ -206,6 +206,9 @@ if(ANDROID OR WIN32)
|
||||
set_source_files_properties(qml/ShareActionStub.qml PROPERTIES
|
||||
QT_QML_SOURCE_TYPENAME ShareAction
|
||||
)
|
||||
set_source_files_properties(qml/GlobalMenuStub.qml PROPERTIES
|
||||
QT_QML_SOURCE_TYPENAME GlobalMenu
|
||||
)
|
||||
endif()
|
||||
|
||||
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||
@@ -320,7 +323,10 @@ if(NOT ANDROID AND NOT WIN32)
|
||||
qml/EditMenu.qml
|
||||
)
|
||||
else()
|
||||
qt_target_qml_sources(neochat QML_FILES qml/ShareActionStub.qml)
|
||||
qt_target_qml_sources(neochat QML_FILES
|
||||
qml/ShareActionStub.qml
|
||||
qml/GlobalMenuStub.qml
|
||||
)
|
||||
endif()
|
||||
|
||||
configure_file(config-neochat.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-neochat.h)
|
||||
@@ -421,6 +427,7 @@ target_link_libraries(neochat PUBLIC
|
||||
KF6::ConfigGui
|
||||
KF6::CoreAddons
|
||||
KF6::SonnetCore
|
||||
KF6::IconThemes
|
||||
KF6::ColorScheme
|
||||
KF6::ItemModels
|
||||
QuotientQt6
|
||||
@@ -494,6 +501,7 @@ if(ANDROID)
|
||||
"network-connect"
|
||||
"list-remove-user"
|
||||
"org.kde.neochat"
|
||||
"org.kde.neochat.tray"
|
||||
"preferences-system-users"
|
||||
"preferences-desktop-theme-global"
|
||||
"notifications"
|
||||
@@ -531,6 +539,7 @@ if(ANDROID)
|
||||
"object-rotate-left"
|
||||
"object-rotate-right"
|
||||
"add-subtitle"
|
||||
"security-high"
|
||||
"security-low"
|
||||
"security-low-symbolic"
|
||||
"kde"
|
||||
@@ -538,6 +547,8 @@ if(ANDROID)
|
||||
"edit-delete"
|
||||
"user-home-symbolic"
|
||||
"pin-symbolic"
|
||||
"kt-restore-defaults-symbolic"
|
||||
"user-symbolic"
|
||||
)
|
||||
ecm_add_android_apk(neochat-app ANDROID_DIR ${CMAKE_SOURCE_DIR}/android)
|
||||
else()
|
||||
|
||||
@@ -283,12 +283,12 @@ QQC2.Control {
|
||||
if (quickFormatBar.visible && selectedText.length > 0) {
|
||||
quickFormatBar.close();
|
||||
}
|
||||
} else if (event.key === Qt.Key_Escape && completionMenu.visible) {
|
||||
completionMenu.close();
|
||||
}
|
||||
}
|
||||
Keys.onShortcutOverride: event => {
|
||||
if (completionMenu.visible) {
|
||||
completionMenu.close();
|
||||
} else if ((_private.chatBarCache.isReplying || _private.chatBarCache.attachmentPath.length > 0) && event.key === Qt.Key_Escape) {
|
||||
if ((_private.chatBarCache.isReplying || _private.chatBarCache.attachmentPath.length > 0) && event.key === Qt.Key_Escape) {
|
||||
_private.chatBarCache.attachmentPath = "";
|
||||
_private.chatBarCache.replyId = "";
|
||||
_private.chatBarCache.threadId = "";
|
||||
@@ -315,11 +315,13 @@ QQC2.Control {
|
||||
icon.name: modelData.isBusy ? "" : (modelData.icon.name.length > 0 ? modelData.icon.name : modelData.icon.source)
|
||||
onClicked: modelData.trigger()
|
||||
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.text: modelData.tooltip
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
|
||||
PieProgressBar {
|
||||
contentItem: PieProgressBar {
|
||||
visible: modelData.isBusy
|
||||
progress: root.currentRoom.fileUploadingProgress
|
||||
}
|
||||
|
||||
@@ -117,6 +117,10 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
||||
});
|
||||
});
|
||||
connect(this, &ChatDocumentHandler::documentChanged, this, [this]() {
|
||||
if (!m_document) {
|
||||
m_highlighter->setDocument(nullptr);
|
||||
return;
|
||||
}
|
||||
m_highlighter->setDocument(m_document->textDocument());
|
||||
});
|
||||
connect(this, &ChatDocumentHandler::cursorPositionChanged, this, [this]() {
|
||||
@@ -222,11 +226,14 @@ void ChatDocumentHandler::complete(int index)
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure we only search for the beginning of the current completion identifier
|
||||
const auto fromIndex = qMax(completionStartIndex(), 0);
|
||||
|
||||
if (m_completionModel->autoCompletionType() == CompletionModel::User) {
|
||||
auto name = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::DisplayNameRole).toString();
|
||||
auto id = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char('@'), cursorPosition() - 1);
|
||||
auto at = text.indexOf(QLatin1Char('@'), fromIndex);
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
@@ -239,7 +246,7 @@ void ChatDocumentHandler::complete(int index)
|
||||
} else if (m_completionModel->autoCompletionType() == CompletionModel::Command) {
|
||||
auto command = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedTextRole).toString();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char('/'));
|
||||
auto at = text.indexOf(QLatin1Char('/'), fromIndex);
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
@@ -247,7 +254,7 @@ void ChatDocumentHandler::complete(int index)
|
||||
} else if (m_completionModel->autoCompletionType() == CompletionModel::Room) {
|
||||
auto alias = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char('#'), cursorPosition() - 1);
|
||||
auto at = text.indexOf(QLatin1Char('#'), fromIndex);
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
@@ -260,7 +267,7 @@ void ChatDocumentHandler::complete(int index)
|
||||
} else if (m_completionModel->autoCompletionType() == CompletionModel::Emoji) {
|
||||
auto shortcode = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedTextRole).toString();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char(':'));
|
||||
auto at = text.indexOf(QLatin1Char(':'), fromIndex);
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
|
||||
@@ -168,7 +168,6 @@ void Controller::addConnection(NeoChatConnection *c)
|
||||
connect(c, &NeoChatConnection::syncDone, this, [this, c]() {
|
||||
m_notificationsManager.handleNotifications(c);
|
||||
});
|
||||
connect(c, &NeoChatConnection::showInviteNotification, &m_notificationsManager, &NotificationsManager::postInviteNotification);
|
||||
|
||||
c->sync();
|
||||
|
||||
@@ -295,7 +294,7 @@ bool Controller::supportSystemTray() const
|
||||
void Controller::setQuitOnLastWindowClosed()
|
||||
{
|
||||
#ifndef Q_OS_ANDROID
|
||||
if (NeoChatConfig::self()->systemTray()) {
|
||||
if (supportSystemTray() && NeoChatConfig::self()->systemTray()) {
|
||||
m_trayIcon = new TrayIcon(this);
|
||||
m_trayIcon->show();
|
||||
} else {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
@@ -37,7 +38,7 @@ FormCard.FormCardPage {
|
||||
}
|
||||
|
||||
function openEventSource(stateKey: string): void {
|
||||
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
model: stateKeysModel,
|
||||
allowEdit: true,
|
||||
room: root.room,
|
||||
|
||||
@@ -50,12 +50,17 @@ public:
|
||||
LiveLocation, /**< The initial event of a shared live location (i.e., the place where this is supposed to be shown in the timeline). */
|
||||
Encrypted, /**< An encrypted message that cannot be decrypted. */
|
||||
Reply, /**< A component to show a replied-to message. */
|
||||
Reaction, /**< A component to show the reactions to this message. */
|
||||
LinkPreview, /**< A preview of a URL in the message. */
|
||||
LinkPreviewLoad, /**< A loading dialog for a link preview. */
|
||||
ChatBar, /**< A text edit for editing a message. */
|
||||
ThreadRoot, /**< The root message of the thread. */
|
||||
ThreadBody, /**< The other messages in the thread. */
|
||||
ReplyButton, /**< A button to reply in the current thread. */
|
||||
FetchButton, /**< A button to fetch more messages in the current thread. */
|
||||
Verification, /**< A user verification session start message. */
|
||||
Loading, /**< The component is loading. */
|
||||
Separator, /**< A horizontal separator. */
|
||||
Other, /**< Anything that cannot be classified as another type. */
|
||||
};
|
||||
Q_ENUM(Type);
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2019 Kitsune Ral <Kitsune-Ral@users.sf.net>
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "joinrulesevent.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
QString JoinRulesEvent::joinRule() const
|
||||
{
|
||||
return fromJson<QString>(contentJson()["join_rule"_L1]);
|
||||
}
|
||||
|
||||
QJsonArray JoinRulesEvent::allow() const
|
||||
{
|
||||
return contentJson()["allow"_L1].toArray();
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/events/stateevent.h>
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
/**
|
||||
* @class JoinRulesEvent
|
||||
*
|
||||
* Class to define a join rule state event.
|
||||
*
|
||||
* @sa Quotient::StateEvent
|
||||
*/
|
||||
class JoinRulesEvent : public StateEvent
|
||||
{
|
||||
public:
|
||||
QUO_EVENT(JoinRulesEvent, "m.room.join_rules")
|
||||
|
||||
explicit JoinRulesEvent(const QJsonObject &obj)
|
||||
: StateEvent(obj)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The join rule for the room.
|
||||
*
|
||||
* see https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules for
|
||||
* the available join rules for a room.
|
||||
*/
|
||||
QString joinRule() const;
|
||||
|
||||
/**
|
||||
* @brief The allow rule for restricted rooms.
|
||||
*
|
||||
* see https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules for
|
||||
* full details on allow rules.
|
||||
*/
|
||||
QJsonArray allow() const;
|
||||
};
|
||||
}
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
#include "utils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
@@ -22,7 +21,6 @@ LinkPreviewer::LinkPreviewer(const QUrl &url, QObject *parent)
|
||||
Q_ASSERT(dynamic_cast<Connection *>(this->parent()));
|
||||
|
||||
connect(this, &LinkPreviewer::urlChanged, this, &LinkPreviewer::emptyChanged);
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, &LinkPreviewer::loadUrlPreview);
|
||||
|
||||
loadUrlPreview();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ Kirigami.Page {
|
||||
|
||||
property bool showExisting: false
|
||||
property bool _showExisting: showExisting && root.currentStepString === root.initialStep
|
||||
property bool showSettings: true
|
||||
property alias currentStep: module.item
|
||||
property string currentStepString: initialStep
|
||||
property string initialStep: "LoginRegister"
|
||||
@@ -265,6 +266,7 @@ Kirigami.Page {
|
||||
FormCard.FormCard {
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing * 2
|
||||
maximumWidth: Kirigami.Units.gridUnit * 20
|
||||
visible: root.showSettings
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18nc("@action:button", "Settings")
|
||||
icon.name: "settings-configure"
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <KCrash>
|
||||
#endif
|
||||
|
||||
#include <KIconTheme>
|
||||
#include <KLocalizedContext>
|
||||
#include <KLocalizedString>
|
||||
|
||||
@@ -101,6 +102,7 @@ Q_DECL_EXPORT
|
||||
#endif
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
KIconTheme::initTheme();
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||
|
||||
#ifdef HAVE_WEBVIEW
|
||||
@@ -237,6 +239,7 @@ int main(int argc, char *argv[])
|
||||
Q_IMPORT_QML_PLUGIN(org_kde_neochat_chatbarPlugin)
|
||||
|
||||
qml_register_types_org_kde_neochat();
|
||||
qmlRegisterUncreatableMetaObject(Quotient::staticMetaObject, "Quotient", 1, 0, "JoinRule", u"Access to JoinRule enum only"_s);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
|
||||
@@ -136,7 +136,11 @@ QList<ActionsModel::Action> actions{
|
||||
Action{
|
||||
u"plain"_s,
|
||||
[](const QString &text, NeoChatRoom *room, ChatBarCache *) {
|
||||
#if Quotient_VERSION_MINOR > 9
|
||||
room->postText(text.toHtmlEscaped());
|
||||
#else
|
||||
room->postPlainText(text.toHtmlEscaped());
|
||||
#endif
|
||||
return QString();
|
||||
},
|
||||
std::nullopt,
|
||||
|
||||
79
src/models/commonroomsmodel.cpp
Normal file
79
src/models/commonroomsmodel.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "commonroomsmodel.h"
|
||||
#include "jobs/neochatgetcommonroomsjob.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
CommonRoomsModel::CommonRoomsModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
NeoChatConnection *CommonRoomsModel::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
void CommonRoomsModel::setConnection(NeoChatConnection *connection)
|
||||
{
|
||||
m_connection = connection;
|
||||
Q_EMIT connectionChanged();
|
||||
reload();
|
||||
}
|
||||
|
||||
QString CommonRoomsModel::userId() const
|
||||
{
|
||||
return m_userId;
|
||||
}
|
||||
|
||||
void CommonRoomsModel::setUserId(const QString &userId)
|
||||
{
|
||||
m_userId = userId;
|
||||
Q_EMIT userIdChanged();
|
||||
reload();
|
||||
}
|
||||
|
||||
QVariant CommonRoomsModel::data(const QModelIndex &index, int roleName) const
|
||||
{
|
||||
Q_UNUSED(index)
|
||||
Q_UNUSED(roleName)
|
||||
return {};
|
||||
}
|
||||
|
||||
int CommonRoomsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_commonRooms.size();
|
||||
}
|
||||
|
||||
void CommonRoomsModel::reload()
|
||||
{
|
||||
if (!m_connection || m_userId.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_connection->canCheckMutualRooms()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Checking if you have mutual rooms with yourself doesn't make sense and servers reject it too
|
||||
if (m_connection->userId() == m_userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_connection->callApi<NeochatGetCommonRoomsJob>(m_userId).then([this](const auto job) {
|
||||
const auto &replyData = job->jsonData();
|
||||
beginResetModel();
|
||||
for (const auto &roomId : replyData[u"joined"_s].toArray()) {
|
||||
m_commonRooms.push_back(roomId.toString());
|
||||
}
|
||||
endResetModel();
|
||||
Q_EMIT countChanged();
|
||||
});
|
||||
}
|
||||
|
||||
#include "moc_commonroomsmodel.cpp"
|
||||
58
src/models/commonroomsmodel.h
Normal file
58
src/models/commonroomsmodel.h
Normal file
@@ -0,0 +1,58 @@
|
||||
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
/**
|
||||
* @brief Model to show the common or mutual rooms between you and another user.
|
||||
*/
|
||||
class CommonRoomsModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
Q_PROPERTY(NeoChatConnection *connection WRITE setConnection READ connection NOTIFY connectionChanged REQUIRED)
|
||||
Q_PROPERTY(QString userId WRITE setUserId READ userId NOTIFY userIdChanged REQUIRED)
|
||||
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
TextRole = Qt::DisplayRole,
|
||||
LongitudeRole,
|
||||
LatitudeRole,
|
||||
AssetRole,
|
||||
AuthorRole,
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
explicit CommonRoomsModel(QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] NeoChatConnection *connection() const;
|
||||
void setConnection(NeoChatConnection *connection);
|
||||
|
||||
[[nodiscard]] QString userId() const;
|
||||
void setUserId(const QString &userId);
|
||||
|
||||
[[nodiscard]] QVariant data(const QModelIndex &index, int roleName) const override;
|
||||
[[nodiscard]] Q_INVOKABLE int rowCount(const QModelIndex &parent = {}) const override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
void userIdChanged();
|
||||
void countChanged();
|
||||
|
||||
private:
|
||||
void reload();
|
||||
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
QString m_userId;
|
||||
QList<QString> m_commonRooms;
|
||||
};
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "messagecontentmodel.h"
|
||||
#include "eventhandler.h"
|
||||
#include "messagecomponenttype.h"
|
||||
#include "neochatconfig.h"
|
||||
|
||||
#include <QImageReader>
|
||||
@@ -27,6 +28,7 @@
|
||||
#include "chatbarcache.h"
|
||||
#include "filetype.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "models/reactionmodel.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "texthandler.h"
|
||||
@@ -130,9 +132,6 @@ void MessageContentModel::initializeModel()
|
||||
connect(m_room, &NeoChatRoom::urlPreviewEnabledChanged, this, [this]() {
|
||||
resetContent();
|
||||
});
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, [this]() {
|
||||
resetContent();
|
||||
});
|
||||
connect(m_room, &Room::memberNameUpdated, this, [this](RoomMember member) {
|
||||
if (m_room != nullptr) {
|
||||
if (senderId().isEmpty() || senderId() == member.id()) {
|
||||
@@ -152,12 +151,18 @@ void MessageContentModel::initializeModel()
|
||||
updateReplyModel();
|
||||
resetModel();
|
||||
});
|
||||
connect(m_room, &Room::updatedEvent, this, [this](const QString &eventId) {
|
||||
if (eventId == m_eventId) {
|
||||
updateReactionModel();
|
||||
}
|
||||
});
|
||||
|
||||
initializeEvent();
|
||||
if (m_currentState == Available || m_currentState == Pending) {
|
||||
updateReplyModel();
|
||||
}
|
||||
resetModel();
|
||||
updateReactionModel();
|
||||
}
|
||||
|
||||
void MessageContentModel::initializeEvent()
|
||||
@@ -340,6 +345,21 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
||||
if (role == ReplyContentModelRole) {
|
||||
return QVariant::fromValue<MessageContentModel *>(m_replyModel);
|
||||
}
|
||||
if (role == ReactionModelRole) {
|
||||
return QVariant::fromValue<ReactionModel *>(m_reactionModel);
|
||||
;
|
||||
}
|
||||
if (role == ThreadRootRole) {
|
||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event.first);
|
||||
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||
if (roomMessageEvent && (roomMessageEvent->isThreaded() || m_room->threads().contains(roomMessageEvent->id()))) {
|
||||
#else
|
||||
if (roomMessageEvent && roomMessageEvent->isThreaded()) {
|
||||
#endif
|
||||
return roomMessageEvent->threadRootEventId();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
if (role == LinkPreviewerRole) {
|
||||
if (component.type == MessageComponentType::LinkPreview) {
|
||||
return QVariant::fromValue<LinkPreviewer *>(
|
||||
@@ -366,27 +386,33 @@ int MessageContentModel::rowCount(const QModelIndex &parent) const
|
||||
|
||||
QHash<int, QByteArray> MessageContentModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
|
||||
roles[DisplayRole] = "display";
|
||||
roles[ComponentTypeRole] = "componentType";
|
||||
roles[ComponentAttributesRole] = "componentAttributes";
|
||||
roles[EventIdRole] = "eventId";
|
||||
roles[TimeRole] = "time";
|
||||
roles[TimeStringRole] = "timeString";
|
||||
roles[AuthorRole] = "author";
|
||||
roles[MediaInfoRole] = "mediaInfo";
|
||||
roles[FileTransferInfoRole] = "fileTransferInfo";
|
||||
roles[ItineraryModelRole] = "itineraryModel";
|
||||
roles[LatitudeRole] = "latitude";
|
||||
roles[LongitudeRole] = "longitude";
|
||||
roles[AssetRole] = "asset";
|
||||
roles[PollHandlerRole] = "pollHandler";
|
||||
roles[ReplyEventIdRole] = "replyEventId";
|
||||
roles[ReplyAuthorRole] = "replyAuthor";
|
||||
roles[ReplyContentModelRole] = "replyContentModel";
|
||||
roles[ThreadRootRole] = "threadRoot";
|
||||
roles[LinkPreviewerRole] = "linkPreviewer";
|
||||
roles[ChatBarCacheRole] = "chatBarCache";
|
||||
return roleNamesStatic();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> MessageContentModel::roleNamesStatic()
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[MessageContentModel::DisplayRole] = "display";
|
||||
roles[MessageContentModel::ComponentTypeRole] = "componentType";
|
||||
roles[MessageContentModel::ComponentAttributesRole] = "componentAttributes";
|
||||
roles[MessageContentModel::EventIdRole] = "eventId";
|
||||
roles[MessageContentModel::TimeRole] = "time";
|
||||
roles[MessageContentModel::TimeStringRole] = "timeString";
|
||||
roles[MessageContentModel::AuthorRole] = "author";
|
||||
roles[MessageContentModel::MediaInfoRole] = "mediaInfo";
|
||||
roles[MessageContentModel::FileTransferInfoRole] = "fileTransferInfo";
|
||||
roles[MessageContentModel::ItineraryModelRole] = "itineraryModel";
|
||||
roles[MessageContentModel::LatitudeRole] = "latitude";
|
||||
roles[MessageContentModel::LongitudeRole] = "longitude";
|
||||
roles[MessageContentModel::AssetRole] = "asset";
|
||||
roles[MessageContentModel::PollHandlerRole] = "pollHandler";
|
||||
roles[MessageContentModel::ReplyEventIdRole] = "replyEventId";
|
||||
roles[MessageContentModel::ReplyAuthorRole] = "replyAuthor";
|
||||
roles[MessageContentModel::ReplyContentModelRole] = "replyContentModel";
|
||||
roles[MessageContentModel::ReactionModelRole] = "reactionModel";
|
||||
roles[MessageContentModel::ThreadRootRole] = "threadRoot";
|
||||
roles[MessageContentModel::LinkPreviewerRole] = "linkPreviewer";
|
||||
roles[MessageContentModel::ChatBarCacheRole] = "chatBarCache";
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -464,6 +490,21 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
|
||||
newComponents = addLinkPreviews(newComponents);
|
||||
}
|
||||
|
||||
if ((m_reactionModel && m_reactionModel->rowCount() > 0)) {
|
||||
newComponents += MessageComponent{MessageComponentType::Reaction, QString(), {}};
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||
if (NeoChatConfig::self()->threads() && roomMessageEvent && (roomMessageEvent->isThreaded() || m_room->threads().contains(roomMessageEvent->id()))
|
||||
&& roomMessageEvent->id() == roomMessageEvent->threadRootEventId()) {
|
||||
#else
|
||||
if (NeoChatConfig::self()->threads() && roomMessageEvent && roomMessageEvent->isThreaded()
|
||||
&& roomMessageEvent->id() == roomMessageEvent->threadRootEventId()) {
|
||||
#endif
|
||||
newComponents += MessageComponent{MessageComponentType::Separator, {}, {}};
|
||||
newComponents += MessageComponent{MessageComponentType::ThreadBody, u"Thread Body"_s, {}};
|
||||
}
|
||||
|
||||
// If the event is already threaded the ThreadModel will handle displaying a chat bar.
|
||||
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||
if (isThreading && roomMessageEvent && !(roomMessageEvent->isThreaded() || m_room->threads().contains(roomMessageEvent->id()))) {
|
||||
@@ -487,7 +528,7 @@ void MessageContentModel::updateReplyModel()
|
||||
if (roomMessageEvent == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!roomMessageEvent->isReply() || (roomMessageEvent->isThreaded() && NeoChatConfig::self()->threads())) {
|
||||
if (!roomMessageEvent->isReply(!NeoChatConfig::self()->threads()) || (roomMessageEvent->isThreaded() && NeoChatConfig::self()->threads())) {
|
||||
if (m_replyModel) {
|
||||
delete m_replyModel;
|
||||
}
|
||||
@@ -498,7 +539,7 @@ void MessageContentModel::updateReplyModel()
|
||||
return;
|
||||
}
|
||||
|
||||
m_replyModel = new MessageContentModel(m_room, roomMessageEvent->replyEventId(), true, false, this);
|
||||
m_replyModel = new MessageContentModel(m_room, roomMessageEvent->replyEventId(!NeoChatConfig::self()->threads()), true, false, this);
|
||||
|
||||
connect(m_replyModel, &MessageContentModel::eventUpdated, this, [this]() {
|
||||
Q_EMIT dataChanged(index(0), index(0), {ReplyAuthorRole});
|
||||
@@ -705,4 +746,25 @@ void MessageContentModel::updateItineraryModel()
|
||||
}
|
||||
}
|
||||
|
||||
void MessageContentModel::updateReactionModel()
|
||||
{
|
||||
if (m_reactionModel != nullptr && m_reactionModel->rowCount() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_reactionModel == nullptr) {
|
||||
m_reactionModel = new ReactionModel(this, m_eventId, m_room);
|
||||
connect(m_reactionModel, &ReactionModel::reactionsUpdated, this, &MessageContentModel::updateReactionModel);
|
||||
}
|
||||
|
||||
if (m_reactionModel->rowCount() <= 0) {
|
||||
m_reactionModel->disconnect(this);
|
||||
delete m_reactionModel;
|
||||
m_reactionModel = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
resetContent();
|
||||
}
|
||||
|
||||
#include "moc_messagecontentmodel.cpp"
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/events/roomevent.h>
|
||||
#include <Quotient/room.h>
|
||||
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "itinerarymodel.h"
|
||||
#include "messagecomponent.h"
|
||||
#include "models/reactionmodel.h"
|
||||
#include "neochatroommember.h"
|
||||
|
||||
/**
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
ReplyAuthorRole, /**< The author of the event that was replied to. */
|
||||
ReplyContentModelRole, /**< The MessageContentModel for the reply event. */
|
||||
|
||||
ReactionModelRole, /**< Reaction model for this event. */
|
||||
|
||||
ThreadRootRole, /**< The thread root event ID for the event. */
|
||||
|
||||
LinkPreviewerRole, /**< The link preview details. */
|
||||
@@ -91,6 +93,8 @@ public:
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
static QHash<int, QByteArray> roleNamesStatic();
|
||||
|
||||
/**
|
||||
* @brief Close the link preview at the given index.
|
||||
*
|
||||
@@ -123,6 +127,7 @@ private:
|
||||
QPointer<MessageContentModel> m_replyModel;
|
||||
void updateReplyModel();
|
||||
|
||||
ReactionModel *m_reactionModel = nullptr;
|
||||
ItineraryModel *m_itineraryModel = nullptr;
|
||||
|
||||
QList<MessageComponent> componentsForType(MessageComponentType::Type type);
|
||||
@@ -133,4 +138,6 @@ private:
|
||||
|
||||
void updateItineraryModel();
|
||||
bool m_emptyItinerary = false;
|
||||
|
||||
void updateReactionModel();
|
||||
};
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
|
||||
#include "neochatconfig.h"
|
||||
|
||||
#include <Quotient/events/encryptedevent.h>
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
#include <Quotient/events/stickerevent.h>
|
||||
#if Quotient_VERSION_MINOR > 9
|
||||
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||
#include <Quotient/thread.h>
|
||||
#endif
|
||||
|
||||
@@ -120,6 +121,14 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
if (role == ContentModelRole) {
|
||||
if (event->get().is<EncryptedEvent>() || event->get().is<StickerEvent>()) {
|
||||
return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(event->get().id()));
|
||||
}
|
||||
|
||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(&event.value().get());
|
||||
if (NeoChatConfig::self()->threads() && roomMessageEvent && roomMessageEvent->isThreaded()) {
|
||||
return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(roomMessageEvent->threadRootEventId()));
|
||||
}
|
||||
return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(&event->get()));
|
||||
}
|
||||
|
||||
@@ -169,7 +178,7 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(&event.value().get());
|
||||
#if Quotient_VERSION_MINOR > 9
|
||||
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||
if (roomMessageEvent && (roomMessageEvent->isThreaded() || m_room->threads().contains(event.value().get().id()))) {
|
||||
const auto &thread = m_room->threads().value(roomMessageEvent->isThreaded() ? roomMessageEvent->threadRootEventId() : event.value().get().id());
|
||||
if (thread.latestEventId != event.value().get().id()) {
|
||||
@@ -212,6 +221,9 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
if (role == IsThreadedRole) {
|
||||
if (!NeoChatConfig::self()->threads()) {
|
||||
return false;
|
||||
}
|
||||
if (auto roomMessageEvent = eventCast<const RoomMessageEvent>(&event.value().get())) {
|
||||
return roomMessageEvent->isThreaded();
|
||||
}
|
||||
@@ -254,18 +266,6 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
|
||||
return m_readMarkerModels.contains(event.value().get().id());
|
||||
}
|
||||
|
||||
if (role == ReactionRole) {
|
||||
if (m_reactionModels.contains(event.value().get().id())) {
|
||||
return QVariant::fromValue<ReactionModel *>(m_reactionModels[event.value().get().id()].data());
|
||||
} else {
|
||||
return QVariantList();
|
||||
}
|
||||
}
|
||||
|
||||
if (role == ShowReactionsRole) {
|
||||
return m_reactionModels.contains(event.value().get().id());
|
||||
}
|
||||
|
||||
if (role == VerifiedRole) {
|
||||
if (event.value().get().originalEvent()) {
|
||||
auto encrypted = dynamic_cast<const EncryptedEvent *>(event.value().get().originalEvent());
|
||||
@@ -319,8 +319,6 @@ QHash<int, QByteArray> MessageModel::roleNames() const
|
||||
roles[ShowSectionRole] = "showSection";
|
||||
roles[ReadMarkersRole] = "readMarkers";
|
||||
roles[ShowReadMarkersRole] = "showReadMarkers";
|
||||
roles[ReactionRole] = "reaction";
|
||||
roles[ShowReactionsRole] = "showReactions";
|
||||
roles[VerifiedRole] = "verified";
|
||||
roles[AuthorDisplayNameRole] = "authorDisplayName";
|
||||
roles[IsRedactedRole] = "isRedacted";
|
||||
@@ -450,31 +448,6 @@ void MessageModel::createEventObjects(const Quotient::RoomEvent *event, bool isP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto roomEvent = eventCast<const RoomMessageEvent>(event)) {
|
||||
// ReactionModel handles updates to add and remove reactions, we only need to
|
||||
// handle adding and removing whole models here.
|
||||
if (m_reactionModels.contains(eventId)) {
|
||||
// If a model already exists but now has no reactions remove it
|
||||
if (m_reactionModels[eventId]->rowCount() <= 0) {
|
||||
m_reactionModels.remove(eventId);
|
||||
if (!resetting) {
|
||||
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (m_room->relatedEvents(*event, Quotient::EventRelation::AnnotationType).count() > 0) {
|
||||
// If a model doesn't exist and there are reactions add it.
|
||||
auto reactionModel = QSharedPointer<ReactionModel>(new ReactionModel(roomEvent, m_room));
|
||||
if (reactionModel->rowCount() > 0) {
|
||||
m_reactionModels[eventId] = reactionModel;
|
||||
if (!resetting) {
|
||||
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageModel::clearModel()
|
||||
@@ -500,7 +473,6 @@ void MessageModel::clearModel()
|
||||
|
||||
void MessageModel::clearEventObjects()
|
||||
{
|
||||
m_reactionModels.clear();
|
||||
m_readMarkerModels.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ class MessageModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
/**
|
||||
* @brief The current room that the model is getting its messages from.
|
||||
@@ -76,8 +77,6 @@ public:
|
||||
|
||||
ReadMarkersRole, /**< The first 5 other users at the event for read marker tracking. */
|
||||
ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */
|
||||
ReactionRole, /**< List model for this event. */
|
||||
ShowReactionsRole, /**< Whether there are any reactions to be shown. */
|
||||
|
||||
VerifiedRole, /**< Whether an encrypted message is sent in a verified session. */
|
||||
AuthorDisplayNameRole, /**< The displayname for the event's sender; for name change events, the old displayname. */
|
||||
@@ -154,7 +153,6 @@ private:
|
||||
bool movingEvent = false;
|
||||
|
||||
QMap<QString, QSharedPointer<ReadMarkerModel>> m_readMarkerModels;
|
||||
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
||||
|
||||
void createEventObjects(const Quotient::RoomEvent *event, bool isPending = false);
|
||||
};
|
||||
|
||||
@@ -121,6 +121,10 @@ void NotificationsModel::loadData()
|
||||
const auto &authorAvatar = avatar.isValid() && avatar.scheme() == u"mxc"_s ? avatar : QUrl();
|
||||
|
||||
const auto &roomEvent = eventCast<const RoomEvent>(notification.event.get());
|
||||
if (!roomEvent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
beginInsertRows({}, m_notifications.length(), m_notifications.length());
|
||||
m_notifications += Notification{
|
||||
.roomId = notification.roomId,
|
||||
|
||||
@@ -9,22 +9,27 @@
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include "neochatroom.h"
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
ReactionModel::ReactionModel(const Quotient::RoomMessageEvent *event, NeoChatRoom *room)
|
||||
: QAbstractListModel(nullptr)
|
||||
ReactionModel::ReactionModel(MessageContentModel *parent, const QString &eventId, NeoChatRoom *room)
|
||||
: QAbstractListModel(parent)
|
||||
, m_room(room)
|
||||
, m_event(event)
|
||||
, m_eventId(eventId)
|
||||
{
|
||||
if (m_event != nullptr && m_room != nullptr) {
|
||||
connect(m_room, &NeoChatRoom::updatedEvent, this, [this](const QString &eventId) {
|
||||
if (m_event && m_event->id() == eventId) {
|
||||
updateReactions();
|
||||
}
|
||||
});
|
||||
Q_ASSERT(parent);
|
||||
Q_ASSERT(parent != nullptr);
|
||||
Q_ASSERT(!eventId.isEmpty());
|
||||
Q_ASSERT(room != nullptr);
|
||||
|
||||
updateReactions();
|
||||
}
|
||||
connect(m_room, &NeoChatRoom::updatedEvent, this, [this](const QString &eventId) {
|
||||
if (m_eventId == eventId) {
|
||||
updateReactions();
|
||||
}
|
||||
});
|
||||
|
||||
updateReactions();
|
||||
}
|
||||
|
||||
QVariant ReactionModel::data(const QModelIndex &index, int role) const
|
||||
@@ -99,12 +104,16 @@ int ReactionModel::rowCount(const QModelIndex &parent) const
|
||||
|
||||
void ReactionModel::updateReactions()
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
|
||||
m_reactions.clear();
|
||||
m_shortcodes.clear();
|
||||
|
||||
const auto &annotations = m_room->relatedEvents(*m_event, Quotient::EventRelation::AnnotationType);
|
||||
const auto &annotations = m_room->relatedEvents(m_eventId, Quotient::EventRelation::AnnotationType);
|
||||
if (annotations.isEmpty()) {
|
||||
endResetModel();
|
||||
return;
|
||||
|
||||
@@ -3,11 +3,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "neochatroom.h"
|
||||
#include <QAbstractListModel>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/events/reactionevent.h>
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class RoomMessageEvent;
|
||||
}
|
||||
|
||||
class MessageContentModel;
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
* @class ReactionModel
|
||||
*
|
||||
@@ -38,7 +47,7 @@ public:
|
||||
HasLocalMember, /**< Whether the local member is in the list of authors. */
|
||||
};
|
||||
|
||||
explicit ReactionModel(const Quotient::RoomMessageEvent *event, NeoChatRoom *room);
|
||||
explicit ReactionModel(MessageContentModel *parent, const QString &eventId, NeoChatRoom *room);
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
@@ -61,9 +70,15 @@ public:
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* @brief The reactions in the model have been updated.
|
||||
*/
|
||||
void reactionsUpdated();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
const Quotient::RoomMessageEvent *m_event;
|
||||
QString m_eventId;
|
||||
QList<Reaction> m_reactions;
|
||||
QMap<QString, QString> m_shortcodes;
|
||||
|
||||
|
||||
@@ -348,6 +348,12 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
||||
return QVariant::fromValue(room);
|
||||
}
|
||||
if (role == SubtitleTextRole) {
|
||||
if (room->isInvite()) {
|
||||
if (room->isDirectChat()) {
|
||||
return i18nc("@info:label", "Invited you to chat");
|
||||
}
|
||||
return i18nc("@info:label", "%1 invited you", room->member(room->invitingUserId()).displayName());
|
||||
}
|
||||
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
@@ -11,18 +11,18 @@
|
||||
#include "chatbarcache.h"
|
||||
#include "eventhandler.h"
|
||||
#include "messagecomponenttype.h"
|
||||
#include "messagecontentmodel.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
ThreadModel::ThreadModel(const QString &threadRootId, NeoChatRoom *room)
|
||||
: QConcatenateTablesProxyModel(room)
|
||||
, m_threadRootId(threadRootId)
|
||||
, m_threadFetchModel(new ThreadFetchModel(this))
|
||||
, m_threadChatBarModel(new ThreadChatBarModel(this, room))
|
||||
{
|
||||
Q_ASSERT(!m_threadRootId.isEmpty());
|
||||
Q_ASSERT(room);
|
||||
|
||||
m_threadRootContentModel = std::unique_ptr<MessageContentModel>(new MessageContentModel(room, threadRootId));
|
||||
|
||||
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 0)
|
||||
connect(room, &Quotient::Room::pendingEventAdded, this, [this](const Quotient::RoomEvent *event) {
|
||||
#else
|
||||
@@ -49,7 +49,7 @@ ThreadModel::ThreadModel(const QString &threadRootId, NeoChatRoom *room)
|
||||
// If the thread was created by the local user fetchMore() won't find the current
|
||||
// pending event.
|
||||
checkPending();
|
||||
fetchMore({});
|
||||
fetchMoreEvents(3);
|
||||
addModels();
|
||||
}
|
||||
|
||||
@@ -73,29 +73,24 @@ QString ThreadModel::threadRootId() const
|
||||
return m_threadRootId;
|
||||
}
|
||||
|
||||
MessageContentModel *ThreadModel::threadRootContentModel() const
|
||||
{
|
||||
return m_threadRootContentModel.get();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ThreadModel::roleNames() const
|
||||
{
|
||||
return m_threadRootContentModel->roleNames();
|
||||
return MessageContentModel::roleNamesStatic();
|
||||
}
|
||||
|
||||
bool ThreadModel::canFetchMore(const QModelIndex &parent) const
|
||||
bool ThreadModel::moreEventsAvailable(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return !m_currentJob && m_nextBatch.has_value();
|
||||
}
|
||||
|
||||
void ThreadModel::fetchMore(const QModelIndex &parent)
|
||||
void ThreadModel::fetchMoreEvents(int max)
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
if (!m_currentJob && m_nextBatch.has_value()) {
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent());
|
||||
const auto connection = room->connection();
|
||||
m_currentJob = connection->callApi<Quotient::GetRelatingEventsWithRelTypeJob>(room->id(), m_threadRootId, u"m.thread"_s, *m_nextBatch, QString(), 5);
|
||||
m_currentJob = connection->callApi<Quotient::GetRelatingEventsWithRelTypeJob>(room->id(), m_threadRootId, u"m.thread"_s, *m_nextBatch, QString(), max);
|
||||
Q_EMIT moreEventsAvailableChanged();
|
||||
connect(m_currentJob, &Quotient::BaseJob::success, this, [this]() {
|
||||
auto newEvents = m_currentJob->chunk();
|
||||
for (auto &event : newEvents) {
|
||||
@@ -115,6 +110,7 @@ void ThreadModel::fetchMore(const QModelIndex &parent)
|
||||
}
|
||||
|
||||
m_currentJob.clear();
|
||||
Q_EMIT moreEventsAvailableChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -134,11 +130,11 @@ void ThreadModel::addModels()
|
||||
clearModels();
|
||||
}
|
||||
|
||||
addSourceModel(m_threadRootContentModel.get());
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent());
|
||||
if (room == nullptr) {
|
||||
return;
|
||||
}
|
||||
addSourceModel(m_threadFetchModel);
|
||||
for (auto it = m_events.crbegin(); it != m_events.crend(); ++it) {
|
||||
const auto contentModel = room->contentModelForEvent(*it);
|
||||
if (contentModel != nullptr) {
|
||||
@@ -153,12 +149,11 @@ void ThreadModel::addModels()
|
||||
|
||||
void ThreadModel::clearModels()
|
||||
{
|
||||
removeSourceModel(m_threadRootContentModel.get());
|
||||
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent());
|
||||
if (room == nullptr) {
|
||||
return;
|
||||
}
|
||||
removeSourceModel(m_threadFetchModel);
|
||||
for (const auto &model : m_events) {
|
||||
const auto contentModel = room->contentModelForEvent(model);
|
||||
if (sourceModels().contains(contentModel)) {
|
||||
@@ -168,6 +163,76 @@ void ThreadModel::clearModels()
|
||||
removeSourceModel(m_threadChatBarModel);
|
||||
}
|
||||
|
||||
void ThreadModel::closeLinkPreview(int row)
|
||||
{
|
||||
if (row < 0 || row >= rowCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto index = this->index(row, 0);
|
||||
if (!index.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto sourceIndex = mapToSource(index);
|
||||
const auto sourceModel = sourceIndex.model();
|
||||
if (sourceModel == nullptr) {
|
||||
return;
|
||||
}
|
||||
// This is a bit silly but we can only get a const reference to the model from the
|
||||
// index so we need to search the source models.
|
||||
for (const auto &model : sourceModels()) {
|
||||
if (model == sourceModel) {
|
||||
const auto sourceContentModel = dynamic_cast<MessageContentModel *>(model);
|
||||
if (sourceContentModel == nullptr) {
|
||||
return;
|
||||
}
|
||||
sourceContentModel->closeLinkPreview(sourceIndex.row());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThreadFetchModel::ThreadFetchModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
const auto threadModel = dynamic_cast<ThreadModel *>(parent);
|
||||
Q_ASSERT(threadModel != nullptr);
|
||||
connect(threadModel, &ThreadModel::moreEventsAvailableChanged, this, [this]() {
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
});
|
||||
}
|
||||
|
||||
QVariant ThreadFetchModel::data(const QModelIndex &idx, int role) const
|
||||
{
|
||||
if (idx.row() < 0 || idx.row() > 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (role == ComponentTypeRole) {
|
||||
return MessageComponentType::FetchButton;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
int ThreadFetchModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
const auto threadModel = dynamic_cast<ThreadModel *>(this->parent());
|
||||
if (threadModel == nullptr) {
|
||||
qWarning() << "ThreadFetchModel created with incorrect parent, a ThreadModel must be set as the parent on creation.";
|
||||
return {};
|
||||
}
|
||||
return threadModel->moreEventsAvailable({}) ? 1 : 0;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ThreadFetchModel::roleNames() const
|
||||
{
|
||||
return {
|
||||
{ComponentTypeRole, "componentType"},
|
||||
};
|
||||
}
|
||||
|
||||
ThreadChatBarModel::ThreadChatBarModel(QObject *parent, NeoChatRoom *room)
|
||||
: QAbstractListModel(parent)
|
||||
, m_room(room)
|
||||
|
||||
@@ -19,7 +19,52 @@
|
||||
#include "messagecontentmodel.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
class ReactionModel;
|
||||
|
||||
/**
|
||||
* @class ThreadFetchModel
|
||||
*
|
||||
* A model to provide a fetch more historical messages button in a thread.
|
||||
*/
|
||||
class ThreadFetchModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*
|
||||
* The role values need to match MessageContentModel not to blow up.
|
||||
*
|
||||
* @sa MessageContentModel
|
||||
*/
|
||||
enum Roles {
|
||||
ComponentTypeRole = MessageContentModel::ComponentTypeRole, /**< The type of component to visualise the message. */
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
explicit ThreadFetchModel(QObject *parent);
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
*
|
||||
* @sa QAbstractItemModel::data
|
||||
*/
|
||||
[[nodiscard]] QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
||||
|
||||
/**
|
||||
* @brief 1 or 0, depending on whether there are more messages to download.
|
||||
*
|
||||
* @sa QAbstractItemModel::rowCount
|
||||
*/
|
||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
/**
|
||||
* @brief Returns a map with ComponentTypeRole it's the only one.
|
||||
*
|
||||
* @sa Roles, QAbstractItemModel::roleNames()
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class ThreadChatBarModel
|
||||
@@ -91,11 +136,6 @@ public:
|
||||
|
||||
QString threadRootId() const;
|
||||
|
||||
/**
|
||||
* @brief The content model for the thread root event.
|
||||
*/
|
||||
MessageContentModel *threadRootContentModel() const;
|
||||
|
||||
/**
|
||||
* @brief Returns a mapping from Role enum values to role names.
|
||||
*
|
||||
@@ -104,29 +144,33 @@ public:
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
/**
|
||||
* @brief Whether there is more data available for the model to fetch.
|
||||
*
|
||||
* @sa QAbstractItemModel::canFetchMore()
|
||||
* @brief Whether there are more events for the model to fetch.
|
||||
*/
|
||||
bool canFetchMore(const QModelIndex &parent) const override;
|
||||
bool moreEventsAvailable(const QModelIndex &parent) const;
|
||||
|
||||
/**
|
||||
* @brief Fetches the next batch of model data if any is available.
|
||||
*
|
||||
* @sa QAbstractItemModel::fetchMore()
|
||||
* @brief Fetches the next batch of events if any is available.
|
||||
*/
|
||||
void fetchMore(const QModelIndex &parent) override;
|
||||
Q_INVOKABLE void fetchMoreEvents(int max = 5);
|
||||
|
||||
/**
|
||||
* @brief Close the link preview at the given index.
|
||||
*
|
||||
* If the given index is not a link preview component, nothing happens.
|
||||
*/
|
||||
Q_INVOKABLE void closeLinkPreview(int row);
|
||||
|
||||
Q_SIGNALS:
|
||||
void moreEventsAvailableChanged();
|
||||
|
||||
private:
|
||||
QString m_threadRootId;
|
||||
|
||||
std::unique_ptr<MessageContentModel> m_threadRootContentModel;
|
||||
QPointer<MessageContentModel> m_threadRootContentModel;
|
||||
|
||||
std::deque<QString> m_events;
|
||||
ThreadFetchModel *m_threadFetchModel;
|
||||
ThreadChatBarModel *m_threadChatBarModel;
|
||||
|
||||
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
||||
|
||||
QPointer<Quotient::GetRelatingEventsWithRelTypeJob> m_currentJob = nullptr;
|
||||
std::optional<QString> m_nextBatch = QString();
|
||||
bool m_addingPending = false;
|
||||
|
||||
@@ -27,24 +27,20 @@ void TimelineMessageModel::connectNewRoom()
|
||||
}
|
||||
|
||||
connect(m_room, &Room::aboutToAddNewMessages, this, [this](RoomEventsRange events) {
|
||||
for (auto &&event : events) {
|
||||
Q_EMIT newEventAdded(event.get());
|
||||
}
|
||||
m_initialized = true;
|
||||
beginInsertRows({}, timelineServerIndex(), timelineServerIndex() + int(events.size()) - 1);
|
||||
});
|
||||
connect(m_room, &Room::aboutToAddHistoricalMessages, this, [this](RoomEventsRange events) {
|
||||
for (auto &event : events) {
|
||||
Q_EMIT newEventAdded(event.get());
|
||||
}
|
||||
if (rowCount() > 0) {
|
||||
rowBelowInserted = rowCount() - 1; // See #312
|
||||
}
|
||||
m_initialized = true;
|
||||
beginInsertRows({}, rowCount(), rowCount() + int(events.size()) - 1);
|
||||
});
|
||||
connect(m_room, &Room::addedMessages, this, [this](int lowest, int biggest) {
|
||||
if (m_initialized) {
|
||||
for (int i = lowest; i == biggest; ++i) {
|
||||
const auto event = m_room->findInTimeline(i)->event();
|
||||
Q_EMIT newEventAdded(event);
|
||||
}
|
||||
|
||||
endInsertRows();
|
||||
}
|
||||
if (!m_lastReadEventIndex.isValid()) {
|
||||
|
||||
@@ -32,42 +32,6 @@ class TimelineMessageModel : public MessageModel
|
||||
QML_ELEMENT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*/
|
||||
enum EventRoles {
|
||||
DelegateTypeRole = Qt::UserRole + 1, /**< The delegate type of the message. */
|
||||
EventIdRole, /**< The matrix event ID of the event. */
|
||||
TimeRole, /**< The timestamp for when the event was sent (as a QDateTime). */
|
||||
SectionRole, /**< The date of the event as a string. */
|
||||
AuthorRole, /**< The author of the event. */
|
||||
HighlightRole, /**< Whether the event should be highlighted. */
|
||||
SpecialMarksRole, /**< Whether the event is hidden or not. */
|
||||
ProgressInfoRole, /**< Progress info when downloading files. */
|
||||
GenericDisplayRole, /**< A generic string based upon the message type. */
|
||||
MediaInfoRole, /**< The media info for the event. */
|
||||
|
||||
ContentModelRole, /**< The MessageContentModel for the event. */
|
||||
|
||||
IsThreadedRole, /**< Whether the message is in a thread. */
|
||||
ThreadRootRole, /**< The Matrix ID of the thread root message, if any . */
|
||||
|
||||
ShowSectionRole, /**< Whether the section header should be shown. */
|
||||
|
||||
ReadMarkersRole, /**< The first 5 other users at the event for read marker tracking. */
|
||||
ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */
|
||||
ReactionRole, /**< List model for this event. */
|
||||
ShowReactionsRole, /**< Whether there are any reactions to be shown. */
|
||||
|
||||
VerifiedRole, /**< Whether an encrypted message is sent in a verified session. */
|
||||
AuthorDisplayNameRole, /**< The displayname for the event's sender; for name change events, the old displayname. */
|
||||
IsRedactedRole, /**< Whether an event has been deleted. */
|
||||
IsPendingRole, /**< Whether an event is waiting to be accepted by the server. */
|
||||
IsEditableRole, /**< Whether the event can be edited by the user. */
|
||||
LastRole, // Keep this last
|
||||
};
|
||||
Q_ENUM(EventRoles)
|
||||
|
||||
explicit TimelineMessageModel(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,7 +39,7 @@ struct WebShortcutModelPrivate;
|
||||
* }
|
||||
* QQC2.MenuSeparator {}
|
||||
* QQC2.MenuItem {
|
||||
* text: i18n("Configure Web Shortcuts...")
|
||||
* text: i18n("Configure Web Shortcuts…")
|
||||
* icon.name: "configure"
|
||||
* onTriggered: webshortcutmodel.configureWebShortcuts()
|
||||
* }
|
||||
|
||||
@@ -290,6 +290,7 @@ Name[hu]=Megosztás
|
||||
Name[ia]=Comparti
|
||||
Name[it]=Condivisione
|
||||
Name[ka]=გაზიარება
|
||||
Name[ko]=공유
|
||||
Name[lv]=Kopīgot
|
||||
Name[nl]=Gedeelde
|
||||
Name[nn]=Del
|
||||
@@ -303,6 +304,7 @@ Name[ta]=பகிர்
|
||||
Name[tr]=Paylaş
|
||||
Name[uk]=Оприлюднення
|
||||
Name[x-test]=xxSharexx
|
||||
Name[zh_CN]=分享
|
||||
Name[zh_TW]=分享
|
||||
Comment=The result of sharing a piece of content
|
||||
Comment[ar]=نتيجة مشاركة محتوى
|
||||
@@ -323,6 +325,7 @@ Comment[hu]=Tartalom megosztásának eredménye
|
||||
Comment[ia]=Le exito de compartir un pecietta de contento
|
||||
Comment[it]=Il risultato della condivisione di un contenuto
|
||||
Comment[ka]=შემცველობის ნაწილის გაზიარების შედეგი
|
||||
Comment[ko]=콘텐츠 공유 결과
|
||||
Comment[lv]=Satura kopīgošanas rezultāts
|
||||
Comment[nl]=Het resultaat van het delen van een stukje inhoud
|
||||
Comment[nn]=Resultatet av deling av innhald
|
||||
@@ -336,5 +339,6 @@ Comment[ta]=எதையோ பகிர்ந்ததன் விளைவ
|
||||
Comment[tr]=Bir parça içerik paylaşımının sonucu
|
||||
Comment[uk]=Результат оприлюднення даних
|
||||
Comment[x-test]=xxThe result of sharing a piece of contentxx
|
||||
Comment[zh_CN]=分享一个内容得到的结果
|
||||
Comment[zh_TW]=分享一份內容之後的結果
|
||||
Action=Popup
|
||||
|
||||
@@ -104,6 +104,7 @@
|
||||
</entry>
|
||||
<entry name="ShowLinkPreview" type="bool">
|
||||
<label>Show preview of the links in the chat messages</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="SystemTray" type="bool">
|
||||
<label>Close NeoChat to system tray</label>
|
||||
|
||||
@@ -107,10 +107,6 @@ void NeoChatConnection::connectSignals()
|
||||
Q_EMIT homeHaveHighlightNotificationsChanged();
|
||||
});
|
||||
});
|
||||
connect(this, &NeoChatConnection::invitedRoom, this, [this](Quotient::Room *room) {
|
||||
auto r = dynamic_cast<NeoChatRoom *>(room);
|
||||
connect(r, &NeoChatRoom::showInviteNotification, this, &NeoChatConnection::showInviteNotification);
|
||||
});
|
||||
connect(this, &NeoChatConnection::leftRoom, this, [this](Room *room, Room *prev) {
|
||||
Q_UNUSED(room)
|
||||
if (prev && prev->isDirectChat()) {
|
||||
@@ -149,6 +145,10 @@ void NeoChatConnection::connectSignals()
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::PreferUsingEncryptionChanged, this, [] {
|
||||
setDirectChatEncryptionDefault(NeoChatConfig::preferUsingEncryption());
|
||||
});
|
||||
setGlobalUrlPreviewEnabled(NeoChatConfig::showLinkPreview());
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, [this]() {
|
||||
setGlobalUrlPreviewEnabled(NeoChatConfig::showLinkPreview());
|
||||
});
|
||||
}
|
||||
|
||||
int NeoChatConnection::badgeNotificationCount() const
|
||||
@@ -171,6 +171,25 @@ void NeoChatConnection::refreshBadgeNotificationCount()
|
||||
}
|
||||
}
|
||||
|
||||
bool NeoChatConnection::globalUrlPreviewEnabled()
|
||||
{
|
||||
return m_globalUrlPreviewEnabled;
|
||||
}
|
||||
|
||||
void NeoChatConnection::setGlobalUrlPreviewEnabled(bool newState)
|
||||
{
|
||||
if (m_globalUrlPreviewEnabled == newState) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_globalUrlPreviewEnabled = newState;
|
||||
if (!m_globalUrlPreviewEnabled) {
|
||||
m_linkPreviewers.clear();
|
||||
}
|
||||
NeoChatConfig::setShowLinkPreview(m_globalUrlPreviewEnabled);
|
||||
Q_EMIT globalUrlPreviewEnabledChanged();
|
||||
}
|
||||
|
||||
void NeoChatConnection::logout(bool serverSideLogout)
|
||||
{
|
||||
SettingsGroup(u"Accounts"_s).remove(userId());
|
||||
@@ -476,9 +495,12 @@ QCoro::Task<void> NeoChatConnection::setupPushNotifications(QString endpoint)
|
||||
false);
|
||||
|
||||
qInfo() << "Registered for push notifications";
|
||||
m_pushNotificationsEnabled = true;
|
||||
} else {
|
||||
qWarning() << "There's no gateway, not setting up push notifications.";
|
||||
m_pushNotificationsEnabled = false;
|
||||
}
|
||||
Q_EMIT enablePushNotificationsChanged();
|
||||
#else
|
||||
Q_UNUSED(endpoint)
|
||||
co_return;
|
||||
@@ -506,7 +528,7 @@ QString NeoChatConnection::accountDataJsonString(const QString &type) const
|
||||
|
||||
LinkPreviewer *NeoChatConnection::previewerForLink(const QUrl &link)
|
||||
{
|
||||
if (!NeoChatConfig::showLinkPreview()) {
|
||||
if (!m_globalUrlPreviewEnabled) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -540,4 +562,18 @@ bool NeoChatConnection::canEraseData() const
|
||||
return m_canEraseData;
|
||||
}
|
||||
|
||||
bool NeoChatConnection::pushNotificationsAvailable() const
|
||||
{
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool NeoChatConnection::enablePushNotifications() const
|
||||
{
|
||||
return m_pushNotificationsEnabled;
|
||||
}
|
||||
|
||||
#include "moc_neochatconnection.cpp"
|
||||
|
||||
@@ -32,6 +32,11 @@ class NeoChatConnection : public Quotient::Connection
|
||||
*/
|
||||
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether URL previews are enabled globally.
|
||||
*/
|
||||
Q_PROPERTY(bool globalUrlPreviewEnabled READ globalUrlPreviewEnabled WRITE setGlobalUrlPreviewEnabled NOTIFY globalUrlPreviewEnabledChanged)
|
||||
|
||||
/**
|
||||
* @brief The model with the account's 3PIDs.
|
||||
*/
|
||||
@@ -90,6 +95,16 @@ class NeoChatConnection : public Quotient::Connection
|
||||
*/
|
||||
Q_PROPERTY(bool canEraseData READ canEraseData NOTIFY canEraseDataChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether this build of NeoChat supports push notifications via KUnifiedPush.
|
||||
*/
|
||||
Q_PROPERTY(bool pushNotificationsAvailable READ pushNotificationsAvailable CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief Whether we successfully set up push notifications with the server.
|
||||
*/
|
||||
Q_PROPERTY(bool enablePushNotifications READ enablePushNotifications NOTIFY enablePushNotificationsChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the status after an attempt to change the password on an account.
|
||||
@@ -172,17 +187,24 @@ public:
|
||||
int badgeNotificationCount() const;
|
||||
void refreshBadgeNotificationCount();
|
||||
|
||||
bool globalUrlPreviewEnabled();
|
||||
void setGlobalUrlPreviewEnabled(bool newState);
|
||||
|
||||
bool directChatInvites() const;
|
||||
|
||||
// note: this is intentionally a copied QString because
|
||||
// the reference could be destroyed before the task is finished
|
||||
QCoro::Task<void> setupPushNotifications(QString endpoint);
|
||||
|
||||
bool pushNotificationsAvailable() const;
|
||||
bool enablePushNotifications() const;
|
||||
|
||||
bool isOnline() const;
|
||||
|
||||
LinkPreviewer *previewerForLink(const QUrl &link);
|
||||
|
||||
Q_SIGNALS:
|
||||
void globalUrlPreviewEnabledChanged();
|
||||
void labelChanged();
|
||||
void identityServerChanged();
|
||||
void directChatNotificationsChanged();
|
||||
@@ -196,6 +218,7 @@ Q_SIGNALS:
|
||||
void badgeNotificationCountChanged(NeoChatConnection *connection, int count);
|
||||
void canCheckMutualRoomsChanged();
|
||||
void canEraseDataChanged();
|
||||
void enablePushNotificationsChanged();
|
||||
|
||||
/**
|
||||
* @brief Request a message be shown to the user of the given type.
|
||||
@@ -207,11 +230,6 @@ Q_SIGNALS:
|
||||
*/
|
||||
void errorOccured(const QString &error);
|
||||
|
||||
/**
|
||||
* @brief Request a notification be shown for an invite to this room.
|
||||
*/
|
||||
void showInviteNotification(NeoChatRoom *room);
|
||||
|
||||
private:
|
||||
bool m_isOnline = true;
|
||||
void setIsOnline(bool isOnline);
|
||||
@@ -221,9 +239,11 @@ private:
|
||||
void connectSignals();
|
||||
|
||||
int m_badgeNotificationCount = 0;
|
||||
bool m_globalUrlPreviewEnabled = true;
|
||||
|
||||
QCache<QUrl, LinkPreviewer> m_linkPreviewers;
|
||||
|
||||
bool m_canCheckMutualRooms = false;
|
||||
bool m_canEraseData = false;
|
||||
bool m_pushNotificationsEnabled = false;
|
||||
};
|
||||
|
||||
@@ -40,10 +40,10 @@
|
||||
#include "chatbarcache.h"
|
||||
#include "clipboard.h"
|
||||
#include "eventhandler.h"
|
||||
#include "events/joinrulesevent.h"
|
||||
#include "events/pollevent.h"
|
||||
#include "filetransferpseudojob.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroommember.h"
|
||||
#include "roomlastmessageprovider.h"
|
||||
#include "spacehierarchycache.h"
|
||||
@@ -127,16 +127,12 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
||||
updatePushNotificationState(u"m.push_rules"_s);
|
||||
|
||||
Q_EMIT canEncryptRoomChanged();
|
||||
if (this->joinState() == JoinState::Invite) {
|
||||
Q_EMIT showInviteNotification(this);
|
||||
}
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
connect(this, &Room::changed, this, [this] {
|
||||
Q_EMIT canEncryptRoomChanged();
|
||||
Q_EMIT parentIdsChanged();
|
||||
Q_EMIT canonicalParentChanged();
|
||||
Q_EMIT joinRuleChanged();
|
||||
Q_EMIT readOnlyChanged();
|
||||
});
|
||||
connect(connection, &Connection::capabilitiesLoaded, this, &NeoChatRoom::maxRoomVersionChanged);
|
||||
@@ -160,6 +156,10 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
||||
Q_EMIT childrenHaveHighlightNotificationsChanged();
|
||||
}
|
||||
});
|
||||
|
||||
const auto neochatconnection = static_cast<NeoChatConnection *>(connection);
|
||||
Q_ASSERT(neochatconnection);
|
||||
connect(neochatconnection, &NeoChatConnection::globalUrlPreviewEnabledChanged, this, &NeoChatRoom::urlPreviewEnabledChanged);
|
||||
}
|
||||
|
||||
bool NeoChatRoom::visible() const
|
||||
@@ -608,63 +608,12 @@ void NeoChatRoom::deleteMessagesByUser(const QString &user, const QString &reaso
|
||||
doDeleteMessagesByUser(user, reason);
|
||||
}
|
||||
|
||||
QString NeoChatRoom::joinRule() const
|
||||
{
|
||||
auto joinRulesEvent = currentState().get<JoinRulesEvent>();
|
||||
if (!joinRulesEvent) {
|
||||
return {};
|
||||
}
|
||||
return joinRulesEvent->joinRule();
|
||||
}
|
||||
|
||||
void NeoChatRoom::setJoinRule(const QString &joinRule, const QList<QString> &allowedSpaces)
|
||||
{
|
||||
if (!canSendState("m.room.join_rules"_L1)) {
|
||||
qWarning() << "Power level too low to set join rules";
|
||||
return;
|
||||
}
|
||||
auto actualRule = joinRule;
|
||||
if (joinRule == "restricted"_L1 && allowedSpaces.isEmpty()) {
|
||||
actualRule = "private"_L1;
|
||||
}
|
||||
|
||||
QJsonArray allowConditions;
|
||||
if (actualRule == "restricted"_L1) {
|
||||
for (auto allowedSpace : allowedSpaces) {
|
||||
allowConditions += QJsonObject{{"type"_L1, "m.room_membership"_L1}, {"room_id"_L1, allowedSpace}};
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject content;
|
||||
content.insert("join_rule"_L1, joinRule);
|
||||
if (!allowConditions.isEmpty()) {
|
||||
content.insert("allow"_L1, allowConditions);
|
||||
}
|
||||
qWarning() << content;
|
||||
setState("m.room.join_rules"_L1, {}, content);
|
||||
// Not emitting joinRuleChanged() here, since that would override the change in the UI with the *current* value, which is not the *new* value.
|
||||
}
|
||||
|
||||
QList<QString> NeoChatRoom::restrictedIds() const
|
||||
{
|
||||
auto joinRulesEvent = currentState().get<JoinRulesEvent>();
|
||||
if (!joinRulesEvent) {
|
||||
return {};
|
||||
}
|
||||
if (joinRulesEvent->joinRule() != "restricted"_L1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QList<QString> roomIds;
|
||||
for (auto allow : joinRulesEvent->allow()) {
|
||||
roomIds += allow.toObject().value("room_id"_L1).toString();
|
||||
}
|
||||
return roomIds;
|
||||
}
|
||||
|
||||
QString NeoChatRoom::historyVisibility() const
|
||||
{
|
||||
return currentState().get("m.room.history_visibility"_L1)->contentJson()["history_visibility"_L1].toString();
|
||||
if (auto stateEvent = currentState().get("m.room.history_visibility"_L1)) {
|
||||
return stateEvent->contentJson()["history_visibility"_L1].toString();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void NeoChatRoom::setHistoryVisibility(const QString &historyVisibilityRule)
|
||||
@@ -731,6 +680,9 @@ void NeoChatRoom::setDefaultUrlPreviewState(const bool &defaultUrlPreviewState)
|
||||
|
||||
bool NeoChatRoom::urlPreviewEnabled() const
|
||||
{
|
||||
if (!static_cast<NeoChatConnection *>(connection())->globalUrlPreviewEnabled()) {
|
||||
return false;
|
||||
}
|
||||
if (hasAccountData("org.matrix.room.preview_urls"_L1)) {
|
||||
return !accountData("org.matrix.room.preview_urls"_L1)->contentJson()["disable"_L1].toBool();
|
||||
} else {
|
||||
@@ -1719,7 +1671,11 @@ void NeoChatRoom::cleanupExtraEvent(const QString &eventId)
|
||||
}
|
||||
QString NeoChatRoom::invitingUserId() const
|
||||
{
|
||||
return currentState().get<RoomMemberEvent>(connection()->userId())->senderId();
|
||||
auto event = currentState().get<RoomMemberEvent>(connection()->userId());
|
||||
if (!event) {
|
||||
return {};
|
||||
}
|
||||
return event->senderId();
|
||||
}
|
||||
|
||||
void NeoChatRoom::setRoomState(const QString &type, const QString &stateKey, const QByteArray &content)
|
||||
@@ -1729,10 +1685,6 @@ void NeoChatRoom::setRoomState(const QString &type, const QString &stateKey, con
|
||||
|
||||
NeochatRoomMember *NeoChatRoom::qmlSafeMember(const QString &memberId)
|
||||
{
|
||||
if (!isMember(memberId)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!m_memberObjects.contains(memberId)) {
|
||||
return m_memberObjects.emplace(memberId, std::make_unique<NeochatRoomMember>(this, memberId)).first->second.get();
|
||||
}
|
||||
@@ -1774,7 +1726,9 @@ MessageContentModel *NeoChatRoom::contentModelForEvent(const Quotient::RoomEvent
|
||||
auto eventId = event->id();
|
||||
const auto txnId = event->transactionId();
|
||||
if (!m_eventContentModels.contains(eventId) && !m_eventContentModels.contains(txnId)) {
|
||||
return m_eventContentModels.emplace(eventId, std::make_unique<MessageContentModel>(this, eventId.isEmpty() ? txnId : eventId, false, eventId.isEmpty()))
|
||||
return m_eventContentModels
|
||||
.emplace(eventId.isEmpty() ? txnId : eventId,
|
||||
std::make_unique<MessageContentModel>(this, eventId.isEmpty() ? txnId : eventId, false, eventId.isEmpty()))
|
||||
.first->second.get();
|
||||
}
|
||||
|
||||
@@ -1809,4 +1763,23 @@ ThreadModel *NeoChatRoom::modelForThread(const QString &threadRootId)
|
||||
return m_threadModels[threadRootId].get();
|
||||
}
|
||||
|
||||
void NeoChatRoom::pinEvent(const QString &eventId)
|
||||
{
|
||||
auto eventIds = pinnedEventIds();
|
||||
eventIds.push_back(eventId);
|
||||
setPinnedEvents(eventIds);
|
||||
}
|
||||
|
||||
void NeoChatRoom::unpinEvent(const QString &eventId)
|
||||
{
|
||||
auto eventIds = pinnedEventIds();
|
||||
eventIds.removeAll(eventId);
|
||||
setPinnedEvents(eventIds);
|
||||
}
|
||||
|
||||
bool NeoChatRoom::isEventPinned(const QString &eventId) const
|
||||
{
|
||||
return pinnedEventIds().contains(eventId);
|
||||
}
|
||||
|
||||
#include "moc_neochatroom.cpp"
|
||||
|
||||
@@ -134,22 +134,6 @@ class NeoChatRoom : public Quotient::Room
|
||||
*/
|
||||
Q_PROPERTY(bool readOnly READ readOnly NOTIFY readOnlyChanged)
|
||||
|
||||
/**
|
||||
* @brief The current join rule for the room as a QString.
|
||||
*
|
||||
* Possible values are [public, knock, invite, private, restricted].
|
||||
*
|
||||
* @sa https://spec.matrix.org/v1.5/client-server-api/#mroomjoin_rules
|
||||
*/
|
||||
Q_PROPERTY(QString joinRule READ joinRule WRITE setJoinRule NOTIFY joinRuleChanged)
|
||||
|
||||
/**
|
||||
* @brief The space IDs that members of can join this room.
|
||||
*
|
||||
* Empty if the join rule is not restricted.
|
||||
*/
|
||||
Q_PROPERTY(QList<QString> restrictedIds READ restrictedIds NOTIFY joinRuleChanged)
|
||||
|
||||
/**
|
||||
* @brief Get the maximum room version that the server supports.
|
||||
*
|
||||
@@ -420,25 +404,6 @@ public:
|
||||
|
||||
bool readOnly() const;
|
||||
|
||||
[[nodiscard]] QString joinRule() const;
|
||||
|
||||
/**
|
||||
* @brief Set the join rule for the room.
|
||||
*
|
||||
* Will fail if the user doesn't have the required privileges.
|
||||
*
|
||||
* @param joinRule the join rule [public, knock, invite, private, restricted].
|
||||
* @param allowedSpaces only used when the join rule is restricted. This is a
|
||||
* list of space Matrix IDs that members of can join without an invite.
|
||||
* If the rule is restricted and this list is empty it is treated as a join
|
||||
* rule of private instead.
|
||||
*
|
||||
* @sa https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules
|
||||
*/
|
||||
Q_INVOKABLE void setJoinRule(const QString &joinRule, const QList<QString> &allowedSpaces = {});
|
||||
|
||||
QList<QString> restrictedIds() const;
|
||||
|
||||
int maxRoomVersion() const;
|
||||
|
||||
/**
|
||||
@@ -588,6 +553,15 @@ public:
|
||||
*/
|
||||
Quotient::FileTransferInfo cachedFileTransferInfo(const Quotient::RoomEvent *event) const;
|
||||
|
||||
/**
|
||||
* @brief Return a NeochatRoomMember object for the given user ID.
|
||||
*
|
||||
* @warning Because we can't guarantee that a member state event is downloaded
|
||||
* before a message they sent arrives this will create the object unconditionally
|
||||
* assuming that the state event will turn up later. It is therefor the
|
||||
* responsibility of the caller to ensure that they only ask for objects
|
||||
* for real senders.
|
||||
*/
|
||||
NeochatRoomMember *qmlSafeMember(const QString &memberId);
|
||||
|
||||
/**
|
||||
@@ -625,7 +599,24 @@ public:
|
||||
* A model is created is one doesn't exist. Will return nullptr if threadRootId
|
||||
* is empty.
|
||||
*/
|
||||
ThreadModel *modelForThread(const QString &threadRootId);
|
||||
Q_INVOKABLE ThreadModel *modelForThread(const QString &threadRootId);
|
||||
|
||||
/**
|
||||
* @brief Pin a message in the room.
|
||||
* @param eventId The id of the event to pin.
|
||||
*/
|
||||
Q_INVOKABLE void pinEvent(const QString &eventId);
|
||||
|
||||
/**
|
||||
* @brief Unpin a message in the room.
|
||||
* @param eventId The id of the event to unpin.
|
||||
*/
|
||||
Q_INVOKABLE void unpinEvent(const QString &eventId);
|
||||
|
||||
/**
|
||||
* @return True if @p eventId is pinned in the room.
|
||||
*/
|
||||
Q_INVOKABLE bool isEventPinned(const QString &eventId) const;
|
||||
|
||||
private:
|
||||
bool m_visible = false;
|
||||
@@ -684,7 +675,6 @@ Q_SIGNALS:
|
||||
void displayNameChanged();
|
||||
void pushNotificationStateChanged(PushNotificationState::State state);
|
||||
void canEncryptRoomChanged();
|
||||
void joinRuleChanged();
|
||||
void historyVisibilityChanged();
|
||||
void defaultUrlPreviewStateChanged();
|
||||
void urlPreviewEnabledChanged();
|
||||
@@ -697,14 +687,6 @@ Q_SIGNALS:
|
||||
*/
|
||||
void showMessage(MessageType::Type messageType, const QString &message);
|
||||
|
||||
/**
|
||||
* @brief Request a notification be shown for an invite to this room.
|
||||
*
|
||||
* @note This may later be blocked if there are any rules on where invites can
|
||||
* come from, but this is not NeoChatRoom's responsibility.
|
||||
*/
|
||||
void showInviteNotification(NeoChatRoom *room);
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* @brief Upload a file to the matrix server and post the file to the room.
|
||||
|
||||
@@ -127,9 +127,8 @@ void NotificationsManager::processNotificationJob(QPointer<NeoChatConnection> co
|
||||
}
|
||||
auto sender = room->member(notification["event"_L1]["sender"_L1].toString());
|
||||
|
||||
// Don't display notifications for events in invited rooms
|
||||
// This should prevent empty notifications from appearing when they shouldn't
|
||||
if (room->joinState() == JoinState::Invite) {
|
||||
postInviteNotification(qobject_cast<NeoChatRoom *>(room));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -244,7 +243,7 @@ void NotificationsManager::postNotification(NeoChatRoom *room,
|
||||
|
||||
if (canReply) {
|
||||
std::unique_ptr<KNotificationReplyAction> replyAction(new KNotificationReplyAction(i18n("Reply")));
|
||||
replyAction->setPlaceholderText(i18n("Reply..."));
|
||||
replyAction->setPlaceholderText(i18n("Reply…"));
|
||||
connect(replyAction.get(), &KNotificationReplyAction::replied, this, [room, replyEventId](const QString &text) {
|
||||
TextHandler textHandler;
|
||||
textHandler.setData(text);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"Name[ia]": "Tobias Fella",
|
||||
"Name[it]": "Tobias Fella",
|
||||
"Name[ka]": "Tobias Fella",
|
||||
"Name[ko]": "Tobias Fella",
|
||||
"Name[lv]": "Tobias Fella",
|
||||
"Name[nl]": "Tobias Fella",
|
||||
"Name[nn]": "Tobias Fella",
|
||||
@@ -35,6 +36,7 @@
|
||||
"Name[tr]": "Tobias Fella",
|
||||
"Name[uk]": "Tobias Fella",
|
||||
"Name[x-test]": "xxTobias Fellaxx",
|
||||
"Name[zh_CN]": "Tobias Fella",
|
||||
"Name[zh_TW]": "Tobias Fella"
|
||||
}
|
||||
],
|
||||
@@ -58,6 +60,7 @@
|
||||
"Description[ia]": "Comparti via NeoChat",
|
||||
"Description[it]": "Condividi tramite NeoChat",
|
||||
"Description[ka]": "გააზიარეთ NeoChat-ით",
|
||||
"Description[ko]": "NeoChat으로 공유",
|
||||
"Description[lv]": "Kopīgot ar „NeoChat“",
|
||||
"Description[nl]": "Delen via NeoChat",
|
||||
"Description[nn]": "Del via NeoChat",
|
||||
@@ -70,6 +73,7 @@
|
||||
"Description[tr]": "NeoChat ile Paylaş",
|
||||
"Description[uk]": "Оприлюднити за допомогою NeoChat",
|
||||
"Description[x-test]": "xxShare via NeoChatxx",
|
||||
"Description[zh_CN]": "通过 NeoChat 分享",
|
||||
"Description[zh_TW]": "透過 NeoChat 分享",
|
||||
"Icon": "org.kde.neochat.tray",
|
||||
"License": "GPL",
|
||||
@@ -94,6 +98,7 @@
|
||||
"Name[ia]": "Neochat",
|
||||
"Name[it]": "NeoChat",
|
||||
"Name[ka]": "NeoChat",
|
||||
"Name[ko]": "NeoChat",
|
||||
"Name[lv]": "NeoChat",
|
||||
"Name[nl]": "NeoChat",
|
||||
"Name[nn]": "NeoChat",
|
||||
@@ -107,6 +112,7 @@
|
||||
"Name[tr]": "NeoChat",
|
||||
"Name[uk]": "NeoChat",
|
||||
"Name[x-test]": "xxNeoChatxx",
|
||||
"Name[zh_CN]": "NeoChat",
|
||||
"Name[zh_TW]": "NeoChat",
|
||||
"X-Purpose-ActionDisplay": "NeoChat"
|
||||
},
|
||||
|
||||
@@ -87,7 +87,18 @@ KirigamiComponents.ConvergentContextMenu {
|
||||
QQC2.Action {
|
||||
text: i18nc("@action:inmenu", "Verify This Device")
|
||||
icon.name: "security-low"
|
||||
onTriggered: root.connection.startSelfVerification()
|
||||
onTriggered: {
|
||||
root.connection.startSelfVerification();
|
||||
const dialog = Qt.createComponent("org.kde.kirigami", "PromptDialog").createObject(QQC2.Overlay.overlay, {
|
||||
title: i18nc("@title", "Verification Request Sent"),
|
||||
subtitle: i18nc("@info:label", "To proceed, accept the verification request on another device."),
|
||||
standardButtons: Kirigami.Dialog.Ok
|
||||
})
|
||||
dialog.open();
|
||||
root.connection.onNewKeyVerificationSession.connect(() => {
|
||||
dialog.close();
|
||||
});
|
||||
}
|
||||
enabled: Controller.csSupported
|
||||
}
|
||||
|
||||
|
||||
@@ -34,9 +34,8 @@ Delegates.RoundedItemDelegate {
|
||||
Keys.onSpacePressed: selected()
|
||||
Keys.onEnterPressed: selected()
|
||||
|
||||
onPressAndHold: root.contextMenuRequested()
|
||||
|
||||
TapHandler {
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus
|
||||
acceptedButtons: Qt.RightButton | Qt.LeftButton
|
||||
onTapped: (eventPoint, button) => {
|
||||
if (button === Qt.RightButton) {
|
||||
@@ -47,6 +46,12 @@ Delegates.RoundedItemDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedDevices: PointerDevice.TouchScreen
|
||||
onTapped: root.selected()
|
||||
onLongPressed: root.contextMenuRequested()
|
||||
}
|
||||
|
||||
contentItem: AvatarNotification {
|
||||
id: avatarNotification
|
||||
source: root.source
|
||||
|
||||
@@ -75,7 +75,7 @@ KirigamiComponents.ConvergentContextMenu {
|
||||
|
||||
component RemoveMessageAction: Kirigami.Action {
|
||||
visible: author.isLocalMember || currentRoom.canSendState("redact")
|
||||
text: i18nc("@action:button", "Remove")
|
||||
text: i18nc("@action:button", "Remove…")
|
||||
icon.name: "edit-delete-remove"
|
||||
icon.color: "red"
|
||||
onTriggered: {
|
||||
@@ -132,6 +132,15 @@ KirigamiComponents.ConvergentContextMenu {
|
||||
}
|
||||
}
|
||||
|
||||
component PinMessageAction: Kirigami.Action {
|
||||
readonly property bool pinned: currentRoom.isEventPinned(root.eventId)
|
||||
|
||||
visible: currentRoom.canSendState("m.room.pinned_events")
|
||||
text: pinned ? i18nc("@action:button 'Unpin' as in 'Unpin this message'", "Unpin") : i18nc("@action:button 'Pin' as in 'Pin the message in the room'", "Pin")
|
||||
icon.name: pinned ? "window-unpin-symbolic" : "pin-symbolic"
|
||||
onTriggered: pinned ? currentRoom.unpinEvent(root.eventId) : currentRoom.pinEvent(root.eventId)
|
||||
}
|
||||
|
||||
headerContentItem: RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
|
||||
@@ -24,10 +24,6 @@ RowLayout {
|
||||
*/
|
||||
signal textChanged(string newText)
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: Kirigami.Units.largeSpacing
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.fillWidth: true
|
||||
visible: !root.collapsed
|
||||
@@ -78,21 +74,6 @@ RowLayout {
|
||||
Component {
|
||||
id: menu
|
||||
QQC2.Menu {
|
||||
QQC2.MenuItem {
|
||||
text: i18n("Explore rooms")
|
||||
icon.name: "compass"
|
||||
onTriggered: {
|
||||
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18nc("@title", "Explore Rooms")
|
||||
});
|
||||
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
RoomManager.resolveResource(roomId.length > 0 ? roomId : alias, isJoined ? "" : "join");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.MenuItem {
|
||||
text: i18n("Find your friends")
|
||||
icon.name: "list-add-user"
|
||||
|
||||
@@ -71,7 +71,7 @@ DelegateContextMenu {
|
||||
|
||||
Kirigami.Action {
|
||||
visible: author.id === currentRoom.localMember.id || currentRoom.canSendState("redact")
|
||||
text: i18n("Remove")
|
||||
text: i18n("Remove…")
|
||||
icon.name: "edit-delete-remove"
|
||||
icon.color: "red"
|
||||
onTriggered: {
|
||||
@@ -90,6 +90,8 @@ DelegateContextMenu {
|
||||
}
|
||||
}
|
||||
|
||||
DelegateContextMenu.PinMessageAction {}
|
||||
|
||||
DelegateContextMenu.ReportMessageAction {}
|
||||
|
||||
DelegateContextMenu.ShowUserAction {}
|
||||
|
||||
@@ -15,14 +15,14 @@ import org.kde.neochat.settings
|
||||
Labs.MenuBar {
|
||||
id: root
|
||||
|
||||
property NeoChatConnection connection
|
||||
required property NeoChatConnection connection
|
||||
|
||||
Labs.Menu {
|
||||
title: i18nc("menu", "NeoChat")
|
||||
|
||||
Labs.MenuItem {
|
||||
enabled: pageStack.layers.currentItem.title !== i18n("Configure NeoChat…")
|
||||
text: i18nc("menu", "Configure NeoChat...")
|
||||
text: i18nc("menu", "Configure NeoChat…")
|
||||
|
||||
shortcut: StandardKey.Preferences
|
||||
onTriggered: NeoChatSettingsView.open()
|
||||
@@ -40,7 +40,7 @@ Labs.MenuBar {
|
||||
Labs.MenuItem {
|
||||
text: i18nc("menu", "Find your friends")
|
||||
enabled: pageStack.layers.currentItem.title !== i18n("Find your friends") && AccountRegistry.accountCount > 0
|
||||
onTriggered: pushReplaceLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18nc("@title", "Find your friends")
|
||||
@@ -51,8 +51,11 @@ Labs.MenuBar {
|
||||
enabled: pageStack.layers.currentItem.title !== i18n("Find your friends") && AccountRegistry.accountCount > 0
|
||||
shortcut: StandardKey.New
|
||||
onTriggered: {
|
||||
const dialog = createRoomDialog.createObject(root.overlay);
|
||||
dialog.open();
|
||||
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'CreateRoomDialog'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18nc("@title", "Create a Room")
|
||||
});
|
||||
}
|
||||
}
|
||||
Labs.MenuItem {
|
||||
|
||||
10
src/qml/GlobalMenuStub.qml
Normal file
10
src/qml/GlobalMenuStub.qml
Normal file
@@ -0,0 +1,10 @@
|
||||
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Item {
|
||||
required property NeoChatConnection connection
|
||||
}
|
||||
@@ -19,6 +19,11 @@ ColumnLayout {
|
||||
*/
|
||||
required property NeoChatRoom room
|
||||
|
||||
/**
|
||||
* @brief The canonical alias of the room, if it exists. Otherwise falls back to the first available alias.
|
||||
*/
|
||||
readonly property var roomAlias: room.aliases[0]
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
@@ -73,8 +78,8 @@ ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
font: Kirigami.Theme.smallFont
|
||||
textFormat: TextEdit.PlainText
|
||||
visible: root.room && root.room.canonicalAlias
|
||||
text: root.room && root.room.canonicalAlias ? root.room.canonicalAlias : ""
|
||||
visible: root.room && root.roomAlias
|
||||
text: root.room && root.roomAlias ? root.roomAlias : ""
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ QQC2.Control {
|
||||
|
||||
contentItem: QQC2.Label {
|
||||
text: root.text.startsWith("https://matrix.to/") ? "" : root.text
|
||||
elide: Text.ElideRight
|
||||
Accessible.description: i18nc("@info screenreader", "The currently selected link")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
@@ -6,22 +7,89 @@ import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.components as KirigamiComponents
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property NeoChatRoom currentRoom
|
||||
readonly property var invitingMember: currentRoom.member(currentRoom.invitingUserId())
|
||||
|
||||
text: i18n("Accept this invitation?")
|
||||
explanation: root.currentRoom.connection.canCheckMutualRooms ? i18n("You can reject invitations from unknown users under Security settings.") : ""
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
KirigamiComponents.Avatar {
|
||||
id: avatar
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
name: root.invitingMember.displayName
|
||||
source: root.invitingMember.avatarUrl
|
||||
color: root.invitingMember.color
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: !root.currentRoom.isDirectChat()
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
QQC2.Label {
|
||||
text: i18nc("@info:label", "%1 has invited you to join", root.invitingMember.displayName)
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
text: root.currentRoom.displayName
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: root.currentRoom.isDirectChat()
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Kirigami.Heading {
|
||||
text: root.currentRoom.displayName
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
text: i18nc("@info:label", "This user is inviting you to chat.")
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
text: i18n("You can reject invitations from unknown users under Security settings.")
|
||||
visible: root.currentRoom.connection.canCheckMutualRooms
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: Kirigami.Units.mediumSpacing
|
||||
|
||||
QQC2.Button {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: i18nc("@action:button The thing being rejected is an invitation to chat", "Reject and ignore user")
|
||||
text: i18nc("@action:button The thing being rejected is an invitation to chat", "Reject and Ignore User")
|
||||
icon.name: "list-remove-symbolic"
|
||||
|
||||
onClicked: {
|
||||
RoomManager.leaveRoom(root.currentRoom);
|
||||
@@ -30,18 +98,18 @@ Kirigami.PlaceholderMessage {
|
||||
}
|
||||
QQC2.Button {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: i18n("Reject")
|
||||
icon.name: "cards-block-symbolic"
|
||||
text: i18nc("@action:button", "Reject")
|
||||
|
||||
onClicked: RoomManager.leaveRoom(root.currentRoom)
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: i18n("Accept")
|
||||
icon.name: "dialog-ok-symbolic"
|
||||
text: i18nc("@action:button", "Accept")
|
||||
|
||||
onClicked: {
|
||||
root.currentRoom.acceptInvitation();
|
||||
}
|
||||
onClicked: root.currentRoom.acceptInvitation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user