Compare commits
58 Commits
work/carl/
...
work/tobia
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4984181613 | ||
|
|
af75136269 | ||
|
|
c8eb75a148 | ||
|
|
5109b4fcd1 | ||
|
|
1b7f482d0b | ||
|
|
6f9a273d39 | ||
|
|
51d354a9c8 | ||
|
|
40b2b9554b | ||
|
|
ba1aca84ff | ||
|
|
17688a49d5 | ||
|
|
5ff199cc3e | ||
|
|
81a79105d7 | ||
|
|
e39760ccfb | ||
|
|
1c43da2532 | ||
|
|
2846def00f | ||
|
|
e2eb6ab33c | ||
|
|
35b08d085c | ||
|
|
064b0581a7 | ||
|
|
0cc38aa69a | ||
|
|
8312483659 | ||
|
|
0ceb0b4421 | ||
|
|
cc373365fb | ||
|
|
75d9b6e2a1 | ||
|
|
78fa38ba68 | ||
|
|
0b28712a34 | ||
|
|
48937c8d9a | ||
|
|
8c966a5e1a | ||
|
|
f49dd371b7 | ||
|
|
6947fbc12a | ||
|
|
550dc43dc0 | ||
|
|
23c9a4fea7 | ||
|
|
7d26f3351f | ||
|
|
b546554fef | ||
|
|
cc058a7cd3 | ||
|
|
7654b83339 | ||
|
|
93426546ad | ||
|
|
23bc38ca6c | ||
|
|
5ccce364d3 | ||
|
|
b488b55a71 | ||
|
|
0bace17074 | ||
|
|
ad6c7dbd1f | ||
|
|
2a6e63595e | ||
|
|
4d62ad1938 | ||
|
|
e7c3a24011 | ||
|
|
37468607fe | ||
|
|
5b007129e3 | ||
|
|
93ceb4d49c | ||
|
|
85b806fcba | ||
|
|
20596aabb8 | ||
|
|
825108c59e | ||
|
|
09c31b20e6 | ||
|
|
78271a3738 | ||
|
|
e029aaadfc | ||
|
|
f6efa35ed2 | ||
|
|
728bad00b4 | ||
|
|
97f3013f7a | ||
|
|
269a832ac9 | ||
|
|
3b5b7af531 |
@@ -2,7 +2,7 @@
|
||||
"id": "org.kde.neochat",
|
||||
"branch": "master",
|
||||
"runtime": "org.kde.Platform",
|
||||
"runtime-version": "6.6-kf6preview",
|
||||
"runtime-version": "6.6",
|
||||
"sdk": "org.kde.Sdk",
|
||||
"command": "neochat",
|
||||
"tags": [
|
||||
|
||||
@@ -49,3 +49,7 @@ 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 "5.240.0")
|
||||
set(KF_MIN_VERSION "6.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 5.105)
|
||||
set(KDE_COMPILERSETTINGS_LEVEL 6.0)
|
||||
|
||||
include(FeatureSummary)
|
||||
include(ECMSetupVersion)
|
||||
@@ -72,6 +72,10 @@ 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
|
||||
|
||||
@@ -55,6 +55,7 @@ private Q_SLOTS:
|
||||
void genericBody_data();
|
||||
void genericBody();
|
||||
void nullGenericBody();
|
||||
void markdownBody();
|
||||
void subtitle();
|
||||
void nullSubtitle();
|
||||
void mediaInfo();
|
||||
@@ -293,6 +294,13 @@ 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());
|
||||
|
||||
@@ -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,6 +59,7 @@
|
||||
<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>
|
||||
@@ -78,8 +79,11 @@
|
||||
<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>
|
||||
@@ -89,6 +93,7 @@
|
||||
<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>
|
||||
@@ -100,6 +105,7 @@
|
||||
<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>
|
||||
@@ -125,6 +131,7 @@
|
||||
<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>
|
||||
@@ -152,6 +159,7 @@
|
||||
<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>
|
||||
@@ -178,6 +186,7 @@
|
||||
<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>
|
||||
@@ -204,6 +213,7 @@
|
||||
<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>
|
||||
@@ -234,8 +244,7 @@
|
||||
<keyword>Matrix</keyword>
|
||||
<keyword>Kirigami</keyword>
|
||||
</keywords>
|
||||
<developer>
|
||||
<id>kde.org</id>
|
||||
<developer id="kde.org">
|
||||
<name>The KDE Community</name>
|
||||
<url>https://kde.org</url>
|
||||
</developer>
|
||||
@@ -264,6 +273,7 @@
|
||||
<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>
|
||||
@@ -285,8 +295,11 @@
|
||||
<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>
|
||||
@@ -296,6 +309,7 @@
|
||||
<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
|
||||
@@ -316,6 +330,7 @@
|
||||
<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>
|
||||
@@ -345,6 +360,7 @@
|
||||
<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>
|
||||
@@ -366,6 +382,7 @@
|
||||
<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
1992
po/hu/neochat.po
1992
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,6 +3,10 @@
|
||||
# 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
|
||||
@@ -165,6 +169,8 @@ 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
|
||||
@@ -207,19 +213,6 @@ 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
|
||||
@@ -304,24 +297,10 @@ 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
|
||||
@@ -330,13 +309,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()
|
||||
@@ -420,9 +399,14 @@ 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
|
||||
@@ -556,7 +540,7 @@ if(NOT ANDROID)
|
||||
set_target_properties(neochat-app PROPERTIES OUTPUT_NAME "neochat")
|
||||
endif()
|
||||
|
||||
if(TARGET KF6::DBusAddons)
|
||||
if(TARGET KF6::DBusAddons AND NOT WIN32)
|
||||
target_link_libraries(neochat PUBLIC KF6::DBusAddons)
|
||||
target_compile_definitions(neochat PUBLIC -DHAVE_KDBUSADDONS)
|
||||
endif()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "chatbarcache.h"
|
||||
|
||||
#include "chatdocumenthandler.h"
|
||||
#include "eventhandler.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
@@ -117,7 +118,7 @@ QString ChatBarCache::relationMessage() const
|
||||
|
||||
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
|
||||
EventHandler eventhandler(room, &**event);
|
||||
return eventhandler.getPlainBody();
|
||||
return eventhandler.getMarkdownBody();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -163,6 +164,54 @@ 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,8 +5,11 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickTextDocument>
|
||||
#include <QTextCursor>
|
||||
|
||||
class ChatDocumentHandler;
|
||||
|
||||
/**
|
||||
* @brief Defines a user mention in the current chat or edit text.
|
||||
*/
|
||||
@@ -174,6 +177,11 @@ 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,6 +40,7 @@ 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). */
|
||||
|
||||
@@ -280,6 +280,22 @@ 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,6 +185,13 @@ 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.
|
||||
*
|
||||
|
||||
36
src/fakerunner.cpp
Normal file
36
src/fakerunner.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
// 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"
|
||||
31
src/fakerunner.h
Normal file
31
src/fakerunner.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// 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,6 +11,7 @@
|
||||
#include <QQmlNetworkAccessManagerFactory>
|
||||
#include <QQuickStyle>
|
||||
#include <QQuickWindow>
|
||||
#include <QtQml/QQmlExtensionPlugin>
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include <QGuiApplication>
|
||||
@@ -44,10 +45,16 @@
|
||||
#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
|
||||
@@ -186,6 +193,9 @@ 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);
|
||||
@@ -196,6 +206,14 @@ 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();
|
||||
}
|
||||
@@ -205,6 +223,8 @@ 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());
|
||||
@@ -215,26 +235,32 @@ 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();
|
||||
for (const auto &arg : args) {
|
||||
RoomManager::instance().resolveResource(arg);
|
||||
}
|
||||
});
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
|
||||
@@ -247,6 +273,10 @@ 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);
|
||||
|
||||
@@ -255,7 +285,7 @@ int main(int argc, char *argv[])
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!parser.positionalArguments().isEmpty()) {
|
||||
if (!parser.positionalArguments().isEmpty() && !parser.isSet("share"_ls)) {
|
||||
RoomManager::instance().setUrlArgument(parser.positionalArguments()[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "itinerarymodel.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QProcess>
|
||||
|
||||
#include "config-neochat.h"
|
||||
@@ -16,20 +17,6 @@ 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()) {
|
||||
@@ -133,11 +120,7 @@ QString ItineraryModel::path() const
|
||||
|
||||
void ItineraryModel::setPath(const QString &path)
|
||||
{
|
||||
if (path == m_path) {
|
||||
return;
|
||||
}
|
||||
m_path = path;
|
||||
Q_EMIT pathChanged();
|
||||
loadData();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,19 +4,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QJsonArray>
|
||||
#include <QPointer>
|
||||
#include <QQmlEngine>
|
||||
#include <QString>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
|
||||
class ItineraryModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
public:
|
||||
enum Roles {
|
||||
@@ -37,9 +34,6 @@ 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;
|
||||
|
||||
@@ -50,12 +44,7 @@ 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,11 +64,13 @@ 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});
|
||||
}
|
||||
});
|
||||
@@ -152,6 +154,9 @@ 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();
|
||||
}
|
||||
@@ -209,6 +214,7 @@ 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";
|
||||
@@ -240,11 +246,19 @@ 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(), {}};
|
||||
}
|
||||
@@ -260,3 +274,25 @@ 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,6 +8,7 @@
|
||||
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "eventhandler.h"
|
||||
#include "itinerarymodel.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
@@ -45,6 +46,7 @@ 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. */
|
||||
@@ -92,4 +94,7 @@ private:
|
||||
void updateComponents(bool isEditing = false);
|
||||
|
||||
LinkPreviewer *m_linkPreviewer = nullptr;
|
||||
ItineraryModel *m_itineraryModel = nullptr;
|
||||
|
||||
void updateItineraryModel();
|
||||
};
|
||||
|
||||
@@ -162,6 +162,30 @@ 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();
|
||||
@@ -174,28 +198,6 @@ 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;
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ QHash<int, QByteArray> RoomTreeModel::roleNames() const
|
||||
// TODO room type changes
|
||||
QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <Quotient/room.h>
|
||||
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
SpaceChildrenModel::SpaceChildrenModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
@@ -32,19 +33,13 @@ void SpaceChildrenModel::setSpace(NeoChatRoom *space)
|
||||
}
|
||||
// disconnect the new room signal from the old connection in case it is different.
|
||||
if (m_space != nullptr) {
|
||||
disconnect(m_space->connection(), &Quotient::Connection::loadedRoomState, this, nullptr);
|
||||
m_space->connection()->disconnect(this);
|
||||
m_space->disconnect(this);
|
||||
}
|
||||
|
||||
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())) {
|
||||
@@ -66,6 +61,17 @@ 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;
|
||||
@@ -112,6 +118,11 @@ 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);
|
||||
@@ -120,8 +131,7 @@ void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJ
|
||||
insertChildren(job->rooms(), index(insertRow, 0, parent));
|
||||
});
|
||||
}
|
||||
parentItem->insertChild(insertRow,
|
||||
new SpaceTreeItem(dynamic_cast<NeoChatConnection *>(m_space->connection()),
|
||||
parentItem->insertChild(new SpaceTreeItem(dynamic_cast<NeoChatConnection *>(m_space->connection()),
|
||||
parentItem,
|
||||
children[i].roomId,
|
||||
children[i].name,
|
||||
|
||||
@@ -60,4 +60,65 @@ 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,6 +46,8 @@ 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,6 +37,11 @@ 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()) {
|
||||
@@ -50,12 +55,20 @@ int SpaceTreeItem::childCount() const
|
||||
return m_children.count();
|
||||
}
|
||||
|
||||
bool SpaceTreeItem::insertChild(int row, SpaceTreeItem *newChild)
|
||||
bool SpaceTreeItem::insertChild(SpaceTreeItem *newChild)
|
||||
{
|
||||
if (row < 0 || row > m_children.size()) {
|
||||
if (newChild == nullptr) {
|
||||
return false;
|
||||
}
|
||||
m_children.insert(row, newChild);
|
||||
|
||||
for (auto it = m_children.begin(), end = m_children.end(); it != end; ++it) {
|
||||
if (*it == newChild) {
|
||||
*it = newChild;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
m_children.append(newChild);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ public:
|
||||
Quotient::StateEvents childStates = {});
|
||||
~SpaceTreeItem();
|
||||
|
||||
bool operator==(const SpaceTreeItem &other) const;
|
||||
|
||||
/**
|
||||
* @brief Return the child at the given row number.
|
||||
*
|
||||
@@ -48,9 +50,9 @@ public:
|
||||
int childCount() const;
|
||||
|
||||
/**
|
||||
* @brief Insert the given child at the given row number.
|
||||
* @brief Insert the given child.
|
||||
*/
|
||||
bool insertChild(int row, SpaceTreeItem *newChild);
|
||||
bool insertChild(SpaceTreeItem *newChild);
|
||||
|
||||
/**
|
||||
* @brief Remove the child at the given row number.
|
||||
|
||||
@@ -76,6 +76,7 @@ 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
|
||||
@@ -253,12 +254,14 @@ 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
|
||||
@@ -270,12 +273,14 @@ 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
|
||||
|
||||
@@ -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)
|
||||
void NeoChatRoom::addChild(const QString &childId, bool setChildParent, bool canonical, bool suggested, const QString &order)
|
||||
{
|
||||
if (!isSpace()) {
|
||||
return;
|
||||
@@ -1337,7 +1337,9 @@ 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}});
|
||||
setState("m.space.child"_ls,
|
||||
childId,
|
||||
QJsonObject{{QLatin1String("via"), QJsonArray{connection()->domain()}}, {"suggested"_ls, suggested}, {"order"_ls, order}});
|
||||
|
||||
if (setChildParent) {
|
||||
if (auto child = static_cast<NeoChatRoom *>(connection()->room(childId))) {
|
||||
@@ -1403,6 +1405,28 @@ 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);
|
||||
Q_INVOKABLE void addChild(const QString &childId, bool setChildParent = false, bool canonical = false, bool suggested = false, const QString &order = {});
|
||||
|
||||
/**
|
||||
* @brief Remove the given room as a child.
|
||||
@@ -583,6 +583,8 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE void toggleChildSuggested(const QString &childId);
|
||||
|
||||
void setChildOrder(const QString &childId, const QString &order = {});
|
||||
|
||||
bool isInvite() const;
|
||||
|
||||
bool readOnly() const;
|
||||
|
||||
9
src/purpose/CMakeLists.txt
Normal file
9
src/purpose/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
# 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
|
||||
)
|
||||
55
src/purpose/purposeplugin.cpp
Normal file
55
src/purpose/purposeplugin.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
// 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"
|
||||
60
src/purpose/purposeplugin.json
Normal file
60
src/purpose/purposeplugin.json
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"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("qrc:/org/kde/neochat/qml/MessageSourceSheet.qml", {
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
|
||||
sourceText: root.connection.accountDataJsonString(modelData)
|
||||
}, {
|
||||
title: i18nc("@title:window", "Event Source"),
|
||||
|
||||
@@ -34,7 +34,25 @@ QQC2.Control {
|
||||
|
||||
onActiveFocusChanged: textField.forceActiveFocus()
|
||||
|
||||
onCurrentRoomChanged: _private.chatBarCache = currentRoom.mainCache
|
||||
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 = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The ActionsHandler object to use.
|
||||
@@ -223,7 +241,7 @@ QQC2.Control {
|
||||
x: textField.cursorRectangle.x
|
||||
y: textField.cursorRectangle.y - height
|
||||
|
||||
onFormattingSelected: root.formatText(format, selectionStart, selectionEnd)
|
||||
onFormattingSelected: _private.formatText(format, selectionStart, selectionEnd)
|
||||
}
|
||||
|
||||
Keys.onDeletePressed: {
|
||||
|
||||
@@ -23,17 +23,27 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import org.kde.neochat.config
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
title: i18nc("@title:window", "General")
|
||||
title: i18nc("@title:window", "Proxy")
|
||||
|
||||
property int currentType
|
||||
property bool proxyConfigChanged: false
|
||||
|
||||
@@ -35,7 +35,7 @@ Kirigami.ScrollablePage {
|
||||
}
|
||||
|
||||
footer: Kirigami.PlaceholderMessage {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width
|
||||
text: i18n("Loading…")
|
||||
visible: notificationsModel.nextToken.length > 0 && listView.count > 0
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ ColumnLayout {
|
||||
id: roomListModel
|
||||
connection: root.connection
|
||||
}
|
||||
currentIndex: -1
|
||||
currentIndex: 0
|
||||
Component.onCompleted: currentIndex = roomListModel.rowForRoom(root.room)
|
||||
onCurrentValueChanged: root.room = roomListModel.roomByAliasOrId(roomComboBox.currentValue)
|
||||
}
|
||||
|
||||
@@ -72,25 +72,6 @@ QQC2.ScrollView {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: devtoolsButton
|
||||
|
||||
icon.name: "tools"
|
||||
text: i18n("Open developer tools")
|
||||
visible: Config.developerTools && !root.room.isSpace
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
onClicked: {
|
||||
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'DevtoolsPage.qml'), {
|
||||
room: root.room,
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18n("Developer Tools")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: searchButton
|
||||
visible: !root.room.isSpace
|
||||
|
||||
@@ -136,7 +136,11 @@ Kirigami.Page {
|
||||
if (root.spaceChanging) {
|
||||
treeView.expandRecursively();
|
||||
if (spaceDrawer.showDirectChats || spaceDrawer.selectedSpaceId.length < 1) {
|
||||
RoomManager.resolveResource(treeView.itemAtIndex(treeView.index(1, 0)).currentRoom.id);
|
||||
const item = treeView.itemAtIndex(treeView.index(1, 0))
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
RoomManager.resolveResource(item.currentRoom.id);
|
||||
}
|
||||
root.spaceChanging = false;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import QtQuick.Layouts
|
||||
import Qt.labs.qmlmodels
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.timeline
|
||||
|
||||
/**
|
||||
* @brief Component for visualising the loaded media items in the room.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import QtQuick
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.timeline
|
||||
|
||||
/**
|
||||
* @brief Component for finding messages in a room.
|
||||
|
||||
@@ -38,7 +38,7 @@ ColumnLayout {
|
||||
}
|
||||
FormCard.FormCard {
|
||||
Repeater {
|
||||
model: room.connection.getSupportedRoomVersions()
|
||||
model: root.connection.getSupportedRoomVersions()
|
||||
|
||||
delegate: FormCard.FormTextDelegate {
|
||||
text: modelData.id
|
||||
|
||||
@@ -11,7 +11,7 @@ import org.kde.neochat
|
||||
KirigamiSettings.CategorizedSettings {
|
||||
id: root
|
||||
|
||||
required property NeoChatConnection connection
|
||||
property NeoChatConnection connection
|
||||
|
||||
objectName: "settingsPage"
|
||||
actions: [
|
||||
|
||||
@@ -18,6 +18,7 @@ Item {
|
||||
required property bool expanded
|
||||
required property int hasChildren
|
||||
required property int depth
|
||||
required property int row
|
||||
required property string roomId
|
||||
required property string displayName
|
||||
required property url avatarUrl
|
||||
@@ -36,10 +37,17 @@ Item {
|
||||
signal createRoom
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
anchors.centerIn: root
|
||||
id: mainDelegate
|
||||
property int row: root.row
|
||||
|
||||
anchors.horizontalCenter: root.horizontalCenter
|
||||
anchors.verticalCenter: root.verticalCenter
|
||||
width: sizeHelper.currentWidth
|
||||
|
||||
highlighted: dropArea.containsDrag
|
||||
|
||||
contentItem: RowLayout {
|
||||
z: 1
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
RowLayout {
|
||||
@@ -140,15 +148,55 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
onTapped: {
|
||||
MouseArea {
|
||||
id: dragArea
|
||||
anchors.fill: parent
|
||||
|
||||
drag.target: mainDelegate
|
||||
drag.axis: Drag.YAxis
|
||||
|
||||
drag.onActiveChanged: {
|
||||
if (!dragArea.drag.active) {
|
||||
mainDelegate.Drag.drop();
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (root.isSpace) {
|
||||
root.treeView.toggleExpanded(row);
|
||||
root.treeView.toggleExpanded(root.row);
|
||||
} else {
|
||||
RoomManager.resolveResource(root.roomId, root.isJoined ? "" : "join");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
when: mainDelegate.Drag.active && root.parentRoom.canSendState("m.space.child")
|
||||
ParentChange {
|
||||
target: mainDelegate
|
||||
parent: root.treeView
|
||||
}
|
||||
|
||||
AnchorChanges {
|
||||
target: mainDelegate
|
||||
anchors.horizontalCenter: undefined
|
||||
anchors.verticalCenter: undefined
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Drag.active: dragArea.drag.active
|
||||
Drag.hotSpot.x: mainDelegate.width / 2
|
||||
Drag.hotSpot.y: Kirigami.Units.smallSpacing
|
||||
}
|
||||
|
||||
DropArea {
|
||||
id: dropArea
|
||||
anchors.fill: parent
|
||||
onDropped: (drag) => {
|
||||
root.treeView.model.move(root.treeView.index(drag.source.row, 0), root.treeView.index(root.row, 0))
|
||||
}
|
||||
}
|
||||
|
||||
DelegateSizeHelper {
|
||||
|
||||
@@ -31,7 +31,7 @@ FormCard.FormCardPage {
|
||||
|
||||
delegate: FormCard.FormButtonDelegate {
|
||||
text: model.stateKey
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer('qrc:/org/kde/neochat/qml/MessageSourceSheet.qml', {
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
|
||||
sourceText: stateKeysModel.stateEventJson(stateKeysModel.index(model.index, 0))
|
||||
}, {
|
||||
title: i18nc("@title:window", "Event Source"),
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.kde.kitemmodels
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.config
|
||||
import org.kde.neochat.timeline
|
||||
|
||||
QQC2.ScrollView {
|
||||
id: root
|
||||
@@ -79,6 +80,10 @@ QQC2.ScrollView {
|
||||
|
||||
model: root.messageFilterModel
|
||||
|
||||
onCountChanged: if (root.roomChanging) {
|
||||
root.positionViewAtBeginning();
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: messageListView.atYBeginning
|
||||
|
||||
@@ -118,6 +118,22 @@ RowLayout {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
visible: false
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
visible: Config.developerTools
|
||||
icon.name: "tools"
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'DevtoolsPage.qml'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18n("Developer Tools")
|
||||
});
|
||||
text: i18n("Open developer tools")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
Layout.minimumWidth: Layout.preferredWidth
|
||||
Layout.alignment: Qt.AlignRight
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
icon.name: "settings-configure"
|
||||
onClicked: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'SettingsPage.qml'), {
|
||||
|
||||
@@ -211,6 +211,17 @@ FormCard.FormCardPage {
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormCard {
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18nc("@action:button", "Open proxy settings")
|
||||
icon.name: "settings-configure"
|
||||
onClicked: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "NetworkProxyPage.qml"), {}, {
|
||||
title: i18nc("@title:window", "Proxy Settings")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
LoginHelper.init();
|
||||
module.item.forceActiveFocus();
|
||||
|
||||
@@ -53,6 +53,9 @@ Kirigami.ApplicationWindow {
|
||||
MatrixImageProvider.connection = root.connection;
|
||||
RoomManager.connection = root.connection;
|
||||
SpaceHierarchyCache.connection = root.connection;
|
||||
if (ShareHandler.text && root.connection) {
|
||||
root.handleShare();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
@@ -236,6 +239,9 @@ Kirigami.ApplicationWindow {
|
||||
RoomManager.connection = root.connection;
|
||||
SpaceHierarchyCache.connection = root.connection;
|
||||
WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);
|
||||
if (ShareHandler.text && root.connection) {
|
||||
root.handleShare()
|
||||
}
|
||||
if (Config.minimizeToSystemTrayOnStartup && !Kirigami.Settings.isMobile && Controller.supportSystemTray && Config.systemTray) {
|
||||
restoreWindowGeometryConnections.enabled = true; // To restore window size and position
|
||||
} else {
|
||||
@@ -425,10 +431,12 @@ Kirigami.ApplicationWindow {
|
||||
|
||||
z: 20
|
||||
x: 0
|
||||
Accessible.ignored: true
|
||||
y: parent.height - implicitHeight
|
||||
contentItem: QQC2.Label {
|
||||
id: linkText
|
||||
text: parent.text.startsWith("https://matrix.to/") ? "" : parent.text
|
||||
Accessible.description: i18nc("@info screenreader", "The currently selected link")
|
||||
}
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
background: Rectangle {
|
||||
@@ -448,4 +456,25 @@ Kirigami.ApplicationWindow {
|
||||
});
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: ShareHandler
|
||||
function onTextChanged(): void {
|
||||
if (root.connection && ShareHandler.text.length > 0) {
|
||||
handleShare();
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleShare(): void {
|
||||
const dialog = applicationWindow().pageStack.pushDialogLayer("qrc:/org/kde/neochat/qml/ChooseRoomDialog.qml", {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18nc("@title", "Share"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
})
|
||||
dialog.chosen.connect(function(targetRoomId) {
|
||||
RoomManager.resolveResource(targetRoomId)
|
||||
ShareHandler.room = targetRoomId
|
||||
dialog.closeDialog()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
37
src/sharehandler.cpp
Normal file
37
src/sharehandler.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "sharehandler.h"
|
||||
|
||||
ShareHandler::ShareHandler(QObject *parent)
|
||||
: QObject(parent)
|
||||
{}
|
||||
|
||||
QString ShareHandler::text() const
|
||||
{
|
||||
return m_text;
|
||||
}
|
||||
|
||||
void ShareHandler::setText(const QString &text)
|
||||
{
|
||||
if (text == m_text) {
|
||||
return;
|
||||
}
|
||||
m_text = text;
|
||||
Q_EMIT textChanged();
|
||||
}
|
||||
|
||||
QString ShareHandler::room() const
|
||||
{
|
||||
return m_room;
|
||||
}
|
||||
|
||||
void ShareHandler::setRoom(const QString &roomId)
|
||||
{
|
||||
if (roomId == m_room) {
|
||||
return;
|
||||
}
|
||||
m_room = roomId;
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
46
src/sharehandler.h
Normal file
46
src/sharehandler.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
class ShareHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
|
||||
Q_PROPERTY(QString room READ room WRITE setRoom NOTIFY roomChanged)
|
||||
|
||||
public:
|
||||
static ShareHandler &instance()
|
||||
{
|
||||
static ShareHandler _instance;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
static ShareHandler *create(QQmlEngine *, QJSEngine *)
|
||||
{
|
||||
QQmlEngine::setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
|
||||
return &instance();
|
||||
}
|
||||
|
||||
QString text() const;
|
||||
void setText(const QString &url);
|
||||
|
||||
QString room() const;
|
||||
void setRoom(const QString &roomId);
|
||||
|
||||
Q_SIGNALS:
|
||||
void textChanged();
|
||||
void roomChanged();
|
||||
|
||||
private:
|
||||
explicit ShareHandler(QObject *parent = nullptr);
|
||||
|
||||
QString m_text;
|
||||
QString m_room;
|
||||
};
|
||||
@@ -350,8 +350,9 @@ MessageComponent TextHandler::nextBlock(const QString &string,
|
||||
|
||||
QString TextHandler::stripBlockTags(QString string, const QString &tagType) const
|
||||
{
|
||||
if (blockTags.contains(tagType) && tagType != QStringLiteral("ol") && tagType != QStringLiteral("ul") && tagType != QStringLiteral("table")) {
|
||||
string.replace(QLatin1String("<%1>").arg(tagType), QString()).replace(QLatin1String("</%1>").arg(tagType), QString());
|
||||
if (blockTags.contains(tagType) && tagType != QStringLiteral("ol") && tagType != QStringLiteral("ul") && tagType != QStringLiteral("table")
|
||||
&& string.startsWith(QLatin1String("<%1").arg(tagType))) {
|
||||
string.remove(0, string.indexOf(u'>') + 1).remove(string.indexOf(QLatin1String("</%1>").arg(tagType)), string.size());
|
||||
}
|
||||
|
||||
if (string.startsWith(QStringLiteral("\n"))) {
|
||||
@@ -363,7 +364,7 @@ QString TextHandler::stripBlockTags(QString string, const QString &tagType) cons
|
||||
if (tagType == QStringLiteral("pre")) {
|
||||
if (string.startsWith(QStringLiteral("<code"))) {
|
||||
string.remove(0, string.indexOf(u'>') + 1);
|
||||
string.remove(string.size() - 7, string.size());
|
||||
string.remove(string.indexOf(QLatin1String("</code>")), string.size());
|
||||
}
|
||||
if (string.endsWith(QStringLiteral("\n"))) {
|
||||
string.remove(string.size() - 1, string.size());
|
||||
@@ -371,8 +372,8 @@ QString TextHandler::stripBlockTags(QString string, const QString &tagType) cons
|
||||
}
|
||||
if (tagType == QStringLiteral("blockquote")) {
|
||||
if (string.startsWith(QStringLiteral("<p>"))) {
|
||||
string.remove(0, 3);
|
||||
string.remove(string.size() - 4, string.size());
|
||||
string.remove(0, string.indexOf(u'>') + 1);
|
||||
string.remove(string.indexOf(QLatin1String("</p>")), string.size());
|
||||
}
|
||||
if (!string.startsWith(u'"')) {
|
||||
string.prepend(u'"');
|
||||
@@ -697,28 +698,7 @@ QString TextHandler::emoteString(const NeoChatRoom *room, const Quotient::RoomEv
|
||||
QString TextHandler::convertCodeLanguageString(const QString &languageString)
|
||||
{
|
||||
const int equalsPos = languageString.indexOf(u'-');
|
||||
auto data = languageString.right(languageString.length() - equalsPos - 1);
|
||||
|
||||
// The standard markdown syntax uses lower case. This will get a subgroup of
|
||||
// single word languages to work.
|
||||
if (data.first(1).isLower()) {
|
||||
data[0] = data[0].toUpper();
|
||||
}
|
||||
|
||||
if (data == QStringLiteral("Cpp")) {
|
||||
data = QStringLiteral("C++");
|
||||
}
|
||||
if (data == QStringLiteral("Json")) {
|
||||
data = QStringLiteral("JSON");
|
||||
}
|
||||
if (data == QStringLiteral("Html")) {
|
||||
data = QStringLiteral("HTML");
|
||||
}
|
||||
if (data == QStringLiteral("Qml")) {
|
||||
data = QStringLiteral("QML");
|
||||
}
|
||||
|
||||
return data;
|
||||
return languageString.right(languageString.length() - equalsPos - 1);
|
||||
}
|
||||
|
||||
#include "moc_texthandler.cpp"
|
||||
|
||||
38
src/timeline/CMakeLists.txt
Normal file
38
src/timeline/CMakeLists.txt
Normal file
@@ -0,0 +1,38 @@
|
||||
# SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
qt_add_library(timeline STATIC)
|
||||
qt_add_qml_module(timeline
|
||||
URI org.kde.neochat.timeline
|
||||
QML_FILES
|
||||
EventDelegate.qml
|
||||
TimelineDelegate.qml
|
||||
MessageDelegate.qml
|
||||
LoadingDelegate.qml
|
||||
ReadMarkerDelegate.qml
|
||||
StateDelegate.qml
|
||||
TimelineEndDelegate.qml
|
||||
Bubble.qml
|
||||
AvatarFlow.qml
|
||||
ReactionDelegate.qml
|
||||
SectionDelegate.qml
|
||||
MessageComponentChooser.qml
|
||||
AudioComponent.qml
|
||||
CodeComponent.qml
|
||||
EncryptedComponent.qml
|
||||
FileComponent.qml
|
||||
ImageComponent.qml
|
||||
ItineraryComponent.qml
|
||||
LinkPreviewComponent.qml
|
||||
LiveLocationComponent.qml
|
||||
LoadComponent.qml
|
||||
LocationComponent.qml
|
||||
MessageEditComponent.qml
|
||||
MimeComponent.qml
|
||||
PollComponent.qml
|
||||
QuoteComponent.qml
|
||||
ReplyComponent.qml
|
||||
StateComponent.qml
|
||||
TextComponent.qml
|
||||
VideoComponent.qml
|
||||
)
|
||||
@@ -108,8 +108,32 @@ 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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user