Compare commits
96 Commits
work/redst
...
work/thiag
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edee51a08a | ||
|
|
2a8cd74ab1 | ||
|
|
63bc7055c2 | ||
|
|
1cca9733d6 | ||
|
|
1104da5e2c | ||
|
|
3a9718c09d | ||
|
|
55362c5573 | ||
|
|
0bba2299b3 | ||
|
|
45685af9e9 | ||
|
|
6c416a9338 | ||
|
|
1b0027e1d2 | ||
|
|
2409adf516 | ||
|
|
554801dfe4 | ||
|
|
20c23917e9 | ||
|
|
ef953b7574 | ||
|
|
6b79795229 | ||
|
|
9cb7ec2348 | ||
|
|
437c981d30 | ||
|
|
0334cae4c8 | ||
|
|
24c405d747 | ||
|
|
a3f5962809 | ||
|
|
0deb7495f0 | ||
|
|
d34f89fc4b | ||
|
|
a909ed498f | ||
|
|
16f4e17e8f | ||
|
|
0e9592a96c | ||
|
|
704ee6a53a | ||
|
|
5b9afbce9a | ||
|
|
396cc8e8ef | ||
|
|
bf776b5c06 | ||
|
|
be319f88d3 | ||
|
|
af40d555d4 | ||
|
|
f802dbe686 | ||
|
|
2379e3d83b | ||
|
|
9e90ac0412 | ||
|
|
c27948ca3c | ||
|
|
c3b9d664df | ||
|
|
31ef0a5223 | ||
|
|
14c58acea1 | ||
|
|
5dae20603e | ||
|
|
3f6fa94289 | ||
|
|
117615a8b0 | ||
|
|
4a52773c7d | ||
|
|
edfee495c6 | ||
|
|
7d112df7c6 | ||
|
|
9acaaade45 | ||
|
|
aaca28dbf6 | ||
|
|
d4ef5f9d4d | ||
|
|
2095dea801 | ||
|
|
a36f7ef10d | ||
|
|
9874962ee3 | ||
|
|
4b08022075 | ||
|
|
dc3db3aec4 | ||
|
|
0568c2a93d | ||
|
|
7ab0a6fc9e | ||
|
|
d6b780762e | ||
|
|
5ef66b5cf6 | ||
|
|
19e8cd5e48 | ||
|
|
df5117892f | ||
|
|
aaa4216f55 | ||
|
|
85ee5084b6 | ||
|
|
bb9ce117de | ||
|
|
00c5aa26bb | ||
|
|
bae4de227c | ||
|
|
253f891c5a | ||
|
|
6966159062 | ||
|
|
07d3b80c3e | ||
|
|
a41d0f3214 | ||
|
|
1ee15de78b | ||
|
|
b044358970 | ||
|
|
d2e11bb3bb | ||
|
|
a55bac899c | ||
|
|
c2380fb8df | ||
|
|
f31c644b13 | ||
|
|
26cd621d0e | ||
|
|
4c58512c54 | ||
|
|
04c1b47660 | ||
|
|
7b249e9fa6 | ||
|
|
46593ef68f | ||
|
|
b70f73c7d6 | ||
|
|
ece5e34fa2 | ||
|
|
74b400288d | ||
|
|
83f19b0631 | ||
|
|
c905d2d6fb | ||
|
|
d0c1eb2f04 | ||
|
|
d7d9d29c1d | ||
|
|
2a3f019ec6 | ||
|
|
d384d50b0d | ||
|
|
be8cb12bba | ||
|
|
a1aa2918be | ||
|
|
d7536bccb3 | ||
|
|
6b677355e1 | ||
|
|
85d625f6ac | ||
|
|
f5d6f87afe | ||
|
|
31d755f407 | ||
|
|
ebfc20d4b4 |
@@ -110,7 +110,7 @@
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/quotient-im/libQuotient.git",
|
||||
"branch": "0.8.x",
|
||||
"branch": "dev",
|
||||
"disable-submodules": true
|
||||
}
|
||||
],
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# KDE Applications version, managed by release script.
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "24")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "11")
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "25")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "03")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
@@ -94,7 +94,7 @@ else()
|
||||
set_package_properties(KF6QQC2DesktopStyle PROPERTIES
|
||||
TYPE RUNTIME
|
||||
)
|
||||
ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0)
|
||||
ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0 REQUIRED)
|
||||
|
||||
find_package(ICU 61.0 COMPONENTS uc)
|
||||
set_package_properties(ICU PROPERTIES
|
||||
@@ -107,7 +107,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.8.2)
|
||||
find_package(QuotientQt6 0.9)
|
||||
set_package_properties(QuotientQt6 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
DESCRIPTION "Qt wrapper around Matrix API"
|
||||
@@ -115,11 +115,6 @@ set_package_properties(QuotientQt6 PROPERTIES
|
||||
PURPOSE "Talk with matrix server"
|
||||
)
|
||||
|
||||
if (NOT TARGET Olm::Olm)
|
||||
message(FATAL_ERROR "NeoChat requires Quotient with the E2EE feature enabled")
|
||||
endif()
|
||||
|
||||
|
||||
find_package(cmark)
|
||||
set_package_properties(cmark PROPERTIES
|
||||
TYPE REQUIRED
|
||||
@@ -128,13 +123,12 @@ set_package_properties(cmark PROPERTIES
|
||||
PURPOSE "Convert markdown to html"
|
||||
)
|
||||
|
||||
ecm_find_qmlmodule(org.kde.kquickimageeditor 1.0)
|
||||
ecm_find_qmlmodule(org.kde.kitemmodels 1.0)
|
||||
ecm_find_qmlmodule(org.kde.quickcharts 1.0)
|
||||
ecm_find_qmlmodule(QtLocation)
|
||||
ecm_find_qmlmodule(org.kde.prison)
|
||||
ecm_find_qmlmodule(QtLocation REQUIRED)
|
||||
ecm_find_qmlmodule(org.kde.kitemmodels 1.0 REQUIRED)
|
||||
ecm_find_qmlmodule(org.kde.quickcharts 1.0 REQUIRED)
|
||||
ecm_find_qmlmodule(org.kde.prison REQUIRED)
|
||||
|
||||
find_package(KQuickImageEditor COMPONENTS)
|
||||
find_package(KQuickImageEditor REQUIRED COMPONENTS)
|
||||
set_package_properties(KQuickImageEditor PROPERTIES
|
||||
TYPE REQUIRED
|
||||
DESCRIPTION "Simple image editor for QtQuick applications"
|
||||
|
||||
@@ -11,7 +11,7 @@ A Qt/QML based Matrix client.
|
||||
|
||||
<a href='https://matrix.org'><img src='https://matrix.org/docs/legacy/made-for-matrix.png' alt='Made for Matrix' height=64 target=_blank /></a>
|
||||
<a href='https://flathub.org/apps/details/org.kde.neochat'><img width='190px' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-i-en.png'/></a>
|
||||
<a href='https://snapcraft.io/neochat'><img width='190px' alt='Download on the Snap Store' src='https://snapcraft.io/static/images/badges/en/snap-store-black.svg'/></a>
|
||||
<a href='https://snapcraft.io/neochat'><img width='190px' alt='Download on the Snap Store' src='https://apps.kde.org/store_badges/snapstore/en.svg'/></a>
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
@@ -5,6 +5,26 @@
|
||||
"!room_id_1234:localhost:1234": {
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"m.federate": true,
|
||||
"predecessor": {
|
||||
"event_id": "$something:example.org",
|
||||
"room_id": "!oldroom:example.org"
|
||||
},
|
||||
"room_version": "11"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "",
|
||||
"type": "m.room.create",
|
||||
"unsigned": {
|
||||
"age": 1234,
|
||||
"membership": "join"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "m.room.member",
|
||||
"state_key": "@user:localhost:1234",
|
||||
@@ -26,6 +46,26 @@
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"m.federate": true,
|
||||
"predecessor": {
|
||||
"event_id": "$something:example.org",
|
||||
"room_id": "!oldroom:example.org"
|
||||
},
|
||||
"room_version": "11"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
|
||||
"sender": "@example:example.org",
|
||||
"state_key": "",
|
||||
"type": "m.room.create",
|
||||
"unsigned": {
|
||||
"age": 1234,
|
||||
"membership": "join"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "m.room.message",
|
||||
"sender": "@user:localhost:1234",
|
||||
|
||||
@@ -53,12 +53,6 @@ ecm_add_test(
|
||||
TEST_NAME messageeventmodeltest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
actionshandlertest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
TEST_NAME actionshandlertest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
windowcontrollertest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 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 <QTest>
|
||||
|
||||
#include "actionshandler.h"
|
||||
#include "chatbarcache.h"
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
class ActionsHandlerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
Quotient::Connection *connection = Quotient::Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
|
||||
private Q_SLOTS:
|
||||
void nullObject();
|
||||
};
|
||||
|
||||
void ActionsHandlerTest::nullObject()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
|
||||
ActionsHandler::handleMessageEvent(nullptr, nullptr);
|
||||
|
||||
auto chatBarCache = new ChatBarCache(this);
|
||||
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
|
||||
ActionsHandler::handleMessageEvent(nullptr, chatBarCache);
|
||||
|
||||
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"));
|
||||
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
|
||||
ActionsHandler::handleMessageEvent(room, nullptr);
|
||||
|
||||
// The final one should throw no warning so we make sure.
|
||||
QTest::failOnWarning("ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
|
||||
ActionsHandler::handleMessageEvent(room, chatBarCache);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(ActionsHandlerTest)
|
||||
#include "actionshandlertest.moc"
|
||||
@@ -535,7 +535,7 @@ void TextHandlerTest::componentOutput_data()
|
||||
QVariantMap{{QStringLiteral("class"), QStringLiteral("html")}}}};
|
||||
QTest::newRow("quote") << QStringLiteral("<p>Text</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>")
|
||||
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}},
|
||||
MessageComponent{MessageComponentType::Quote, QStringLiteral("\"blockquote\""), {}}};
|
||||
MessageComponent{MessageComponentType::Quote, QStringLiteral("“blockquote”"), {}}};
|
||||
QTest::newRow("no tag first paragraph") << QStringLiteral("Text\n<p>Text</p>")
|
||||
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}},
|
||||
MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}}};
|
||||
|
||||
@@ -54,9 +54,12 @@
|
||||
<summary xml:lang="ar">دردش على ماتركس</summary>
|
||||
<summary xml:lang="ca">Xat a Matrix</summary>
|
||||
<summary xml:lang="ca-valencia">Xat a Matrix</summary>
|
||||
<summary xml:lang="en-GB">Chat on Matrix</summary>
|
||||
<summary xml:lang="es">Charle en Matrix</summary>
|
||||
<summary xml:lang="eu">Berriketa Matrix-en</summary>
|
||||
<summary xml:lang="fr">Discuter sur Matrix</summary>
|
||||
<summary xml:lang="gl">Charlar en Matrix</summary>
|
||||
<summary xml:lang="hu">Csevegés Matrixon</summary>
|
||||
<summary xml:lang="ia">Conversation en ditecto sur Matrix</summary>
|
||||
<summary xml:lang="it">Chat su Matrix</summary>
|
||||
<summary xml:lang="ka">ისაუბრეთ Matrix-ზე</summary>
|
||||
@@ -285,6 +288,7 @@
|
||||
<value key="KDE::windows_store::StoreLogoSquare">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/storelogo-1080x1080.png</value>
|
||||
<value key="KDE::windows_store::Icon">https://invent.kde.org/network/neochat/-/raw/master/icons/300-apps-neochat.png</value>
|
||||
<value key="KDE::windows_store::PromotionalArt16x9">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/promoimage-1920x1080.png</value>
|
||||
<value key="KDE::supporters">Tanguy Fardet</value>
|
||||
</custom>
|
||||
<launchable type="desktop-id">org.kde.neochat.desktop</launchable>
|
||||
<screenshots>
|
||||
|
||||
@@ -87,47 +87,23 @@ GenericName[uk]=Клієнт Matrix
|
||||
GenericName[x-test]=xxMatrix Clientxx
|
||||
GenericName[zh_CN]=Matrix 客户端
|
||||
GenericName[zh_TW]=Matrix 用戶端
|
||||
Comment=Client for the Matrix protocol
|
||||
Comment[ar]=عميل لميفاق ماتركس
|
||||
Comment[az]=Matrix protokolu üçün müştəri
|
||||
Comment[ca]=Client per al protocol Matrix
|
||||
Comment[ca@valencia]=Client per al protocol Matrix
|
||||
Comment[de]=Programm für das Matrix-Protokoll
|
||||
Comment[el]=Πελάτης για το πρωτόκολλο Matrix
|
||||
Comment[en_GB]=Client for the Matrix protocol
|
||||
Comment[eo]=Kliento por la Matrix-protokolo
|
||||
Comment[es]=Cliente para el protocolo Matrix
|
||||
Comment[eu]=Matrix protokolorako bezeroa
|
||||
Comment[fi]=Asiakas Matrix-yhteyskäytännölle
|
||||
Comment[fr]=Client pour le protocole « Matrix »
|
||||
Comment[gl]=Cliente para o protocolo Matrix.
|
||||
Comment[he]=לקוח לפרוטוקול Matrix
|
||||
Comment[hu]=Kliens a Matrix protokollhoz
|
||||
Comment[ia]=Cliente per le protocollo de Matrix
|
||||
Comment[id]=Klien untuk protokol Matrix
|
||||
Comment[ie]=Un cliente del protocol Matrix
|
||||
Comment[it]=Client per il protocollo Matrix
|
||||
Comment[ka]=კლიენტი Matrix-ის პროტოკოლისთვის
|
||||
Comment[ko]=Matrix 프로토콜용 클라이언트
|
||||
Comment[lt]=Matrix protokolo kliento programa
|
||||
Comment[lv]=Klients „Matrix“ protokolam
|
||||
Comment[nl]=Client voor het Matrix-protocol
|
||||
Comment[nn]=Klient for Matrix-protokollen
|
||||
Comment[pa]=ਮੈਟਰਿਕਸ ਪਰੋਟੋਕਾਲ ਲਈ ਕਲਾਈਂਟ ਹੈ
|
||||
Comment[pl]=Program obsługi protokołu Matriksa
|
||||
Comment[pt]=Cliente para o protocolo Matrix
|
||||
Comment[pt_BR]=Cliente para o protocolo Matrix
|
||||
Comment[ro]=Client pentru protocolul Matrix
|
||||
Comment[ru]=Клиент для протокола Matrix
|
||||
Comment[sk]=Klient protokolu Matrix
|
||||
Comment[sl]=Odjemalec za protokol Matrix
|
||||
Comment[sv]=Klient för protokollet Matrix
|
||||
Comment[ta]=Matrix நெறிமுறைக்கான வாங்கி
|
||||
Comment[tr]=Matrix protokolü için istemci
|
||||
Comment[uk]=Клієнт протоколу Matrix
|
||||
Comment[x-test]=xxClient for the Matrix protocolxx
|
||||
Comment[zh_CN]=为 Matrix 协议打造的客户端
|
||||
Comment[zh_TW]=Matrix 通訊協定的用戶端
|
||||
Comment=Chat on Matrix
|
||||
Comment[ca]=Xat a Matrix
|
||||
Comment[ca@valencia]=Xat a Matrix
|
||||
Comment[es]=Chat en Matrix
|
||||
Comment[eu]=Berriketa Matrix-en
|
||||
Comment[fr]=Clavarder sur Matrix
|
||||
Comment[gl]=Charle en Matrix
|
||||
Comment[hu]=Csevegés Matrixon
|
||||
Comment[ia]=Conversation en ditecto sur Matrix
|
||||
Comment[it]= su Matrix
|
||||
Comment[ka]=ჩატი Matrix-ზე
|
||||
Comment[nl]=Chat op Matrix
|
||||
Comment[pl]=Rozmawiaj na Matriksie
|
||||
Comment[sl]=Klepet na Matrixu
|
||||
Comment[tr]=Matrix Üzerinde Sohbet Et
|
||||
Comment[uk]=Спілкування у Matrix
|
||||
Comment[x-test]=xxChat on Matrixxx
|
||||
MimeType=x-scheme-handler/matrix;
|
||||
Exec=neochat %u
|
||||
Terminal=false
|
||||
|
||||
625
po/ar/neochat.po
625
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
627
po/az/neochat.po
627
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
624
po/ca/neochat.po
624
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
616
po/cs/neochat.po
616
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
624
po/da/neochat.po
624
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
625
po/de/neochat.po
625
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
627
po/el/neochat.po
627
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
625
po/eo/neochat.po
625
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
624
po/es/neochat.po
624
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
626
po/eu/neochat.po
626
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
625
po/fi/neochat.po
625
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
628
po/fr/neochat.po
628
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
671
po/gl/neochat.po
671
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
674
po/hu/neochat.po
674
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
622
po/ia/neochat.po
622
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
627
po/id/neochat.po
627
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
627
po/ie/neochat.po
627
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
626
po/it/neochat.po
626
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
607
po/ja/neochat.po
607
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
622
po/ka/neochat.po
622
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
625
po/ko/neochat.po
625
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
607
po/lt/neochat.po
607
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
625
po/lv/neochat.po
625
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
626
po/nl/neochat.po
626
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
619
po/nn/neochat.po
619
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
627
po/pa/neochat.po
627
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
624
po/pl/neochat.po
624
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
627
po/pt/neochat.po
627
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
734
po/ru/neochat.po
734
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
1238
po/sk/neochat.po
1238
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
622
po/sl/neochat.po
622
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
625
po/sv/neochat.po
625
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
625
po/ta/neochat.po
625
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
832
po/tr/neochat.po
832
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
624
po/uk/neochat.po
624
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
@@ -92,7 +92,7 @@ parts:
|
||||
- olm
|
||||
- qtkeychain
|
||||
source: https://github.com/quotient-im/libQuotient.git
|
||||
source-tag: 0.8.2
|
||||
source-tag: 0.9.0
|
||||
source-depth: 1
|
||||
plugin: cmake
|
||||
build-packages:
|
||||
|
||||
@@ -10,8 +10,6 @@ endif()
|
||||
add_library(neochat STATIC
|
||||
controller.cpp
|
||||
controller.h
|
||||
actionshandler.cpp
|
||||
actionshandler.h
|
||||
models/emojimodel.cpp
|
||||
models/emojimodel.h
|
||||
emojitones.cpp
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "actionshandler.h"
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "models/actionsmodel.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "texthandler.h"
|
||||
|
||||
using namespace Quotient;
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
void ActionsHandler::handleMessageEvent(NeoChatRoom *room, ChatBarCache *chatBarCache)
|
||||
{
|
||||
if (room == nullptr || chatBarCache == nullptr) {
|
||||
qWarning() << "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!chatBarCache->attachmentPath().isEmpty()) {
|
||||
QUrl url(chatBarCache->attachmentPath());
|
||||
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
|
||||
room->uploadFile(QUrl(path), chatBarCache->text().isEmpty() ? path.mid(path.lastIndexOf(u'/') + 1) : chatBarCache->text());
|
||||
chatBarCache->setAttachmentPath({});
|
||||
chatBarCache->setText({});
|
||||
return;
|
||||
}
|
||||
|
||||
const auto handledText = handleMentions(chatBarCache);
|
||||
const auto result = handleQuickEdit(room, handledText);
|
||||
if (!result) {
|
||||
handleMessage(room, handledText, chatBarCache);
|
||||
}
|
||||
}
|
||||
|
||||
QString ActionsHandler::handleMentions(ChatBarCache *chatBarCache)
|
||||
{
|
||||
const auto mentions = chatBarCache->mentions();
|
||||
std::sort(mentions->begin(), mentions->end(), [](const auto &a, const auto &b) -> bool {
|
||||
return a.cursor.anchor() > b.cursor.anchor();
|
||||
});
|
||||
|
||||
auto handledText = chatBarCache->text();
|
||||
for (const auto &mention : *mentions) {
|
||||
if (mention.text.isEmpty() || mention.id.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
handledText = handledText.replace(mention.cursor.anchor(),
|
||||
mention.cursor.position() - mention.cursor.anchor(),
|
||||
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text.toHtmlEscaped(), mention.id));
|
||||
}
|
||||
mentions->clear();
|
||||
|
||||
return handledText;
|
||||
}
|
||||
|
||||
bool ActionsHandler::handleQuickEdit(NeoChatRoom *room, const QString &handledText)
|
||||
{
|
||||
if (room == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NeoChatConfig::allowQuickEdit()) {
|
||||
QRegularExpression sed(QStringLiteral("^s/([^/]*)/([^/]*)(/g)?$"));
|
||||
auto match = sed.match(handledText);
|
||||
if (match.hasMatch()) {
|
||||
const QString regex = match.captured(1);
|
||||
const QString replacement = match.captured(2).toHtmlEscaped();
|
||||
const QString flags = match.captured(3);
|
||||
|
||||
for (auto it = room->messageEvents().crbegin(); it != room->messageEvents().crend(); it++) {
|
||||
if (const auto event = eventCast<const RoomMessageEvent>(&**it)) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (event->senderId() == room->localMember().id() && event->has<EventContent::TextContent>()) {
|
||||
#else
|
||||
if (event->senderId() == room->localMember().id() && event->hasTextContent()) {
|
||||
#endif
|
||||
QString originalString;
|
||||
if (event->content()) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
originalString = event->get<EventContent::TextContent>()->body;
|
||||
#else
|
||||
originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content())->body;
|
||||
#endif
|
||||
} else {
|
||||
originalString = event->plainBody();
|
||||
}
|
||||
if (flags == "/g"_L1) {
|
||||
room->postHtmlMessage(handledText, originalString.replace(regex, replacement), event->msgtype(), {}, event->id());
|
||||
} else {
|
||||
room->postHtmlMessage(handledText,
|
||||
originalString.replace(originalString.indexOf(regex), regex.size(), replacement),
|
||||
event->msgtype(),
|
||||
{},
|
||||
event->id());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ActionsHandler::handleMessage(NeoChatRoom *room, QString handledText, ChatBarCache *chatBarCache)
|
||||
{
|
||||
if (room == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto messageType = RoomMessageEvent::MsgType::Text;
|
||||
|
||||
if (handledText.startsWith(QLatin1Char('/'))) {
|
||||
for (const auto &action : ActionsModel::instance().allActions()) {
|
||||
if (handledText.indexOf(action.prefix) == 1
|
||||
&& (handledText.indexOf(" "_ls) == action.prefix.length() + 1 || handledText.length() == action.prefix.length() + 1)) {
|
||||
handledText = action.handle(handledText.mid(action.prefix.length() + 1).trimmed(), room, chatBarCache);
|
||||
if (action.messageType.has_value()) {
|
||||
messageType = *action.messageType;
|
||||
}
|
||||
if (action.messageAction) {
|
||||
break;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextHandler textHandler;
|
||||
textHandler.setData(handledText);
|
||||
handledText = textHandler.handleSendText();
|
||||
|
||||
if (handledText.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
room->postMessage(chatBarCache->text(), handledText, messageType, chatBarCache->replyId(), chatBarCache->editId(), chatBarCache->threadId());
|
||||
}
|
||||
|
||||
#include "moc_actionshandler.cpp"
|
||||
@@ -1,43 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
class ChatBarCache;
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
* @class ActionsHandler
|
||||
*
|
||||
* This class contains functions to handle chat messages ready for posting to a room.
|
||||
*
|
||||
* Everything that needs to be done to prepare the message for posting in a room
|
||||
* including:
|
||||
* - File handling
|
||||
* - User mentions
|
||||
* - Quick edits
|
||||
* - Chat actions
|
||||
* - Custom emojis
|
||||
*
|
||||
* @note A chat action is a message starting with /, resulting in something other
|
||||
* than a normal message being sent (e.g. /me, /join).
|
||||
*
|
||||
* @sa ActionsModel, NeoChatRoom
|
||||
*/
|
||||
class ActionsHandler
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Pre-process text and send message event.
|
||||
*/
|
||||
static void handleMessageEvent(NeoChatRoom *room, ChatBarCache *chatBarCache);
|
||||
|
||||
private:
|
||||
static QString handleMentions(ChatBarCache *chatBarCache);
|
||||
static bool handleQuickEdit(NeoChatRoom *room, const QString &handledText);
|
||||
|
||||
static void handleMessage(NeoChatRoom *room, QString handledText, ChatBarCache *chatBarCache);
|
||||
};
|
||||
@@ -251,20 +251,22 @@ QQC2.Control {
|
||||
}
|
||||
}
|
||||
Keys.onEnterPressed: event => {
|
||||
const controlIsPressed = event.modifiers & Qt.ControlModifier;
|
||||
if (completionMenu.visible) {
|
||||
completionMenu.complete();
|
||||
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile) {
|
||||
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile || NeoChatConfig.sendMessageWith === 1 && !controlIsPressed || NeoChatConfig.sendMessageWith === 0 && controlIsPressed) {
|
||||
textField.insert(cursorPosition, "\n");
|
||||
} else {
|
||||
} else if (NeoChatConfig.sendMessageWith === 0 && !controlIsPressed || NeoChatConfig.sendMessageWith === 1 && controlIsPressed) {
|
||||
_private.postMessage();
|
||||
}
|
||||
}
|
||||
Keys.onReturnPressed: event => {
|
||||
const controlIsPressed = event.modifiers & Qt.ControlModifier;
|
||||
if (completionMenu.visible) {
|
||||
completionMenu.complete();
|
||||
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile) {
|
||||
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile || NeoChatConfig.sendMessageWith === 1 && !controlIsPressed || NeoChatConfig.sendMessageWith === 0 && controlIsPressed) {
|
||||
textField.insert(cursorPosition, "\n");
|
||||
} else {
|
||||
} else if (NeoChatConfig.sendMessageWith === 0 && !controlIsPressed || NeoChatConfig.sendMessageWith === 1 && controlIsPressed) {
|
||||
_private.postMessage();
|
||||
}
|
||||
}
|
||||
@@ -405,7 +407,6 @@ QQC2.Control {
|
||||
repeatTimer.stop();
|
||||
root.currentRoom.markAllMessagesAsRead();
|
||||
textField.clear();
|
||||
_private.chatBarCache.clearRelations();
|
||||
messageSent();
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,9 @@ QQC2.ItemDelegate {
|
||||
anchors.fill: parent
|
||||
visible: root.emoji.startsWith("mxc") || root.isImage
|
||||
source: visible ? root.emoji : ""
|
||||
fillMode: Image.PreserveAspectFit
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ QQC2.ScrollView {
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
anchors.centerIn: parent
|
||||
icon.name: root.stickers ? "stickers" : "preferences-desktop-emoticons"
|
||||
text: root.stickers ? i18n("No stickers") : i18n("No emojis")
|
||||
visible: emojis.count === 0
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: root.categoryIconSize + QQC2.ScrollBar.horizontal.height
|
||||
QQC2.ScrollBar.horizontal.height: QQC2.ScrollBar.horizontal.visible ? QQC2.ScrollBar.horizontal.implicitHeight : 0
|
||||
visible: categories.count !== 0
|
||||
|
||||
ListView {
|
||||
id: categories
|
||||
@@ -201,8 +202,13 @@ ColumnLayout {
|
||||
width: root.categoryIconSize
|
||||
height: width
|
||||
checked: stickerModel.packIndex === model.index
|
||||
padding: Kirigami.Units.largeSpacing
|
||||
|
||||
contentItem: Image {
|
||||
source: model.avatarUrl
|
||||
fillMode: Image.PreserveAspectFit
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
}
|
||||
QQC2.ToolTip.text: model.name
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
#include "actionshandler.h"
|
||||
#include "chatdocumenthandler.h"
|
||||
#include "eventhandler.h"
|
||||
#include "models/actionsmodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "texthandler.h"
|
||||
|
||||
ChatBarCache::ChatBarCache(QObject *parent)
|
||||
: QObject(parent)
|
||||
@@ -29,6 +30,37 @@ void ChatBarCache::setText(const QString &text)
|
||||
Q_EMIT textChanged();
|
||||
}
|
||||
|
||||
QString ChatBarCache::sendText() const
|
||||
{
|
||||
if (!attachmentPath().isEmpty()) {
|
||||
QUrl url(attachmentPath());
|
||||
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
|
||||
return text().isEmpty() ? path.mid(path.lastIndexOf(u'/') + 1) : text();
|
||||
}
|
||||
|
||||
return formatMentions();
|
||||
}
|
||||
|
||||
QString ChatBarCache::formatMentions() const
|
||||
{
|
||||
auto mentions = m_mentions;
|
||||
std::sort(mentions.begin(), mentions.end(), [](const auto &a, const auto &b) {
|
||||
return a.cursor.anchor() > b.cursor.anchor();
|
||||
});
|
||||
|
||||
auto formattedText = text();
|
||||
for (const auto &mention : mentions) {
|
||||
if (mention.text.isEmpty() || mention.id.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
formattedText = formattedText.replace(mention.cursor.anchor(),
|
||||
mention.cursor.position() - mention.cursor.anchor(),
|
||||
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text.toHtmlEscaped(), mention.id));
|
||||
}
|
||||
|
||||
return formattedText;
|
||||
}
|
||||
|
||||
bool ChatBarCache::isReplying() const
|
||||
{
|
||||
return m_relationType == Reply && !m_relationId.isEmpty();
|
||||
@@ -268,7 +300,35 @@ void ChatBarCache::postMessage()
|
||||
return;
|
||||
}
|
||||
|
||||
ActionsHandler::handleMessageEvent(room, this);
|
||||
if (!attachmentPath().isEmpty()) {
|
||||
room->uploadFile(QUrl(attachmentPath()), sendText());
|
||||
clearCache();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = ActionsModel::handleAction(room, this);
|
||||
if (!result.first.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
TextHandler textHandler;
|
||||
textHandler.setData(*std::get<std::optional<QString>>(result));
|
||||
const auto sendText = textHandler.handleSendText();
|
||||
|
||||
if (sendText.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
room->postMessage(text(), sendText, *std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result), replyId(), editId(), threadId());
|
||||
clearCache();
|
||||
}
|
||||
|
||||
void ChatBarCache::clearCache()
|
||||
{
|
||||
setText({});
|
||||
m_mentions.clear();
|
||||
m_savedText = QString();
|
||||
clearRelations();
|
||||
}
|
||||
|
||||
#include "moc_chatbarcache.cpp"
|
||||
|
||||
@@ -153,6 +153,7 @@ public:
|
||||
explicit ChatBarCache(QObject *parent = nullptr);
|
||||
|
||||
QString text() const;
|
||||
QString sendText() const;
|
||||
void setText(const QString &text);
|
||||
|
||||
bool isReplying() const;
|
||||
@@ -215,6 +216,8 @@ Q_SIGNALS:
|
||||
|
||||
private:
|
||||
QString m_text = QString();
|
||||
QString formatMentions() const;
|
||||
|
||||
QString m_relationId = QString();
|
||||
RelationType m_relationType = RelationType::None;
|
||||
QString m_threadId = QString();
|
||||
@@ -223,4 +226,6 @@ private:
|
||||
QString m_savedText;
|
||||
|
||||
QPointer<MessageContentModel> m_relationContentModel;
|
||||
|
||||
void clearCache();
|
||||
};
|
||||
|
||||
@@ -63,11 +63,7 @@ Controller::Controller(QObject *parent)
|
||||
});
|
||||
} else {
|
||||
auto c = new NeoChatConnection(this);
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
c->assumeIdentity(QStringLiteral("@user:localhost:1234"), QStringLiteral("device_1234"), QStringLiteral("token_1234"));
|
||||
#else
|
||||
c->assumeIdentity(QStringLiteral("@user:localhost:1234"), QStringLiteral("token_1234"));
|
||||
#endif
|
||||
connect(c, &Connection::connected, this, [c, this]() {
|
||||
m_accountRegistry.add(c);
|
||||
c->syncLoop();
|
||||
@@ -230,11 +226,7 @@ void Controller::invokeLogin()
|
||||
Qt::SingleShotConnection);
|
||||
}
|
||||
});
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
connection->assumeIdentity(account.userId(), account.deviceId(), accessToken);
|
||||
#else
|
||||
connection->assumeIdentity(account.userId(), accessToken);
|
||||
#endif
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -441,11 +433,7 @@ void Controller::removeConnection(const QString &userId)
|
||||
|
||||
bool Controller::csSupported() const
|
||||
{
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Controller::revertToDefaultConfig()
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
@@ -23,7 +24,7 @@ ColumnLayout {
|
||||
model: root.connection.accountDataEventTypes
|
||||
delegate: FormCard.FormButtonDelegate {
|
||||
text: modelData
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
onClicked: root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
sourceText: root.connection.accountDataJsonString(modelData)
|
||||
}, {
|
||||
title: i18nc("@title:window", "Event Source"),
|
||||
|
||||
@@ -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
|
||||
@@ -47,7 +48,7 @@ ColumnLayout {
|
||||
model: root.room.accountDataEventTypes
|
||||
delegate: FormCard.FormButtonDelegate {
|
||||
text: modelData
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
onClicked: root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
sourceText: root.room.roomAcountDataJson(text)
|
||||
}, {
|
||||
title: i18n("Event Source"),
|
||||
@@ -77,7 +78,7 @@ ColumnLayout {
|
||||
if (model.eventCount === 1) {
|
||||
openEventSource(model.type, model.stateKey);
|
||||
} else {
|
||||
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'StateKeys'), {
|
||||
root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'StateKeys'), {
|
||||
room: root.room,
|
||||
eventType: model.type
|
||||
}, {
|
||||
@@ -89,7 +90,7 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
function openEventSource(type: string, stateKey: string): void {
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
onClicked: root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
model: stateModel,
|
||||
allowEdit: true,
|
||||
room: root.room,
|
||||
|
||||
@@ -225,18 +225,10 @@ QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
|
||||
{
|
||||
QString body;
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (event.has<EventContent::FileContent>()) {
|
||||
#else
|
||||
if (event.hasFileContent()) {
|
||||
#endif
|
||||
// if filename is given or body is equal to filename,
|
||||
// then body is a caption
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
QString filename = event.get<EventContent::FileContent>()->originalName;
|
||||
#else
|
||||
QString filename = event.content()->fileInfo()->originalName;
|
||||
#endif
|
||||
QString body = event.plainBody();
|
||||
if (filename.isEmpty() || filename == body) {
|
||||
return QString();
|
||||
@@ -244,13 +236,8 @@ QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
|
||||
return body;
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (event.has<EventContent::TextContent>() && event.content()) {
|
||||
body = event.get<EventContent::TextContent>()->body;
|
||||
#else
|
||||
if (event.hasTextContent() && event.content()) {
|
||||
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
||||
#endif
|
||||
} else {
|
||||
body = event.plainBody();
|
||||
}
|
||||
@@ -477,13 +464,8 @@ QString EventHandler::getMessageBody(const NeoChatRoom *room, const RoomMessageE
|
||||
{
|
||||
TextHandler textHandler;
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (event.has<EventContent::FileContent>()) {
|
||||
QString fileCaption = event.get<EventContent::FileContent>()->originalName;
|
||||
#else
|
||||
if (event.hasFileContent()) {
|
||||
QString fileCaption = event.content()->fileInfo()->originalName;
|
||||
#endif
|
||||
if (fileCaption.isEmpty()) {
|
||||
fileCaption = event.plainBody();
|
||||
} else if (fileCaption != event.plainBody()) {
|
||||
@@ -494,13 +476,8 @@ QString EventHandler::getMessageBody(const NeoChatRoom *room, const RoomMessageE
|
||||
}
|
||||
|
||||
QString body;
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (event.has<EventContent::TextContent>() && event.content()) {
|
||||
body = event.get<EventContent::TextContent>()->body;
|
||||
#else
|
||||
if (event.hasTextContent() && event.content()) {
|
||||
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
||||
#endif
|
||||
} else {
|
||||
body = event.plainBody();
|
||||
}
|
||||
@@ -715,28 +692,15 @@ QVariantMap EventHandler::getMediaInfoForEvent(const NeoChatRoom *room, const Qu
|
||||
// Get the file info for the event.
|
||||
if (event->is<RoomMessageEvent>()) {
|
||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event);
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (!roomMessageEvent->has<EventContent::FileContentBase>()) {
|
||||
#else
|
||||
if (!roomMessageEvent->hasFileContent()) {
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
const auto content = roomMessageEvent->get<EventContent::FileContentBase>();
|
||||
QVariantMap mediaInfo = getMediaInfoFromFileInfo(room, content.get(), eventId, false, false);
|
||||
#else
|
||||
const auto content = static_cast<const EventContent::FileContent *>(roomMessageEvent->content());
|
||||
QVariantMap mediaInfo = getMediaInfoFromFileInfo(room, content, eventId, false, false);
|
||||
#endif
|
||||
// if filename isn't specifically given, it is in body
|
||||
// https://spec.matrix.org/latest/client-server-api/#mfile
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
mediaInfo["filename"_ls] = content->commonInfo().originalName.isEmpty() ? roomMessageEvent->plainBody() : content->commonInfo().originalName;
|
||||
#else
|
||||
mediaInfo["filename"_ls] = (content->fileInfo()->originalName.isEmpty()) ? roomMessageEvent->plainBody() : content->fileInfo()->originalName;
|
||||
#endif
|
||||
|
||||
return mediaInfo;
|
||||
} else if (event->is<StickerEvent>()) {
|
||||
@@ -750,11 +714,7 @@ QVariantMap EventHandler::getMediaInfoForEvent(const NeoChatRoom *room, const Qu
|
||||
}
|
||||
|
||||
QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
const Quotient::EventContent::FileContentBase *fileContent,
|
||||
#else
|
||||
const Quotient::EventContent::TypedBase *fileContent,
|
||||
#endif
|
||||
const QString &eventId,
|
||||
bool isThumbnail,
|
||||
bool isSticker)
|
||||
@@ -762,18 +722,10 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
||||
QVariantMap mediaInfo;
|
||||
|
||||
// Get the mxc URL for the media.
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (!fileContent->url().isValid() || fileContent->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
||||
#else
|
||||
if (!fileContent->fileInfo()->url().isValid() || fileContent->fileInfo()->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
||||
#endif
|
||||
mediaInfo["source"_ls] = QUrl();
|
||||
} else {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
QUrl source = room->makeMediaUrl(eventId, fileContent->url());
|
||||
#else
|
||||
QUrl source = room->makeMediaUrl(eventId, fileContent->fileInfo()->url());
|
||||
#endif
|
||||
|
||||
if (source.isValid()) {
|
||||
mediaInfo["source"_ls] = source;
|
||||
@@ -790,25 +742,15 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
||||
mediaInfo["mimeIcon"_ls] = mimeType.iconName();
|
||||
|
||||
// Add media size if available.
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
mediaInfo["size"_ls] = fileContent->commonInfo().payloadSize;
|
||||
#else
|
||||
mediaInfo["size"_ls] = static_cast<const EventContent::FileContent *>(fileContent)->fileInfo()->payloadSize;
|
||||
#endif
|
||||
|
||||
mediaInfo["isSticker"_ls] = isSticker;
|
||||
|
||||
// Add parameter depending on media type.
|
||||
if (mimeType.name().contains(QStringLiteral("image"))) {
|
||||
if (auto castInfo = static_cast<const EventContent::ImageContent *>(fileContent)) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
mediaInfo["width"_ls] = castInfo->imageSize.width();
|
||||
mediaInfo["height"_ls] = castInfo->imageSize.height();
|
||||
#else
|
||||
const auto imageInfo = static_cast<const EventContent::ImageInfo *>(castInfo->fileInfo());
|
||||
mediaInfo["width"_ls] = imageInfo->imageSize.width();
|
||||
mediaInfo["height"_ls] = imageInfo->imageSize.height();
|
||||
#endif
|
||||
|
||||
// TODO: Images in certain formats (e.g. WebP) will be erroneously marked as animated, even if they are static.
|
||||
mediaInfo["animated"_ls] = QMovie::supportedFormats().contains(mimeType.preferredSuffix().toUtf8());
|
||||
|
||||
@@ -290,11 +290,7 @@ private:
|
||||
|
||||
static QVariantMap getMediaInfoForEvent(const NeoChatRoom *room, const Quotient::RoomEvent *event);
|
||||
QVariantMap static getMediaInfoFromFileInfo(const NeoChatRoom *room,
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
const Quotient::EventContent::FileContentBase *fileContent,
|
||||
#else
|
||||
const Quotient::EventContent::TypedBase *fileContent,
|
||||
#endif
|
||||
const QString &eventId,
|
||||
bool isThumbnail = false,
|
||||
bool isSticker = false);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "imagepackevent.h"
|
||||
#include <QJsonObject>
|
||||
#include <Quotient/omittable.h>
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
@@ -11,10 +10,10 @@ ImagePackEventContent::ImagePackEventContent(const QJsonObject &json)
|
||||
{
|
||||
if (json.contains(QStringLiteral("pack"))) {
|
||||
pack = ImagePackEventContent::Pack{
|
||||
fromJson<Omittable<QString>>(json["pack"_ls].toObject()["display_name"_ls]),
|
||||
fromJson<Omittable<QUrl>>(json["pack"_ls].toObject()["avatar_url"_ls]),
|
||||
fromJson<Omittable<QStringList>>(json["pack"_ls].toObject()["usage"_ls]),
|
||||
fromJson<Omittable<QString>>(json["pack"_ls].toObject()["attribution"_ls]),
|
||||
fromJson<std::optional<QString>>(json["pack"_ls].toObject()["display_name"_ls]),
|
||||
fromJson<std::optional<QUrl>>(json["pack"_ls].toObject()["avatar_url"_ls]),
|
||||
fromJson<std::optional<QStringList>>(json["pack"_ls].toObject()["usage"_ls]),
|
||||
fromJson<std::optional<QString>>(json["pack"_ls].toObject()["attribution"_ls]),
|
||||
};
|
||||
} else {
|
||||
pack = std::nullopt;
|
||||
@@ -31,9 +30,9 @@ ImagePackEventContent::ImagePackEventContent(const QJsonObject &json)
|
||||
images += ImagePackImage{
|
||||
k,
|
||||
fromJson<QUrl>(json["images"_ls][k]["url"_ls].toString()),
|
||||
fromJson<Omittable<QString>>(json["images"_ls][k]["body"_ls]),
|
||||
fromJson<std::optional<QString>>(json["images"_ls][k]["body"_ls]),
|
||||
info,
|
||||
fromJson<Omittable<QStringList>>(json["images"_ls][k]["usage"_ls]),
|
||||
fromJson<std::optional<QStringList>>(json["images"_ls][k]["usage"_ls]),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,10 @@
|
||||
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/e2ee/sssshandler.h>
|
||||
#include <Quotient/keyimport.h>
|
||||
#include <Quotient/keyverificationsession.h>
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
#include <Quotient/keyimport.h>
|
||||
#endif
|
||||
|
||||
#include "controller.h"
|
||||
#include "neochatconfig.h"
|
||||
|
||||
@@ -43,11 +40,9 @@ struct ForeignSSSSHandler {
|
||||
QML_NAMED_ELEMENT(SSSSHandler)
|
||||
};
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
struct ForeignKeyImport {
|
||||
Q_GADGET
|
||||
QML_SINGLETON
|
||||
QML_FOREIGN(Quotient::KeyImport)
|
||||
QML_NAMED_ELEMENT(KeyImport)
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
NeochatAdd3PIdJob::NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const Omittable<QJsonObject> &auth)
|
||||
NeochatAdd3PIdJob::NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const std::optional<QJsonObject> &auth)
|
||||
: BaseJob(HttpVerb::Post, QStringLiteral("Add3PIDJob"), makePath("/_matrix/client/v3", "/account/3pid/add"))
|
||||
{
|
||||
QJsonObject _dataJson;
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/omittable.h>
|
||||
|
||||
class NeochatAdd3PIdJob : public Quotient::BaseJob
|
||||
{
|
||||
public:
|
||||
explicit NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const Quotient::Omittable<QJsonObject> &auth = {});
|
||||
explicit NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const std::optional<QJsonObject> &auth = {});
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const Omittable<QJsonObject> &auth)
|
||||
NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const std::optional<QJsonObject> &auth)
|
||||
: BaseJob(HttpVerb::Post, QStringLiteral("ChangePasswordJob"), "/_matrix/client/r0/account/password")
|
||||
{
|
||||
QJsonObject _data;
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
|
||||
#include <Quotient/omittable.h>
|
||||
|
||||
class NeochatChangePasswordJob : public Quotient::BaseJob
|
||||
{
|
||||
public:
|
||||
explicit NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const Quotient::Omittable<QJsonObject> &auth = {});
|
||||
explicit NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const std::optional<QJsonObject> &auth = {});
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
NeoChatDeactivateAccountJob::NeoChatDeactivateAccountJob(const Omittable<QJsonObject> &auth)
|
||||
NeoChatDeactivateAccountJob::NeoChatDeactivateAccountJob(const std::optional<QJsonObject> &auth)
|
||||
: BaseJob(HttpVerb::Post, QStringLiteral("DisableDeviceJob"), "_matrix/client/v3/account/deactivate")
|
||||
{
|
||||
QJsonObject data;
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/omittable.h>
|
||||
|
||||
class NeoChatDeactivateAccountJob : public Quotient::BaseJob
|
||||
{
|
||||
public:
|
||||
explicit NeoChatDeactivateAccountJob(const Quotient::Omittable<QJsonObject> &auth = {});
|
||||
explicit NeoChatDeactivateAccountJob(const std::optional<QJsonObject> &auth = {});
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const Omittable<QJsonObject> &auth)
|
||||
NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const std::optional<QJsonObject> &auth)
|
||||
: BaseJob(HttpVerb::Delete, QStringLiteral("DeleteDeviceJob"), QStringLiteral("/_matrix/client/r0/devices/%1").arg(deviceId).toLatin1())
|
||||
{
|
||||
QJsonObject _data;
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/omittable.h>
|
||||
|
||||
class NeochatDeleteDeviceJob : public Quotient::BaseJob
|
||||
{
|
||||
public:
|
||||
explicit NeochatDeleteDeviceJob(const QString &deviceId, const Quotient::Omittable<QJsonObject> &auth = {});
|
||||
explicit NeochatDeleteDeviceJob(const QString &deviceId, const std::optional<QJsonObject> &auth = {});
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/omittable.h>
|
||||
|
||||
// TODO: Upstream to libQuotient
|
||||
class NeochatGetCommonRoomsJob : public Quotient::BaseJob
|
||||
|
||||
@@ -7,6 +7,7 @@ import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
@@ -90,11 +91,29 @@ Kirigami.Page {
|
||||
id: loadedAccounts
|
||||
model: AccountRegistry
|
||||
delegate: FormCard.FormButtonDelegate {
|
||||
text: model.userId
|
||||
id: delegate
|
||||
|
||||
required property string userId
|
||||
required property NeoChatConnection connection
|
||||
|
||||
text: connection.localUser.displayName
|
||||
description: connection.localUser.id
|
||||
leadingPadding: Kirigami.Units.largeSpacing
|
||||
|
||||
onClicked: {
|
||||
Controller.activeConnection = model.connection;
|
||||
Controller.activeConnection = delegate.connection;
|
||||
root.connectionChosen();
|
||||
}
|
||||
leading: KirigamiComponents.Avatar {
|
||||
id: avatar
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: Kirigami.Units.gridUnit
|
||||
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) : ""
|
||||
implicitWidth: Kirigami.Units.iconSizes.medium
|
||||
implicitHeight: Kirigami.Units.iconSizes.medium
|
||||
}
|
||||
}
|
||||
}
|
||||
Repeater {
|
||||
|
||||
@@ -140,7 +140,7 @@ int main(int argc, char *argv[])
|
||||
KAboutData about(QStringLiteral("neochat"),
|
||||
i18n("NeoChat"),
|
||||
QStringLiteral(NEOCHAT_VERSION_STRING),
|
||||
i18n("Matrix client"),
|
||||
i18n("Chat on Matrix"),
|
||||
KAboutLicense::GPL_V3,
|
||||
i18n("© 2018-2020 Black Hat, 2020-2024 KDE Community"));
|
||||
about.addAuthor(i18n("Carl Schwan"),
|
||||
|
||||
@@ -163,11 +163,7 @@ void AccountEmoticonModel::setEmoticonImage(int index, const QUrl &source)
|
||||
QCoro::Task<void> AccountEmoticonModel::doSetEmoticonImage(int index, QUrl source)
|
||||
{
|
||||
auto job = m_connection->uploadFile(source.isLocalFile() ? source.toLocalFile() : source.toString());
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
co_await qCoro(job.get(), &BaseJob::finished);
|
||||
#else
|
||||
co_await qCoro(job, &BaseJob::finished);
|
||||
#endif
|
||||
if (job->error() != BaseJob::NoError) {
|
||||
co_return;
|
||||
}
|
||||
@@ -189,11 +185,7 @@ QCoro::Task<void> AccountEmoticonModel::doSetEmoticonImage(int index, QUrl sourc
|
||||
QCoro::Task<void> AccountEmoticonModel::doAddEmoticon(QUrl source, QString shortcode, QString description, QString type)
|
||||
{
|
||||
auto job = m_connection->uploadFile(source.isLocalFile() ? source.toLocalFile() : source.toString());
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
co_await qCoro(job.get(), &BaseJob::finished);
|
||||
#else
|
||||
co_await qCoro(job, &BaseJob::finished);
|
||||
#endif
|
||||
if (job->error() != BaseJob::NoError) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "enums/messagetype.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "roommanager.h"
|
||||
@@ -17,6 +18,7 @@
|
||||
|
||||
using Action = ActionsModel::Action;
|
||||
using namespace Quotient;
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
QStringList rainbowColors{"#ff2b00"_ls, "#ff5500"_ls, "#ff8000"_ls, "#ffaa00"_ls, "#ffd500"_ls, "#ffff00"_ls, "#d4ff00"_ls, "#aaff00"_ls, "#80ff00"_ls,
|
||||
"#55ff00"_ls, "#2bff00"_ls, "#00ff00"_ls, "#00ff2b"_ls, "#00ff55"_ls, "#00ff80"_ls, "#00ffaa"_ls, "#00ffd5"_ls, "#00ffff"_ls,
|
||||
@@ -574,3 +576,73 @@ QList<Action> &ActionsModel::allActions() const
|
||||
{
|
||||
return actions;
|
||||
}
|
||||
|
||||
bool ActionsModel::handleQuickEditAction(NeoChatRoom *room, const QString &messageText)
|
||||
{
|
||||
if (room == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NeoChatConfig::allowQuickEdit()) {
|
||||
QRegularExpression sed(QStringLiteral("^s/([^/]*)/([^/]*)(/g)?$"));
|
||||
auto match = sed.match(messageText);
|
||||
if (match.hasMatch()) {
|
||||
const QString regex = match.captured(1);
|
||||
const QString replacement = match.captured(2).toHtmlEscaped();
|
||||
const QString flags = match.captured(3);
|
||||
|
||||
for (auto it = room->messageEvents().crbegin(); it != room->messageEvents().crend(); it++) {
|
||||
if (const auto event = eventCast<const RoomMessageEvent>(&**it)) {
|
||||
if (event->senderId() == room->localMember().id() && event->has<EventContent::TextContent>()) {
|
||||
QString originalString;
|
||||
if (event->content()) {
|
||||
originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content().get())->body;
|
||||
} else {
|
||||
originalString = event->plainBody();
|
||||
}
|
||||
if (flags == "/g"_L1) {
|
||||
room->postHtmlMessage(messageText, originalString.replace(regex, replacement), event->msgtype(), {}, event->id());
|
||||
} else {
|
||||
room->postHtmlMessage(messageText,
|
||||
originalString.replace(originalString.indexOf(regex), regex.size(), replacement),
|
||||
event->msgtype(),
|
||||
{},
|
||||
event->id());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<std::optional<QString>, std::optional<Quotient::RoomMessageEvent::MsgType>> ActionsModel::handleAction(NeoChatRoom *room, ChatBarCache *chatBarCache)
|
||||
{
|
||||
auto sendText = chatBarCache->sendText();
|
||||
const auto edited = handleQuickEditAction(room, sendText);
|
||||
if (edited) {
|
||||
return std::make_pair(std::nullopt, std::nullopt);
|
||||
}
|
||||
|
||||
std::optional<Quotient::RoomMessageEvent::MsgType> messageType = std::nullopt;
|
||||
if (sendText.startsWith(QLatin1Char('/'))) {
|
||||
for (const auto &action : ActionsModel::instance().allActions()) {
|
||||
if (sendText.indexOf(action.prefix) == 1
|
||||
&& (sendText.indexOf(" "_ls) == action.prefix.length() + 1 || sendText.length() == action.prefix.length() + 1)) {
|
||||
sendText = action.handle(sendText.mid(action.prefix.length() + 1).trimmed(), room, chatBarCache);
|
||||
if (action.messageType.has_value()) {
|
||||
messageType = action.messageType;
|
||||
}
|
||||
if (action.messageAction) {
|
||||
break;
|
||||
} else {
|
||||
return std::make_pair(std::nullopt, std::nullopt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(sendText, messageType);
|
||||
}
|
||||
|
||||
@@ -90,6 +90,21 @@ public:
|
||||
*/
|
||||
QList<Action> &allActions() const;
|
||||
|
||||
/**
|
||||
* @brief Handle special sed style edit action.
|
||||
*
|
||||
* @return True if the message has a sed edit which was actioned. False otherwise.
|
||||
*/
|
||||
static bool handleQuickEditAction(NeoChatRoom *room, const QString &messageText);
|
||||
|
||||
/**
|
||||
* @brief Handle any action within the message contained in the given ChatBarCache.
|
||||
*
|
||||
* @return A modified or unmodified string that needs to be sent or an empty string if
|
||||
* the handled action replaces sending a normal message.
|
||||
*/
|
||||
static std::pair<std::optional<QString>, std::optional<Quotient::RoomMessageEvent::MsgType>> handleAction(NeoChatRoom *room, ChatBarCache *chatBarCache);
|
||||
|
||||
private:
|
||||
ActionsModel() = default;
|
||||
};
|
||||
|
||||
@@ -85,13 +85,7 @@ QVariant CompletionModel::data(const QModelIndex &index, int role) const
|
||||
return m_filterModel->data(filterIndex, RoomListModel::CanonicalAliasRole);
|
||||
}
|
||||
if (role == IconNameRole) {
|
||||
auto mediaId = m_filterModel->data(filterIndex, RoomListModel::AvatarRole).toString();
|
||||
if (mediaId.isEmpty()) {
|
||||
return QVariant();
|
||||
}
|
||||
if (m_room) {
|
||||
return m_room->connection()->makeMediaUrl(QUrl(QStringLiteral("mxc://%1").arg(mediaId)));
|
||||
}
|
||||
return m_filterModel->data(filterIndex, RoomListModel::AvatarRole).toString();
|
||||
}
|
||||
}
|
||||
if (m_autoCompletionType == Emoji) {
|
||||
|
||||
@@ -129,11 +129,7 @@ void DevicesModel::logout(const QString &deviceId, const QString &password)
|
||||
QJsonObject identifier = {{"type"_ls, "m.id.user"_ls}, {"user"_ls, m_connection->user()->id()}};
|
||||
authData["identifier"_ls] = identifier;
|
||||
auto innerJob = m_connection->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId, authData);
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
connect(innerJob.get(), &BaseJob::success, this, onSuccess);
|
||||
#else
|
||||
connect(innerJob, &BaseJob::success, this, onSuccess);
|
||||
#endif
|
||||
} else {
|
||||
onSuccess();
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ MessageContentModel::MessageContentModel(NeoChatRoom *room, const QString &event
|
||||
: QAbstractListModel(parent)
|
||||
, m_room(room)
|
||||
, m_eventId(eventId)
|
||||
, m_isPending(isPending)
|
||||
, m_currentState(isPending ? Pending : Unknown)
|
||||
, m_isReply(isReply)
|
||||
{
|
||||
initializeModel();
|
||||
@@ -45,19 +45,27 @@ void MessageContentModel::initializeModel()
|
||||
Q_ASSERT(m_room != nullptr);
|
||||
Q_ASSERT(!m_eventId.isEmpty());
|
||||
|
||||
connect(this, &MessageContentModel::eventUnavailable, this, &MessageContentModel::getEvent);
|
||||
|
||||
connect(m_room, &NeoChatRoom::pendingEventAdded, this, [this]() {
|
||||
if (m_room != nullptr && m_currentState == Unknown) {
|
||||
initializeEvent();
|
||||
updateReplyModel();
|
||||
resetModel();
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::pendingEventAboutToMerge, this, [this](Quotient::RoomEvent *serverEvent) {
|
||||
if (m_room != nullptr) {
|
||||
if (m_eventId == serverEvent->id() || m_eventId == serverEvent->transactionId()) {
|
||||
beginResetModel();
|
||||
m_isPending = false;
|
||||
m_eventId = serverEvent->id();
|
||||
initializeEvent();
|
||||
endResetModel();
|
||||
}
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::pendingEventMerged, this, [this]() {
|
||||
if (m_room != nullptr && m_currentState == Pending) {
|
||||
initializeEvent();
|
||||
updateReplyModel();
|
||||
resetModel();
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::addedMessages, this, [this](int fromIndex, int toIndex) {
|
||||
if (m_room != nullptr) {
|
||||
for (int i = fromIndex; i <= toIndex; i++) {
|
||||
@@ -143,20 +151,33 @@ void MessageContentModel::initializeModel()
|
||||
});
|
||||
|
||||
initializeEvent();
|
||||
updateReplyModel();
|
||||
if (m_currentState == Available || m_currentState == Pending) {
|
||||
updateReplyModel();
|
||||
}
|
||||
resetModel();
|
||||
}
|
||||
|
||||
void MessageContentModel::initializeEvent()
|
||||
{
|
||||
const auto event = m_room->getEvent(m_eventId);
|
||||
if (event == nullptr) {
|
||||
Q_EMIT eventUnavailable();
|
||||
if (m_currentState == UnAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto eventResult = m_room->getEvent(m_eventId);
|
||||
if (eventResult.first == nullptr) {
|
||||
if (m_currentState != Pending) {
|
||||
getEvent();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (eventResult.second) {
|
||||
m_currentState = Pending;
|
||||
} else {
|
||||
m_currentState = Available;
|
||||
}
|
||||
|
||||
if (m_eventSenderObject == nullptr) {
|
||||
auto senderId = event->senderId();
|
||||
auto senderId = eventResult.first->senderId();
|
||||
// A pending event might not have a sender ID set yet but in that case it must
|
||||
// be the local member.
|
||||
if (senderId.isEmpty()) {
|
||||
@@ -172,7 +193,6 @@ void MessageContentModel::getEvent()
|
||||
Quotient::connectUntil(m_room.get(), &NeoChatRoom::extraEventLoaded, this, [this](const QString &eventId) {
|
||||
if (m_room != nullptr) {
|
||||
if (eventId == m_eventId) {
|
||||
m_notFound = false;
|
||||
initializeEvent();
|
||||
updateReplyModel();
|
||||
resetModel();
|
||||
@@ -184,7 +204,7 @@ void MessageContentModel::getEvent()
|
||||
Quotient::connectUntil(m_room.get(), &NeoChatRoom::extraEventNotFound, this, [this](const QString &eventId) {
|
||||
if (m_room != nullptr) {
|
||||
if (eventId == m_eventId) {
|
||||
m_notFound = true;
|
||||
m_currentState = UnAvailable;
|
||||
resetModel();
|
||||
return true;
|
||||
}
|
||||
@@ -237,7 +257,7 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
||||
const auto component = m_components[index.row()];
|
||||
|
||||
const auto event = m_room->getEvent(m_eventId);
|
||||
if (event == nullptr) {
|
||||
if (event.first == nullptr) {
|
||||
if (role == DisplayRole) {
|
||||
if (m_isReply) {
|
||||
return i18n("Loading reply");
|
||||
@@ -252,7 +272,7 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
||||
}
|
||||
|
||||
if (role == DisplayRole) {
|
||||
if (m_notFound || m_room->connection()->isIgnored(m_eventSenderId)) {
|
||||
if (m_currentState == UnAvailable || m_room->connection()->isIgnored(m_eventSenderId)) {
|
||||
Kirigami::Platform::PlatformTheme *theme =
|
||||
static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
|
||||
|
||||
@@ -276,7 +296,7 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
||||
if (!component.content.isEmpty()) {
|
||||
return component.content;
|
||||
}
|
||||
return EventHandler::richBody(m_room, event);
|
||||
return EventHandler::richBody(m_room, event.first);
|
||||
}
|
||||
if (role == ComponentTypeRole) {
|
||||
return component.type;
|
||||
@@ -285,53 +305,53 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
||||
return component.attributes;
|
||||
}
|
||||
if (role == EventIdRole) {
|
||||
return EventHandler::id(event);
|
||||
return EventHandler::id(event.first);
|
||||
}
|
||||
if (role == TimeRole) {
|
||||
const auto pendingIt = std::find_if(m_room->pendingEvents().cbegin(), m_room->pendingEvents().cend(), [event](const PendingEventItem &pendingEvent) {
|
||||
return event->transactionId() == pendingEvent->transactionId();
|
||||
return event.first->transactionId() == pendingEvent->transactionId();
|
||||
});
|
||||
|
||||
auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated();
|
||||
return EventHandler::time(event, m_isPending, lastUpdated);
|
||||
return EventHandler::time(event.first, m_currentState == Pending, lastUpdated);
|
||||
}
|
||||
if (role == TimeStringRole) {
|
||||
const auto pendingIt = std::find_if(m_room->pendingEvents().cbegin(), m_room->pendingEvents().cend(), [event](const PendingEventItem &pendingEvent) {
|
||||
return event->transactionId() == pendingEvent->transactionId();
|
||||
return event.first->transactionId() == pendingEvent->transactionId();
|
||||
});
|
||||
|
||||
auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated();
|
||||
return EventHandler::timeString(event, QStringLiteral("hh:mm"), m_isPending, lastUpdated);
|
||||
return EventHandler::timeString(event.first, QStringLiteral("hh:mm"), m_currentState == Pending, lastUpdated);
|
||||
}
|
||||
if (role == AuthorRole) {
|
||||
return QVariant::fromValue<NeochatRoomMember *>(m_eventSenderObject.get());
|
||||
}
|
||||
if (role == MediaInfoRole) {
|
||||
return EventHandler::mediaInfo(m_room, event);
|
||||
return EventHandler::mediaInfo(m_room, event.first);
|
||||
}
|
||||
if (role == FileTransferInfoRole) {
|
||||
return QVariant::fromValue(m_room->cachedFileTransferInfo(event));
|
||||
return QVariant::fromValue(m_room->cachedFileTransferInfo(event.first));
|
||||
}
|
||||
if (role == ItineraryModelRole) {
|
||||
return QVariant::fromValue<ItineraryModel *>(m_itineraryModel);
|
||||
}
|
||||
if (role == LatitudeRole) {
|
||||
return EventHandler::latitude(event);
|
||||
return EventHandler::latitude(event.first);
|
||||
}
|
||||
if (role == LongitudeRole) {
|
||||
return EventHandler::longitude(event);
|
||||
return EventHandler::longitude(event.first);
|
||||
}
|
||||
if (role == AssetRole) {
|
||||
return EventHandler::locationAssetType(event);
|
||||
return EventHandler::locationAssetType(event.first);
|
||||
}
|
||||
if (role == PollHandlerRole) {
|
||||
return QVariant::fromValue<PollHandler *>(m_room->poll(m_eventId));
|
||||
}
|
||||
if (role == ReplyEventIdRole) {
|
||||
return EventHandler::replyId(event);
|
||||
return EventHandler::replyId(event.first);
|
||||
}
|
||||
if (role == ReplyAuthorRole) {
|
||||
return QVariant::fromValue(EventHandler::replyAuthor(m_room, event));
|
||||
return QVariant::fromValue(EventHandler::replyAuthor(m_room, event.first));
|
||||
}
|
||||
if (role == ReplyContentModelRole) {
|
||||
return QVariant::fromValue<MessageContentModel *>(m_replyModel);
|
||||
@@ -387,18 +407,17 @@ QHash<int, QByteArray> MessageContentModel::roleNames() const
|
||||
|
||||
void MessageContentModel::resetModel()
|
||||
{
|
||||
const auto event = m_room->getEvent(m_eventId);
|
||||
|
||||
beginResetModel();
|
||||
m_components.clear();
|
||||
|
||||
if (m_room->connection()->isIgnored(m_eventSenderId) || m_notFound) {
|
||||
if (m_room->connection()->isIgnored(m_eventSenderId) || m_currentState == UnAvailable) {
|
||||
m_components += MessageComponent{MessageComponentType::Text, QString(), {}};
|
||||
endResetModel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event == nullptr) {
|
||||
const auto event = m_room->getEvent(m_eventId);
|
||||
if (event.first == nullptr) {
|
||||
m_components += MessageComponent{MessageComponentType::Loading, QString(), {}};
|
||||
endResetModel();
|
||||
return;
|
||||
@@ -431,19 +450,19 @@ void MessageContentModel::resetContent(bool isEditing, bool isThreading)
|
||||
QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEditing, bool isThreading)
|
||||
{
|
||||
const auto event = m_room->getEvent(m_eventId);
|
||||
if (event == nullptr) {
|
||||
if (event.first == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QList<MessageComponent> newComponents;
|
||||
|
||||
if (eventCast<const Quotient::RoomMessageEvent>(event)
|
||||
&& eventCast<const Quotient::RoomMessageEvent>(event)->rawMsgtype() == QStringLiteral("m.key.verification.request")) {
|
||||
if (eventCast<const Quotient::RoomMessageEvent>(event.first)
|
||||
&& eventCast<const Quotient::RoomMessageEvent>(event.first)->rawMsgtype() == QStringLiteral("m.key.verification.request")) {
|
||||
newComponents += MessageComponent{MessageComponentType::Verification, QString(), {}};
|
||||
return newComponents;
|
||||
}
|
||||
|
||||
if (event->isRedacted()) {
|
||||
if (event.first->isRedacted()) {
|
||||
newComponents += MessageComponent{MessageComponentType::Text, QString(), {}};
|
||||
return newComponents;
|
||||
}
|
||||
@@ -455,7 +474,7 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
|
||||
if (isEditing) {
|
||||
newComponents += MessageComponent{MessageComponentType::ChatBar, QString(), {}};
|
||||
} else {
|
||||
newComponents.append(componentsForType(MessageComponentType::typeForEvent(*event)));
|
||||
newComponents.append(componentsForType(MessageComponentType::typeForEvent(*event.first)));
|
||||
}
|
||||
|
||||
if (m_room->urlPreviewEnabled()) {
|
||||
@@ -463,7 +482,7 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
|
||||
}
|
||||
|
||||
// If the event is already threaded the ThreadModel will handle displaying a chat bar.
|
||||
if (isThreading && !EventHandler::isThreaded(event)) {
|
||||
if (isThreading && !EventHandler::isThreaded(event.first)) {
|
||||
newComponents += MessageComponent{MessageComponentType::ChatBar, QString(), {}};
|
||||
}
|
||||
|
||||
@@ -473,11 +492,11 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
|
||||
void MessageContentModel::updateReplyModel()
|
||||
{
|
||||
const auto event = m_room->getEvent(m_eventId);
|
||||
if (event == nullptr || m_isReply) {
|
||||
if (event.first == nullptr || m_isReply) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EventHandler::hasReply(event) || (EventHandler::isThreaded(event) && NeoChatConfig::self()->threads())) {
|
||||
if (!EventHandler::hasReply(event.first) || (EventHandler::isThreaded(event.first) && NeoChatConfig::self()->threads())) {
|
||||
if (m_replyModel) {
|
||||
delete m_replyModel;
|
||||
}
|
||||
@@ -488,7 +507,7 @@ void MessageContentModel::updateReplyModel()
|
||||
return;
|
||||
}
|
||||
|
||||
m_replyModel = new MessageContentModel(m_room, EventHandler::replyId(event), true, false, this);
|
||||
m_replyModel = new MessageContentModel(m_room, EventHandler::replyId(event.first), true, false, this);
|
||||
|
||||
connect(m_replyModel, &MessageContentModel::eventUpdated, this, [this]() {
|
||||
Q_EMIT dataChanged(index(0), index(0), {ReplyAuthorRole});
|
||||
@@ -498,13 +517,13 @@ void MessageContentModel::updateReplyModel()
|
||||
QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentType::Type type)
|
||||
{
|
||||
const auto event = m_room->getEvent(m_eventId);
|
||||
if (event == nullptr) {
|
||||
if (event.first == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case MessageComponentType::Text: {
|
||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event);
|
||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first);
|
||||
auto body = EventHandler::rawMessageBody(*roomMessageEvent);
|
||||
return TextHandler().textComponents(body,
|
||||
EventHandler::messageBodyInputFormat(*roomMessageEvent),
|
||||
@@ -515,26 +534,17 @@ QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentT
|
||||
case MessageComponentType::File: {
|
||||
QList<MessageComponent> components;
|
||||
components += MessageComponent{MessageComponentType::File, QString(), {}};
|
||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event);
|
||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first);
|
||||
|
||||
if (m_emptyItinerary) {
|
||||
if (!m_isReply) {
|
||||
auto fileTransferInfo = m_room->cachedFileTransferInfo(event);
|
||||
auto fileTransferInfo = m_room->cachedFileTransferInfo(event.first);
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
Q_ASSERT(roomMessageEvent->content() != nullptr && roomMessageEvent->has<EventContent::FileContent>());
|
||||
const QMimeType mimeType = roomMessageEvent->get<EventContent::FileContent>()->mimeType;
|
||||
#else
|
||||
Q_ASSERT(roomMessageEvent->content() != nullptr && roomMessageEvent->hasFileContent());
|
||||
const QMimeType mimeType = roomMessageEvent->content()->fileInfo()->mimeType;
|
||||
#endif
|
||||
if (mimeType.name() == QStringLiteral("text/plain") || mimeType.parentMimeTypes().contains(QStringLiteral("text/plain"))) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
QString originalName = roomMessageEvent->get<EventContent::FileContent>()->originalName;
|
||||
#else
|
||||
QString originalName = roomMessageEvent->content()->fileInfo()->originalName;
|
||||
#endif
|
||||
if (originalName.isEmpty()) {
|
||||
originalName = roomMessageEvent->plainBody();
|
||||
}
|
||||
@@ -576,17 +586,24 @@ QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentT
|
||||
case MessageComponentType::Image:
|
||||
case MessageComponentType::Audio:
|
||||
case MessageComponentType::Video: {
|
||||
if (!event->is<StickerEvent>()) {
|
||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event);
|
||||
QList<MessageComponent> components;
|
||||
components += MessageComponent{type, QString(), {}};
|
||||
auto body = EventHandler::rawMessageBody(*roomMessageEvent);
|
||||
components += TextHandler().textComponents(body,
|
||||
EventHandler::messageBodyInputFormat(*roomMessageEvent),
|
||||
m_room,
|
||||
roomMessageEvent,
|
||||
roomMessageEvent->isReplaced());
|
||||
return components;
|
||||
if (!event.first->is<StickerEvent>()) {
|
||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first);
|
||||
const auto fileContent = roomMessageEvent->get<EventContent::FileContentBase>();
|
||||
if (fileContent != nullptr) {
|
||||
const auto fileInfo = fileContent->commonInfo();
|
||||
const auto body = EventHandler::rawMessageBody(*roomMessageEvent);
|
||||
// Do not attach the description to the image, if it's the same as the original filename.
|
||||
if (fileInfo.originalName != body) {
|
||||
QList<MessageComponent> components;
|
||||
components += MessageComponent{type, QString(), {}};
|
||||
components += TextHandler().textComponents(body,
|
||||
EventHandler::messageBodyInputFormat(*roomMessageEvent),
|
||||
m_room,
|
||||
roomMessageEvent,
|
||||
roomMessageEvent->isReplaced());
|
||||
return components;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
@@ -644,7 +661,7 @@ QList<MessageComponent> MessageContentModel::addLinkPreviews(QList<MessageCompon
|
||||
|
||||
void MessageContentModel::closeLinkPreview(int row)
|
||||
{
|
||||
if (row < 0 || row > m_components.size()) {
|
||||
if (row < 0 || row >= m_components.size()) {
|
||||
qWarning() << "closeLinkPreview() called with row" << row << "which does not exist. m_components.size() =" << m_components.size();
|
||||
return;
|
||||
}
|
||||
@@ -654,6 +671,7 @@ void MessageContentModel::closeLinkPreview(int row)
|
||||
m_removedLinkPreviews += m_components[row].attributes["link"_ls].toUrl();
|
||||
m_components.remove(row);
|
||||
m_components.squeeze();
|
||||
endResetModel();
|
||||
resetContent();
|
||||
}
|
||||
}
|
||||
@@ -661,17 +679,13 @@ void MessageContentModel::closeLinkPreview(int row)
|
||||
void MessageContentModel::updateItineraryModel()
|
||||
{
|
||||
const auto event = m_room->getEvent(m_eventId);
|
||||
if (m_room == nullptr || event == nullptr) {
|
||||
if (m_room == nullptr || event.first == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event)) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first)) {
|
||||
if (roomMessageEvent->has<EventContent::FileContent>()) {
|
||||
#else
|
||||
if (roomMessageEvent->hasFileContent()) {
|
||||
#endif
|
||||
auto filePath = m_room->cachedFileTransferInfo(event).localPath;
|
||||
auto filePath = m_room->cachedFileTransferInfo(event.first).localPath;
|
||||
if (filePath.isEmpty() && m_itineraryModel != nullptr) {
|
||||
delete m_itineraryModel;
|
||||
m_itineraryModel = nullptr;
|
||||
|
||||
@@ -31,6 +31,14 @@ class MessageContentModel : public QAbstractListModel
|
||||
Q_PROPERTY(bool showAuthor READ showAuthor WRITE setShowAuthor NOTIFY showAuthorChanged)
|
||||
|
||||
public:
|
||||
enum MessageState {
|
||||
Unknown, /**< The message state is unknown. */
|
||||
Pending, /**< The message is a new pending message which the server has not yet acknowledged. */
|
||||
Available, /**< The message is available and acknowledged by the server. */
|
||||
UnAvailable, /**< The message can't be retrieved either because it doesn't exist or is blocked. */
|
||||
};
|
||||
Q_ENUM(MessageState)
|
||||
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*/
|
||||
@@ -98,7 +106,6 @@ public:
|
||||
|
||||
Q_SIGNALS:
|
||||
void showAuthorChanged();
|
||||
void eventUnavailable();
|
||||
void eventUpdated();
|
||||
|
||||
private:
|
||||
@@ -107,10 +114,9 @@ private:
|
||||
QString m_eventSenderId;
|
||||
std::unique_ptr<NeochatRoomMember> m_eventSenderObject = nullptr;
|
||||
|
||||
bool m_isPending;
|
||||
MessageState m_currentState = Unknown;
|
||||
bool m_showAuthor = true;
|
||||
bool m_isReply;
|
||||
bool m_notFound = false;
|
||||
|
||||
void initializeModel();
|
||||
void initializeEvent();
|
||||
|
||||
@@ -160,12 +160,21 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
refreshLastUserEvents(i);
|
||||
}
|
||||
});
|
||||
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 0)
|
||||
connect(m_currentRoom, &Room::pendingEventAdded, this, [this](const Quotient::RoomEvent *event) {
|
||||
m_initialized = true;
|
||||
createEventObjects(event, true);
|
||||
beginInsertRows({}, 0, 0);
|
||||
endInsertRows();
|
||||
});
|
||||
#else
|
||||
connect(m_currentRoom, &Room::pendingEventAboutToAdd, this, [this](Quotient::RoomEvent *event) {
|
||||
m_initialized = true;
|
||||
createEventObjects(event);
|
||||
createEventObjects(event, true);
|
||||
beginInsertRows({}, 0, 0);
|
||||
});
|
||||
connect(m_currentRoom, &Room::pendingEventAdded, this, &MessageEventModel::endInsertRows);
|
||||
#endif
|
||||
connect(m_currentRoom, &Room::pendingEventAboutToMerge, this, [this](RoomEvent *, int i) {
|
||||
Q_EMIT dataChanged(index(i, 0), index(i, 0), {IsPendingRole});
|
||||
if (i == 0) {
|
||||
@@ -505,11 +514,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
|
||||
if (role == ProgressInfoRole) {
|
||||
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (e->has<EventContent::FileContent>()) {
|
||||
#else
|
||||
if (e->hasFileContent()) {
|
||||
#endif
|
||||
return QVariant::fromValue(m_currentRoom->cachedFileTransferInfo(&evt));
|
||||
}
|
||||
}
|
||||
@@ -622,7 +627,7 @@ int MessageEventModel::eventIdToRow(const QString &eventID) const
|
||||
return it - m_currentRoom->messageEvents().rbegin() + timelineBaseIndex();
|
||||
}
|
||||
|
||||
void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event)
|
||||
void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event, bool isPending)
|
||||
{
|
||||
if (event == nullptr) {
|
||||
return;
|
||||
@@ -645,7 +650,7 @@ void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event)
|
||||
|
||||
if (!m_contentModels.contains(eventId) && !m_contentModels.contains(event->transactionId())) {
|
||||
if (!event->isStateEvent() || event->matrixType() == QStringLiteral("org.matrix.msc3672.beacon_info")) {
|
||||
m_contentModels[eventId] = std::unique_ptr<MessageContentModel>(new MessageContentModel(m_currentRoom, eventId));
|
||||
m_contentModels[eventId] = std::unique_ptr<MessageContentModel>(new MessageContentModel(m_currentRoom, eventId, false, isPending));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ private:
|
||||
int refreshEventRoles(const QString &eventId, const QList<int> &roles = {});
|
||||
void moveReadMarker(const QString &toEventId);
|
||||
|
||||
void createEventObjects(const Quotient::RoomEvent *event);
|
||||
void createEventObjects(const Quotient::RoomEvent *event, bool isPending = false);
|
||||
// Hack to ensure that we don't call endInsertRows when we haven't called beginInsertRows
|
||||
bool m_initialized = false;
|
||||
|
||||
|
||||
@@ -311,12 +311,7 @@ void PushRuleModel::addKeyword(const QString &keyword, const QString &roomId)
|
||||
pushConditions.append(keywordCondition);
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
auto job = m_connection->callApi<Quotient::SetPushRuleJob>(PushRuleKind::kindString(kind),
|
||||
#else
|
||||
auto job = m_connection->callApi<Quotient::SetPushRuleJob>(QLatin1String("global"),
|
||||
PushRuleKind::kindString(kind),
|
||||
#endif
|
||||
keyword,
|
||||
actions,
|
||||
QString(),
|
||||
@@ -341,11 +336,7 @@ void PushRuleModel::removeKeyword(const QString &keyword)
|
||||
}
|
||||
|
||||
auto kind = PushRuleKind::kindString(m_rules[index].kind);
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(kind, m_rules[index].id);
|
||||
#else
|
||||
auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(QStringLiteral("global"), kind, m_rules[index].id);
|
||||
#endif
|
||||
connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
|
||||
qWarning() << QLatin1String("Unable to remove push rule for keyword %1: ").arg(m_rules[index].id) << job->errorString();
|
||||
});
|
||||
@@ -353,18 +344,10 @@ void PushRuleModel::removeKeyword(const QString &keyword)
|
||||
|
||||
void PushRuleModel::setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled)
|
||||
{
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(kind, ruleId);
|
||||
#else
|
||||
auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(QStringLiteral("global"), kind, ruleId);
|
||||
#endif
|
||||
connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled, this]() {
|
||||
if (job->enabled() != enabled) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
m_connection->callApi<Quotient::SetPushRuleEnabledJob>(kind, ruleId, enabled);
|
||||
#else
|
||||
m_connection->callApi<Quotient::SetPushRuleEnabledJob>(QStringLiteral("global"), kind, ruleId, enabled);
|
||||
#endif
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -378,11 +361,7 @@ void PushRuleModel::setNotificationRuleActions(const QString &kind, const QStrin
|
||||
actions = actionToVariant(action);
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
m_connection->callApi<Quotient::SetPushRuleActionsJob>(kind, ruleId, actions);
|
||||
#else
|
||||
m_connection->callApi<Quotient::SetPushRuleActionsJob>(QStringLiteral("global"), kind, ruleId, actions);
|
||||
#endif
|
||||
}
|
||||
|
||||
PushRuleAction::Action PushRuleModel::variantToAction(const QList<QVariant> &actions, bool enabled)
|
||||
|
||||
@@ -212,7 +212,7 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
|
||||
return room->displayName().toHtmlEscaped();
|
||||
}
|
||||
if (role == AvatarRole) {
|
||||
return room->avatarMediaId();
|
||||
return room->avatarMediaUrl();
|
||||
}
|
||||
if (role == CanonicalAliasRole) {
|
||||
return room->canonicalAlias();
|
||||
|
||||
@@ -286,6 +286,7 @@ QHash<int, QByteArray> RoomTreeModel::roleNames() const
|
||||
roles[IconRole] = "icon";
|
||||
roles[AttentionRole] = "attention";
|
||||
roles[FavouriteRole] = "favourite";
|
||||
roles[RoomTypeRole] = "roomType";
|
||||
return roles;
|
||||
}
|
||||
|
||||
@@ -323,7 +324,7 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
||||
return room->displayName();
|
||||
}
|
||||
if (role == AvatarRole) {
|
||||
return room->avatarMediaId();
|
||||
return room->avatarMediaUrl();
|
||||
}
|
||||
if (role == CanonicalAliasRole) {
|
||||
return room->canonicalAlias();
|
||||
@@ -385,6 +386,11 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
||||
if (role == FavouriteRole) {
|
||||
return room->isFavourite();
|
||||
}
|
||||
if (role == RoomTypeRole) {
|
||||
if (room->creation()) {
|
||||
return room->creation()->contentPart<QString>("type"_L1);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
IconRole,
|
||||
AttentionRole, /**< Whether there are any notifications. */
|
||||
FavouriteRole, /**< Whether the room is favourited. */
|
||||
RoomTypeRole, /**< The room's type. */
|
||||
};
|
||||
Q_ENUM(EventRoles)
|
||||
explicit RoomTreeModel(QObject *parent = nullptr);
|
||||
|
||||
@@ -157,6 +157,11 @@ bool SortFilterRoomTreeModel::filterAcceptsRow(int source_row, const QModelIndex
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hide rooms with defined types, assuming that data-holding rooms have a defined type
|
||||
if (!sourceModel()->data(index, RoomTreeModel::RoomTypeRole).toString().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static auto config = NeoChatConfig::self();
|
||||
if (config->allRoomsInHome() && RoomManager::instance().currentSpace().isEmpty()) {
|
||||
return acceptRoom;
|
||||
|
||||
@@ -93,11 +93,7 @@ void SpaceChildrenModel::refreshModel()
|
||||
});
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR >= 9
|
||||
void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::SpaceHierarchyRoomsChunk> children, const QModelIndex &parent)
|
||||
#else
|
||||
void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::ChildRoomsChunk> children, const QModelIndex &parent)
|
||||
#endif
|
||||
{
|
||||
SpaceTreeItem *parentItem = getItem(parent);
|
||||
|
||||
|
||||
@@ -144,9 +144,5 @@ private:
|
||||
|
||||
void refreshModel();
|
||||
|
||||
#if Quotient_VERSION_MINOR >= 9
|
||||
void insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::SpaceHierarchyRoomsChunk> children, const QModelIndex &parent = QModelIndex());
|
||||
#else
|
||||
void insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::ChildRoomsChunk> children, const QModelIndex &parent = QModelIndex());
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <Quotient/events/event.h>
|
||||
#include <Quotient/events/stickerevent.h>
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/omittable.h>
|
||||
#include <memory>
|
||||
|
||||
#include "chatbarcache.h"
|
||||
|
||||
@@ -87,11 +87,7 @@ QVariant UserListModel::data(const QModelIndex &index, int role) const
|
||||
return memberId;
|
||||
}
|
||||
if (role == AvatarRole) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
return m_currentRoom->member(memberId).avatarUrl();
|
||||
#else
|
||||
return m_currentRoom->memberAvatar(memberId).url();
|
||||
#endif
|
||||
}
|
||||
if (role == ObjectRole) {
|
||||
return QVariant::fromValue(memberId);
|
||||
@@ -176,11 +172,7 @@ void UserListModel::refreshAllMembers()
|
||||
|
||||
if (m_currentRoom != nullptr) {
|
||||
m_members = m_currentRoom->joinedMemberIds();
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
MemberSorter sorter;
|
||||
#else
|
||||
MemberSorter sorter(m_currentRoom);
|
||||
#endif
|
||||
std::sort(m_members.begin(), m_members.end(), [&sorter, this](const auto &left, const auto &right) {
|
||||
const auto leftPl = m_currentRoom->getUserPowerLevel(left);
|
||||
const auto rightPl = m_currentRoom->getUserPowerLevel(right);
|
||||
|
||||
@@ -261,7 +261,7 @@ Action=Popup
|
||||
Name=Share
|
||||
Name[ar]=شارك
|
||||
Name[ca]=Compartició
|
||||
Name[ca@valencia]=Compartició
|
||||
Name[ca@valencia]=Compartiu
|
||||
Name[cs]=Sdílet
|
||||
Name[de]=Teilen
|
||||
Name[el]=Κοινοποίηση
|
||||
|
||||
@@ -27,6 +27,18 @@
|
||||
<label>Use s/text/replacement syntax to edit your last message.</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="SendMessageWith" type="Enum">
|
||||
<label>Key combination to send a message</label>
|
||||
<choices>
|
||||
<choice name="Enter">
|
||||
<label>Enter</label>
|
||||
</choice>
|
||||
<choice name="CtrlEnter">
|
||||
<label>Ctrl+Enter</label>
|
||||
</choice>
|
||||
<default>Enter</default>
|
||||
</choices>
|
||||
</entry>
|
||||
<entry name="ShowLocalMessagesOnRight" type="bool">
|
||||
<label>"Show your messages on the right</label>
|
||||
<default>true</default>
|
||||
|
||||
@@ -533,7 +533,6 @@ LinkPreviewer *NeoChatConnection::previewerForLink(const QUrl &link)
|
||||
return previewer;
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
KeyImport::Error NeoChatConnection::exportMegolmSessions(const QString &passphrase, const QString &path)
|
||||
{
|
||||
KeyImport keyImport;
|
||||
@@ -548,6 +547,5 @@ KeyImport::Error NeoChatConnection::exportMegolmSessions(const QString &passphra
|
||||
file.close();
|
||||
return KeyImport::Success;
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "moc_neochatconnection.cpp"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user