Compare commits
71 Commits
work/redst
...
work/tobia
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27e5713731 | ||
|
|
073e756364 | ||
|
|
f153e57fdb | ||
|
|
bac93e778e | ||
|
|
2aeed10429 | ||
|
|
ea6ad902a7 | ||
|
|
f9c53ee3b0 | ||
|
|
3f0843647c | ||
|
|
a50df870e7 | ||
|
|
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 |
@@ -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": [
|
||||
{
|
||||
@@ -40,59 +50,19 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "olm",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"config-opts": [ "-DOLM_TESTS=OFF" ],
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://gitlab.matrix.org/matrix-org/olm.git",
|
||||
"tag": "3.2.10",
|
||||
"x-checker-data": {
|
||||
"type": "git",
|
||||
"tag-pattern": "^([\\d.]+)$"
|
||||
},
|
||||
"commit": "9908862979147a71dc6abaecd521be526ae77be1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "libsecret",
|
||||
"buildsystem": "meson",
|
||||
"config-opts": [
|
||||
"-Dmanpage=false",
|
||||
"-Dvapi=false",
|
||||
"-Dgtk_doc=false",
|
||||
"-Dintrospection=false",
|
||||
"-Dgcrypt=false"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://download.gnome.org/sources/libsecret/0.20/libsecret-0.20.5.tar.xz",
|
||||
"sha256": "3fb3ce340fcd7db54d87c893e69bfc2b1f6e4d4b279065ffe66dac9f0fd12b4d",
|
||||
"x-checker-data": {
|
||||
"type": "gnome",
|
||||
"name": "libsecret",
|
||||
"stable-only": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "qtkeychain",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/frankosterfeld/qtkeychain/archive/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,52 +70,49 @@
|
||||
"-DBUILD_WITH_QT6=ON",
|
||||
"-DCMAKE_INSTALL_LIBDIR=/app/lib",
|
||||
"-DLIB_INSTALL_DIR=/app/lib",
|
||||
"-DBUILD_TRANSLATIONS=NO"
|
||||
"-DBUILD_TRANSLATIONS=NO",
|
||||
"-DBUILD_TESTING=OFF"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "libQuotient",
|
||||
"name": "integral",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/quotient-im/libQuotient.git",
|
||||
"branch": "dev",
|
||||
"disable-submodules": true
|
||||
"url": "https://invent.kde.org/tfella/integral.git",
|
||||
"branch": "master"
|
||||
}
|
||||
],
|
||||
"config-opts": [
|
||||
"-DBUILD_WITH_QT6=ON",
|
||||
"-DQuotient_ENABLE_E2EE=ON",
|
||||
"-DBUILD_TESTING=OFF"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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 +125,15 @@
|
||||
{
|
||||
"name": "neochat",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"config-opts": [
|
||||
"-DBUILD_TESTING=OFF",
|
||||
"-DNEOCHAT_FLATPAK=ON"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"config-opts": [
|
||||
"-DNEOCHAT_FLATPAK=ON"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -8,13 +8,14 @@ include:
|
||||
- /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/windows-qt6.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/flatpak.yml
|
||||
- /gitlab-templates/snap-snapcraft-lxd.yml
|
||||
- /gitlab-templates/craft-android-qt6-apks.yml
|
||||
- /gitlab-templates/craft-appimage-qt6.yml
|
||||
- /gitlab-templates/craft-windows-x86-64-qt6.yml
|
||||
- /gitlab-templates/craft-windows-appx-qt6.yml
|
||||
# - /gitlab-templates/snap-snapcraft-lxd.yml
|
||||
# - /gitlab-templates/craft-android-qt6-apks.yml
|
||||
# - /gitlab-templates/craft-appimage-qt6.yml
|
||||
# - /gitlab-templates/craft-windows-x86-64-qt6.yml
|
||||
# - /gitlab-templates/craft-windows-appx-qt6.yml
|
||||
|
||||
@@ -8,7 +8,7 @@ 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_MINOR "07")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
@@ -49,8 +49,6 @@ if(NEOCHAT_FLATPAK)
|
||||
include(cmake/Flatpak.cmake)
|
||||
endif()
|
||||
|
||||
set(QUOTIENT_FORCE_NAMESPACED_INCLUDES TRUE)
|
||||
|
||||
ecm_setup_version(${PROJECT_VERSION}
|
||||
VARIABLE_PREFIX NEOCHAT
|
||||
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
|
||||
@@ -107,13 +105,7 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
|
||||
find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(QuotientQt6 0.9)
|
||||
set_package_properties(QuotientQt6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
DESCRIPTION "Qt wrapper around Matrix API"
|
||||
URL "https://github.com/quotient-im/libQuotient/"
|
||||
PURPOSE "Talk with matrix server"
|
||||
)
|
||||
find_package(Integral 0.1 REQUIRED)
|
||||
|
||||
find_package(cmark)
|
||||
set_package_properties(cmark PROPERTIES
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "models/messagecontentmodel.h"
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
@@ -32,7 +33,7 @@ private Q_SLOTS:
|
||||
|
||||
void MessageContentModelTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
|
||||
connection = new NeoChatConnection;
|
||||
}
|
||||
|
||||
void MessageContentModelTest::missingEvent()
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -473,6 +473,7 @@
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<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"/>
|
||||
|
||||
@@ -112,6 +112,7 @@ Comment[ka]=ჩატი 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[sa]=Matrix इत्यत्र गपशपं कुर्वन्तु
|
||||
Comment[sl]=Klepet na Matrixu
|
||||
Comment[sv]=Chatta på Matrix
|
||||
|
||||
494
po/ar/neochat.po
494
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
504
po/az/neochat.po
504
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
515
po/ca/neochat.po
515
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
934
po/cs/neochat.po
934
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
486
po/da/neochat.po
486
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
510
po/de/neochat.po
510
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
508
po/el/neochat.po
508
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
598
po/eo/neochat.po
598
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
1299
po/es/neochat.po
1299
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
513
po/eu/neochat.po
513
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
722
po/fi/neochat.po
722
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
506
po/fr/neochat.po
506
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
504
po/gl/neochat.po
504
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
511
po/hi/neochat.po
511
po/hi/neochat.po
File diff suppressed because it is too large
Load Diff
511
po/hu/neochat.po
511
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
500
po/ia/neochat.po
500
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
509
po/id/neochat.po
509
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
497
po/ie/neochat.po
497
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
500
po/it/neochat.po
500
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
469
po/ja/neochat.po
469
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
495
po/ka/neochat.po
495
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
508
po/ko/neochat.po
508
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
469
po/lt/neochat.po
469
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
526
po/lv/neochat.po
526
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
497
po/nl/neochat.po
497
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
528
po/nn/neochat.po
528
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
504
po/pa/neochat.po
504
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
503
po/pl/neochat.po
503
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
509
po/pt/neochat.po
509
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
512
po/ru/neochat.po
512
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
511
po/sa/neochat.po
511
po/sa/neochat.po
File diff suppressed because it is too large
Load Diff
507
po/sk/neochat.po
507
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
494
po/sl/neochat.po
494
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
494
po/sv/neochat.po
494
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
514
po/ta/neochat.po
514
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
499
po/tr/neochat.po
499
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
496
po/uk/neochat.po
496
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
@@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
---
|
||||
name: neochat
|
||||
base: core22
|
||||
base: core24
|
||||
adopt-info: neochat
|
||||
grade: stable
|
||||
confinement: strict
|
||||
@@ -106,8 +106,6 @@ parts:
|
||||
build-snaps:
|
||||
- cmake
|
||||
build-packages:
|
||||
- gcc-13
|
||||
- g++-13
|
||||
- libssl-dev
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
@@ -115,11 +113,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
|
||||
@@ -132,6 +125,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
|
||||
@@ -153,6 +148,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"
|
||||
build-packages:
|
||||
- cmark
|
||||
- libcmark-dev
|
||||
@@ -175,3 +172,13 @@ parts:
|
||||
- libcmark0.30.2
|
||||
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
|
||||
|
||||
@@ -10,194 +10,192 @@ endif()
|
||||
add_library(neochat STATIC
|
||||
controller.cpp
|
||||
controller.h
|
||||
models/emojimodel.cpp
|
||||
models/emojimodel.h
|
||||
emojitones.cpp
|
||||
emojitones.h
|
||||
models/customemojimodel.cpp
|
||||
models/customemojimodel.h
|
||||
clipboard.cpp
|
||||
clipboard.h
|
||||
models/timelinemessagemodel.cpp
|
||||
models/timelinemessagemodel.h
|
||||
models/messagefiltermodel.cpp
|
||||
models/messagefiltermodel.h
|
||||
models/roomlistmodel.cpp
|
||||
models/roomlistmodel.h
|
||||
models/sortfilterspacelistmodel.cpp
|
||||
models/sortfilterspacelistmodel.h
|
||||
models/accountemoticonmodel.cpp
|
||||
models/accountemoticonmodel.h
|
||||
spacehierarchycache.cpp
|
||||
spacehierarchycache.h
|
||||
roommanager.cpp
|
||||
roommanager.h
|
||||
# models/emojimodel.cpp
|
||||
# models/emojimodel.h
|
||||
# emojitones.cpp
|
||||
# emojitones.h
|
||||
# models/customemojimodel.cpp
|
||||
# models/customemojimodel.h
|
||||
# clipboard.cpp
|
||||
# clipboard.h
|
||||
# models/timelinemessagemodel.cpp
|
||||
# models/timelinemessagemodel.h
|
||||
# models/messagefiltermodel.cpp
|
||||
# models/messagefiltermodel.h
|
||||
# models/roomlistmodel.cpp
|
||||
# models/roomlistmodel.h
|
||||
# models/sortfilterspacelistmodel.cpp
|
||||
# models/sortfilterspacelistmodel.h
|
||||
# models/accountemoticonmodel.cpp
|
||||
# models/accountemoticonmodel.h
|
||||
# spacehierarchycache.cpp
|
||||
# spacehierarchycache.h
|
||||
# roommanager.cpp
|
||||
# roommanager.h
|
||||
neochatroom.cpp
|
||||
neochatroom.h
|
||||
models/userlistmodel.cpp
|
||||
models/userlistmodel.h
|
||||
models/userfiltermodel.cpp
|
||||
models/userfiltermodel.h
|
||||
models/publicroomlistmodel.cpp
|
||||
models/publicroomlistmodel.h
|
||||
models/spacechildrenmodel.cpp
|
||||
models/spacechildrenmodel.h
|
||||
models/spacechildsortfiltermodel.cpp
|
||||
models/spacechildsortfiltermodel.h
|
||||
models/spacetreeitem.cpp
|
||||
models/spacetreeitem.h
|
||||
models/userdirectorylistmodel.cpp
|
||||
models/userdirectorylistmodel.h
|
||||
# models/publicroomlistmodel.cpp
|
||||
# models/publicroomlistmodel.h
|
||||
# models/spacechildrenmodel.cpp
|
||||
# models/spacechildrenmodel.h
|
||||
# models/spacechildsortfiltermodel.cpp
|
||||
# models/spacechildsortfiltermodel.h
|
||||
# models/spacetreeitem.cpp
|
||||
# models/spacetreeitem.h
|
||||
# models/userdirectorylistmodel.cpp
|
||||
# models/userdirectorylistmodel.h
|
||||
models/pushrulemodel.cpp
|
||||
models/pushrulemodel.h
|
||||
models/emoticonfiltermodel.cpp
|
||||
models/emoticonfiltermodel.h
|
||||
notificationsmanager.cpp
|
||||
notificationsmanager.h
|
||||
models/sortfilterroomlistmodel.cpp
|
||||
models/sortfilterroomlistmodel.h
|
||||
# models/emoticonfiltermodel.cpp
|
||||
# models/emoticonfiltermodel.h
|
||||
# notificationsmanager.cpp
|
||||
# notificationsmanager.h
|
||||
# models/sortfilterroomlistmodel.cpp
|
||||
# models/sortfilterroomlistmodel.h
|
||||
models/roomtreemodel.cpp
|
||||
models/roomtreemodel.h
|
||||
chatdocumenthandler.cpp
|
||||
chatdocumenthandler.h
|
||||
models/devicesmodel.cpp
|
||||
models/devicesmodel.h
|
||||
models/devicesproxymodel.cpp
|
||||
filetype.cpp
|
||||
filetype.h
|
||||
login.cpp
|
||||
login.h
|
||||
models/webshortcutmodel.cpp
|
||||
models/webshortcutmodel.h
|
||||
# models/devicesmodel.cpp
|
||||
# models/devicesmodel.h
|
||||
# models/devicesproxymodel.cpp
|
||||
# filetype.cpp
|
||||
# filetype.h
|
||||
# login.cpp
|
||||
# login.h
|
||||
# models/webshortcutmodel.cpp
|
||||
# models/webshortcutmodel.h
|
||||
blurhash.cpp
|
||||
blurhash.h
|
||||
blurhashimageprovider.cpp
|
||||
blurhashimageprovider.h
|
||||
models/mediamessagefiltermodel.cpp
|
||||
models/mediamessagefiltermodel.h
|
||||
urlhelper.cpp
|
||||
urlhelper.h
|
||||
# models/mediamessagefiltermodel.cpp
|
||||
# models/mediamessagefiltermodel.h
|
||||
# urlhelper.cpp
|
||||
# urlhelper.h
|
||||
windowcontroller.cpp
|
||||
windowcontroller.h
|
||||
linkpreviewer.cpp
|
||||
linkpreviewer.h
|
||||
# linkpreviewer.cpp
|
||||
# linkpreviewer.h
|
||||
models/completionmodel.cpp
|
||||
models/completionmodel.h
|
||||
models/completionproxymodel.cpp
|
||||
models/completionproxymodel.h
|
||||
models/actionsmodel.cpp
|
||||
models/actionsmodel.h
|
||||
models/serverlistmodel.cpp
|
||||
models/serverlistmodel.h
|
||||
models/statemodel.cpp
|
||||
models/statemodel.h
|
||||
models/statefiltermodel.cpp
|
||||
models/statefiltermodel.h
|
||||
filetransferpseudojob.cpp
|
||||
filetransferpseudojob.h
|
||||
models/searchmodel.cpp
|
||||
models/searchmodel.h
|
||||
texthandler.cpp
|
||||
texthandler.h
|
||||
# models/completionproxymodel.cpp
|
||||
# models/completionproxymodel.h
|
||||
# models/actionsmodel.cpp
|
||||
# models/actionsmodel.h
|
||||
# models/serverlistmodel.cpp
|
||||
# models/serverlistmodel.h
|
||||
# models/statemodel.cpp
|
||||
# models/statemodel.h
|
||||
# models/statefiltermodel.cpp
|
||||
# models/statefiltermodel.h
|
||||
# filetransferpseudojob.cpp
|
||||
# filetransferpseudojob.h
|
||||
# models/searchmodel.cpp
|
||||
# models/searchmodel.h
|
||||
# texthandler.cpp
|
||||
# texthandler.h
|
||||
logger.cpp
|
||||
logger.h
|
||||
models/stickermodel.cpp
|
||||
models/stickermodel.h
|
||||
models/imagepacksmodel.cpp
|
||||
models/imagepacksmodel.h
|
||||
events/imagepackevent.cpp
|
||||
events/imagepackevent.h
|
||||
events/joinrulesevent.cpp
|
||||
events/joinrulesevent.h
|
||||
models/reactionmodel.cpp
|
||||
models/reactionmodel.h
|
||||
# models/stickermodel.cpp
|
||||
# models/stickermodel.h
|
||||
# models/imagepacksmodel.cpp
|
||||
# models/imagepacksmodel.h
|
||||
# events/imagepackevent.cpp
|
||||
# events/imagepackevent.h
|
||||
# models/reactionmodel.cpp
|
||||
# models/reactionmodel.h
|
||||
delegatesizehelper.cpp
|
||||
delegatesizehelper.h
|
||||
models/livelocationsmodel.cpp
|
||||
models/livelocationsmodel.h
|
||||
models/locationsmodel.cpp
|
||||
models/locationsmodel.h
|
||||
locationhelper.cpp
|
||||
locationhelper.h
|
||||
events/pollevent.cpp
|
||||
pollhandler.cpp
|
||||
# models/livelocationsmodel.cpp
|
||||
# models/livelocationsmodel.h
|
||||
# models/locationsmodel.cpp
|
||||
# models/locationsmodel.h
|
||||
# locationhelper.cpp
|
||||
# locationhelper.h
|
||||
# events/pollevent.cpp
|
||||
# pollhandler.cpp
|
||||
utils.h
|
||||
utils.cpp
|
||||
registration.cpp
|
||||
# registration.cpp
|
||||
neochatconnection.cpp
|
||||
neochatconnection.h
|
||||
jobs/neochatgetcommonroomsjob.cpp
|
||||
jobs/neochatgetcommonroomsjob.h
|
||||
# jobs/neochatgetcommonroomsjob.cpp
|
||||
# jobs/neochatgetcommonroomsjob.h
|
||||
mediasizehelper.cpp
|
||||
mediasizehelper.h
|
||||
eventhandler.cpp
|
||||
enums/delegatetype.h
|
||||
roomlastmessageprovider.cpp
|
||||
roomlastmessageprovider.h
|
||||
# eventhandler.cpp
|
||||
# enums/delegatetype.h
|
||||
# roomlastmessageprovider.cpp
|
||||
# roomlastmessageprovider.h
|
||||
chatbarcache.cpp
|
||||
chatbarcache.h
|
||||
colorschemer.cpp
|
||||
colorschemer.h
|
||||
models/notificationsmodel.cpp
|
||||
models/notificationsmodel.h
|
||||
models/timelinemodel.cpp
|
||||
models/timelinemodel.h
|
||||
# models/notificationsmodel.cpp
|
||||
# models/notificationsmodel.h
|
||||
# models/timelinemodel.cpp
|
||||
# models/timelinemodel.h
|
||||
enums/pushrule.h
|
||||
models/itinerarymodel.cpp
|
||||
models/itinerarymodel.h
|
||||
# models/itinerarymodel.cpp
|
||||
# models/itinerarymodel.h
|
||||
proxycontroller.cpp
|
||||
proxycontroller.h
|
||||
models/linemodel.cpp
|
||||
models/linemodel.h
|
||||
events/locationbeaconevent.h
|
||||
events/widgetevent.h
|
||||
enums/messagecomponenttype.h
|
||||
models/messagecontentmodel.cpp
|
||||
models/messagecontentmodel.h
|
||||
# events/locationbeaconevent.h
|
||||
# events/widgetevent.h
|
||||
# enums/messagecomponenttype.h
|
||||
# models/messagecontentmodel.cpp
|
||||
# models/messagecontentmodel.h
|
||||
enums/neochatroomtype.h
|
||||
models/sortfilterroomtreemodel.cpp
|
||||
models/sortfilterroomtreemodel.h
|
||||
mediamanager.cpp
|
||||
mediamanager.h
|
||||
models/statekeysmodel.cpp
|
||||
models/statekeysmodel.h
|
||||
sharehandler.cpp
|
||||
sharehandler.h
|
||||
# models/statekeysmodel.cpp
|
||||
# models/statekeysmodel.h
|
||||
# sharehandler.cpp
|
||||
# sharehandler.h
|
||||
models/roomtreeitem.cpp
|
||||
models/roomtreeitem.h
|
||||
foreigntypes.h
|
||||
models/threepidmodel.cpp
|
||||
models/threepidmodel.h
|
||||
threepidaddhelper.cpp
|
||||
threepidaddhelper.h
|
||||
identityserverhelper.cpp
|
||||
identityserverhelper.h
|
||||
enums/powerlevel.cpp
|
||||
enums/powerlevel.h
|
||||
models/permissionsmodel.cpp
|
||||
models/permissionsmodel.h
|
||||
threepidbindhelper.cpp
|
||||
threepidbindhelper.h
|
||||
models/readmarkermodel.cpp
|
||||
models/readmarkermodel.h
|
||||
neochatroommember.cpp
|
||||
neochatroommember.h
|
||||
models/threadmodel.cpp
|
||||
models/threadmodel.h
|
||||
enums/messagetype.h
|
||||
messagecomponent.h
|
||||
# models/threepidmodel.cpp
|
||||
# models/threepidmodel.h
|
||||
# threepidaddhelper.cpp
|
||||
# threepidaddhelper.h
|
||||
# identityserverhelper.cpp
|
||||
# identityserverhelper.h
|
||||
# enums/powerlevel.cpp
|
||||
# enums/powerlevel.h
|
||||
# models/permissionsmodel.cpp
|
||||
# models/permissionsmodel.h
|
||||
# threepidbindhelper.cpp
|
||||
# threepidbindhelper.h
|
||||
# models/readmarkermodel.cpp
|
||||
# models/readmarkermodel.h
|
||||
# neochatroommember.cpp
|
||||
# neochatroommember.h
|
||||
# models/threadmodel.cpp
|
||||
# models/threadmodel.h
|
||||
# enums/messagetype.h
|
||||
# messagecomponent.h
|
||||
enums/roomsortparameter.cpp
|
||||
enums/roomsortparameter.h
|
||||
models/roomsortparametermodel.cpp
|
||||
models/roomsortparametermodel.h
|
||||
models/messagemodel.cpp
|
||||
models/messagemodel.h
|
||||
models/messagecontentfiltermodel.cpp
|
||||
models/messagecontentfiltermodel.h
|
||||
models/pinnedmessagemodel.cpp
|
||||
models/pinnedmessagemodel.h
|
||||
models/commonroomsmodel.cpp
|
||||
models/commonroomsmodel.h
|
||||
# models/roomsortparametermodel.cpp
|
||||
# models/roomsortparametermodel.h
|
||||
# models/messagemodel.cpp
|
||||
# models/messagemodel.h
|
||||
# models/messagecontentfiltermodel.cpp
|
||||
# models/messagecontentfiltermodel.h
|
||||
# models/pinnedmessagemodel.cpp
|
||||
# models/pinnedmessagemodel.h
|
||||
# models/commonroomsmodel.cpp
|
||||
# models/commonroomsmodel.h
|
||||
)
|
||||
|
||||
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||
@@ -298,6 +296,9 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||
qml/HoverLinkIndicator.qml
|
||||
qml/AvatarNotification.qml
|
||||
qml/ReasonDialog.qml
|
||||
SOURCES
|
||||
# messageattached.cpp
|
||||
# messageattached.h
|
||||
DEPENDENCIES
|
||||
QtCore
|
||||
QtQuick
|
||||
@@ -397,10 +398,10 @@ endif()
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_RUNNER)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_X11=1)
|
||||
target_sources(neochat PRIVATE runner.cpp)
|
||||
# target_sources(neochat PRIVATE runner.cpp)
|
||||
|
||||
if (TARGET KUnifiedPush)
|
||||
target_sources(neochat PRIVATE fakerunner.cpp)
|
||||
# target_sources(neochat PRIVATE fakerunner.cpp)
|
||||
endif()
|
||||
else()
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_X11=0)
|
||||
@@ -426,7 +427,7 @@ target_link_libraries(neochat PUBLIC
|
||||
KF6::IconThemes
|
||||
KF6::ColorScheme
|
||||
KF6::ItemModels
|
||||
QuotientQt6
|
||||
Integral
|
||||
cmark::cmark
|
||||
QCoro::Core
|
||||
QCoro::Network
|
||||
|
||||
@@ -344,13 +344,13 @@ QQC2.Control {
|
||||
Item {
|
||||
implicitWidth: replyComponent.implicitWidth
|
||||
implicitHeight: replyComponent.implicitHeight
|
||||
ReplyComponent {
|
||||
id: replyComponent
|
||||
replyEventId: _private.chatBarCache.replyId
|
||||
replyAuthor: _private.chatBarCache.relationAuthor
|
||||
replyContentModel: _private.chatBarCache.relationEventContentModel
|
||||
maxContentWidth: paneLoader.item.width
|
||||
}
|
||||
// ReplyComponent {
|
||||
// id: replyComponent
|
||||
// replyEventId: _private.chatBarCache.replyId
|
||||
// replyAuthor: _private.chatBarCache.relationAuthor
|
||||
// replyContentModel: _private.chatBarCache.relationEventContentModel
|
||||
// Message.maxContentWidth: paneLoader.item.width
|
||||
// }
|
||||
QQC2.Button {
|
||||
id: cancelButton
|
||||
|
||||
@@ -498,23 +498,23 @@ QQC2.Control {
|
||||
}
|
||||
}
|
||||
|
||||
EmojiDialog {
|
||||
id: emojiDialog
|
||||
|
||||
x: root.width - width
|
||||
y: -implicitHeight
|
||||
|
||||
modal: false
|
||||
includeCustom: true
|
||||
closeOnChosen: false
|
||||
|
||||
currentRoom: root.currentRoom
|
||||
|
||||
onChosen: emoji => insertText(emoji)
|
||||
onClosed: if (emojiAction.checked) {
|
||||
emojiAction.checked = false;
|
||||
}
|
||||
}
|
||||
// EmojiDialog {
|
||||
// id: emojiDialog
|
||||
//
|
||||
// x: root.width - width
|
||||
// y: -implicitHeight
|
||||
//
|
||||
// modal: false
|
||||
// includeCustom: true
|
||||
// closeOnChosen: false
|
||||
//
|
||||
// currentRoom: root.currentRoom
|
||||
//
|
||||
// onChosen: emoji => insertText(emoji)
|
||||
// onClosed: if (emojiAction.checked) {
|
||||
// emojiAction.checked = false;
|
||||
// }
|
||||
// }
|
||||
|
||||
function insertText(text) {
|
||||
let initialCursorPosition = textField.cursorPosition;
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "chatdocumenthandler.h"
|
||||
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
#include "chatdocumenthandler.h"
|
||||
#include "eventhandler.h"
|
||||
#include "models/actionsmodel.h"
|
||||
// #include "chatdocumenthandler.h"
|
||||
// #include "eventhandler.h"
|
||||
// #include "models/actionsmodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "texthandler.h"
|
||||
// #include "texthandler.h"
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
@@ -88,7 +89,7 @@ void ChatBarCache::setReplyId(const QString &replyId)
|
||||
m_relationType = Reply;
|
||||
}
|
||||
m_attachmentPath = QString();
|
||||
delete m_relationContentModel;
|
||||
// delete m_relationContentModel;
|
||||
Q_EMIT relationIdChanged(oldEventId, m_relationId);
|
||||
Q_EMIT attachmentPathChanged();
|
||||
}
|
||||
@@ -118,27 +119,27 @@ void ChatBarCache::setEditId(const QString &editId)
|
||||
m_relationType = Edit;
|
||||
}
|
||||
m_attachmentPath = QString();
|
||||
delete m_relationContentModel;
|
||||
// delete m_relationContentModel;
|
||||
Q_EMIT relationIdChanged(oldEventId, m_relationId);
|
||||
Q_EMIT attachmentPathChanged();
|
||||
}
|
||||
|
||||
Quotient::RoomMember ChatBarCache::relationAuthor() const
|
||||
{
|
||||
if (parent() == nullptr) {
|
||||
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return {};
|
||||
}
|
||||
auto room = dynamic_cast<NeoChatRoom *>(parent());
|
||||
if (room == nullptr) {
|
||||
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return {};
|
||||
}
|
||||
if (m_relationId.isEmpty()) {
|
||||
return room->member(QString());
|
||||
}
|
||||
return room->member((*room->findInTimeline(m_relationId))->senderId());
|
||||
}
|
||||
// Quotient::RoomMember ChatBarCache::relationAuthor() const
|
||||
// {
|
||||
// if (parent() == nullptr) {
|
||||
// qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
// return {};
|
||||
// }
|
||||
// auto room = dynamic_cast<NeoChatRoom *>(parent());
|
||||
// if (room == nullptr) {
|
||||
// qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
// return {};
|
||||
// }
|
||||
// if (m_relationId.isEmpty()) {
|
||||
// return room->member(QString());
|
||||
// }
|
||||
// return room->member((*room->findInTimeline(m_relationId))->senderId());
|
||||
// }
|
||||
|
||||
QString ChatBarCache::relationMessage() const
|
||||
{
|
||||
@@ -155,33 +156,33 @@ QString ChatBarCache::relationMessage() const
|
||||
return {};
|
||||
}
|
||||
|
||||
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
|
||||
return EventHandler::markdownBody(&**event);
|
||||
}
|
||||
// if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
|
||||
// return EventHandler::markdownBody(&**event);
|
||||
// }
|
||||
return {};
|
||||
}
|
||||
|
||||
MessageContentModel *ChatBarCache::relationEventContentModel()
|
||||
{
|
||||
if (parent() == nullptr) {
|
||||
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return nullptr;
|
||||
}
|
||||
if (m_relationId.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (m_relationContentModel != nullptr) {
|
||||
return m_relationContentModel;
|
||||
}
|
||||
|
||||
auto room = dynamic_cast<NeoChatRoom *>(parent());
|
||||
if (room == nullptr) {
|
||||
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return nullptr;
|
||||
}
|
||||
m_relationContentModel = new MessageContentModel(room, m_relationId, true);
|
||||
return m_relationContentModel;
|
||||
}
|
||||
// MessageContentModel *ChatBarCache::relationEventContentModel()
|
||||
// {
|
||||
// if (parent() == nullptr) {
|
||||
// qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
// return nullptr;
|
||||
// }
|
||||
// if (m_relationId.isEmpty()) {
|
||||
// return nullptr;
|
||||
// }
|
||||
// if (m_relationContentModel != nullptr) {
|
||||
// return m_relationContentModel;
|
||||
// }
|
||||
//
|
||||
// auto room = dynamic_cast<NeoChatRoom *>(parent());
|
||||
// if (room == nullptr) {
|
||||
// qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
// return nullptr;
|
||||
// }
|
||||
// m_relationContentModel = new MessageContentModel(room, m_relationId, true);
|
||||
// return m_relationContentModel;
|
||||
// }
|
||||
|
||||
bool ChatBarCache::isThreaded() const
|
||||
{
|
||||
@@ -215,7 +216,7 @@ void ChatBarCache::setAttachmentPath(const QString &attachmentPath)
|
||||
m_attachmentPath = attachmentPath;
|
||||
m_relationType = None;
|
||||
const auto oldEventId = std::exchange(m_relationId, QString());
|
||||
delete m_relationContentModel;
|
||||
// delete m_relationContentModel;
|
||||
Q_EMIT attachmentPathChanged();
|
||||
Q_EMIT relationIdChanged(oldEventId, m_relationId);
|
||||
}
|
||||
@@ -225,7 +226,7 @@ void ChatBarCache::clearRelations()
|
||||
const auto oldEventId = std::exchange(m_relationId, QString());
|
||||
const auto oldThreadId = std::exchange(m_threadId, QString());
|
||||
m_attachmentPath = QString();
|
||||
delete m_relationContentModel;
|
||||
// delete m_relationContentModel;
|
||||
Q_EMIT relationIdChanged(oldEventId, m_relationId);
|
||||
Q_EMIT threadIdChanged(oldThreadId, m_threadId);
|
||||
Q_EMIT attachmentPathChanged();
|
||||
@@ -238,7 +239,7 @@ QList<Mention> *ChatBarCache::mentions()
|
||||
|
||||
void ChatBarCache::updateMentions(QQuickTextDocument *document, ChatDocumentHandler *documentHandler)
|
||||
{
|
||||
documentHandler->setDocument(document);
|
||||
// documentHandler->setDocument(document);
|
||||
|
||||
if (parent() == nullptr) {
|
||||
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
@@ -253,35 +254,35 @@ void ChatBarCache::updateMentions(QQuickTextDocument *document, ChatDocumentHand
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
|
||||
if (const auto &roomMessageEvent = &*event->viewAs<Quotient::RoomMessageEvent>()) {
|
||||
// Replaces the mentions that are baked into the HTML but plaintext in the original markdown
|
||||
const QRegularExpression re(uR"lit(<a\shref="https:\/\/matrix.to\/#\/([\S]*)"\s?>([\S]*)<\/a>)lit"_s);
|
||||
|
||||
m_mentions.clear();
|
||||
|
||||
int linkSize = 0;
|
||||
auto matches = re.globalMatch(EventHandler::rawMessageBody(*roomMessageEvent));
|
||||
while (matches.hasNext()) {
|
||||
const QRegularExpressionMatch match = matches.next();
|
||||
if (match.hasMatch()) {
|
||||
const QString id = match.captured(1);
|
||||
const QString name = match.captured(2);
|
||||
|
||||
const int position = match.capturedStart(0) - linkSize;
|
||||
const int end = position + name.length();
|
||||
linkSize += match.capturedLength(0) - name.length();
|
||||
|
||||
QTextCursor cursor(documentHandler->document()->textDocument());
|
||||
cursor.setPosition(position);
|
||||
cursor.setPosition(end, QTextCursor::KeepAnchor);
|
||||
cursor.setKeepPositionOnInsert(true);
|
||||
|
||||
m_mentions.push_back(Mention{.cursor = cursor, .text = name, .start = position, .position = end, .id = id});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
|
||||
// if (const auto &roomMessageEvent = &*event->viewAs<Quotient::RoomMessageEvent>()) {
|
||||
// // Replaces the mentions that are baked into the HTML but plaintext in the original markdown
|
||||
// const QRegularExpression re(uR"lit(<a\shref="https:\/\/matrix.to\/#\/([\S]*)"\s?>([\S]*)<\/a>)lit"_s);
|
||||
//
|
||||
// m_mentions.clear();
|
||||
//
|
||||
// int linkSize = 0;
|
||||
// auto matches = re.globalMatch(EventHandler::rawMessageBody(*roomMessageEvent));
|
||||
// while (matches.hasNext()) {
|
||||
// const QRegularExpressionMatch match = matches.next();
|
||||
// if (match.hasMatch()) {
|
||||
// const QString id = match.captured(1);
|
||||
// const QString name = match.captured(2);
|
||||
//
|
||||
// const int position = match.capturedStart(0) - linkSize;
|
||||
// const int end = position + name.length();
|
||||
// linkSize += match.capturedLength(0) - name.length();
|
||||
//
|
||||
// QTextCursor cursor(documentHandler->document()->textDocument());
|
||||
// cursor.setPosition(position);
|
||||
// cursor.setPosition(end, QTextCursor::KeepAnchor);
|
||||
// cursor.setKeepPositionOnInsert(true);
|
||||
//
|
||||
// m_mentions.push_back(Mention{.cursor = cursor, .text = name, .start = position, .position = end, .id = id});
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
QString ChatBarCache::savedText() const
|
||||
@@ -308,37 +309,37 @@ void ChatBarCache::postMessage()
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = ActionsModel::handleAction(room, this);
|
||||
if (!result.second.has_value()) {
|
||||
return;
|
||||
}
|
||||
// const auto result = ActionsModel::handleAction(room, this);
|
||||
// if (!result.second.has_value()) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
TextHandler textHandler;
|
||||
textHandler.setData(*std::get<std::optional<QString>>(result));
|
||||
const auto sendText = textHandler.handleSendText();
|
||||
|
||||
if (sendText.length() == 0) {
|
||||
return;
|
||||
}
|
||||
// TextHandler textHandler;
|
||||
// textHandler.setData(*std::get<std::optional<QString>>(result));
|
||||
// const auto sendText = textHandler.handleSendText();
|
||||
//
|
||||
// if (sendText.length() == 0) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
bool isReply = !replyId().isEmpty();
|
||||
const auto replyIt = room->findInTimeline(replyId());
|
||||
if (replyIt == room->historyEdge()) {
|
||||
isReply = false;
|
||||
}
|
||||
// const auto replyIt = room->findInTimeline(replyId());
|
||||
// if (replyIt == room->historyEdge()) {
|
||||
// isReply = false;
|
||||
// }
|
||||
|
||||
auto content = std::make_unique<Quotient::EventContent::TextContent>(sendText, u"text/html"_s);
|
||||
std::optional<Quotient::EventRelation> relatesTo = std::nullopt;
|
||||
// auto content = std::make_unique<Quotient::EventContent::TextContent>(sendText, u"text/html"_s);
|
||||
// std::optional<Quotient::EventRelation> relatesTo = std::nullopt;
|
||||
//
|
||||
// if (!threadId().isEmpty()) {
|
||||
// relatesTo = Quotient::EventRelation::replyInThread(threadId(), !isReply, isReply ? replyId() : threadId());
|
||||
// } else if (!editId().isEmpty()) {
|
||||
// relatesTo = Quotient::EventRelation::replace(editId());
|
||||
// } else if (isReply) {
|
||||
// relatesTo = Quotient::EventRelation::replyTo(replyId());
|
||||
// }
|
||||
|
||||
if (!threadId().isEmpty()) {
|
||||
relatesTo = Quotient::EventRelation::replyInThread(threadId(), !isReply, isReply ? replyId() : threadId());
|
||||
} else if (!editId().isEmpty()) {
|
||||
relatesTo = Quotient::EventRelation::replace(editId());
|
||||
} else if (isReply) {
|
||||
relatesTo = Quotient::EventRelation::replyTo(replyId());
|
||||
}
|
||||
|
||||
room->post<Quotient::RoomMessageEvent>(text(), *std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result), std::move(content), relatesTo);
|
||||
// room->post<Quotient::RoomMessageEvent>(text(), *std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result), std::move(content), relatesTo);
|
||||
clearCache();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <QQuickTextDocument>
|
||||
#include <QTextCursor>
|
||||
|
||||
#include "models/messagecontentmodel.h"
|
||||
// #include "models/messagecontentmodel.h"
|
||||
|
||||
class ChatDocumentHandler;
|
||||
|
||||
@@ -102,7 +102,7 @@ class ChatBarCache : public QObject
|
||||
*
|
||||
* @sa Quotient::RoomMember
|
||||
*/
|
||||
Q_PROPERTY(Quotient::RoomMember relationAuthor READ relationAuthor NOTIFY relationIdChanged)
|
||||
// Q_PROPERTY(Quotient::RoomMember relationAuthor READ relationAuthor NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief The content of the related message.
|
||||
@@ -116,7 +116,7 @@ class ChatBarCache : public QObject
|
||||
*
|
||||
* Will be nullptr if no related message.
|
||||
*/
|
||||
Q_PROPERTY(MessageContentModel *relationEventContentModel READ relationEventContentModel NOTIFY relationIdChanged)
|
||||
// Q_PROPERTY(MessageContentModel *relationEventContentModel READ relationEventContentModel NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the chat bar is replying in a thread.
|
||||
@@ -164,10 +164,10 @@ public:
|
||||
QString editId() const;
|
||||
void setEditId(const QString &editId);
|
||||
|
||||
Quotient::RoomMember relationAuthor() const;
|
||||
// Quotient::RoomMember relationAuthor() const;
|
||||
|
||||
QString relationMessage() const;
|
||||
MessageContentModel *relationEventContentModel();
|
||||
// MessageContentModel *relationEventContentModel();
|
||||
|
||||
bool isThreaded() const;
|
||||
QString threadId() const;
|
||||
@@ -225,7 +225,7 @@ private:
|
||||
QList<Mention> m_mentions;
|
||||
QString m_savedText;
|
||||
|
||||
QPointer<MessageContentModel> m_relationContentModel;
|
||||
// QPointer<MessageContentModel> m_relationContentModel;
|
||||
|
||||
void clearCache();
|
||||
};
|
||||
|
||||
@@ -105,7 +105,7 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
||||
, m_completionModel(new CompletionModel(this))
|
||||
{
|
||||
connect(this, &ChatDocumentHandler::roomChanged, this, [this]() {
|
||||
m_completionModel->setRoom(m_room);
|
||||
// m_completionModel->setRoom(m_room);
|
||||
static QPointer<NeoChatRoom> previousRoom = nullptr;
|
||||
if (previousRoom) {
|
||||
disconnect(m_chatBarCache, &ChatBarCache::textChanged, this, nullptr);
|
||||
@@ -113,7 +113,7 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
||||
previousRoom = m_room;
|
||||
connect(m_chatBarCache, &ChatBarCache::textChanged, this, [this]() {
|
||||
int start = completionStartIndex();
|
||||
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||
// m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||
});
|
||||
});
|
||||
connect(this, &ChatDocumentHandler::documentChanged, this, [this]() {
|
||||
@@ -124,7 +124,7 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
|
||||
return;
|
||||
}
|
||||
int start = completionStartIndex();
|
||||
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||
// m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -217,55 +217,58 @@ void ChatDocumentHandler::complete(int index)
|
||||
qCWarning(ChatDocumentHandling) << "complete called with m_document set to nullptr.";
|
||||
return;
|
||||
}
|
||||
if (m_completionModel->autoCompletionType() == CompletionModel::None) {
|
||||
qCWarning(ChatDocumentHandling) << "complete called with m_completionModel->autoCompletionType() == CompletionModel::None.";
|
||||
return;
|
||||
}
|
||||
// if (m_completionModel->autoCompletionType() == CompletionModel::None) {
|
||||
// qCWarning(ChatDocumentHandling) << "complete called with m_completionModel->autoCompletionType() == CompletionModel::None.";
|
||||
// return;
|
||||
// }
|
||||
|
||||
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);
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
cursor.insertText(name + u" "_s);
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursor.position() + name.size(), QTextCursor::KeepAnchor);
|
||||
cursor.setKeepPositionOnInsert(true);
|
||||
pushMention({cursor, name, 0, 0, id});
|
||||
m_highlighter->rehighlight();
|
||||
} 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('/'));
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
cursor.insertText(u"/%1 "_s.arg(command));
|
||||
} 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);
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
cursor.insertText(alias + u" "_s);
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursor.position() + alias.size(), QTextCursor::KeepAnchor);
|
||||
cursor.setKeepPositionOnInsert(true);
|
||||
pushMention({cursor, alias, 0, 0, alias});
|
||||
m_highlighter->rehighlight();
|
||||
} 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(':'));
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
cursor.insertText(shortcode);
|
||||
}
|
||||
// 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.indexOf(QLatin1Char('@'), fromIndex);
|
||||
// QTextCursor cursor(document()->textDocument());
|
||||
// cursor.setPosition(at);
|
||||
// cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
// cursor.insertText(name + u" "_s);
|
||||
// cursor.setPosition(at);
|
||||
// cursor.setPosition(cursor.position() + name.size(), QTextCursor::KeepAnchor);
|
||||
// cursor.setKeepPositionOnInsert(true);
|
||||
// pushMention({cursor, name, 0, 0, id});
|
||||
// m_highlighter->rehighlight();
|
||||
// } 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.indexOf(QLatin1Char('/'), fromIndex);
|
||||
// QTextCursor cursor(document()->textDocument());
|
||||
// cursor.setPosition(at);
|
||||
// cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
// cursor.insertText(u"/%1 "_s.arg(command));
|
||||
// } 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.indexOf(QLatin1Char('#'), fromIndex);
|
||||
// QTextCursor cursor(document()->textDocument());
|
||||
// cursor.setPosition(at);
|
||||
// cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
// cursor.insertText(alias + u" "_s);
|
||||
// cursor.setPosition(at);
|
||||
// cursor.setPosition(cursor.position() + alias.size(), QTextCursor::KeepAnchor);
|
||||
// cursor.setKeepPositionOnInsert(true);
|
||||
// pushMention({cursor, alias, 0, 0, alias});
|
||||
// m_highlighter->rehighlight();
|
||||
// } 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.indexOf(QLatin1Char(':'), fromIndex);
|
||||
// QTextCursor cursor(document()->textDocument());
|
||||
// cursor.setPosition(at);
|
||||
// cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
// cursor.insertText(shortcode);
|
||||
// }
|
||||
}
|
||||
|
||||
CompletionModel *ChatDocumentHandler::completionModel() const
|
||||
|
||||
@@ -8,19 +8,16 @@
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <QFile>
|
||||
#include <QGuiApplication>
|
||||
#include <QTimer>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <Quotient/csapi/notifications.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
#include <Quotient/settings.h>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "notificationsmanager.h"
|
||||
// #include "neochatroom.h"
|
||||
// #include "notificationsmanager.h"
|
||||
#include "proxycontroller.h"
|
||||
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
@@ -43,12 +40,13 @@
|
||||
|
||||
bool testMode = false;
|
||||
|
||||
using namespace Quotient;
|
||||
using namespace Integral;
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
Controller::Controller(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
Connection::setRoomType<NeoChatRoom>();
|
||||
// Connection::setRoomType<NeoChatRoom>();
|
||||
|
||||
ProxyController::instance().setApplicationProxy();
|
||||
|
||||
@@ -57,18 +55,18 @@ Controller::Controller(QObject *parent)
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::SystemTrayChanged, this, &Controller::setQuitOnLastWindowClosed);
|
||||
#endif
|
||||
|
||||
if (!testMode) {
|
||||
QTimer::singleShot(0, this, [this] {
|
||||
invokeLogin();
|
||||
});
|
||||
} else {
|
||||
auto c = new NeoChatConnection(this);
|
||||
c->assumeIdentity(u"@user:localhost:1234"_s, u"device_1234"_s, u"token_1234"_s);
|
||||
connect(c, &Connection::connected, this, [c, this]() {
|
||||
m_accountRegistry.add(c);
|
||||
c->syncLoop();
|
||||
});
|
||||
}
|
||||
// if (!testMode) {
|
||||
// QTimer::singleShot(0, this, [this] {
|
||||
// invokeLogin();
|
||||
// });
|
||||
// } else {
|
||||
// auto c = new NeoChatConnection(this);
|
||||
// c->assumeIdentity(u"@user:localhost:1234"_s, u"device_1234"_s, u"token_1234"_s);
|
||||
// connect(c, &Connection::connected, this, [c, this]() {
|
||||
// m_accountRegistry.add(c);
|
||||
// c->syncLoop();
|
||||
// });
|
||||
// }
|
||||
|
||||
QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [this] {
|
||||
delete m_trayIcon;
|
||||
@@ -99,31 +97,31 @@ Controller::Controller(QObject *parent)
|
||||
#endif
|
||||
|
||||
static int oldAccountCount = 0;
|
||||
connect(&m_accountRegistry, &AccountRegistry::accountCountChanged, this, [this]() {
|
||||
if (m_accountRegistry.size() > oldAccountCount) {
|
||||
auto connection = dynamic_cast<NeoChatConnection *>(m_accountRegistry.accounts()[m_accountRegistry.size() - 1]);
|
||||
connect(
|
||||
connection,
|
||||
&NeoChatConnection::syncDone,
|
||||
this,
|
||||
[this, connection] {
|
||||
if (!m_endpoint.isEmpty()) {
|
||||
connection->setupPushNotifications(m_endpoint);
|
||||
}
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
}
|
||||
oldAccountCount = m_accountRegistry.size();
|
||||
});
|
||||
// connect(&m_accountRegistry, &AccountRegistry::accountCountChanged, this, [this]() {
|
||||
// if (m_accountRegistry.size() > oldAccountCount) {
|
||||
// auto connection = dynamic_cast<NeoChatConnection *>(m_accountRegistry.accounts()[m_accountRegistry.size() - 1]);
|
||||
// connect(
|
||||
// connection,
|
||||
// &NeoChatConnection::syncDone,
|
||||
// this,
|
||||
// [this, connection] {
|
||||
// if (!m_endpoint.isEmpty()) {
|
||||
// connection->setupPushNotifications(m_endpoint);
|
||||
// }
|
||||
// },
|
||||
// Qt::SingleShotConnection);
|
||||
// }
|
||||
// oldAccountCount = m_accountRegistry.size();
|
||||
// });
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
auto connector = new KUnifiedPush::Connector(u"org.kde.neochat"_s);
|
||||
connect(connector, &KUnifiedPush::Connector::endpointChanged, this, [this](const QString &endpoint) {
|
||||
m_endpoint = endpoint;
|
||||
for (auto "ientConnection : m_accountRegistry) {
|
||||
auto connection = dynamic_cast<NeoChatConnection *>(quotientConnection);
|
||||
connection->setupPushNotifications(endpoint);
|
||||
}
|
||||
// for (auto "ientConnection : m_accountRegistry) {
|
||||
// auto connection = dynamic_cast<NeoChatConnection *>(quotientConnection);
|
||||
// connection->setupPushNotifications(endpoint);
|
||||
// }
|
||||
});
|
||||
|
||||
connector->registerClient(
|
||||
@@ -140,147 +138,6 @@ Controller &Controller::instance()
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void Controller::addConnection(NeoChatConnection *c)
|
||||
{
|
||||
Q_ASSERT_X(c, __FUNCTION__, "Attempt to add a null connection");
|
||||
|
||||
m_accountRegistry.add(c);
|
||||
|
||||
c->setLazyLoading(true);
|
||||
|
||||
connect(c, &NeoChatConnection::syncDone, this, [c] {
|
||||
c->sync(30000);
|
||||
c->saveState();
|
||||
});
|
||||
connect(c, &NeoChatConnection::loggedOut, this, [this, c] {
|
||||
if (accounts().count() > 1) {
|
||||
// Only set the connection if the account being logged out is currently active
|
||||
if (c == activeConnection()) {
|
||||
setActiveConnection(dynamic_cast<NeoChatConnection *>(accounts().accounts()[0]));
|
||||
}
|
||||
} else {
|
||||
setActiveConnection(nullptr);
|
||||
}
|
||||
|
||||
dropConnection(c);
|
||||
});
|
||||
connect(c, &NeoChatConnection::badgeNotificationCountChanged, this, &Controller::updateBadgeNotificationCount);
|
||||
connect(c, &NeoChatConnection::syncDone, this, [this, c]() {
|
||||
m_notificationsManager.handleNotifications(c);
|
||||
});
|
||||
|
||||
c->sync();
|
||||
|
||||
Q_EMIT connectionAdded(c);
|
||||
}
|
||||
|
||||
void Controller::dropConnection(NeoChatConnection *c)
|
||||
{
|
||||
Q_ASSERT_X(c, __FUNCTION__, "Attempt to drop a null connection");
|
||||
|
||||
c->disconnect(this);
|
||||
c->disconnect(&m_notificationsManager);
|
||||
m_accountRegistry.drop(c);
|
||||
Q_EMIT connectionDropped(c);
|
||||
}
|
||||
|
||||
void Controller::invokeLogin()
|
||||
{
|
||||
const auto accounts = SettingsGroup("Accounts"_L1).childGroups();
|
||||
for (const auto &accountId : accounts) {
|
||||
AccountSettings account{accountId};
|
||||
m_accountsLoading += accountId;
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
if (!account.homeserver().isEmpty()) {
|
||||
auto accessTokenLoadingJob = loadAccessTokenFromKeyChain(account.userId());
|
||||
connect(accessTokenLoadingJob, &QKeychain::Job::finished, this, [accountId, this, accessTokenLoadingJob](QKeychain::Job *) {
|
||||
AccountSettings account{accountId};
|
||||
QString accessToken;
|
||||
if (accessTokenLoadingJob->error() == QKeychain::Error::NoError) {
|
||||
accessToken = QString::fromLatin1(accessTokenLoadingJob->binaryData());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
auto connection = new NeoChatConnection(account.homeserver());
|
||||
m_connectionsLoading[accountId] = connection;
|
||||
connect(connection, &NeoChatConnection::connected, this, [this, connection, accountId] {
|
||||
connection->loadState();
|
||||
if (connection->allRooms().size() == 0 || connection->allRooms()[0]->currentState().get<RoomCreateEvent>()) {
|
||||
addConnection(connection);
|
||||
m_accountsLoading.removeAll(connection->userId());
|
||||
m_connectionsLoading.remove(accountId);
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
} else {
|
||||
connect(
|
||||
connection->allRooms()[0],
|
||||
&Room::baseStateLoaded,
|
||||
this,
|
||||
[this, connection, accountId]() {
|
||||
addConnection(connection);
|
||||
m_accountsLoading.removeAll(connection->userId());
|
||||
m_connectionsLoading.remove(accountId);
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
}
|
||||
});
|
||||
connection->assumeIdentity(account.userId(), account.deviceId(), accessToken);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const QString &userId)
|
||||
{
|
||||
qDebug() << "Reading access token from the keychain for" << userId;
|
||||
auto job = new QKeychain::ReadPasswordJob(qAppName(), this);
|
||||
job->setKey(userId);
|
||||
|
||||
// Handling of errors
|
||||
connect(job, &QKeychain::Job::finished, this, [this, job]() {
|
||||
if (job->error() == QKeychain::Error::NoError) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (job->error()) {
|
||||
case QKeychain::EntryNotFound:
|
||||
Q_EMIT errorOccured(i18n("Access token wasn't found: Maybe it was deleted?"));
|
||||
break;
|
||||
case QKeychain::AccessDeniedByUser:
|
||||
case QKeychain::AccessDenied:
|
||||
Q_EMIT errorOccured(i18n("Access to keychain was denied: Please allow NeoChat to read the access token"));
|
||||
break;
|
||||
case QKeychain::NoBackendAvailable:
|
||||
Q_EMIT errorOccured(i18n("No keychain available: Please install a keychain, e.g. KWallet or GNOME keyring on Linux"));
|
||||
break;
|
||||
case QKeychain::OtherError:
|
||||
Q_EMIT errorOccured(i18n("Unable to read access token: %1", job->errorString()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
job->start();
|
||||
|
||||
return job;
|
||||
}
|
||||
|
||||
void Controller::saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken)
|
||||
{
|
||||
qDebug() << "Save the access token to the keychain for " << userId;
|
||||
auto job = new QKeychain::WritePasswordJob(qAppName());
|
||||
job->setAutoDelete(true);
|
||||
job->setKey(userId);
|
||||
job->setBinaryData(accessToken);
|
||||
connect(job, &QKeychain::WritePasswordJob::finished, this, [job]() {
|
||||
if (job->error()) {
|
||||
qWarning() << "Could not save access token to the keychain: " << qPrintable(job->errorString());
|
||||
}
|
||||
});
|
||||
job->start();
|
||||
}
|
||||
|
||||
bool Controller::supportSystemTray() const
|
||||
{
|
||||
#ifdef Q_OS_ANDROID
|
||||
@@ -294,7 +151,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 {
|
||||
@@ -322,7 +179,7 @@ void Controller::setActiveConnection(NeoChatConnection *connection)
|
||||
|
||||
if (m_connection != nullptr) {
|
||||
m_connection->disconnect(this);
|
||||
m_connection->disconnect(&m_notificationsManager);
|
||||
// m_connection->disconnect(&m_notificationsManager);
|
||||
}
|
||||
|
||||
m_connection = connection;
|
||||
@@ -331,7 +188,7 @@ void Controller::setActiveConnection(NeoChatConnection *connection)
|
||||
m_connection->refreshBadgeNotificationCount();
|
||||
updateBadgeNotificationCount(m_connection, m_connection->badgeNotificationCount());
|
||||
|
||||
connect(m_connection, &NeoChatConnection::errorOccured, this, &Controller::errorOccured);
|
||||
// connect(m_connection, &NeoChatConnection::errorOccured, this, &Controller::errorOccured);
|
||||
}
|
||||
|
||||
Q_EMIT activeConnectionChanged(m_connection);
|
||||
@@ -346,7 +203,7 @@ void Controller::listenForNotifications()
|
||||
connect(timer, &QTimer::timeout, qGuiApp, &QGuiApplication::quit);
|
||||
|
||||
connect(connector, &KUnifiedPush::Connector::messageReceived, [timer](const QByteArray &data) {
|
||||
instance().m_notificationsManager.postPushNotification(data);
|
||||
// instance().m_notificationsManager.postPushNotification(data);
|
||||
timer->stop();
|
||||
});
|
||||
|
||||
@@ -360,7 +217,7 @@ void Controller::listenForNotifications()
|
||||
|
||||
void Controller::clearInvitationNotification(const QString &roomId)
|
||||
{
|
||||
m_notificationsManager.clearInvitationNotification(roomId);
|
||||
// m_notificationsManager.clearInvitationNotification(roomId);
|
||||
}
|
||||
|
||||
void Controller::updateBadgeNotificationCount(NeoChatConnection *connection, int count)
|
||||
@@ -402,9 +259,9 @@ bool Controller::isFlatpak() const
|
||||
#endif
|
||||
}
|
||||
|
||||
AccountRegistry &Controller::accounts()
|
||||
Accounts &Controller::accounts()
|
||||
{
|
||||
return m_accountRegistry;
|
||||
return m_accounts;
|
||||
}
|
||||
|
||||
QString Controller::loadFileContent(const QString &path) const
|
||||
@@ -420,20 +277,6 @@ void Controller::setTestMode(bool test)
|
||||
testMode = test;
|
||||
}
|
||||
|
||||
void Controller::removeConnection(const QString &userId)
|
||||
{
|
||||
// When loadAccessTokenFromKeyChain() fails m_connectionsLoading won't have an
|
||||
// entry for it so we need to check both separately.
|
||||
if (m_accountsLoading.contains(userId)) {
|
||||
m_accountsLoading.removeAll(userId);
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
}
|
||||
if (m_connectionsLoading.contains(userId) && m_connectionsLoading[userId]) {
|
||||
auto connection = m_connectionsLoading[userId];
|
||||
SettingsGroup("Accounts"_L1).remove(userId);
|
||||
}
|
||||
}
|
||||
|
||||
bool Controller::csSupported() const
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include "notificationsmanager.h"
|
||||
#include <Quotient/accountregistry.h>
|
||||
// #include "notificationsmanager.h"
|
||||
#include <Integral/Accounts>
|
||||
|
||||
class TrayIcon;
|
||||
|
||||
@@ -63,21 +63,6 @@ public:
|
||||
void setActiveConnection(NeoChatConnection *connection);
|
||||
[[nodiscard]] NeoChatConnection *activeConnection() const;
|
||||
|
||||
/**
|
||||
* @brief Add a new connection to the account registry.
|
||||
*/
|
||||
void addConnection(NeoChatConnection *c);
|
||||
|
||||
/**
|
||||
* @brief Drop a connection from the account registry.
|
||||
*/
|
||||
void dropConnection(NeoChatConnection *c);
|
||||
|
||||
/**
|
||||
* @brief Save an access token to the keychain for the given account.
|
||||
*/
|
||||
void saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken);
|
||||
|
||||
[[nodiscard]] bool supportSystemTray() const;
|
||||
|
||||
bool isFlatpak() const;
|
||||
@@ -97,12 +82,10 @@ public:
|
||||
|
||||
Q_INVOKABLE QString loadFileContent(const QString &path) const;
|
||||
|
||||
Quotient::AccountRegistry &accounts();
|
||||
Integral::Accounts &accounts();
|
||||
|
||||
static void setTestMode(bool testMode);
|
||||
|
||||
Q_INVOKABLE void removeConnection(const QString &userId);
|
||||
|
||||
bool csSupported() const;
|
||||
|
||||
/**
|
||||
@@ -122,18 +105,15 @@ private:
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
TrayIcon *m_trayIcon = nullptr;
|
||||
|
||||
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const QString &account);
|
||||
|
||||
Quotient::AccountRegistry m_accountRegistry;
|
||||
Integral::Accounts m_accounts;
|
||||
QStringList m_accountsLoading;
|
||||
QMap<QString, QPointer<NeoChatConnection>> m_connectionsLoading;
|
||||
QString m_endpoint;
|
||||
QStringList m_shownImages;
|
||||
|
||||
NotificationsManager m_notificationsManager;
|
||||
// NotificationsManager m_notificationsManager;
|
||||
|
||||
private Q_SLOTS:
|
||||
void invokeLogin();
|
||||
void setQuitOnLastWindowClosed();
|
||||
void updateBadgeNotificationCount(NeoChatConnection *connection, int count);
|
||||
|
||||
|
||||
@@ -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,6 +50,7 @@ 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. */
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "neochatroom.h"
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <Integral/lib.rs.h>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
class NeoChatRoomType : public QObject
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
/**
|
||||
* @brief Defines the room list categories a room can be assigned.
|
||||
*/
|
||||
enum Types {
|
||||
enum Type {
|
||||
Invited, /**< The user has been invited to the room. */
|
||||
Favorite, /**< The room is set as a favourite. */
|
||||
Direct, /**< The room is a direct chat. */
|
||||
@@ -32,23 +32,23 @@ public:
|
||||
AddDirect, /**< So we can show the add friend delegate. */
|
||||
TypesCount, /**< Number of different types (this should always be last). */
|
||||
};
|
||||
Q_ENUM(Types);
|
||||
Q_ENUM(Type);
|
||||
|
||||
static NeoChatRoomType::Types typeForRoom(const NeoChatRoom *room)
|
||||
static NeoChatRoomType::Type typeForRoom(rust::Box<sdk::RoomListRoom> room)
|
||||
{
|
||||
if (room->isSpace()) {
|
||||
if (room->is_space()) {
|
||||
return NeoChatRoomType::Space;
|
||||
}
|
||||
if (room->joinState() == Quotient::JoinState::Invite) {
|
||||
if (room->state() == 2) {
|
||||
return NeoChatRoomType::Invited;
|
||||
}
|
||||
if (room->isFavourite()) {
|
||||
if (room->is_favourite()) {
|
||||
return NeoChatRoomType::Favorite;
|
||||
}
|
||||
if (room->isLowPriority()) {
|
||||
if (room->is_low_priority()) {
|
||||
return NeoChatRoomType::Deprioritized;
|
||||
}
|
||||
if (room->isDirectChat()) {
|
||||
if (false /*room->isDirectChat()*/) {
|
||||
return NeoChatRoomType::Direct;
|
||||
}
|
||||
return NeoChatRoomType::Normal;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
@@ -5,8 +6,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroom.h"
|
||||
// #include "neochatconfig.h"
|
||||
#include <Integral/Utils>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -56,27 +57,27 @@ QList<RoomSortParameter::Parameter> RoomSortParameter::allParameterList()
|
||||
|
||||
QList<RoomSortParameter::Parameter> RoomSortParameter::currentParameterList()
|
||||
{
|
||||
QList<RoomSortParameter::Parameter> configParamList;
|
||||
switch (static_cast<NeoChatConfig::EnumSortOrder::type>(NeoChatConfig::sortOrder())) {
|
||||
case NeoChatConfig::EnumSortOrder::Activity:
|
||||
configParamList = activitySortPriorities;
|
||||
break;
|
||||
case NeoChatConfig::EnumSortOrder::Alphabetical:
|
||||
configParamList = alphabeticalSortPriorities;
|
||||
break;
|
||||
case NeoChatConfig::EnumSortOrder::LastMessage:
|
||||
configParamList = lastMessageSortPriorities;
|
||||
break;
|
||||
case NeoChatConfig::EnumSortOrder::Custom: {
|
||||
const auto intList = NeoChatConfig::customSortOrder();
|
||||
std::transform(intList.constBegin(), intList.constEnd(), std::back_inserter(configParamList), [](int param) {
|
||||
return static_cast<Parameter>(param);
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
QList<RoomSortParameter::Parameter> configParamList = activitySortPriorities;
|
||||
// switch (static_cast<NeoChatConfig::EnumSortOrder::type>(NeoChatConfig::sortOrder())) {
|
||||
// case NeoChatConfig::EnumSortOrder::Activity:
|
||||
// configParamList = activitySortPriorities;
|
||||
// break;
|
||||
// case NeoChatConfig::EnumSortOrder::Alphabetical:
|
||||
// configParamList = alphabeticalSortPriorities;
|
||||
// break;
|
||||
// case NeoChatConfig::EnumSortOrder::LastMessage:
|
||||
// configParamList = lastMessageSortPriorities;
|
||||
// break;
|
||||
// case NeoChatConfig::EnumSortOrder::Custom: {
|
||||
// const auto intList = NeoChatConfig::customSortOrder();
|
||||
// std::transform(intList.constBegin(), intList.constEnd(), std::back_inserter(configParamList), [](int param) {
|
||||
// return static_cast<Parameter>(param);
|
||||
// });
|
||||
// break;
|
||||
// }
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
|
||||
if (configParamList.isEmpty()) {
|
||||
return activitySortPriorities;
|
||||
@@ -90,73 +91,74 @@ void RoomSortParameter::saveNewParameterList(const QList<Parameter> &newList)
|
||||
std::transform(newList.constBegin(), newList.constEnd(), std::back_inserter(intList), [](Parameter param) {
|
||||
return static_cast<int>(param);
|
||||
});
|
||||
NeoChatConfig::setCustomSortOrder(intList);
|
||||
NeoChatConfig::setSortOrder(NeoChatConfig::EnumSortOrder::Custom);
|
||||
NeoChatConfig::self()->save();
|
||||
// NeoChatConfig::setCustomSortOrder(intList);
|
||||
// NeoChatConfig::setSortOrder(NeoChatConfig::EnumSortOrder::Custom);
|
||||
// NeoChatConfig::self()->save();
|
||||
}
|
||||
|
||||
int RoomSortParameter::compareParameter(Parameter parameter, NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
|
||||
int RoomSortParameter::compareParameter(Parameter parameter, rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom)
|
||||
{
|
||||
switch (parameter) {
|
||||
case AlphabeticalAscending:
|
||||
return compareParameter<AlphabeticalAscending>(leftRoom, rightRoom);
|
||||
return compareParameter<AlphabeticalAscending>(leftRoom->box_me(), rightRoom->box_me());
|
||||
case AlphabeticalDescending:
|
||||
return compareParameter<AlphabeticalDescending>(leftRoom, rightRoom);
|
||||
return compareParameter<AlphabeticalDescending>(leftRoom->box_me(), rightRoom->box_me());
|
||||
case HasUnread:
|
||||
return compareParameter<HasUnread>(leftRoom, rightRoom);
|
||||
return compareParameter<HasUnread>(leftRoom->box_me(), rightRoom->box_me());
|
||||
case MostUnread:
|
||||
return compareParameter<MostUnread>(leftRoom, rightRoom);
|
||||
return compareParameter<MostUnread>(leftRoom->box_me(), rightRoom->box_me());
|
||||
case HasHighlight:
|
||||
return compareParameter<HasHighlight>(leftRoom, rightRoom);
|
||||
return compareParameter<HasHighlight>(leftRoom->box_me(), rightRoom->box_me());
|
||||
case MostHighlights:
|
||||
return compareParameter<MostHighlights>(leftRoom, rightRoom);
|
||||
return compareParameter<MostHighlights>(leftRoom->box_me(), rightRoom->box_me());
|
||||
case LastActive:
|
||||
return compareParameter<LastActive>(leftRoom, rightRoom);
|
||||
return compareParameter<LastActive>(leftRoom->box_me(), rightRoom->box_me());
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalAscending>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalAscending>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom)
|
||||
{
|
||||
return -typeCompare(leftRoom->displayName(), rightRoom->displayName());
|
||||
return -typeCompare(stringFromRust(leftRoom->display_name()), stringFromRust(rightRoom->display_name()));
|
||||
}
|
||||
|
||||
template<>
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalDescending>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalDescending>(rust::Box<sdk::RoomListRoom> leftRoom,
|
||||
rust::Box<sdk::RoomListRoom> rightRoom)
|
||||
{
|
||||
return typeCompare(leftRoom->displayName(), rightRoom->displayName());
|
||||
return typeCompare(stringFromRust(leftRoom->display_name()), stringFromRust(rightRoom->display_name()));
|
||||
}
|
||||
|
||||
template<>
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::HasUnread>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::HasUnread>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom)
|
||||
{
|
||||
return typeCompare(leftRoom->contextAwareNotificationCount() > 0, rightRoom->contextAwareNotificationCount() > 0);
|
||||
return typeCompare(leftRoom->num_unread_messages() > 0, rightRoom->num_unread_messages() > 0);
|
||||
}
|
||||
|
||||
template<>
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::MostUnread>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::MostUnread>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom)
|
||||
{
|
||||
return typeCompare(leftRoom->contextAwareNotificationCount(), rightRoom->contextAwareNotificationCount());
|
||||
return typeCompare(leftRoom->num_unread_messages(), rightRoom->num_unread_messages());
|
||||
}
|
||||
|
||||
template<>
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::HasHighlight>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::HasHighlight>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom)
|
||||
{
|
||||
const auto leftHighlight = leftRoom->highlightCount() > 0 && leftRoom->contextAwareNotificationCount() > 0;
|
||||
const auto rightHighlight = rightRoom->highlightCount() > 0 && rightRoom->contextAwareNotificationCount() > 0;
|
||||
const auto leftHighlight = leftRoom->num_unread_mentions() > 0 && leftRoom->num_unread_messages() > 0;
|
||||
const auto rightHighlight = rightRoom->num_unread_mentions() > 0 && rightRoom->num_unread_messages() > 0;
|
||||
return typeCompare(leftHighlight, rightHighlight);
|
||||
}
|
||||
|
||||
template<>
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::MostHighlights>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::MostHighlights>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom)
|
||||
{
|
||||
return typeCompare(int(leftRoom->highlightCount()), int(rightRoom->highlightCount()));
|
||||
return typeCompare(int(leftRoom->num_unread_mentions()), int(rightRoom->num_unread_mentions()));
|
||||
}
|
||||
|
||||
template<>
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::LastActive>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::LastActive>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom)
|
||||
{
|
||||
return typeCompare(leftRoom->lastActiveTime(), rightRoom->lastActiveTime());
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
class NeoChatRoom;
|
||||
#include <Integral/lib.rs.h>
|
||||
|
||||
class Room;
|
||||
|
||||
/**
|
||||
* @class RoomSortParameter
|
||||
@@ -116,27 +118,29 @@ public:
|
||||
*
|
||||
* @sa Parameter
|
||||
*/
|
||||
static int compareParameter(Parameter parameter, NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
|
||||
static int compareParameter(Parameter parameter, rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom);
|
||||
|
||||
private:
|
||||
template<Parameter parameter>
|
||||
static int compareParameter(NeoChatRoom *, NeoChatRoom *)
|
||||
static int compareParameter(rust::Box<sdk::RoomListRoom>, rust::Box<sdk::RoomListRoom>)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalAscending>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalAscending>(rust::Box<sdk::RoomListRoom> leftRoom,
|
||||
rust::Box<sdk::RoomListRoom> rightRoom);
|
||||
template<>
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalDescending>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalDescending>(rust::Box<sdk::RoomListRoom> leftRoom,
|
||||
rust::Box<sdk::RoomListRoom> rightRoom);
|
||||
template<>
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::HasUnread>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::HasUnread>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom);
|
||||
template<>
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::MostUnread>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::MostUnread>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom);
|
||||
template<>
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::HasHighlight>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::HasHighlight>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom);
|
||||
template<>
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::MostHighlights>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::MostHighlights>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom);
|
||||
template<>
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::LastActive>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
|
||||
int RoomSortParameter::compareParameter<RoomSortParameter::LastActive>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -5,44 +5,36 @@
|
||||
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/e2ee/sssshandler.h>
|
||||
#include <Quotient/keyimport.h>
|
||||
#include <Quotient/keyverificationsession.h>
|
||||
#include <Quotient/roommember.h>
|
||||
#include <Integral/Accounts>
|
||||
#include <Integral/Homeserver>
|
||||
|
||||
#include "controller.h"
|
||||
#include "neochatconfig.h"
|
||||
|
||||
struct ForeignAccountRegistry {
|
||||
struct ForeignAccounts {
|
||||
Q_GADGET
|
||||
QML_FOREIGN(Quotient::AccountRegistry)
|
||||
QML_NAMED_ELEMENT(AccountRegistry)
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
public:
|
||||
static Quotient::AccountRegistry *create(QQmlEngine *, QJSEngine *)
|
||||
QML_FOREIGN(Integral::Accounts)
|
||||
QML_NAMED_ELEMENT(Accounts)
|
||||
static Integral::Accounts *create(QQmlEngine *, QJSEngine *)
|
||||
{
|
||||
QQmlEngine::setObjectOwnership(&Controller::instance().accounts(), QQmlEngine::CppOwnership);
|
||||
return &Controller::instance().accounts();
|
||||
auto &accounts = Controller::instance().accounts();
|
||||
QQmlEngine::setObjectOwnership(&accounts, QQmlEngine::CppOwnership);
|
||||
return &accounts;
|
||||
}
|
||||
};
|
||||
|
||||
struct ForeignKeyVerificationSession {
|
||||
struct ForeignHomeserver {
|
||||
Q_GADGET
|
||||
QML_FOREIGN(Quotient::KeyVerificationSession)
|
||||
QML_NAMED_ELEMENT(KeyVerificationSession)
|
||||
QML_UNCREATABLE("")
|
||||
QML_ELEMENT
|
||||
QML_FOREIGN(Integral::Homeserver)
|
||||
QML_NAMED_ELEMENT(Homeserver)
|
||||
};
|
||||
|
||||
struct ForeignSSSSHandler {
|
||||
struct ForeignConnection {
|
||||
Q_GADGET
|
||||
QML_FOREIGN(Quotient::SSSSHandler)
|
||||
QML_NAMED_ELEMENT(SSSSHandler)
|
||||
};
|
||||
|
||||
struct ForeignKeyImport {
|
||||
Q_GADGET
|
||||
QML_SINGLETON
|
||||
QML_FOREIGN(Quotient::KeyImport)
|
||||
QML_NAMED_ELEMENT(KeyImport)
|
||||
QML_ELEMENT
|
||||
QML_FOREIGN(Integral::Connection)
|
||||
QML_NAMED_ELEMENT(Connection)
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -16,8 +16,15 @@ LoginStep {
|
||||
onActiveFocusChanged: if (activeFocus)
|
||||
matrixIdField.forceActiveFocus()
|
||||
|
||||
Component.onCompleted: {
|
||||
LoginHelper.matrixId = "";
|
||||
property Homeserver homeserver
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
interval: 500
|
||||
repeat: false
|
||||
onTriggered: if (matrixIdField.text.length > 0) {
|
||||
root.homeserver.resolveFromMatrixId(matrixIdField.text)
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormTextFieldDelegate {
|
||||
@@ -26,7 +33,7 @@ LoginStep {
|
||||
placeholderText: "@user:example.org"
|
||||
Accessible.name: i18n("Matrix ID")
|
||||
onTextChanged: {
|
||||
LoginHelper.matrixId = text;
|
||||
timer.restart()
|
||||
}
|
||||
|
||||
Keys.onReturnPressed: {
|
||||
@@ -35,17 +42,17 @@ LoginStep {
|
||||
}
|
||||
|
||||
nextAction: Kirigami.Action {
|
||||
text: LoginHelper.isLoggedIn ? i18n("Already logged in") : (LoginHelper.testing && matrixIdField.acceptableInput) ? i18n("Loading…") : i18nc("@action:button", "Continue")
|
||||
// text: LoginHelper.isLoggedIn ? i18n("Already logged in") : (LoginHelper.testing && matrixIdField.acceptableInput) ? i18n("Loading…") : i18nc("@action:button", "Continue")
|
||||
onTriggered: {
|
||||
if (LoginHelper.supportsSso && LoginHelper.supportsPassword) {
|
||||
if (root.homeserver.ssoLoginSupported && root.homeserver.passwordLoginSupported) {
|
||||
processed("LoginMethod");
|
||||
} else if (LoginHelper.supportsSso) {
|
||||
} else if (root.homeserver.ssoLoginSupported) {
|
||||
processed("Sso");
|
||||
} else {
|
||||
processed("Password");
|
||||
}
|
||||
}
|
||||
enabled: LoginHelper.homeserverReachable
|
||||
enabled: root.homeserver.loginFlowsLoaded
|
||||
}
|
||||
previousAction: Kirigami.Action {
|
||||
onTriggered: {
|
||||
|
||||
@@ -12,6 +12,8 @@ import org.kde.neochat
|
||||
LoginStep {
|
||||
id: root
|
||||
|
||||
property Homeserver homeserver
|
||||
|
||||
Connections {
|
||||
target: LoginHelper
|
||||
function onConnected() {
|
||||
@@ -26,7 +28,6 @@ LoginStep {
|
||||
id: passwordField
|
||||
|
||||
label: i18n("Password:")
|
||||
onTextChanged: LoginHelper.password = text
|
||||
enabled: !LoginHelper.isLoggingIn
|
||||
echoMode: TextInput.Password
|
||||
Accessible.name: i18n("Password")
|
||||
@@ -39,10 +40,10 @@ LoginStep {
|
||||
|
||||
nextAction: Kirigami.Action {
|
||||
text: i18nc("@action:button", "Login")
|
||||
enabled: passwordField.text.length > 0 && !LoginHelper.isLoggingIn
|
||||
// enabled: passwordField.text.length > 0 && !LoginHelper.isLoggingIn
|
||||
onTriggered: {
|
||||
root.clearError();
|
||||
LoginHelper.login();
|
||||
let pending = Accounts.loginWithPassword(root.homeserver.matrixId, passwordField.text)
|
||||
}
|
||||
}
|
||||
previousAction: Kirigami.Action {
|
||||
|
||||
@@ -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"
|
||||
@@ -26,6 +27,10 @@ Kirigami.Page {
|
||||
title: i18n("Welcome")
|
||||
globalToolBarStyle: Kirigami.ApplicationHeaderStyle.None
|
||||
|
||||
Homeserver {
|
||||
id: homeserver
|
||||
}
|
||||
|
||||
header: QQC2.Control {
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
@@ -80,7 +85,7 @@ Kirigami.Page {
|
||||
FormCard.FormHeader {
|
||||
id: existingAccountsHeader
|
||||
title: i18nc("@title", "Continue with an existing account")
|
||||
visible: (loadedAccounts.count > 0 || loadingAccounts.count > 0) && root._showExisting
|
||||
// visible: (loadedAccounts.count > 0 || loadingAccounts.count > 0) && root._showExisting
|
||||
maximumWidth: Kirigami.Units.gridUnit * 20
|
||||
}
|
||||
|
||||
@@ -89,15 +94,21 @@ Kirigami.Page {
|
||||
maximumWidth: Kirigami.Units.gridUnit * 20
|
||||
Repeater {
|
||||
id: loadedAccounts
|
||||
model: AccountRegistry
|
||||
model: Accounts
|
||||
delegate: FormCard.FormButtonDelegate {
|
||||
id: delegate
|
||||
|
||||
required property string userId
|
||||
required property NeoChatConnection connection
|
||||
required property string matrixId
|
||||
required property string displayName
|
||||
required property string avatarUrl
|
||||
required property int index
|
||||
required property bool ready
|
||||
required property Connection connection
|
||||
|
||||
text: QmlUtils.escapeString(connection.localUser.displayName)
|
||||
description: connection.localUser.id
|
||||
enabled: ready
|
||||
|
||||
text: QmlUtils.escapeString(delegate.displayName)
|
||||
description: delegate.matrixId
|
||||
leadingPadding: Kirigami.Units.largeSpacing
|
||||
|
||||
onClicked: {
|
||||
@@ -108,62 +119,12 @@ Kirigami.Page {
|
||||
id: avatar
|
||||
name: delegate.text
|
||||
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
|
||||
source: delegate.connection.localUser.avatarUrl.toString().length > 0 ? delegate.connection.makeMediaUrl(delegate.connection.localUser.avatarUrl) : ""
|
||||
source: delegate.avatarUrl
|
||||
implicitWidth: Kirigami.Units.iconSizes.medium
|
||||
implicitHeight: Kirigami.Units.iconSizes.medium
|
||||
}
|
||||
}
|
||||
}
|
||||
Repeater {
|
||||
id: loadingAccounts
|
||||
model: Controller.accountsLoading
|
||||
delegate: FormCard.AbstractFormDelegate {
|
||||
id: loadingDelegate
|
||||
|
||||
topPadding: Kirigami.Units.smallSpacing
|
||||
bottomPadding: Kirigami.Units.smallSpacing
|
||||
|
||||
background: null
|
||||
contentItem: RowLayout {
|
||||
spacing: 0
|
||||
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: i18nc("As in 'this account is still loading'", "%1 (loading)", modelData)
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.Wrap
|
||||
maximumLineCount: 2
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
Accessible.ignored: true // base class sets this text on root already
|
||||
}
|
||||
|
||||
QQC2.ToolButton {
|
||||
text: i18nc("@action:button", "Log out of this account")
|
||||
icon.name: "im-kick-user"
|
||||
onClicked: Controller.removeConnection(modelData)
|
||||
display: QQC2.Button.IconOnly
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
enabled: true
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
|
||||
}
|
||||
|
||||
FormCard.FormArrow {
|
||||
Layout.leftMargin: Kirigami.Units.smallSpacing
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
direction: Qt.RightArrow
|
||||
visible: root.background.visible
|
||||
}
|
||||
}
|
||||
}
|
||||
onCountChanged: {
|
||||
if (loadingAccounts.count === 0 && loadedAccounts.count === 1 && showExisting) {
|
||||
Controller.activeConnection = AccountRegistry.data(AccountRegistry.index(0, 0), 257);
|
||||
root.connectionChosen();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
@@ -187,6 +148,7 @@ Kirigami.Page {
|
||||
root.currentStepString = nextStep;
|
||||
headerMessage.text = "";
|
||||
headerMessage.visible = false;
|
||||
module.item.homeserver = homeserver
|
||||
if (!module.item.noControls) {
|
||||
module.item.forceActiveFocus();
|
||||
} else {
|
||||
@@ -210,33 +172,33 @@ Kirigami.Page {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Registration
|
||||
|
||||
function onNextStepChanged() {
|
||||
if (Registration.nextStep === "m.login.recaptcha") {
|
||||
stepConnections.onProcessed("Captcha");
|
||||
}
|
||||
if (Registration.nextStep === "m.login.terms") {
|
||||
stepConnections.onProcessed("Terms");
|
||||
}
|
||||
if (Registration.nextStep === "m.login.email.identity") {
|
||||
stepConnections.onProcessed("Email");
|
||||
}
|
||||
if (Registration.nextStep === "loading") {
|
||||
stepConnections.onProcessed("Loading");
|
||||
}
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: LoginHelper
|
||||
|
||||
function onLoginErrorOccured(message) {
|
||||
headerMessage.text = message;
|
||||
headerMessage.visible = message.length > 0;
|
||||
headerMessage.type = Kirigami.MessageType.Error;
|
||||
}
|
||||
}
|
||||
// Connections {
|
||||
// target: Registration
|
||||
//
|
||||
// function onNextStepChanged() {
|
||||
// if (Registration.nextStep === "m.login.recaptcha") {
|
||||
// stepConnections.onProcessed("Captcha");
|
||||
// }
|
||||
// if (Registration.nextStep === "m.login.terms") {
|
||||
// stepConnections.onProcessed("Terms");
|
||||
// }
|
||||
// if (Registration.nextStep === "m.login.email.identity") {
|
||||
// stepConnections.onProcessed("Email");
|
||||
// }
|
||||
// if (Registration.nextStep === "loading") {
|
||||
// stepConnections.onProcessed("Loading");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Connections {
|
||||
// target: LoginHelper
|
||||
//
|
||||
// function onLoginErrorOccured(message) {
|
||||
// headerMessage.text = message;
|
||||
// headerMessage.visible = message.length > 0;
|
||||
// headerMessage.type = Kirigami.MessageType.Error;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
@@ -265,6 +227,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"
|
||||
@@ -274,11 +237,11 @@ Kirigami.Page {
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
LoginHelper.init();
|
||||
module.item.forceActiveFocus();
|
||||
Registration.username = "";
|
||||
Registration.password = "";
|
||||
Registration.email = "";
|
||||
}
|
||||
// Component.onCompleted: {
|
||||
// LoginHelper.init();
|
||||
// module.item.forceActiveFocus();
|
||||
// Registration.username = "";
|
||||
// Registration.password = "";
|
||||
// Registration.email = "";
|
||||
// }
|
||||
}
|
||||
|
||||
129
src/main.cpp
129
src/main.cpp
@@ -13,7 +13,12 @@
|
||||
#include <QQuickStyle>
|
||||
#include <QQuickWindow>
|
||||
#include <QtQml/QQmlExtensionPlugin>
|
||||
#include <Quotient/connection.h>
|
||||
|
||||
#include <Integral/Connection_p>
|
||||
#include <Integral/NetworkAccessManager>
|
||||
#include <Integral/PendingConnection>
|
||||
|
||||
// #include <Quotient/connection.h>
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include <QGuiApplication>
|
||||
@@ -43,31 +48,35 @@
|
||||
|
||||
#include "neochat-version.h"
|
||||
|
||||
#include <Quotient/networkaccessmanager.h>
|
||||
// #include <Quotient/networkaccessmanager.h>
|
||||
|
||||
#include "blurhashimageprovider.h"
|
||||
#include "colorschemer.h"
|
||||
#include "controller.h"
|
||||
// #include "controller.h"
|
||||
#include "logger.h"
|
||||
#include "roommanager.h"
|
||||
#include "sharehandler.h"
|
||||
// #include "roommanager.h"
|
||||
// #include "sharehandler.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "windowcontroller.h"
|
||||
|
||||
#ifdef HAVE_RUNNER
|
||||
#include "runner.h"
|
||||
// #include "runner.h"
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMetaType>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_RUNNER) && defined(HAVE_KUNIFIEDPUSH)
|
||||
#include "fakerunner.h"
|
||||
// #include "fakerunner.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
using namespace Quotient;
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
using namespace Integral;
|
||||
|
||||
void qml_register_types_org_kde_neochat();
|
||||
|
||||
@@ -158,14 +167,14 @@ int main(int argc, char *argv[])
|
||||
about.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails"));
|
||||
about.setOrganizationDomain("kde.org");
|
||||
|
||||
about.addComponent(u"libQuotient"_s,
|
||||
i18n("A Qt library to write cross-platform clients for Matrix"),
|
||||
i18nc("<version number> (built against <possibly different version number>)",
|
||||
"%1 (built against %2)",
|
||||
Quotient::versionString(),
|
||||
QStringLiteral(Quotient_VERSION_STRING)),
|
||||
u"https://github.com/quotient-im/libquotient"_s,
|
||||
KAboutLicense::LGPL_V2_1);
|
||||
// about.addComponent(u"libQuotient"_s,
|
||||
// i18n("A Qt library to write cross-platform clients for Matrix"),
|
||||
// i18nc("<version number> (built against <possibly different version number>)",
|
||||
// "%1 (built against %2)",
|
||||
// Quotient::versionString(),
|
||||
// QStringLiteral(Quotient_VERSION_STRING)),
|
||||
// u"https://github.com/quotient-im/libquotient"_s,
|
||||
// KAboutLicense::LGPL_V2_1);
|
||||
|
||||
KAboutData::setApplicationData(about);
|
||||
QGuiApplication::setWindowIcon(QIcon::fromTheme(u"org.kde.neochat"_s));
|
||||
@@ -174,10 +183,13 @@ int main(int argc, char *argv[])
|
||||
KCrash::initialize();
|
||||
#endif
|
||||
|
||||
PendingConnection::setConnectionType<NeoChatConnection>();
|
||||
Connection::setRoomType<NeoChatRoom>();
|
||||
|
||||
initLogging();
|
||||
|
||||
Connection::setEncryptionDefault(true);
|
||||
Connection::setDirectChatEncryptionDefault(true);
|
||||
// Connection::setEncryptionDefault(true);
|
||||
// Connection::setDirectChatEncryptionDefault(true);
|
||||
|
||||
#ifdef NEOCHAT_FLATPAK
|
||||
// Copy over the included FontConfig configuration to the
|
||||
@@ -185,7 +197,7 @@ int main(int argc, char *argv[])
|
||||
QFile::copy(u"/app/etc/fonts/conf.d/99-noto-mono-color-emoji.conf"_s, u"/var/config/fontconfig/conf.d/99-noto-mono-color-emoji.conf"_s);
|
||||
#endif
|
||||
|
||||
ColorSchemer colorScheme;
|
||||
// ColorSchemer colorScheme;
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(i18n("Client for the matrix communication protocol"));
|
||||
@@ -208,7 +220,7 @@ int main(int argc, char *argv[])
|
||||
about.setupCommandLine(&parser);
|
||||
parser.process(app);
|
||||
about.processCommandLine(&parser);
|
||||
Controller::setTestMode(parser.isSet("test"_L1));
|
||||
// Controller::setTestMode(parser.isSet("test"_L1));
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
if (parser.isSet(dbusActivatedOption)) {
|
||||
@@ -220,10 +232,10 @@ int main(int argc, char *argv[])
|
||||
// Because KRunner may call us on the D-Bus (under the same service name org.kde.neochat) then it may
|
||||
// accidentally activate us for push notifications instead. If this happens, then immediately quit if the fake
|
||||
// runner is called.
|
||||
QDBusConnection::sessionBus().registerObject("/RoomRunner"_L1, new FakeRunner(), QDBusConnection::ExportScriptableContents);
|
||||
// QDBusConnection::sessionBus().registerObject("/RoomRunner"_L1, new FakeRunner(), QDBusConnection::ExportScriptableContents);
|
||||
#endif
|
||||
|
||||
Controller::listenForNotifications();
|
||||
// Controller::listenForNotifications();
|
||||
return QCoreApplication::exec();
|
||||
}
|
||||
#endif
|
||||
@@ -239,50 +251,51 @@ 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;
|
||||
|
||||
#ifdef HAVE_KDBUSADDONS
|
||||
service.connect(&service,
|
||||
&KDBusService::activateRequested,
|
||||
&RoomManager::instance(),
|
||||
[&engine](const QStringList &arguments, const QString &workingDirectory) {
|
||||
Q_UNUSED(workingDirectory);
|
||||
|
||||
QWindow *window = windowFromEngine(&engine);
|
||||
KWindowSystem::updateStartupId(window);
|
||||
|
||||
WindowController::instance().showAndRaiseWindow(QString());
|
||||
|
||||
// Open matrix uri
|
||||
if (arguments.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto args = arguments;
|
||||
args.removeFirst();
|
||||
if (args.length() == 2 && args[0] == "--share"_L1) {
|
||||
ShareHandler::instance().setText(args[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &arg : args) {
|
||||
RoomManager::instance().resolveResource(arg);
|
||||
}
|
||||
});
|
||||
#endif
|
||||
// #ifdef HAVE_KDBUSADDONS
|
||||
// service.connect(&service,
|
||||
// &KDBusService::activateRequested,
|
||||
// &RoomManager::instance(),
|
||||
// [&engine](const QStringList &arguments, const QString &workingDirectory) {
|
||||
// Q_UNUSED(workingDirectory);
|
||||
//
|
||||
// QWindow *window = windowFromEngine(&engine);
|
||||
// KWindowSystem::updateStartupId(window);
|
||||
//
|
||||
// // WindowController::instance().showAndRaiseWindow(QString());
|
||||
//
|
||||
// // Open matrix uri
|
||||
// if (arguments.isEmpty()) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// auto args = arguments;
|
||||
// args.removeFirst();
|
||||
// if (args.length() == 2 && args[0] == "--share"_L1) {
|
||||
// // ShareHandler::instance().setText(args[1]);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// for (const auto &arg : args) {
|
||||
// // RoomManager::instance().resolveResource(arg);
|
||||
// }
|
||||
// });
|
||||
// #endif
|
||||
|
||||
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
|
||||
engine.setNetworkAccessManagerFactory(new NetworkAccessManagerFactory());
|
||||
|
||||
if (parser.isSet("ignore-ssl-errors"_L1)) {
|
||||
QObject::connect(NetworkAccessManager::instance(), &QNetworkAccessManager::sslErrors, NetworkAccessManager::instance(), [](QNetworkReply *reply) {
|
||||
reply->ignoreSslErrors();
|
||||
});
|
||||
// QObject::connect(NetworkAccessManager::instance(), &QNetworkAccessManager::sslErrors, NetworkAccessManager::instance(), [](QNetworkReply *reply) {
|
||||
// reply->ignoreSslErrors();
|
||||
// });
|
||||
}
|
||||
|
||||
if (parser.isSet("share"_L1)) {
|
||||
ShareHandler::instance().setText(parser.value(shareOption));
|
||||
// ShareHandler::instance().setText(parser.value(shareOption));
|
||||
}
|
||||
|
||||
engine.addImageProvider(u"blurhash"_s, new BlurhashImageProvider);
|
||||
@@ -293,12 +306,12 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
if (!parser.positionalArguments().isEmpty() && !parser.isSet("share"_L1)) {
|
||||
RoomManager::instance().setUrlArgument(parser.positionalArguments()[0]);
|
||||
// RoomManager::instance().setUrlArgument(parser.positionalArguments()[0]);
|
||||
}
|
||||
|
||||
#ifdef HAVE_RUNNER
|
||||
auto runner = Runner::create(&engine, &engine);
|
||||
QDBusConnection::sessionBus().registerObject("/RoomRunner"_L1, runner, QDBusConnection::ExportScriptableContents);
|
||||
// auto runner = Runner::create(&engine, &engine);
|
||||
// QDBusConnection::sessionBus().registerObject("/RoomRunner"_L1, runner, QDBusConnection::ExportScriptableContents);
|
||||
#endif
|
||||
|
||||
QWindow *window = windowFromEngine(&engine);
|
||||
|
||||
126
src/messageattached.cpp
Normal file
126
src/messageattached.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
// SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "messageattached.h"
|
||||
|
||||
MessageAttached::MessageAttached(QObject *parent)
|
||||
: QQuickAttachedPropertyPropagator(parent)
|
||||
{
|
||||
if (parent == nullptr) {
|
||||
qWarning() << "Message must be attached to an Item" << parent;
|
||||
return;
|
||||
}
|
||||
initialize();
|
||||
}
|
||||
|
||||
MessageAttached *MessageAttached::qmlAttachedProperties(QObject *object)
|
||||
{
|
||||
return new MessageAttached(object);
|
||||
}
|
||||
|
||||
NeoChatRoom *MessageAttached::room() const
|
||||
{
|
||||
return m_room;
|
||||
}
|
||||
|
||||
void MessageAttached::setRoom(NeoChatRoom *room)
|
||||
{
|
||||
m_explicitRoom = true;
|
||||
if (m_room == room) {
|
||||
return;
|
||||
}
|
||||
m_room = room;
|
||||
propagateMessage(this);
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
QQuickItem *MessageAttached::timeline() const
|
||||
{
|
||||
return m_timeline;
|
||||
}
|
||||
|
||||
void MessageAttached::setTimeline(QQuickItem *timeline)
|
||||
{
|
||||
m_explicitTimeline = true;
|
||||
if (m_timeline == timeline) {
|
||||
return;
|
||||
}
|
||||
m_timeline = timeline;
|
||||
propagateMessage(this);
|
||||
Q_EMIT timelineChanged();
|
||||
}
|
||||
|
||||
int MessageAttached::index() const
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
|
||||
void MessageAttached::setIndex(int index)
|
||||
{
|
||||
m_explicitIndex = true;
|
||||
if (m_index == index) {
|
||||
return;
|
||||
}
|
||||
m_index = index;
|
||||
propagateMessage(this);
|
||||
Q_EMIT indexChanged();
|
||||
}
|
||||
|
||||
qreal MessageAttached::maxContentWidth() const
|
||||
{
|
||||
return m_maxContentWidth;
|
||||
}
|
||||
|
||||
void MessageAttached::setMaxContentWidth(qreal maxContentWidth)
|
||||
{
|
||||
m_explicitMaxContentWidth = true;
|
||||
if (m_maxContentWidth == maxContentWidth) {
|
||||
return;
|
||||
}
|
||||
m_maxContentWidth = maxContentWidth;
|
||||
propagateMessage(this);
|
||||
Q_EMIT maxContentWidthChanged();
|
||||
}
|
||||
|
||||
void MessageAttached::propagateMessage(MessageAttached *message)
|
||||
{
|
||||
if (m_explicitRoom || m_room != message->room()) {
|
||||
m_room = message->room();
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
if (m_explicitTimeline || m_timeline != message->timeline()) {
|
||||
m_timeline = message->timeline();
|
||||
Q_EMIT timelineChanged();
|
||||
}
|
||||
|
||||
if (m_explicitIndex || m_index != message->index()) {
|
||||
m_index = message->index();
|
||||
Q_EMIT indexChanged();
|
||||
}
|
||||
|
||||
if (m_explicitMaxContentWidth || m_maxContentWidth != message->maxContentWidth()) {
|
||||
m_maxContentWidth = message->maxContentWidth();
|
||||
Q_EMIT maxContentWidthChanged();
|
||||
}
|
||||
|
||||
const auto styles = attachedChildren();
|
||||
for (auto *child : attachedChildren()) {
|
||||
MessageAttached *message = qobject_cast<MessageAttached *>(child);
|
||||
if (message != nullptr) {
|
||||
message->propagateMessage(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageAttached::attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent)
|
||||
{
|
||||
Q_UNUSED(oldParent);
|
||||
|
||||
MessageAttached *attachedParent = qobject_cast<MessageAttached *>(newParent);
|
||||
if (attachedParent) {
|
||||
propagateMessage(attachedParent);
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_messageattached.cpp"
|
||||
78
src/messageattached.h
Normal file
78
src/messageattached.h
Normal file
@@ -0,0 +1,78 @@
|
||||
// SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickAttachedPropertyPropagator>
|
||||
#include <QQuickItem>
|
||||
|
||||
#include "neochatroom.h"
|
||||
|
||||
class MessageAttached : public QQuickAttachedPropertyPropagator
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_NAMED_ELEMENT(Message)
|
||||
QML_ATTACHED(MessageAttached)
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
/**
|
||||
* @brief The room that the message comes from.
|
||||
*/
|
||||
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged FINAL)
|
||||
|
||||
/**
|
||||
* @brief The timeline for the current message.
|
||||
*/
|
||||
Q_PROPERTY(QQuickItem *timeline READ timeline WRITE setTimeline NOTIFY timelineChanged FINAL)
|
||||
|
||||
/**
|
||||
* @brief The index of the message in the timeline
|
||||
*/
|
||||
Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged FINAL)
|
||||
|
||||
/**
|
||||
* @brief The width available to the message content.
|
||||
*/
|
||||
Q_PROPERTY(qreal maxContentWidth READ maxContentWidth WRITE setMaxContentWidth NOTIFY maxContentWidthChanged FINAL)
|
||||
|
||||
public:
|
||||
explicit MessageAttached(QObject *parent = nullptr);
|
||||
|
||||
static MessageAttached *qmlAttachedProperties(QObject *object);
|
||||
|
||||
NeoChatRoom *room() const;
|
||||
void setRoom(NeoChatRoom *room);
|
||||
|
||||
QQuickItem *timeline() const;
|
||||
void setTimeline(QQuickItem *timeline);
|
||||
|
||||
int index() const;
|
||||
void setIndex(int index);
|
||||
|
||||
qreal maxContentWidth() const;
|
||||
void setMaxContentWidth(qreal maxContentWidth);
|
||||
|
||||
Q_SIGNALS:
|
||||
void roomChanged();
|
||||
void timelineChanged();
|
||||
void indexChanged();
|
||||
void maxContentWidthChanged();
|
||||
|
||||
protected:
|
||||
void propagateMessage(MessageAttached *message);
|
||||
void attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent) override;
|
||||
|
||||
private:
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
bool m_explicitRoom = false;
|
||||
|
||||
QPointer<QQuickItem> m_timeline;
|
||||
bool m_explicitTimeline = false;
|
||||
|
||||
int m_index;
|
||||
bool m_explicitIndex = false;
|
||||
|
||||
qreal m_maxContentWidth = -1;
|
||||
bool m_explicitMaxContentWidth = false;
|
||||
};
|
||||
@@ -60,6 +60,11 @@ void CommonRoomsModel::reload()
|
||||
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();
|
||||
|
||||
@@ -4,23 +4,23 @@
|
||||
#include "completionmodel.h"
|
||||
#include <QDebug>
|
||||
|
||||
#include "actionsmodel.h"
|
||||
#include "completionproxymodel.h"
|
||||
#include "customemojimodel.h"
|
||||
#include "emojimodel.h"
|
||||
// #include "actionsmodel.h"
|
||||
// #include "completionproxymodel.h"
|
||||
// #include "customemojimodel.h"
|
||||
// #include "emojimodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "roommanager.h"
|
||||
#include "userlistmodel.h"
|
||||
// #include "roommanager.h"
|
||||
// #include "userlistmodel.h"
|
||||
|
||||
CompletionModel::CompletionModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, m_filterModel(new CompletionProxyModel())
|
||||
, m_userListModel(RoomManager::instance().userListModel())
|
||||
// , m_filterModel(new CompletionProxyModel())
|
||||
// , m_userListModel(RoomManager::instance().userListModel())
|
||||
, m_emojiModel(new QConcatenateTablesProxyModel(this))
|
||||
{
|
||||
connect(this, &CompletionModel::textChanged, this, &CompletionModel::updateCompletion);
|
||||
m_emojiModel->addSourceModel(&CustomEmojiModel::instance());
|
||||
m_emojiModel->addSourceModel(&EmojiModel::instance());
|
||||
// m_emojiModel->addSourceModel(&CustomEmojiModel::instance());
|
||||
// m_emojiModel->addSourceModel(&EmojiModel::instance());
|
||||
}
|
||||
|
||||
QString CompletionModel::text() const
|
||||
@@ -41,67 +41,68 @@ int CompletionModel::rowCount(const QModelIndex &parent) const
|
||||
if (m_autoCompletionType == None) {
|
||||
return 0;
|
||||
}
|
||||
return m_filterModel->rowCount();
|
||||
// return m_filterModel->rowCount();
|
||||
return {};
|
||||
}
|
||||
|
||||
QVariant CompletionModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (index.row() < 0 || index.row() >= m_filterModel->rowCount()) {
|
||||
return {};
|
||||
}
|
||||
auto filterIndex = m_filterModel->index(index.row(), 0);
|
||||
if (m_autoCompletionType == User) {
|
||||
if (role == DisplayNameRole) {
|
||||
return m_filterModel->data(filterIndex, UserListModel::DisplayNameRole);
|
||||
}
|
||||
if (role == SubtitleRole) {
|
||||
return m_filterModel->data(filterIndex, UserListModel::UserIdRole);
|
||||
}
|
||||
if (role == IconNameRole) {
|
||||
return m_filterModel->data(filterIndex, UserListModel::AvatarRole);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_autoCompletionType == Command) {
|
||||
if (role == DisplayNameRole) {
|
||||
return u"%1 %2"_s.arg(m_filterModel->data(filterIndex, ActionsModel::Prefix).toString(),
|
||||
m_filterModel->data(filterIndex, ActionsModel::Parameters).toString());
|
||||
}
|
||||
if (role == SubtitleRole) {
|
||||
return m_filterModel->data(filterIndex, ActionsModel::Description);
|
||||
}
|
||||
if (role == IconNameRole) {
|
||||
return u"invalid"_s;
|
||||
}
|
||||
if (role == ReplacedTextRole) {
|
||||
return m_filterModel->data(filterIndex, ActionsModel::Prefix);
|
||||
}
|
||||
}
|
||||
if (m_autoCompletionType == Room) {
|
||||
if (role == DisplayNameRole) {
|
||||
return m_filterModel->data(filterIndex, RoomListModel::DisplayNameRole);
|
||||
}
|
||||
if (role == SubtitleRole) {
|
||||
return m_filterModel->data(filterIndex, RoomListModel::CanonicalAliasRole);
|
||||
}
|
||||
if (role == IconNameRole) {
|
||||
return m_filterModel->data(filterIndex, RoomListModel::AvatarRole).toString();
|
||||
}
|
||||
}
|
||||
if (m_autoCompletionType == Emoji) {
|
||||
if (role == DisplayNameRole) {
|
||||
return m_filterModel->data(filterIndex, CustomEmojiModel::DisplayRole);
|
||||
}
|
||||
if (role == IconNameRole) {
|
||||
return m_filterModel->data(filterIndex, CustomEmojiModel::MxcUrl);
|
||||
}
|
||||
if (role == ReplacedTextRole) {
|
||||
return m_filterModel->data(filterIndex, CustomEmojiModel::ReplacedTextRole);
|
||||
}
|
||||
if (role == SubtitleRole) {
|
||||
return m_filterModel->data(filterIndex, EmojiModel::DescriptionRole);
|
||||
}
|
||||
}
|
||||
// if (index.row() < 0 || index.row() >= m_filterModel->rowCount()) {
|
||||
// return {};
|
||||
// }
|
||||
// auto filterIndex = m_filterModel->index(index.row(), 0);
|
||||
// if (m_autoCompletionType == User) {
|
||||
// if (role == DisplayNameRole) {
|
||||
// return m_filterModel->data(filterIndex, UserListModel::DisplayNameRole);
|
||||
// }
|
||||
// if (role == SubtitleRole) {
|
||||
// return m_filterModel->data(filterIndex, UserListModel::UserIdRole);
|
||||
// }
|
||||
// if (role == IconNameRole) {
|
||||
// return m_filterModel->data(filterIndex, UserListModel::AvatarRole);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (m_autoCompletionType == Command) {
|
||||
// if (role == DisplayNameRole) {
|
||||
// return u"%1 %2"_s.arg(m_filterModel->data(filterIndex, ActionsModel::Prefix).toString(),
|
||||
// m_filterModel->data(filterIndex, ActionsModel::Parameters).toString());
|
||||
// }
|
||||
// if (role == SubtitleRole) {
|
||||
// return m_filterModel->data(filterIndex, ActionsModel::Description);
|
||||
// }
|
||||
// if (role == IconNameRole) {
|
||||
// return u"invalid"_s;
|
||||
// }
|
||||
// if (role == ReplacedTextRole) {
|
||||
// return m_filterModel->data(filterIndex, ActionsModel::Prefix);
|
||||
// }
|
||||
// }
|
||||
// if (m_autoCompletionType == Room) {
|
||||
// if (role == DisplayNameRole) {
|
||||
// return m_filterModel->data(filterIndex, RoomListModel::DisplayNameRole);
|
||||
// }
|
||||
// if (role == SubtitleRole) {
|
||||
// return m_filterModel->data(filterIndex, RoomListModel::CanonicalAliasRole);
|
||||
// }
|
||||
// if (role == IconNameRole) {
|
||||
// return m_filterModel->data(filterIndex, RoomListModel::AvatarRole).toString();
|
||||
// }
|
||||
// }
|
||||
// if (m_autoCompletionType == Emoji) {
|
||||
// if (role == DisplayNameRole) {
|
||||
// return m_filterModel->data(filterIndex, CustomEmojiModel::DisplayRole);
|
||||
// }
|
||||
// if (role == IconNameRole) {
|
||||
// return m_filterModel->data(filterIndex, CustomEmojiModel::MxcUrl);
|
||||
// }
|
||||
// if (role == ReplacedTextRole) {
|
||||
// return m_filterModel->data(filterIndex, CustomEmojiModel::ReplacedTextRole);
|
||||
// }
|
||||
// if (role == SubtitleRole) {
|
||||
// return m_filterModel->data(filterIndex, EmojiModel::DescriptionRole);
|
||||
// }
|
||||
// }
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -118,50 +119,50 @@ QHash<int, QByteArray> CompletionModel::roleNames() const
|
||||
|
||||
void CompletionModel::updateCompletion()
|
||||
{
|
||||
if (text().startsWith(QLatin1Char('@'))) {
|
||||
m_filterModel->setSourceModel(m_userListModel);
|
||||
m_filterModel->setFilterRole(UserListModel::UserIdRole);
|
||||
m_filterModel->setSecondaryFilterRole(UserListModel::DisplayNameRole);
|
||||
m_filterModel->setFullText(m_fullText);
|
||||
m_filterModel->setFilterText(m_text);
|
||||
m_autoCompletionType = User;
|
||||
m_filterModel->invalidate();
|
||||
} else if (text().startsWith(QLatin1Char('/'))) {
|
||||
m_filterModel->setSourceModel(&ActionsModel::instance());
|
||||
m_filterModel->setFilterRole(ActionsModel::Prefix);
|
||||
m_filterModel->setSecondaryFilterRole(-1);
|
||||
m_filterModel->setFullText(m_fullText);
|
||||
m_filterModel->setFilterText(m_text.mid(1));
|
||||
m_autoCompletionType = Command;
|
||||
m_filterModel->invalidate();
|
||||
} else if (text().startsWith(QLatin1Char('#'))) {
|
||||
m_autoCompletionType = Room;
|
||||
m_filterModel->setSourceModel(m_roomListModel);
|
||||
m_filterModel->setFilterRole(RoomListModel::CanonicalAliasRole);
|
||||
m_filterModel->setSecondaryFilterRole(RoomListModel::DisplayNameRole);
|
||||
m_filterModel->setFullText(m_fullText);
|
||||
m_filterModel->setFilterText(m_text);
|
||||
m_filterModel->invalidate();
|
||||
} else if (text().startsWith(QLatin1Char(':')) && text().size() > 1 && !text()[1].isUpper()
|
||||
&& (m_fullText.indexOf(QLatin1Char(':'), 1) == -1
|
||||
|| (m_fullText.indexOf(QLatin1Char(' ')) != -1 && m_fullText.indexOf(QLatin1Char(':'), 1) > m_fullText.indexOf(QLatin1Char(' '), 1)))) {
|
||||
m_filterModel->setSourceModel(m_emojiModel);
|
||||
m_autoCompletionType = Emoji;
|
||||
m_filterModel->setFilterRole(CustomEmojiModel::Name);
|
||||
m_filterModel->setSecondaryFilterRole(EmojiModel::DescriptionRole);
|
||||
m_filterModel->setFullText(m_fullText);
|
||||
m_filterModel->setFilterText(m_text);
|
||||
m_filterModel->invalidate();
|
||||
} else {
|
||||
m_autoCompletionType = None;
|
||||
}
|
||||
// if (text().startsWith(QLatin1Char('@'))) {
|
||||
// m_filterModel->setSourceModel(m_userListModel);
|
||||
// m_filterModel->setFilterRole(UserListModel::UserIdRole);
|
||||
// m_filterModel->setSecondaryFilterRole(UserListModel::DisplayNameRole);
|
||||
// m_filterModel->setFullText(m_fullText);
|
||||
// m_filterModel->setFilterText(m_text);
|
||||
// m_autoCompletionType = User;
|
||||
// m_filterModel->invalidate();
|
||||
// } else if (text().startsWith(QLatin1Char('/'))) {
|
||||
// m_filterModel->setSourceModel(&ActionsModel::instance());
|
||||
// m_filterModel->setFilterRole(ActionsModel::Prefix);
|
||||
// m_filterModel->setSecondaryFilterRole(-1);
|
||||
// m_filterModel->setFullText(m_fullText);
|
||||
// m_filterModel->setFilterText(m_text.mid(1));
|
||||
// m_autoCompletionType = Command;
|
||||
// m_filterModel->invalidate();
|
||||
// } else if (text().startsWith(QLatin1Char('#'))) {
|
||||
// m_autoCompletionType = Room;
|
||||
// m_filterModel->setSourceModel(m_roomListModel);
|
||||
// m_filterModel->setFilterRole(RoomListModel::CanonicalAliasRole);
|
||||
// m_filterModel->setSecondaryFilterRole(RoomListModel::DisplayNameRole);
|
||||
// m_filterModel->setFullText(m_fullText);
|
||||
// m_filterModel->setFilterText(m_text);
|
||||
// m_filterModel->invalidate();
|
||||
// } else if (text().startsWith(QLatin1Char(':')) && text().size() > 1 && !text()[1].isUpper()
|
||||
// && (m_fullText.indexOf(QLatin1Char(':'), 1) == -1
|
||||
// || (m_fullText.indexOf(QLatin1Char(' ')) != -1 && m_fullText.indexOf(QLatin1Char(':'), 1) > m_fullText.indexOf(QLatin1Char(' '), 1)))) {
|
||||
// m_filterModel->setSourceModel(m_emojiModel);
|
||||
// m_autoCompletionType = Emoji;
|
||||
// m_filterModel->setFilterRole(CustomEmojiModel::Name);
|
||||
// m_filterModel->setSecondaryFilterRole(EmojiModel::DescriptionRole);
|
||||
// m_filterModel->setFullText(m_fullText);
|
||||
// m_filterModel->setFilterText(m_text);
|
||||
// m_filterModel->invalidate();
|
||||
// } else {
|
||||
// m_autoCompletionType = None;
|
||||
// }
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
NeoChatRoom *CompletionModel::room() const
|
||||
{
|
||||
return m_room;
|
||||
return m_room.get();
|
||||
}
|
||||
|
||||
void CompletionModel::setRoom(NeoChatRoom *room)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <QQmlEngine>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "roomlistmodel.h"
|
||||
// #include "roomlistmodel.h"
|
||||
|
||||
class CompletionProxyModel;
|
||||
class UserListModel;
|
||||
@@ -47,7 +47,7 @@ class CompletionModel : public QAbstractListModel
|
||||
/**
|
||||
* @brief The RoomListModel to be used for room completions.
|
||||
*/
|
||||
Q_PROPERTY(RoomListModel *roomListModel READ roomListModel WRITE setRoomListModel NOTIFY roomListModelChanged)
|
||||
// Q_PROPERTY(RoomListModel *roomListModel READ roomListModel WRITE setRoomListModel NOTIFY roomListModelChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
|
||||
@@ -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,10 @@ 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)
|
||||
@@ -400,6 +409,7 @@ QHash<int, QByteArray> MessageContentModel::roleNamesStatic()
|
||||
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";
|
||||
@@ -480,11 +490,16 @@ 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 (roomMessageEvent && (roomMessageEvent->isThreaded() || m_room->threads().contains(roomMessageEvent->id()))
|
||||
if (NeoChatConfig::self()->threads() && roomMessageEvent && (roomMessageEvent->isThreaded() || m_room->threads().contains(roomMessageEvent->id()))
|
||||
&& roomMessageEvent->id() == roomMessageEvent->threadRootEventId()) {
|
||||
#else
|
||||
if (isThreading && roomMessageEvent && roomMessageEvent->isThreaded() && roomMessageEvent->id() == roomMessageEvent->threadRootEventId()) {
|
||||
if (NeoChatConfig::self()->threads() && roomMessageEvent && roomMessageEvent->isThreaded()
|
||||
&& roomMessageEvent->id() == roomMessageEvent->threadRootEventId()) {
|
||||
#endif
|
||||
newComponents += MessageComponent{MessageComponentType::Separator, {}, {}};
|
||||
newComponents += MessageComponent{MessageComponentType::ThreadBody, u"Thread Body"_s, {}};
|
||||
@@ -513,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;
|
||||
}
|
||||
@@ -524,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});
|
||||
@@ -731,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. */
|
||||
@@ -125,6 +127,7 @@ private:
|
||||
QPointer<MessageContentModel> m_replyModel;
|
||||
void updateReplyModel();
|
||||
|
||||
ReactionModel *m_reactionModel = nullptr;
|
||||
ItineraryModel *m_itineraryModel = nullptr;
|
||||
|
||||
QList<MessageComponent> componentsForType(MessageComponentType::Type type);
|
||||
@@ -135,4 +138,6 @@ private:
|
||||
|
||||
void updateItineraryModel();
|
||||
bool m_emptyItinerary = false;
|
||||
|
||||
void updateReactionModel();
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "neochatconfig.h"
|
||||
|
||||
#include <Quotient/events/encryptedevent.h>
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
#include <Quotient/events/stickerevent.h>
|
||||
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||
@@ -120,8 +121,12 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
if (role == ContentModelRole) {
|
||||
if (event->get().is<EncryptedEvent>()) {
|
||||
return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(event->get().id()));
|
||||
}
|
||||
|
||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(&event.value().get());
|
||||
if (roomMessageEvent && roomMessageEvent->isThreaded()) {
|
||||
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()));
|
||||
@@ -216,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();
|
||||
}
|
||||
@@ -258,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());
|
||||
@@ -323,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";
|
||||
@@ -454,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()
|
||||
@@ -504,7 +473,6 @@ void MessageModel::clearModel()
|
||||
|
||||
void MessageModel::clearEventObjects()
|
||||
{
|
||||
m_reactionModels.clear();
|
||||
m_readMarkerModels.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -77,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. */
|
||||
@@ -155,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);
|
||||
};
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <Quotient/converters.h>
|
||||
#include <Quotient/csapi/definitions/push_ruleset.h>
|
||||
#include <Quotient/csapi/pushrules.h>
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
// #include <Quotient/converters.h>
|
||||
// #include <Quotient/csapi/definitions/push_ruleset.h>
|
||||
// #include <Quotient/csapi/pushrules.h>
|
||||
// #include <Quotient/jobs/basejob.h>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
|
||||
@@ -74,18 +74,18 @@ void PushRuleModel::updateNotificationRules(const QString &type)
|
||||
return;
|
||||
}
|
||||
|
||||
const QJsonObject ruleDataJson = m_connection->accountDataJson(u"m.push_rules"_s);
|
||||
const Quotient::PushRuleset ruleData = Quotient::fromJson<Quotient::PushRuleset>(ruleDataJson["global"_L1].toObject());
|
||||
// const QJsonObject ruleDataJson = m_connection->accountDataJson(u"m.push_rules"_s);
|
||||
// const Quotient::PushRuleset ruleData = Quotient::fromJson<Quotient::PushRuleset>(ruleDataJson["global"_L1].toObject());
|
||||
|
||||
beginResetModel();
|
||||
m_rules.clear();
|
||||
|
||||
// Doing this 5 times because PushRuleset is a struct.
|
||||
setRules(ruleData.override, PushRuleKind::Override);
|
||||
setRules(ruleData.content, PushRuleKind::Content);
|
||||
setRules(ruleData.room, PushRuleKind::Room);
|
||||
setRules(ruleData.sender, PushRuleKind::Sender);
|
||||
setRules(ruleData.underride, PushRuleKind::Underride);
|
||||
// setRules(ruleData.override, PushRuleKind::Override);
|
||||
// setRules(ruleData.content, PushRuleKind::Content);
|
||||
// setRules(ruleData.room, PushRuleKind::Room);
|
||||
// setRules(ruleData.sender, PushRuleKind::Sender);
|
||||
// setRules(ruleData.underride, PushRuleKind::Underride);
|
||||
|
||||
Q_EMIT globalNotificationsEnabledChanged();
|
||||
Q_EMIT globalNotificationsSetChanged();
|
||||
@@ -93,28 +93,28 @@ void PushRuleModel::updateNotificationRules(const QString &type)
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void PushRuleModel::setRules(QList<Quotient::PushRule> rules, PushRuleKind::Kind kind)
|
||||
{
|
||||
for (const auto &rule : rules) {
|
||||
QString roomId;
|
||||
if (rule.conditions.size() > 0) {
|
||||
for (const auto &condition : std::as_const(rule.conditions)) {
|
||||
if (condition.key == u"room_id"_s) {
|
||||
roomId = condition.pattern;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_rules.append(Rule{
|
||||
rule.ruleId,
|
||||
kind,
|
||||
variantToAction(rule.actions, rule.enabled),
|
||||
getSection(rule),
|
||||
rule.enabled,
|
||||
roomId,
|
||||
});
|
||||
}
|
||||
}
|
||||
// void PushRuleModel::setRules(QList<Quotient::PushRule> rules, PushRuleKind::Kind kind)
|
||||
// {
|
||||
// for (const auto &rule : rules) {
|
||||
// QString roomId;
|
||||
// if (rule.conditions.size() > 0) {
|
||||
// for (const auto &condition : std::as_const(rule.conditions)) {
|
||||
// if (condition.key == u"room_id"_s) {
|
||||
// roomId = condition.pattern;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// m_rules.append(Rule{
|
||||
// rule.ruleId,
|
||||
// kind,
|
||||
// variantToAction(rule.actions, rule.enabled),
|
||||
// getSection(rule),
|
||||
// rule.enabled,
|
||||
// roomId,
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
int PushRuleModel::getRuleIndex(const QString &ruleId) const
|
||||
{
|
||||
@@ -126,51 +126,51 @@ int PushRuleModel::getRuleIndex(const QString &ruleId) const
|
||||
return -1;
|
||||
}
|
||||
|
||||
PushRuleSection::Section PushRuleModel::getSection(Quotient::PushRule rule)
|
||||
{
|
||||
auto ruleId = rule.ruleId;
|
||||
|
||||
if (defaultSections.contains(ruleId)) {
|
||||
return defaultSections.value(ruleId);
|
||||
} else {
|
||||
if (rule.ruleId.startsWith(u'.')) {
|
||||
return PushRuleSection::Unknown;
|
||||
}
|
||||
/**
|
||||
* If the rule name resolves to a matrix id for a room that the user is part
|
||||
* of it shouldn't appear in the global list as it's overriding the global
|
||||
* state for that room.
|
||||
*
|
||||
* Rooms that the user hasn't joined shouldn't have a rule.
|
||||
*/
|
||||
if (m_connection->room(ruleId) != nullptr) {
|
||||
return PushRuleSection::Undefined;
|
||||
}
|
||||
/**
|
||||
* If the rule name resolves to a matrix id for a user it shouldn't appear
|
||||
* in the global list as it's a rule to block notifications from a user and
|
||||
* is handled elsewhere.
|
||||
*/
|
||||
auto testUserId = ruleId;
|
||||
// Rules for user matrix IDs often don't have the @ on the beginning so add
|
||||
// if not there to avoid malformed ID.
|
||||
if (!testUserId.startsWith(u'@')) {
|
||||
testUserId.prepend(u'@');
|
||||
}
|
||||
if (testUserId.startsWith(u'@') && !Quotient::serverPart(testUserId).isEmpty() && m_connection->user(testUserId) != nullptr) {
|
||||
return PushRuleSection::Undefined;
|
||||
}
|
||||
// If the rule has push conditions and one is a room ID it is a room only keyword.
|
||||
if (!rule.conditions.isEmpty()) {
|
||||
for (const auto &condition : std::as_const(rule.conditions)) {
|
||||
if (condition.key == u"room_id"_s) {
|
||||
return PushRuleSection::RoomKeywords;
|
||||
}
|
||||
}
|
||||
}
|
||||
return PushRuleSection::Keywords;
|
||||
}
|
||||
}
|
||||
// PushRuleSection::Section PushRuleModel::getSection(Quotient::PushRule rule)
|
||||
// {
|
||||
// auto ruleId = rule.ruleId;
|
||||
//
|
||||
// if (defaultSections.contains(ruleId)) {
|
||||
// return defaultSections.value(ruleId);
|
||||
// } else {
|
||||
// if (rule.ruleId.startsWith(u'.')) {
|
||||
// return PushRuleSection::Unknown;
|
||||
// }
|
||||
// /**
|
||||
// * If the rule name resolves to a matrix id for a room that the user is part
|
||||
// * of it shouldn't appear in the global list as it's overriding the global
|
||||
// * state for that room.
|
||||
// *
|
||||
// * Rooms that the user hasn't joined shouldn't have a rule.
|
||||
// */
|
||||
// if (m_connection->room(ruleId) != nullptr) {
|
||||
// return PushRuleSection::Undefined;
|
||||
// }
|
||||
// /**
|
||||
// * If the rule name resolves to a matrix id for a user it shouldn't appear
|
||||
// * in the global list as it's a rule to block notifications from a user and
|
||||
// * is handled elsewhere.
|
||||
// */
|
||||
// auto testUserId = ruleId;
|
||||
// // Rules for user matrix IDs often don't have the @ on the beginning so add
|
||||
// // if not there to avoid malformed ID.
|
||||
// if (!testUserId.startsWith(u'@')) {
|
||||
// testUserId.prepend(u'@');
|
||||
// }
|
||||
// if (testUserId.startsWith(u'@') && !Quotient::serverPart(testUserId).isEmpty() && m_connection->user(testUserId) != nullptr) {
|
||||
// return PushRuleSection::Undefined;
|
||||
// }
|
||||
// // If the rule has push conditions and one is a room ID it is a room only keyword.
|
||||
// if (!rule.conditions.isEmpty()) {
|
||||
// for (const auto &condition : std::as_const(rule.conditions)) {
|
||||
// if (condition.key == u"room_id"_s) {
|
||||
// return PushRuleSection::RoomKeywords;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return PushRuleSection::Keywords;
|
||||
// }
|
||||
// }
|
||||
|
||||
PushRuleAction::Action PushRuleModel::defaultState() const
|
||||
{
|
||||
@@ -294,33 +294,33 @@ void PushRuleModel::addKeyword(const QString &keyword, const QString &roomId)
|
||||
{
|
||||
PushRuleKind::Kind kind = PushRuleKind::Content;
|
||||
const QList<QVariant> actions = actionToVariant(m_defaultKeywordAction);
|
||||
QList<Quotient::PushCondition> pushConditions;
|
||||
if (!roomId.isEmpty()) {
|
||||
kind = PushRuleKind::Override;
|
||||
|
||||
Quotient::PushCondition roomCondition;
|
||||
roomCondition.kind = u"event_match"_s;
|
||||
roomCondition.key = u"room_id"_s;
|
||||
roomCondition.pattern = roomId;
|
||||
pushConditions.append(roomCondition);
|
||||
|
||||
Quotient::PushCondition keywordCondition;
|
||||
keywordCondition.kind = u"event_match"_s;
|
||||
keywordCondition.key = u"content.body"_s;
|
||||
keywordCondition.pattern = keyword;
|
||||
pushConditions.append(keywordCondition);
|
||||
}
|
||||
|
||||
auto job = m_connection->callApi<Quotient::SetPushRuleJob>(PushRuleKind::kindString(kind),
|
||||
keyword,
|
||||
actions,
|
||||
QString(),
|
||||
QString(),
|
||||
pushConditions,
|
||||
roomId.isEmpty() ? keyword : QString());
|
||||
connect(job, &Quotient::BaseJob::failure, this, [job, keyword]() {
|
||||
qWarning() << "Unable to set push rule for keyword %1: "_L1.arg(keyword) << job->errorString();
|
||||
});
|
||||
// QList<Quotient::PushCondition> pushConditions;
|
||||
// if (!roomId.isEmpty()) {
|
||||
// kind = PushRuleKind::Override;
|
||||
//
|
||||
// Quotient::PushCondition roomCondition;
|
||||
// roomCondition.kind = u"event_match"_s;
|
||||
// roomCondition.key = u"room_id"_s;
|
||||
// roomCondition.pattern = roomId;
|
||||
// pushConditions.append(roomCondition);
|
||||
//
|
||||
// Quotient::PushCondition keywordCondition;
|
||||
// keywordCondition.kind = u"event_match"_s;
|
||||
// keywordCondition.key = u"content.body"_s;
|
||||
// keywordCondition.pattern = keyword;
|
||||
// pushConditions.append(keywordCondition);
|
||||
// }
|
||||
//
|
||||
// auto job = m_connection->callApi<Quotient::SetPushRuleJob>(PushRuleKind::kindString(kind),
|
||||
// keyword,
|
||||
// actions,
|
||||
// QString(),
|
||||
// QString(),
|
||||
// pushConditions,
|
||||
// roomId.isEmpty() ? keyword : QString());
|
||||
// connect(job, &Quotient::BaseJob::failure, this, [job, keyword]() {
|
||||
// qWarning() << "Unable to set push rule for keyword %1: "_L1.arg(keyword) << job->errorString();
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,20 +336,20 @@ void PushRuleModel::removeKeyword(const QString &keyword)
|
||||
}
|
||||
|
||||
auto kind = PushRuleKind::kindString(m_rules[index].kind);
|
||||
auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(kind, m_rules[index].id);
|
||||
connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
|
||||
qWarning() << "Unable to remove push rule for keyword %1: "_L1.arg(m_rules[index].id) << job->errorString();
|
||||
});
|
||||
// auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(kind, m_rules[index].id);
|
||||
// connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
|
||||
// qWarning() << "Unable to remove push rule for keyword %1: "_L1.arg(m_rules[index].id) << job->errorString();
|
||||
// });
|
||||
}
|
||||
|
||||
void PushRuleModel::setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled)
|
||||
{
|
||||
auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(kind, ruleId);
|
||||
connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled, this]() {
|
||||
if (job->enabled() != enabled) {
|
||||
m_connection->callApi<Quotient::SetPushRuleEnabledJob>(kind, ruleId, enabled);
|
||||
}
|
||||
});
|
||||
// auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(kind, ruleId);
|
||||
// connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled, this]() {
|
||||
// if (job->enabled() != enabled) {
|
||||
// m_connection->callApi<Quotient::SetPushRuleEnabledJob>(kind, ruleId, enabled);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
void PushRuleModel::setNotificationRuleActions(const QString &kind, const QString &ruleId, PushRuleAction::Action action)
|
||||
@@ -361,7 +361,7 @@ void PushRuleModel::setNotificationRuleActions(const QString &kind, const QStrin
|
||||
actions = actionToVariant(action);
|
||||
}
|
||||
|
||||
m_connection->callApi<Quotient::SetPushRuleActionsJob>(kind, ruleId, actions);
|
||||
// m_connection->callApi<Quotient::SetPushRuleActionsJob>(kind, ruleId, actions);
|
||||
}
|
||||
|
||||
PushRuleAction::Action PushRuleModel::variantToAction(const QList<QVariant> &actions, bool enabled)
|
||||
@@ -378,14 +378,14 @@ PushRuleAction::Action PushRuleModel::variantToAction(const QList<QVariant> &act
|
||||
continue;
|
||||
}
|
||||
|
||||
QJsonObject action = i.toJsonObject();
|
||||
if (action["set_tweak"_L1].toString() == u"sound"_s) {
|
||||
isNoisy = true;
|
||||
} else if (action["set_tweak"_L1].toString() == u"highlight"_s) {
|
||||
if (action["value"_L1].toString() != u"false"_s) {
|
||||
highlightEnabled = true;
|
||||
}
|
||||
}
|
||||
// QJsonObject action = i.toJsonObject();
|
||||
// if (action["set_tweak"_L1].toString() == u"sound"_s) {
|
||||
// isNoisy = true;
|
||||
// } else if (action["set_tweak"_L1].toString() == u"highlight"_s) {
|
||||
// if (action["value"_L1].toString() != u"false"_s) {
|
||||
// highlightEnabled = true;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
@@ -424,15 +424,15 @@ QList<QVariant> PushRuleModel::actionToVariant(PushRuleAction::Action action, co
|
||||
actions.append(u"dont_notify"_s);
|
||||
}
|
||||
if (action == PushRuleAction::Noisy || action == PushRuleAction::NoisyHighlight) {
|
||||
QJsonObject soundTweak;
|
||||
soundTweak.insert("set_tweak"_L1, u"sound"_s);
|
||||
soundTweak.insert("value"_L1, sound);
|
||||
actions.append(soundTweak);
|
||||
// QJsonObject soundTweak;
|
||||
// soundTweak.insert("set_tweak"_L1, u"sound"_s);
|
||||
// soundTweak.insert("value"_L1, sound);
|
||||
// actions.append(soundTweak);
|
||||
}
|
||||
if (action == PushRuleAction::Highlight || action == PushRuleAction::NoisyHighlight) {
|
||||
QJsonObject highlightTweak;
|
||||
highlightTweak.insert("set_tweak"_L1, u"highlight"_s);
|
||||
actions.append(highlightTweak);
|
||||
// QJsonObject highlightTweak;
|
||||
// highlightTweak.insert("set_tweak"_L1, u"highlight"_s);
|
||||
// actions.append(highlightTweak);
|
||||
}
|
||||
|
||||
return actions;
|
||||
@@ -452,7 +452,7 @@ void PushRuleModel::setConnection(NeoChatConnection *connection)
|
||||
Q_EMIT connectionChanged();
|
||||
|
||||
if (m_connection) {
|
||||
connect(m_connection, &NeoChatConnection::accountDataChanged, this, &PushRuleModel::updateNotificationRules);
|
||||
// connect(m_connection, &NeoChatConnection::accountDataChanged, this, &PushRuleModel::updateNotificationRules);
|
||||
updateNotificationRules(u"m.push_rules"_s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <QAbstractListModel>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/csapi/definitions/push_rule.h>
|
||||
// #include <Quotient/csapi/definitions/push_rule.h>
|
||||
|
||||
#include "enums/pushrule.h"
|
||||
#include "neochatconnection.h"
|
||||
@@ -130,10 +130,10 @@ private:
|
||||
QList<Rule> m_rules;
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
|
||||
void setRules(QList<Quotient::PushRule> rules, PushRuleKind::Kind kind);
|
||||
// void setRules(QList<Quotient::PushRule> rules, PushRuleKind::Kind kind);
|
||||
|
||||
int getRuleIndex(const QString &ruleId) const;
|
||||
PushRuleSection::Section getSection(Quotient::PushRule rule);
|
||||
// PushRuleSection::Section getSection(Quotient::PushRule rule);
|
||||
|
||||
void setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled);
|
||||
void setNotificationRuleActions(const QString &kind, const QString &ruleId, PushRuleAction::Action action);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ RoomTreeItem::RoomTreeItem(TreeData data, RoomTreeItem *parent)
|
||||
|
||||
bool RoomTreeItem::operator==(const RoomTreeItem &other) const
|
||||
{
|
||||
if (std::holds_alternative<NeoChatRoomType::Types>(m_data) && std::holds_alternative<NeoChatRoomType::Types>(other.data())) {
|
||||
return std::get<NeoChatRoomType::Types>(m_data) == std::get<NeoChatRoomType::Types>(m_data);
|
||||
if (std::holds_alternative<NeoChatRoomType::Type>(m_data) && std::holds_alternative<NeoChatRoomType::Type>(other.data())) {
|
||||
return std::get<NeoChatRoomType::Type>(m_data) == std::get<NeoChatRoomType::Type>(m_data);
|
||||
}
|
||||
if (std::holds_alternative<NeoChatRoom *>(m_data) && std::holds_alternative<NeoChatRoom *>(other.data())) {
|
||||
return std::get<NeoChatRoom *>(m_data)->id() == std::get<NeoChatRoom *>(m_data)->id();
|
||||
if (std::holds_alternative<RoomWrapper *>(m_data) && std::holds_alternative<RoomWrapper *>(other.data())) {
|
||||
return (*std::get<RoomWrapper *>(m_data)->item)->id() == (*std::get<RoomWrapper *>(other.data())->item)->id();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -84,13 +84,13 @@ RoomTreeItem::TreeData RoomTreeItem::data() const
|
||||
return m_data;
|
||||
}
|
||||
|
||||
std::optional<int> RoomTreeItem::rowForRoom(Quotient::Room *room) const
|
||||
std::optional<int> RoomTreeItem::rowForRoom(rust::Box<sdk::RoomListRoom> room) const
|
||||
{
|
||||
Q_ASSERT_X(std::holds_alternative<NeoChatRoomType::Types>(m_data), __FUNCTION__, "rowForRoom only works items for rooms not categories");
|
||||
Q_ASSERT_X(std::holds_alternative<NeoChatRoomType::Type>(m_data), __FUNCTION__, "rowForRoom only works items for rooms not categories");
|
||||
|
||||
int i = 0;
|
||||
for (const auto &child : m_children) {
|
||||
if (std::get<NeoChatRoom *>(child->data()) == room) {
|
||||
if ((*std::get<RoomWrapper *>(child->data())->item)->id() == room->id()) {
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
|
||||
// SPDX-FileCopyrightText: 2024 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "enums/neochatroomtype.h"
|
||||
#include "neochatroomtype.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
namespace sdk
|
||||
{
|
||||
struct RoomListRoom;
|
||||
}
|
||||
|
||||
struct RoomWrapper {
|
||||
std::optional<rust::Box<sdk::RoomListRoom>> item;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class RoomTreeItem
|
||||
*
|
||||
* This class defines an item in the space tree hierarchy model.
|
||||
*
|
||||
* @note This is separate from Quotient::Room and NeoChatRoom because we don't have
|
||||
* full room information for any room/space the user hasn't joined and we
|
||||
* don't want to create one for ever possible child in a space as that would
|
||||
* be expensive.
|
||||
*
|
||||
* @sa Quotient::Room, NeoChatRoom
|
||||
* This class defines an item in a room tree.
|
||||
*/
|
||||
class RoomTreeItem
|
||||
{
|
||||
public:
|
||||
using TreeData = std::variant<NeoChatRoom *, NeoChatRoomType::Types>;
|
||||
using TreeData = std::variant<RoomWrapper *, NeoChatRoomType::Type>;
|
||||
|
||||
explicit RoomTreeItem(TreeData data, RoomTreeItem *parent = nullptr);
|
||||
|
||||
@@ -68,7 +69,7 @@ public:
|
||||
*/
|
||||
TreeData data() const;
|
||||
|
||||
std::optional<int> rowForRoom(Quotient::Room *room) const;
|
||||
std::optional<int> rowForRoom(rust::Box<sdk::RoomListRoom> room) const;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<RoomTreeItem>> m_children;
|
||||
|
||||
@@ -1,23 +1,45 @@
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "roomtreemodel.h"
|
||||
|
||||
#include <Quotient/room.h>
|
||||
|
||||
#include "eventhandler.h"
|
||||
#include "neochatconnection.h"
|
||||
// #include "eventhandler.h"
|
||||
#include "neochatroomtype.h"
|
||||
#include "spacehierarchycache.h"
|
||||
#include "rust/cxx.h"
|
||||
#include <Integral/lib.rs.h>
|
||||
// #include "spacehierarchycache.h"
|
||||
#include <Integral/RoomStream>
|
||||
#include <Integral/Utils>
|
||||
|
||||
using namespace Quotient;
|
||||
using namespace Integral;
|
||||
|
||||
class RoomTreeModel::Private
|
||||
{
|
||||
public:
|
||||
QPointer<Integral::Connection> connection;
|
||||
std::unique_ptr<RoomStream> roomStream = nullptr;
|
||||
std::unique_ptr<RoomTreeItem> rootItem;
|
||||
// Since the rooms are streamed as vector diffs we need to keep track of them
|
||||
// for things like the index value of insert to make sense.
|
||||
QList<QPersistentModelIndex> roomIndexes;
|
||||
|
||||
void roomsUpdate();
|
||||
void resetTree();
|
||||
|
||||
RoomTreeModel *q = nullptr;
|
||||
};
|
||||
|
||||
RoomTreeModel::RoomTreeModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, m_rootItem(new RoomTreeItem(nullptr))
|
||||
, d(std::make_unique<Private>())
|
||||
{
|
||||
d->q = this;
|
||||
}
|
||||
|
||||
RoomTreeModel::~RoomTreeModel() = default;
|
||||
|
||||
RoomTreeItem *RoomTreeModel::getItem(const QModelIndex &index) const
|
||||
{
|
||||
if (index.isValid()) {
|
||||
@@ -26,179 +48,226 @@ RoomTreeItem *RoomTreeModel::getItem(const QModelIndex &index) const
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return m_rootItem.get();
|
||||
return d->rootItem.get();
|
||||
}
|
||||
|
||||
void RoomTreeModel::resetModel()
|
||||
{
|
||||
if (m_connection == nullptr) {
|
||||
if (d->connection == nullptr) {
|
||||
beginResetModel();
|
||||
m_rootItem.reset();
|
||||
d->rootItem.reset();
|
||||
d->roomStream.reset();
|
||||
endResetModel();
|
||||
return;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
m_rootItem.reset(new RoomTreeItem(nullptr));
|
||||
d->resetTree();
|
||||
|
||||
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
|
||||
m_rootItem->insertChild(std::make_unique<RoomTreeItem>(NeoChatRoomType::Types(i), m_rootItem.get()));
|
||||
}
|
||||
d->roomStream = d->connection->roomStream();
|
||||
connect(d->roomStream.get(), &RoomStream::roomsUpdate, this, [this]() {
|
||||
d->roomsUpdate();
|
||||
});
|
||||
|
||||
for (const auto &r : m_connection->allRooms()) {
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(r);
|
||||
const auto type = NeoChatRoomType::typeForRoom(room);
|
||||
const auto categoryItem = m_rootItem->child(type);
|
||||
if (categoryItem->insertChild(std::make_unique<RoomTreeItem>(room, categoryItem))) {
|
||||
connectRoomSignals(room);
|
||||
}
|
||||
}
|
||||
d->roomStream->startStream();
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void RoomTreeModel::setConnection(NeoChatConnection *connection)
|
||||
void RoomTreeModel::Private::resetTree()
|
||||
{
|
||||
if (m_connection == connection) {
|
||||
rootItem.reset(new RoomTreeItem(nullptr));
|
||||
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
|
||||
rootItem->insertChild(std::make_unique<RoomTreeItem>(NeoChatRoomType::Type(i), rootItem.get()));
|
||||
}
|
||||
}
|
||||
|
||||
void RoomTreeModel::setConnection(Connection *connection)
|
||||
{
|
||||
if (d->connection == connection) {
|
||||
return;
|
||||
}
|
||||
if (m_connection) {
|
||||
disconnect(m_connection.get(), nullptr, this, nullptr);
|
||||
if (d->connection) {
|
||||
d->connection->disconnect(this);
|
||||
}
|
||||
m_connection = connection;
|
||||
d->connection = connection;
|
||||
|
||||
resetModel();
|
||||
|
||||
connect(connection, &Connection::newRoom, this, &RoomTreeModel::newRoom);
|
||||
connect(connection, &Connection::leftRoom, this, &RoomTreeModel::leftRoom);
|
||||
connect(connection, &Connection::aboutToDeleteRoom, this, &RoomTreeModel::leftRoom);
|
||||
|
||||
Q_EMIT connectionChanged();
|
||||
}
|
||||
|
||||
void RoomTreeModel::newRoom(Room *r)
|
||||
void RoomTreeModel::Private::roomsUpdate()
|
||||
{
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(r);
|
||||
const auto type = NeoChatRoomType::typeForRoom(room);
|
||||
// Check if the room is already in the model.
|
||||
const auto checkRoomIndex = indexForRoom(room);
|
||||
if (checkRoomIndex.isValid()) {
|
||||
// If the room is in the wrong type category for whatever reason, move it.
|
||||
if (checkRoomIndex.parent().row() != type) {
|
||||
moveRoom(room);
|
||||
const auto diff = roomStream->next();
|
||||
|
||||
switch (diff->op()) {
|
||||
case 0: { // Append
|
||||
for (const auto &it : diff->items_vec()) {
|
||||
const auto type = NeoChatRoomType::typeForRoom(it.box_me());
|
||||
const auto parentItem = rootItem->child(type);
|
||||
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
|
||||
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{it.box_me()}, parentItem))) {
|
||||
// connectRoomSignals(room);
|
||||
}
|
||||
q->endInsertRows();
|
||||
roomIndexes.append(q->indexForRoom(it.box_me()));
|
||||
}
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
const auto parentItem = m_rootItem->child(type);
|
||||
beginInsertRows(index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
|
||||
parentItem->insertChild(std::make_unique<RoomTreeItem>(room, parentItem));
|
||||
connectRoomSignals(room);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void RoomTreeModel::leftRoom(Room *r)
|
||||
{
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(r);
|
||||
auto index = indexForRoom(room);
|
||||
if (!index.isValid()) {
|
||||
return;
|
||||
case 1: { // Clear
|
||||
q->beginResetModel();
|
||||
resetTree();
|
||||
roomIndexes.clear();
|
||||
q->endResetModel();
|
||||
break;
|
||||
}
|
||||
|
||||
const auto parentItem = getItem(index.parent());
|
||||
Q_ASSERT(parentItem);
|
||||
|
||||
beginRemoveRows(index.parent(), index.row(), index.row());
|
||||
parentItem->removeChild(index.row());
|
||||
room->disconnect(this);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
void RoomTreeModel::moveRoom(Quotient::Room *room)
|
||||
{
|
||||
// We can't assume the type as it has changed so currently the return of
|
||||
// NeoChatRoomType::typeForRoom doesn't match it's current location. So find the room.
|
||||
NeoChatRoomType::Types oldType;
|
||||
int oldRow = -1;
|
||||
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
|
||||
const auto categoryItem = m_rootItem->child(i);
|
||||
const auto row = categoryItem->rowForRoom(room);
|
||||
if (row) {
|
||||
oldType = static_cast<NeoChatRoomType::Types>(i);
|
||||
oldRow = *row;
|
||||
case 2: { // Push Front
|
||||
const auto type = NeoChatRoomType::typeForRoom(diff->item());
|
||||
const auto parentItem = rootItem->child(type);
|
||||
q->beginInsertRows(q->index(parentItem->row(), 0), 0, 0);
|
||||
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{diff->item()}, parentItem))) {
|
||||
// connectRoomSignals(room);
|
||||
}
|
||||
q->endInsertRows();
|
||||
roomIndexes.prepend(q->indexForRoom(diff->item()));
|
||||
break;
|
||||
}
|
||||
|
||||
if (oldRow == -1) {
|
||||
return;
|
||||
case 3: { // Push Back
|
||||
const auto type = NeoChatRoomType::typeForRoom(diff->item());
|
||||
const auto parentItem = rootItem->child(type);
|
||||
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
|
||||
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{diff->item()}, parentItem))) {
|
||||
// connectRoomSignals(room);
|
||||
}
|
||||
q->endInsertRows();
|
||||
roomIndexes.append(q->indexForRoom(diff->item()));
|
||||
break;
|
||||
}
|
||||
auto neochatRoom = dynamic_cast<NeoChatRoom *>(room);
|
||||
const auto newType = NeoChatRoomType::typeForRoom(neochatRoom);
|
||||
if (newType == oldType) {
|
||||
return;
|
||||
case 4: { // Pop Front
|
||||
const auto index = roomIndexes.front();
|
||||
q->beginRemoveRows(index.parent(), index.row(), index.row());
|
||||
const auto parentItem = q->getItem(index.parent());
|
||||
parentItem->removeChild(index.row());
|
||||
roomIndexes.removeFirst();
|
||||
q->endRemoveRows();
|
||||
break;
|
||||
}
|
||||
case 5: { // Pop Back
|
||||
const auto index = roomIndexes.back();
|
||||
q->beginRemoveRows(index.parent(), index.row(), index.row());
|
||||
const auto parentItem = q->getItem(index.parent());
|
||||
parentItem->removeChild(index.row());
|
||||
roomIndexes.removeLast();
|
||||
q->endRemoveRows();
|
||||
break;
|
||||
}
|
||||
case 6: { // Insert
|
||||
const auto type = NeoChatRoomType::typeForRoom(diff->item());
|
||||
const auto parentItem = rootItem->child(type);
|
||||
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
|
||||
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{diff->item()}, parentItem))) {
|
||||
// connectRoomSignals(room);
|
||||
}
|
||||
q->endInsertRows();
|
||||
roomIndexes.insert(diff->index(), q->indexForRoom(diff->item()));
|
||||
break;
|
||||
}
|
||||
case 7: { // Set
|
||||
const auto index = roomIndexes.at(diff->index());
|
||||
q->beginRemoveRows(index.parent(), index.row(), index.row());
|
||||
q->getItem(index.parent())->removeChild(index.row());
|
||||
q->endRemoveRows();
|
||||
|
||||
const auto oldParent = index(oldType, 0, {});
|
||||
auto oldParentItem = getItem(oldParent);
|
||||
Q_ASSERT(oldParentItem);
|
||||
const auto type = NeoChatRoomType::typeForRoom(diff->item());
|
||||
const auto parentItem = rootItem->child(type);
|
||||
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
|
||||
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{diff->item()}, parentItem))) {
|
||||
// connectRoomSignals(room);
|
||||
}
|
||||
q->endInsertRows();
|
||||
roomIndexes[diff->index()] = q->indexForRoom(diff->item());
|
||||
break;
|
||||
}
|
||||
case 8: { // Remove
|
||||
const auto index = roomIndexes.at(diff->index());
|
||||
q->beginRemoveRows(index.parent(), index.row(), index.row());
|
||||
q->getItem(index.parent())->removeChild(index.row());
|
||||
q->endRemoveRows();
|
||||
roomIndexes.removeAt(diff->index());
|
||||
break;
|
||||
}
|
||||
case 9: { // Truncate
|
||||
for (int i = q->rowCount({}) - 1; i >= int(diff->index()); i--) {
|
||||
const auto index = roomIndexes.at(i);
|
||||
q->beginRemoveRows(index.parent(), index.row(), index.row());
|
||||
q->getItem(index.parent())->removeChild(index.row());
|
||||
q->endRemoveRows();
|
||||
roomIndexes.removeAt(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 10: { // Reset
|
||||
q->beginResetModel();
|
||||
resetTree();
|
||||
roomIndexes.clear();
|
||||
q->endResetModel();
|
||||
|
||||
const auto newParent = index(newType, 0, {});
|
||||
auto newParentItem = getItem(newParent);
|
||||
Q_ASSERT(newParentItem);
|
||||
|
||||
// HACK: We're doing this as a remove then insert because moving doesn't work
|
||||
// properly with DelegateChooser for whatever reason.
|
||||
Q_ASSERT(checkIndex(index(oldRow, 0, oldParent), QAbstractItemModel::CheckIndexOption::IndexIsValid));
|
||||
beginRemoveRows(oldParent, oldRow, oldRow);
|
||||
const bool success = oldParentItem->removeChild(oldRow);
|
||||
Q_ASSERT(success);
|
||||
endRemoveRows();
|
||||
beginInsertRows(newParent, newParentItem->childCount(), newParentItem->childCount());
|
||||
newParentItem->insertChild(std::make_unique<RoomTreeItem>(neochatRoom, newParentItem));
|
||||
endInsertRows();
|
||||
for (const auto &it : diff->items_vec()) {
|
||||
const auto type = NeoChatRoomType::typeForRoom(it.box_me());
|
||||
const auto parentItem = rootItem->child(type);
|
||||
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
|
||||
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{it.box_me()}, parentItem))) {
|
||||
// connectRoomSignals(room);
|
||||
}
|
||||
q->endInsertRows();
|
||||
roomIndexes.append(q->indexForRoom(it.box_me()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RoomTreeModel::connectRoomSignals(NeoChatRoom *room)
|
||||
{
|
||||
connect(room, &Room::displaynameChanged, this, [this, room] {
|
||||
refreshRoomRoles(room, {DisplayNameRole});
|
||||
});
|
||||
connect(room, &Room::unreadStatsChanged, this, [this, room] {
|
||||
refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole});
|
||||
});
|
||||
connect(room, &Room::avatarChanged, this, [this, room] {
|
||||
refreshRoomRoles(room, {AvatarRole});
|
||||
});
|
||||
connect(room, &Room::tagsChanged, this, [this, room] {
|
||||
moveRoom(room);
|
||||
});
|
||||
connect(room, &Room::joinStateChanged, this, [this, room] {
|
||||
refreshRoomRoles(room);
|
||||
});
|
||||
connect(room, &Room::addedMessages, this, [this, room] {
|
||||
refreshRoomRoles(room, {SubtitleTextRole});
|
||||
});
|
||||
connect(room, &Room::pendingEventMerged, this, [this, room] {
|
||||
refreshRoomRoles(room, {SubtitleTextRole});
|
||||
});
|
||||
connect(room, &NeoChatRoom::pushNotificationStateChanged, this, [this, room] {
|
||||
refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole});
|
||||
});
|
||||
}
|
||||
// void RoomTreeModel::connectRoomSignals(NeoChatRoom *room)
|
||||
// {
|
||||
// connect(room, &Room::displaynameChanged, this, [this, room] {
|
||||
// refreshRoomRoles(room, {DisplayNameRole});
|
||||
// });
|
||||
// connect(room, &Room::unreadStatsChanged, this, [this, room] {
|
||||
// refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole});
|
||||
// });
|
||||
// connect(room, &Room::avatarChanged, this, [this, room] {
|
||||
// refreshRoomRoles(room, {AvatarRole});
|
||||
// });
|
||||
// connect(room, &Room::tagsChanged, this, [this, room] {
|
||||
// moveRoom(room);
|
||||
// });
|
||||
// connect(room, &Room::joinStateChanged, this, [this, room] {
|
||||
// refreshRoomRoles(room);
|
||||
// });
|
||||
// connect(room, &Room::addedMessages, this, [this, room] {
|
||||
// refreshRoomRoles(room, {SubtitleTextRole});
|
||||
// });
|
||||
// connect(room, &Room::pendingEventMerged, this, [this, room] {
|
||||
// refreshRoomRoles(room, {SubtitleTextRole});
|
||||
// });
|
||||
// connect(room, &NeoChatRoom::pushNotificationStateChanged, this, [this, room] {
|
||||
// refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole});
|
||||
// });
|
||||
// }
|
||||
|
||||
void RoomTreeModel::refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles)
|
||||
{
|
||||
const auto index = indexForRoom(room);
|
||||
if (!index.isValid()) {
|
||||
qCritical() << "Room" << room->id() << "not found in the room list";
|
||||
return;
|
||||
}
|
||||
Q_EMIT dataChanged(index, index, roles);
|
||||
}
|
||||
// void RoomTreeModel::refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles)
|
||||
// {
|
||||
// const auto index = indexForRoom(room);
|
||||
// if (!index.isValid()) {
|
||||
// qCritical() << "Room" << room->id() << "not found in the room list";
|
||||
// return;
|
||||
// }
|
||||
// Q_EMIT dataChanged(index, index, roles);
|
||||
// }
|
||||
|
||||
NeoChatConnection *RoomTreeModel::connection() const
|
||||
Connection *RoomTreeModel::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
return d->connection;
|
||||
}
|
||||
|
||||
int RoomTreeModel::columnCount(const QModelIndex &parent) const
|
||||
@@ -215,7 +284,7 @@ int RoomTreeModel::rowCount(const QModelIndex &parent) const
|
||||
}
|
||||
|
||||
if (!parent.isValid()) {
|
||||
parentItem = m_rootItem.get();
|
||||
parentItem = d->rootItem.get();
|
||||
} else {
|
||||
parentItem = static_cast<RoomTreeItem *>(parent.internalPointer());
|
||||
}
|
||||
@@ -239,7 +308,7 @@ QModelIndex RoomTreeModel::parent(const QModelIndex &index) const
|
||||
}
|
||||
RoomTreeItem *parentItem = childItem->parentItem();
|
||||
|
||||
if (parentItem == m_rootItem.get()) {
|
||||
if (parentItem == d->rootItem.get()) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
@@ -295,7 +364,7 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
|
||||
RoomTreeItem *child = getItem(index);
|
||||
if (std::holds_alternative<NeoChatRoomType::Types>(child->data())) {
|
||||
if (std::holds_alternative<NeoChatRoomType::Type>(child->data())) {
|
||||
if (role == DisplayNameRole) {
|
||||
return NeoChatRoomType::typeName(index.row());
|
||||
}
|
||||
@@ -314,92 +383,91 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto room = std::get<NeoChatRoom *>(child->data());
|
||||
const auto room = std::get<RoomWrapper *>(child->data());
|
||||
Q_ASSERT(room);
|
||||
|
||||
if (role == DisplayNameRole) {
|
||||
return room->displayName();
|
||||
return stringFromRust((*room->item)->display_name()).toHtmlEscaped();
|
||||
}
|
||||
if (role == AvatarRole) {
|
||||
return room->avatarMediaUrl();
|
||||
return u"%1?user_id=%2"_s.arg(stringFromRust((*room->item)->avatar_url()), d->connection->matrixId());
|
||||
}
|
||||
if (role == CanonicalAliasRole) {
|
||||
return room->canonicalAlias();
|
||||
return stringFromRust((*room->item)->canonical_alias()).toHtmlEscaped();
|
||||
}
|
||||
if (role == TopicRole) {
|
||||
return room->topic();
|
||||
return stringFromRust((*room->item)->topic()).toHtmlEscaped();
|
||||
}
|
||||
if (role == CategoryRole) {
|
||||
return NeoChatRoomType::typeForRoom(room);
|
||||
return NeoChatRoomType::typeForRoom((*room->item)->box_me());
|
||||
}
|
||||
if (role == ContextNotificationCountRole) {
|
||||
return int(room->contextAwareNotificationCount());
|
||||
return int((*room->item)->num_unread_messages());
|
||||
}
|
||||
if (role == HasHighlightNotificationsRole) {
|
||||
return room->highlightCount() > 0 && room->contextAwareNotificationCount() > 0;
|
||||
return (*room->item)->num_unread_mentions() > 0 && (*room->item)->num_unread_messages() > 0;
|
||||
}
|
||||
if (role == JoinStateRole) {
|
||||
if (!room->successorId().isEmpty()) {
|
||||
if (!(*room->item)->tombstone()->replacement_room().empty()) {
|
||||
return u"upgraded"_s;
|
||||
}
|
||||
return QVariant::fromValue(room->joinState());
|
||||
return QVariant::fromValue((*room->item)->state());
|
||||
}
|
||||
if (role == CurrentRoomRole) {
|
||||
return QVariant::fromValue(room);
|
||||
return {};
|
||||
// return QVariant::fromValue(room);
|
||||
}
|
||||
if (role == SubtitleTextRole) {
|
||||
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
|
||||
return QString();
|
||||
}
|
||||
return EventHandler::subtitleText(room, room->lastEvent());
|
||||
return {};
|
||||
// if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
|
||||
// return QString();
|
||||
// }
|
||||
// return EventHandler::subtitleText(room, room->lastEvent());
|
||||
}
|
||||
if (role == AvatarImageRole) {
|
||||
return room->avatar(128);
|
||||
return {};
|
||||
// return room->avatar(128);
|
||||
}
|
||||
if (role == RoomIdRole) {
|
||||
return room->id();
|
||||
return stringFromRust((*room->item)->id()).toHtmlEscaped();
|
||||
}
|
||||
if (role == IsSpaceRole) {
|
||||
return room->isSpace();
|
||||
return (*room->item)->is_space();
|
||||
}
|
||||
if (role == IsChildSpaceRole) {
|
||||
return SpaceHierarchyCache::instance().isChild(room->id());
|
||||
return false;
|
||||
// return SpaceHierarchyCache::instance().isChild(room->id());
|
||||
}
|
||||
if (role == ReplacementIdRole) {
|
||||
return room->successorId();
|
||||
return stringFromRust((*room->item)->tombstone()->replacement_room()).toHtmlEscaped();
|
||||
}
|
||||
if (role == IsDirectChat) {
|
||||
return room->isDirectChat();
|
||||
return false;
|
||||
// return room->isDirectChat();
|
||||
}
|
||||
if (role == DelegateTypeRole) {
|
||||
return u"normal"_s;
|
||||
}
|
||||
if (role == RoomTypeRole) {
|
||||
if (room->creation()) {
|
||||
return room->creation()->contentPart<QString>("type"_L1);
|
||||
}
|
||||
return stringFromRust((*room->item)->room_type()).toHtmlEscaped();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QModelIndex RoomTreeModel::indexForRoom(NeoChatRoom *room) const
|
||||
QModelIndex RoomTreeModel::indexForRoom(rust::Box<sdk::RoomListRoom> room) const
|
||||
{
|
||||
if (room == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Try and find by checking type.
|
||||
const auto type = NeoChatRoomType::typeForRoom(room);
|
||||
const auto parentItem = m_rootItem->child(type);
|
||||
const auto row = parentItem->rowForRoom(room);
|
||||
const auto type = NeoChatRoomType::typeForRoom(room->box_me());
|
||||
const auto parentItem = d->rootItem->child(type);
|
||||
const auto row = parentItem->rowForRoom(room->box_me());
|
||||
if (row) {
|
||||
return index(*row, 0, index(type, 0));
|
||||
}
|
||||
// Double check that the room isn't in the wrong category.
|
||||
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
|
||||
const auto parentItem = m_rootItem->child(i);
|
||||
const auto row = parentItem->rowForRoom(room);
|
||||
const auto parentItem = d->rootItem->child(i);
|
||||
const auto row = parentItem->rowForRoom(room->box_me());
|
||||
if (row) {
|
||||
return index(*row, 0, index(i, 0));
|
||||
}
|
||||
@@ -408,4 +476,13 @@ QModelIndex RoomTreeModel::indexForRoom(NeoChatRoom *room) const
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<rust::Box<sdk::RoomListRoom>> RoomTreeModel::roomForIndex(QModelIndex index) const
|
||||
{
|
||||
RoomTreeItem *child = getItem(index);
|
||||
if (std::holds_alternative<NeoChatRoomType::Type>(child->data())) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return (*std::get<RoomWrapper *>(child->data())->item)->box_me();
|
||||
}
|
||||
|
||||
#include "moc_roomtreemodel.cpp"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
@@ -6,23 +7,20 @@
|
||||
#include <QAbstractItemModel>
|
||||
#include <QPointer>
|
||||
|
||||
#include "enums/neochatroomtype.h"
|
||||
#include "roomtreeitem.h"
|
||||
|
||||
namespace Quotient
|
||||
namespace Integral
|
||||
{
|
||||
class Connection;
|
||||
class Room;
|
||||
}
|
||||
|
||||
class NeoChatConnection;
|
||||
class NeoChatRoom;
|
||||
|
||||
class RoomTreeModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
Q_PROPERTY(Integral::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -51,9 +49,10 @@ public:
|
||||
};
|
||||
Q_ENUM(EventRoles)
|
||||
explicit RoomTreeModel(QObject *parent = nullptr);
|
||||
~RoomTreeModel();
|
||||
|
||||
void setConnection(NeoChatConnection *connection);
|
||||
NeoChatConnection *connection() const;
|
||||
void setConnection(Integral::Connection *connection);
|
||||
Integral::Connection *connection() const;
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
@@ -75,23 +74,21 @@ public:
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
Q_INVOKABLE QModelIndex indexForRoom(NeoChatRoom *room) const;
|
||||
QModelIndex indexForRoom(rust::Box<sdk::RoomListRoom> room) const;
|
||||
std::optional<rust::Box<sdk::RoomListRoom>> roomForIndex(QModelIndex index) const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
std::unique_ptr<RoomTreeItem> m_rootItem;
|
||||
class Private;
|
||||
std::unique_ptr<Private> d;
|
||||
|
||||
RoomTreeItem *getItem(const QModelIndex &index) const;
|
||||
|
||||
void resetModel();
|
||||
void connectRoomSignals(NeoChatRoom *room);
|
||||
|
||||
void newRoom(Quotient::Room *room);
|
||||
void leftRoom(Quotient::Room *room);
|
||||
void moveRoom(Quotient::Room *room);
|
||||
// void connectRoomSignals(NeoChatRoom *room);
|
||||
|
||||
void refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles = {});
|
||||
// void refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles = {});
|
||||
};
|
||||
|
||||
@@ -4,26 +4,26 @@
|
||||
|
||||
#include "sortfilterroomtreemodel.h"
|
||||
|
||||
#include "enums/roomsortparameter.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "roomsortparameter.h"
|
||||
// #include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatroomtype.h"
|
||||
#include "roommanager.h"
|
||||
#include <Integral/Room>
|
||||
// #include "roommanager.h"
|
||||
#include "roomtreemodel.h"
|
||||
#include "spacehierarchycache.h"
|
||||
// #include "spacehierarchycache.h"
|
||||
|
||||
SortFilterRoomTreeModel::SortFilterRoomTreeModel(RoomTreeModel *sourceModel, QObject *parent)
|
||||
SortFilterRoomTreeModel::SortFilterRoomTreeModel(QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
Q_ASSERT(sourceModel);
|
||||
setSourceModel(sourceModel);
|
||||
// Q_ASSERT(sourceModel);
|
||||
// setSourceModel(sourceModel);
|
||||
|
||||
setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::SortOrderChanged, this, [this]() {
|
||||
setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
|
||||
invalidateFilter();
|
||||
});
|
||||
// setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
|
||||
// connect(NeoChatConfig::self(), &NeoChatConfig::SortOrderChanged, this, [this]() {
|
||||
// setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
|
||||
// invalidateFilter();
|
||||
// });
|
||||
|
||||
setRecursiveFilteringEnabled(true);
|
||||
sort(0);
|
||||
@@ -34,13 +34,13 @@ SortFilterRoomTreeModel::SortFilterRoomTreeModel(RoomTreeModel *sourceModel, QOb
|
||||
connect(this->sourceModel(), &QAbstractItemModel::rowsRemoved, this, &SortFilterRoomTreeModel::invalidateFilter);
|
||||
});
|
||||
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::CollapsedChanged, this, &SortFilterRoomTreeModel::invalidateFilter);
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::AllRoomsInHomeChanged, this, [this]() {
|
||||
invalidateFilter();
|
||||
if (NeoChatConfig::self()->allRoomsInHome()) {
|
||||
RoomManager::instance().resetState();
|
||||
}
|
||||
});
|
||||
// connect(NeoChatConfig::self(), &NeoChatConfig::CollapsedChanged, this, &SortFilterRoomTreeModel::invalidateFilter);
|
||||
// connect(NeoChatConfig::self(), &NeoChatConfig::AllRoomsInHomeChanged, this, [this]() {
|
||||
// invalidateFilter();
|
||||
// if (NeoChatConfig::self()->allRoomsInHome()) {
|
||||
// RoomManager::instance().resetState();
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
void SortFilterRoomTreeModel::setRoomSortOrder(SortFilterRoomTreeModel::RoomSortOrder sortOrder)
|
||||
@@ -78,14 +78,14 @@ bool SortFilterRoomTreeModel::lessThan(const QModelIndex &source_left, const QMo
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto leftRoom = dynamic_cast<NeoChatRoom *>(treeModel->connection()->room(source_left.data(RoomTreeModel::RoomIdRole).toString()));
|
||||
const auto rightRoom = dynamic_cast<NeoChatRoom *>(treeModel->connection()->room(source_right.data(RoomTreeModel::RoomIdRole).toString()));
|
||||
if (leftRoom == nullptr || rightRoom == nullptr) {
|
||||
const auto leftRoom = treeModel->roomForIndex(source_left);
|
||||
const auto rightRoom = treeModel->roomForIndex(source_right);
|
||||
if (!leftRoom.has_value() || !rightRoom.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto sortRole : RoomSortParameter::currentParameterList()) {
|
||||
auto result = RoomSortParameter::compareParameter(sortRole, leftRoom, rightRoom);
|
||||
auto result = RoomSortParameter::compareParameter(sortRole, leftRoom.value()->box_me(), rightRoom.value()->box_me());
|
||||
|
||||
if (result != 0) {
|
||||
return result > 0;
|
||||
@@ -141,20 +141,22 @@ bool SortFilterRoomTreeModel::filterAcceptsRow(int source_row, const QModelIndex
|
||||
return false;
|
||||
}
|
||||
|
||||
static auto config = NeoChatConfig::self();
|
||||
if (config->allRoomsInHome() && RoomManager::instance().currentSpace().isEmpty()) {
|
||||
return acceptRoom;
|
||||
}
|
||||
return acceptRoom;
|
||||
|
||||
if (m_activeSpaceId.isEmpty()) {
|
||||
if (!SpaceHierarchyCache::instance().isChild(sourceModel()->data(index, RoomTreeModel::RoomIdRole).toString())) {
|
||||
return acceptRoom;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
const auto &rooms = SpaceHierarchyCache::instance().getRoomListForSpace(m_activeSpaceId, false);
|
||||
return std::find(rooms.begin(), rooms.end(), sourceModel()->data(index, RoomTreeModel::RoomIdRole).toString()) != rooms.end() && acceptRoom;
|
||||
}
|
||||
// static auto config = NeoChatConfig::self();
|
||||
// if (config->allRoomsInHome() && RoomManager::instance().currentSpace().isEmpty()) {
|
||||
// return acceptRoom;
|
||||
// }
|
||||
//
|
||||
// if (m_activeSpaceId.isEmpty()) {
|
||||
// if (!SpaceHierarchyCache::instance().isChild(sourceModel()->data(index, RoomTreeModel::RoomIdRole).toString())) {
|
||||
// return acceptRoom;
|
||||
// }
|
||||
// return false;
|
||||
// } else {
|
||||
// const auto &rooms = SpaceHierarchyCache::instance().getRoomListForSpace(m_activeSpaceId, false);
|
||||
// return std::find(rooms.begin(), rooms.end(), sourceModel()->data(index, RoomTreeModel::RoomIdRole).toString()) != rooms.end() && acceptRoom;
|
||||
// }
|
||||
}
|
||||
|
||||
QString SortFilterRoomTreeModel::activeSpaceId() const
|
||||
@@ -192,7 +194,7 @@ QModelIndex SortFilterRoomTreeModel::currentRoomIndex() const
|
||||
return {};
|
||||
}
|
||||
|
||||
return mapFromSource(roomModel->indexForRoom(RoomManager::instance().currentRoom()));
|
||||
return {}; // mapFromSource(roomModel->indexForRoom(RoomManager::instance().currentRoom()));
|
||||
}
|
||||
|
||||
#include "moc_sortfilterroomtreemodel.cpp"
|
||||
|
||||
@@ -32,7 +32,7 @@ class SortFilterRoomTreeModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
// QML_UNCREATABLE("")
|
||||
|
||||
/**
|
||||
* @brief The text to use to filter room names.
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
};
|
||||
Q_ENUM(Mode)
|
||||
|
||||
explicit SortFilterRoomTreeModel(RoomTreeModel *sourceModel, QObject *parent = nullptr);
|
||||
explicit SortFilterRoomTreeModel(QObject *parent = nullptr);
|
||||
|
||||
void setRoomSortOrder(RoomSortOrder sortOrder);
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "messagecontentmodel.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
class ReactionModel;
|
||||
|
||||
/**
|
||||
* @class ThreadFetchModel
|
||||
@@ -172,8 +171,6 @@ private:
|
||||
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;
|
||||
|
||||
@@ -13,13 +13,35 @@
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
ThreePIdModel::ThreePIdModel(NeoChatConnection *connection)
|
||||
: QAbstractListModel(connection)
|
||||
ThreePIdModel::ThreePIdModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
Q_ASSERT(connection);
|
||||
connect(connection, &NeoChatConnection::stateChanged, this, [this]() {
|
||||
}
|
||||
|
||||
NeoChatConnection *ThreePIdModel::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
void ThreePIdModel::setConnection(NeoChatConnection *connection)
|
||||
{
|
||||
if (m_connection == connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_connection != nullptr) {
|
||||
m_connection->disconnect(this);
|
||||
}
|
||||
|
||||
m_connection = connection;
|
||||
if (m_connection) {
|
||||
connect(m_connection, &NeoChatConnection::stateChanged, this, [this]() {
|
||||
refreshModel();
|
||||
});
|
||||
refreshModel();
|
||||
});
|
||||
}
|
||||
|
||||
Q_EMIT connectionChanged();
|
||||
}
|
||||
|
||||
QVariant ThreePIdModel::data(const QModelIndex &index, int role) const
|
||||
@@ -62,12 +84,14 @@ QHash<int, QByteArray> ThreePIdModel::roleNames() const
|
||||
|
||||
void ThreePIdModel::refreshModel()
|
||||
{
|
||||
const auto connection = dynamic_cast<NeoChatConnection *>(this->parent());
|
||||
if (connection != nullptr && connection->isLoggedIn()) {
|
||||
const auto threePIdJob = connection->callApi<Quotient::GetAccount3PIDsJob>();
|
||||
connect(threePIdJob, &Quotient::BaseJob::success, this, [this, threePIdJob]() {
|
||||
if (m_connection != nullptr && m_connection->isLoggedIn()) {
|
||||
if (m_job.isRunning()) {
|
||||
m_job.cancel();
|
||||
}
|
||||
m_job = m_connection->callApi<Quotient::GetAccount3PIDsJob>();
|
||||
connect(m_job, &Quotient::BaseJob::success, this, [this]() {
|
||||
beginResetModel();
|
||||
m_threePIds = threePIdJob->threepids();
|
||||
m_threePIds = m_job->threepids();
|
||||
endResetModel();
|
||||
|
||||
refreshBindStatus();
|
||||
@@ -77,25 +101,24 @@ void ThreePIdModel::refreshModel()
|
||||
|
||||
void ThreePIdModel::refreshBindStatus()
|
||||
{
|
||||
const auto connection = dynamic_cast<NeoChatConnection *>(this->parent());
|
||||
if (connection == nullptr || !connection->hasIdentityServer()) {
|
||||
if (m_connection == nullptr || !m_connection->hasIdentityServer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto openIdJob = connection->callApi<Quotient::RequestOpenIdTokenJob>(connection->userId());
|
||||
connect(openIdJob, &Quotient::BaseJob::success, this, [this, connection, openIdJob]() {
|
||||
const auto requestUrl = QUrl(connection->identityServer().toString() + u"/_matrix/identity/v2/account/register"_s);
|
||||
const auto openIdJob = m_connection->callApi<Quotient::RequestOpenIdTokenJob>(m_connection->userId());
|
||||
connect(openIdJob, &Quotient::BaseJob::success, this, [this, openIdJob]() {
|
||||
const auto requestUrl = QUrl(m_connection->identityServer().toString() + u"/_matrix/identity/v2/account/register"_s);
|
||||
if (!(requestUrl.scheme() == u"https"_s || requestUrl.scheme() == u"http"_s)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QNetworkRequest request(requestUrl);
|
||||
auto newRequest = Quotient::NetworkAccessManager::instance()->post(request, QJsonDocument(openIdJob->jsonData()).toJson());
|
||||
connect(newRequest, &QNetworkReply::finished, this, [this, connection, newRequest]() {
|
||||
connect(newRequest, &QNetworkReply::finished, this, [this, newRequest]() {
|
||||
QJsonObject replyJson = QJsonDocument::fromJson(newRequest->readAll()).object();
|
||||
const auto identityServerToken = replyJson["token"_L1].toString();
|
||||
|
||||
const auto requestUrl = QUrl(connection->identityServer().toString() + u"/_matrix/identity/v2/hash_details"_s);
|
||||
const auto requestUrl = QUrl(m_connection->identityServer().toString() + u"/_matrix/identity/v2/hash_details"_s);
|
||||
if (!(requestUrl.scheme() == u"https"_s || requestUrl.scheme() == u"http"_s)) {
|
||||
return;
|
||||
}
|
||||
@@ -104,11 +127,11 @@ void ThreePIdModel::refreshBindStatus()
|
||||
hashRequest.setRawHeader("Authorization", "Bearer " + identityServerToken.toLatin1());
|
||||
|
||||
auto hashReply = Quotient::NetworkAccessManager::instance()->get(hashRequest);
|
||||
connect(hashReply, &QNetworkReply::finished, this, [this, connection, identityServerToken, hashReply]() {
|
||||
connect(hashReply, &QNetworkReply::finished, this, [this, identityServerToken, hashReply]() {
|
||||
QJsonObject replyJson = QJsonDocument::fromJson(hashReply->readAll()).object();
|
||||
const auto lookupPepper = replyJson["lookup_pepper"_L1].toString();
|
||||
|
||||
const auto requestUrl = QUrl(connection->identityServer().toString() + u"/_matrix/identity/v2/lookup"_s);
|
||||
const auto requestUrl = QUrl(m_connection->identityServer().toString() + u"/_matrix/identity/v2/lookup"_s);
|
||||
if (!(requestUrl.scheme() == u"https"_s || requestUrl.scheme() == u"http"_s)) {
|
||||
return;
|
||||
}
|
||||
@@ -127,13 +150,13 @@ void ThreePIdModel::refreshBindStatus()
|
||||
requestData["addresses"_L1] = idLookups;
|
||||
|
||||
auto lookupReply = Quotient::NetworkAccessManager::instance()->post(lookupRequest, QJsonDocument(requestData).toJson(QJsonDocument::Compact));
|
||||
connect(lookupReply, &QNetworkReply::finished, this, [this, connection, lookupReply]() {
|
||||
connect(lookupReply, &QNetworkReply::finished, this, [this, lookupReply]() {
|
||||
beginResetModel();
|
||||
m_bindings.clear();
|
||||
|
||||
QJsonObject mappings = QJsonDocument::fromJson(lookupReply->readAll()).object()["mappings"_L1].toObject();
|
||||
for (const auto &id : mappings.keys()) {
|
||||
if (mappings[id] == connection->userId()) {
|
||||
if (mappings[id] == m_connection->userId()) {
|
||||
m_bindings += id.section(u' ', 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/csapi/administrative_contact.h>
|
||||
#include <Quotient/jobs/jobhandle.h>
|
||||
|
||||
class NeoChatConnection;
|
||||
|
||||
@@ -19,19 +20,27 @@ class ThreePIdModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
/**
|
||||
* @brief The current connection for the model to use.
|
||||
*/
|
||||
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*/
|
||||
enum EventRoles {
|
||||
enum Roles {
|
||||
AddressRole = Qt::DisplayRole, /**< The third-party identifier address. */
|
||||
MediumRole, /**< The medium of the third-party identifier. One of: [email, msisdn]. */
|
||||
IsBoundRole, /**< Whether the 3PID is bound to the current identity server. */
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
explicit ThreePIdModel(NeoChatConnection *parent);
|
||||
explicit ThreePIdModel(QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] NeoChatConnection *connection() const;
|
||||
void setConnection(NeoChatConnection *connection);
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
@@ -56,9 +65,15 @@ public:
|
||||
|
||||
Q_INVOKABLE void refreshModel();
|
||||
|
||||
Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
QVector<Quotient::GetAccount3PIDsJob::ThirdPartyIdentifier> m_threePIds;
|
||||
|
||||
Quotient::JobHandle<Quotient::GetAccount3PIDsJob> m_job;
|
||||
|
||||
QList<QString> m_bindings;
|
||||
|
||||
void refreshBindStatus();
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
#include <QGuiApplication>
|
||||
|
||||
#include <Quotient/avatar.h>
|
||||
#include <Quotient/events/roompowerlevelsevent.h>
|
||||
// #include <Quotient/avatar.h>
|
||||
// #include <Quotient/events/roompowerlevelsevent.h>
|
||||
|
||||
#include "enums/powerlevel.h"
|
||||
#include "neochatroom.h"
|
||||
@@ -30,7 +30,7 @@ void UserListModel::setRoom(NeoChatRoom *room)
|
||||
// last room's objects before the room is actually changed
|
||||
beginResetModel();
|
||||
m_currentRoom->disconnect(this);
|
||||
m_currentRoom->connection()->disconnect(this);
|
||||
// m_currentRoom->connection()->disconnect(this);
|
||||
m_currentRoom = nullptr;
|
||||
m_members.clear();
|
||||
endResetModel();
|
||||
@@ -39,21 +39,21 @@ void UserListModel::setRoom(NeoChatRoom *room)
|
||||
m_currentRoom = room;
|
||||
|
||||
if (m_currentRoom) {
|
||||
connect(m_currentRoom, &Room::memberJoined, this, &UserListModel::memberJoined);
|
||||
connect(m_currentRoom, &Room::memberLeft, this, &UserListModel::memberLeft);
|
||||
connect(m_currentRoom, &Room::memberNameUpdated, this, [this](RoomMember member) {
|
||||
refreshMember(member, {DisplayNameRole});
|
||||
});
|
||||
connect(m_currentRoom, &Room::memberAvatarUpdated, this, [this](RoomMember member) {
|
||||
refreshMember(member, {AvatarRole});
|
||||
});
|
||||
connect(m_currentRoom, &Room::memberListChanged, this, [this]() {
|
||||
// this is slow
|
||||
UserListModel::refreshAllMembers();
|
||||
});
|
||||
connect(m_currentRoom->connection(), &Connection::loggedOut, this, [this]() {
|
||||
setRoom(nullptr);
|
||||
});
|
||||
// connect(m_currentRoom, &Room::memberJoined, this, &UserListModel::memberJoined);
|
||||
// connect(m_currentRoom, &Room::memberLeft, this, &UserListModel::memberLeft);
|
||||
// connect(m_currentRoom, &Room::memberNameUpdated, this, [this](RoomMember member) {
|
||||
// refreshMember(member, {DisplayNameRole});
|
||||
// });
|
||||
// connect(m_currentRoom, &Room::memberAvatarUpdated, this, [this](RoomMember member) {
|
||||
// refreshMember(member, {AvatarRole});
|
||||
// });
|
||||
// connect(m_currentRoom, &Room::memberListChanged, this, [this]() {
|
||||
// // this is slow
|
||||
// UserListModel::refreshAllMembers();
|
||||
// });
|
||||
// connect(m_currentRoom->connection(), &Connection::loggedOut, this, [this]() {
|
||||
// setRoom(nullptr);
|
||||
// });
|
||||
}
|
||||
|
||||
m_active = false;
|
||||
@@ -80,40 +80,40 @@ QVariant UserListModel::data(const QModelIndex &index, int role) const
|
||||
return {};
|
||||
}
|
||||
auto memberId = m_members.at(index.row());
|
||||
if (role == DisplayNameRole) {
|
||||
return m_currentRoom->member(memberId).disambiguatedName();
|
||||
}
|
||||
if (role == UserIdRole) {
|
||||
return memberId;
|
||||
}
|
||||
if (role == AvatarRole) {
|
||||
return m_currentRoom->member(memberId).avatarUrl();
|
||||
}
|
||||
if (role == ObjectRole) {
|
||||
return QVariant::fromValue(memberId);
|
||||
}
|
||||
if (role == PowerLevelRole) {
|
||||
auto plEvent = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
|
||||
if (!plEvent) {
|
||||
return 0;
|
||||
}
|
||||
return plEvent->powerLevelForUser(memberId);
|
||||
}
|
||||
if (role == PowerLevelStringRole) {
|
||||
auto pl = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
|
||||
// User might not in the room yet, in this case pl can be nullptr.
|
||||
// e.g. When invited but user not accepted or denied the invitation.
|
||||
if (!pl) {
|
||||
return u"Not Available"_s;
|
||||
}
|
||||
|
||||
auto userPl = pl->powerLevelForUser(memberId);
|
||||
|
||||
return i18nc("%1 is the name of the power level, e.g. admin and %2 is the value that represents.",
|
||||
"%1 (%2)",
|
||||
PowerLevel::nameForLevel(PowerLevel::levelForValue(userPl)),
|
||||
userPl);
|
||||
}
|
||||
// if (role == DisplayNameRole) {
|
||||
// return m_currentRoom->member(memberId).disambiguatedName();
|
||||
// }
|
||||
// if (role == UserIdRole) {
|
||||
// return memberId;
|
||||
// }
|
||||
// if (role == AvatarRole) {
|
||||
// return m_currentRoom->member(memberId).avatarUrl();
|
||||
// }
|
||||
// if (role == ObjectRole) {
|
||||
// return QVariant::fromValue(memberId);
|
||||
// }
|
||||
// if (role == PowerLevelRole) {
|
||||
// auto plEvent = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
|
||||
// if (!plEvent) {
|
||||
// return 0;
|
||||
// }
|
||||
// return plEvent->powerLevelForUser(memberId);
|
||||
// }
|
||||
// if (role == PowerLevelStringRole) {
|
||||
// auto pl = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
|
||||
// // User might not in the room yet, in this case pl can be nullptr.
|
||||
// // e.g. When invited but user not accepted or denied the invitation.
|
||||
// if (!pl) {
|
||||
// return u"Not Available"_s;
|
||||
// }
|
||||
//
|
||||
// auto userPl = pl->powerLevelForUser(memberId);
|
||||
//
|
||||
// return i18nc("%1 is the name of the power level, e.g. admin and %2 is the value that represents.",
|
||||
// "%1 (%2)",
|
||||
// PowerLevel::nameForLevel(PowerLevel::levelForValue(userPl)),
|
||||
// userPl);
|
||||
// }
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -134,65 +134,65 @@ bool UserListModel::event(QEvent *event)
|
||||
return QObject::event(event);
|
||||
}
|
||||
|
||||
void UserListModel::memberJoined(const Quotient::RoomMember &member)
|
||||
{
|
||||
auto pos = findUserPos(member);
|
||||
beginInsertRows(QModelIndex(), pos, pos);
|
||||
m_members.insert(pos, member.id());
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void UserListModel::memberLeft(const Quotient::RoomMember &member)
|
||||
{
|
||||
auto pos = findUserPos(member);
|
||||
if (pos != m_members.size()) {
|
||||
beginRemoveRows(QModelIndex(), pos, pos);
|
||||
m_members.removeAt(pos);
|
||||
endRemoveRows();
|
||||
} else {
|
||||
qWarning() << "Trying to remove a room member not in the user list";
|
||||
}
|
||||
}
|
||||
|
||||
void UserListModel::refreshMember(const Quotient::RoomMember &member, const QList<int> &roles)
|
||||
{
|
||||
auto pos = findUserPos(member);
|
||||
if (pos != m_members.size()) {
|
||||
// The update will have changed the state event so we need to insert the updated member object.
|
||||
m_members.insert(pos, member.id());
|
||||
Q_EMIT dataChanged(index(pos), index(pos), roles);
|
||||
} else {
|
||||
qWarning() << "Trying to access a room member not in the user list";
|
||||
}
|
||||
}
|
||||
// void UserListModel::memberJoined(const Quotient::RoomMember &member)
|
||||
// {
|
||||
// auto pos = findUserPos(member);
|
||||
// beginInsertRows(QModelIndex(), pos, pos);
|
||||
// m_members.insert(pos, member.id());
|
||||
// endInsertRows();
|
||||
// }
|
||||
//
|
||||
// void UserListModel::memberLeft(const Quotient::RoomMember &member)
|
||||
// {
|
||||
// auto pos = findUserPos(member);
|
||||
// if (pos != m_members.size()) {
|
||||
// beginRemoveRows(QModelIndex(), pos, pos);
|
||||
// m_members.removeAt(pos);
|
||||
// endRemoveRows();
|
||||
// } else {
|
||||
// qWarning() << "Trying to remove a room member not in the user list";
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void UserListModel::refreshMember(const Quotient::RoomMember &member, const QList<int> &roles)
|
||||
// {
|
||||
// auto pos = findUserPos(member);
|
||||
// if (pos != m_members.size()) {
|
||||
// // The update will have changed the state event so we need to insert the updated member object.
|
||||
// m_members.insert(pos, member.id());
|
||||
// Q_EMIT dataChanged(index(pos), index(pos), roles);
|
||||
// } else {
|
||||
// qWarning() << "Trying to access a room member not in the user list";
|
||||
// }
|
||||
// }
|
||||
|
||||
void UserListModel::refreshAllMembers()
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
if (m_currentRoom != nullptr) {
|
||||
m_members = m_currentRoom->joinedMemberIds();
|
||||
MemberSorter sorter;
|
||||
std::sort(m_members.begin(), m_members.end(), [&sorter, this](const auto &left, const auto &right) {
|
||||
const auto leftPl = m_currentRoom->memberEffectivePowerLevel(left);
|
||||
const auto rightPl = m_currentRoom->memberEffectivePowerLevel(right);
|
||||
if (leftPl > rightPl) {
|
||||
return true;
|
||||
} else if (rightPl > leftPl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return sorter(m_currentRoom->member(left), m_currentRoom->member(right));
|
||||
});
|
||||
// m_members = m_currentRoom->joinedMemberIds();
|
||||
// MemberSorter sorter;
|
||||
// std::sort(m_members.begin(), m_members.end(), [&sorter, this](const auto &left, const auto &right) {
|
||||
// const auto leftPl = m_currentRoom->memberEffectivePowerLevel(left);
|
||||
// const auto rightPl = m_currentRoom->memberEffectivePowerLevel(right);
|
||||
// if (leftPl > rightPl) {
|
||||
// return true;
|
||||
// } else if (rightPl > leftPl) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// return sorter(m_currentRoom->member(left), m_currentRoom->member(right));
|
||||
// });
|
||||
}
|
||||
endResetModel();
|
||||
Q_EMIT usersRefreshed();
|
||||
}
|
||||
|
||||
int UserListModel::findUserPos(const RoomMember &member) const
|
||||
{
|
||||
return findUserPos(member.id());
|
||||
}
|
||||
// int UserListModel::findUserPos(const RoomMember &member) const
|
||||
// {
|
||||
// return findUserPos(member.id());
|
||||
// }
|
||||
|
||||
int UserListModel::findUserPos(const QString &userId) const
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/room.h>
|
||||
#include <Integral/Room>
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QObject>
|
||||
@@ -87,9 +87,9 @@ protected:
|
||||
bool event(QEvent *event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void memberJoined(const Quotient::RoomMember &member);
|
||||
void memberLeft(const Quotient::RoomMember &member);
|
||||
void refreshMember(const Quotient::RoomMember &member, const QList<int> &roles = {});
|
||||
// void memberJoined(const Quotient::RoomMember &member);
|
||||
// void memberLeft(const Quotient::RoomMember &member);
|
||||
// void refreshMember(const Quotient::RoomMember &member, const QList<int> &roles = {});
|
||||
void refreshAllMembers();
|
||||
|
||||
private:
|
||||
@@ -98,6 +98,6 @@ private:
|
||||
|
||||
bool m_active = false;
|
||||
|
||||
int findUserPos(const Quotient::RoomMember &member) const;
|
||||
// int findUserPos(const Quotient::RoomMember &member) const;
|
||||
[[nodiscard]] int findUserPos(const QString &username) const;
|
||||
};
|
||||
|
||||
@@ -303,6 +303,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]=نتيجة مشاركة محتوى
|
||||
@@ -336,5 +337,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>
|
||||
|
||||
@@ -6,145 +6,142 @@
|
||||
#include <QImageReader>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroom.h"
|
||||
#include "spacehierarchycache.h"
|
||||
// #include "neochatconfig.h"
|
||||
// #include "neochatroom.h"
|
||||
// #include "spacehierarchycache.h"
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
// #include <Quotient/jobs/basejob.h>
|
||||
// #include <Quotient/quotient_common.h>
|
||||
#include <qt6keychain/keychain.h>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <Quotient/csapi/content-repo.h>
|
||||
#include <Quotient/csapi/profile.h>
|
||||
#include <Quotient/csapi/registration.h>
|
||||
#include <Quotient/csapi/versions.h>
|
||||
#include <Quotient/jobs/downloadfilejob.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
#include <Quotient/room.h>
|
||||
#include <Quotient/settings.h>
|
||||
#include <Quotient/user.h>
|
||||
// #include <Quotient/csapi/content-repo.h>
|
||||
// #include <Quotient/csapi/profile.h>
|
||||
// #include <Quotient/csapi/registration.h>
|
||||
// #include <Quotient/csapi/versions.h>
|
||||
// #include <Quotient/jobs/downloadfilejob.h>
|
||||
// #include <Quotient/qt_connection_util.h>
|
||||
// #include <Quotient/room.h>
|
||||
// #include <Quotient/settings.h>
|
||||
// #include <Quotient/user.h>
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
#include <QCoroNetwork>
|
||||
#include <Quotient/csapi/pusher.h>
|
||||
#include <Quotient/networkaccessmanager.h>
|
||||
#endif
|
||||
// #ifdef HAVE_KUNIFIEDPUSH
|
||||
// #include <QCoroNetwork>
|
||||
// #include <Quotient/csapi/pusher.h>
|
||||
// #include <Quotient/networkaccessmanager.h>
|
||||
// #endif
|
||||
|
||||
using namespace Quotient;
|
||||
#include <Integral/Connection_p>
|
||||
|
||||
using namespace Integral;
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
NeoChatConnection::NeoChatConnection(QObject *parent)
|
||||
: Connection(parent)
|
||||
, m_threePIdModel(new ThreePIdModel(this))
|
||||
NeoChatConnection::NeoChatConnection(std::unique_ptr<Connection::Private> d)
|
||||
: Connection(std::move(d))
|
||||
{
|
||||
m_linkPreviewers.setMaxCost(20);
|
||||
connectSignals();
|
||||
}
|
||||
|
||||
NeoChatConnection::NeoChatConnection(const QUrl &server, QObject *parent)
|
||||
: Connection(server, parent)
|
||||
, m_threePIdModel(new ThreePIdModel(this))
|
||||
{
|
||||
m_linkPreviewers.setMaxCost(20);
|
||||
// m_linkPreviewers.setMaxCost(20);
|
||||
connectSignals();
|
||||
}
|
||||
|
||||
void NeoChatConnection::connectSignals()
|
||||
{
|
||||
connect(this, &NeoChatConnection::accountDataChanged, this, [this](const QString &type) {
|
||||
if (type == u"org.kde.neochat.account_label"_s) {
|
||||
Q_EMIT labelChanged();
|
||||
}
|
||||
if (type == u"m.identity_server"_s) {
|
||||
Q_EMIT identityServerChanged();
|
||||
}
|
||||
});
|
||||
connect(this, &NeoChatConnection::syncDone, this, [this] {
|
||||
setIsOnline(true);
|
||||
});
|
||||
connect(this, &NeoChatConnection::networkError, this, [this]() {
|
||||
setIsOnline(false);
|
||||
});
|
||||
connect(this, &NeoChatConnection::requestFailed, this, [this](BaseJob *job) {
|
||||
if (job->error() == BaseJob::UserConsentRequired) {
|
||||
Q_EMIT userConsentRequired(job->errorUrl());
|
||||
}
|
||||
});
|
||||
connect(this, &NeoChatConnection::requestFailed, this, [this](BaseJob *job) {
|
||||
if (dynamic_cast<DownloadFileJob *>(job) && job->jsonData()["errcode"_L1].toString() == "M_TOO_LARGE"_L1) {
|
||||
Q_EMIT showMessage(MessageType::Warning, i18n("File too large to download.<br />Contact your matrix server administrator for support."));
|
||||
}
|
||||
});
|
||||
connect(this, &NeoChatConnection::directChatsListChanged, this, [this](DirectChatsMap additions, DirectChatsMap removals) {
|
||||
Q_EMIT directChatInvitesChanged();
|
||||
for (const auto &chatId : additions) {
|
||||
if (const auto chat = room(chatId)) {
|
||||
connect(chat, &Room::unreadStatsChanged, this, [this]() {
|
||||
refreshBadgeNotificationCount();
|
||||
Q_EMIT directChatNotificationsChanged();
|
||||
Q_EMIT directChatsHaveHighlightNotificationsChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const auto &chatId : removals) {
|
||||
if (const auto chat = room(chatId)) {
|
||||
disconnect(chat, &Room::unreadStatsChanged, this, nullptr);
|
||||
}
|
||||
}
|
||||
});
|
||||
connect(this, &NeoChatConnection::joinedRoom, this, [this](Room *room) {
|
||||
if (room->isDirectChat()) {
|
||||
connect(room, &Room::unreadStatsChanged, this, [this]() {
|
||||
Q_EMIT directChatNotificationsChanged();
|
||||
Q_EMIT directChatsHaveHighlightNotificationsChanged();
|
||||
});
|
||||
}
|
||||
connect(room, &Room::unreadStatsChanged, this, [this]() {
|
||||
refreshBadgeNotificationCount();
|
||||
Q_EMIT homeNotificationsChanged();
|
||||
Q_EMIT homeHaveHighlightNotificationsChanged();
|
||||
});
|
||||
});
|
||||
connect(this, &NeoChatConnection::leftRoom, this, [this](Room *room, Room *prev) {
|
||||
Q_UNUSED(room)
|
||||
if (prev && prev->isDirectChat()) {
|
||||
Q_EMIT directChatInvitesChanged();
|
||||
Q_EMIT directChatNotificationsChanged();
|
||||
Q_EMIT directChatsHaveHighlightNotificationsChanged();
|
||||
}
|
||||
refreshBadgeNotificationCount();
|
||||
Q_EMIT homeNotificationsChanged();
|
||||
Q_EMIT homeHaveHighlightNotificationsChanged();
|
||||
});
|
||||
|
||||
connect(&SpaceHierarchyCache::instance(), &SpaceHierarchyCache::spaceHierarchyChanged, this, [this]() {
|
||||
refreshBadgeNotificationCount();
|
||||
Q_EMIT homeNotificationsChanged();
|
||||
Q_EMIT homeHaveHighlightNotificationsChanged();
|
||||
});
|
||||
|
||||
// Fetch unstable features
|
||||
// TODO: Expose unstableFeatures() in libQuotient
|
||||
connect(
|
||||
this,
|
||||
&Connection::connected,
|
||||
this,
|
||||
[this] {
|
||||
auto job = callApi<GetVersionsJob>(BackgroundRequest);
|
||||
connect(job, &GetVersionsJob::success, this, [this, job] {
|
||||
m_canCheckMutualRooms = job->unstableFeatures().contains("uk.half-shot.msc2666.query_mutual_rooms"_L1);
|
||||
Q_EMIT canCheckMutualRoomsChanged();
|
||||
m_canEraseData = job->unstableFeatures().contains("org.matrix.msc4025"_L1) || job->versions().count("v1.10"_L1);
|
||||
Q_EMIT canEraseDataChanged();
|
||||
});
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
setDirectChatEncryptionDefault(NeoChatConfig::preferUsingEncryption());
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::PreferUsingEncryptionChanged, this, [] {
|
||||
setDirectChatEncryptionDefault(NeoChatConfig::preferUsingEncryption());
|
||||
});
|
||||
// connect(this, &NeoChatConnection::accountDataChanged, this, [this](const QString &type) {
|
||||
// if (type == u"org.kde.neochat.account_label"_s) {
|
||||
// Q_EMIT labelChanged();
|
||||
// }
|
||||
// if (type == u"m.identity_server"_s) {
|
||||
// Q_EMIT identityServerChanged();
|
||||
// }
|
||||
// });
|
||||
// connect(this, &NeoChatConnection::syncDone, this, [this] {
|
||||
// setIsOnline(true);
|
||||
// });
|
||||
// connect(this, &NeoChatConnection::networkError, this, [this]() {
|
||||
// setIsOnline(false);
|
||||
// });
|
||||
// connect(this, &NeoChatConnection::requestFailed, this, [this](BaseJob *job) {
|
||||
// if (job->error() == BaseJob::UserConsentRequired) {
|
||||
// Q_EMIT userConsentRequired(job->errorUrl());
|
||||
// }
|
||||
// });
|
||||
// connect(this, &NeoChatConnection::requestFailed, this, [this](BaseJob *job) {
|
||||
// if (dynamic_cast<DownloadFileJob *>(job) && job->jsonData()["errcode"_L1].toString() == "M_TOO_LARGE"_L1) {
|
||||
// Q_EMIT showMessage(MessageType::Warning, i18n("File too large to download.<br />Contact your matrix server administrator for support."));
|
||||
// }
|
||||
// });
|
||||
// connect(this, &NeoChatConnection::directChatsListChanged, this, [this](DirectChatsMap additions, DirectChatsMap removals) {
|
||||
// Q_EMIT directChatInvitesChanged();
|
||||
// for (const auto &chatId : additions) {
|
||||
// if (const auto chat = room(chatId)) {
|
||||
// connect(chat, &Room::unreadStatsChanged, this, [this]() {
|
||||
// refreshBadgeNotificationCount();
|
||||
// Q_EMIT directChatNotificationsChanged();
|
||||
// Q_EMIT directChatsHaveHighlightNotificationsChanged();
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// for (const auto &chatId : removals) {
|
||||
// if (const auto chat = room(chatId)) {
|
||||
// disconnect(chat, &Room::unreadStatsChanged, this, nullptr);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// connect(this, &NeoChatConnection::joinedRoom, this, [this](Room *room) {
|
||||
// if (room->isDirectChat()) {
|
||||
// connect(room, &Room::unreadStatsChanged, this, [this]() {
|
||||
// Q_EMIT directChatNotificationsChanged();
|
||||
// Q_EMIT directChatsHaveHighlightNotificationsChanged();
|
||||
// });
|
||||
// }
|
||||
// connect(room, &Room::unreadStatsChanged, this, [this]() {
|
||||
// refreshBadgeNotificationCount();
|
||||
// Q_EMIT homeNotificationsChanged();
|
||||
// Q_EMIT homeHaveHighlightNotificationsChanged();
|
||||
// });
|
||||
// });
|
||||
// connect(this, &NeoChatConnection::leftRoom, this, [this](Room *room, Room *prev) {
|
||||
// Q_UNUSED(room)
|
||||
// if (prev && prev->isDirectChat()) {
|
||||
// Q_EMIT directChatInvitesChanged();
|
||||
// Q_EMIT directChatNotificationsChanged();
|
||||
// Q_EMIT directChatsHaveHighlightNotificationsChanged();
|
||||
// }
|
||||
// refreshBadgeNotificationCount();
|
||||
// Q_EMIT homeNotificationsChanged();
|
||||
// Q_EMIT homeHaveHighlightNotificationsChanged();
|
||||
// });
|
||||
//
|
||||
// connect(&SpaceHierarchyCache::instance(), &SpaceHierarchyCache::spaceHierarchyChanged, this, [this]() {
|
||||
// refreshBadgeNotificationCount();
|
||||
// Q_EMIT homeNotificationsChanged();
|
||||
// Q_EMIT homeHaveHighlightNotificationsChanged();
|
||||
// });
|
||||
//
|
||||
// // Fetch unstable features
|
||||
// // TODO: Expose unstableFeatures() in libQuotient
|
||||
// connect(
|
||||
// this,
|
||||
// &Connection::connected,
|
||||
// this,
|
||||
// [this] {
|
||||
// auto job = callApi<GetVersionsJob>(BackgroundRequest);
|
||||
// connect(job, &GetVersionsJob::success, this, [this, job] {
|
||||
// m_canCheckMutualRooms = job->unstableFeatures().contains("uk.half-shot.msc2666.query_mutual_rooms"_L1);
|
||||
// Q_EMIT canCheckMutualRoomsChanged();
|
||||
// m_canEraseData = job->unstableFeatures().contains("org.matrix.msc4025"_L1) || job->versions().count("v1.10"_L1);
|
||||
// Q_EMIT canEraseDataChanged();
|
||||
// });
|
||||
// },
|
||||
// Qt::SingleShotConnection);
|
||||
// setDirectChatEncryptionDefault(NeoChatConfig::preferUsingEncryption());
|
||||
// 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
|
||||
@@ -155,11 +152,11 @@ int NeoChatConnection::badgeNotificationCount() const
|
||||
void NeoChatConnection::refreshBadgeNotificationCount()
|
||||
{
|
||||
int count = 0;
|
||||
for (const auto &r : allRooms()) {
|
||||
if (const auto room = static_cast<NeoChatRoom *>(r)) {
|
||||
count += room->contextAwareNotificationCount();
|
||||
}
|
||||
}
|
||||
// for (const auto &r : allRooms()) {
|
||||
// if (const auto room = static_cast<NeoChatRoom *>(r)) {
|
||||
// count += room->contextAwareNotificationCount();
|
||||
// }
|
||||
// }
|
||||
|
||||
if (count != m_badgeNotificationCount) {
|
||||
m_badgeNotificationCount = count;
|
||||
@@ -167,50 +164,71 @@ void NeoChatConnection::refreshBadgeNotificationCount()
|
||||
}
|
||||
}
|
||||
|
||||
void NeoChatConnection::logout(bool serverSideLogout)
|
||||
bool NeoChatConnection::globalUrlPreviewEnabled()
|
||||
{
|
||||
SettingsGroup(u"Accounts"_s).remove(userId());
|
||||
return m_globalUrlPreviewEnabled;
|
||||
}
|
||||
|
||||
QKeychain::DeletePasswordJob job(qAppName());
|
||||
job.setAutoDelete(true);
|
||||
job.setKey(userId());
|
||||
QEventLoop loop;
|
||||
QKeychain::DeletePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
|
||||
job.start();
|
||||
loop.exec();
|
||||
|
||||
if (!serverSideLogout) {
|
||||
void NeoChatConnection::setGlobalUrlPreviewEnabled(bool newState)
|
||||
{
|
||||
if (m_globalUrlPreviewEnabled == newState) {
|
||||
return;
|
||||
}
|
||||
Connection::logout();
|
||||
|
||||
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());
|
||||
//
|
||||
// QKeychain::DeletePasswordJob job(qAppName());
|
||||
// job.setAutoDelete(true);
|
||||
// job.setKey(userId());
|
||||
// QEventLoop loop;
|
||||
// QKeychain::DeletePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
|
||||
// job.start();
|
||||
// loop.exec();
|
||||
//
|
||||
// if (!serverSideLogout) {
|
||||
// return;
|
||||
// }
|
||||
// Connection::logout();
|
||||
}
|
||||
|
||||
bool NeoChatConnection::setAvatar(const QUrl &avatarSource)
|
||||
{
|
||||
QString decoded = avatarSource.path();
|
||||
if (decoded.isEmpty()) {
|
||||
callApi<SetAvatarUrlJob>(user()->id(), avatarSource);
|
||||
return true;
|
||||
}
|
||||
if (QImageReader(decoded).read().isNull()) {
|
||||
return false;
|
||||
} else {
|
||||
return user()->setAvatar(decoded);
|
||||
}
|
||||
// QString decoded = avatarSource.path();
|
||||
// if (decoded.isEmpty()) {
|
||||
// callApi<SetAvatarUrlJob>(user()->id(), avatarSource);
|
||||
// return true;
|
||||
// }
|
||||
// if (QImageReader(decoded).read().isNull()) {
|
||||
// return false;
|
||||
// } else {
|
||||
// return user()->setAvatar(decoded);
|
||||
// }
|
||||
return {};
|
||||
}
|
||||
|
||||
QVariantList NeoChatConnection::getSupportedRoomVersions() const
|
||||
{
|
||||
const auto &roomVersions = availableRoomVersions();
|
||||
QVariantList supportedRoomVersions;
|
||||
for (const auto &v : roomVersions) {
|
||||
QVariantMap roomVersionMap;
|
||||
roomVersionMap.insert("id"_L1, v.id);
|
||||
roomVersionMap.insert("status"_L1, v.status);
|
||||
roomVersionMap.insert("isStable"_L1, v.isStable());
|
||||
supportedRoomVersions.append(roomVersionMap);
|
||||
}
|
||||
return supportedRoomVersions;
|
||||
// const auto &roomVersions = availableRoomVersions();
|
||||
// QVariantList supportedRoomVersions;
|
||||
// for (const auto &v : roomVersions) {
|
||||
// QVariantMap roomVersionMap;
|
||||
// roomVersionMap.insert("id"_L1, v.id);
|
||||
// roomVersionMap.insert("status"_L1, v.status);
|
||||
// roomVersionMap.insert("isStable"_L1, v.isStable());
|
||||
// supportedRoomVersions.append(roomVersionMap);
|
||||
// }
|
||||
// return supportedRoomVersions;
|
||||
return {};
|
||||
}
|
||||
|
||||
bool NeoChatConnection::canCheckMutualRooms() const
|
||||
@@ -220,90 +238,86 @@ bool NeoChatConnection::canCheckMutualRooms() const
|
||||
|
||||
void NeoChatConnection::changePassword(const QString ¤tPassword, const QString &newPassword)
|
||||
{
|
||||
auto job = callApi<ChangePasswordJob>(newPassword, false);
|
||||
connect(job, &BaseJob::result, this, [this, job, currentPassword, newPassword] {
|
||||
if (job->error() == 103) {
|
||||
QJsonObject replyData = job->jsonData();
|
||||
AuthenticationData authData;
|
||||
authData.session = replyData["session"_L1].toString();
|
||||
authData.type = "m.login.password"_L1;
|
||||
authData.authInfo["password"_L1] = currentPassword;
|
||||
authData.authInfo["user"_L1] = user()->id();
|
||||
authData.authInfo["identifier"_L1] = QJsonObject{{"type"_L1, "m.id.user"_L1}, {"user"_L1, user()->id()}};
|
||||
auto innerJob = callApi<ChangePasswordJob>(newPassword, false, authData);
|
||||
connect(innerJob, &BaseJob::success, this, [this]() {
|
||||
Q_EMIT passwordStatus(PasswordStatus::Success);
|
||||
});
|
||||
connect(innerJob, &BaseJob::failure, this, [innerJob, this]() {
|
||||
Q_EMIT passwordStatus(innerJob->jsonData()["errcode"_L1] == "M_FORBIDDEN"_L1 ? PasswordStatus::Wrong : PasswordStatus::Other);
|
||||
});
|
||||
}
|
||||
});
|
||||
// auto job = callApi<ChangePasswordJob>(newPassword, false);
|
||||
// connect(job, &BaseJob::result, this, [this, job, currentPassword, newPassword] {
|
||||
// if (job->error() == 103) {
|
||||
// QJsonObject replyData = job->jsonData();
|
||||
// AuthenticationData authData;
|
||||
// authData.session = replyData["session"_L1].toString();
|
||||
// authData.type = "m.login.password"_L1;
|
||||
// authData.authInfo["password"_L1] = currentPassword;
|
||||
// authData.authInfo["user"_L1] = user()->id();
|
||||
// authData.authInfo["identifier"_L1] = QJsonObject{{"type"_L1, "m.id.user"_L1}, {"user"_L1, user()->id()}};
|
||||
// auto innerJob = callApi<ChangePasswordJob>(newPassword, false, authData);
|
||||
// connect(innerJob, &BaseJob::success, this, [this]() {
|
||||
// Q_EMIT passwordStatus(PasswordStatus::Success);
|
||||
// });
|
||||
// connect(innerJob, &BaseJob::failure, this, [innerJob, this]() {
|
||||
// Q_EMIT passwordStatus(innerJob->jsonData()["errcode"_L1] == "M_FORBIDDEN"_L1 ? PasswordStatus::Wrong : PasswordStatus::Other);
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
void NeoChatConnection::setLabel(const QString &label)
|
||||
{
|
||||
QJsonObject json{
|
||||
{"account_label"_L1, label},
|
||||
};
|
||||
setAccountData("org.kde.neochat.account_label"_L1, json);
|
||||
Q_EMIT labelChanged();
|
||||
// QJsonObject json{
|
||||
// {"account_label"_L1, label},
|
||||
// };
|
||||
// setAccountData("org.kde.neochat.account_label"_L1, json);
|
||||
// Q_EMIT labelChanged();
|
||||
}
|
||||
|
||||
QString NeoChatConnection::label() const
|
||||
{
|
||||
return accountDataJson("org.kde.neochat.account_label"_L1)["account_label"_L1].toString();
|
||||
// return accountDataJson("org.kde.neochat.account_label"_L1)["account_label"_L1].toString();
|
||||
return {};
|
||||
}
|
||||
|
||||
void NeoChatConnection::deactivateAccount(const QString &password, const bool erase)
|
||||
{
|
||||
auto job = callApi<DeactivateAccountJob>();
|
||||
connect(job, &BaseJob::result, this, [this, job, password, erase] {
|
||||
if (job->error() == 103) {
|
||||
QJsonObject replyData = job->jsonData();
|
||||
AuthenticationData authData;
|
||||
authData.session = replyData["session"_L1].toString();
|
||||
authData.authInfo["password"_L1] = password;
|
||||
authData.type = "m.login.password"_L1;
|
||||
authData.authInfo["user"_L1] = user()->id();
|
||||
QJsonObject identifier = {{"type"_L1, "m.id.user"_L1}, {"user"_L1, user()->id()}};
|
||||
authData.authInfo["identifier"_L1] = identifier;
|
||||
auto innerJob = callApi<DeactivateAccountJob>(authData, QString{}, erase);
|
||||
connect(innerJob, &BaseJob::success, this, [this]() {
|
||||
logout(false);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ThreePIdModel *NeoChatConnection::threePIdModel() const
|
||||
{
|
||||
return m_threePIdModel;
|
||||
// auto job = callApi<DeactivateAccountJob>();
|
||||
// connect(job, &BaseJob::result, this, [this, job, password, erase] {
|
||||
// if (job->error() == 103) {
|
||||
// QJsonObject replyData = job->jsonData();
|
||||
// AuthenticationData authData;
|
||||
// authData.session = replyData["session"_L1].toString();
|
||||
// authData.authInfo["password"_L1] = password;
|
||||
// authData.type = "m.login.password"_L1;
|
||||
// authData.authInfo["user"_L1] = user()->id();
|
||||
// QJsonObject identifier = {{"type"_L1, "m.id.user"_L1}, {"user"_L1, user()->id()}};
|
||||
// authData.authInfo["identifier"_L1] = identifier;
|
||||
// auto innerJob = callApi<DeactivateAccountJob>(authData, QString{}, erase);
|
||||
// connect(innerJob, &BaseJob::success, this, [this]() {
|
||||
// logout(false);
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
bool NeoChatConnection::hasIdentityServer() const
|
||||
{
|
||||
if (!hasAccountData(u"m.identity_server"_s)) {
|
||||
return false;
|
||||
}
|
||||
// if (!hasAccountData(u"m.identity_server"_s)) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
const auto url = accountData(u"m.identity_server"_s)->contentPart<QUrl>("base_url"_L1);
|
||||
if (!url.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
// const auto url = accountData(u"m.identity_server"_s)->contentPart<QUrl>("base_url"_L1);
|
||||
// if (!url.isEmpty()) {
|
||||
// return true;
|
||||
// }
|
||||
return false;
|
||||
}
|
||||
|
||||
QUrl NeoChatConnection::identityServer() const
|
||||
{
|
||||
if (!hasAccountData(u"m.identity_server"_s)) {
|
||||
return {};
|
||||
}
|
||||
// if (!hasAccountData(u"m.identity_server"_s)) {
|
||||
// return {};
|
||||
// }
|
||||
|
||||
const auto url = accountData(u"m.identity_server"_s)->contentPart<QUrl>("base_url"_L1);
|
||||
if (!url.isEmpty()) {
|
||||
return url;
|
||||
}
|
||||
// const auto url = accountData(u"m.identity_server"_s)->contentPart<QUrl>("base_url"_L1);
|
||||
// if (!url.isEmpty()) {
|
||||
// return url;
|
||||
// }
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -318,92 +332,93 @@ QString NeoChatConnection::identityServerUIString() const
|
||||
|
||||
void NeoChatConnection::createRoom(const QString &name, const QString &topic, const QString &parent, bool setChildParent)
|
||||
{
|
||||
QList<CreateRoomJob::StateEvent> initialStateEvents;
|
||||
if (!parent.isEmpty()) {
|
||||
initialStateEvents.append(CreateRoomJob::StateEvent{
|
||||
"m.space.parent"_L1,
|
||||
QJsonObject{
|
||||
{"canonical"_L1, true},
|
||||
{"via"_L1, QJsonArray{domain()}},
|
||||
},
|
||||
parent,
|
||||
});
|
||||
}
|
||||
|
||||
const auto job = Connection::createRoom(Connection::PublishRoom, QString(), name, topic, QStringList(), {}, {}, {}, initialStateEvents);
|
||||
if (!parent.isEmpty()) {
|
||||
connect(job, &Quotient::CreateRoomJob::success, this, [this, parent, setChildParent, job]() {
|
||||
if (setChildParent) {
|
||||
if (auto parentRoom = room(parent)) {
|
||||
parentRoom->setState(u"m.space.child"_s, job->roomId(), QJsonObject{{"via"_L1, QJsonArray{domain()}}});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
connect(job, &CreateRoomJob::failure, this, [this, job] {
|
||||
Q_EMIT errorOccured(i18n("Room creation failed: %1", job->errorString()));
|
||||
});
|
||||
// QList<CreateRoomJob::StateEvent> initialStateEvents;
|
||||
// if (!parent.isEmpty()) {
|
||||
// initialStateEvents.append(CreateRoomJob::StateEvent{
|
||||
// "m.space.parent"_L1,
|
||||
// QJsonObject{
|
||||
// {"canonical"_L1, true},
|
||||
// {"via"_L1, QJsonArray{domain()}},
|
||||
// },
|
||||
// parent,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// const auto job = Connection::createRoom(Connection::PublishRoom, QString(), name, topic, QStringList(), {}, {}, {}, initialStateEvents);
|
||||
// if (!parent.isEmpty()) {
|
||||
// connect(job, &Quotient::CreateRoomJob::success, this, [this, parent, setChildParent, job]() {
|
||||
// if (setChildParent) {
|
||||
// if (auto parentRoom = room(parent)) {
|
||||
// parentRoom->setState(u"m.space.child"_s, job->roomId(), QJsonObject{{"via"_L1, QJsonArray{domain()}}});
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// connect(job, &CreateRoomJob::failure, this, [this, job] {
|
||||
// Q_EMIT errorOccured(i18n("Room creation failed: %1", job->errorString()));
|
||||
// });
|
||||
}
|
||||
|
||||
void NeoChatConnection::createSpace(const QString &name, const QString &topic, const QString &parent, bool setChildParent)
|
||||
{
|
||||
QList<CreateRoomJob::StateEvent> initialStateEvents;
|
||||
if (!parent.isEmpty()) {
|
||||
initialStateEvents.append(CreateRoomJob::StateEvent{
|
||||
"m.space.parent"_L1,
|
||||
QJsonObject{
|
||||
{"canonical"_L1, true},
|
||||
{"via"_L1, QJsonArray{domain()}},
|
||||
},
|
||||
parent,
|
||||
});
|
||||
}
|
||||
|
||||
const auto job =
|
||||
Connection::createRoom(Connection::UnpublishRoom, {}, name, topic, {}, {}, {}, false, initialStateEvents, {}, QJsonObject{{"type"_L1, "m.space"_L1}});
|
||||
if (!parent.isEmpty()) {
|
||||
connect(job, &Quotient::CreateRoomJob::success, this, [this, parent, setChildParent, job]() {
|
||||
if (setChildParent) {
|
||||
if (auto parentRoom = room(parent)) {
|
||||
parentRoom->setState(u"m.space.child"_s, job->roomId(), QJsonObject{{"via"_L1, QJsonArray{domain()}}});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
connect(job, &CreateRoomJob::failure, this, [this, job] {
|
||||
Q_EMIT errorOccured(i18n("Space creation failed: %1", job->errorString()));
|
||||
});
|
||||
// QList<CreateRoomJob::StateEvent> initialStateEvents;
|
||||
// if (!parent.isEmpty()) {
|
||||
// initialStateEvents.append(CreateRoomJob::StateEvent{
|
||||
// "m.space.parent"_L1,
|
||||
// QJsonObject{
|
||||
// {"canonical"_L1, true},
|
||||
// {"via"_L1, QJsonArray{domain()}},
|
||||
// },
|
||||
// parent,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// const auto job =
|
||||
// Connection::createRoom(Connection::UnpublishRoom, {}, name, topic, {}, {}, {}, false, initialStateEvents, {}, QJsonObject{{"type"_L1,
|
||||
// "m.space"_L1}});
|
||||
// if (!parent.isEmpty()) {
|
||||
// connect(job, &Quotient::CreateRoomJob::success, this, [this, parent, setChildParent, job]() {
|
||||
// if (setChildParent) {
|
||||
// if (auto parentRoom = room(parent)) {
|
||||
// parentRoom->setState(u"m.space.child"_s, job->roomId(), QJsonObject{{"via"_L1, QJsonArray{domain()}}});
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// connect(job, &CreateRoomJob::failure, this, [this, job] {
|
||||
// Q_EMIT errorOccured(i18n("Space creation failed: %1", job->errorString()));
|
||||
// });
|
||||
}
|
||||
|
||||
bool NeoChatConnection::directChatExists(Quotient::User *user)
|
||||
{
|
||||
return directChats().contains(user);
|
||||
}
|
||||
// bool NeoChatConnection::directChatExists(Quotient::User *user)
|
||||
// {
|
||||
// return directChats().contains(user);
|
||||
// }
|
||||
|
||||
qsizetype NeoChatConnection::directChatNotifications() const
|
||||
{
|
||||
qsizetype notifications = 0;
|
||||
QStringList added; // The same ID can be in the list multiple times.
|
||||
for (const auto &chatId : directChats()) {
|
||||
if (!added.contains(chatId)) {
|
||||
if (const auto chat = room(chatId)) {
|
||||
notifications += dynamic_cast<NeoChatRoom *>(chat)->contextAwareNotificationCount();
|
||||
added += chatId;
|
||||
}
|
||||
}
|
||||
}
|
||||
// for (const auto &chatId : directChats()) {
|
||||
// if (!added.contains(chatId)) {
|
||||
// if (const auto chat = room(chatId)) {
|
||||
// notifications += dynamic_cast<NeoChatRoom *>(chat)->contextAwareNotificationCount();
|
||||
// added += chatId;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return notifications;
|
||||
}
|
||||
|
||||
bool NeoChatConnection::directChatsHaveHighlightNotifications() const
|
||||
{
|
||||
for (const auto &childId : directChats()) {
|
||||
if (const auto child = static_cast<NeoChatRoom *>(room(childId))) {
|
||||
if (child->highlightCount() > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// for (const auto &childId : directChats()) {
|
||||
// if (const auto child = static_cast<NeoChatRoom *>(room(childId))) {
|
||||
// if (child->highlightCount() > 0) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -411,75 +426,78 @@ qsizetype NeoChatConnection::homeNotifications() const
|
||||
{
|
||||
qsizetype notifications = 0;
|
||||
QStringList added;
|
||||
const auto &spaceHierarchyCache = SpaceHierarchyCache::instance();
|
||||
for (const auto &r : allRooms()) {
|
||||
if (const auto room = static_cast<NeoChatRoom *>(r)) {
|
||||
if (!added.contains(room->id()) && !room->isDirectChat() && !spaceHierarchyCache.isChild(room->id())) {
|
||||
notifications += dynamic_cast<NeoChatRoom *>(room)->contextAwareNotificationCount();
|
||||
added += room->id();
|
||||
}
|
||||
}
|
||||
}
|
||||
// const auto &spaceHierarchyCache = SpaceHierarchyCache::instance();
|
||||
// for (const auto &r : allRooms()) {
|
||||
// if (const auto room = static_cast<NeoChatRoom *>(r)) {
|
||||
// if (!added.contains(room->id()) && !room->isDirectChat() && !spaceHierarchyCache.isChild(room->id())) {
|
||||
// notifications += dynamic_cast<NeoChatRoom *>(room)->contextAwareNotificationCount();
|
||||
// added += room->id();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return notifications;
|
||||
}
|
||||
|
||||
bool NeoChatConnection::homeHaveHighlightNotifications() const
|
||||
{
|
||||
const auto &spaceHierarchyCache = SpaceHierarchyCache::instance();
|
||||
for (const auto &r : allRooms()) {
|
||||
if (const auto room = static_cast<NeoChatRoom *>(r)) {
|
||||
if (!room->isDirectChat() && !spaceHierarchyCache.isChild(room->id()) && room->highlightCount() > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// const auto &spaceHierarchyCache = SpaceHierarchyCache::instance();
|
||||
// for (const auto &r : allRooms()) {
|
||||
// if (const auto room = static_cast<NeoChatRoom *>(r)) {
|
||||
// if (!room->isDirectChat() && !spaceHierarchyCache.isChild(room->id()) && room->highlightCount() > 0) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NeoChatConnection::directChatInvites() const
|
||||
{
|
||||
auto inviteRooms = rooms(JoinState::Invite);
|
||||
for (const auto inviteRoom : inviteRooms) {
|
||||
if (inviteRoom->isDirectChat()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// auto inviteRooms = rooms(JoinState::Invite);
|
||||
// for (const auto inviteRoom : inviteRooms) {
|
||||
// if (inviteRoom->isDirectChat()) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
return false;
|
||||
}
|
||||
|
||||
QCoro::Task<void> NeoChatConnection::setupPushNotifications(QString endpoint)
|
||||
{
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
QUrl gatewayEndpoint(endpoint);
|
||||
gatewayEndpoint.setPath(u"/_matrix/push/v1/notify"_s);
|
||||
|
||||
QNetworkRequest checkGateway(gatewayEndpoint);
|
||||
auto reply = co_await NetworkAccessManager::instance()->get(checkGateway);
|
||||
|
||||
// We want to check if this UnifiedPush server has a Matrix gateway
|
||||
// This is because Matrix does not natively support UnifiedPush
|
||||
const auto &replyJson = QJsonDocument::fromJson(reply->readAll()).object();
|
||||
|
||||
if (replyJson["unifiedpush"_L1]["gateway"_L1].toString() == u"matrix"_s) {
|
||||
callApi<PostPusherJob>(endpoint,
|
||||
u"http"_s,
|
||||
u"org.kde.neochat"_s,
|
||||
u"NeoChat"_s,
|
||||
deviceId(),
|
||||
QString(), // profileTag is intentionally left empty for now, it's optional
|
||||
u"en-US"_s,
|
||||
PostPusherJob::PusherData{QUrl::fromUserInput(gatewayEndpoint.toString()), u" "_s},
|
||||
false);
|
||||
|
||||
qInfo() << "Registered for push notifications";
|
||||
} else {
|
||||
qWarning() << "There's no gateway, not setting up push notifications.";
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(endpoint)
|
||||
co_return;
|
||||
#endif
|
||||
}
|
||||
// QCoro::Task<void> NeoChatConnection::setupPushNotifications(QString endpoint)
|
||||
// {
|
||||
// #ifdef HAVE_KUNIFIEDPUSH
|
||||
// QUrl gatewayEndpoint(endpoint);
|
||||
// gatewayEndpoint.setPath(u"/_matrix/push/v1/notify"_s);
|
||||
//
|
||||
// QNetworkRequest checkGateway(gatewayEndpoint);
|
||||
// auto reply = co_await NetworkAccessManager::instance()->get(checkGateway);
|
||||
//
|
||||
// // We want to check if this UnifiedPush server has a Matrix gateway
|
||||
// // This is because Matrix does not natively support UnifiedPush
|
||||
// const auto &replyJson = QJsonDocument::fromJson(reply->readAll()).object();
|
||||
//
|
||||
// if (replyJson["unifiedpush"_L1]["gateway"_L1].toString() == u"matrix"_s) {
|
||||
// callApi<PostPusherJob>(endpoint,
|
||||
// u"http"_s,
|
||||
// u"org.kde.neochat"_s,
|
||||
// u"NeoChat"_s,
|
||||
// deviceId(),
|
||||
// QString(), // profileTag is intentionally left empty for now, it's optional
|
||||
// u"en-US"_s,
|
||||
// PostPusherJob::PusherData{QUrl::fromUserInput(gatewayEndpoint.toString()), u" "_s},
|
||||
// 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;
|
||||
// #endif
|
||||
// }
|
||||
|
||||
bool NeoChatConnection::isOnline() const
|
||||
{
|
||||
@@ -497,43 +515,57 @@ void NeoChatConnection::setIsOnline(bool isOnline)
|
||||
|
||||
QString NeoChatConnection::accountDataJsonString(const QString &type) const
|
||||
{
|
||||
return QString::fromUtf8(QJsonDocument(accountDataJson(type)).toJson());
|
||||
return {}; // QString::fromUtf8(QJsonDocument(accountDataJson(type)).toJson());
|
||||
}
|
||||
|
||||
LinkPreviewer *NeoChatConnection::previewerForLink(const QUrl &link)
|
||||
{
|
||||
if (!NeoChatConfig::showLinkPreview()) {
|
||||
return nullptr;
|
||||
}
|
||||
// LinkPreviewer *NeoChatConnection::previewerForLink(const QUrl &link)
|
||||
// {
|
||||
// if (!m_globalUrlPreviewEnabled) {
|
||||
// return nullptr;
|
||||
// }
|
||||
//
|
||||
// auto previewer = m_linkPreviewers.object(link);
|
||||
// if (previewer != nullptr) {
|
||||
// return previewer;
|
||||
// }
|
||||
//
|
||||
// previewer = new LinkPreviewer(link, this);
|
||||
// m_linkPreviewers.insert(link, previewer);
|
||||
// return previewer;
|
||||
// }
|
||||
|
||||
auto previewer = m_linkPreviewers.object(link);
|
||||
if (previewer != nullptr) {
|
||||
return previewer;
|
||||
}
|
||||
|
||||
previewer = new LinkPreviewer(link, this);
|
||||
m_linkPreviewers.insert(link, previewer);
|
||||
return previewer;
|
||||
}
|
||||
|
||||
KeyImport::Error NeoChatConnection::exportMegolmSessions(const QString &passphrase, const QString &path)
|
||||
{
|
||||
KeyImport keyImport;
|
||||
auto result = keyImport.exportKeys(passphrase, this);
|
||||
if (!result.has_value()) {
|
||||
return result.error();
|
||||
}
|
||||
QUrl url(path);
|
||||
QFile file(url.toLocalFile());
|
||||
file.open(QFile::WriteOnly);
|
||||
file.write(result.value());
|
||||
file.close();
|
||||
return KeyImport::Success;
|
||||
}
|
||||
// KeyImport::Error NeoChatConnection::exportMegolmSessions(const QString &passphrase, const QString &path)
|
||||
// {
|
||||
// KeyImport keyImport;
|
||||
// auto result = keyImport.exportKeys(passphrase, this);
|
||||
// if (!result.has_value()) {
|
||||
// return result.error();
|
||||
// }
|
||||
// QUrl url(path);
|
||||
// QFile file(url.toLocalFile());
|
||||
// file.open(QFile::WriteOnly);
|
||||
// file.write(result.value());
|
||||
// file.close();
|
||||
// return KeyImport::Success;
|
||||
// }
|
||||
|
||||
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"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user