Compare commits
72 Commits
work/tobia
...
v25.03.80
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32bea56a6d | ||
|
|
661cf22667 | ||
|
|
ad1254fb71 | ||
|
|
7514a8a6f7 | ||
|
|
716ae11941 | ||
|
|
4ff16ff402 | ||
|
|
bd598b9c44 | ||
|
|
f1253e4ede | ||
|
|
79a3da3358 | ||
|
|
5a6bdfbbba | ||
|
|
3d663be506 | ||
|
|
0ada4cdebe | ||
|
|
d103de96aa | ||
|
|
f248b04834 | ||
|
|
9572f20682 | ||
|
|
409cec08fc | ||
|
|
51f330eae9 | ||
|
|
51750267e5 | ||
|
|
99948d5151 | ||
|
|
030726e6fb | ||
|
|
1fad54272f | ||
|
|
4af4bfd55f | ||
|
|
77cedef5bb | ||
|
|
db36f187dc | ||
|
|
2861eb9c60 | ||
|
|
9811c0d97a | ||
|
|
e9c21373ed | ||
|
|
bda23ec54a | ||
|
|
e23641375b | ||
|
|
024d54345a | ||
|
|
59fd4d3916 | ||
|
|
88d684b6c1 | ||
|
|
94fdf777cb | ||
|
|
dea70152e4 | ||
|
|
614caf5ca0 | ||
|
|
25dbae37fb | ||
|
|
e060032e6a | ||
|
|
4725410c0f | ||
|
|
20488ee400 | ||
|
|
b1c0619af5 | ||
|
|
ade730179a | ||
|
|
9264ad26d6 | ||
|
|
9020e2c7cb | ||
|
|
0f51c34b24 | ||
|
|
f6a427e865 | ||
|
|
9b95930463 | ||
|
|
cb96b4991e | ||
|
|
cde7a51cde | ||
|
|
046d611f56 | ||
|
|
d7b3748159 | ||
|
|
188c9fc726 | ||
|
|
dbc735e63b | ||
|
|
8750486f7b | ||
|
|
6dc4baeeb5 | ||
|
|
ff28828a2e | ||
|
|
e28452dfd1 | ||
|
|
5d7cb5c28f | ||
|
|
08b29f7081 | ||
|
|
c9e034b5b3 | ||
|
|
d9f0ff466f | ||
|
|
6b4b895102 | ||
|
|
0c7e02e7c9 | ||
|
|
4d1c82a623 | ||
|
|
8d33fe6221 | ||
|
|
9d27651411 | ||
|
|
268975bc3b | ||
|
|
66343ba11e | ||
|
|
684cd85a7a | ||
|
|
ef9a80e76f | ||
|
|
fbb5f02379 | ||
|
|
5f4bde96e9 | ||
|
|
f8c8a68840 |
@@ -25,13 +25,23 @@
|
||||
"modules": [
|
||||
{
|
||||
"name": "kirigamiaddons",
|
||||
"config-opts": [ "-DBUILD_TESTING=OFF" ],
|
||||
"config-opts": [
|
||||
"-DBUILD_TESTING=OFF"
|
||||
],
|
||||
"buildsystem": "cmake-ninja",
|
||||
"sources": [ { "type": "git", "url": "https://invent.kde.org/libraries/kirigami-addons.git", "commit": "34d311219e8b7209746a98b3a29b91ded05ff936" } ]
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://invent.kde.org/libraries/kirigami-addons.git"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "kquickimageeditor",
|
||||
"config-opts": [ "-DBUILD_WITH_QT6=ON" ],
|
||||
"config-opts": [
|
||||
"-DBUILD_WITH_QT6=ON",
|
||||
"-DBUILD_TESTING=OFF"
|
||||
],
|
||||
"buildsystem": "cmake-ninja",
|
||||
"sources": [
|
||||
{
|
||||
@@ -43,17 +53,19 @@
|
||||
{
|
||||
"name": "olm",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"config-opts": [ "-DOLM_TESTS=OFF" ],
|
||||
"config-opts": [
|
||||
"-DOLM_TESTS=OFF"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://gitlab.matrix.org/matrix-org/olm.git",
|
||||
"tag": "3.2.10",
|
||||
"tag": "3.2.16",
|
||||
"x-checker-data": {
|
||||
"type": "git",
|
||||
"tag-pattern": "^([\\d.]+)$"
|
||||
},
|
||||
"commit": "9908862979147a71dc6abaecd521be526ae77be1"
|
||||
"commit": "7e0c8277032e40308987257b711b38af8d77cc69"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -65,13 +77,13 @@
|
||||
"-Dvapi=false",
|
||||
"-Dgtk_doc=false",
|
||||
"-Dintrospection=false",
|
||||
"-Dgcrypt=false"
|
||||
"-Dcrypto=disabled"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://download.gnome.org/sources/libsecret/0.20/libsecret-0.20.5.tar.xz",
|
||||
"sha256": "3fb3ce340fcd7db54d87c893e69bfc2b1f6e4d4b279065ffe66dac9f0fd12b4d",
|
||||
"url": "https://download.gnome.org/sources/libsecret/0.21/libsecret-0.21.6.tar.xz",
|
||||
"sha256": "747b8c175be108c880d3adfb9c3537ea66e520e4ad2dccf5dce58003aeeca090",
|
||||
"x-checker-data": {
|
||||
"type": "gnome",
|
||||
"name": "libsecret",
|
||||
@@ -86,13 +98,13 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/frankosterfeld/qtkeychain/archive/0.14.2.tar.gz",
|
||||
"sha256": "cf2e972b783ba66334a79a30f6b3a1ea794a1dc574d6c3bebae5ffd2f0399571",
|
||||
"url": "https://github.com/frankosterfeld/qtkeychain/archive/refs/tags/0.15.0.tar.gz",
|
||||
"sha256": "f4254dc8f0933b06d90672d683eab08ef770acd8336e44dfa030ce041dc2ca22",
|
||||
"x-checker-data": {
|
||||
"type": "anitya",
|
||||
"project-id": 4138,
|
||||
"stable-only": true,
|
||||
"url-template": "https://github.com/frankosterfeld/qtkeychain/archive/v$version.tar.gz"
|
||||
"url-template": "https://github.com/frankosterfeld/qtkeychain/archive/refs/tags/$version.tar.gz"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -100,7 +112,8 @@
|
||||
"-DBUILD_WITH_QT6=ON",
|
||||
"-DCMAKE_INSTALL_LIBDIR=/app/lib",
|
||||
"-DLIB_INSTALL_DIR=/app/lib",
|
||||
"-DBUILD_TRANSLATIONS=NO"
|
||||
"-DBUILD_TRANSLATIONS=NO",
|
||||
"-DBUILD_TESTING=OFF"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -123,29 +136,31 @@
|
||||
{
|
||||
"name": "cmark",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"config-opts": [ "-DCMARK_TESTS=OFF" ],
|
||||
"config-opts": [
|
||||
"-DCMARK_TESTS=OFF",
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
"-DCMAKE_INSTALL_PREFIX=/app"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/commonmark/cmark.git"
|
||||
}
|
||||
],
|
||||
"config-opts": [
|
||||
"-DCMARK_TESTS=OFF",
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
"-DCMAKE_INSTALL_PREFIX=/app"
|
||||
],
|
||||
"builddir": true
|
||||
},
|
||||
{
|
||||
"name": "qcoro",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"config-opts": [ "-DQCORO_BUILD_EXAMPLES=OFF", "-DBUILD_TESTING=OFF" ],
|
||||
"config-opts": [
|
||||
"-DQCORO_BUILD_EXAMPLES=OFF",
|
||||
"-DBUILD_TESTING=OFF"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.7.0.tar.gz",
|
||||
"sha256": "23ef0217926e67c8d2eb861cf91617da2f7d8d5a9ae6c62321b21448b1669210",
|
||||
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.11.0.tar.gz",
|
||||
"sha256": "9942c5b4c533192f6c5954dc6d10178b3829075e6a621b67df73f0a4b74d8297",
|
||||
"x-checker-data": {
|
||||
"type": "anitya",
|
||||
"project-id": 236236,
|
||||
@@ -158,14 +173,15 @@
|
||||
{
|
||||
"name": "neochat",
|
||||
"buildsystem": "cmake-ninja",
|
||||
"config-opts": [
|
||||
"-DBUILD_TESTING=OFF",
|
||||
"-DNEOCHAT_FLATPAK=ON"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"config-opts": [
|
||||
"-DNEOCHAT_FLATPAK=ON"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -10,6 +10,7 @@ include:
|
||||
- /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
|
||||
|
||||
@@ -9,7 +9,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_MICRO "70")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "80")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||
@@ -66,8 +66,6 @@ if (QT_KNOWN_POLICY_QTP0004)
|
||||
qt_policy(SET QTP0004 NEW)
|
||||
endif ()
|
||||
|
||||
find_package(sentry REQUIRED)
|
||||
|
||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels IconThemes ColorScheme)
|
||||
set_package_properties(KF6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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
|
||||
|
||||
420
po/ar/neochat.po
420
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
418
po/az/neochat.po
418
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
429
po/ca/neochat.po
429
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
848
po/cs/neochat.po
848
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
400
po/da/neochat.po
400
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
424
po/de/neochat.po
424
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
422
po/el/neochat.po
422
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
512
po/eo/neochat.po
512
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
1213
po/es/neochat.po
1213
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
427
po/eu/neochat.po
427
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
636
po/fi/neochat.po
636
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
430
po/fr/neochat.po
430
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
418
po/gl/neochat.po
418
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
425
po/hi/neochat.po
425
po/hi/neochat.po
File diff suppressed because it is too large
Load Diff
425
po/hu/neochat.po
425
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
424
po/ia/neochat.po
424
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
423
po/id/neochat.po
423
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
411
po/ie/neochat.po
411
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
512
po/it/neochat.po
512
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
383
po/ja/neochat.po
383
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
409
po/ka/neochat.po
409
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
422
po/ko/neochat.po
422
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
383
po/lt/neochat.po
383
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
440
po/lv/neochat.po
440
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
411
po/nl/neochat.po
411
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
442
po/nn/neochat.po
442
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
418
po/pa/neochat.po
418
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
528
po/pl/neochat.po
528
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
423
po/pt/neochat.po
423
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
426
po/ru/neochat.po
426
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
425
po/sa/neochat.po
425
po/sa/neochat.po
File diff suppressed because it is too large
Load Diff
421
po/sk/neochat.po
421
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
408
po/sl/neochat.po
408
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
408
po/sv/neochat.po
408
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
428
po/ta/neochat.po
428
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
413
po/tr/neochat.po
413
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
410
po/uk/neochat.po
410
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
---
|
||||
name: neochat
|
||||
base: core22
|
||||
base: core24
|
||||
adopt-info: neochat
|
||||
grade: stable
|
||||
confinement: strict
|
||||
@@ -106,8 +106,6 @@ parts:
|
||||
build-snaps:
|
||||
- cmake
|
||||
build-packages:
|
||||
- gcc-13
|
||||
- g++-13
|
||||
- libssl-dev
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
@@ -115,11 +113,6 @@ parts:
|
||||
- -DBUILD_TESTING=OFF
|
||||
- -DQuotient_ENABLE_E2EE=ON
|
||||
- -DBUILD_WITH_QT6=ON
|
||||
override-build: |
|
||||
"update-alternatives --install /usr/bin/gcc gcc\
|
||||
/usr/bin/gcc-13 100 --slave /usr/bin/g++ g++ \
|
||||
/usr/bin/g++-13 --slave /usr/bin/gcov gcov /usr/bin/gcov-13"
|
||||
craftctl default
|
||||
prime:
|
||||
- -usr/include
|
||||
- -usr/lib/*/pkgconfig
|
||||
@@ -132,6 +125,8 @@ parts:
|
||||
plugin: cmake
|
||||
build-environment:
|
||||
- PATH: /snap/bin:${PATH}
|
||||
- PYTHONPATH: ${CRAFT_STAGE}/lib/python3.12/site-packages:${CRAFT_STAGE}/usr/lib/python3/dist-packages
|
||||
- LD_LIBRARY_PATH: "/snap/mesa-2404/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/kde-qt6-core24-sdk/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libproxy:$LD_LIBRARY_PATH"
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
@@ -153,6 +148,8 @@ parts:
|
||||
plugin: cmake
|
||||
build-environment:
|
||||
- PATH: /snap/bin:${PATH}
|
||||
- PYTHONPATH: ${CRAFT_STAGE}/lib/python3.12/site-packages:${CRAFT_STAGE}/usr/lib/python3/dist-packages
|
||||
- LD_LIBRARY_PATH: "/snap/mesa-2404/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/kde-qt6-core24-sdk/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libproxy:$LD_LIBRARY_PATH"
|
||||
build-packages:
|
||||
- cmark
|
||||
- libcmark-dev
|
||||
@@ -175,3 +172,13 @@ parts:
|
||||
- libcmark0.30.2
|
||||
prime:
|
||||
- usr/lib/*/libcmark.so*
|
||||
|
||||
gpu-2404:
|
||||
after: [neochat]
|
||||
source: https://github.com/canonical/gpu-snap.git
|
||||
plugin: dump
|
||||
override-prime: |
|
||||
craftctl default
|
||||
${CRAFT_PART_SRC}/bin/gpu-2404-cleanup mesa-2404
|
||||
prime:
|
||||
- bin/gpu-2404-wrapper
|
||||
|
||||
@@ -107,8 +107,6 @@ add_library(neochat STATIC
|
||||
models/imagepacksmodel.h
|
||||
events/imagepackevent.cpp
|
||||
events/imagepackevent.h
|
||||
events/joinrulesevent.cpp
|
||||
events/joinrulesevent.h
|
||||
models/reactionmodel.cpp
|
||||
models/reactionmodel.h
|
||||
delegatesizehelper.cpp
|
||||
@@ -198,8 +196,6 @@ add_library(neochat STATIC
|
||||
models/pinnedmessagemodel.h
|
||||
models/commonroomsmodel.cpp
|
||||
models/commonroomsmodel.h
|
||||
sentryintegration.cpp
|
||||
sentryintegration.h
|
||||
)
|
||||
|
||||
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||
@@ -300,7 +296,6 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||
qml/HoverLinkIndicator.qml
|
||||
qml/AvatarNotification.qml
|
||||
qml/ReasonDialog.qml
|
||||
qml/SendLogsDialog.qml
|
||||
DEPENDENCIES
|
||||
QtCore
|
||||
QtQuick
|
||||
@@ -433,7 +428,6 @@ target_link_libraries(neochat PUBLIC
|
||||
cmark::cmark
|
||||
QCoro::Core
|
||||
QCoro::Network
|
||||
sentry::sentry
|
||||
)
|
||||
|
||||
if (TARGET KF6::Crash)
|
||||
|
||||
@@ -222,11 +222,14 @@ void ChatDocumentHandler::complete(int index)
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure we only search for the beginning of the current completion identifier
|
||||
const auto fromIndex = qMax(completionStartIndex(), 0);
|
||||
|
||||
if (m_completionModel->autoCompletionType() == CompletionModel::User) {
|
||||
auto name = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::DisplayNameRole).toString();
|
||||
auto id = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char('@'), cursorPosition() - 1);
|
||||
auto at = text.indexOf(QLatin1Char('@'), fromIndex);
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
@@ -239,7 +242,7 @@ void ChatDocumentHandler::complete(int index)
|
||||
} else if (m_completionModel->autoCompletionType() == CompletionModel::Command) {
|
||||
auto command = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedTextRole).toString();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char('/'));
|
||||
auto at = text.indexOf(QLatin1Char('/'), fromIndex);
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
@@ -247,7 +250,7 @@ void ChatDocumentHandler::complete(int index)
|
||||
} else if (m_completionModel->autoCompletionType() == CompletionModel::Room) {
|
||||
auto alias = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char('#'), cursorPosition() - 1);
|
||||
auto at = text.indexOf(QLatin1Char('#'), fromIndex);
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
@@ -260,7 +263,7 @@ void ChatDocumentHandler::complete(int index)
|
||||
} else if (m_completionModel->autoCompletionType() == CompletionModel::Emoji) {
|
||||
auto shortcode = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedTextRole).toString();
|
||||
auto text = getText();
|
||||
auto at = text.lastIndexOf(QLatin1Char(':'));
|
||||
auto at = text.indexOf(QLatin1Char(':'), fromIndex);
|
||||
QTextCursor cursor(document()->textDocument());
|
||||
cursor.setPosition(at);
|
||||
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
|
||||
|
||||
@@ -294,7 +294,7 @@ bool Controller::supportSystemTray() const
|
||||
void Controller::setQuitOnLastWindowClosed()
|
||||
{
|
||||
#ifndef Q_OS_ANDROID
|
||||
if (NeoChatConfig::self()->systemTray()) {
|
||||
if (supportSystemTray() && NeoChatConfig::self()->systemTray()) {
|
||||
m_trayIcon = new TrayIcon(this);
|
||||
m_trayIcon->show();
|
||||
} else {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
@@ -37,7 +38,7 @@ FormCard.FormCardPage {
|
||||
}
|
||||
|
||||
function openEventSource(stateKey: string): void {
|
||||
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
model: stateKeysModel,
|
||||
allowEdit: true,
|
||||
room: root.room,
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
LiveLocation, /**< The initial event of a shared live location (i.e., the place where this is supposed to be shown in the timeline). */
|
||||
Encrypted, /**< An encrypted message that cannot be decrypted. */
|
||||
Reply, /**< A component to show a replied-to message. */
|
||||
Reaction, /**< A component to show the reactions to this message. */
|
||||
LinkPreview, /**< A preview of a URL in the message. */
|
||||
LinkPreviewLoad, /**< A loading dialog for a link preview. */
|
||||
ChatBar, /**< A text edit for editing a message. */
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2019 Kitsune Ral <Kitsune-Ral@users.sf.net>
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "joinrulesevent.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
QString JoinRulesEvent::joinRule() const
|
||||
{
|
||||
return fromJson<QString>(contentJson()["join_rule"_L1]);
|
||||
}
|
||||
|
||||
QJsonArray JoinRulesEvent::allow() const
|
||||
{
|
||||
return contentJson()["allow"_L1].toArray();
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/events/stateevent.h>
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
/**
|
||||
* @class JoinRulesEvent
|
||||
*
|
||||
* Class to define a join rule state event.
|
||||
*
|
||||
* @sa Quotient::StateEvent
|
||||
*/
|
||||
class JoinRulesEvent : public StateEvent
|
||||
{
|
||||
public:
|
||||
QUO_EVENT(JoinRulesEvent, "m.room.join_rules")
|
||||
|
||||
explicit JoinRulesEvent(const QJsonObject &obj)
|
||||
: StateEvent(obj)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The join rule for the room.
|
||||
*
|
||||
* see https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules for
|
||||
* the available join rules for a room.
|
||||
*/
|
||||
QString joinRule() const;
|
||||
|
||||
/**
|
||||
* @brief The allow rule for restricted rooms.
|
||||
*
|
||||
* see https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules for
|
||||
* full details on allow rules.
|
||||
*/
|
||||
QJsonArray allow() const;
|
||||
};
|
||||
}
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
|
||||
#include "neochatconfig.h"
|
||||
#include "utils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
@@ -22,7 +21,6 @@ LinkPreviewer::LinkPreviewer(const QUrl &url, QObject *parent)
|
||||
Q_ASSERT(dynamic_cast<Connection *>(this->parent()));
|
||||
|
||||
connect(this, &LinkPreviewer::urlChanged, this, &LinkPreviewer::emptyChanged);
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, &LinkPreviewer::loadUrlPreview);
|
||||
|
||||
loadUrlPreview();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ Kirigami.Page {
|
||||
|
||||
property bool showExisting: false
|
||||
property bool _showExisting: showExisting && root.currentStepString === root.initialStep
|
||||
property bool showSettings: true
|
||||
property alias currentStep: module.item
|
||||
property string currentStepString: initialStep
|
||||
property string initialStep: "LoginRegister"
|
||||
@@ -265,6 +266,7 @@ Kirigami.Page {
|
||||
FormCard.FormCard {
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing * 2
|
||||
maximumWidth: Kirigami.Units.gridUnit * 20
|
||||
visible: root.showSettings
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18nc("@action:button", "Settings")
|
||||
icon.name: "settings-configure"
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
#include "controller.h"
|
||||
#include "logger.h"
|
||||
#include "roommanager.h"
|
||||
#include "sentryintegration.h"
|
||||
#include "sharehandler.h"
|
||||
#include "windowcontroller.h"
|
||||
|
||||
@@ -175,8 +174,6 @@ int main(int argc, char *argv[])
|
||||
KCrash::initialize();
|
||||
#endif
|
||||
|
||||
Sentry::instance();
|
||||
|
||||
initLogging();
|
||||
|
||||
Connection::setEncryptionDefault(true);
|
||||
@@ -242,6 +239,7 @@ int main(int argc, char *argv[])
|
||||
Q_IMPORT_QML_PLUGIN(org_kde_neochat_chatbarPlugin)
|
||||
|
||||
qml_register_types_org_kde_neochat();
|
||||
qmlRegisterUncreatableMetaObject(Quotient::staticMetaObject, "Quotient", 1, 0, "JoinRule", u"Access to JoinRule enum only"_s);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "messagecontentmodel.h"
|
||||
#include "eventhandler.h"
|
||||
#include "messagecomponenttype.h"
|
||||
#include "neochatconfig.h"
|
||||
|
||||
#include <QImageReader>
|
||||
@@ -27,6 +28,7 @@
|
||||
#include "chatbarcache.h"
|
||||
#include "filetype.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "models/reactionmodel.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "texthandler.h"
|
||||
@@ -130,9 +132,6 @@ void MessageContentModel::initializeModel()
|
||||
connect(m_room, &NeoChatRoom::urlPreviewEnabledChanged, this, [this]() {
|
||||
resetContent();
|
||||
});
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, [this]() {
|
||||
resetContent();
|
||||
});
|
||||
connect(m_room, &Room::memberNameUpdated, this, [this](RoomMember member) {
|
||||
if (m_room != nullptr) {
|
||||
if (senderId().isEmpty() || senderId() == member.id()) {
|
||||
@@ -152,12 +151,18 @@ void MessageContentModel::initializeModel()
|
||||
updateReplyModel();
|
||||
resetModel();
|
||||
});
|
||||
connect(m_room, &Room::updatedEvent, this, [this](const QString &eventId) {
|
||||
if (eventId == m_eventId) {
|
||||
updateReactionModel();
|
||||
}
|
||||
});
|
||||
|
||||
initializeEvent();
|
||||
if (m_currentState == Available || m_currentState == Pending) {
|
||||
updateReplyModel();
|
||||
}
|
||||
resetModel();
|
||||
updateReactionModel();
|
||||
}
|
||||
|
||||
void MessageContentModel::initializeEvent()
|
||||
@@ -340,6 +345,10 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
||||
if (role == ReplyContentModelRole) {
|
||||
return QVariant::fromValue<MessageContentModel *>(m_replyModel);
|
||||
}
|
||||
if (role == ReactionModelRole) {
|
||||
return QVariant::fromValue<ReactionModel *>(m_reactionModel);
|
||||
;
|
||||
}
|
||||
if (role == ThreadRootRole) {
|
||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event.first);
|
||||
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||
@@ -400,6 +409,7 @@ QHash<int, QByteArray> MessageContentModel::roleNamesStatic()
|
||||
roles[MessageContentModel::ReplyEventIdRole] = "replyEventId";
|
||||
roles[MessageContentModel::ReplyAuthorRole] = "replyAuthor";
|
||||
roles[MessageContentModel::ReplyContentModelRole] = "replyContentModel";
|
||||
roles[MessageContentModel::ReactionModelRole] = "reactionModel";
|
||||
roles[MessageContentModel::ThreadRootRole] = "threadRoot";
|
||||
roles[MessageContentModel::LinkPreviewerRole] = "linkPreviewer";
|
||||
roles[MessageContentModel::ChatBarCacheRole] = "chatBarCache";
|
||||
@@ -480,11 +490,16 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
|
||||
newComponents = addLinkPreviews(newComponents);
|
||||
}
|
||||
|
||||
if ((m_reactionModel && m_reactionModel->rowCount() > 0)) {
|
||||
newComponents += MessageComponent{MessageComponentType::Reaction, QString(), {}};
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||
if (roomMessageEvent && (roomMessageEvent->isThreaded() || m_room->threads().contains(roomMessageEvent->id()))
|
||||
if (NeoChatConfig::self()->threads() && roomMessageEvent && (roomMessageEvent->isThreaded() || m_room->threads().contains(roomMessageEvent->id()))
|
||||
&& roomMessageEvent->id() == roomMessageEvent->threadRootEventId()) {
|
||||
#else
|
||||
if (isThreading && roomMessageEvent && roomMessageEvent->isThreaded() && roomMessageEvent->id() == roomMessageEvent->threadRootEventId()) {
|
||||
if (NeoChatConfig::self()->threads() && roomMessageEvent && roomMessageEvent->isThreaded()
|
||||
&& roomMessageEvent->id() == roomMessageEvent->threadRootEventId()) {
|
||||
#endif
|
||||
newComponents += MessageComponent{MessageComponentType::Separator, {}, {}};
|
||||
newComponents += MessageComponent{MessageComponentType::ThreadBody, u"Thread Body"_s, {}};
|
||||
@@ -513,7 +528,7 @@ void MessageContentModel::updateReplyModel()
|
||||
if (roomMessageEvent == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!roomMessageEvent->isReply() || (roomMessageEvent->isThreaded() && NeoChatConfig::self()->threads())) {
|
||||
if (!roomMessageEvent->isReply(!NeoChatConfig::self()->threads()) || (roomMessageEvent->isThreaded() && NeoChatConfig::self()->threads())) {
|
||||
if (m_replyModel) {
|
||||
delete m_replyModel;
|
||||
}
|
||||
@@ -524,7 +539,7 @@ void MessageContentModel::updateReplyModel()
|
||||
return;
|
||||
}
|
||||
|
||||
m_replyModel = new MessageContentModel(m_room, roomMessageEvent->replyEventId(), true, false, this);
|
||||
m_replyModel = new MessageContentModel(m_room, roomMessageEvent->replyEventId(!NeoChatConfig::self()->threads()), true, false, this);
|
||||
|
||||
connect(m_replyModel, &MessageContentModel::eventUpdated, this, [this]() {
|
||||
Q_EMIT dataChanged(index(0), index(0), {ReplyAuthorRole});
|
||||
@@ -731,4 +746,25 @@ void MessageContentModel::updateItineraryModel()
|
||||
}
|
||||
}
|
||||
|
||||
void MessageContentModel::updateReactionModel()
|
||||
{
|
||||
if (m_reactionModel != nullptr && m_reactionModel->rowCount() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_reactionModel == nullptr) {
|
||||
m_reactionModel = new ReactionModel(this, m_eventId, m_room);
|
||||
connect(m_reactionModel, &ReactionModel::reactionsUpdated, this, &MessageContentModel::updateReactionModel);
|
||||
}
|
||||
|
||||
if (m_reactionModel->rowCount() <= 0) {
|
||||
m_reactionModel->disconnect(this);
|
||||
delete m_reactionModel;
|
||||
m_reactionModel = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
resetContent();
|
||||
}
|
||||
|
||||
#include "moc_messagecontentmodel.cpp"
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/events/roomevent.h>
|
||||
#include <Quotient/room.h>
|
||||
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "itinerarymodel.h"
|
||||
#include "messagecomponent.h"
|
||||
#include "models/reactionmodel.h"
|
||||
#include "neochatroommember.h"
|
||||
|
||||
/**
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
ReplyAuthorRole, /**< The author of the event that was replied to. */
|
||||
ReplyContentModelRole, /**< The MessageContentModel for the reply event. */
|
||||
|
||||
ReactionModelRole, /**< Reaction model for this event. */
|
||||
|
||||
ThreadRootRole, /**< The thread root event ID for the event. */
|
||||
|
||||
LinkPreviewerRole, /**< The link preview details. */
|
||||
@@ -125,6 +127,7 @@ private:
|
||||
QPointer<MessageContentModel> m_replyModel;
|
||||
void updateReplyModel();
|
||||
|
||||
ReactionModel *m_reactionModel = nullptr;
|
||||
ItineraryModel *m_itineraryModel = nullptr;
|
||||
|
||||
QList<MessageComponent> componentsForType(MessageComponentType::Type type);
|
||||
@@ -135,4 +138,6 @@ private:
|
||||
|
||||
void updateItineraryModel();
|
||||
bool m_emptyItinerary = false;
|
||||
|
||||
void updateReactionModel();
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "neochatconfig.h"
|
||||
|
||||
#include <Quotient/events/encryptedevent.h>
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
#include <Quotient/events/stickerevent.h>
|
||||
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||
@@ -120,8 +121,12 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
if (role == ContentModelRole) {
|
||||
if (event->get().is<EncryptedEvent>()) {
|
||||
return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(event->get().id()));
|
||||
}
|
||||
|
||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(&event.value().get());
|
||||
if (roomMessageEvent && roomMessageEvent->isThreaded()) {
|
||||
if (NeoChatConfig::self()->threads() && roomMessageEvent && roomMessageEvent->isThreaded()) {
|
||||
return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(roomMessageEvent->threadRootEventId()));
|
||||
}
|
||||
return QVariant::fromValue<MessageContentModel *>(m_room->contentModelForEvent(&event->get()));
|
||||
@@ -216,6 +221,9 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
if (role == IsThreadedRole) {
|
||||
if (!NeoChatConfig::self()->threads()) {
|
||||
return false;
|
||||
}
|
||||
if (auto roomMessageEvent = eventCast<const RoomMessageEvent>(&event.value().get())) {
|
||||
return roomMessageEvent->isThreaded();
|
||||
}
|
||||
@@ -258,18 +266,6 @@ QVariant MessageModel::data(const QModelIndex &idx, int role) const
|
||||
return m_readMarkerModels.contains(event.value().get().id());
|
||||
}
|
||||
|
||||
if (role == ReactionRole) {
|
||||
if (m_reactionModels.contains(event.value().get().id())) {
|
||||
return QVariant::fromValue<ReactionModel *>(m_reactionModels[event.value().get().id()].data());
|
||||
} else {
|
||||
return QVariantList();
|
||||
}
|
||||
}
|
||||
|
||||
if (role == ShowReactionsRole) {
|
||||
return m_reactionModels.contains(event.value().get().id());
|
||||
}
|
||||
|
||||
if (role == VerifiedRole) {
|
||||
if (event.value().get().originalEvent()) {
|
||||
auto encrypted = dynamic_cast<const EncryptedEvent *>(event.value().get().originalEvent());
|
||||
@@ -323,8 +319,6 @@ QHash<int, QByteArray> MessageModel::roleNames() const
|
||||
roles[ShowSectionRole] = "showSection";
|
||||
roles[ReadMarkersRole] = "readMarkers";
|
||||
roles[ShowReadMarkersRole] = "showReadMarkers";
|
||||
roles[ReactionRole] = "reaction";
|
||||
roles[ShowReactionsRole] = "showReactions";
|
||||
roles[VerifiedRole] = "verified";
|
||||
roles[AuthorDisplayNameRole] = "authorDisplayName";
|
||||
roles[IsRedactedRole] = "isRedacted";
|
||||
@@ -454,31 +448,6 @@ void MessageModel::createEventObjects(const Quotient::RoomEvent *event, bool isP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto roomEvent = eventCast<const RoomMessageEvent>(event)) {
|
||||
// ReactionModel handles updates to add and remove reactions, we only need to
|
||||
// handle adding and removing whole models here.
|
||||
if (m_reactionModels.contains(eventId)) {
|
||||
// If a model already exists but now has no reactions remove it
|
||||
if (m_reactionModels[eventId]->rowCount() <= 0) {
|
||||
m_reactionModels.remove(eventId);
|
||||
if (!resetting) {
|
||||
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (m_room->relatedEvents(*event, Quotient::EventRelation::AnnotationType).count() > 0) {
|
||||
// If a model doesn't exist and there are reactions add it.
|
||||
auto reactionModel = QSharedPointer<ReactionModel>(new ReactionModel(roomEvent, m_room));
|
||||
if (reactionModel->rowCount() > 0) {
|
||||
m_reactionModels[eventId] = reactionModel;
|
||||
if (!resetting) {
|
||||
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageModel::clearModel()
|
||||
@@ -504,7 +473,6 @@ void MessageModel::clearModel()
|
||||
|
||||
void MessageModel::clearEventObjects()
|
||||
{
|
||||
m_reactionModels.clear();
|
||||
m_readMarkerModels.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -77,8 +77,6 @@ public:
|
||||
|
||||
ReadMarkersRole, /**< The first 5 other users at the event for read marker tracking. */
|
||||
ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */
|
||||
ReactionRole, /**< List model for this event. */
|
||||
ShowReactionsRole, /**< Whether there are any reactions to be shown. */
|
||||
|
||||
VerifiedRole, /**< Whether an encrypted message is sent in a verified session. */
|
||||
AuthorDisplayNameRole, /**< The displayname for the event's sender; for name change events, the old displayname. */
|
||||
@@ -155,7 +153,6 @@ private:
|
||||
bool movingEvent = false;
|
||||
|
||||
QMap<QString, QSharedPointer<ReadMarkerModel>> m_readMarkerModels;
|
||||
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
||||
|
||||
void createEventObjects(const Quotient::RoomEvent *event, bool isPending = false);
|
||||
};
|
||||
|
||||
@@ -9,22 +9,27 @@
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include "neochatroom.h"
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
ReactionModel::ReactionModel(const Quotient::RoomMessageEvent *event, NeoChatRoom *room)
|
||||
: QAbstractListModel(nullptr)
|
||||
ReactionModel::ReactionModel(MessageContentModel *parent, const QString &eventId, NeoChatRoom *room)
|
||||
: QAbstractListModel(parent)
|
||||
, m_room(room)
|
||||
, m_event(event)
|
||||
, m_eventId(eventId)
|
||||
{
|
||||
if (m_event != nullptr && m_room != nullptr) {
|
||||
connect(m_room, &NeoChatRoom::updatedEvent, this, [this](const QString &eventId) {
|
||||
if (m_event && m_event->id() == eventId) {
|
||||
updateReactions();
|
||||
}
|
||||
});
|
||||
Q_ASSERT(parent);
|
||||
Q_ASSERT(parent != nullptr);
|
||||
Q_ASSERT(!eventId.isEmpty());
|
||||
Q_ASSERT(room != nullptr);
|
||||
|
||||
updateReactions();
|
||||
}
|
||||
connect(m_room, &NeoChatRoom::updatedEvent, this, [this](const QString &eventId) {
|
||||
if (m_eventId == eventId) {
|
||||
updateReactions();
|
||||
}
|
||||
});
|
||||
|
||||
updateReactions();
|
||||
}
|
||||
|
||||
QVariant ReactionModel::data(const QModelIndex &index, int role) const
|
||||
@@ -99,12 +104,16 @@ int ReactionModel::rowCount(const QModelIndex &parent) const
|
||||
|
||||
void ReactionModel::updateReactions()
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
|
||||
m_reactions.clear();
|
||||
m_shortcodes.clear();
|
||||
|
||||
const auto &annotations = m_room->relatedEvents(*m_event, Quotient::EventRelation::AnnotationType);
|
||||
const auto &annotations = m_room->relatedEvents(m_eventId, Quotient::EventRelation::AnnotationType);
|
||||
if (annotations.isEmpty()) {
|
||||
endResetModel();
|
||||
return;
|
||||
|
||||
@@ -3,11 +3,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "neochatroom.h"
|
||||
#include <QAbstractListModel>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/events/reactionevent.h>
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class RoomMessageEvent;
|
||||
}
|
||||
|
||||
class MessageContentModel;
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
* @class ReactionModel
|
||||
*
|
||||
@@ -38,7 +47,7 @@ public:
|
||||
HasLocalMember, /**< Whether the local member is in the list of authors. */
|
||||
};
|
||||
|
||||
explicit ReactionModel(const Quotient::RoomMessageEvent *event, NeoChatRoom *room);
|
||||
explicit ReactionModel(MessageContentModel *parent, const QString &eventId, NeoChatRoom *room);
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
@@ -61,9 +70,15 @@ public:
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* @brief The reactions in the model have been updated.
|
||||
*/
|
||||
void reactionsUpdated();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
const Quotient::RoomMessageEvent *m_event;
|
||||
QString m_eventId;
|
||||
QList<Reaction> m_reactions;
|
||||
QMap<QString, QString> m_shortcodes;
|
||||
|
||||
|
||||
@@ -348,6 +348,12 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
||||
return QVariant::fromValue(room);
|
||||
}
|
||||
if (role == SubtitleTextRole) {
|
||||
if (room->isInvite()) {
|
||||
if (room->isDirectChat()) {
|
||||
return i18nc("@info:label", "Invited you to chat");
|
||||
}
|
||||
return i18nc("@info:label", "%1 invited you", room->member(room->invitingUserId()).displayName());
|
||||
}
|
||||
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "messagecontentmodel.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
class ReactionModel;
|
||||
|
||||
/**
|
||||
* @class ThreadFetchModel
|
||||
@@ -172,8 +171,6 @@ private:
|
||||
ThreadFetchModel *m_threadFetchModel;
|
||||
ThreadChatBarModel *m_threadChatBarModel;
|
||||
|
||||
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
||||
|
||||
QPointer<Quotient::GetRelatingEventsWithRelTypeJob> m_currentJob = nullptr;
|
||||
std::optional<QString> m_nextBatch = QString();
|
||||
bool m_addingPending = false;
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -303,6 +303,7 @@ Name[ta]=பகிர்
|
||||
Name[tr]=Paylaş
|
||||
Name[uk]=Оприлюднення
|
||||
Name[x-test]=xxSharexx
|
||||
Name[zh_CN]=分享
|
||||
Name[zh_TW]=分享
|
||||
Comment=The result of sharing a piece of content
|
||||
Comment[ar]=نتيجة مشاركة محتوى
|
||||
@@ -336,5 +337,6 @@ Comment[ta]=எதையோ பகிர்ந்ததன் விளைவ
|
||||
Comment[tr]=Bir parça içerik paylaşımının sonucu
|
||||
Comment[uk]=Результат оприлюднення даних
|
||||
Comment[x-test]=xxThe result of sharing a piece of contentxx
|
||||
Comment[zh_CN]=分享一个内容得到的结果
|
||||
Comment[zh_TW]=分享一份內容之後的結果
|
||||
Action=Popup
|
||||
|
||||
@@ -104,6 +104,7 @@
|
||||
</entry>
|
||||
<entry name="ShowLinkPreview" type="bool">
|
||||
<label>Show preview of the links in the chat messages</label>
|
||||
<default>true</default>
|
||||
</entry>
|
||||
<entry name="SystemTray" type="bool">
|
||||
<label>Close NeoChat to system tray</label>
|
||||
|
||||
@@ -145,6 +145,10 @@ void NeoChatConnection::connectSignals()
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::PreferUsingEncryptionChanged, this, [] {
|
||||
setDirectChatEncryptionDefault(NeoChatConfig::preferUsingEncryption());
|
||||
});
|
||||
setGlobalUrlPreviewEnabled(NeoChatConfig::showLinkPreview());
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, [this]() {
|
||||
setGlobalUrlPreviewEnabled(NeoChatConfig::showLinkPreview());
|
||||
});
|
||||
}
|
||||
|
||||
int NeoChatConnection::badgeNotificationCount() const
|
||||
@@ -167,6 +171,25 @@ void NeoChatConnection::refreshBadgeNotificationCount()
|
||||
}
|
||||
}
|
||||
|
||||
bool NeoChatConnection::globalUrlPreviewEnabled()
|
||||
{
|
||||
return m_globalUrlPreviewEnabled;
|
||||
}
|
||||
|
||||
void NeoChatConnection::setGlobalUrlPreviewEnabled(bool newState)
|
||||
{
|
||||
if (m_globalUrlPreviewEnabled == newState) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_globalUrlPreviewEnabled = newState;
|
||||
if (!m_globalUrlPreviewEnabled) {
|
||||
m_linkPreviewers.clear();
|
||||
}
|
||||
NeoChatConfig::setShowLinkPreview(m_globalUrlPreviewEnabled);
|
||||
Q_EMIT globalUrlPreviewEnabledChanged();
|
||||
}
|
||||
|
||||
void NeoChatConnection::logout(bool serverSideLogout)
|
||||
{
|
||||
SettingsGroup(u"Accounts"_s).remove(userId());
|
||||
@@ -472,9 +495,12 @@ QCoro::Task<void> NeoChatConnection::setupPushNotifications(QString endpoint)
|
||||
false);
|
||||
|
||||
qInfo() << "Registered for push notifications";
|
||||
m_pushNotificationsEnabled = true;
|
||||
} else {
|
||||
qWarning() << "There's no gateway, not setting up push notifications.";
|
||||
m_pushNotificationsEnabled = false;
|
||||
}
|
||||
Q_EMIT enablePushNotificationsChanged();
|
||||
#else
|
||||
Q_UNUSED(endpoint)
|
||||
co_return;
|
||||
@@ -502,7 +528,7 @@ QString NeoChatConnection::accountDataJsonString(const QString &type) const
|
||||
|
||||
LinkPreviewer *NeoChatConnection::previewerForLink(const QUrl &link)
|
||||
{
|
||||
if (!NeoChatConfig::showLinkPreview()) {
|
||||
if (!m_globalUrlPreviewEnabled) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -536,4 +562,18 @@ bool NeoChatConnection::canEraseData() const
|
||||
return m_canEraseData;
|
||||
}
|
||||
|
||||
bool NeoChatConnection::pushNotificationsAvailable() const
|
||||
{
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool NeoChatConnection::enablePushNotifications() const
|
||||
{
|
||||
return m_pushNotificationsEnabled;
|
||||
}
|
||||
|
||||
#include "moc_neochatconnection.cpp"
|
||||
|
||||
@@ -32,6 +32,11 @@ class NeoChatConnection : public Quotient::Connection
|
||||
*/
|
||||
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether URL previews are enabled globally.
|
||||
*/
|
||||
Q_PROPERTY(bool globalUrlPreviewEnabled READ globalUrlPreviewEnabled WRITE setGlobalUrlPreviewEnabled NOTIFY globalUrlPreviewEnabledChanged)
|
||||
|
||||
/**
|
||||
* @brief The model with the account's 3PIDs.
|
||||
*/
|
||||
@@ -90,6 +95,16 @@ class NeoChatConnection : public Quotient::Connection
|
||||
*/
|
||||
Q_PROPERTY(bool canEraseData READ canEraseData NOTIFY canEraseDataChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether this build of NeoChat supports push notifications via KUnifiedPush.
|
||||
*/
|
||||
Q_PROPERTY(bool pushNotificationsAvailable READ pushNotificationsAvailable CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief Whether we successfully set up push notifications with the server.
|
||||
*/
|
||||
Q_PROPERTY(bool enablePushNotifications READ enablePushNotifications NOTIFY enablePushNotificationsChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the status after an attempt to change the password on an account.
|
||||
@@ -172,17 +187,24 @@ public:
|
||||
int badgeNotificationCount() const;
|
||||
void refreshBadgeNotificationCount();
|
||||
|
||||
bool globalUrlPreviewEnabled();
|
||||
void setGlobalUrlPreviewEnabled(bool newState);
|
||||
|
||||
bool directChatInvites() const;
|
||||
|
||||
// note: this is intentionally a copied QString because
|
||||
// the reference could be destroyed before the task is finished
|
||||
QCoro::Task<void> setupPushNotifications(QString endpoint);
|
||||
|
||||
bool pushNotificationsAvailable() const;
|
||||
bool enablePushNotifications() const;
|
||||
|
||||
bool isOnline() const;
|
||||
|
||||
LinkPreviewer *previewerForLink(const QUrl &link);
|
||||
|
||||
Q_SIGNALS:
|
||||
void globalUrlPreviewEnabledChanged();
|
||||
void labelChanged();
|
||||
void identityServerChanged();
|
||||
void directChatNotificationsChanged();
|
||||
@@ -196,6 +218,7 @@ Q_SIGNALS:
|
||||
void badgeNotificationCountChanged(NeoChatConnection *connection, int count);
|
||||
void canCheckMutualRoomsChanged();
|
||||
void canEraseDataChanged();
|
||||
void enablePushNotificationsChanged();
|
||||
|
||||
/**
|
||||
* @brief Request a message be shown to the user of the given type.
|
||||
@@ -216,9 +239,11 @@ private:
|
||||
void connectSignals();
|
||||
|
||||
int m_badgeNotificationCount = 0;
|
||||
bool m_globalUrlPreviewEnabled = true;
|
||||
|
||||
QCache<QUrl, LinkPreviewer> m_linkPreviewers;
|
||||
|
||||
bool m_canCheckMutualRooms = false;
|
||||
bool m_canEraseData = false;
|
||||
bool m_pushNotificationsEnabled = false;
|
||||
};
|
||||
|
||||
@@ -40,10 +40,10 @@
|
||||
#include "chatbarcache.h"
|
||||
#include "clipboard.h"
|
||||
#include "eventhandler.h"
|
||||
#include "events/joinrulesevent.h"
|
||||
#include "events/pollevent.h"
|
||||
#include "filetransferpseudojob.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroommember.h"
|
||||
#include "roomlastmessageprovider.h"
|
||||
#include "spacehierarchycache.h"
|
||||
@@ -133,7 +133,6 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
||||
Q_EMIT canEncryptRoomChanged();
|
||||
Q_EMIT parentIdsChanged();
|
||||
Q_EMIT canonicalParentChanged();
|
||||
Q_EMIT joinRuleChanged();
|
||||
Q_EMIT readOnlyChanged();
|
||||
});
|
||||
connect(connection, &Connection::capabilitiesLoaded, this, &NeoChatRoom::maxRoomVersionChanged);
|
||||
@@ -157,6 +156,10 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
||||
Q_EMIT childrenHaveHighlightNotificationsChanged();
|
||||
}
|
||||
});
|
||||
|
||||
const auto neochatconnection = static_cast<NeoChatConnection *>(connection);
|
||||
Q_ASSERT(neochatconnection);
|
||||
connect(neochatconnection, &NeoChatConnection::globalUrlPreviewEnabledChanged, this, &NeoChatRoom::urlPreviewEnabledChanged);
|
||||
}
|
||||
|
||||
bool NeoChatRoom::visible() const
|
||||
@@ -605,63 +608,12 @@ void NeoChatRoom::deleteMessagesByUser(const QString &user, const QString &reaso
|
||||
doDeleteMessagesByUser(user, reason);
|
||||
}
|
||||
|
||||
QString NeoChatRoom::joinRule() const
|
||||
{
|
||||
auto joinRulesEvent = currentState().get<JoinRulesEvent>();
|
||||
if (!joinRulesEvent) {
|
||||
return {};
|
||||
}
|
||||
return joinRulesEvent->joinRule();
|
||||
}
|
||||
|
||||
void NeoChatRoom::setJoinRule(const QString &joinRule, const QList<QString> &allowedSpaces)
|
||||
{
|
||||
if (!canSendState("m.room.join_rules"_L1)) {
|
||||
qWarning() << "Power level too low to set join rules";
|
||||
return;
|
||||
}
|
||||
auto actualRule = joinRule;
|
||||
if (joinRule == "restricted"_L1 && allowedSpaces.isEmpty()) {
|
||||
actualRule = "private"_L1;
|
||||
}
|
||||
|
||||
QJsonArray allowConditions;
|
||||
if (actualRule == "restricted"_L1) {
|
||||
for (auto allowedSpace : allowedSpaces) {
|
||||
allowConditions += QJsonObject{{"type"_L1, "m.room_membership"_L1}, {"room_id"_L1, allowedSpace}};
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject content;
|
||||
content.insert("join_rule"_L1, joinRule);
|
||||
if (!allowConditions.isEmpty()) {
|
||||
content.insert("allow"_L1, allowConditions);
|
||||
}
|
||||
qWarning() << content;
|
||||
setState("m.room.join_rules"_L1, {}, content);
|
||||
// Not emitting joinRuleChanged() here, since that would override the change in the UI with the *current* value, which is not the *new* value.
|
||||
}
|
||||
|
||||
QList<QString> NeoChatRoom::restrictedIds() const
|
||||
{
|
||||
auto joinRulesEvent = currentState().get<JoinRulesEvent>();
|
||||
if (!joinRulesEvent) {
|
||||
return {};
|
||||
}
|
||||
if (joinRulesEvent->joinRule() != "restricted"_L1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QList<QString> roomIds;
|
||||
for (auto allow : joinRulesEvent->allow()) {
|
||||
roomIds += allow.toObject().value("room_id"_L1).toString();
|
||||
}
|
||||
return roomIds;
|
||||
}
|
||||
|
||||
QString NeoChatRoom::historyVisibility() const
|
||||
{
|
||||
return currentState().get("m.room.history_visibility"_L1)->contentJson()["history_visibility"_L1].toString();
|
||||
if (auto stateEvent = currentState().get("m.room.history_visibility"_L1)) {
|
||||
return stateEvent->contentJson()["history_visibility"_L1].toString();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void NeoChatRoom::setHistoryVisibility(const QString &historyVisibilityRule)
|
||||
@@ -728,6 +680,9 @@ void NeoChatRoom::setDefaultUrlPreviewState(const bool &defaultUrlPreviewState)
|
||||
|
||||
bool NeoChatRoom::urlPreviewEnabled() const
|
||||
{
|
||||
if (!static_cast<NeoChatConnection *>(connection())->globalUrlPreviewEnabled()) {
|
||||
return false;
|
||||
}
|
||||
if (hasAccountData("org.matrix.room.preview_urls"_L1)) {
|
||||
return !accountData("org.matrix.room.preview_urls"_L1)->contentJson()["disable"_L1].toBool();
|
||||
} else {
|
||||
@@ -1767,7 +1722,9 @@ MessageContentModel *NeoChatRoom::contentModelForEvent(const Quotient::RoomEvent
|
||||
auto eventId = event->id();
|
||||
const auto txnId = event->transactionId();
|
||||
if (!m_eventContentModels.contains(eventId) && !m_eventContentModels.contains(txnId)) {
|
||||
return m_eventContentModels.emplace(eventId, std::make_unique<MessageContentModel>(this, eventId.isEmpty() ? txnId : eventId, false, eventId.isEmpty()))
|
||||
return m_eventContentModels
|
||||
.emplace(eventId.isEmpty() ? txnId : eventId,
|
||||
std::make_unique<MessageContentModel>(this, eventId.isEmpty() ? txnId : eventId, false, eventId.isEmpty()))
|
||||
.first->second.get();
|
||||
}
|
||||
|
||||
@@ -1802,4 +1759,23 @@ ThreadModel *NeoChatRoom::modelForThread(const QString &threadRootId)
|
||||
return m_threadModels[threadRootId].get();
|
||||
}
|
||||
|
||||
void NeoChatRoom::pinEvent(const QString &eventId)
|
||||
{
|
||||
auto eventIds = pinnedEventIds();
|
||||
eventIds.push_back(eventId);
|
||||
setPinnedEvents(eventIds);
|
||||
}
|
||||
|
||||
void NeoChatRoom::unpinEvent(const QString &eventId)
|
||||
{
|
||||
auto eventIds = pinnedEventIds();
|
||||
eventIds.removeAll(eventId);
|
||||
setPinnedEvents(eventIds);
|
||||
}
|
||||
|
||||
bool NeoChatRoom::isEventPinned(const QString &eventId) const
|
||||
{
|
||||
return pinnedEventIds().contains(eventId);
|
||||
}
|
||||
|
||||
#include "moc_neochatroom.cpp"
|
||||
|
||||
@@ -134,22 +134,6 @@ class NeoChatRoom : public Quotient::Room
|
||||
*/
|
||||
Q_PROPERTY(bool readOnly READ readOnly NOTIFY readOnlyChanged)
|
||||
|
||||
/**
|
||||
* @brief The current join rule for the room as a QString.
|
||||
*
|
||||
* Possible values are [public, knock, invite, private, restricted].
|
||||
*
|
||||
* @sa https://spec.matrix.org/v1.5/client-server-api/#mroomjoin_rules
|
||||
*/
|
||||
Q_PROPERTY(QString joinRule READ joinRule WRITE setJoinRule NOTIFY joinRuleChanged)
|
||||
|
||||
/**
|
||||
* @brief The space IDs that members of can join this room.
|
||||
*
|
||||
* Empty if the join rule is not restricted.
|
||||
*/
|
||||
Q_PROPERTY(QList<QString> restrictedIds READ restrictedIds NOTIFY joinRuleChanged)
|
||||
|
||||
/**
|
||||
* @brief Get the maximum room version that the server supports.
|
||||
*
|
||||
@@ -420,25 +404,6 @@ public:
|
||||
|
||||
bool readOnly() const;
|
||||
|
||||
[[nodiscard]] QString joinRule() const;
|
||||
|
||||
/**
|
||||
* @brief Set the join rule for the room.
|
||||
*
|
||||
* Will fail if the user doesn't have the required privileges.
|
||||
*
|
||||
* @param joinRule the join rule [public, knock, invite, private, restricted].
|
||||
* @param allowedSpaces only used when the join rule is restricted. This is a
|
||||
* list of space Matrix IDs that members of can join without an invite.
|
||||
* If the rule is restricted and this list is empty it is treated as a join
|
||||
* rule of private instead.
|
||||
*
|
||||
* @sa https://spec.matrix.org/latest/client-server-api/#mroomjoin_rules
|
||||
*/
|
||||
Q_INVOKABLE void setJoinRule(const QString &joinRule, const QList<QString> &allowedSpaces = {});
|
||||
|
||||
QList<QString> restrictedIds() const;
|
||||
|
||||
int maxRoomVersion() const;
|
||||
|
||||
/**
|
||||
@@ -636,6 +601,23 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE ThreadModel *modelForThread(const QString &threadRootId);
|
||||
|
||||
/**
|
||||
* @brief Pin a message in the room.
|
||||
* @param eventId The id of the event to pin.
|
||||
*/
|
||||
Q_INVOKABLE void pinEvent(const QString &eventId);
|
||||
|
||||
/**
|
||||
* @brief Unpin a message in the room.
|
||||
* @param eventId The id of the event to unpin.
|
||||
*/
|
||||
Q_INVOKABLE void unpinEvent(const QString &eventId);
|
||||
|
||||
/**
|
||||
* @return True if @p eventId is pinned in the room.
|
||||
*/
|
||||
Q_INVOKABLE bool isEventPinned(const QString &eventId) const;
|
||||
|
||||
private:
|
||||
bool m_visible = false;
|
||||
|
||||
@@ -693,7 +675,6 @@ Q_SIGNALS:
|
||||
void displayNameChanged();
|
||||
void pushNotificationStateChanged(PushNotificationState::State state);
|
||||
void canEncryptRoomChanged();
|
||||
void joinRuleChanged();
|
||||
void historyVisibilityChanged();
|
||||
void defaultUrlPreviewStateChanged();
|
||||
void urlPreviewEnabledChanged();
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"Name[tr]": "Tobias Fella",
|
||||
"Name[uk]": "Tobias Fella",
|
||||
"Name[x-test]": "xxTobias Fellaxx",
|
||||
"Name[zh_CN]": "Tobias Fella",
|
||||
"Name[zh_TW]": "Tobias Fella"
|
||||
}
|
||||
],
|
||||
@@ -70,6 +71,7 @@
|
||||
"Description[tr]": "NeoChat ile Paylaş",
|
||||
"Description[uk]": "Оприлюднити за допомогою NeoChat",
|
||||
"Description[x-test]": "xxShare via NeoChatxx",
|
||||
"Description[zh_CN]": "通过 NeoChat 分享",
|
||||
"Description[zh_TW]": "透過 NeoChat 分享",
|
||||
"Icon": "org.kde.neochat.tray",
|
||||
"License": "GPL",
|
||||
@@ -107,6 +109,7 @@
|
||||
"Name[tr]": "NeoChat",
|
||||
"Name[uk]": "NeoChat",
|
||||
"Name[x-test]": "xxNeoChatxx",
|
||||
"Name[zh_CN]": "NeoChat",
|
||||
"Name[zh_TW]": "NeoChat",
|
||||
"X-Purpose-ActionDisplay": "NeoChat"
|
||||
},
|
||||
|
||||
@@ -87,25 +87,27 @@ KirigamiComponents.ConvergentContextMenu {
|
||||
QQC2.Action {
|
||||
text: i18nc("@action:inmenu", "Verify This Device")
|
||||
icon.name: "security-low"
|
||||
onTriggered: root.connection.startSelfVerification()
|
||||
onTriggered: {
|
||||
root.connection.startSelfVerification();
|
||||
const dialog = Qt.createComponent("org.kde.kirigami", "PromptDialog").createObject(QQC2.Overlay.overlay, {
|
||||
title: i18nc("@title", "Verification Request Sent"),
|
||||
subtitle: i18nc("@info:label", "To proceed, accept the verification request on another device."),
|
||||
standardButtons: Kirigami.Dialog.Ok
|
||||
})
|
||||
dialog.open();
|
||||
root.connection.onNewKeyVerificationSession.connect(() => {
|
||||
dialog.close();
|
||||
});
|
||||
}
|
||||
enabled: Controller.csSupported
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
text: i18nc("@action:inmenu", "Do Sentry Things")
|
||||
onTriggered: root.window.pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "SendLogsDialog"), {}, {
|
||||
title: i18nc("@title:window", "Send Logs")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
QQC2.Action {
|
||||
text: i18n("Logout")
|
||||
icon.name: "im-kick-user"
|
||||
onTriggered: confirmLogoutDialogComponent.createObject(QQC2.ApplicationWindow.window.overlay).open()
|
||||
}
|
||||
|
||||
|
||||
readonly property Component confirmLogoutDialogComponent: ConfirmLogoutDialog {
|
||||
connection: root.connection
|
||||
}
|
||||
|
||||
@@ -34,9 +34,8 @@ Delegates.RoundedItemDelegate {
|
||||
Keys.onSpacePressed: selected()
|
||||
Keys.onEnterPressed: selected()
|
||||
|
||||
onPressAndHold: root.contextMenuRequested()
|
||||
|
||||
TapHandler {
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus
|
||||
acceptedButtons: Qt.RightButton | Qt.LeftButton
|
||||
onTapped: (eventPoint, button) => {
|
||||
if (button === Qt.RightButton) {
|
||||
@@ -47,6 +46,11 @@ Delegates.RoundedItemDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedDevices: PointerDevice.TouchScreen
|
||||
onLongPressed: root.contextMenuRequested()
|
||||
}
|
||||
|
||||
contentItem: AvatarNotification {
|
||||
id: avatarNotification
|
||||
source: root.source
|
||||
|
||||
@@ -75,7 +75,7 @@ KirigamiComponents.ConvergentContextMenu {
|
||||
|
||||
component RemoveMessageAction: Kirigami.Action {
|
||||
visible: author.isLocalMember || currentRoom.canSendState("redact")
|
||||
text: i18nc("@action:button", "Remove")
|
||||
text: i18nc("@action:button", "Remove…")
|
||||
icon.name: "edit-delete-remove"
|
||||
icon.color: "red"
|
||||
onTriggered: {
|
||||
@@ -132,6 +132,15 @@ KirigamiComponents.ConvergentContextMenu {
|
||||
}
|
||||
}
|
||||
|
||||
component PinMessageAction: Kirigami.Action {
|
||||
readonly property bool pinned: currentRoom.isEventPinned(root.eventId)
|
||||
|
||||
visible: currentRoom.canSendState("m.room.pinned_events")
|
||||
text: pinned ? i18nc("@action:button 'Unpin' as in 'Unpin this message'", "Unpin") : i18nc("@action:button 'Pin' as in 'Pin the message in the room'", "Pin")
|
||||
icon.name: pinned ? "window-unpin-symbolic" : "pin-symbolic"
|
||||
onTriggered: pinned ? currentRoom.unpinEvent(root.eventId) : currentRoom.pinEvent(root.eventId)
|
||||
}
|
||||
|
||||
headerContentItem: RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
|
||||
@@ -78,21 +78,6 @@ RowLayout {
|
||||
Component {
|
||||
id: menu
|
||||
QQC2.Menu {
|
||||
QQC2.MenuItem {
|
||||
text: i18n("Explore rooms")
|
||||
icon.name: "compass"
|
||||
onTriggered: {
|
||||
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18nc("@title", "Explore Rooms")
|
||||
});
|
||||
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
RoomManager.resolveResource(roomId.length > 0 ? roomId : alias, isJoined ? "" : "join");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.MenuItem {
|
||||
text: i18n("Find your friends")
|
||||
icon.name: "list-add-user"
|
||||
|
||||
@@ -71,7 +71,7 @@ DelegateContextMenu {
|
||||
|
||||
Kirigami.Action {
|
||||
visible: author.id === currentRoom.localMember.id || currentRoom.canSendState("redact")
|
||||
text: i18n("Remove")
|
||||
text: i18n("Remove…")
|
||||
icon.name: "edit-delete-remove"
|
||||
icon.color: "red"
|
||||
onTriggered: {
|
||||
@@ -90,6 +90,8 @@ DelegateContextMenu {
|
||||
}
|
||||
}
|
||||
|
||||
DelegateContextMenu.PinMessageAction {}
|
||||
|
||||
DelegateContextMenu.ReportMessageAction {}
|
||||
|
||||
DelegateContextMenu.ShowUserAction {}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
@@ -6,22 +7,89 @@ import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.components as KirigamiComponents
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property NeoChatRoom currentRoom
|
||||
readonly property var invitingMember: currentRoom.member(currentRoom.invitingUserId())
|
||||
|
||||
text: i18n("Accept this invitation?")
|
||||
explanation: root.currentRoom.connection.canCheckMutualRooms ? i18n("You can reject invitations from unknown users under Security settings.") : ""
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
KirigamiComponents.Avatar {
|
||||
id: avatar
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
name: root.invitingMember.displayName
|
||||
source: root.invitingMember.avatarUrl
|
||||
color: root.invitingMember.color
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: !root.currentRoom.isDirectChat()
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
QQC2.Label {
|
||||
text: i18nc("@info:label", "%1 has invited you to join", root.invitingMember.displayName)
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
text: root.currentRoom.displayName
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: root.currentRoom.isDirectChat()
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Kirigami.Heading {
|
||||
text: root.currentRoom.displayName
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
text: i18nc("@info:label", "This user is inviting you to chat.")
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
text: i18n("You can reject invitations from unknown users under Security settings.")
|
||||
visible: root.currentRoom.connection.canCheckMutualRooms
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: Kirigami.Units.mediumSpacing
|
||||
|
||||
QQC2.Button {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: i18nc("@action:button The thing being rejected is an invitation to chat", "Reject and ignore user")
|
||||
text: i18nc("@action:button The thing being rejected is an invitation to chat", "Reject and Ignore User")
|
||||
icon.name: "list-remove-symbolic"
|
||||
|
||||
onClicked: {
|
||||
RoomManager.leaveRoom(root.currentRoom);
|
||||
@@ -30,18 +98,18 @@ Kirigami.PlaceholderMessage {
|
||||
}
|
||||
QQC2.Button {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: i18n("Reject")
|
||||
icon.name: "cards-block-symbolic"
|
||||
text: i18nc("@action:button", "Reject")
|
||||
|
||||
onClicked: RoomManager.leaveRoom(root.currentRoom)
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: i18n("Accept")
|
||||
icon.name: "dialog-ok-symbolic"
|
||||
text: i18nc("@action:button", "Accept")
|
||||
|
||||
onClicked: {
|
||||
root.currentRoom.acceptInvitation();
|
||||
}
|
||||
onClicked: root.currentRoom.acceptInvitation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ DelegateContextMenu {
|
||||
}
|
||||
QQC2.Action {
|
||||
text: i18nc("@action:inmenu", "Copy Message Link")
|
||||
icon.name: "edit-copy"
|
||||
icon.name: "link-symbolic"
|
||||
onTriggered: {
|
||||
Clipboard.saveText("https://matrix.to/#/" + currentRoom.id + "/" + root.eventId);
|
||||
}
|
||||
@@ -86,6 +86,7 @@ DelegateContextMenu {
|
||||
Kirigami.Action {
|
||||
separator: true
|
||||
}
|
||||
DelegateContextMenu.PinMessageAction {}
|
||||
DelegateContextMenu.ReportMessageAction {}
|
||||
DelegateContextMenu.ShowUserAction {}
|
||||
Kirigami.Action {
|
||||
|
||||
@@ -42,8 +42,6 @@ Delegates.RoundedItemDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
onPressAndHold: createRoomListContextMenu()
|
||||
|
||||
Keys.onSpacePressed: clicked()
|
||||
Keys.onEnterPressed: clicked()
|
||||
Keys.onReturnPressed: clicked()
|
||||
@@ -54,6 +52,11 @@ Delegates.RoundedItemDelegate {
|
||||
onTapped: (eventPoint, button) => root.createRoomListContextMenu()
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedDevices: PointerDevice.TouchScreen
|
||||
onLongPressed: root.createRoomListContextMenu()
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.kirigamiaddons.labs.components as Components
|
||||
|
||||
import Quotient
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Kirigami.Dialog {
|
||||
@@ -42,7 +44,7 @@ Kirigami.Dialog {
|
||||
ids.push(spaceGroup.buttons[i].modelData.id);
|
||||
}
|
||||
}
|
||||
root.room.setJoinRule("restricted", ids);
|
||||
root.room.setJoinRule(JoinRule.Restricted, ids);
|
||||
}
|
||||
|
||||
QQC2.ButtonGroup {
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
title: i18nc("@title:dialog", "Upload Logs")
|
||||
|
||||
FormCard.FormTextDelegate {
|
||||
text: i18nc("@info", "Uploading NeoChat's logs can help with finding bugs in the app. After uploading the logs, a token will be shown which identifies the file you have uploaded. Please add this token when creating a bug report for your problem. Uploaded logs are only accessible to KDE developers and will never contain secret data.");
|
||||
textItem.wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
FormCard.FormTextAreaDelegate {
|
||||
id: description
|
||||
label: i18nc("@label", "Description")
|
||||
placeholderText: i18nc("@info:placeholder", "Things are not working")
|
||||
}
|
||||
|
||||
QQC2.DialogButtonBox {
|
||||
QQC2.Button {
|
||||
text: i18nc("@action:button", "Upload")
|
||||
//TODO icon
|
||||
}
|
||||
standardButtons: QQC2.DialogButtonBox.Cancel
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,8 @@ QQC2.Control {
|
||||
onSelected: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'NotificationsView'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18nc("@title", "Notifications")
|
||||
title: i18nc("@title", "Notifications"),
|
||||
modality: Qt.NonModal
|
||||
})
|
||||
}
|
||||
|
||||
@@ -274,6 +275,31 @@ QQC2.Control {
|
||||
title: i18nc("@title", "Create a Space")
|
||||
})
|
||||
}
|
||||
|
||||
AvatarTabButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: width - Kirigami.Units.smallSpacing
|
||||
Layout.maximumHeight: width - Kirigami.Units.smallSpacing
|
||||
|
||||
text: i18nc("@action:button", "Explore rooms")
|
||||
contentItem: Kirigami.Icon {
|
||||
source: "compass"
|
||||
}
|
||||
|
||||
activeFocusOnTab: true
|
||||
|
||||
onSelected: {
|
||||
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), {
|
||||
connection: root.connection,
|
||||
keyword: RoomManager.sortFilterRoomTreeModel.filterText
|
||||
}, {
|
||||
title: i18nc("@title", "Explore Rooms")
|
||||
});
|
||||
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
RoomManager.resolveResource(roomId.length > 0 ? roomId : alias, isJoined ? "" : "join");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +116,16 @@ Kirigami.Dialog {
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Chip {
|
||||
visible: root.room
|
||||
text: root.room ? QmlUtils.nameForPowerLevelValue(root.room.memberEffectivePowerLevel(root.user.id)) : ""
|
||||
closable: false
|
||||
checkable: false
|
||||
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ RowLayout {
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.minimumHeight: bottomEdge ? Kirigami.Units.gridUnit * 3 : -1
|
||||
|
||||
onVisibleChanged: {
|
||||
@@ -34,59 +35,74 @@ RowLayout {
|
||||
accountsPopup.close();
|
||||
}
|
||||
}
|
||||
KirigamiComponents.AvatarButton {
|
||||
|
||||
QQC2.ToolButton {
|
||||
id: accountButton
|
||||
readonly property url avatarUrl: root.connection.localUser.avatarUrl
|
||||
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
down: accountMenu.opened || pressed
|
||||
|
||||
text: i18n("Edit this account")
|
||||
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
|
||||
source: avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(avatarUrl) : ""
|
||||
name: root.connection.localUser.displayName
|
||||
onClicked: accountMenu.popup()
|
||||
|
||||
activeFocusOnTab: true
|
||||
|
||||
onClicked: {
|
||||
NeoChatSettingsView.open("accounts")
|
||||
}
|
||||
|
||||
onPressAndHold: accountMenu.popup();
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: accountMenu.popup()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: Math.round(root.width * 0.55)
|
||||
visible: !root.collapsed
|
||||
spacing: 0
|
||||
QQC2.Label {
|
||||
id: displayNameLabel
|
||||
Layout.fillWidth: true
|
||||
text: root.connection.localUser.displayName
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
Layout.fillHeight: true
|
||||
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.text: i18nc("@info:tooltip", "Manage Account")
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
KirigamiComponents.Avatar {
|
||||
readonly property url avatarUrl: root.connection.localUser.avatarUrl
|
||||
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
|
||||
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
|
||||
source: avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(avatarUrl) : ""
|
||||
name: root.connection.localUser.displayName
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: Math.round(root.width * 0.55)
|
||||
visible: !root.collapsed
|
||||
spacing: 0
|
||||
QQC2.Label {
|
||||
id: displayNameLabel
|
||||
Layout.fillWidth: true
|
||||
text: root.connection.localUser.displayName
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
QQC2.Label {
|
||||
id: idLabel
|
||||
Layout.fillWidth: true
|
||||
text: (root.connection.label.length > 0 ? (root.connection.label + " ") : "") + root.connection.localUser.id
|
||||
font.pointSize: displayNameLabel.font.pointSize * 0.8
|
||||
opacity: 0.7
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
QQC2.Label {
|
||||
id: idLabel
|
||||
Layout.fillWidth: true
|
||||
text: (root.connection.label.length > 0 ? (root.connection.label + " ") : "") + root.connection.localUser.id
|
||||
font.pointSize: displayNameLabel.font.pointSize * 0.8
|
||||
opacity: 0.7
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
|
||||
AccountMenu {
|
||||
id: accountMenu
|
||||
connection: root.connection
|
||||
window: QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.ActionToolBar {
|
||||
alignment: Qt.AlignRight
|
||||
display: QQC2.Button.IconOnly
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: maximumContentWidth
|
||||
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
text: i18n("Switch User")
|
||||
@@ -106,12 +122,6 @@ RowLayout {
|
||||
]
|
||||
}
|
||||
|
||||
AccountMenu {
|
||||
id: accountMenu
|
||||
y: root.bottomEdge ? -height : accountButton.height
|
||||
connection: root.connection
|
||||
window: accountButton.QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow
|
||||
}
|
||||
Component {
|
||||
id: accountSwitchDialog
|
||||
AccountSwitchDialog {}
|
||||
|
||||
@@ -324,7 +324,15 @@ void RoomManager::visitRoom(Room *r, const QString &eventId)
|
||||
|
||||
void RoomManager::joinRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QStringList &viaServers)
|
||||
{
|
||||
auto job = account->joinRoom(roomAliasOrId, viaServers);
|
||||
QStringList vias = viaServers;
|
||||
|
||||
// If no one gives us a homeserver suggestion, try the server specified in the alias/id.
|
||||
// Otherwise joining a remote room not on our homeserver will fail.
|
||||
if (vias.empty()) {
|
||||
vias.append(roomAliasOrId.mid(roomAliasOrId.lastIndexOf(':'_L1) + 1));
|
||||
}
|
||||
|
||||
auto job = account->joinRoom(roomAliasOrId, vias);
|
||||
connect(
|
||||
job.get(),
|
||||
&Quotient::BaseJob::finished,
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "sentryintegration.h"
|
||||
|
||||
#include <sentry.h>
|
||||
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
Sentry &Sentry::instance()
|
||||
{
|
||||
static Sentry _instance;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
Sentry::Sentry()
|
||||
: QObject()
|
||||
{
|
||||
QString dsn = QStringLiteral("https://%1@crash-reports.kde.org/%2").arg("296a68fe1cf24ee79fafad735365d8d6"_L1, "18"_L1);
|
||||
auto options = sentry_options_new();
|
||||
sentry_options_set_dsn(options, dsn.toLatin1().data());
|
||||
sentry_options_set_release(options, "neochat@TESTING");
|
||||
sentry_options_set_debug(options, false);
|
||||
sentry_options_add_attachment(options, "/home/tobias/.local/share/KDE/neochat/neochat.log.0");
|
||||
sentry_init(options);
|
||||
}
|
||||
|
||||
void Sentry::sendLogs()
|
||||
{
|
||||
auto event = sentry_value_new_message_event(SENTRY_LEVEL_INFO, "custom", "It works!");
|
||||
auto uuid = sentry_capture_event(event);
|
||||
auto str = (char *)malloc(37);
|
||||
sentry_uuid_as_string(&uuid, str);
|
||||
qWarning() << "sent logs" << str;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
class Sentry : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
public:
|
||||
static Sentry &instance();
|
||||
static Sentry *create(QQmlEngine *engine, QJSEngine *)
|
||||
{
|
||||
engine->setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
|
||||
return &instance();
|
||||
}
|
||||
|
||||
Q_INVOKABLE void sendLogs();
|
||||
|
||||
private:
|
||||
Sentry();
|
||||
};
|
||||
@@ -117,7 +117,7 @@ FormCard.FormCardPage {
|
||||
id: addAccountDelegate
|
||||
text: i18n("Add Account")
|
||||
icon.name: "list-add"
|
||||
onClicked: root.QQC2.ApplicationWindow.window.pageStack.layers.push(Qt.createComponent('org.kde.neochat.login', 'WelcomePage'))
|
||||
onClicked: root.QQC2.ApplicationWindow.window.pageStack.layers.push(Qt.createComponent('org.kde.neochat.login', 'WelcomePage'), { showSettings: false })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,17 @@ FormCard.FormCardPage {
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing * 4
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18n("Enable notifications for this account")
|
||||
description: i18n("Whether push notifications are generated by your Matrix server")
|
||||
description: {
|
||||
if (connection.pushNotificationsAvailable) {
|
||||
if (connection.enablePushNotifications) {
|
||||
return i18n("Notifications can appear even when NeoChat isn't running.");
|
||||
} else {
|
||||
return i18n("Push notifications are available but could not be enabled.");
|
||||
}
|
||||
} else {
|
||||
return i18n("Notifications will only appear when NeoChat is running.");
|
||||
}
|
||||
}
|
||||
checked: root.pushRuleModel.globalNotificationsEnabled
|
||||
enabled: root.pushRuleModel.globalNotificationsSet
|
||||
onToggled: {
|
||||
|
||||
@@ -267,6 +267,7 @@ FormCard.FormCardPage {
|
||||
}
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
enabled: root.connection.globalUrlPreviewEnabled
|
||||
text: i18n("Enable URL previews")
|
||||
// Most users won't see the above setting so tell them the default.
|
||||
description: room.defaultUrlPreviewState ? i18n("URL previews are enabled by default in this room") : i18n("URL previews are disabled by default in this room")
|
||||
@@ -276,6 +277,19 @@ FormCard.FormCardPage {
|
||||
}
|
||||
}
|
||||
}
|
||||
Kirigami.InlineMessage {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: Kirigami.Units.gridUnit * 30
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: i18nc("As in the user has switched off showing previews of hyperlinks in timeline messages", "URL previews are currently disabled for your account")
|
||||
type: Kirigami.MessageType.Information
|
||||
visible: !root.connection.globalUrlPreviewEnabled
|
||||
actions: Kirigami.Action {
|
||||
text: i18n("Enable")
|
||||
onTriggered: root.connection.globalUrlPreviewEnabled = true
|
||||
}
|
||||
}
|
||||
FormCard.FormHeader {
|
||||
title: i18n("Official Parent Spaces")
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import Quotient
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
@@ -45,23 +47,23 @@ FormCard.FormCardPage {
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("@option:check", "Private (invite only)")
|
||||
description: i18n("Only invited people can join.")
|
||||
checked: room.joinRule === "invite"
|
||||
checked: room.joinRule === JoinRule.Invite
|
||||
enabled: room.canSendState("m.room.join_rules")
|
||||
onCheckedChanged: if (checked && room.joinRule != "invite") {
|
||||
root.room.joinRule = "invite";
|
||||
onCheckedChanged: if (checked && room.joinRule != JoinRule.Invite) {
|
||||
root.room.joinRule = JoinRule.Invite;
|
||||
}
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("@option:check", "Space members")
|
||||
description: i18n("Anyone in the selected spaces can find and join.") + (!["8", "9", "10"].includes(room.version) ? `\n${needUpgradeRoom}` : "")
|
||||
checked: room.joinRule === "restricted"
|
||||
checked: room.joinRule === JoinRule.Restricted
|
||||
enabled: room.canSendState("m.room.join_rules") && ["8", "9", "10"].includes(room.version)
|
||||
onCheckedChanged: if (checked && room.joinRule != "restricted") {
|
||||
onCheckedChanged: if (checked && room.joinRule != JoinRule.Restricted) {
|
||||
selectSpacesDialog.createObject(QQC2.Overlay.overlay).open();
|
||||
}
|
||||
|
||||
contentItem.children: QQC2.Button {
|
||||
visible: root.room.joinRule === "restricted"
|
||||
visible: root.room.joinRule === JoinRule.Restricted
|
||||
text: i18n("Select spaces")
|
||||
icon.name: "list-add"
|
||||
|
||||
@@ -82,20 +84,20 @@ FormCard.FormCardPage {
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("@option:check", "Knock")
|
||||
description: i18n("People not in the room need to request an invite to join the room.") + (!["7", "8", "9", "10"].includes(room.version) ? `\n${needUpgradeRoom}` : "")
|
||||
checked: room.joinRule === "knock"
|
||||
checked: room.joinRule === JoinRule.Knock
|
||||
// https://spec.matrix.org/v1.4/rooms/#feature-matrix
|
||||
enabled: room.canSendState("m.room.join_rules") && ["7", "8", "9", "10"].includes(room.version)
|
||||
onCheckedChanged: if (checked && room.joinRule != "knock") {
|
||||
root.room.joinRule = "knock";
|
||||
onCheckedChanged: if (checked && room.joinRule != JoinRule.Knock) {
|
||||
root.room.joinRule = JoinRule.Knock;
|
||||
}
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("@option:check", "Public")
|
||||
description: i18nc("@option:check", "Anyone can find and join.")
|
||||
checked: room.joinRule === "public"
|
||||
checked: room.joinRule === JoinRule.Public
|
||||
enabled: room.canSendState("m.room.join_rules")
|
||||
onCheckedChanged: if (checked && root.room.joinRule != "public") {
|
||||
root.room.joinRule = "public";
|
||||
onCheckedChanged: if (checked && root.room.joinRule != JoinRule.Public) {
|
||||
root.room.joinRule = JoinRule.Public;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,6 +193,14 @@ DelegateChooser {
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MessageComponentType.Reaction
|
||||
delegate: ReactionComponent {
|
||||
room: root.room
|
||||
maxContentWidth: root.maxContentWidth
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MessageComponentType.LinkPreview
|
||||
delegate: LinkPreviewComponent {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user