Compare commits
7 Commits
work/tobia
...
work/carl/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3748b6902c | ||
|
|
a4917d82e9 | ||
|
|
b9bf37089b | ||
|
|
1844a90fc0 | ||
|
|
23099046a3 | ||
|
|
bdf4ee43c8 | ||
|
|
c6300179d8 |
@@ -2,7 +2,7 @@
|
||||
"id": "org.kde.neochat",
|
||||
"branch": "master",
|
||||
"runtime": "org.kde.Platform",
|
||||
"runtime-version": "6.6",
|
||||
"runtime-version": "6.6-kf6preview",
|
||||
"sdk": "org.kde.Sdk",
|
||||
"command": "neochat",
|
||||
"tags": [
|
||||
|
||||
@@ -49,7 +49,3 @@ License: CC0-1.0
|
||||
Files: appiumtests/data/*
|
||||
Copyright: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
License: CC0-1.0
|
||||
|
||||
Files: src/purpose/purposeplugin.json
|
||||
Copyright: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
License: BSD-2-Clause
|
||||
|
||||
@@ -14,7 +14,7 @@ set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_
|
||||
|
||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||
|
||||
set(KF_MIN_VERSION "6.0")
|
||||
set(KF_MIN_VERSION "5.240.0")
|
||||
set(QT_MIN_VERSION "6.5")
|
||||
|
||||
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
|
||||
@@ -24,7 +24,7 @@ set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(KDE_COMPILERSETTINGS_LEVEL 6.0)
|
||||
set(KDE_COMPILERSETTINGS_LEVEL 5.105)
|
||||
|
||||
include(FeatureSummary)
|
||||
include(ECMSetupVersion)
|
||||
@@ -72,10 +72,6 @@ set_package_properties(KF6Kirigami PROPERTIES
|
||||
)
|
||||
find_package(KF6KirigamiAddons 0.7.2 REQUIRED)
|
||||
|
||||
if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT NEOCHAT_FLATPAK AND NOT NEOCHAT_APPIMAGE)
|
||||
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Purpose)
|
||||
endif ()
|
||||
|
||||
if(ANDROID)
|
||||
find_package(OpenSSL)
|
||||
set_package_properties(OpenSSL PROPERTIES
|
||||
|
||||
@@ -23,6 +23,11 @@ ecm_add_test(
|
||||
TEST_NAME delegatesizehelpertest
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
roomtreemodeltest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
)
|
||||
|
||||
ecm_add_test(
|
||||
mediasizehelpertest.cpp
|
||||
LINK_LIBRARIES neochat Qt::Test
|
||||
|
||||
@@ -55,7 +55,6 @@ private Q_SLOTS:
|
||||
void genericBody_data();
|
||||
void genericBody();
|
||||
void nullGenericBody();
|
||||
void markdownBody();
|
||||
void subtitle();
|
||||
void nullSubtitle();
|
||||
void mediaInfo();
|
||||
@@ -294,13 +293,6 @@ void EventHandlerTest::nullGenericBody()
|
||||
QCOMPARE(noEventHandler.getGenericBody(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::markdownBody()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||
|
||||
QCOMPARE(eventHandler.getMarkdownBody(), QStringLiteral("This is an example\ntext message"));
|
||||
}
|
||||
|
||||
void EventHandlerTest::subtitle()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <Quotient/syncdata.h>
|
||||
#include "neochatconnection.h"
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
@@ -27,7 +25,8 @@ private Q_SLOTS:
|
||||
|
||||
void NeoChatRoomTest::initTestCase()
|
||||
{
|
||||
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
|
||||
auto connection = new NeoChatConnection;
|
||||
Connection::makeMockConnection(connection, QStringLiteral("@bob:kde.org"));
|
||||
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), "test-min-sync.json"_ls);
|
||||
}
|
||||
|
||||
|
||||
70
autotests/roomtreemodeltest.cpp
Normal file
70
autotests/roomtreemodeltest.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
// SPDX-FileCopyrightText: 2024 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include <QAbstractItemModelTester>
|
||||
#include <QTest>
|
||||
|
||||
#include "enums/neochatroomtype.h"
|
||||
#include "models/roomtreemodel.h"
|
||||
#include "models/sortfilterroomtreemodel.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "testutils.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
class RoomTreeModelTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void testTreeModel();
|
||||
};
|
||||
|
||||
void RoomTreeModelTest::testTreeModel()
|
||||
{
|
||||
auto connection = new NeoChatConnection;
|
||||
Connection::makeMockConnection(connection, QStringLiteral("@bob:kde.org"));
|
||||
|
||||
auto room = dynamic_cast<NeoChatRoom *>(new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QStringLiteral("test-min-sync.json")));
|
||||
QVERIFY(room);
|
||||
connection->addRoom(room);
|
||||
|
||||
RoomTreeModel model;
|
||||
model.setConnection(connection);
|
||||
|
||||
SortFilterRoomTreeModel filterModel;
|
||||
filterModel.setSourceModel(&model);
|
||||
|
||||
QAbstractItemModelTester tester(&model);
|
||||
QAbstractItemModelTester testerFilter(&filterModel);
|
||||
|
||||
QCOMPARE(model.rowCount(), static_cast<int>(NeoChatRoomType::TypesCount));
|
||||
|
||||
// Check data category
|
||||
auto category = static_cast<int>(NeoChatRoomType::typeForRoom(room));
|
||||
QCOMPARE(category, NeoChatRoomType::Normal);
|
||||
auto normalCategoryIdx = model.index(category, 0);
|
||||
QCOMPARE(model.data(normalCategoryIdx, RoomTreeModel::DisplayNameRole).toString(), QStringLiteral("Normal"));
|
||||
QCOMPARE(model.data(normalCategoryIdx, RoomTreeModel::DelegateTypeRole).toString(), QStringLiteral("section"));
|
||||
QCOMPARE(model.data(normalCategoryIdx, RoomTreeModel::IconRole).toString(), QStringLiteral("group"));
|
||||
QCOMPARE(model.data(normalCategoryIdx, RoomTreeModel::CategoryRole).toInt(), category);
|
||||
QCOMPARE(model.rowCount(normalCategoryIdx), 1);
|
||||
|
||||
// Check data room
|
||||
auto roomIdx = model.index(0, 0, normalCategoryIdx);
|
||||
QCOMPARE(model.data(roomIdx, RoomTreeModel::CurrentRoomRole).value<NeoChatRoom *>(), room);
|
||||
QCOMPARE(model.data(roomIdx, RoomTreeModel::CategoryRole).toInt(), category);
|
||||
|
||||
// Move room
|
||||
room->setProperty("isFavorite", true);
|
||||
model.moveRoom(room);
|
||||
|
||||
auto newCategory = static_cast<int>(NeoChatRoomType::typeForRoom(room));
|
||||
QCOMPARE(newCategory, NeoChatRoomType::Favorite);
|
||||
auto newCategoryIdx = model.index(newCategory, 0);
|
||||
QVERIFY(newCategoryIdx != normalCategoryIdx);
|
||||
}
|
||||
|
||||
QTEST_MAIN(RoomTreeModelTest)
|
||||
|
||||
#include "roomtreemodeltest.moc"
|
||||
@@ -513,7 +513,7 @@ void TextHandlerTest::componentOutput_data()
|
||||
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}},
|
||||
MessageComponent{MessageComponentType::Code,
|
||||
QStringLiteral("Some code"),
|
||||
QVariantMap{{QStringLiteral("class"), QStringLiteral("html")}}}};
|
||||
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\""), {}}};
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
<summary xml:lang="fi">Keskustelu ystäviesi kanssa Matrixissa</summary>
|
||||
<summary xml:lang="fr">Discuter avec vos ami(e)s sur le réseau Matrix</summary>
|
||||
<summary xml:lang="gl">Charle coas súas amizades en Matrix.</summary>
|
||||
<summary xml:lang="hu">Csevegjen barátaival a matrixon</summary>
|
||||
<summary xml:lang="ia">Starta Conversation con tu amicos sur matrix</summary>
|
||||
<summary xml:lang="it">Conversa con i tuoi contatti su matrix</summary>
|
||||
<summary xml:lang="ka">ესაუბრეთ მეგობრებს Matrix-ზე</summary>
|
||||
@@ -79,11 +78,8 @@
|
||||
<p>NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
|
||||
<p xml:lang="ca">El NeoChat és una aplicació de xat que us permet aprofitar plenament la xarxa Matrix. Proporciona una manera segura d'enviar missatges de text, vídeos i arxius d'àudio a la vostra família, companys i amics.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat és una aplicació de xat que us permet aprofitar plenament la xarxa Matrix. Proporciona una manera segura d'enviar missatges de text, vídeos i arxius d'àudio a la vostra família, companys i amics.</p>
|
||||
<p xml:lang="eo">NeoChat estas babilej-apo, kiu ebligas al vi plene profiti de la Matrix-reto. Ĝi provizas al vi sekuran manieron sendi tekstmesaĝojn, filmetojn kaj sondosierojn al via familio, kolegoj kaj amikoj.</p>
|
||||
<p xml:lang="es">NeoChat es una aplicación de chat que le permite aprovechar al máximo la red Matrix. Le proporciona un modo seguro de enviar mensajes de texto, vídeos y archivos de sonido a su familia, colegas y amigos.</p>
|
||||
<p xml:lang="eu">NeoChat, Matrix sarearen abantaila guztiei probetsua ateratzeko aukera ematen dizun berriketa aplikaizo bat da. Zure familiari, kideei eta lagunei testu mezuak, bideoak eta audio fitxategiak era seguruan bidaltzeko aukera ematen dizu.</p>
|
||||
<p xml:lang="fr">NeoChat est une application de discussions vous permettant de profiter pleinement du réseau Matrix. Elle vous offre un moyen sécurisé d’envoyer des messages de texte, des vidéos et des fichiers audio à votre famille, vos collègues et vos ami(e)s.</p>
|
||||
<p xml:lang="hu">A NeoChat egy olyan csevegőalkalmazás, amellyel teljes mértékben kihasználhatja a Matrix hálózatot. Biztonságos módot biztosít szöveges üzenetek, videók és hangfájlok küldéséhez családtagjainak, kollégáinak és barátainak.</p>
|
||||
<p xml:lang="ia">NeoChat es un app de conversation que te permitte prender avantage plen del rete Matrix. Il te forni un modo secur de inviar messages de texto, videos e files audio a tui familia, collegas e amicos.</p>
|
||||
<p xml:lang="it">NeoChat è un'applicazione di chat che ti consente di sfruttare appieno la rete Matrix. Ti fornisce un modo sicuro per inviare messaggi di testo, video e file audio a familiari, colleghi e amici.</p>
|
||||
<p xml:lang="ka">NeoChat ჩატის აპია, რომელიც საშუალება გაძლევთ, Matrix-ის ქსელის საშუალებები ბოლომდე გამოიყენოთ. ის გაძლევთ უსაფრთხო გზას, გააგზავნოთ ტექსტური შეტყობინებები, ვიდეოებ და აუდიოფაილები თქვენს ოჯახთან, კოლეგებთან და მეგობრებთან.</p>
|
||||
@@ -93,7 +89,6 @@
|
||||
<p xml:lang="tr">NeoChat, Matrix ağının tüm özelliklerini kullanan bir sohbet uygulamasıdır. Ailenize, arkadaşlarınıza ve iş arkadaşlarınıza metin iletileri, ses ve video dosyaları göndermenin kolay bir yolunu sunar.</p>
|
||||
<p xml:lang="uk">NeoChat є програмою для спілкування, за допомогою якої ви можете скористатися усіма перевагами мережі Matrix. За її допомогою ви можете безпечно надсилати текстові повідомлення, відео та звукові файли вашим родичам, колегам та друзям.</p>
|
||||
<p xml:lang="x-test">xxNeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.xx</p>
|
||||
<p xml:lang="zh-TW">NeoChat 是一個讓您能夠完全利用 Matrix 網路的聊天應用程式。它讓您安全地傳送文字訊息、影片或音訊檔給家人、同事或朋友等等。</p>
|
||||
<p>NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
|
||||
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. على هذا النحو يتم دعم كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP والخيوط وبعض جوانب التشفير من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار ، ولكن يبقى الهدف توفير الدعم النهائي للمواصفات بأكملها.</p>
|
||||
<p xml:lang="ca">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptatge d'extrem a extrem. Hi ha algunes altres omissions més petites a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu segueix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
@@ -105,7 +100,6 @@
|
||||
<p xml:lang="fi">NeoChat pyrkii olemaan Matrix-määritelmän täysominaisuuksinen sovellus, joten se tukee kaikkea nykyisessä vakaassa määritelmässä muutamaa huomattavaa poikkeusta lukuun ottamatta (VoIP, säikeet ja jotkin piirteet päästä päähän -salauksessa). Joitakin pienempiäkin puutteita on Matrix-määritelmän jatkuvan kehityksen vuoksi, mutta lopputavoitteena on tarjota määritelmän täysi tuki.</p>
|
||||
<p xml:lang="fr">L'objectif de NeoChat est d'être une application complète pour le protocole Matrix. En tant que tel, tout dans la spécification stable actuelle avec les exceptions notables de VoIP, les processus et certains aspects du chiffrement de bout en bout sont pris en charge. Il y a quelques autres petites omissions en raison du fait que la spécification du protocole Matrix est en constante évolution. Cependant, l'objectif reste de fournir un soutien éventuel pour l'ensemble de la spécification.</p>
|
||||
<p xml:lang="gl">NeoChat pretende ser unha aplicación completa para a especificación de Matrix. Coas excepcións de VoIP, conversas fiadas e algúns aspectos da cifraxe de extremo a extremo, a versión estábel segue as especificacións. Existen algunhas outras pequenas omisións debido ao feito de que Matrix está en continua evolución pero a intención é implementar a especificación completa.</p>
|
||||
<p xml:lang="hu">A NeoChat célja, hogy a Matrix specifikációnak megfelelő teljes funkcionalitású alkalmazás legyen. Mint ilyen, a jelenlegi stabil specifikáció támogatott a VoIP, a szálak és a végpontok közötti titkosítás egyes elemeinek kivételével. Van még néhány kisebb hiányosság annak köszönhetően, hogy a Matrix specifikáció folyamatosan fejlődik, de végső cél a teljes specifikáció megvalósítása.</p>
|
||||
<p xml:lang="ia">NeoChat aspira a esser un application plenemente eminente per le specification de Matrix. Tal como omne cosas in le specification currentemente stabile con le exceptiones notabile de VOIP, threads e alcun aspectos del cryptation End-to-End es supportate. Il ha ltere pauc omissiones, debite al facto que le specification de Matrix es in evolution constante ma le aspiration remane a fornir supporto eventual per le integre specification.</p>
|
||||
<p xml:lang="it">NeoChat mira ad essere un'applicazione completa per le specifiche Matrix. Pertanto, sono supportati tutti gli elementi dell'attuale specifica stabile con le notevoli eccezioni di VoIP, conversazioni e alcuni aspetti della cifratura end-to-end. Ci sono alcune altre piccole omissioni dovute al fatto che le specifiche Matrix sono in continua evoluzione, ma l'obiettivo rimane quello di fornire un eventuale supporto per l'intera specifica.</p>
|
||||
<p xml:lang="ka">NeoChat მიზნად ისახავს Matrix სპეციფიკაციის სრული განხორციელება ჰქონდეს. როგორც ასეთი, ყველაფერი მიმდინარე სპეციფიკაციიდან, VoIP-ის, ძაფებისა და გამჭოლი დაშიფვრის ზოგიერთი ასპექტის გარდა, მხარდაჭერილია. შეძლება ასევე იყოს მცირე ლაფსუსებიც იმის გამო, რომ Matrix-ის სპეციფიკაცია მუდმივად ვითარდება, მაგრამ ჩვენი მიზანი მისი სრული მხარდაჭერაა.</p>
|
||||
@@ -131,7 +125,6 @@
|
||||
<p xml:lang="fi">Matrix-määritelmän kehittyessä NeoChat tukee myös monia epävakaita ominaisuuksia. Tällä hetkellä näitä ovat:</p>
|
||||
<p xml:lang="fr">En raison de la nature du développement des spécifications du protocole Matrix, NeoChat prend également en charge de nombreuses fonctionnalités instables. Actuellement, ce sont :</p>
|
||||
<p xml:lang="gl">Debido á natureza do desenvolvemento da especificación de Matrix, NeoChat tamén inclúe varias funcionalidades non estábeis:</p>
|
||||
<p xml:lang="hu">A Matrix specifikáció fejlesztésének jellegéből adódóan a NeoChat számos instabil funkciót is támogat. Jelenleg a következőket:</p>
|
||||
<p xml:lang="ia">Debite al natura del disveloppamento de specification de Matrix NeoChat tamben supporta numerose characteristicas instabile. Currentemente istes es:</p>
|
||||
<p xml:lang="it">A causa della natura dello sviluppo delle specifiche Matrix, NeoChat supporta anche numerose funzionalità instabili. Attualmente queste sono:</p>
|
||||
<p xml:lang="ka">Matrix-ის სპეციფიკაციის განვითარების ბუნების გამო NeoChat-ს ასევე აქვს უამრავი არასტაბილური ფუნქციაც. ახლა ისინია:</p>
|
||||
@@ -159,7 +152,6 @@
|
||||
<li xml:lang="fi">Kyselyt – MSC3381</li>
|
||||
<li xml:lang="fr">Sondages - MSC3381</li>
|
||||
<li xml:lang="gl">Enquisas — MSC3381</li>
|
||||
<li xml:lang="hu">Szavazások - MSC3381</li>
|
||||
<li xml:lang="ia">Inquestas - MSC3381</li>
|
||||
<li xml:lang="it">Sondaggi - MSC3381</li>
|
||||
<li xml:lang="ka">Polls - MSC3381</li>
|
||||
@@ -186,7 +178,6 @@
|
||||
<li xml:lang="fi">Tarrapakkaukset – MSC2545</li>
|
||||
<li xml:lang="fr">Paquets d'auto-collants - MSC2545</li>
|
||||
<li xml:lang="gl">Paquetes de adhesivos — MSC2545</li>
|
||||
<li xml:lang="hu">Matricacsomagok - MSC2545</li>
|
||||
<li xml:lang="ia">Etiquetta gummate (sticker) -MSC2545</li>
|
||||
<li xml:lang="it">Pacchetti di adesivi - MSC2545</li>
|
||||
<li xml:lang="ka">სტიკერების პაკეტები - MSC2545</li>
|
||||
@@ -213,7 +204,6 @@
|
||||
<li xml:lang="fi">Sijaintitapahtumat – MSC3488</li>
|
||||
<li xml:lang="fr">Événements de lieu - MSC3488</li>
|
||||
<li xml:lang="gl">Localización de eventos — MSC3488</li>
|
||||
<li xml:lang="hu">Események helyadatai - MSC3488</li>
|
||||
<li xml:lang="ia">Eventos de Location - MSC3488</li>
|
||||
<li xml:lang="it">Località eventi - MSC3488</li>
|
||||
<li xml:lang="ka">მდებარეობის მოვლენები - MSC3488</li>
|
||||
@@ -244,7 +234,8 @@
|
||||
<keyword>Matrix</keyword>
|
||||
<keyword>Kirigami</keyword>
|
||||
</keywords>
|
||||
<developer id="kde.org">
|
||||
<developer>
|
||||
<id>kde.org</id>
|
||||
<name>The KDE Community</name>
|
||||
<url>https://kde.org</url>
|
||||
</developer>
|
||||
@@ -273,7 +264,6 @@
|
||||
<caption xml:lang="fi">Päänäkymä, jossa huoneluettelo, keskustelu ja huoneen tiedot</caption>
|
||||
<caption xml:lang="fr">Vue principale avec la liste des salons ainsi que des informations sur les salons et forums de discussions</caption>
|
||||
<caption xml:lang="gl">Vista principal coa lista de salas, a charla, e información da sala.</caption>
|
||||
<caption xml:lang="hu">A fő nézet a szobalistával, csevegéssel és szobainformációkkal</caption>
|
||||
<caption xml:lang="ia">Vista principal con lista de sala, chat e information de sala</caption>
|
||||
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
|
||||
<caption xml:lang="ka">მთავარი ხედი სურათების სიით, ჩატით და ოთახის ინფორმაციით</caption>
|
||||
@@ -295,11 +285,8 @@
|
||||
<caption>Discover new communities with Matrix Spaces</caption>
|
||||
<caption xml:lang="ca">Descobriu comunitats noves amb els espais de Matrix</caption>
|
||||
<caption xml:lang="ca-valencia">Descobriu comunitats noves amb els espais de Matrix</caption>
|
||||
<caption xml:lang="eo">Malkovru novajn komunumojn per Matrix Spaces</caption>
|
||||
<caption xml:lang="es">Descubra nuevas comunidades con los espacios de Matrix</caption>
|
||||
<caption xml:lang="eu">Ezagutu komunitate berriak Matrixeko Tokiak erabiliz</caption>
|
||||
<caption xml:lang="fr">Découvrez de nouvelles communautés avec les espaces sous Matrix</caption>
|
||||
<caption xml:lang="hu">Fedezzen fel új közösségeket a Matrix Terek segítségével</caption>
|
||||
<caption xml:lang="ia">Discoperi nove communitate con Matrix Spaces (Spatios de Matrix)</caption>
|
||||
<caption xml:lang="it">Scopri nuove comunità con Matrix Spaces</caption>
|
||||
<caption xml:lang="ka">აღმოაჩინეთ ახალი საზოგადოებები Matrix Spaces-თან ერთად</caption>
|
||||
@@ -309,7 +296,6 @@
|
||||
<caption xml:lang="tr">Matrix Alanlar ile yeni topluluklar keşfedin</caption>
|
||||
<caption xml:lang="uk">Пошук нових спільнот за допомогою Matrix Spaces</caption>
|
||||
<caption xml:lang="x-test">xxDiscover new communities with Matrix Spacesxx</caption>
|
||||
<caption xml:lang="zh-TW">利用 Matrix 聊天空間發現新的社群</caption>
|
||||
</screenshot>
|
||||
<!--
|
||||
Currently invalid. See https://github.com/ximion/appstream/issues/611
|
||||
@@ -330,7 +316,6 @@
|
||||
<caption xml:lang="fi">Päänäkymä, jossa huoneluettelo, keskustelu ja huoneen tiedot</caption>
|
||||
<caption xml:lang="fr">Vue principale avec la liste des salons ainsi que des informations sur les salons et forums de discussions</caption>
|
||||
<caption xml:lang="gl">Vista principal coa lista de salas, a charla, e información da sala.</caption>
|
||||
<caption xml:lang="hu">A fő nézet a szobalistával, csevegéssel és szobainformációkkal</caption>
|
||||
<caption xml:lang="ia">Vista principal con lista de sala, chat e information de sala</caption>
|
||||
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
|
||||
<caption xml:lang="ka">მთავარი ხედი სურათების სიით, ჩატით და ოთახის ინფორმაციით</caption>
|
||||
@@ -360,7 +345,6 @@
|
||||
<caption xml:lang="fi">Kirjautumisnäkymä</caption>
|
||||
<caption xml:lang="fr">Écran de connexion</caption>
|
||||
<caption xml:lang="gl">Pantalla de identificación.</caption>
|
||||
<caption xml:lang="hu">Bejelentkező képernyő</caption>
|
||||
<caption xml:lang="ia">Schermo de accesso</caption>
|
||||
<caption xml:lang="it">Schermata di accesso</caption>
|
||||
<caption xml:lang="ka">შესვლის ეკრანი</caption>
|
||||
@@ -382,7 +366,6 @@
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release version="24.02.1" date="2024-03-21"/>
|
||||
<release version="24.02.0" date="2024-02-28">
|
||||
<url>https://kde.org/announcements/megarelease/6/#neochat</url>
|
||||
<description>
|
||||
|
||||
635
po/ar/neochat.po
635
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
628
po/az/neochat.po
628
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
641
po/ca/neochat.po
641
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
629
po/cs/neochat.po
629
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
631
po/de/neochat.po
631
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
632
po/el/neochat.po
632
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
646
po/eo/neochat.po
646
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
632
po/es/neochat.po
632
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
633
po/eu/neochat.po
633
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
636
po/fr/neochat.po
636
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
1980
po/hu/neochat.po
1980
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
629
po/ia/neochat.po
629
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
629
po/id/neochat.po
629
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
626
po/ie/neochat.po
626
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
638
po/it/neochat.po
638
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
614
po/ja/neochat.po
614
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
632
po/ka/neochat.po
632
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
623
po/ko/neochat.po
623
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
618
po/lt/neochat.po
618
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
630
po/nl/neochat.po
630
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
637
po/nn/neochat.po
637
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
626
po/pa/neochat.po
626
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
629
po/pl/neochat.po
629
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
631
po/pt/neochat.po
631
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
631
po/ru/neochat.po
631
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
628
po/sk/neochat.po
628
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
1115
po/sl/neochat.po
1115
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
631
po/sv/neochat.po
631
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
721
po/ta/neochat.po
721
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
633
po/tr/neochat.po
633
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
632
po/uk/neochat.po
632
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,10 +3,6 @@
|
||||
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT NEOCHAT_FLATPAK AND NOT NEOCHAT_APPIMAGE)
|
||||
add_subdirectory(purpose)
|
||||
endif()
|
||||
|
||||
add_library(neochat STATIC
|
||||
controller.cpp
|
||||
controller.h
|
||||
@@ -169,8 +165,6 @@ add_library(neochat STATIC
|
||||
mediamanager.h
|
||||
models/statekeysmodel.cpp
|
||||
models/statekeysmodel.h
|
||||
sharehandler.cpp
|
||||
sharehandler.h
|
||||
)
|
||||
|
||||
qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
@@ -213,6 +207,19 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/RoomData.qml
|
||||
qml/ServerData.qml
|
||||
qml/EmojiPicker.qml
|
||||
qml/TimelineDelegate.qml
|
||||
qml/ReplyComponent.qml
|
||||
qml/StateDelegate.qml
|
||||
qml/MessageDelegate.qml
|
||||
qml/Bubble.qml
|
||||
qml/SectionDelegate.qml
|
||||
qml/ReactionDelegate.qml
|
||||
qml/EventDelegate.qml
|
||||
qml/ReadMarkerDelegate.qml
|
||||
qml/MimeComponent.qml
|
||||
qml/StateComponent.qml
|
||||
qml/MessageEditComponent.qml
|
||||
qml/AvatarFlow.qml
|
||||
qml/LoginStep.qml
|
||||
qml/Login.qml
|
||||
qml/Homeserver.qml
|
||||
@@ -297,10 +304,24 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/SelectSpacesDialog.qml
|
||||
qml/AttachDialog.qml
|
||||
qml/NotificationsView.qml
|
||||
qml/LoadingDelegate.qml
|
||||
qml/TimelineEndDelegate.qml
|
||||
qml/SearchPage.qml
|
||||
qml/ServerComboBox.qml
|
||||
qml/UserSearchPage.qml
|
||||
qml/ManualUserDialog.qml
|
||||
qml/MessageComponentChooser.qml
|
||||
qml/TextComponent.qml
|
||||
qml/ImageComponent.qml
|
||||
qml/VideoComponent.qml
|
||||
qml/AudioComponent.qml
|
||||
qml/EncryptedComponent.qml
|
||||
qml/FileComponent.qml
|
||||
qml/LocationComponent.qml
|
||||
qml/LiveLocationComponent.qml
|
||||
qml/PollComponent.qml
|
||||
qml/LinkPreviewComponent.qml
|
||||
qml/LoadComponent.qml
|
||||
qml/RecommendedSpaceDialog.qml
|
||||
qml/RoomTreeSection.qml
|
||||
qml/DelegateContextMenu.qml
|
||||
@@ -309,13 +330,13 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/IgnoredUsersDialog.qml
|
||||
qml/AccountData.qml
|
||||
qml/StateKeys.qml
|
||||
qml/CodeComponent.qml
|
||||
qml/QuoteComponent.qml
|
||||
RESOURCES
|
||||
qml/confetti.png
|
||||
qml/glowdot.png
|
||||
)
|
||||
|
||||
add_subdirectory(timeline)
|
||||
|
||||
if(UNIX)
|
||||
qt_target_qml_sources(neochat QML_FILES qml/ShareAction.qml)
|
||||
else()
|
||||
@@ -399,14 +420,9 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_RUNNER)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_X11)
|
||||
target_sources(neochat PRIVATE runner.cpp)
|
||||
|
||||
if (TARGET KUnifiedPush)
|
||||
target_sources(neochat PRIVATE fakerunner.cpp)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
|
||||
target_link_libraries(neochat PRIVATE timelineplugin)
|
||||
target_link_libraries(neochat PUBLIC
|
||||
Qt::Core
|
||||
Qt::Quick
|
||||
@@ -540,7 +556,7 @@ if(NOT ANDROID)
|
||||
set_target_properties(neochat-app PROPERTIES OUTPUT_NAME "neochat")
|
||||
endif()
|
||||
|
||||
if(TARGET KF6::DBusAddons AND NOT WIN32)
|
||||
if(TARGET KF6::DBusAddons)
|
||||
target_link_libraries(neochat PUBLIC KF6::DBusAddons)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_KDBUSADDONS)
|
||||
endif()
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "chatbarcache.h"
|
||||
|
||||
#include "chatdocumenthandler.h"
|
||||
#include "eventhandler.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
@@ -118,7 +117,7 @@ QString ChatBarCache::relationMessage() const
|
||||
|
||||
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
|
||||
EventHandler eventhandler(room, &**event);
|
||||
return eventhandler.getMarkdownBody();
|
||||
return eventhandler.getPlainBody();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -164,54 +163,6 @@ QList<Mention> *ChatBarCache::mentions()
|
||||
return &m_mentions;
|
||||
}
|
||||
|
||||
void ChatBarCache::updateMentions(QQuickTextDocument *document, ChatDocumentHandler *documentHandler)
|
||||
{
|
||||
documentHandler->setDocument(document);
|
||||
|
||||
if (parent() == nullptr) {
|
||||
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return;
|
||||
}
|
||||
if (m_relationId.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
auto room = dynamic_cast<NeoChatRoom *>(parent());
|
||||
if (room == nullptr) {
|
||||
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
|
||||
if (const auto &roomMessageEvent = &*event->viewAs<Quotient::RoomMessageEvent>()) {
|
||||
// Replaces the mentions that are baked into the HTML but plaintext in the original markdown
|
||||
const QRegularExpression re(QStringLiteral(R"lit(<a\shref="https:\/\/matrix.to\/#\/([\S]*)"\s?>([\S]*)<\/a>)lit"));
|
||||
|
||||
m_mentions.clear();
|
||||
|
||||
int linkSize = 0;
|
||||
auto matches = re.globalMatch(EventHandler::rawMessageBody(*roomMessageEvent));
|
||||
while (matches.hasNext()) {
|
||||
const QRegularExpressionMatch match = matches.next();
|
||||
if (match.hasMatch()) {
|
||||
const QString id = match.captured(1);
|
||||
const QString name = match.captured(2);
|
||||
|
||||
const int position = match.capturedStart(0) - linkSize;
|
||||
const int end = position + name.length();
|
||||
linkSize += match.capturedLength(0) - name.length();
|
||||
|
||||
QTextCursor cursor(documentHandler->document()->textDocument());
|
||||
cursor.setPosition(position);
|
||||
cursor.setPosition(end, QTextCursor::KeepAnchor);
|
||||
cursor.setKeepPositionOnInsert(true);
|
||||
|
||||
m_mentions.push_back(Mention{.cursor = cursor, .text = name, .start = position, .position = end, .id = id});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString ChatBarCache::savedText() const
|
||||
{
|
||||
return m_savedText;
|
||||
|
||||
@@ -5,11 +5,8 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickTextDocument>
|
||||
#include <QTextCursor>
|
||||
|
||||
class ChatDocumentHandler;
|
||||
|
||||
/**
|
||||
* @brief Defines a user mention in the current chat or edit text.
|
||||
*/
|
||||
@@ -177,11 +174,6 @@ public:
|
||||
*/
|
||||
QList<Mention> *mentions();
|
||||
|
||||
/**
|
||||
* @brief Update the mentions in @p document when editing a message.
|
||||
*/
|
||||
Q_INVOKABLE void updateMentions(QQuickTextDocument *document, ChatDocumentHandler *documentHandler);
|
||||
|
||||
/**
|
||||
* @brief Get the saved chat bar text.
|
||||
*/
|
||||
|
||||
@@ -40,7 +40,6 @@ public:
|
||||
Code, /**< A code section. */
|
||||
Quote, /**< A quote section. */
|
||||
File, /**< A message that is a file. */
|
||||
Itinerary, /**< A preview for a file that can integrate with KDE itinerary.. */
|
||||
Poll, /**< The initial event for a poll. */
|
||||
Location, /**< A location event. */
|
||||
LiveLocation, /**< The initial event of a shared live location (i.e., the place where this is supposed to be shown in the timeline). */
|
||||
|
||||
@@ -29,6 +29,7 @@ public:
|
||||
Deprioritized, /**< The room is set as low priority. */
|
||||
Space, /**< The room is a space. */
|
||||
AddDirect, /**< So we can show the add friend delegate. */
|
||||
TypesCount, /**< Number of different types. */
|
||||
};
|
||||
Q_ENUM(Types);
|
||||
|
||||
@@ -40,7 +41,8 @@ public:
|
||||
if (room->joinState() == Quotient::JoinState::Invite) {
|
||||
return NeoChatRoomType::Invited;
|
||||
}
|
||||
if (room->isFavourite()) {
|
||||
// HACK for the unit tests
|
||||
if (room->isFavourite() || room->property("isFavorite").toBool()) {
|
||||
return NeoChatRoomType::Favorite;
|
||||
}
|
||||
if (room->isLowPriority()) {
|
||||
|
||||
@@ -280,22 +280,6 @@ QString EventHandler::getPlainBody(bool stripNewlines) const
|
||||
return getBody(m_event, Qt::PlainText, stripNewlines);
|
||||
}
|
||||
|
||||
QString EventHandler::getMarkdownBody() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getMarkdownBody called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!m_event->is<RoomMessageEvent>()) {
|
||||
qCWarning(EventHandling) << "getMarkdownBody called when m_event isn't a RoomMessageEvent.";
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto roomMessageEvent = eventCast<const RoomMessageEvent>(m_event);
|
||||
return roomMessageEvent->plainBody();
|
||||
}
|
||||
|
||||
QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat format, bool stripNewlines) const
|
||||
{
|
||||
if (event->isRedacted()) {
|
||||
|
||||
@@ -185,13 +185,6 @@ public:
|
||||
*/
|
||||
QString getPlainBody(bool stripNewlines = false) const;
|
||||
|
||||
/**
|
||||
* @brief Output the original body for the message content, useful for editing the original message.
|
||||
*
|
||||
* The event type must be a room message event.
|
||||
*/
|
||||
QString getMarkdownBody() const;
|
||||
|
||||
/**
|
||||
* @brief Output a generic string for the message content ready for display.
|
||||
*
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "fakerunner.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDBusMetaType>
|
||||
|
||||
Q_SCRIPTABLE RemoteActions FakeRunner::Actions()
|
||||
{
|
||||
QCoreApplication::quit();
|
||||
return {};
|
||||
}
|
||||
|
||||
Q_SCRIPTABLE RemoteMatches FakeRunner::Match(const QString &searchTerm)
|
||||
{
|
||||
QCoreApplication::quit();
|
||||
return {};
|
||||
}
|
||||
|
||||
Q_SCRIPTABLE void FakeRunner::Run(const QString &id, const QString &actionId)
|
||||
{
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
|
||||
FakeRunner::FakeRunner()
|
||||
: QObject()
|
||||
{
|
||||
qDBusRegisterMetaType<RemoteMatch>();
|
||||
qDBusRegisterMetaType<RemoteMatches>();
|
||||
qDBusRegisterMetaType<RemoteAction>();
|
||||
qDBusRegisterMetaType<RemoteActions>();
|
||||
qDBusRegisterMetaType<RemoteImage>();
|
||||
}
|
||||
|
||||
#include "moc_fakerunner.cpp"
|
||||
@@ -1,31 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDBusContext>
|
||||
|
||||
#include "runner.h"
|
||||
|
||||
/**
|
||||
* This is a close-to-identical copy of the regular Runner interface,
|
||||
* only used when activated for push notifications. This stubs it out so
|
||||
* Plasma Search and Kickoff doesn't accidentally activate the push notification
|
||||
* service.
|
||||
*
|
||||
* @sa Runner
|
||||
*/
|
||||
class FakeRunner : public QObject, protected QDBusContext
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.kde.krunner1")
|
||||
|
||||
public:
|
||||
Q_SCRIPTABLE RemoteActions Actions();
|
||||
|
||||
Q_SCRIPTABLE RemoteMatches Match(const QString &searchTerm);
|
||||
|
||||
Q_SCRIPTABLE void Run(const QString &id, const QString &actionId);
|
||||
|
||||
FakeRunner();
|
||||
};
|
||||
66
src/main.cpp
66
src/main.cpp
@@ -11,7 +11,6 @@
|
||||
#include <QQmlNetworkAccessManagerFactory>
|
||||
#include <QQuickStyle>
|
||||
#include <QQuickWindow>
|
||||
#include <QtQml/QQmlExtensionPlugin>
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include <QGuiApplication>
|
||||
@@ -45,16 +44,10 @@
|
||||
#include "neochatconfig.h"
|
||||
#include "roommanager.h"
|
||||
#include "windowcontroller.h"
|
||||
#include "sharehandler.h"
|
||||
|
||||
#ifdef HAVE_RUNNER
|
||||
#include "runner.h"
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMetaType>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_RUNNER) && defined(HAVE_KUNIFIEDPUSH)
|
||||
#include "fakerunner.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
@@ -193,9 +186,6 @@ int main(int argc, char *argv[])
|
||||
parser.addOption(dbusActivatedOption);
|
||||
#endif
|
||||
|
||||
QCommandLineOption shareOption(QStringLiteral("share"), i18n("Share a URL to Matrix"), QStringLiteral("text"));
|
||||
parser.addOption(shareOption);
|
||||
|
||||
about.setupCommandLine(&parser);
|
||||
parser.process(app);
|
||||
about.processCommandLine(&parser);
|
||||
@@ -206,14 +196,6 @@ int main(int argc, char *argv[])
|
||||
// We want to be replaceable by the main client
|
||||
KDBusService service(KDBusService::Replace);
|
||||
|
||||
#ifdef HAVE_RUNNER
|
||||
// If we are built with KRunner and KUnifiedPush support, we need to do something special.
|
||||
// Because KRunner may call us on the D-Bus (under the same service name org.kde.neochat) then it may
|
||||
// accidentally activate us for push notifications instead. If this happens, then immediately quit if the fake
|
||||
// runner is called.
|
||||
QDBusConnection::sessionBus().registerObject("/RoomRunner"_ls, new FakeRunner(), QDBusConnection::ExportScriptableContents);
|
||||
#endif
|
||||
|
||||
Controller::listenForNotifications();
|
||||
return QCoreApplication::exec();
|
||||
}
|
||||
@@ -223,8 +205,6 @@ int main(int argc, char *argv[])
|
||||
KDBusService service(KDBusService::Unique);
|
||||
#endif
|
||||
|
||||
Q_IMPORT_QML_PLUGIN(org_kde_neochat_timelinePlugin)
|
||||
|
||||
qml_register_types_org_kde_neochat();
|
||||
qmlRegisterSingletonInstance("org.kde.neochat.config", 1, 0, "Config", NeoChatConfig::self());
|
||||
qmlRegisterSingletonInstance("org.kde.neochat.accounts", 1, 0, "AccountRegistry", &Controller::instance().accounts());
|
||||
@@ -235,32 +215,26 @@ int main(int argc, char *argv[])
|
||||
|
||||
#ifdef HAVE_KDBUSADDONS
|
||||
service.connect(&service,
|
||||
&KDBusService::activateRequested,
|
||||
&RoomManager::instance(),
|
||||
[&engine](const QStringList &arguments, const QString &workingDirectory) {
|
||||
Q_UNUSED(workingDirectory);
|
||||
&KDBusService::activateRequested,
|
||||
&RoomManager::instance(),
|
||||
[&engine](const QStringList &arguments, const QString &workingDirectory) {
|
||||
Q_UNUSED(workingDirectory);
|
||||
|
||||
QWindow *window = windowFromEngine(&engine);
|
||||
KWindowSystem::updateStartupId(window);
|
||||
QWindow *window = windowFromEngine(&engine);
|
||||
KWindowSystem::updateStartupId(window);
|
||||
|
||||
WindowController::instance().showAndRaiseWindow(QString());
|
||||
WindowController::instance().showAndRaiseWindow(QString());
|
||||
|
||||
// Open matrix uri
|
||||
if (arguments.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto args = arguments;
|
||||
args.removeFirst();
|
||||
if (args.length() == 2 && args[0] == "--share"_ls) {
|
||||
ShareHandler::instance().setText(args[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto &arg : args) {
|
||||
RoomManager::instance().resolveResource(arg);
|
||||
}
|
||||
});
|
||||
// Open matrix uri
|
||||
if (arguments.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
auto args = arguments;
|
||||
args.removeFirst();
|
||||
for (const auto &arg : args) {
|
||||
RoomManager::instance().resolveResource(arg);
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
|
||||
@@ -273,10 +247,6 @@ int main(int argc, char *argv[])
|
||||
});
|
||||
}
|
||||
|
||||
if (parser.isSet("share"_ls)) {
|
||||
ShareHandler::instance().setText(parser.value(shareOption));
|
||||
}
|
||||
|
||||
engine.addImageProvider(QLatin1String("mxc"), MatrixImageProvider::create(&engine, &engine));
|
||||
engine.addImageProvider(QLatin1String("blurhash"), new BlurhashImageProvider);
|
||||
|
||||
@@ -285,7 +255,7 @@ int main(int argc, char *argv[])
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!parser.positionalArguments().isEmpty() && !parser.isSet("share"_ls)) {
|
||||
if (!parser.positionalArguments().isEmpty()) {
|
||||
RoomManager::instance().setUrlArgument(parser.positionalArguments()[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "itinerarymodel.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QProcess>
|
||||
|
||||
#include "config-neochat.h"
|
||||
@@ -17,6 +16,20 @@ ItineraryModel::ItineraryModel(QObject *parent)
|
||||
{
|
||||
}
|
||||
|
||||
void ItineraryModel::setConnection(NeoChatConnection *connection)
|
||||
{
|
||||
if (m_connection == connection) {
|
||||
return;
|
||||
}
|
||||
m_connection = connection;
|
||||
Q_EMIT connectionChanged();
|
||||
}
|
||||
|
||||
NeoChatConnection *ItineraryModel::connection() const
|
||||
{
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
QVariant ItineraryModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
@@ -120,7 +133,11 @@ QString ItineraryModel::path() const
|
||||
|
||||
void ItineraryModel::setPath(const QString &path)
|
||||
{
|
||||
if (path == m_path) {
|
||||
return;
|
||||
}
|
||||
m_path = path;
|
||||
Q_EMIT pathChanged();
|
||||
loadData();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,16 +4,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QJsonArray>
|
||||
#include <QPointer>
|
||||
#include <QQmlEngine>
|
||||
#include <QString>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
|
||||
class ItineraryModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
@@ -34,6 +37,9 @@ public:
|
||||
Q_ENUM(Roles)
|
||||
explicit ItineraryModel(QObject *parent = nullptr);
|
||||
|
||||
void setConnection(NeoChatConnection *connection);
|
||||
NeoChatConnection *connection() const;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
int rowCount(const QModelIndex &parent = {}) const override;
|
||||
|
||||
@@ -44,7 +50,12 @@ public:
|
||||
|
||||
Q_INVOKABLE void sendToItinerary();
|
||||
|
||||
Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
void pathChanged();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
QJsonArray m_data;
|
||||
QString m_path;
|
||||
void loadData();
|
||||
|
||||
@@ -64,13 +64,11 @@ MessageContentModel::MessageContentModel(const Quotient::RoomEvent *event, NeoCh
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::fileTransferCompleted, this, [this](const QString &eventId) {
|
||||
if (m_event != nullptr && eventId == m_event->id()) {
|
||||
updateComponents();
|
||||
Q_EMIT dataChanged(index(0), index(rowCount() - 1), {FileTransferInfoRole});
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::fileTransferFailed, this, [this](const QString &eventId) {
|
||||
if (m_event != nullptr && eventId == m_event->id()) {
|
||||
updateComponents();
|
||||
Q_EMIT dataChanged(index(0), index(rowCount() - 1), {FileTransferInfoRole});
|
||||
}
|
||||
});
|
||||
@@ -154,9 +152,6 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
||||
return QVariant::fromValue(m_room->fileTransferInfo(event->id()));
|
||||
}
|
||||
}
|
||||
if (role == ItineraryModelRole) {
|
||||
return QVariant::fromValue<ItineraryModel *>(m_itineraryModel);
|
||||
}
|
||||
if (role == LatitudeRole) {
|
||||
return eventHandler.getLatitude();
|
||||
}
|
||||
@@ -214,7 +209,6 @@ QHash<int, QByteArray> MessageContentModel::roleNames() const
|
||||
roles[AuthorRole] = "author";
|
||||
roles[MediaInfoRole] = "mediaInfo";
|
||||
roles[FileTransferInfoRole] = "fileTransferInfo";
|
||||
roles[ItineraryModelRole] = "itineraryModel";
|
||||
roles[LatitudeRole] = "latitude";
|
||||
roles[LongitudeRole] = "longitude";
|
||||
roles[AssetRole] = "asset";
|
||||
@@ -246,19 +240,11 @@ void MessageContentModel::updateComponents(bool isEditing)
|
||||
|
||||
if (isEditing) {
|
||||
m_components += MessageComponent{MessageComponentType::Edit, QString(), {}};
|
||||
} else if (m_event->isRedacted()) {
|
||||
m_components += MessageComponent{MessageComponentType::Text, QString(), {}};
|
||||
} else {
|
||||
if (eventHandler.messageComponentType() == MessageComponentType::Text) {
|
||||
const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event);
|
||||
auto body = EventHandler::rawMessageBody(*event);
|
||||
m_components.append(TextHandler().textComponents(body, EventHandler::messageBodyInputFormat(*event), m_room, event, event->isReplaced()));
|
||||
} else if (eventHandler.messageComponentType() == MessageComponentType::File) {
|
||||
m_components += MessageComponent{MessageComponentType::File, QString(), {}};
|
||||
updateItineraryModel();
|
||||
if (m_itineraryModel != nullptr) {
|
||||
m_components += MessageComponent{MessageComponentType::Itinerary, QString(), {}};
|
||||
}
|
||||
} else {
|
||||
m_components += MessageComponent{eventHandler.messageComponentType(), QString(), {}};
|
||||
}
|
||||
@@ -274,25 +260,3 @@ void MessageContentModel::updateComponents(bool isEditing)
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void MessageContentModel::updateItineraryModel()
|
||||
{
|
||||
if (m_room == nullptr || m_event == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
if (event->hasFileContent()) {
|
||||
auto filePath = m_room->fileTransferInfo(event->id()).localPath;
|
||||
if (filePath.isEmpty() && m_itineraryModel != nullptr) {
|
||||
delete m_itineraryModel;
|
||||
m_itineraryModel = nullptr;
|
||||
} else if (!filePath.isEmpty()) {
|
||||
if (m_itineraryModel == nullptr) {
|
||||
m_itineraryModel = new ItineraryModel(this);
|
||||
}
|
||||
m_itineraryModel->setPath(filePath.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "eventhandler.h"
|
||||
#include "itinerarymodel.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
@@ -46,7 +45,6 @@ public:
|
||||
AuthorRole, /**< The author of the event. */
|
||||
MediaInfoRole, /**< The media info for the event. */
|
||||
FileTransferInfoRole, /**< FileTransferInfo for any downloading files. */
|
||||
ItineraryModelRole, /**< The itinerary model for a file. */
|
||||
LatitudeRole, /**< Latitude for a location event. */
|
||||
LongitudeRole, /**< Longitude for a location event. */
|
||||
AssetRole, /**< Type of location event, e.g. self pin of the user location. */
|
||||
@@ -94,7 +92,4 @@ private:
|
||||
void updateComponents(bool isEditing = false);
|
||||
|
||||
LinkPreviewer *m_linkPreviewer = nullptr;
|
||||
ItineraryModel *m_itineraryModel = nullptr;
|
||||
|
||||
void updateItineraryModel();
|
||||
};
|
||||
|
||||
@@ -162,30 +162,6 @@ QHash<int, QByteArray> ReactionModel::roleNames() const
|
||||
};
|
||||
}
|
||||
|
||||
bool isEmoji(const QString &text)
|
||||
{
|
||||
#ifdef HAVE_ICU
|
||||
QTextBoundaryFinder finder(QTextBoundaryFinder::Grapheme, text);
|
||||
int from = 0;
|
||||
while (finder.toNextBoundary() != -1) {
|
||||
auto to = finder.position();
|
||||
if (text[from].isSpace()) {
|
||||
from = to;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto first = text.mid(from, to - from).toUcs4()[0];
|
||||
if (!u_hasBinaryProperty(first, UCHAR_EMOJI)) {
|
||||
return false;
|
||||
}
|
||||
from = to;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
QString ReactionModel::reactionText(QString text) const
|
||||
{
|
||||
text = text.toHtmlEscaped();
|
||||
@@ -198,6 +174,28 @@ QString ReactionModel::reactionText(QString text) const
|
||||
return QStringLiteral("<img src=\"%1\" width=\"%2\" height=\"%2\">")
|
||||
.arg(m_room->connection()->makeMediaUrl(QUrl(text)).toString(), QString::number(size));
|
||||
}
|
||||
const auto isEmoji = [](const QString &text) {
|
||||
#ifdef HAVE_ICU
|
||||
QTextBoundaryFinder finder(QTextBoundaryFinder::Grapheme, text);
|
||||
int from = 0;
|
||||
while (finder.toNextBoundary() != -1) {
|
||||
auto to = finder.position();
|
||||
if (text[from].isSpace()) {
|
||||
from = to;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto first = text.mid(from, to - from).toUcs4()[0];
|
||||
if (!u_hasBinaryProperty(first, UCHAR_EMOJI_PRESENTATION)) {
|
||||
return false;
|
||||
}
|
||||
from = to;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
};
|
||||
|
||||
return isEmoji(text) ? QStringLiteral("<span style=\"font-family: 'emoji';\">") + text + QStringLiteral("</span>") : text;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,177 @@
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
TreeItem::TreeItem(TreeData treeData, TreeItem *parent)
|
||||
: m_treeData(treeData)
|
||||
, m_parentItem(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void TreeItem::appendChild(std::unique_ptr<TreeItem> &&child)
|
||||
{
|
||||
m_childItems.push_back(std::move(child));
|
||||
}
|
||||
|
||||
bool TreeItem::insertChildren(int position, int count, TreeData treeData)
|
||||
{
|
||||
if (position < 0 || position > qsizetype(m_childItems.size()))
|
||||
return false;
|
||||
|
||||
for (int row = 0; row < count; ++row) {
|
||||
m_childItems.insert(m_childItems.cbegin() + position, std::make_unique<TreeItem>(treeData, this));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TreeItem::removeChildren(int position, int count)
|
||||
{
|
||||
if (position < 0 || position + count > qsizetype(m_childItems.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int row = 0; row < count; ++row) {
|
||||
m_childItems.erase(m_childItems.cbegin() + position);
|
||||
qWarning() << "removing" << position;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TreeItem *TreeItem::child(int row)
|
||||
{
|
||||
return row >= 0 && row < childCount() ? m_childItems.at(row).get() : nullptr;
|
||||
}
|
||||
|
||||
int TreeItem::childCount() const
|
||||
{
|
||||
return int(m_childItems.size());
|
||||
}
|
||||
|
||||
int TreeItem::row() const
|
||||
{
|
||||
if (m_parentItem == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
const auto it = std::find_if(m_parentItem->m_childItems.cbegin(), m_parentItem->m_childItems.cend(), [this](const std::unique_ptr<TreeItem> &treeItem) {
|
||||
return treeItem.get() == this;
|
||||
});
|
||||
|
||||
if (it != m_parentItem->m_childItems.cend())
|
||||
return std::distance(m_parentItem->m_childItems.cbegin(), it);
|
||||
Q_ASSERT(false); // should not happen
|
||||
return -1;
|
||||
}
|
||||
|
||||
QVariant TreeItem::data(int role) const
|
||||
{
|
||||
if (!m_parentItem) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (std::holds_alternative<NeoChatRoomType::Types>(m_treeData)) {
|
||||
const auto row = this->row();
|
||||
switch (role) {
|
||||
case RoomTreeModel::IsCategoryRole:
|
||||
return true;
|
||||
case RoomTreeModel::DisplayNameRole:
|
||||
return NeoChatRoomType::typeName(row);
|
||||
case RoomTreeModel::DelegateTypeRole:
|
||||
if (row == NeoChatRoomType::Search) {
|
||||
return QStringLiteral("search");
|
||||
}
|
||||
if (row == NeoChatRoomType::AddDirect) {
|
||||
return QStringLiteral("addDirect");
|
||||
}
|
||||
return QStringLiteral("section");
|
||||
case RoomTreeModel::IconRole:
|
||||
return NeoChatRoomType::typeIconName(row);
|
||||
case RoomTreeModel::CategoryRole:
|
||||
return row;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto room = std::get<NeoChatRoom *>(m_treeData);
|
||||
|
||||
switch (role) {
|
||||
case RoomTreeModel::IsCategoryRole:
|
||||
return false;
|
||||
case RoomTreeModel::DisplayNameRole:
|
||||
return room->displayName();
|
||||
case RoomTreeModel::AvatarRole:
|
||||
return room->avatarMediaId();
|
||||
case RoomTreeModel::CanonicalAliasRole:
|
||||
return room->canonicalAlias();
|
||||
case RoomTreeModel::TopicRole:
|
||||
return room->topic();
|
||||
case RoomTreeModel::CategoryRole:
|
||||
return NeoChatRoomType::typeForRoom(room);
|
||||
case RoomTreeModel::ContextNotificationCountRole:
|
||||
return room->contextAwareNotificationCount();
|
||||
case RoomTreeModel::HasHighlightNotificationsRole:
|
||||
return room->highlightCount() > 0 && room->contextAwareNotificationCount() > 0;
|
||||
case RoomTreeModel::LastActiveTimeRole:
|
||||
return room->lastActiveTime();
|
||||
case RoomTreeModel::JoinStateRole:
|
||||
if (!room->successorId().isEmpty()) {
|
||||
return QStringLiteral("upgraded");
|
||||
}
|
||||
return QVariant::fromValue(room->joinState());
|
||||
case RoomTreeModel::CurrentRoomRole:
|
||||
return QVariant::fromValue(room);
|
||||
case RoomTreeModel::SubtitleTextRole: {
|
||||
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
|
||||
return QString();
|
||||
}
|
||||
EventHandler eventHandler(room, room->lastEvent());
|
||||
return eventHandler.subtitleText();
|
||||
}
|
||||
case RoomTreeModel::AvatarImageRole:
|
||||
return room->avatar(128);
|
||||
case RoomTreeModel::RoomIdRole:
|
||||
return room->id();
|
||||
case RoomTreeModel::IsSpaceRole:
|
||||
return room->isSpace();
|
||||
case RoomTreeModel::IsChildSpaceRole:
|
||||
return SpaceHierarchyCache::instance().isChild(room->id());
|
||||
case RoomTreeModel::ReplacementIdRole:
|
||||
return room->successorId();
|
||||
case RoomTreeModel::IsDirectChat:
|
||||
return room->isDirectChat();
|
||||
case RoomTreeModel::DelegateTypeRole:
|
||||
return QStringLiteral("normal");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
TreeItem *TreeItem::parentItem() const
|
||||
{
|
||||
return m_parentItem;
|
||||
}
|
||||
|
||||
std::optional<int> TreeItem::position(Quotient::Room *room) const
|
||||
{
|
||||
Q_ASSERT_X(std::holds_alternative<NeoChatRoomType::Types>(m_treeData), __FUNCTION__, "containsRoom only works in category items");
|
||||
|
||||
int i = 0;
|
||||
for (const auto &child : m_childItems) {
|
||||
if (std::get<NeoChatRoom *>(child->treeData()) == room) {
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TreeItem::TreeData TreeItem::treeData() const
|
||||
{
|
||||
return m_treeData;
|
||||
}
|
||||
|
||||
RoomTreeModel::RoomTreeModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
{
|
||||
@@ -21,15 +192,18 @@ RoomTreeModel::RoomTreeModel(QObject *parent)
|
||||
|
||||
void RoomTreeModel::initializeCategories()
|
||||
{
|
||||
for (const auto &key : m_rooms.keys()) {
|
||||
for (const auto &room : m_rooms[key]) {
|
||||
room->disconnect(this);
|
||||
}
|
||||
m_rootItem.reset(new TreeItem(nullptr, nullptr));
|
||||
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
|
||||
m_rootItem->appendChild(std::make_unique<TreeItem>(NeoChatRoomType::Types(i), m_rootItem.get()));
|
||||
}
|
||||
m_rooms.clear();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
m_rooms[NeoChatRoomType::Types(i)] = {};
|
||||
}
|
||||
|
||||
TreeItem *RoomTreeModel::getItem(const QModelIndex &index) const
|
||||
{
|
||||
if (index.isValid()) {
|
||||
return static_cast<TreeItem *>(index.internalPointer());
|
||||
}
|
||||
return m_rootItem.get();
|
||||
}
|
||||
|
||||
void RoomTreeModel::setConnection(NeoChatConnection *connection)
|
||||
@@ -68,24 +242,34 @@ void RoomTreeModel::newRoom(Room *r)
|
||||
return;
|
||||
}
|
||||
|
||||
beginInsertRows(index(type, 0), m_rooms[type].size(), m_rooms[type].size());
|
||||
m_rooms[type].append(room);
|
||||
auto categoryItem = m_rootItem->child(type);
|
||||
beginInsertRows(index(type, 0), categoryItem->childCount(), categoryItem->childCount());
|
||||
categoryItem->appendChild(std::make_unique<TreeItem>(room, categoryItem));
|
||||
connectRoomSignals(room);
|
||||
endInsertRows();
|
||||
qWarning() << "adding room" << type << "new count" << categoryItem->childCount();
|
||||
}
|
||||
|
||||
void RoomTreeModel::leftRoom(Room *r)
|
||||
{
|
||||
const auto room = dynamic_cast<NeoChatRoom *>(r);
|
||||
const auto type = NeoChatRoomType::typeForRoom(room);
|
||||
auto row = m_rooms[type].indexOf(room);
|
||||
if (row == -1) {
|
||||
|
||||
auto idx = indexForRoom(room);
|
||||
if (!idx.isValid()) {
|
||||
return;
|
||||
}
|
||||
beginRemoveRows(index(type, 0), row, row);
|
||||
m_rooms[type][row]->disconnect(this);
|
||||
m_rooms[type].removeAt(row);
|
||||
|
||||
auto parentItem = getItem(idx.parent());
|
||||
Q_ASSERT(parentItem);
|
||||
|
||||
beginRemoveRows(idx.parent(), idx.row(), idx.row());
|
||||
const bool success = parentItem->removeChildren(idx.row(), 1);
|
||||
room->disconnect(this);
|
||||
endRemoveRows();
|
||||
|
||||
if (success) {
|
||||
qWarning() << "Unable to remove room";
|
||||
}
|
||||
}
|
||||
|
||||
void RoomTreeModel::moveRoom(Quotient::Room *room)
|
||||
@@ -94,31 +278,46 @@ void RoomTreeModel::moveRoom(Quotient::Room *room)
|
||||
// NeoChatRoomType::typeForRoom doesn't match it's current location. So find the room.
|
||||
NeoChatRoomType::Types oldType;
|
||||
int oldRow = -1;
|
||||
for (const auto &key : m_rooms.keys()) {
|
||||
if (m_rooms[key].contains(room)) {
|
||||
oldType = key;
|
||||
oldRow = m_rooms[key].indexOf(room);
|
||||
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
|
||||
auto categoryItem = m_rootItem->child(i);
|
||||
auto position = categoryItem->position(room);
|
||||
if (position) {
|
||||
oldType = static_cast<NeoChatRoomType::Types>(i);
|
||||
oldRow = *position;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldRow == -1) {
|
||||
return;
|
||||
}
|
||||
const auto newType = NeoChatRoomType::typeForRoom(dynamic_cast<NeoChatRoom *>(room));
|
||||
auto neochatRoom = dynamic_cast<NeoChatRoom *>(room);
|
||||
const auto newType = NeoChatRoomType::typeForRoom(neochatRoom);
|
||||
if (newType == oldType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto oldParent = index(oldType, 0, {});
|
||||
auto oldParentItem = getItem(oldParent);
|
||||
Q_ASSERT(oldParentItem);
|
||||
|
||||
const auto newParent = index(newType, 0, {});
|
||||
auto newParentItem = getItem(newParent);
|
||||
Q_ASSERT(newParentItem);
|
||||
|
||||
// HACK: We're doing this as a remove then insert because moving doesn't work
|
||||
// properly with DelegateChooser for whatever reason.
|
||||
|
||||
Q_ASSERT(checkIndex(index(oldRow, 0, oldParent), QAbstractItemModel::CheckIndexOption::IndexIsValid));
|
||||
beginRemoveRows(oldParent, oldRow, oldRow);
|
||||
m_rooms[oldType].removeAt(oldRow);
|
||||
const bool success = oldParentItem->removeChildren(oldRow, 1);
|
||||
Q_ASSERT(success);
|
||||
endRemoveRows();
|
||||
beginInsertRows(newParent, m_rooms[newType].size(), m_rooms[newType].size());
|
||||
m_rooms[newType].append(dynamic_cast<NeoChatRoom *>(room));
|
||||
|
||||
beginInsertRows(newParent, newParentItem->childCount(), newParentItem->childCount());
|
||||
newParentItem->appendChild(std::make_unique<TreeItem>(neochatRoom, newParentItem));
|
||||
endInsertRows();
|
||||
|
||||
// Q_ASSERT(checkIndex(index(newParentItem->childCount() - 1, 0, newParent), QAbstractItemModel::CheckIndexOption::IndexIsValid));
|
||||
}
|
||||
|
||||
void RoomTreeModel::connectRoomSignals(NeoChatRoom *room)
|
||||
@@ -151,14 +350,12 @@ void RoomTreeModel::connectRoomSignals(NeoChatRoom *room)
|
||||
|
||||
void RoomTreeModel::refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles)
|
||||
{
|
||||
const auto roomType = NeoChatRoomType::typeForRoom(room);
|
||||
const auto it = std::find(m_rooms[roomType].begin(), m_rooms[roomType].end(), room);
|
||||
if (it == m_rooms[roomType].end()) {
|
||||
const auto idx = indexForRoom(room);
|
||||
if (!idx.isValid()) {
|
||||
qCritical() << "Room" << room->id() << "not found in the room list";
|
||||
return;
|
||||
}
|
||||
const auto parentIndex = index(roomType, 0, {});
|
||||
const auto idx = index(it - m_rooms[roomType].begin(), 0, parentIndex);
|
||||
|
||||
Q_EMIT dataChanged(idx, idx, roles);
|
||||
}
|
||||
|
||||
@@ -169,38 +366,42 @@ NeoChatConnection *RoomTreeModel::connection() const
|
||||
|
||||
int RoomTreeModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return 1;
|
||||
const TreeItem *parentItem = getItem(parent);
|
||||
return parentItem ? 1 : 0;
|
||||
}
|
||||
|
||||
int RoomTreeModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (!parent.isValid()) {
|
||||
return m_rooms.keys().size();
|
||||
}
|
||||
if (!parent.parent().isValid()) {
|
||||
return m_rooms.values()[parent.row()].size();
|
||||
}
|
||||
return 0;
|
||||
const TreeItem *parentItem = getItem(parent);
|
||||
return parentItem ? parentItem->childCount() : 0;
|
||||
}
|
||||
|
||||
QModelIndex RoomTreeModel::parent(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.internalPointer()) {
|
||||
if (!index.isValid()) {
|
||||
return {};
|
||||
}
|
||||
return this->index(NeoChatRoomType::typeForRoom(static_cast<NeoChatRoom *>(index.internalPointer())), 0, QModelIndex());
|
||||
|
||||
TreeItem *childItem = getItem(index);
|
||||
Q_ASSERT(childItem);
|
||||
TreeItem *parentItem = childItem->parentItem();
|
||||
|
||||
return parentItem != m_rootItem.get() ? createIndex(parentItem->row(), 0, parentItem) : QModelIndex{};
|
||||
}
|
||||
|
||||
QModelIndex RoomTreeModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (!parent.isValid()) {
|
||||
return createIndex(row, column, nullptr);
|
||||
}
|
||||
if (row >= rowCount(parent)) {
|
||||
if (parent.isValid() && parent.column() != 0) {
|
||||
return {};
|
||||
}
|
||||
return createIndex(row, column, m_rooms[NeoChatRoomType::Types(parent.row())][row]);
|
||||
|
||||
TreeItem *parentItem = getItem(parent);
|
||||
Q_ASSERT(parentItem);
|
||||
|
||||
if (auto *childItem = parentItem->child(row)) {
|
||||
return createIndex(row, column, childItem);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> RoomTreeModel::roleNames() const
|
||||
@@ -232,102 +433,12 @@ QHash<int, QByteArray> RoomTreeModel::roleNames() const
|
||||
QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (!index.parent().isValid()) {
|
||||
if (role == DisplayNameRole) {
|
||||
return NeoChatRoomType::typeName(index.row());
|
||||
}
|
||||
if (role == DelegateTypeRole) {
|
||||
if (index.row() == NeoChatRoomType::Search) {
|
||||
return QStringLiteral("search");
|
||||
}
|
||||
if (index.row() == NeoChatRoomType::AddDirect) {
|
||||
return QStringLiteral("addDirect");
|
||||
}
|
||||
return QStringLiteral("section");
|
||||
}
|
||||
if (role == IconRole) {
|
||||
return NeoChatRoomType::typeIconName(index.row());
|
||||
}
|
||||
if (role == CategoryRole) {
|
||||
return index.row();
|
||||
}
|
||||
qWarning() << index.row() << rowCount(index.parent());
|
||||
Q_ASSERT(false);
|
||||
return {};
|
||||
}
|
||||
const auto room = m_rooms.values()[index.parent().row()][index.row()].get();
|
||||
Q_ASSERT(room);
|
||||
|
||||
if (role == DisplayNameRole) {
|
||||
return room->displayName();
|
||||
}
|
||||
if (role == AvatarRole) {
|
||||
return room->avatarMediaId();
|
||||
}
|
||||
if (role == CanonicalAliasRole) {
|
||||
return room->canonicalAlias();
|
||||
}
|
||||
if (role == TopicRole) {
|
||||
return room->topic();
|
||||
}
|
||||
if (role == CategoryRole) {
|
||||
return NeoChatRoomType::typeForRoom(room);
|
||||
}
|
||||
if (role == ContextNotificationCountRole) {
|
||||
return int(room->contextAwareNotificationCount());
|
||||
}
|
||||
if (role == HasHighlightNotificationsRole) {
|
||||
return room->highlightCount() > 0 && room->contextAwareNotificationCount() > 0;
|
||||
}
|
||||
if (role == LastActiveTimeRole) {
|
||||
return room->lastActiveTime();
|
||||
}
|
||||
if (role == JoinStateRole) {
|
||||
if (!room->successorId().isEmpty()) {
|
||||
return QStringLiteral("upgraded");
|
||||
}
|
||||
return QVariant::fromValue(room->joinState());
|
||||
}
|
||||
if (role == CurrentRoomRole) {
|
||||
return QVariant::fromValue(room);
|
||||
}
|
||||
if (role == SubtitleTextRole) {
|
||||
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
|
||||
return QString();
|
||||
}
|
||||
EventHandler eventHandler(room, room->lastEvent());
|
||||
return eventHandler.subtitleText();
|
||||
}
|
||||
if (role == AvatarImageRole) {
|
||||
return room->avatar(128);
|
||||
}
|
||||
if (role == RoomIdRole) {
|
||||
return room->id();
|
||||
}
|
||||
if (role == IsSpaceRole) {
|
||||
return room->isSpace();
|
||||
}
|
||||
if (role == IsChildSpaceRole) {
|
||||
return SpaceHierarchyCache::instance().isChild(room->id());
|
||||
}
|
||||
if (role == ReplacementIdRole) {
|
||||
return room->successorId();
|
||||
}
|
||||
if (role == IsDirectChat) {
|
||||
return room->isDirectChat();
|
||||
}
|
||||
if (role == DelegateTypeRole) {
|
||||
return QStringLiteral("normal");
|
||||
}
|
||||
if (role == AttentionRole) {
|
||||
return room->notificationCount() + room->highlightCount() > 0;
|
||||
}
|
||||
if (role == FavouriteRole) {
|
||||
return room->isFavourite();
|
||||
}
|
||||
|
||||
return {};
|
||||
return getItem(index)->data(role);
|
||||
}
|
||||
|
||||
QModelIndex RoomTreeModel::indexForRoom(NeoChatRoom *room) const
|
||||
@@ -336,18 +447,18 @@ QModelIndex RoomTreeModel::indexForRoom(NeoChatRoom *room) const
|
||||
return {};
|
||||
}
|
||||
|
||||
// Try and find by checking type.
|
||||
const auto type = NeoChatRoomType::typeForRoom(room);
|
||||
auto row = m_rooms[type].indexOf(room);
|
||||
if (row >= 0) {
|
||||
return index(row, 0, index(type, 0));
|
||||
}
|
||||
// Double check that the room isn't in the wrong category.
|
||||
for (const auto &key : m_rooms.keys()) {
|
||||
if (m_rooms[key].contains(room)) {
|
||||
return index(m_rooms[key].indexOf(room), 0, index(key, 0));
|
||||
const auto roomType = NeoChatRoomType::typeForRoom(room);
|
||||
const auto roomTypeItem = m_rootItem->child(roomType);
|
||||
|
||||
for (int i = 0, count = roomTypeItem->childCount(); i < count; i++) {
|
||||
auto roomItem = roomTypeItem->child(i);
|
||||
if (std::get<NeoChatRoom *>(roomItem->treeData()) == room) {
|
||||
const auto parentIndex = index(roomType, 0, {});
|
||||
const auto idx = index(i, 0, parentIndex);
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,32 @@ class Room;
|
||||
|
||||
class NeoChatConnection;
|
||||
class NeoChatRoom;
|
||||
class RoomTreeModelTest;
|
||||
|
||||
class TreeItem
|
||||
{
|
||||
public:
|
||||
using TreeData = std::variant<NeoChatRoom *, NeoChatRoomType::Types>;
|
||||
|
||||
explicit TreeItem(TreeData data, TreeItem *parentItem);
|
||||
|
||||
TreeItem *child(int row);
|
||||
int childCount() const;
|
||||
QVariant data(int role) const;
|
||||
void appendChild(std::unique_ptr<TreeItem> &&child);
|
||||
bool insertChildren(int position, int count, TreeData treeData);
|
||||
TreeItem *parentItem() const;
|
||||
bool removeChildren(int position, int count);
|
||||
bool removeColumns(int position, int columns);
|
||||
std::optional<int> position(Quotient::Room *room) const;
|
||||
int row() const;
|
||||
TreeData treeData() const;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<TreeItem>> m_childItems;
|
||||
TreeData m_treeData;
|
||||
TreeItem *m_parentItem;
|
||||
};
|
||||
|
||||
class RoomTreeModel : public QAbstractItemModel
|
||||
{
|
||||
@@ -49,6 +75,7 @@ public:
|
||||
IconRole,
|
||||
AttentionRole, /**< Whether there are any notifications. */
|
||||
FavouriteRole, /**< Whether the room is favourited. */
|
||||
IsCategoryRole, /**< Whether the item in the model is a category */
|
||||
};
|
||||
Q_ENUM(EventRoles)
|
||||
explicit RoomTreeModel(QObject *parent = nullptr);
|
||||
@@ -76,6 +103,8 @@ public:
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
TreeItem *getItem(const QModelIndex &index) const;
|
||||
|
||||
Q_INVOKABLE QModelIndex indexForRoom(NeoChatRoom *room) const;
|
||||
|
||||
Q_SIGNALS:
|
||||
@@ -83,7 +112,6 @@ Q_SIGNALS:
|
||||
|
||||
private:
|
||||
QPointer<NeoChatConnection> m_connection = nullptr;
|
||||
QMap<NeoChatRoomType::Types, QList<QPointer<NeoChatRoom>>> m_rooms;
|
||||
|
||||
void initializeCategories();
|
||||
void connectRoomSignals(NeoChatRoom *room);
|
||||
@@ -93,4 +121,8 @@ private:
|
||||
void moveRoom(Quotient::Room *room);
|
||||
|
||||
void refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles = {});
|
||||
|
||||
std::unique_ptr<TreeItem> m_rootItem;
|
||||
|
||||
friend RoomTreeModelTest;
|
||||
};
|
||||
|
||||
@@ -119,20 +119,28 @@ QString SortFilterRoomTreeModel::filterText() const
|
||||
|
||||
bool SortFilterRoomTreeModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||
{
|
||||
// root node
|
||||
if (!source_parent.isValid()) {
|
||||
if (sourceModel()->data(sourceModel()->index(source_row, 0), RoomTreeModel::CategoryRole).toInt() == NeoChatRoomType::Search
|
||||
&& NeoChatConfig::collapsed()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
|
||||
if (!index.isValid()) {
|
||||
qWarning() << source_row << source_parent << sourceModel()->rowCount(source_parent);
|
||||
Q_ASSERT(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sourceModel()->data(index, RoomTreeModel::IsCategoryRole).toBool()) {
|
||||
if (sourceModel()->data(index, RoomTreeModel::CategoryRole).toInt() == NeoChatRoomType::Search && NeoChatConfig::collapsed()) {
|
||||
return true;
|
||||
}
|
||||
if (sourceModel()->data(sourceModel()->index(source_row, 0), RoomTreeModel::CategoryRole).toInt() == NeoChatRoomType::AddDirect
|
||||
&& m_mode == DirectChats) {
|
||||
if (sourceModel()->data(index, RoomTreeModel::CategoryRole).toInt() == NeoChatRoomType::AddDirect && m_mode == DirectChats) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
|
||||
|
||||
bool acceptRoom = sourceModel()->data(index, RoomTreeModel::DisplayNameRole).toString().contains(m_filterText, Qt::CaseInsensitive)
|
||||
&& sourceModel()->data(index, RoomTreeModel::IsSpaceRole).toBool() == false;
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <Quotient/room.h>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
SpaceChildrenModel::SpaceChildrenModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
@@ -33,13 +32,19 @@ void SpaceChildrenModel::setSpace(NeoChatRoom *space)
|
||||
}
|
||||
// disconnect the new room signal from the old connection in case it is different.
|
||||
if (m_space != nullptr) {
|
||||
m_space->connection()->disconnect(this);
|
||||
m_space->disconnect(this);
|
||||
disconnect(m_space->connection(), &Quotient::Connection::loadedRoomState, this, nullptr);
|
||||
}
|
||||
|
||||
m_space = space;
|
||||
Q_EMIT spaceChanged();
|
||||
|
||||
for (auto job : m_currentJobs) {
|
||||
if (job) {
|
||||
job->abandon();
|
||||
}
|
||||
}
|
||||
m_currentJobs.clear();
|
||||
|
||||
auto connection = m_space->connection();
|
||||
connect(connection, &Quotient::Connection::loadedRoomState, this, [this](Quotient::Room *room) {
|
||||
if (m_pendingChildren.contains(room->name())) {
|
||||
@@ -61,17 +66,6 @@ bool SpaceChildrenModel::loading() const
|
||||
|
||||
void SpaceChildrenModel::refreshModel()
|
||||
{
|
||||
for (auto job : m_currentJobs) {
|
||||
if (job) {
|
||||
job->abandon();
|
||||
}
|
||||
}
|
||||
m_currentJobs.clear();
|
||||
|
||||
if (m_space == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
m_replacedRooms.clear();
|
||||
delete m_rootItem;
|
||||
@@ -118,11 +112,6 @@ void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJ
|
||||
if (!successorId.isEmpty()) {
|
||||
m_replacedRooms += successorId;
|
||||
}
|
||||
if (dynamic_cast<NeoChatRoom *>(room)->isSpace()) {
|
||||
connect(room, &Quotient::Room::changed, this, [this]() {
|
||||
refreshModel();
|
||||
});
|
||||
}
|
||||
}
|
||||
if (children[i].childrenState.size() > 0) {
|
||||
auto job = m_space->connection()->callApi<Quotient::GetSpaceHierarchyJob>(children[i].roomId, Quotient::none, Quotient::none, 1);
|
||||
@@ -131,7 +120,8 @@ void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJ
|
||||
insertChildren(job->rooms(), index(insertRow, 0, parent));
|
||||
});
|
||||
}
|
||||
parentItem->insertChild(new SpaceTreeItem(dynamic_cast<NeoChatConnection *>(m_space->connection()),
|
||||
parentItem->insertChild(insertRow,
|
||||
new SpaceTreeItem(dynamic_cast<NeoChatConnection *>(m_space->connection()),
|
||||
parentItem,
|
||||
children[i].roomId,
|
||||
children[i].name,
|
||||
|
||||
@@ -60,65 +60,4 @@ bool SpaceChildSortFilterModel::filterAcceptsRow(int sourceRow, const QModelInde
|
||||
return true;
|
||||
}
|
||||
|
||||
void SpaceChildSortFilterModel::move(const QModelIndex ¤tIndex, const QModelIndex &targetIndex)
|
||||
{
|
||||
const auto rootSpace = dynamic_cast<SpaceChildrenModel *>(sourceModel())->space();
|
||||
if (rootSpace == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto connection = rootSpace->connection();
|
||||
|
||||
const auto currentParent = currentIndex.parent();
|
||||
auto targetParent = targetIndex.parent();
|
||||
NeoChatRoom *currentParentSpace = nullptr;
|
||||
if (!currentParent.isValid()) {
|
||||
currentParentSpace = rootSpace;
|
||||
} else {
|
||||
currentParentSpace = static_cast<NeoChatRoom *>(connection->room(currentParent.data(SpaceChildrenModel::RoomIDRole).toString()));
|
||||
}
|
||||
NeoChatRoom *targetParentSpace = nullptr;
|
||||
if (!targetParent.isValid()) {
|
||||
targetParentSpace = rootSpace;
|
||||
} else {
|
||||
targetParentSpace = static_cast<NeoChatRoom *>(connection->room(targetParent.data(SpaceChildrenModel::RoomIDRole).toString()));
|
||||
}
|
||||
// If both parents are not resolvable to a room object we don't have the permissions
|
||||
// required for this action.
|
||||
if (currentParentSpace == nullptr || targetParentSpace == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto currentRow = currentIndex.row();
|
||||
auto targetRow = targetIndex.row();
|
||||
|
||||
const auto moveRoomId = currentIndex.data(SpaceChildrenModel::RoomIDRole).toString();
|
||||
auto targetRoom = static_cast<NeoChatRoom *>(connection->room(targetIndex.data(SpaceChildrenModel::RoomIDRole).toString()));
|
||||
// If the target room is a space, assume we want to drop the room into it.
|
||||
if (targetRoom != nullptr && targetRoom->isSpace()) {
|
||||
targetParent = targetIndex;
|
||||
targetParentSpace = targetRoom;
|
||||
targetRow = rowCount(targetParent);
|
||||
}
|
||||
|
||||
const auto newRowCount = rowCount(targetParent) + (currentParentSpace != targetParentSpace ? 1 : 0);
|
||||
for (int i = 0; i < newRowCount; i++) {
|
||||
if (currentParentSpace == targetParentSpace && i == currentRow) {
|
||||
continue;
|
||||
}
|
||||
|
||||
targetParentSpace->setChildOrder(index(i, 0, targetParent).data(SpaceChildrenModel::RoomIDRole).toString(),
|
||||
QString::number(i > targetRow ? i + 1 : i, 36));
|
||||
|
||||
if (i == targetRow) {
|
||||
if (currentParentSpace != targetParentSpace) {
|
||||
currentParentSpace->removeChild(moveRoomId, true);
|
||||
targetParentSpace->addChild(moveRoomId, true, false, false, QString::number(i + 1, 36));
|
||||
} else {
|
||||
targetParentSpace->setChildOrder(currentIndex.data(SpaceChildrenModel::RoomIDRole).toString(), QString::number(i + 1, 36));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_spacechildsortfiltermodel.cpp"
|
||||
|
||||
@@ -46,8 +46,6 @@ protected:
|
||||
*/
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
|
||||
Q_INVOKABLE void move(const QModelIndex ¤tIndex, const QModelIndex &targetIndex);
|
||||
|
||||
Q_SIGNALS:
|
||||
void filterTextChanged();
|
||||
|
||||
|
||||
@@ -37,11 +37,6 @@ SpaceTreeItem::~SpaceTreeItem()
|
||||
qDeleteAll(m_children);
|
||||
}
|
||||
|
||||
bool SpaceTreeItem::operator==(const SpaceTreeItem &other) const
|
||||
{
|
||||
return m_id == other.id();
|
||||
}
|
||||
|
||||
SpaceTreeItem *SpaceTreeItem::child(int number)
|
||||
{
|
||||
if (number < 0 || number >= m_children.size()) {
|
||||
@@ -55,20 +50,12 @@ int SpaceTreeItem::childCount() const
|
||||
return m_children.count();
|
||||
}
|
||||
|
||||
bool SpaceTreeItem::insertChild(SpaceTreeItem *newChild)
|
||||
bool SpaceTreeItem::insertChild(int row, SpaceTreeItem *newChild)
|
||||
{
|
||||
if (newChild == nullptr) {
|
||||
if (row < 0 || row > m_children.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto it = m_children.begin(), end = m_children.end(); it != end; ++it) {
|
||||
if (*it == newChild) {
|
||||
*it = newChild;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
m_children.append(newChild);
|
||||
m_children.insert(row, newChild);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,6 @@ public:
|
||||
Quotient::StateEvents childStates = {});
|
||||
~SpaceTreeItem();
|
||||
|
||||
bool operator==(const SpaceTreeItem &other) const;
|
||||
|
||||
/**
|
||||
* @brief Return the child at the given row number.
|
||||
*
|
||||
@@ -50,9 +48,9 @@ public:
|
||||
int childCount() const;
|
||||
|
||||
/**
|
||||
* @brief Insert the given child.
|
||||
* @brief Insert the given child at the given row number.
|
||||
*/
|
||||
bool insertChild(SpaceTreeItem *newChild);
|
||||
bool insertChild(int row, SpaceTreeItem *newChild);
|
||||
|
||||
/**
|
||||
* @brief Remove the child at the given row number.
|
||||
|
||||
@@ -76,7 +76,6 @@ Comment[ru]=Клиент для Matrix — децентрализованног
|
||||
Comment[sk]=Klient pre matrix, decentralizovaný komunikačný protokol
|
||||
Comment[sl]=Odjemalec za decentralizirani komunikacijski protokol matrix
|
||||
Comment[sv]=En klient för matrix, det decentraliserade kommunikationsprotokollet
|
||||
Comment[ta]=மையமில்லா தகவல் பரிமாற்ற நெறிமுறையான மேட்ரிக்ஸுக்கான செயலி
|
||||
Comment[tr]=Merkezi olmayan iletişim protokolü Matrix için bir istemci
|
||||
Comment[uk]=Клієнт matrix, децентралізованого протоколу обміну даними
|
||||
Comment[x-test]=xxA client for matrix, the decentralized communication protocolxx
|
||||
@@ -254,14 +253,12 @@ Name[eo]=Kundividi
|
||||
Name[es]=Compartir
|
||||
Name[eu]=Partekatu
|
||||
Name[fr]=Partager
|
||||
Name[hu]=Megosztás
|
||||
Name[ia]=Comparti
|
||||
Name[it]=Condivisione
|
||||
Name[ka]=გაზიარება
|
||||
Name[nl]=Gedeelde
|
||||
Name[pl]=Udostępnij
|
||||
Name[sl]=Deli
|
||||
Name[ta]=பகிர்
|
||||
Name[tr]=Paylaş
|
||||
Name[uk]=Оприлюднення
|
||||
Name[x-test]=xxSharexx
|
||||
@@ -273,14 +270,12 @@ Comment[eo]=La rezulto el kundividado de enhavero
|
||||
Comment[es]=El resultado de compartir una parte de contenido
|
||||
Comment[eu]=Eduki pieza bat partekatzearen emaitza
|
||||
Comment[fr]=Le résultat du partage d'une partie de contenu.
|
||||
Comment[hu]=Tartalom megosztásának eredménye
|
||||
Comment[ia]=Le exito de compartir un pecietta de contento
|
||||
Comment[it]=Il risultato della condivisione di un contenuto
|
||||
Comment[ka]=შემცველობის ნაწილის გაზიარების შედეგი
|
||||
Comment[nl]=Het resultaat van het delen van een stukje inhoud
|
||||
Comment[pl]=Wynik udostępniania kawałka treści
|
||||
Comment[sl]=Rezultat deljenega kosa vsebine
|
||||
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
|
||||
|
||||
@@ -482,4 +482,9 @@ QString NeoChatConnection::accountDataJsonString(const QString &type) const
|
||||
return QString::fromUtf8(QJsonDocument(accountDataJson(type)).toJson());
|
||||
}
|
||||
|
||||
void NeoChatConnection::addRoom(Quotient::Room *room)
|
||||
{
|
||||
Connection::addRoom(room, false);
|
||||
}
|
||||
|
||||
#include "moc_neochatconnection.cpp"
|
||||
|
||||
@@ -147,6 +147,12 @@ public:
|
||||
|
||||
bool isOnline() const;
|
||||
|
||||
/**
|
||||
* Add room directly in the connection.
|
||||
* @internal for tests
|
||||
*/
|
||||
void addRoom(Quotient::Room *room);
|
||||
|
||||
Q_SIGNALS:
|
||||
void labelChanged();
|
||||
void directChatNotificationsChanged();
|
||||
|
||||
@@ -1329,7 +1329,7 @@ bool NeoChatRoom::childrenHaveHighlightNotifications() const
|
||||
return SpaceHierarchyCache::instance().spaceHasHighlightNotifications(id());
|
||||
}
|
||||
|
||||
void NeoChatRoom::addChild(const QString &childId, bool setChildParent, bool canonical, bool suggested, const QString &order)
|
||||
void NeoChatRoom::addChild(const QString &childId, bool setChildParent, bool canonical, bool suggested)
|
||||
{
|
||||
if (!isSpace()) {
|
||||
return;
|
||||
@@ -1337,9 +1337,7 @@ void NeoChatRoom::addChild(const QString &childId, bool setChildParent, bool can
|
||||
if (!canSendEvent("m.space.child"_ls)) {
|
||||
return;
|
||||
}
|
||||
setState("m.space.child"_ls,
|
||||
childId,
|
||||
QJsonObject{{QLatin1String("via"), QJsonArray{connection()->domain()}}, {"suggested"_ls, suggested}, {"order"_ls, order}});
|
||||
setState("m.space.child"_ls, childId, QJsonObject{{QLatin1String("via"), QJsonArray{connection()->domain()}}, {"suggested"_ls, suggested}});
|
||||
|
||||
if (setChildParent) {
|
||||
if (auto child = static_cast<NeoChatRoom *>(connection()->room(childId))) {
|
||||
@@ -1405,28 +1403,6 @@ void NeoChatRoom::toggleChildSuggested(const QString &childId)
|
||||
}
|
||||
}
|
||||
|
||||
void NeoChatRoom::setChildOrder(const QString &childId, const QString &order)
|
||||
{
|
||||
if (!isSpace()) {
|
||||
return;
|
||||
}
|
||||
if (!canSendEvent("m.space.child"_ls)) {
|
||||
return;
|
||||
}
|
||||
if (const auto childEvent = currentState().get("m.space.child"_ls, childId)) {
|
||||
auto content = childEvent->contentJson();
|
||||
if (!content.contains("via"_ls)) {
|
||||
return;
|
||||
}
|
||||
if (content.value("order"_ls).toString() == order) {
|
||||
return;
|
||||
}
|
||||
|
||||
content.insert("order"_ls, order);
|
||||
setState("m.space.child"_ls, childId, content);
|
||||
}
|
||||
}
|
||||
|
||||
PushNotificationState::State NeoChatRoom::pushNotificationState() const
|
||||
{
|
||||
return m_currentPushNotificationState;
|
||||
|
||||
@@ -560,7 +560,7 @@ public:
|
||||
* Will fail if the user doesn't have the required privileges or this room is
|
||||
* not a space.
|
||||
*/
|
||||
Q_INVOKABLE void addChild(const QString &childId, bool setChildParent = false, bool canonical = false, bool suggested = false, const QString &order = {});
|
||||
Q_INVOKABLE void addChild(const QString &childId, bool setChildParent = false, bool canonical = false, bool suggested = false);
|
||||
|
||||
/**
|
||||
* @brief Remove the given room as a child.
|
||||
@@ -583,8 +583,6 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE void toggleChildSuggested(const QString &childId);
|
||||
|
||||
void setChildOrder(const QString &childId, const QString &order = {});
|
||||
|
||||
bool isInvite() const;
|
||||
|
||||
bool readOnly() const;
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
kcoreaddons_add_plugin(neochatplugin SOURCES purposeplugin.cpp INSTALL_NAMESPACE "kf6/purpose")
|
||||
target_link_libraries(neochatplugin
|
||||
Qt::DBus
|
||||
KF6::Purpose
|
||||
KF6::KIOGui
|
||||
)
|
||||
@@ -1,55 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include <KIO/CommandLauncherJob>
|
||||
#include <KPluginFactory>
|
||||
#include <Purpose/PluginBase>
|
||||
|
||||
class NeoChatJob : public Purpose::Job
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NeoChatJob(QObject *parent)
|
||||
: Purpose::Job(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QStringList arrayToList(const QJsonArray &array)
|
||||
{
|
||||
QStringList ret;
|
||||
for (const auto &val : array) {
|
||||
ret += val.toString();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void start() override
|
||||
{
|
||||
const QJsonArray urlsJson = data().value(QStringLiteral("urls")).toArray();
|
||||
const QString title = data().value(QStringLiteral("title")).toString();
|
||||
const QString message = QStringLiteral("%1 - %2").arg(title, arrayToList(urlsJson).join(QLatin1Char(' ')));
|
||||
|
||||
auto *job = new KIO::CommandLauncherJob(QStringLiteral("neochat"), {QStringLiteral("--share"), message});
|
||||
connect(job, &KJob::finished, this, &NeoChatJob::emitResult);
|
||||
job->start();
|
||||
}
|
||||
};
|
||||
|
||||
class Q_DECL_EXPORT PurposePlugin : public Purpose::PluginBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PurposePlugin(QObject *p, const QVariantList &)
|
||||
: Purpose::PluginBase(p)
|
||||
{
|
||||
}
|
||||
|
||||
Purpose::Job *createJob() const override
|
||||
{
|
||||
return new NeoChatJob(nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
K_PLUGIN_CLASS_WITH_JSON(PurposePlugin, "purposeplugin.json")
|
||||
|
||||
#include "purposeplugin.moc"
|
||||
@@ -1,60 +0,0 @@
|
||||
{
|
||||
"KPlugin": {
|
||||
"Authors": [
|
||||
{
|
||||
"Name": "Tobias Fella",
|
||||
"Name[ca@valencia]": "Tobias Fella",
|
||||
"Name[ca]": "Tobias Fella",
|
||||
"Name[es]": "Tobias Fella",
|
||||
"Name[fr]": "Tobias Fella",
|
||||
"Name[hu]": "Tobias Fella",
|
||||
"Name[ia]": "Tobias Fella",
|
||||
"Name[it]": "Tobias Fella",
|
||||
"Name[ka]": "Tobias Fella",
|
||||
"Name[nl]": "Tobias Fella",
|
||||
"Name[pl]": "Tobias Fella",
|
||||
"Name[sl]": "Tobias Fella",
|
||||
"Name[tr]": "Tobias Fella",
|
||||
"Name[uk]": "Tobias Fella",
|
||||
"Name[x-test]": "xxTobias Fellaxx"
|
||||
}
|
||||
],
|
||||
"Category": "Utilities",
|
||||
"Description": "Share via NeoChat",
|
||||
"Description[ca@valencia]": "Compartix a través de NeoChat",
|
||||
"Description[ca]": "Comparteix a través del NeoChat",
|
||||
"Description[es]": "Compartir mediante NeoChat",
|
||||
"Description[fr]": "Partager grâce à NeoChat",
|
||||
"Description[hu]": "Megosztás NeoChatben",
|
||||
"Description[ia]": "Comparti via NeoChat",
|
||||
"Description[it]": "Condividi tramite NeoChat",
|
||||
"Description[ka]": "გააზიარეთ NeoChat-ით",
|
||||
"Description[nl]": "Delen via NeoChat",
|
||||
"Description[pl]": "Udostępnij przez NeoChat",
|
||||
"Description[sl]": "Deli prek NeoChat",
|
||||
"Description[tr]": "NeoChat ile Paylaş",
|
||||
"Description[uk]": "Оприлюднити за допомогою NeoChat",
|
||||
"Description[x-test]": "xxShare via NeoChatxx",
|
||||
"Icon": "org.kde.neochat",
|
||||
"License": "GPL",
|
||||
"Name": "NeoChat",
|
||||
"Name[ca@valencia]": "NeoChat",
|
||||
"Name[ca]": "NeoChat",
|
||||
"Name[es]": "NeoChat",
|
||||
"Name[fr]": "NeoChat",
|
||||
"Name[hu]": "NeoChat",
|
||||
"Name[ia]": "Neochat",
|
||||
"Name[it]": "NeoChat",
|
||||
"Name[ka]": "NeoChat",
|
||||
"Name[nl]": "NeoChat",
|
||||
"Name[pl]": "NeoChat",
|
||||
"Name[sl]": "NeoChat",
|
||||
"Name[tr]": "NeoChat",
|
||||
"Name[uk]": "NeoChat",
|
||||
"Name[x-test]": "xxNeoChatxx",
|
||||
"X-Purpose-ActionDisplay": "NeoChat"
|
||||
},
|
||||
"X-Purpose-PluginTypes": [
|
||||
"ShareUrl"
|
||||
]
|
||||
}
|
||||
@@ -23,7 +23,7 @@ ColumnLayout {
|
||||
model: root.connection.accountDataEventTypes
|
||||
delegate: FormCard.FormButtonDelegate {
|
||||
text: modelData
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/MessageSourceSheet.qml", {
|
||||
sourceText: root.connection.accountDataJsonString(modelData)
|
||||
}, {
|
||||
title: i18nc("@title:window", "Event Source"),
|
||||
|
||||
@@ -34,25 +34,7 @@ QQC2.Control {
|
||||
|
||||
onActiveFocusChanged: textField.forceActiveFocus()
|
||||
|
||||
onCurrentRoomChanged: {
|
||||
_private.chatBarCache = currentRoom.mainCache
|
||||
if (ShareHandler.text.length > 0 && ShareHandler.room === root.currentRoom.id) {
|
||||
textField.text = ShareHandler.text;
|
||||
ShareHandler.text = "";
|
||||
ShareHandler.room = "";
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ShareHandler
|
||||
function onRoomChanged(): void {
|
||||
if (ShareHandler.text.length > 0 && ShareHandler.room === root.currentRoom.id) {
|
||||
textField.text = ShareHandler.text;
|
||||
ShareHandler.text = "";
|
||||
ShareHandler.room = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
onCurrentRoomChanged: _private.chatBarCache = currentRoom.mainCache
|
||||
|
||||
/**
|
||||
* @brief The ActionsHandler object to use.
|
||||
@@ -241,7 +223,7 @@ QQC2.Control {
|
||||
x: textField.cursorRectangle.x
|
||||
y: textField.cursorRectangle.y - height
|
||||
|
||||
onFormattingSelected: _private.formatText(format, selectionStart, selectionEnd)
|
||||
onFormattingSelected: root.formatText(format, selectionStart, selectionEnd)
|
||||
}
|
||||
|
||||
Keys.onDeletePressed: {
|
||||
|
||||
@@ -108,32 +108,8 @@ QQC2.Control {
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: Kirigami.Units.smallSpacing
|
||||
right: parent.right
|
||||
rightMargin: Kirigami.Units.smallSpacing
|
||||
}
|
||||
visible: root.hovered
|
||||
icon.name: "edit-copy"
|
||||
text: i18n("Copy to clipboard")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: Clipboard.saveText(root.display);
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
radius: Kirigami.Units.smallSpacing
|
||||
border {
|
||||
width: root.hovered ? 1 : 0
|
||||
color: Kirigami.Theme.highlightColor
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -23,27 +23,17 @@ FormCard.FormCardPage {
|
||||
header: QQC2.TabBar {
|
||||
id: tabBar
|
||||
|
||||
readonly property real tabWidth: tabBar.width / tabBar.count
|
||||
|
||||
QQC2.TabButton {
|
||||
text: qsTr("Room Data")
|
||||
|
||||
implicitWidth: tabBar.tabWidth
|
||||
}
|
||||
QQC2.TabButton {
|
||||
text: qsTr("Server Info")
|
||||
|
||||
implicitWidth: tabBar.tabWidth
|
||||
}
|
||||
QQC2.TabButton {
|
||||
text: i18nc("@title:tab", "Account Data")
|
||||
|
||||
implicitWidth: tabBar.tabWidth
|
||||
}
|
||||
QQC2.TabButton {
|
||||
text: i18nc("@title:tab", "Feature Flags")
|
||||
|
||||
implicitWidth: tabBar.tabWidth
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ ColumnLayout {
|
||||
*/
|
||||
readonly property bool downloaded: root.fileTransferInfo && root.fileTransferInfo.completed
|
||||
onDownloadedChanged: {
|
||||
itineraryModel.path = root.fileTransferInfo.localPath;
|
||||
if (autoOpenFile) {
|
||||
openSavedFile();
|
||||
}
|
||||
@@ -144,6 +145,15 @@ ColumnLayout {
|
||||
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; stops downloading the message's file", "Stop Download")
|
||||
onClicked: root.room.cancelFileTransfer(root.eventId)
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "raw"
|
||||
when: true
|
||||
|
||||
PropertyChanges {
|
||||
target: downloadButton
|
||||
onClicked: root.saveFileAs()
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -186,7 +196,6 @@ ColumnLayout {
|
||||
QQC2.Button {
|
||||
id: downloadButton
|
||||
icon.name: "download"
|
||||
onClicked: root.saveFileAs()
|
||||
|
||||
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; offers ability to download its file", "Download")
|
||||
QQC2.ToolTip.visible: hovered
|
||||
@@ -211,4 +220,83 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
Repeater {
|
||||
id: itinerary
|
||||
model: ItineraryModel {
|
||||
id: itineraryModel
|
||||
connection: root.room.connection
|
||||
}
|
||||
delegate: DelegateChooser {
|
||||
role: "type"
|
||||
DelegateChoice {
|
||||
roleValue: "TrainReservation"
|
||||
delegate: ColumnLayout {
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
RowLayout {
|
||||
QQC2.Label {
|
||||
text: model.name
|
||||
}
|
||||
QQC2.Label {
|
||||
text: model.coach ? i18n("Coach: %1, Seat: %2", model.coach, model.seat) : ""
|
||||
visible: model.coach
|
||||
opacity: 0.7
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
ColumnLayout {
|
||||
QQC2.Label {
|
||||
text: model.departureStation + (model.departurePlatform ? (" [" + model.departurePlatform + "]") : "")
|
||||
}
|
||||
QQC2.Label {
|
||||
text: model.departureTime
|
||||
opacity: 0.7
|
||||
}
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
ColumnLayout {
|
||||
QQC2.Label {
|
||||
text: model.arrivalStation + (model.arrivalPlatform ? (" [" + model.arrivalPlatform + "]") : "")
|
||||
}
|
||||
QQC2.Label {
|
||||
text: model.arrivalTime
|
||||
opacity: 0.7
|
||||
Layout.alignment: Qt.AlignRight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "LodgingReservation"
|
||||
delegate: ColumnLayout {
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.Label {
|
||||
text: model.name
|
||||
}
|
||||
QQC2.Label {
|
||||
text: i18nc("<start time> - <end time>", "%1 - %2", model.startTime, model.endTime)
|
||||
}
|
||||
QQC2.Label {
|
||||
text: model.address
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
icon.name: "map-globe"
|
||||
text: i18nc("@action", "Send to KDE Itinerary")
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
onClicked: itineraryModel.sendToItinerary()
|
||||
visible: itinerary.count > 0
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
/**
|
||||
* @brief A component to show a link preview from a message.
|
||||
*/
|
||||
@@ -5,6 +5,8 @@ import QtQuick
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
TimelineDelegate {
|
||||
id: root
|
||||
contentItem: Kirigami.PlaceholderMessage {
|
||||
@@ -117,13 +117,6 @@ DelegateChooser {
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MessageComponentType.Itinerary
|
||||
delegate: ItineraryComponent {
|
||||
maxContentWidth: root.maxContentWidth
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: MessageComponentType.Poll
|
||||
delegate: PollComponent {
|
||||
@@ -170,11 +170,8 @@ QQC2.TextArea {
|
||||
onChatBarCacheChanged: documentHandler.chatBarCache = chatBarCache
|
||||
|
||||
function updateEditText() {
|
||||
// This could possibly be undefined due to some esoteric QtQuick issue. Referencing it somewhere in JS is enough.
|
||||
documentHandler.document;
|
||||
if (chatBarCache?.isEditing && chatBarCache.relationMessage.length > 0) {
|
||||
root.text = chatBarCache.relationMessage;
|
||||
chatBarCache.updateMentions(root.textDocument, documentHandler);
|
||||
root.forceActiveFocus();
|
||||
root.cursorPosition = root.length;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user