Compare commits
97 Commits
work/carl/
...
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 | ||
|
|
66343ba11e | ||
|
|
684cd85a7a | ||
|
|
ef9a80e76f | ||
|
|
fbb5f02379 | ||
|
|
5f4bde96e9 | ||
|
|
f8c8a68840 | ||
|
|
bf6f4a951e | ||
|
|
58c9366548 | ||
|
|
f410ecac2b | ||
|
|
1d1a43ade2 | ||
|
|
37adb56233 | ||
|
|
aca0669bf6 | ||
|
|
b33ab76ff8 | ||
|
|
38a391b7fa | ||
|
|
82434fe87c | ||
|
|
8bf7c36249 | ||
|
|
cff3557a24 | ||
|
|
2c476c4351 | ||
|
|
82c8ab511d | ||
|
|
486ed6edd2 | ||
|
|
a4b0a9ed36 | ||
|
|
bba9c37ba5 | ||
|
|
febc7d1630 | ||
|
|
1ed071949b | ||
|
|
3878c264ef | ||
|
|
21daf0b664 |
@@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -5,13 +5,17 @@ include:
|
||||
- project: sysadmin/ci-utilities
|
||||
file:
|
||||
- /gitlab-templates/reuse-lint.yml
|
||||
- /gitlab-templates/android-qt6.yml
|
||||
- /gitlab-templates/linux-qt6.yml
|
||||
- /gitlab-templates/windows-qt6.yml
|
||||
# - /gitlab-templates/freebsd-qt6.yml
|
||||
- /gitlab-templates/json-validation.yml
|
||||
- /gitlab-templates/xml-lint.yml
|
||||
- /gitlab-templates/yaml-lint.yml
|
||||
# - /gitlab-templates/android-qt6.yml
|
||||
# - /gitlab-templates/linux-qt6.yml
|
||||
# - /gitlab-templates/linux-qt6-next.yml
|
||||
# - /gitlab-templates/windows-qt6.yml
|
||||
# - /gitlab-templates/freebsd-qt6.yml
|
||||
- /gitlab-templates/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
|
||||
|
||||
71
.kde-ci.yml
71
.kde-ci.yml
@@ -2,42 +2,43 @@
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
Dependencies:
|
||||
- 'on': ['Linux', 'Android', 'FreeBSD', 'Windows']
|
||||
'require':
|
||||
'frameworks/extra-cmake-modules': '@latest-kf6'
|
||||
'frameworks/kcoreaddons': '@latest-kf6'
|
||||
'frameworks/kirigami': '@latest-kf6'
|
||||
'frameworks/ki18n': '@latest-kf6'
|
||||
'frameworks/kconfig': '@latest-kf6'
|
||||
'frameworks/syntax-highlighting': '@latest-kf6'
|
||||
'frameworks/kitemmodels': '@latest-kf6'
|
||||
'frameworks/kquickcharts': '@latest-kf6'
|
||||
'frameworks/knotifications': '@latest-kf6'
|
||||
'frameworks/kcolorscheme': '@latest-kf6'
|
||||
'libraries/kquickimageeditor': '@latest-kf6'
|
||||
'frameworks/sonnet': '@latest-kf6'
|
||||
'frameworks/prison': '@latest-kf6'
|
||||
'libraries/kirigami-addons': '@latest-kf6'
|
||||
'third-party/libquotient': '@latest'
|
||||
'third-party/qtkeychain': '@latest'
|
||||
'third-party/cmark': '@latest'
|
||||
'third-party/qcoro': '@latest'
|
||||
- 'on': ['Windows', 'Linux', 'FreeBSD']
|
||||
'require':
|
||||
'frameworks/qqc2-desktop-style': '@latest-kf6'
|
||||
'frameworks/kio': '@latest-kf6'
|
||||
'frameworks/kwindowsystem': '@latest-kf6'
|
||||
'frameworks/kstatusnotifieritem': '@latest-kf6'
|
||||
'frameworks/kcrash': '@latest-kf6'
|
||||
- 'on': ['Linux', 'FreeBSD']
|
||||
'require':
|
||||
'frameworks/kdbusaddons': '@latest-kf6'
|
||||
'frameworks/purpose': '@latest-kf6'
|
||||
- 'on': ['Linux', 'Android', 'FreeBSD', 'Windows']
|
||||
'require':
|
||||
'frameworks/extra-cmake-modules': '@latest-kf6'
|
||||
'frameworks/kcoreaddons': '@latest-kf6'
|
||||
'frameworks/kirigami': '@latest-kf6'
|
||||
'frameworks/ki18n': '@latest-kf6'
|
||||
'frameworks/kconfig': '@latest-kf6'
|
||||
'frameworks/syntax-highlighting': '@latest-kf6'
|
||||
'frameworks/kitemmodels': '@latest-kf6'
|
||||
'frameworks/kquickcharts': '@latest-kf6'
|
||||
'frameworks/knotifications': '@latest-kf6'
|
||||
'frameworks/kcolorscheme': '@latest-kf6'
|
||||
'frameworks/kiconthemes': '@latest-kf6'
|
||||
'libraries/kquickimageeditor': '@latest-kf6'
|
||||
'frameworks/sonnet': '@latest-kf6'
|
||||
'frameworks/prison': '@latest-kf6'
|
||||
'libraries/kirigami-addons': '@latest-kf6'
|
||||
'third-party/libquotient': '@latest'
|
||||
'third-party/qtkeychain': '@latest'
|
||||
'third-party/cmark': '@latest'
|
||||
'third-party/qcoro': '@latest'
|
||||
- 'on': ['Windows', 'Linux', 'FreeBSD']
|
||||
'require':
|
||||
'frameworks/qqc2-desktop-style': '@latest-kf6'
|
||||
'frameworks/kio': '@latest-kf6'
|
||||
'frameworks/kwindowsystem': '@latest-kf6'
|
||||
'frameworks/kstatusnotifieritem': '@latest-kf6'
|
||||
'frameworks/kcrash': '@latest-kf6'
|
||||
- 'on': ['Linux', 'FreeBSD']
|
||||
'require':
|
||||
'frameworks/kdbusaddons': '@latest-kf6'
|
||||
'frameworks/purpose': '@latest-kf6'
|
||||
|
||||
- 'on': ['Linux']
|
||||
'require':
|
||||
'sdk/selenium-webdriver-at-spi': '@latest-kf6'
|
||||
- 'on': ['Linux']
|
||||
'require':
|
||||
'sdk/selenium-webdriver-at-spi': '@latest-kf6'
|
||||
|
||||
Options:
|
||||
per-test-timeout: 90
|
||||
require-passing-tests-on: [ 'Linux', 'Android', 'FreeBSD', 'Windows' ]
|
||||
require-passing-tests-on: ['Linux', 'Android', 'FreeBSD', 'Windows']
|
||||
|
||||
@@ -8,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
|
||||
@@ -66,7 +64,7 @@ if (QT_KNOWN_POLICY_QTP0004)
|
||||
qt_policy(SET QTP0004 NEW)
|
||||
endif ()
|
||||
|
||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels ColorScheme)
|
||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels IconThemes ColorScheme)
|
||||
set_package_properties(KF6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Basic application components"
|
||||
@@ -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,8 @@
|
||||
<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"/>
|
||||
<release version="24.08.3" date="2024-11-07"/>
|
||||
@@ -649,6 +651,9 @@
|
||||
<url>https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/</url>
|
||||
</release>
|
||||
</releases>
|
||||
<requires>
|
||||
<display_length compare="ge">360</display_length>
|
||||
</requires>
|
||||
<branding>
|
||||
<color type="primary" scheme_preference="light">#a6e4f3</color>
|
||||
<color type="primary" scheme_preference="dark">#235670</color>
|
||||
|
||||
@@ -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
|
||||
@@ -119,6 +120,7 @@ Comment[ta]=மேட்ரிக்ஸில் உரையாட உதவு
|
||||
Comment[tr]=Matrix üzerinde sohbet edin
|
||||
Comment[uk]=Спілкування у Matrix
|
||||
Comment[x-test]=xxChat on Matrixxx
|
||||
Comment[zh_CN]=在 Matrix 上聊天
|
||||
Comment[zh_TW]=在 Matrix 上聊天
|
||||
MimeType=x-scheme-handler/matrix;
|
||||
Exec=neochat %u
|
||||
|
||||
622
po/ar/neochat.po
622
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
603
po/az/neochat.po
603
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
616
po/ca/neochat.po
616
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1016
po/cs/neochat.po
1016
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
583
po/da/neochat.po
583
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
617
po/de/neochat.po
617
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
644
po/el/neochat.po
644
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
663
po/eo/neochat.po
663
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
1400
po/es/neochat.po
1400
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
620
po/eu/neochat.po
620
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
789
po/fi/neochat.po
789
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
659
po/fr/neochat.po
659
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
654
po/gl/neochat.po
654
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
618
po/hi/neochat.po
618
po/hi/neochat.po
File diff suppressed because it is too large
Load Diff
618
po/hu/neochat.po
618
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
618
po/ia/neochat.po
618
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
613
po/id/neochat.po
613
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
596
po/ie/neochat.po
596
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
667
po/it/neochat.po
667
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
561
po/ja/neochat.po
561
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
602
po/ka/neochat.po
602
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
615
po/ko/neochat.po
615
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
561
po/lt/neochat.po
561
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
975
po/lv/neochat.po
975
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
604
po/nl/neochat.po
604
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
629
po/nn/neochat.po
629
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
603
po/pa/neochat.po
603
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
683
po/pl/neochat.po
683
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
613
po/pt/neochat.po
613
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
618
po/ru/neochat.po
618
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
618
po/sa/neochat.po
618
po/sa/neochat.po
File diff suppressed because it is too large
Load Diff
607
po/sk/neochat.po
607
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
605
po/sl/neochat.po
605
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
601
po/sv/neochat.po
601
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
726
po/ta/neochat.po
726
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
606
po/tr/neochat.po
606
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
607
po/uk/neochat.po
607
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
|
||||
@@ -28,8 +28,8 @@ apps:
|
||||
compression: lzo
|
||||
|
||||
package-repositories:
|
||||
- type: apt
|
||||
ppa: ubuntu-toolchain-r/test
|
||||
- type: apt
|
||||
ppa: ubuntu-toolchain-r/test
|
||||
|
||||
slots:
|
||||
session-dbus-interface:
|
||||
@@ -66,7 +66,7 @@ parts:
|
||||
- -Dcrypto=disabled
|
||||
- -Dgtk_doc=false
|
||||
build-packages:
|
||||
- meson
|
||||
- meson
|
||||
- libglib2.0-dev
|
||||
- libgcrypt20-dev
|
||||
prime:
|
||||
@@ -81,7 +81,8 @@ parts:
|
||||
plugin: cmake
|
||||
build-environment:
|
||||
- PATH: /snap/bin:${PATH}
|
||||
- PKG_CONFIG_PATH: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET/pkgconfig:$PKG_CONFIG_PATH
|
||||
- PKG_CONFIG_PATH: "$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET\
|
||||
/pkgconfig:$PKG_CONFIG_PATH"
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
@@ -105,8 +106,6 @@ parts:
|
||||
build-snaps:
|
||||
- cmake
|
||||
build-packages:
|
||||
- gcc-13
|
||||
- g++-13
|
||||
- libssl-dev
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
@@ -114,9 +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
|
||||
@@ -129,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
|
||||
@@ -150,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
|
||||
@@ -173,3 +173,12 @@ parts:
|
||||
prime:
|
||||
- usr/lib/*/libcmark.so*
|
||||
|
||||
gpu-2404:
|
||||
after: [neochat]
|
||||
source: https://github.com/canonical/gpu-snap.git
|
||||
plugin: dump
|
||||
override-prime: |
|
||||
craftctl default
|
||||
${CRAFT_PART_SRC}/bin/gpu-2404-cleanup mesa-2404
|
||||
prime:
|
||||
- bin/gpu-2404-wrapper
|
||||
|
||||
@@ -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)
|
||||
@@ -423,9 +424,10 @@ target_link_libraries(neochat PUBLIC
|
||||
KF6::ConfigGui
|
||||
KF6::CoreAddons
|
||||
KF6::SonnetCore
|
||||
KF6::IconThemes
|
||||
KF6::ColorScheme
|
||||
KF6::ItemModels
|
||||
QuotientQt6
|
||||
Integral
|
||||
cmark::cmark
|
||||
QCoro::Core
|
||||
QCoro::Network
|
||||
@@ -542,6 +544,8 @@ if(ANDROID)
|
||||
"edit-delete"
|
||||
"user-home-symbolic"
|
||||
"pin-symbolic"
|
||||
"kt-restore-defaults-symbolic"
|
||||
"user-symbolic"
|
||||
)
|
||||
ecm_add_android_apk(neochat-app ANDROID_DIR ${CMAKE_SOURCE_DIR}/android)
|
||||
else()
|
||||
|
||||
@@ -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,14 +50,17 @@ public:
|
||||
LiveLocation, /**< The initial event of a shared live location (i.e., the place where this is supposed to be shown in the timeline). */
|
||||
Encrypted, /**< An encrypted message that cannot be decrypted. */
|
||||
Reply, /**< A component to show a replied-to message. */
|
||||
Reaction, /**< A component to show the reactions to this message. */
|
||||
LinkPreview, /**< A preview of a URL in the message. */
|
||||
LinkPreviewLoad, /**< A loading dialog for a link preview. */
|
||||
ChatBar, /**< A text edit for editing a message. */
|
||||
ThreadRoot, /**< The root message of the thread. */
|
||||
ThreadBody, /**< The other messages in the thread. */
|
||||
ReplyButton, /**< A button to reply in the current thread. */
|
||||
FetchButton, /**< A button to fetch more messages in the current thread. */
|
||||
Verification, /**< A user verification session start message. */
|
||||
Loading, /**< The component is loading. */
|
||||
Separator, /**< A horizontal separator. */
|
||||
Other, /**< Anything that cannot be classified as another type. */
|
||||
};
|
||||
Q_ENUM(Type);
|
||||
|
||||
@@ -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 = "";
|
||||
// }
|
||||
}
|
||||
|
||||
131
src/main.cpp
131
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>
|
||||
@@ -37,36 +42,41 @@
|
||||
#include <KCrash>
|
||||
#endif
|
||||
|
||||
#include <KIconTheme>
|
||||
#include <KLocalizedContext>
|
||||
#include <KLocalizedString>
|
||||
|
||||
#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();
|
||||
|
||||
@@ -101,6 +111,7 @@ Q_DECL_EXPORT
|
||||
#endif
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
KIconTheme::initTheme();
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||
|
||||
#ifdef HAVE_WEBVIEW
|
||||
@@ -156,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));
|
||||
@@ -172,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
|
||||
@@ -183,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"));
|
||||
@@ -206,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)) {
|
||||
@@ -218,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
|
||||
@@ -237,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);
|
||||
@@ -291,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,12 +490,18 @@ 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, {}};
|
||||
}
|
||||
|
||||
@@ -512,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;
|
||||
}
|
||||
@@ -523,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});
|
||||
@@ -730,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();
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ class MessageModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
/**
|
||||
* @brief The current room that the model is getting its messages from.
|
||||
@@ -76,8 +77,6 @@ public:
|
||||
|
||||
ReadMarkersRole, /**< The first 5 other users at the event for read marker tracking. */
|
||||
ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */
|
||||
ReactionRole, /**< List model for this event. */
|
||||
ShowReactionsRole, /**< Whether there are any reactions to be shown. */
|
||||
|
||||
VerifiedRole, /**< Whether an encrypted message is sent in a verified session. */
|
||||
AuthorDisplayNameRole, /**< The displayname for the event's sender; for name change events, the old displayname. */
|
||||
@@ -154,7 +153,6 @@ private:
|
||||
bool movingEvent = false;
|
||||
|
||||
QMap<QString, QSharedPointer<ReadMarkerModel>> m_readMarkerModels;
|
||||
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
||||
|
||||
void createEventObjects(const Quotient::RoomEvent *event, bool isPending = false);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
ThreadModel::ThreadModel(const QString &threadRootId, NeoChatRoom *room)
|
||||
: QConcatenateTablesProxyModel(room)
|
||||
, m_threadRootId(threadRootId)
|
||||
, m_threadFetchModel(new ThreadFetchModel(this))
|
||||
, m_threadChatBarModel(new ThreadChatBarModel(this, room))
|
||||
{
|
||||
Q_ASSERT(!m_threadRootId.isEmpty());
|
||||
@@ -48,7 +49,7 @@ ThreadModel::ThreadModel(const QString &threadRootId, NeoChatRoom *room)
|
||||
// If the thread was created by the local user fetchMore() won't find the current
|
||||
// pending event.
|
||||
checkPending();
|
||||
fetchMore({});
|
||||
fetchMoreEvents(3);
|
||||
addModels();
|
||||
}
|
||||
|
||||
@@ -77,19 +78,19 @@ QHash<int, QByteArray> ThreadModel::roleNames() const
|
||||
return MessageContentModel::roleNamesStatic();
|
||||
}
|
||||
|
||||
bool ThreadModel::canFetchMore(const QModelIndex &parent) const
|
||||
bool ThreadModel::moreEventsAvailable(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return !m_currentJob && m_nextBatch.has_value();
|
||||
}
|
||||
|
||||
void ThreadModel::fetchMore(const QModelIndex &parent)
|
||||
void ThreadModel::fetchMoreEvents(int max)
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
if (!m_currentJob && m_nextBatch.has_value()) {
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(QObject::parent());
|
||||
const auto connection = room->connection();
|
||||
m_currentJob = connection->callApi<Quotient::GetRelatingEventsWithRelTypeJob>(room->id(), m_threadRootId, u"m.thread"_s, *m_nextBatch, QString(), 5);
|
||||
m_currentJob = connection->callApi<Quotient::GetRelatingEventsWithRelTypeJob>(room->id(), m_threadRootId, u"m.thread"_s, *m_nextBatch, QString(), max);
|
||||
Q_EMIT moreEventsAvailableChanged();
|
||||
connect(m_currentJob, &Quotient::BaseJob::success, this, [this]() {
|
||||
auto newEvents = m_currentJob->chunk();
|
||||
for (auto &event : newEvents) {
|
||||
@@ -109,6 +110,7 @@ void ThreadModel::fetchMore(const QModelIndex &parent)
|
||||
}
|
||||
|
||||
m_currentJob.clear();
|
||||
Q_EMIT moreEventsAvailableChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -132,6 +134,7 @@ void ThreadModel::addModels()
|
||||
if (room == nullptr) {
|
||||
return;
|
||||
}
|
||||
addSourceModel(m_threadFetchModel);
|
||||
for (auto it = m_events.crbegin(); it != m_events.crend(); ++it) {
|
||||
const auto contentModel = room->contentModelForEvent(*it);
|
||||
if (contentModel != nullptr) {
|
||||
@@ -150,6 +153,7 @@ void ThreadModel::clearModels()
|
||||
if (room == nullptr) {
|
||||
return;
|
||||
}
|
||||
removeSourceModel(m_threadFetchModel);
|
||||
for (const auto &model : m_events) {
|
||||
const auto contentModel = room->contentModelForEvent(model);
|
||||
if (sourceModels().contains(contentModel)) {
|
||||
@@ -188,6 +192,47 @@ void ThreadModel::closeLinkPreview(int row)
|
||||
}
|
||||
}
|
||||
|
||||
ThreadFetchModel::ThreadFetchModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
const auto threadModel = dynamic_cast<ThreadModel *>(parent);
|
||||
Q_ASSERT(threadModel != nullptr);
|
||||
connect(threadModel, &ThreadModel::moreEventsAvailableChanged, this, [this]() {
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
});
|
||||
}
|
||||
|
||||
QVariant ThreadFetchModel::data(const QModelIndex &idx, int role) const
|
||||
{
|
||||
if (idx.row() < 0 || idx.row() > 1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (role == ComponentTypeRole) {
|
||||
return MessageComponentType::FetchButton;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
int ThreadFetchModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
const auto threadModel = dynamic_cast<ThreadModel *>(this->parent());
|
||||
if (threadModel == nullptr) {
|
||||
qWarning() << "ThreadFetchModel created with incorrect parent, a ThreadModel must be set as the parent on creation.";
|
||||
return {};
|
||||
}
|
||||
return threadModel->moreEventsAvailable({}) ? 1 : 0;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ThreadFetchModel::roleNames() const
|
||||
{
|
||||
return {
|
||||
{ComponentTypeRole, "componentType"},
|
||||
};
|
||||
}
|
||||
|
||||
ThreadChatBarModel::ThreadChatBarModel(QObject *parent, NeoChatRoom *room)
|
||||
: QAbstractListModel(parent)
|
||||
, m_room(room)
|
||||
|
||||
@@ -19,7 +19,52 @@
|
||||
#include "messagecontentmodel.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
class ReactionModel;
|
||||
|
||||
/**
|
||||
* @class ThreadFetchModel
|
||||
*
|
||||
* A model to provide a fetch more historical messages button in a thread.
|
||||
*/
|
||||
class ThreadFetchModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*
|
||||
* The role values need to match MessageContentModel not to blow up.
|
||||
*
|
||||
* @sa MessageContentModel
|
||||
*/
|
||||
enum Roles {
|
||||
ComponentTypeRole = MessageContentModel::ComponentTypeRole, /**< The type of component to visualise the message. */
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
explicit ThreadFetchModel(QObject *parent);
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
*
|
||||
* @sa QAbstractItemModel::data
|
||||
*/
|
||||
[[nodiscard]] QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
||||
|
||||
/**
|
||||
* @brief 1 or 0, depending on whether there are more messages to download.
|
||||
*
|
||||
* @sa QAbstractItemModel::rowCount
|
||||
*/
|
||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
/**
|
||||
* @brief Returns a map with ComponentTypeRole it's the only one.
|
||||
*
|
||||
* @sa Roles, QAbstractItemModel::roleNames()
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class ThreadChatBarModel
|
||||
@@ -99,18 +144,14 @@ public:
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
/**
|
||||
* @brief Whether there is more data available for the model to fetch.
|
||||
*
|
||||
* @sa QAbstractItemModel::canFetchMore()
|
||||
* @brief Whether there are more events for the model to fetch.
|
||||
*/
|
||||
bool canFetchMore(const QModelIndex &parent) const override;
|
||||
bool moreEventsAvailable(const QModelIndex &parent) const;
|
||||
|
||||
/**
|
||||
* @brief Fetches the next batch of model data if any is available.
|
||||
*
|
||||
* @sa QAbstractItemModel::fetchMore()
|
||||
* @brief Fetches the next batch of events if any is available.
|
||||
*/
|
||||
void fetchMore(const QModelIndex &parent) override;
|
||||
Q_INVOKABLE void fetchMoreEvents(int max = 5);
|
||||
|
||||
/**
|
||||
* @brief Close the link preview at the given index.
|
||||
@@ -119,15 +160,17 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE void closeLinkPreview(int row);
|
||||
|
||||
Q_SIGNALS:
|
||||
void moreEventsAvailableChanged();
|
||||
|
||||
private:
|
||||
QString m_threadRootId;
|
||||
QPointer<MessageContentModel> m_threadRootContentModel;
|
||||
|
||||
std::deque<QString> m_events;
|
||||
ThreadFetchModel *m_threadFetchModel;
|
||||
ThreadChatBarModel *m_threadChatBarModel;
|
||||
|
||||
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
||||
|
||||
QPointer<Quotient::GetRelatingEventsWithRelTypeJob> m_currentJob = nullptr;
|
||||
std::optional<QString> m_nextBatch = QString();
|
||||
bool m_addingPending = false;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -27,24 +27,20 @@ void TimelineMessageModel::connectNewRoom()
|
||||
}
|
||||
|
||||
connect(m_room, &Room::aboutToAddNewMessages, this, [this](RoomEventsRange events) {
|
||||
for (auto &&event : events) {
|
||||
Q_EMIT newEventAdded(event.get());
|
||||
}
|
||||
m_initialized = true;
|
||||
beginInsertRows({}, timelineServerIndex(), timelineServerIndex() + int(events.size()) - 1);
|
||||
});
|
||||
connect(m_room, &Room::aboutToAddHistoricalMessages, this, [this](RoomEventsRange events) {
|
||||
for (auto &event : events) {
|
||||
Q_EMIT newEventAdded(event.get());
|
||||
}
|
||||
if (rowCount() > 0) {
|
||||
rowBelowInserted = rowCount() - 1; // See #312
|
||||
}
|
||||
m_initialized = true;
|
||||
beginInsertRows({}, rowCount(), rowCount() + int(events.size()) - 1);
|
||||
});
|
||||
connect(m_room, &Room::addedMessages, this, [this](int lowest, int biggest) {
|
||||
if (m_initialized) {
|
||||
for (int i = lowest; i == biggest; ++i) {
|
||||
const auto event = m_room->findInTimeline(i)->event();
|
||||
Q_EMIT newEventAdded(event);
|
||||
}
|
||||
|
||||
endInsertRows();
|
||||
}
|
||||
if (!m_lastReadEventIndex.isValid()) {
|
||||
|
||||
@@ -32,42 +32,6 @@ class TimelineMessageModel : public MessageModel
|
||||
QML_ELEMENT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*/
|
||||
enum EventRoles {
|
||||
DelegateTypeRole = Qt::UserRole + 1, /**< The delegate type of the message. */
|
||||
EventIdRole, /**< The matrix event ID of the event. */
|
||||
TimeRole, /**< The timestamp for when the event was sent (as a QDateTime). */
|
||||
SectionRole, /**< The date of the event as a string. */
|
||||
AuthorRole, /**< The author of the event. */
|
||||
HighlightRole, /**< Whether the event should be highlighted. */
|
||||
SpecialMarksRole, /**< Whether the event is hidden or not. */
|
||||
ProgressInfoRole, /**< Progress info when downloading files. */
|
||||
GenericDisplayRole, /**< A generic string based upon the message type. */
|
||||
MediaInfoRole, /**< The media info for the event. */
|
||||
|
||||
ContentModelRole, /**< The MessageContentModel for the event. */
|
||||
|
||||
IsThreadedRole, /**< Whether the message is in a thread. */
|
||||
ThreadRootRole, /**< The Matrix ID of the thread root message, if any . */
|
||||
|
||||
ShowSectionRole, /**< Whether the section header should be shown. */
|
||||
|
||||
ReadMarkersRole, /**< The first 5 other users at the event for read marker tracking. */
|
||||
ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */
|
||||
ReactionRole, /**< List model for this event. */
|
||||
ShowReactionsRole, /**< Whether there are any reactions to be shown. */
|
||||
|
||||
VerifiedRole, /**< Whether an encrypted message is sent in a verified session. */
|
||||
AuthorDisplayNameRole, /**< The displayname for the event's sender; for name change events, the old displayname. */
|
||||
IsRedactedRole, /**< Whether an event has been deleted. */
|
||||
IsPendingRole, /**< Whether an event is waiting to be accepted by the server. */
|
||||
IsEditableRole, /**< Whether the event can be edited by the user. */
|
||||
LastRole, // Keep this last
|
||||
};
|
||||
Q_ENUM(EventRoles)
|
||||
|
||||
explicit TimelineMessageModel(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user