Compare commits
43 Commits
v24.08.3
...
work/tobia
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5edb3e5145 | ||
|
|
a794420d3e | ||
|
|
8b606266c5 | ||
|
|
11e4329d5e | ||
|
|
68dfc6ca81 | ||
|
|
e0c8945431 | ||
|
|
fe60ee00fb | ||
|
|
5990e00577 | ||
|
|
358a699290 | ||
|
|
da6e4378d9 | ||
|
|
5edb0629b3 | ||
|
|
5170854a2c | ||
|
|
37d6033df4 | ||
|
|
ea2f891533 | ||
|
|
42cec7d5ba | ||
|
|
2df2e39d43 | ||
|
|
b34525a4d8 | ||
|
|
0a7bd64b0d | ||
|
|
11fd4f88ec | ||
|
|
4d9452b862 | ||
|
|
060e30e612 | ||
|
|
ade7270add | ||
|
|
338428b0c0 | ||
|
|
54574e4450 | ||
|
|
ae564451b8 | ||
|
|
9a4504ce61 | ||
|
|
80785a2ff3 | ||
|
|
cb8ed02e82 | ||
|
|
a64c1b8eab | ||
|
|
d43aa169c3 | ||
|
|
8ae7141851 | ||
|
|
d3908db2c9 | ||
|
|
55de838a55 | ||
|
|
4cdfc38b87 | ||
|
|
51197d7c1a | ||
|
|
2a2a2e0c05 | ||
|
|
07fee30cc0 | ||
|
|
83c6ce0ace | ||
|
|
fb7303efa0 | ||
|
|
ef5585d312 | ||
|
|
11475259a1 | ||
|
|
9df4cd6f13 | ||
|
|
c9583964c8 |
@@ -8,13 +8,13 @@ cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# KDE Applications version, managed by release script.
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "24")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "08")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "3")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "11")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||
|
||||
set(KF_MIN_VERSION "6.0")
|
||||
set(KF_MIN_VERSION "6.4")
|
||||
set(QT_MIN_VERSION "6.5")
|
||||
|
||||
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
|
||||
@@ -38,7 +38,6 @@ include(KDEGitCommitHooks)
|
||||
include(ECMCheckOutboundLicense)
|
||||
include(ECMQtDeclareLoggingCategory)
|
||||
include(ECMAddAndroidApk)
|
||||
include(ECMQmlModule)
|
||||
if (NOT ANDROID)
|
||||
include(KDEClangFormat)
|
||||
endif()
|
||||
@@ -60,6 +59,7 @@ set_package_properties(Qt6 PROPERTIES
|
||||
PURPOSE "Basic application components"
|
||||
)
|
||||
|
||||
qt_policy(SET QTP0001 NEW)
|
||||
if (QT_KNOWN_POLICY_QTP0004)
|
||||
qt_policy(SET QTP0004 NEW)
|
||||
endif ()
|
||||
|
||||
@@ -274,6 +274,7 @@ void TextHandlerTest::receiveRichInPlainOut_data()
|
||||
QTest::newRow("ampersand") << QStringLiteral("a & b") << QStringLiteral("a & b");
|
||||
QTest::newRow("quote") << QStringLiteral(""a and b"") << QStringLiteral("\"a and b\"");
|
||||
QTest::newRow("new line") << QStringLiteral("new<br>line") << QStringLiteral("new\nline");
|
||||
QTest::newRow("unescape") << QStringLiteral("can't") << QStringLiteral("can't");
|
||||
}
|
||||
|
||||
void TextHandlerTest::receiveRichInPlainOut()
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
<name xml:lang="eo">NeoChat</name>
|
||||
<name xml:lang="es">NeoChat</name>
|
||||
<name xml:lang="eu">NeoChat</name>
|
||||
<name xml:lang="fa">نئوچت</name>
|
||||
<name xml:lang="fi">NeoChat</name>
|
||||
<name xml:lang="fr">NeoChat</name>
|
||||
<name xml:lang="gl">NeoChat</name>
|
||||
@@ -56,7 +55,6 @@
|
||||
<summary xml:lang="ca">Xategeu amb els vostres amics a Matrix</summary>
|
||||
<summary xml:lang="ca-valencia">Xategeu amb els vostres amics a Matrix</summary>
|
||||
<summary xml:lang="cs">Mluvte se svými přáteli na Matrixu</summary>
|
||||
<summary xml:lang="de">Mit den Freunden auf Matrix unterhalten</summary>
|
||||
<summary xml:lang="en-GB">Chat with your friends on matrix</summary>
|
||||
<summary xml:lang="eo">Babilu kun viaj amikoj sur matrix</summary>
|
||||
<summary xml:lang="es">Charle con sus amigos en matrix</summary>
|
||||
@@ -74,7 +72,6 @@
|
||||
<summary xml:lang="nl">Met uw vrienden chatten op matrix</summary>
|
||||
<summary xml:lang="nn">Prat med vennar på Matrix</summary>
|
||||
<summary xml:lang="pl">Rozmawiaj ze swoimi znajomymi w Matriksie</summary>
|
||||
<summary xml:lang="ru">Общение с друзьями в Matrix</summary>
|
||||
<summary xml:lang="sl">Klepet z vašimi prijatelji na matrixu</summary>
|
||||
<summary xml:lang="sv">Chatta med dina vänner på Matrix</summary>
|
||||
<summary xml:lang="ta">மேட்ரிக்ஸு மூலம் உங்கள் நண்பர்களிடம் பேசலாம்</summary>
|
||||
@@ -88,14 +85,12 @@
|
||||
<p xml:lang="ar">نيوتشات هو تطبيق دردشة يتيح لك الاستفادة الكاملة من شبكة Matrix. فهو يوفر لك طريقة آمنة لإرسال الرسائل النصية ومقاطع الفيديو والملفات الصوتية إلى عائلتك وزملائك وأصدقائك.</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="de">NeoChat ist eine Anwendung für Unterhaltungen mit allen Vorteilen des Matrix-Netzwerkes. Sie bietet eine sichere Möglichkeit zum Versenden von Nachrichten, Videos und Audiodateien and die Familienmitglieder.</p>
|
||||
<p xml:lang="en-GB">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="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="fi">NeoChat on keskustelusovellus, jolla Matrix-verkosta saa täyden hyödyn. Se tarjoaa salatun kanavan lähettää perheelle, työkavereille ja ystäville tekstiviestejä sekä video- ja äänitiedostoja.</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="gl">NeoChat é unha aplicación de conversa que lle permite usar todas as funcionalidades da rede Matrix. Fornece unha forma segura de enviar mensaxes de texto e ficheiros de vídeo e son a familiares, amizades ou no traballo.</p>
|
||||
<p xml:lang="he">NeoChat הוא יישום התכתבות שמאפשר לך לנצל את רשת Matrix במלואה. הוא מספק דרך מאובטחת לשליחת הודעות כתובות, סרטונים וקובצי שמע למשפחה, לעמיתים לעבודה ולחברים.</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>
|
||||
@@ -105,7 +100,6 @@
|
||||
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
|
||||
<p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p>
|
||||
<p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p>
|
||||
<p xml:lang="ru">NeoChat — приложение для общения, предоставляющее все преимущества сети Matrix. С его помощью можно безопасно отправлять текстовые сообщения, видеозаписи и звуковые файлы родственникам, коллегам и друзьям.</p>
|
||||
<p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p>
|
||||
<p xml:lang="sv">NeoChat är ett chattprogram som låter dig dra full nytta av Matrix-nätverket. Det ger dig ett säkert sätt att skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner.</p>
|
||||
<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>
|
||||
@@ -116,7 +110,6 @@
|
||||
<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>
|
||||
<p xml:lang="ca-valencia">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'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="de">NeoChat versucht eine vollumfängliche Anwendung für die Spezifikation von Matrix zu sein. Damit wird alles der aktuellen stabilen Spezifikation mit den erwähnenswerten Ausnahmen von VoIP, Diskussionsfäden und ein paar Teilen der Ende-zu-Ende-Verschlüsselung unterstützt. Zudem sind andere kleinere Auslassungen vorhanden, da sich die Matrixspezifikation ständig weiterentwickelt. Nichtsdestotrotz soll letztendlich die gesamte Spezifikation unterstützt werden.</p>
|
||||
<p xml:lang="en-GB">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="eo">NeoChat celas esti plene kapabla aplikaĵo por la Matrix-specifo. Kiel tia, ĉio en la nuna stabila specifo kun la rimarkindaj esceptoj de VoIP, fadenoj kaj kelkaj aspektoj de Fin-al-Fina Ĉifrado estas subtenataj. Estas kelkaj aliaj pli malgrandaj preterlasoj pro la fakto, ke la Matrix-speco konstante evoluas, sed la celo restas provizi finfine subtenon por la tuta specifaĵo.</p>
|
||||
<p xml:lang="es">NeoChat pretende ser una aplicación con todas las funciones para la especificación de Matrix. Como tal, admite todo en la especificación estable actual, con las notables excepciones de VoIP, subprocesos y algunas funciones de cifrado de extremo a extremo. Existen algunas omisiones menos importantes debido al hecho de que la especificación de Matrix está en constante evolución, pero el objetivo sigue siendo brindar compatibilidad final con toda la especificación.</p>
|
||||
@@ -146,7 +139,6 @@
|
||||
<p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يدعم نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p>
|
||||
<p xml:lang="ca">A causa de la naturalesa del desenvolupament de l'especificació de Matrix, el NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
|
||||
<p xml:lang="ca-valencia">A causa de la naturalea del desenvolupament de l'especificació de Matrix, NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
|
||||
<p xml:lang="de">Durch die Weiterentwicklung der Matrix-Spezifikation unterstützt auch NeoChat einige als noch instabil gekennzeichnete Funktionen. Derzeit sind das:</p>
|
||||
<p xml:lang="en-GB">Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:</p>
|
||||
<p xml:lang="eo">Pro la naturo de la Matrix-specifevoluo NeoChat ankaŭ subtenas multajn malstabilajn funkciojn. Nuntempe ĉi tiuj estas:</p>
|
||||
<p xml:lang="es">Debido a la naturaleza del desarrollo de la especificación de Matrix, NeoChat también permite numerosas funciones no estables, como:</p>
|
||||
@@ -196,7 +188,6 @@
|
||||
<li xml:lang="nn">Avstemmingar – MSC3381</li>
|
||||
<li xml:lang="pl">Ankiety - MSC3381</li>
|
||||
<li xml:lang="pt">Inquéritos - MSC3381</li>
|
||||
<li xml:lang="ru">Голосования — MSC3381</li>
|
||||
<li xml:lang="sl">Polls - MSC3381</li>
|
||||
<li xml:lang="sv">Polls - MSC3381</li>
|
||||
<li xml:lang="ta">வாக்கெடுப்புகள் - MSC3381</li>
|
||||
@@ -302,7 +293,6 @@
|
||||
<caption xml:lang="ar">العرض الرئيسة مع قائمة الغرف والدردشات و معلومات الغرفة</caption>
|
||||
<caption xml:lang="ca">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||
<caption xml:lang="ca-valencia">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||
<caption xml:lang="de">Hauptansicht mit Raumliste, Unterhaltung und Raum-Informationen</caption>
|
||||
<caption xml:lang="en-GB">Main view with room list, chat, and room information</caption>
|
||||
<caption xml:lang="eo">Ĉefa vido kun ĉambra listo, babilejo kaj ĉambra informo</caption>
|
||||
<caption xml:lang="es">Vista principal con la lista de salas, chat e información de la sala</caption>
|
||||
@@ -336,14 +326,12 @@
|
||||
<caption xml:lang="ar">اكتشف مجتمعات جديدة مع فضاءات ماتركس</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="de">Neue Gemeinschaften mit den Umgebungen von Matrix erkunden</caption>
|
||||
<caption xml:lang="en-GB">Discover new communities with Matrix Spaces</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="fi">Löydä uusia yhteisöjä Matrix Spacesillä</caption>
|
||||
<caption xml:lang="fr">Découvrez de nouvelles communautés avec les espaces sous Matrix</caption>
|
||||
<caption xml:lang="gl">Descubra novas comunidades dos espazos de Matrix.</caption>
|
||||
<caption xml:lang="he">אפשר להיחשף לקהילות חדשות דרך Matrix Spaces</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>
|
||||
@@ -353,10 +341,8 @@
|
||||
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
|
||||
<caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption>
|
||||
<caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption>
|
||||
<caption xml:lang="ru">Поиск новых сообществ с помощью Matrix Spaces</caption>
|
||||
<caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption>
|
||||
<caption xml:lang="sv">Upptäck nya gemenskaper med Matrix Spaces</caption>
|
||||
<caption xml:lang="ta">மேட்ரிக்ஸு இடங்களின் மூலம் புதிய சமூகங்களைக் கண்டுபிடிக்கலாம்</caption>
|
||||
<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>
|
||||
@@ -375,7 +361,6 @@
|
||||
<caption xml:lang="ar">العرض الرئيسة مع قائمة الغرف والدردشات و معلومات الغرفة</caption>
|
||||
<caption xml:lang="ca">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||
<caption xml:lang="ca-valencia">Vista principal amb la llista de sales, xats i informació de les sales</caption>
|
||||
<caption xml:lang="de">Hauptansicht mit Raumliste, Unterhaltung und Raum-Informationen</caption>
|
||||
<caption xml:lang="en-GB">Main view with room list, chat, and room information</caption>
|
||||
<caption xml:lang="eo">Ĉefa vido kun ĉambra listo, babilejo kaj ĉambra informo</caption>
|
||||
<caption xml:lang="es">Vista principal con la lista de salas, chat e información de la sala</caption>
|
||||
@@ -410,7 +395,6 @@
|
||||
<caption xml:lang="ca">Pantalla d'inici de sessió</caption>
|
||||
<caption xml:lang="ca-valencia">Pantalla d'inici de sessió</caption>
|
||||
<caption xml:lang="cs">Přihlašovací obrazovka</caption>
|
||||
<caption xml:lang="de">Anmeldebildschirm</caption>
|
||||
<caption xml:lang="en-GB">Login screen</caption>
|
||||
<caption xml:lang="eo">Ensaluta ekrano</caption>
|
||||
<caption xml:lang="es">Pantalla de inicio de sesión</caption>
|
||||
@@ -443,10 +427,6 @@
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release version="24.08.3" date="2024-11-07"/>
|
||||
<release version="24.08.2" date="2024-10-10"/>
|
||||
<release version="24.08.1" date="2024-09-12"/>
|
||||
<release version="24.08.0" date="2024-08-22"/>
|
||||
<release version="24.05.2" date="2024-07-04"/>
|
||||
<release version="24.05.1" date="2024-06-13"/>
|
||||
<release version="24.05.0" date="2024-05-23"/>
|
||||
|
||||
@@ -15,7 +15,6 @@ Name[en_GB]=NeoChat
|
||||
Name[eo]=NeoChat
|
||||
Name[es]=NeoChat
|
||||
Name[eu]=NeoChat
|
||||
Name[fa]=نئوچت
|
||||
Name[fi]=NeoChat
|
||||
Name[fr]=NeoChat
|
||||
Name[gl]=NeoChat
|
||||
|
||||
556
po/ar/neochat.po
556
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
552
po/az/neochat.po
552
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
712
po/ca/neochat.po
712
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
592
po/cs/neochat.po
592
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
545
po/da/neochat.po
545
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
1833
po/de/neochat.po
1833
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
554
po/el/neochat.po
554
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
550
po/eo/neochat.po
550
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
540
po/es/neochat.po
540
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
565
po/eu/neochat.po
565
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
5383
po/fa/neochat.po
5383
po/fa/neochat.po
File diff suppressed because it is too large
Load Diff
556
po/fi/neochat.po
556
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
595
po/fr/neochat.po
595
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
533
po/gl/neochat.po
533
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
570
po/hu/neochat.po
570
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
548
po/ia/neochat.po
548
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
555
po/id/neochat.po
555
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
544
po/ie/neochat.po
544
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
544
po/it/neochat.po
544
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
522
po/ja/neochat.po
522
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
542
po/ka/neochat.po
542
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
557
po/ko/neochat.po
557
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
522
po/lt/neochat.po
522
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
560
po/lv/neochat.po
560
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
558
po/nl/neochat.po
558
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
551
po/nn/neochat.po
551
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
552
po/pa/neochat.po
552
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
540
po/pl/neochat.po
540
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
555
po/pt/neochat.po
555
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1564
po/ru/neochat.po
1564
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
552
po/sk/neochat.po
552
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
544
po/sl/neochat.po
544
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
536
po/sv/neochat.po
536
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
602
po/ta/neochat.po
602
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
556
po/tr/neochat.po
556
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
544
po/uk/neochat.po
544
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
158
snapcraft.yaml
158
snapcraft.yaml
@@ -1,158 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2024 Scarlett Moore <sgmoore@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
---
|
||||
name: neochat
|
||||
base: core22
|
||||
adopt-info: neochat
|
||||
grade: stable
|
||||
confinement: strict
|
||||
apps:
|
||||
neochat:
|
||||
extensions:
|
||||
- kde-neon-6
|
||||
command: usr/bin/neochat
|
||||
common-id: org.kde.neochat
|
||||
desktop: usr/share/applications/org.kde.neochat.desktop
|
||||
plugs:
|
||||
- home
|
||||
- removable-media
|
||||
- audio-playback
|
||||
- unity7
|
||||
- network
|
||||
- network-bind
|
||||
- network-manager-observe
|
||||
- password-manager-service
|
||||
- accounts-service
|
||||
|
||||
compression: lzo
|
||||
|
||||
slots:
|
||||
session-dbus-interface:
|
||||
interface: dbus
|
||||
name: org.kde.neochat
|
||||
bus: session
|
||||
|
||||
parts:
|
||||
olm:
|
||||
source: https://gitlab.matrix.org/matrix-org/olm.git
|
||||
source-depth: 1
|
||||
source-tag: '3.2.12'
|
||||
plugin: cmake
|
||||
cmake-parameters:
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
prime:
|
||||
- -usr/include
|
||||
- -usr/lib/*/pkgconfig
|
||||
- -usr/lib/*/cmake
|
||||
|
||||
libsecret:
|
||||
source: https://gitlab.gnome.org/GNOME/libsecret.git
|
||||
source-tag: '0.21.4'
|
||||
source-depth: 1
|
||||
plugin: meson
|
||||
meson-parameters:
|
||||
- --prefix=/usr
|
||||
- -Doptimization=3
|
||||
- -Ddebug=true
|
||||
- -Dmanpage=false
|
||||
- -Dvapi=false
|
||||
- -Dintrospection=false
|
||||
- -Dcrypto=disabled
|
||||
- -Dgtk_doc=false
|
||||
build-packages:
|
||||
- meson
|
||||
- libglib2.0-dev
|
||||
- libgcrypt20-dev
|
||||
prime:
|
||||
- -usr/include
|
||||
- -usr/lib/*/pkgconfig
|
||||
|
||||
qtkeychain:
|
||||
after: [libsecret]
|
||||
source: https://github.com/frankosterfeld/qtkeychain.git
|
||||
source-tag: 0.14.3
|
||||
source-depth: 1
|
||||
plugin: cmake
|
||||
build-environment:
|
||||
- PKG_CONFIG_PATH: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET/pkgconfig:$PKG_CONFIG_PATH
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DBUILD_TRANSLATIONS=NO
|
||||
- -DBUILD_WITH_QT6=ON
|
||||
prime:
|
||||
- -usr/include
|
||||
- -usr/lib/*/pkgconfig
|
||||
- -usr/lib/*/cmake
|
||||
|
||||
libquotient:
|
||||
after:
|
||||
- olm
|
||||
- qtkeychain
|
||||
source: https://github.com/quotient-im/libQuotient.git
|
||||
source-tag: 0.8.2
|
||||
source-depth: 1
|
||||
plugin: cmake
|
||||
build-packages:
|
||||
- libssl-dev
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DBUILD_TESTING=OFF
|
||||
- -DQuotient_ENABLE_E2EE=ON
|
||||
- -DBUILD_WITH_QT6=ON
|
||||
prime:
|
||||
- -usr/include
|
||||
- -usr/lib/*/pkgconfig
|
||||
- -usr/lib/*/cmake
|
||||
|
||||
kquickimageeditor:
|
||||
source: https://invent.kde.org/libraries/kquickimageeditor.git
|
||||
source-tag: 'v0.3.0'
|
||||
source-depth: 1
|
||||
plugin: cmake
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DBUILD_WITH_QT6=ON
|
||||
- -DBUILD_TESTING=OFF
|
||||
prime:
|
||||
- -usr/include
|
||||
- -usr/lib/*/pkgconfig
|
||||
- -usr/lib/*/cmake
|
||||
|
||||
neochat:
|
||||
after:
|
||||
- qtkeychain
|
||||
- libquotient
|
||||
- kquickimageeditor
|
||||
parse-info:
|
||||
- usr/share/metainfo/org.kde.neochat.appdata.xml
|
||||
source: .
|
||||
source-type: local
|
||||
plugin: cmake
|
||||
build-packages:
|
||||
- cmark
|
||||
- libcmark-dev
|
||||
- libsqlite3-dev
|
||||
- libvulkan-dev
|
||||
- libxkbcommon-dev
|
||||
- libicu-dev
|
||||
- libpulse0
|
||||
cmake-parameters:
|
||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||
- -DCMAKE_BUILD_TYPE=Release
|
||||
- -DBUILD_TESTING=OFF
|
||||
prime:
|
||||
- -usr/share/man
|
||||
|
||||
deps:
|
||||
after: [neochat]
|
||||
plugin: nil
|
||||
stage-packages:
|
||||
- libcmark0.30.2
|
||||
prime:
|
||||
- usr/lib/*/libcmark.so*
|
||||
|
||||
@@ -288,6 +288,7 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||
qml/ConsentDialog.qml
|
||||
qml/AskDirectChatConfirmation.qml
|
||||
qml/HoverLinkIndicator.qml
|
||||
qml/AvatarNotification.qml
|
||||
DEPENDENCIES
|
||||
QtCore
|
||||
QtQuick
|
||||
@@ -425,7 +426,7 @@ if (TARGET KF6::Crash)
|
||||
target_link_libraries(neochat PUBLIC KF6::Crash)
|
||||
endif()
|
||||
|
||||
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||
kconfig_target_kcfg_file(neochat FILE neochatconfig.kcfg CLASS_NAME NeoChatConfig MUTATORS GENERATE_PROPERTIES DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR SINGLETON GENERATE_MOC QML_REGISTRATION)
|
||||
|
||||
if(NEOCHAT_FLATPAK)
|
||||
target_compile_definitions(neochat PUBLIC NEOCHAT_FLATPAK)
|
||||
|
||||
@@ -91,18 +91,10 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, Cha
|
||||
|
||||
for (auto it = m_room->messageEvents().crbegin(); it != m_room->messageEvents().crend(); it++) {
|
||||
if (const auto event = eventCast<const RoomMessageEvent>(&**it)) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (event->senderId() == m_room->localMember().id() && event->has<EventContent::TextContent>()) {
|
||||
#else
|
||||
if (event->senderId() == m_room->localMember().id() && event->hasTextContent()) {
|
||||
#endif
|
||||
QString originalString;
|
||||
if (event->content()) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
originalString = event->get<EventContent::TextContent>()->body;
|
||||
#else
|
||||
originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content())->body;
|
||||
#endif
|
||||
} else {
|
||||
originalString = event->plainBody();
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ QQC2.Control {
|
||||
}
|
||||
|
||||
onTextChanged: {
|
||||
if (!repeatTimer.running && Config.typingNotifications) {
|
||||
if (!repeatTimer.running && NeoChatConfig.typingNotifications) {
|
||||
var textExists = text.length > 0;
|
||||
root.currentRoom.sendTypingNotification(textExists);
|
||||
textExists ? repeatTimer.start() : repeatTimer.stop();
|
||||
@@ -353,8 +353,8 @@ QQC2.Control {
|
||||
startBreakpoint: Kirigami.Units.gridUnit * 46
|
||||
endBreakpoint: Kirigami.Units.gridUnit * 66
|
||||
startPercentWidth: 100
|
||||
endPercentWidth: Config.compactLayout ? 100 : 85
|
||||
maxWidth: Config.compactLayout ? -1 : Kirigami.Units.gridUnit * 60
|
||||
endPercentWidth: NeoChatConfig.compactLayout ? 100 : 85
|
||||
maxWidth: NeoChatConfig.compactLayout ? -1 : Kirigami.Units.gridUnit * 60
|
||||
|
||||
parentWidth: root.width
|
||||
}
|
||||
|
||||
@@ -120,11 +120,7 @@ QString ChatBarCache::relationMessage() const
|
||||
|
||||
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
|
||||
EventHandler eventhandler(room, &**event);
|
||||
if (isEditing()) {
|
||||
return eventhandler.getMarkdownBody();
|
||||
} else {
|
||||
return eventhandler.getMarkdownBody().toHtmlEscaped();
|
||||
}
|
||||
return eventhandler.getMarkdownBody();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -9,12 +9,18 @@
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QNetworkReply>
|
||||
#include <QRandomGenerator>
|
||||
#include <QTcpServer>
|
||||
#include <QTimer>
|
||||
|
||||
#include <QCoroNetworkReply>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <Quotient/accountregistry.h>
|
||||
#include <Quotient/csapi/notifications.h>
|
||||
#include <Quotient/csapi/wellknown.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
#include <Quotient/settings.h>
|
||||
|
||||
@@ -45,6 +51,7 @@
|
||||
bool testMode = false;
|
||||
|
||||
using namespace Quotient;
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
Controller::Controller(QObject *parent)
|
||||
: QObject(parent)
|
||||
@@ -64,11 +71,7 @@ Controller::Controller(QObject *parent)
|
||||
});
|
||||
} else {
|
||||
auto c = new NeoChatConnection(this);
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
c->assumeIdentity(QStringLiteral("@user:localhost:1234"), QStringLiteral("device_1234"), QStringLiteral("token_1234"));
|
||||
#else
|
||||
c->assumeIdentity(QStringLiteral("@user:localhost:1234"), QStringLiteral("token_1234"));
|
||||
#endif
|
||||
connect(c, &Connection::connected, this, [c, this]() {
|
||||
m_accountRegistry.add(c);
|
||||
c->syncLoop();
|
||||
@@ -206,33 +209,20 @@ void Controller::invokeLogin()
|
||||
m_connectionsLoading[accountId] = connection;
|
||||
connect(connection, &NeoChatConnection::connected, this, [this, connection, accountId] {
|
||||
connection->loadState();
|
||||
if (connection->allRooms().size() == 0 || connection->allRooms()[0]->currentState().get<RoomCreateEvent>()) {
|
||||
addConnection(connection);
|
||||
m_accountsLoading.removeAll(connection->userId());
|
||||
m_connectionsLoading.remove(accountId);
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
} else {
|
||||
connect(
|
||||
connection->allRooms()[0],
|
||||
&Room::baseStateLoaded,
|
||||
this,
|
||||
[this, connection, accountId]() {
|
||||
addConnection(connection);
|
||||
m_accountsLoading.removeAll(connection->userId());
|
||||
m_connectionsLoading.remove(accountId);
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
}
|
||||
addConnection(connection);
|
||||
m_accountsLoading.removeAll(connection->userId());
|
||||
m_connectionsLoading.remove(accountId);
|
||||
Q_EMIT accountsLoadingChanged();
|
||||
});
|
||||
connect(connection, &NeoChatConnection::networkError, this, [this](const QString &error, const QString &, int, int) {
|
||||
Q_EMIT errorOccured(i18n("Network Error: %1", error), {});
|
||||
});
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
connection->assumeIdentity(account.userId(), account.deviceId(), accessToken);
|
||||
#else
|
||||
connection->assumeIdentity(account.userId(), accessToken);
|
||||
#endif
|
||||
|
||||
if (!account.clientId().isEmpty()) {
|
||||
connection->assumeOidcIdentity(account.userId(), accessToken, account.clientId(), account.tokenEndpoint());
|
||||
} else {
|
||||
connection->assumeIdentity(account.userId(), accessToken);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -273,19 +263,23 @@ QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const QStrin
|
||||
return job;
|
||||
}
|
||||
|
||||
void Controller::saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken)
|
||||
bool Controller::saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken)
|
||||
{
|
||||
qDebug() << "Save the access token to the keychain for " << userId;
|
||||
auto job = new QKeychain::WritePasswordJob(qAppName());
|
||||
job->setAutoDelete(true);
|
||||
job->setKey(userId);
|
||||
job->setBinaryData(accessToken);
|
||||
connect(job, &QKeychain::WritePasswordJob::finished, this, [job]() {
|
||||
if (job->error()) {
|
||||
qWarning() << "Could not save access token to the keychain: " << qPrintable(job->errorString());
|
||||
}
|
||||
});
|
||||
job->start();
|
||||
QKeychain::WritePasswordJob job(qAppName());
|
||||
job.setAutoDelete(false);
|
||||
job.setKey(userId);
|
||||
job.setBinaryData(accessToken);
|
||||
QEventLoop loop;
|
||||
QKeychain::WritePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
|
||||
job.start();
|
||||
loop.exec();
|
||||
|
||||
if (job.error()) {
|
||||
qWarning() << "Could not save access token to the keychain: " << qPrintable(job.errorString());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Controller::supportSystemTray() const
|
||||
@@ -427,11 +421,29 @@ void Controller::removeConnection(const QString &userId)
|
||||
|
||||
bool Controller::csSupported() const
|
||||
{
|
||||
#if Quotient_VERSION_MINOR > 9
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
NeoChatConnection *Controller::pendingOidcConnection() const
|
||||
{
|
||||
return m_pendingOidcConnection.get();
|
||||
}
|
||||
|
||||
void Controller::startOidcLogin(const QString &homeserver)
|
||||
{
|
||||
auto url = QUrl::fromUserInput(homeserver.startsWith(u"https:"_s) ? homeserver : u"https://%1"_s.arg(homeserver));
|
||||
m_pendingOidcConnection = new NeoChatConnection(url, this);
|
||||
Q_EMIT pendingOidcConnectionChanged();
|
||||
m_pendingOidcConnection->startOidcLogin();
|
||||
connect(m_pendingOidcConnection, &Connection::connected, this, [this]() {
|
||||
m_pendingOidcConnection->loadState();
|
||||
addConnection(m_pendingOidcConnection);
|
||||
setActiveConnection(m_pendingOidcConnection);
|
||||
});
|
||||
}
|
||||
|
||||
#include "moc_controller.cpp"
|
||||
|
||||
@@ -52,6 +52,8 @@ class Controller : public QObject
|
||||
|
||||
Q_PROPERTY(bool csSupported READ csSupported CONSTANT)
|
||||
|
||||
Q_PROPERTY(NeoChatConnection *pendingOidcConnection READ pendingOidcConnection NOTIFY pendingOidcConnectionChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Define the types on inline messages that can be shown.
|
||||
@@ -86,7 +88,7 @@ public:
|
||||
/**
|
||||
* @brief Save an access token to the keychain for the given account.
|
||||
*/
|
||||
void saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken);
|
||||
bool saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken);
|
||||
|
||||
[[nodiscard]] bool supportSystemTray() const;
|
||||
|
||||
@@ -106,8 +108,12 @@ public:
|
||||
|
||||
Q_INVOKABLE void removeConnection(const QString &userId);
|
||||
|
||||
Q_INVOKABLE void startOidcLogin(const QString &homeserver);
|
||||
|
||||
bool csSupported() const;
|
||||
|
||||
NeoChatConnection *pendingOidcConnection() const;
|
||||
|
||||
private:
|
||||
explicit Controller(QObject *parent = nullptr);
|
||||
|
||||
@@ -119,10 +125,13 @@ private:
|
||||
void loadSettings();
|
||||
void saveSettings() const;
|
||||
|
||||
QCoro::Task<void> doStartOidcLogin(QString homeserver);
|
||||
|
||||
Quotient::AccountRegistry m_accountRegistry;
|
||||
QStringList m_accountsLoading;
|
||||
QMap<QString, QPointer<NeoChatConnection>> m_connectionsLoading;
|
||||
QString m_endpoint;
|
||||
QPointer<NeoChatConnection> m_pendingOidcConnection;
|
||||
|
||||
private Q_SLOTS:
|
||||
void invokeLogin();
|
||||
@@ -136,4 +145,5 @@ Q_SIGNALS:
|
||||
void activeConnectionChanged(NeoChatConnection *connection);
|
||||
void accountsLoadingChanged();
|
||||
void showMessage(MessageType messageType, const QString &message);
|
||||
void pendingOidcConnectionChanged();
|
||||
};
|
||||
|
||||
@@ -17,25 +17,25 @@ FormCard.FormCardPage {
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check", "Show hidden events in the timeline")
|
||||
checked: Config.showAllEvents
|
||||
checked: NeoChatConfig.showAllEvents
|
||||
|
||||
onToggled: Config.showAllEvents = checked
|
||||
onToggled: NeoChatConfig.showAllEvents = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
id: roomAccountDataVisibleCheck
|
||||
text: i18nc("@option:check Enable the matrix 'threads' feature", "Always allow device verification")
|
||||
description: i18n("Allow the user to start a verification session with devices that were already verified")
|
||||
checked: Config.alwaysVerifyDevice
|
||||
checked: NeoChatConfig.alwaysVerifyDevice
|
||||
|
||||
onToggled: Config.alwaysVerifyDevice = checked
|
||||
onToggled: NeoChatConfig.alwaysVerifyDevice = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check", "Show focus in window header")
|
||||
checked: Config.windowTitleFocus
|
||||
checked: NeoChatConfig.windowTitleFocus
|
||||
|
||||
onToggled: {
|
||||
Config.windowTitleFocus = checked;
|
||||
Config.save();
|
||||
NeoChatConfig.windowTitleFocus = checked;
|
||||
NeoChatConfig.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,23 +18,23 @@ FormCard.FormCardPage {
|
||||
FormCard.FormCheckDelegate {
|
||||
id: roomAccountDataVisibleCheck
|
||||
text: i18nc("@option:check Enable the matrix 'threads' feature", "Threads")
|
||||
checked: Config.threads
|
||||
checked: NeoChatConfig.threads
|
||||
|
||||
onToggled: Config.threads = checked
|
||||
onToggled: NeoChatConfig.threads = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check Enable the matrix 'secret backup' feature", "Secret Backup")
|
||||
checked: Config.secretBackup
|
||||
checked: NeoChatConfig.secretBackup
|
||||
|
||||
onToggled: Config.secretBackup = checked
|
||||
onToggled: NeoChatConfig.secretBackup = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check Enable the matrix feature to add a phone number as a third party ID", "Add phone numbers as 3PIDs")
|
||||
checked: Config.phone3PId
|
||||
checked: NeoChatConfig.phone3PId
|
||||
|
||||
onToggled: {
|
||||
Config.phone3PId = checked
|
||||
Config.save();
|
||||
NeoChatConfig.phone3PId = checked
|
||||
NeoChatConfig.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
|
||||
#include <Quotient/eventitem.h>
|
||||
#include <Quotient/events/encryptionevent.h>
|
||||
#include <Quotient/events/event.h>
|
||||
#include <Quotient/events/eventcontent.h>
|
||||
#include <Quotient/events/reactionevent.h>
|
||||
#include <Quotient/events/redactionevent.h>
|
||||
#include <Quotient/events/roomavatarevent.h>
|
||||
@@ -236,18 +234,10 @@ QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
|
||||
{
|
||||
QString body;
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (event.has<EventContent::FileContent>()) {
|
||||
#else
|
||||
if (event.hasFileContent()) {
|
||||
#endif
|
||||
// if filename is given or body is equal to filename,
|
||||
// then body is a caption
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
QString filename = event.get<EventContent::FileContent>()->originalName;
|
||||
#else
|
||||
QString filename = event.content()->fileInfo()->originalName;
|
||||
#endif
|
||||
QString body = event.plainBody();
|
||||
if (filename.isEmpty() || filename == body) {
|
||||
return QString();
|
||||
@@ -255,13 +245,8 @@ QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
|
||||
return body;
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (event.has<EventContent::TextContent>() && event.content()) {
|
||||
body = event.get<EventContent::TextContent>()->body;
|
||||
#else
|
||||
if (event.hasTextContent() && event.content()) {
|
||||
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
||||
#endif
|
||||
} else {
|
||||
body = event.plainBody();
|
||||
}
|
||||
@@ -309,7 +294,7 @@ QString EventHandler::getBody(const Quotient::RoomEvent *event, Qt::TextFormat f
|
||||
{
|
||||
if (event->isRedacted()) {
|
||||
auto reason = event->redactedBecause()->reason();
|
||||
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>") : i18n("<i>[This message was deleted: %1]</i>", reason.toHtmlEscaped());
|
||||
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>") : i18n("<i>[This message was deleted: %1]</i>", reason);
|
||||
}
|
||||
|
||||
const bool prettyPrint = (format == Qt::RichText);
|
||||
@@ -476,16 +461,11 @@ QString EventHandler::getMessageBody(const RoomMessageEvent &event, Qt::TextForm
|
||||
{
|
||||
TextHandler textHandler;
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (event.has<EventContent::FileContent>()) {
|
||||
QString fileCaption = event.get<EventContent::FileContent>()->originalName;
|
||||
#else
|
||||
if (event.hasFileContent()) {
|
||||
auto fileCaption = event.content()->fileInfo()->originalName;
|
||||
#endif
|
||||
if (fileCaption.isEmpty()) {
|
||||
fileCaption = event.plainBody();
|
||||
} else if (fileCaption != event.plainBody()) {
|
||||
} else if (event.content()->fileInfo()->originalName != event.plainBody()) {
|
||||
fileCaption = event.plainBody() + " | "_ls + fileCaption;
|
||||
}
|
||||
textHandler.setData(fileCaption);
|
||||
@@ -493,13 +473,8 @@ QString EventHandler::getMessageBody(const RoomMessageEvent &event, Qt::TextForm
|
||||
}
|
||||
|
||||
QString body;
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (event.has<EventContent::TextContent>() && event.content()) {
|
||||
body = event.get<EventContent::TextContent>()->body;
|
||||
#else
|
||||
if (event.hasTextContent() && event.content()) {
|
||||
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
||||
#endif
|
||||
} else {
|
||||
body = event.plainBody();
|
||||
}
|
||||
@@ -680,65 +655,39 @@ QVariantMap EventHandler::getMediaInfoForEvent(const Quotient::RoomEvent *event)
|
||||
// Get the file info for the event.
|
||||
if (event->is<RoomMessageEvent>()) {
|
||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event);
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (!roomMessageEvent->has<EventContent::FileContentBase>()) {
|
||||
#else
|
||||
if (!roomMessageEvent->hasFileContent()) {
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
const auto content = roomMessageEvent->get<EventContent::FileContentBase>();
|
||||
QVariantMap mediaInfo = getMediaInfoFromFileInfo(content.get(), eventId, false, false);
|
||||
#else
|
||||
const auto content = static_cast<const EventContent::FileContent *>(roomMessageEvent->content());
|
||||
QVariantMap mediaInfo = getMediaInfoFromFileInfo(content, eventId, false, false);
|
||||
#endif
|
||||
const EventContent::FileInfo *fileInfo;
|
||||
fileInfo = roomMessageEvent->content()->fileInfo();
|
||||
QVariantMap mediaInfo = getMediaInfoFromFileInfo(fileInfo, eventId, false, false);
|
||||
// if filename isn't specifically given, it is in body
|
||||
// https://spec.matrix.org/latest/client-server-api/#mfile
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
mediaInfo["filename"_ls] = content->commonInfo().originalName.isEmpty() ? roomMessageEvent->plainBody() : content->commonInfo().originalName;
|
||||
#else
|
||||
mediaInfo["filename"_ls] = (content->fileInfo()->originalName.isEmpty()) ? roomMessageEvent->plainBody() : content->fileInfo()->originalName;
|
||||
#endif
|
||||
mediaInfo["filename"_ls] = (fileInfo->originalName.isEmpty()) ? roomMessageEvent->plainBody() : fileInfo->originalName;
|
||||
|
||||
return mediaInfo;
|
||||
} else if (event->is<StickerEvent>()) {
|
||||
auto stickerEvent = eventCast<const StickerEvent>(event);
|
||||
auto content = &stickerEvent->image();
|
||||
const EventContent::FileInfo *fileInfo;
|
||||
|
||||
return getMediaInfoFromFileInfo(content, eventId, false, true);
|
||||
auto stickerEvent = eventCast<const StickerEvent>(event);
|
||||
fileInfo = &stickerEvent->image();
|
||||
|
||||
return getMediaInfoFromFileInfo(fileInfo, eventId, false, true);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QVariantMap EventHandler::getMediaInfoFromFileInfo(
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
const Quotient::EventContent::FileContentBase *fileContent,
|
||||
#else
|
||||
const Quotient::EventContent::TypedBase *fileContent,
|
||||
#endif
|
||||
const QString &eventId,
|
||||
bool isThumbnail,
|
||||
bool isSticker) const
|
||||
QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail, bool isSticker) const
|
||||
{
|
||||
QVariantMap mediaInfo;
|
||||
|
||||
// Get the mxc URL for the media.
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (!fileContent->url().isValid() || fileContent->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
||||
#else
|
||||
if (!fileContent->fileInfo()->url().isValid() || fileContent->fileInfo()->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
||||
#endif
|
||||
if (!fileInfo->url().isValid() || fileInfo->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
||||
mediaInfo["source"_ls] = QUrl();
|
||||
} else {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
QUrl source = m_room->makeMediaUrl(eventId, fileContent->url());
|
||||
#else
|
||||
QUrl source = m_room->makeMediaUrl(eventId, fileContent->fileInfo()->url());
|
||||
#endif
|
||||
QUrl source = m_room->makeMediaUrl(eventId, fileInfo->url());
|
||||
|
||||
if (source.isValid()) {
|
||||
mediaInfo["source"_ls] = source;
|
||||
@@ -747,7 +696,7 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(
|
||||
}
|
||||
}
|
||||
|
||||
auto mimeType = fileContent->type();
|
||||
auto mimeType = fileInfo->mimeType;
|
||||
// Add the MIME type for the media if available.
|
||||
mediaInfo["mimeType"_ls] = mimeType.name();
|
||||
|
||||
@@ -755,53 +704,45 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(
|
||||
mediaInfo["mimeIcon"_ls] = mimeType.iconName();
|
||||
|
||||
// Add media size if available.
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
mediaInfo["size"_ls] = fileContent->commonInfo().payloadSize;
|
||||
#else
|
||||
mediaInfo["size"_ls] = static_cast<const EventContent::FileContent *>(fileContent)->fileInfo()->payloadSize;
|
||||
#endif
|
||||
mediaInfo["size"_ls] = fileInfo->payloadSize;
|
||||
|
||||
mediaInfo["isSticker"_ls] = isSticker;
|
||||
|
||||
// Add parameter depending on media type.
|
||||
if (mimeType.name().contains(QStringLiteral("image"))) {
|
||||
if (auto castInfo = static_cast<const EventContent::ImageContent *>(fileContent)) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (auto castInfo = static_cast<const EventContent::ImageContent *>(fileInfo)) {
|
||||
mediaInfo["width"_ls] = castInfo->imageSize.width();
|
||||
mediaInfo["height"_ls] = castInfo->imageSize.height();
|
||||
#else
|
||||
const auto imageInfo = static_cast<const EventContent::ImageInfo *>(castInfo->fileInfo());
|
||||
mediaInfo["width"_ls] = imageInfo->imageSize.width();
|
||||
mediaInfo["height"_ls] = imageInfo->imageSize.height();
|
||||
#endif
|
||||
|
||||
// TODO: Images in certain formats (e.g. WebP) will be erroneously marked as animated, even if they are static.
|
||||
mediaInfo["animated"_ls] = QMovie::supportedFormats().contains(mimeType.preferredSuffix().toUtf8());
|
||||
|
||||
QVariantMap tempInfo;
|
||||
auto thumbnailInfo = getMediaInfoFromTumbnail(castInfo->thumbnail, eventId);
|
||||
if (thumbnailInfo["source"_ls].toUrl().scheme() == "mxc"_ls) {
|
||||
tempInfo = thumbnailInfo;
|
||||
} else {
|
||||
QString blurhash = castInfo->originalInfoJson["xyz.amorgan.blurhash"_ls].toString();
|
||||
if (blurhash.isEmpty()) {
|
||||
tempInfo["source"_ls] = QUrl();
|
||||
if (!isThumbnail) {
|
||||
QVariantMap tempInfo;
|
||||
auto thumbnailInfo = getMediaInfoFromFileInfo(castInfo->thumbnailInfo(), eventId, true);
|
||||
if (thumbnailInfo["source"_ls].toUrl().scheme() == "mxc"_ls) {
|
||||
tempInfo = thumbnailInfo;
|
||||
} else {
|
||||
tempInfo["source"_ls] = QUrl("image://blurhash/"_ls + blurhash);
|
||||
QString blurhash = castInfo->originalInfoJson["xyz.amorgan.blurhash"_ls].toString();
|
||||
if (blurhash.isEmpty()) {
|
||||
tempInfo["source"_ls] = QUrl();
|
||||
} else {
|
||||
tempInfo["source"_ls] = QUrl("image://blurhash/"_ls + blurhash);
|
||||
}
|
||||
}
|
||||
mediaInfo["tempInfo"_ls] = tempInfo;
|
||||
}
|
||||
mediaInfo["tempInfo"_ls] = tempInfo;
|
||||
}
|
||||
}
|
||||
if (mimeType.name().contains(QStringLiteral("video"))) {
|
||||
if (auto castInfo = static_cast<const EventContent::VideoContent *>(fileContent)) {
|
||||
if (auto castInfo = static_cast<const EventContent::VideoContent *>(fileInfo)) {
|
||||
mediaInfo["width"_ls] = castInfo->imageSize.width();
|
||||
mediaInfo["height"_ls] = castInfo->imageSize.height();
|
||||
mediaInfo["duration"_ls] = castInfo->duration;
|
||||
|
||||
if (!isThumbnail) {
|
||||
QVariantMap tempInfo;
|
||||
auto thumbnailInfo = getMediaInfoFromTumbnail(castInfo->thumbnail, eventId);
|
||||
auto thumbnailInfo = getMediaInfoFromFileInfo(castInfo->thumbnailInfo(), eventId, true);
|
||||
if (thumbnailInfo["source"_ls].toUrl().scheme() == "mxc"_ls) {
|
||||
tempInfo = thumbnailInfo;
|
||||
} else {
|
||||
@@ -817,7 +758,7 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(
|
||||
}
|
||||
}
|
||||
if (mimeType.name().contains(QStringLiteral("audio"))) {
|
||||
if (auto castInfo = static_cast<const EventContent::AudioContent *>(fileContent)) {
|
||||
if (auto castInfo = static_cast<const EventContent::AudioContent *>(fileInfo)) {
|
||||
mediaInfo["duration"_ls] = castInfo->duration;
|
||||
}
|
||||
}
|
||||
@@ -825,38 +766,6 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(
|
||||
return mediaInfo;
|
||||
}
|
||||
|
||||
QVariantMap EventHandler::getMediaInfoFromTumbnail(const Quotient::EventContent::Thumbnail &thumbnail, const QString &eventId) const
|
||||
{
|
||||
QVariantMap thumbnailInfo;
|
||||
|
||||
if (!thumbnail.url().isValid() || thumbnail.url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
|
||||
thumbnailInfo["source"_ls] = QUrl();
|
||||
} else {
|
||||
QUrl source = m_room->makeMediaUrl(eventId, thumbnail.url());
|
||||
|
||||
if (source.isValid()) {
|
||||
thumbnailInfo["source"_ls] = source;
|
||||
} else {
|
||||
thumbnailInfo["source"_ls] = QUrl();
|
||||
}
|
||||
}
|
||||
|
||||
auto mimeType = thumbnail.mimeType;
|
||||
// Add the MIME type for the media if available.
|
||||
thumbnailInfo["mimeType"_ls] = mimeType.name();
|
||||
|
||||
// Add the MIME type icon if available.
|
||||
thumbnailInfo["mimeIcon"_ls] = mimeType.iconName();
|
||||
|
||||
// Add media size if available.
|
||||
thumbnailInfo["size"_ls] = thumbnail.payloadSize;
|
||||
|
||||
thumbnailInfo["width"_ls] = thumbnail.imageSize.width();
|
||||
thumbnailInfo["height"_ls] = thumbnail.imageSize.height();
|
||||
|
||||
return thumbnailInfo;
|
||||
}
|
||||
|
||||
bool EventHandler::hasReply() const
|
||||
{
|
||||
if (m_event == nullptr) {
|
||||
|
||||
@@ -339,14 +339,6 @@ private:
|
||||
QString getMessageBody(const Quotient::RoomMessageEvent &event, Qt::TextFormat format, bool stripNewlines) const;
|
||||
|
||||
QVariantMap getMediaInfoForEvent(const Quotient::RoomEvent *event) const;
|
||||
QVariantMap getMediaInfoFromFileInfo(
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
const Quotient::EventContent::FileContentBase *fileContent,
|
||||
#else
|
||||
const Quotient::EventContent::TypedBase *fileContent,
|
||||
#endif
|
||||
const QString &eventId,
|
||||
bool isThumbnail = false,
|
||||
bool isSticker = false) const;
|
||||
QVariantMap getMediaInfoFromTumbnail(const Quotient::EventContent::Thumbnail &thumbnail, const QString &eventId) const;
|
||||
QVariantMap
|
||||
getMediaInfoFromFileInfo(const Quotient::EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail = false, bool isSticker = false) const;
|
||||
};
|
||||
|
||||
@@ -10,22 +10,13 @@
|
||||
#include <Quotient/keyverificationsession.h>
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
#include <Quotient/keyimport.h>
|
||||
#endif
|
||||
|
||||
#include "controller.h"
|
||||
#include "neochatconfig.h"
|
||||
|
||||
struct ForeignConfig {
|
||||
Q_GADGET
|
||||
QML_FOREIGN(NeoChatConfig)
|
||||
QML_NAMED_ELEMENT(Config)
|
||||
QML_SINGLETON
|
||||
public:
|
||||
static NeoChatConfig *create(QQmlEngine *, QJSEngine *)
|
||||
{
|
||||
QQmlEngine::setObjectOwnership(NeoChatConfig::self(), QQmlEngine::CppOwnership);
|
||||
return NeoChatConfig::self();
|
||||
}
|
||||
};
|
||||
|
||||
struct ForeignAccountRegistry {
|
||||
Q_GADGET
|
||||
QML_FOREIGN(Quotient::AccountRegistry)
|
||||
@@ -51,3 +42,12 @@ struct ForeignSSSSHandler {
|
||||
QML_FOREIGN(Quotient::SSSSHandler)
|
||||
QML_NAMED_ELEMENT(SSSSHandler)
|
||||
};
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
struct ForeignKeyImport {
|
||||
Q_GADGET
|
||||
QML_SINGLETON
|
||||
QML_FOREIGN(Quotient::KeyImport)
|
||||
QML_NAMED_ELEMENT(KeyImport)
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -40,7 +40,7 @@ float LocationHelper::zoomToFit(const QRectF &r, float mapWidth, float mapHeight
|
||||
const auto zy = std::log2((mapHeight / (p2.y() - p1.y())));
|
||||
const auto z = std::min(zx, zy);
|
||||
|
||||
return z;
|
||||
return std::clamp(z, 5.0, 18.0);
|
||||
}
|
||||
|
||||
#include "moc_locationhelper.cpp"
|
||||
|
||||
@@ -78,7 +78,9 @@ void LoginHelper::init()
|
||||
account.setHomeserver(m_connection->homeserver());
|
||||
account.setDeviceId(m_connection->deviceId());
|
||||
account.setDeviceName(m_deviceName);
|
||||
Controller::instance().saveAccessTokenToKeyChain(account.userId(), m_connection->accessToken());
|
||||
if (!Controller::instance().saveAccessTokenToKeyChain(account.userId(), m_connection->accessToken())) {
|
||||
qWarning() << "Couldn't save access token";
|
||||
}
|
||||
account.sync();
|
||||
Controller::instance().addConnection(m_connection);
|
||||
Controller::instance().setActiveConnection(m_connection);
|
||||
|
||||
@@ -20,4 +20,5 @@ ecm_add_qml_module(login GENERATE_PLUGIN_SOURCE
|
||||
Sso.qml
|
||||
Terms.qml
|
||||
Username.qml
|
||||
Oidc.qml
|
||||
)
|
||||
|
||||
@@ -31,4 +31,11 @@ LoginStep {
|
||||
text: i18nc("@action:button", "Register")
|
||||
onClicked: root.processed("Homeserver")
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18nc("@action:button", "Login with OIDC")
|
||||
onClicked: root.processed("Oidc")
|
||||
}
|
||||
}
|
||||
|
||||
74
src/login/Oidc.qml
Normal file
74
src/login/Oidc.qml
Normal file
@@ -0,0 +1,74 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
LoginStep {
|
||||
id: root
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: homeserver
|
||||
|
||||
visible: !infoLabel.visible
|
||||
label: i18n("Homeserver")
|
||||
text: "synapse-oidc.element.dev"
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Controller.pendingOidcConnection
|
||||
function onOpenOidcUrl(url: string): void {
|
||||
infoLabel.url = url;
|
||||
infoLabel.visible = true;
|
||||
}
|
||||
function onConnected(): void {
|
||||
root.processed("Loading");
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormTextDelegate {
|
||||
id: infoLabel
|
||||
|
||||
property string url: ""
|
||||
visible: false
|
||||
text: i18n("To continue, please open the following link in your web browser: %1", "<br /><br /><a href='" + url + "'>" + url + "</a>")
|
||||
onLinkActivated: url => Qt.openUrlExternally(url)
|
||||
}
|
||||
FormCard.FormDelegateSeparator { above: openLink }
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
id: openLink
|
||||
visible: infoLabel.visible
|
||||
text: i18n("Open Authorization Link")
|
||||
icon.name: "document-open"
|
||||
onClicked: Qt.openUrlExternally(infoLabel.url.authorizeUrl)
|
||||
}
|
||||
|
||||
FormCard.FormDelegateSeparator {
|
||||
visible: infoLabel.visible
|
||||
above: openLink
|
||||
below: copyLink
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
id: copyLink
|
||||
|
||||
visible: infoLabel.visible
|
||||
text: i18n("Copy Authorization Link")
|
||||
icon.name: "edit-copy"
|
||||
onClicked: {
|
||||
Clipboard.saveText(infoLabel.url)
|
||||
applicationWindow().showPassiveNotification(i18n("Link copied."));
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: !infoLabel.visible
|
||||
text: i18nc("@action:button", "Log in with OIDC")
|
||||
onClicked: Controller.startOidcLogin(homeserver.text)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -148,7 +148,7 @@ int main(int argc, char *argv[])
|
||||
i18n("Maintainer"),
|
||||
QStringLiteral("carl@carlschwan.eu"),
|
||||
QStringLiteral("https://carlschwan.eu"),
|
||||
QStringLiteral("https://carlschwan.eu/avatar.png"));
|
||||
QUrl(QStringLiteral("https://carlschwan.eu/avatar.png")));
|
||||
about.addAuthor(i18n("Tobias Fella"), i18n("Maintainer"), QStringLiteral("tobias.fella@kde.org"), QStringLiteral("https://tobiasfella.de"));
|
||||
about.addAuthor(i18n("James Graham"), i18n("Maintainer"), QStringLiteral("james.h.graham@protonmail.com"));
|
||||
about.addCredit(i18n("Black Hat"), i18n("Original author of Spectral"), QStringLiteral("bhat@encom.eu.org"));
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include <QImageReader>
|
||||
|
||||
#include <Quotient/events/eventcontent.h>
|
||||
#include <Quotient/events/redactionevent.h>
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
#include <Quotient/events/stickerevent.h>
|
||||
@@ -32,8 +31,8 @@
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
MessageContentModel::MessageContentModel(NeoChatRoom *room, const Quotient::RoomEvent *event, bool isReply, bool isPending)
|
||||
: QAbstractListModel(nullptr)
|
||||
MessageContentModel::MessageContentModel(NeoChatRoom *room, const Quotient::RoomEvent *event, bool isReply, bool isPending, MessageContentModel *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, m_room(room)
|
||||
, m_eventId(event != nullptr ? event->id() : QString())
|
||||
, m_eventSenderId(event != nullptr ? event->senderId() : QString())
|
||||
@@ -44,8 +43,8 @@ MessageContentModel::MessageContentModel(NeoChatRoom *room, const Quotient::Room
|
||||
initializeModel();
|
||||
}
|
||||
|
||||
MessageContentModel::MessageContentModel(NeoChatRoom *room, const QString &eventId, bool isReply, bool isPending)
|
||||
: QAbstractListModel(nullptr)
|
||||
MessageContentModel::MessageContentModel(NeoChatRoom *room, const QString &eventId, bool isReply, bool isPending, MessageContentModel *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, m_room(room)
|
||||
, m_eventId(eventId)
|
||||
, m_isPending(isPending)
|
||||
@@ -59,15 +58,16 @@ void MessageContentModel::initializeModel()
|
||||
Q_ASSERT(m_room != nullptr);
|
||||
// Allow making a model for an event that is being downloaded but will appear later
|
||||
// e.g. a reply, but we need an ID to know when it has arrived.
|
||||
Q_ASSERT(!m_eventId.isEmpty());
|
||||
// Also note that a pending event may not have an event ID yet but as long as we have an event
|
||||
// pointer we can pass out the transaction ID until it is set.
|
||||
Q_ASSERT(!m_eventId.isEmpty() || m_event != nullptr);
|
||||
|
||||
Quotient::connectUntil(m_room.get(), &NeoChatRoom::extraEventLoaded, this, [this](const QString &eventId) {
|
||||
if (m_room != nullptr) {
|
||||
if (eventId == m_eventId) {
|
||||
m_event = loadEvent<RoomEvent>(m_room->getEvent(eventId)->fullJson());
|
||||
Q_EMIT eventUpdated();
|
||||
intiializeEvent(m_room->getEvent(eventId));
|
||||
updateReplyModel();
|
||||
resetContent();
|
||||
resetModel();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -165,7 +165,12 @@ void MessageContentModel::intiializeEvent(const QString &eventId)
|
||||
void MessageContentModel::intiializeEvent(const Quotient::RoomEvent *event)
|
||||
{
|
||||
m_event = loadEvent<RoomEvent>(event->fullJson());
|
||||
auto senderId = event->senderId();
|
||||
// a pending event may not previously have had an event ID so update.
|
||||
if (m_eventId.isEmpty()) {
|
||||
m_eventId = m_event->id();
|
||||
}
|
||||
|
||||
auto senderId = m_event->senderId();
|
||||
// A pending event might not have a sender ID set yet but in that case it must
|
||||
// be the local member.
|
||||
if (senderId.isEmpty()) {
|
||||
@@ -226,6 +231,11 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
||||
if (m_event == nullptr) {
|
||||
return QString();
|
||||
}
|
||||
if (m_event->isRedacted()) {
|
||||
auto reason = m_event->redactedBecause()->reason();
|
||||
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>")
|
||||
: i18n("<i>[This message was deleted: %1]</i>", m_event->redactedBecause()->reason());
|
||||
}
|
||||
if (!component.content.isEmpty()) {
|
||||
return component.content;
|
||||
}
|
||||
@@ -418,9 +428,9 @@ void MessageContentModel::updateReplyModel()
|
||||
|
||||
const auto replyEvent = m_room->findInTimeline(eventHandler.getReplyId());
|
||||
if (replyEvent == m_room->historyEdge()) {
|
||||
m_replyModel = new MessageContentModel(m_room, eventHandler.getReplyId(), true);
|
||||
m_replyModel = new MessageContentModel(m_room, eventHandler.getReplyId(), true, false, this);
|
||||
} else {
|
||||
m_replyModel = new MessageContentModel(m_room, replyEvent->get(), true);
|
||||
m_replyModel = new MessageContentModel(m_room, replyEvent->get(), true, false, this);
|
||||
}
|
||||
|
||||
connect(m_replyModel, &MessageContentModel::eventUpdated, this, [this]() {
|
||||
@@ -446,19 +456,10 @@ QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentT
|
||||
auto fileTransferInfo = m_room->cachedFileTransferInfo(m_event.get());
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
Q_ASSERT(event->content() != nullptr && event->has<EventContent::FileContent>());
|
||||
const QMimeType mimeType = event->get<EventContent::FileContent>()->mimeType;
|
||||
#else
|
||||
Q_ASSERT(event->content() != nullptr && event->hasFileContent());
|
||||
Q_ASSERT(event->content() != nullptr && event->content()->fileInfo() != nullptr);
|
||||
const QMimeType mimeType = event->content()->fileInfo()->mimeType;
|
||||
#endif
|
||||
if (mimeType.name() == QStringLiteral("text/plain") || mimeType.parentMimeTypes().contains(QStringLiteral("text/plain"))) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
QString originalName = event->get<EventContent::FileContent>()->originalName;
|
||||
#else
|
||||
QString originalName = event->content()->fileInfo()->originalName;
|
||||
#endif
|
||||
if (originalName.isEmpty()) {
|
||||
originalName = event->plainBody();
|
||||
}
|
||||
@@ -519,7 +520,7 @@ MessageComponent MessageContentModel::linkPreviewComponent(const QUrl &link)
|
||||
if (linkPreviewer->loaded()) {
|
||||
return MessageComponent{MessageComponentType::LinkPreview, QString(), {{"link"_ls, link}}};
|
||||
} else {
|
||||
connect(linkPreviewer, &LinkPreviewer::loadedChanged, this, [this, link]() {
|
||||
connect(linkPreviewer, &LinkPreviewer::loadedChanged, [this, link]() {
|
||||
const auto linkPreviewer = dynamic_cast<NeoChatConnection *>(m_room->connection())->previewerForLink(link);
|
||||
if (linkPreviewer != nullptr && linkPreviewer->loaded()) {
|
||||
for (auto &component : m_components) {
|
||||
@@ -560,11 +561,6 @@ QList<MessageComponent> MessageContentModel::addLinkPreviews(QList<MessageCompon
|
||||
|
||||
void MessageContentModel::closeLinkPreview(int row)
|
||||
{
|
||||
if (row < 0 || row > m_components.size()) {
|
||||
qWarning() << "closeLinkPreview() called with row" << row << "which does not exist. m_components.size() =" << m_components.size();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_components[row].type == MessageComponentType::LinkPreview || m_components[row].type == MessageComponentType::LinkPreviewLoad) {
|
||||
beginResetModel();
|
||||
m_removedLinkPreviews += m_components[row].attributes["link"_ls].toUrl();
|
||||
@@ -581,12 +577,8 @@ void MessageContentModel::updateItineraryModel()
|
||||
}
|
||||
|
||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (event->has<EventContent::FileContent>()) {
|
||||
#else
|
||||
if (event->hasFileContent()) {
|
||||
#endif
|
||||
auto filePath = m_room->cachedFileTransferInfo(event).localPath;
|
||||
auto filePath = m_room->cachedFileTransferInfo(m_event.get()).localPath;
|
||||
if (filePath.isEmpty() && m_itineraryModel != nullptr) {
|
||||
delete m_itineraryModel;
|
||||
m_itineraryModel = nullptr;
|
||||
|
||||
@@ -75,8 +75,12 @@ public:
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
explicit MessageContentModel(NeoChatRoom *room, const Quotient::RoomEvent *event, bool isReply = false, bool isPending = false);
|
||||
MessageContentModel(NeoChatRoom *room, const QString &eventId, bool isReply = false, bool isPending = false);
|
||||
explicit MessageContentModel(NeoChatRoom *room,
|
||||
const Quotient::RoomEvent *event,
|
||||
bool isReply = false,
|
||||
bool isPending = false,
|
||||
MessageContentModel *parent = nullptr);
|
||||
MessageContentModel(NeoChatRoom *room, const QString &eventId, bool isReply = false, bool isPending = false, MessageContentModel *parent = nullptr);
|
||||
|
||||
bool showAuthor() const;
|
||||
void setShowAuthor(bool showAuthor);
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "neochatconfig.h"
|
||||
|
||||
#include <Quotient/csapi/rooms.h>
|
||||
#include <Quotient/events/eventcontent.h>
|
||||
#include <Quotient/events/redactionevent.h>
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
#include <Quotient/events/stickerevent.h>
|
||||
@@ -89,7 +88,6 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
// HACK: Reset the model to a null room first to make sure QML dismantles
|
||||
// last room's objects before the room is actually changed
|
||||
beginResetModel();
|
||||
m_readMarkerModels.clear();
|
||||
m_currentRoom->disconnect(this);
|
||||
m_currentRoom = nullptr;
|
||||
endResetModel();
|
||||
@@ -97,6 +95,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
// Don't clear the member objects until the model has been fully reset and all
|
||||
// refs cleared.
|
||||
m_memberObjects.clear();
|
||||
m_contentModels.clear();
|
||||
m_reactionModels.clear();
|
||||
m_readMarkerModels.clear();
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
@@ -434,17 +435,17 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
EventHandler eventHandler(m_currentRoom, &evt);
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
if (evt.isRedacted()) {
|
||||
auto reason = evt.redactedBecause()->reason();
|
||||
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>")
|
||||
: i18n("<i>[This message was deleted: %1]</i>", evt.redactedBecause()->reason());
|
||||
}
|
||||
return eventHandler.getRichBody();
|
||||
}
|
||||
|
||||
if (role == ContentModelRole) {
|
||||
if (!evt.isStateEvent() && !evt.id().isEmpty()) {
|
||||
return QVariant::fromValue<MessageContentModel *>(new MessageContentModel(m_currentRoom, &evt));
|
||||
}
|
||||
if (evt.isStateEvent()) {
|
||||
if (evt.matrixType() == QStringLiteral("org.matrix.msc3672.beacon_info")) {
|
||||
return QVariant::fromValue<MessageContentModel *>(new MessageContentModel(m_currentRoom, &evt));
|
||||
}
|
||||
if (m_contentModels.contains(evt.id())) {
|
||||
return QVariant::fromValue<MessageContentModel *>(m_contentModels.at(evt.id()).get());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -499,11 +500,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
|
||||
if (role == ProgressInfoRole) {
|
||||
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (e->has<EventContent::FileContent>()) {
|
||||
#else
|
||||
if (e->hasFileContent()) {
|
||||
#endif
|
||||
return QVariant::fromValue(m_currentRoom->cachedFileTransferInfo(&evt));
|
||||
}
|
||||
}
|
||||
@@ -634,6 +631,12 @@ void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event)
|
||||
m_memberObjects[senderId] = std::unique_ptr<NeochatRoomMember>(new NeochatRoomMember(m_currentRoom, senderId));
|
||||
}
|
||||
|
||||
if (!m_contentModels.contains(eventId)) {
|
||||
if (!event->isStateEvent() || event->matrixType() == QStringLiteral("org.matrix.msc3672.beacon_info")) {
|
||||
m_contentModels[eventId] = std::unique_ptr<MessageContentModel>(new MessageContentModel(m_currentRoom, event));
|
||||
}
|
||||
}
|
||||
|
||||
// ReadMarkerModel handles updates to add and remove markers, we only need to
|
||||
// handle adding and removing whole models here.
|
||||
if (m_readMarkerModels.contains(eventId)) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include "linkpreviewer.h"
|
||||
#include "messagecontentmodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatroommember.h"
|
||||
#include "pollhandler.h"
|
||||
@@ -117,6 +118,7 @@ private:
|
||||
KFormat m_format;
|
||||
|
||||
std::map<QString, std::unique_ptr<NeochatRoomMember>> m_memberObjects;
|
||||
std::map<QString, std::unique_ptr<MessageContentModel>> m_contentModels;
|
||||
QMap<QString, QSharedPointer<ReadMarkerModel>> m_readMarkerModels;
|
||||
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
||||
|
||||
|
||||
@@ -247,7 +247,7 @@ void PermissionsModel::setPowerLevel(const QString &permission, const int &newPo
|
||||
powerLevelContent[QLatin1String("events")] = eventPowerLevels;
|
||||
}
|
||||
|
||||
m_room->setState<Quotient::RoomPowerLevelsEvent>(Quotient::fromJson<Quotient::PowerLevelsEventContent>(powerLevelContent));
|
||||
m_room->setState<Quotient::RoomPowerLevelsEvent>(powerLevelContent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -312,12 +312,8 @@ void PushRuleModel::addKeyword(const QString &keyword, const QString &roomId)
|
||||
pushConditions.append(keywordCondition);
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
auto job = m_connection->callApi<Quotient::SetPushRuleJob>(PushRuleKind::kindString(kind),
|
||||
#else
|
||||
auto job = m_connection->callApi<Quotient::SetPushRuleJob>(QLatin1String("global"),
|
||||
PushRuleKind::kindString(kind),
|
||||
#endif
|
||||
keyword,
|
||||
actions,
|
||||
QString(),
|
||||
@@ -342,11 +338,7 @@ void PushRuleModel::removeKeyword(const QString &keyword)
|
||||
}
|
||||
|
||||
auto kind = PushRuleKind::kindString(m_rules[index].kind);
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(kind, m_rules[index].id);
|
||||
#else
|
||||
auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(QStringLiteral("global"), kind, m_rules[index].id);
|
||||
#endif
|
||||
connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
|
||||
qWarning() << QLatin1String("Unable to remove push rule for keyword %1: ").arg(m_rules[index].id) << job->errorString();
|
||||
});
|
||||
@@ -354,18 +346,10 @@ void PushRuleModel::removeKeyword(const QString &keyword)
|
||||
|
||||
void PushRuleModel::setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled)
|
||||
{
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(kind, ruleId);
|
||||
#else
|
||||
auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(QStringLiteral("global"), kind, ruleId);
|
||||
#endif
|
||||
connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled, this]() {
|
||||
if (job->enabled() != enabled) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
m_connection->callApi<Quotient::SetPushRuleEnabledJob>(kind, ruleId, enabled);
|
||||
#else
|
||||
m_connection->callApi<Quotient::SetPushRuleEnabledJob>(QStringLiteral("global"), kind, ruleId, enabled);
|
||||
#endif
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -379,11 +363,7 @@ void PushRuleModel::setNotificationRuleActions(const QString &kind, const QStrin
|
||||
actions = actionToVariant(action);
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
m_connection->callApi<Quotient::SetPushRuleActionsJob>(kind, ruleId, actions);
|
||||
#else
|
||||
m_connection->callApi<Quotient::SetPushRuleActionsJob>(QStringLiteral("global"), kind, ruleId, actions);
|
||||
#endif
|
||||
}
|
||||
|
||||
PushRuleAction::Action PushRuleModel::variantToAction(const QList<QVariant> &actions, bool enabled)
|
||||
|
||||
@@ -3,10 +3,15 @@
|
||||
|
||||
#include "reactionmodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFont>
|
||||
#ifdef HAVE_ICU
|
||||
#include <QTextBoundaryFinder>
|
||||
#include <QTextCharFormat>
|
||||
#include <unicode/uchar.h>
|
||||
#include <unicode/urename.h>
|
||||
#endif
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
@@ -152,6 +157,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();
|
||||
@@ -165,7 +194,7 @@ QString ReactionModel::reactionText(QString text) const
|
||||
.arg(m_room->connection()->makeMediaUrl(QUrl(text)).toString(), QString::number(size));
|
||||
}
|
||||
|
||||
return Utils::isEmoji(text) ? QStringLiteral("<span style=\"font-family: 'emoji';\">") + text + QStringLiteral("</span>") : text;
|
||||
return isEmoji(text) ? QStringLiteral("<span style=\"font-family: 'emoji';\">") + text + QStringLiteral("</span>") : text;
|
||||
}
|
||||
|
||||
#include "moc_reactionmodel.cpp"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
bool UserFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
Q_UNUSED(sourceParent);
|
||||
if (!m_allowEmpty && m_filterText.length() < 1) {
|
||||
if (m_filterText.length() < 1) {
|
||||
return false;
|
||||
}
|
||||
return sourceModel()->data(sourceModel()->index(sourceRow, 0), UserListModel::DisplayNameRole).toString().contains(m_filterText, Qt::CaseInsensitive)
|
||||
@@ -27,15 +27,4 @@ void UserFilterModel::setFilterText(const QString &filterText)
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
bool UserFilterModel::allowEmpty() const
|
||||
{
|
||||
return m_allowEmpty;
|
||||
}
|
||||
|
||||
void UserFilterModel::setAllowEmpty(bool allowEmpty)
|
||||
{
|
||||
m_allowEmpty = allowEmpty;
|
||||
Q_EMIT allowEmptyChanged();
|
||||
}
|
||||
|
||||
#include "moc_userfiltermodel.cpp"
|
||||
|
||||
@@ -24,7 +24,6 @@ class UserFilterModel : public QSortFilterProxyModel
|
||||
* The text is either a desired display name or matrix id.
|
||||
*/
|
||||
Q_PROPERTY(QString filterText READ filterText WRITE setFilterText NOTIFY filterTextChanged)
|
||||
Q_PROPERTY(bool allowEmpty READ allowEmpty WRITE setAllowEmpty NOTIFY allowEmptyChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -37,14 +36,9 @@ public:
|
||||
QString filterText() const;
|
||||
void setFilterText(const QString &filterText);
|
||||
|
||||
bool allowEmpty() const;
|
||||
void setAllowEmpty(bool allowEmpty);
|
||||
|
||||
Q_SIGNALS:
|
||||
void filterTextChanged();
|
||||
void allowEmptyChanged();
|
||||
|
||||
private:
|
||||
QString m_filterText;
|
||||
bool m_allowEmpty = false;
|
||||
};
|
||||
|
||||
@@ -88,11 +88,7 @@ QVariant UserListModel::data(const QModelIndex &index, int role) const
|
||||
return memberId;
|
||||
}
|
||||
if (role == AvatarRole) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
return m_currentRoom->member(memberId).avatarUrl();
|
||||
#else
|
||||
return m_currentRoom->memberAvatar(memberId).url();
|
||||
#endif
|
||||
}
|
||||
if (role == ObjectRole) {
|
||||
return QVariant::fromValue(memberId);
|
||||
|
||||
@@ -125,7 +125,7 @@ void WebShortcutModel::trigger(const QString &data)
|
||||
void WebShortcutModel::configureWebShortcuts()
|
||||
{
|
||||
#ifdef HAVE_KIO
|
||||
auto job = new KIO::CommandLauncherJob(QStringLiteral("kcmshell6"), QStringList() << QStringLiteral("webshortcuts"), this);
|
||||
auto job = new KIO::CommandLauncherJob(QStringLiteral("kcmshell5"), QStringList() << QStringLiteral("webshortcuts"), this);
|
||||
job->exec();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ Name[en_GB]=NeoChat
|
||||
Name[eo]=NeoChat
|
||||
Name[es]=NeoChat
|
||||
Name[eu]=NeoChat
|
||||
Name[fa]=نئوچت
|
||||
Name[fi]=NeoChat
|
||||
Name[fr]=NeoChat
|
||||
Name[gl]=NeoChat
|
||||
@@ -262,16 +261,14 @@ Action=Popup
|
||||
Name=Share
|
||||
Name[ar]=شارك
|
||||
Name[ca]=Compartició
|
||||
Name[ca@valencia]=Compartiu
|
||||
Name[ca@valencia]=Compartició
|
||||
Name[cs]=Sdílet
|
||||
Name[de]=Teilen
|
||||
Name[en_GB]=Share
|
||||
Name[eo]=Kundividi
|
||||
Name[es]=Compartir
|
||||
Name[eu]=Partekatu
|
||||
Name[fi]=Jaa
|
||||
Name[fr]=Partager
|
||||
Name[gl]=Compartir
|
||||
Name[he]=שיתוף
|
||||
Name[hu]=Megosztás
|
||||
Name[ia]=Comparti
|
||||
@@ -282,7 +279,6 @@ Name[nl]=Gedeelde
|
||||
Name[nn]=Del
|
||||
Name[pl]=Udostępnij
|
||||
Name[pt_BR]=Compartilhar
|
||||
Name[ru]=Публикация
|
||||
Name[sl]=Deli
|
||||
Name[sv]=Dela
|
||||
Name[ta]=பகிர்
|
||||
@@ -294,14 +290,12 @@ Comment=The result of sharing a piece of content
|
||||
Comment[ar]=نتيجة مشاركة محتوى
|
||||
Comment[ca]=El resultat de compartir una peça de contingut
|
||||
Comment[ca@valencia]=El resultat de compartir una peça de contingut
|
||||
Comment[de]=Das Ergebnis nach dem Teilen eines Teils des Inhalts
|
||||
Comment[en_GB]=The result of sharing a piece of content
|
||||
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[fi]=Tulos yhden sisältöosasen jakamisesta
|
||||
Comment[fr]=Le résultat du partage d'une partie de contenu.
|
||||
Comment[gl]=O resultado de compartir un fragmento de contido.
|
||||
Comment[he]=תוצאת שיתוף פיסת תוכן
|
||||
Comment[hu]=Tartalom megosztásának eredménye
|
||||
Comment[ia]=Le exito de compartir un pecietta de contento
|
||||
@@ -312,7 +306,6 @@ Comment[nl]=Het resultaat van het delen van een stukje inhoud
|
||||
Comment[nn]=Resultatet av deling av innhald
|
||||
Comment[pl]=Wynik udostępniania kawałka treści
|
||||
Comment[pt_BR]=O resultado de compartilhar um conteúdo
|
||||
Comment[ru]=Результат публикации данных
|
||||
Comment[sl]=Rezultat deljenega kosa vsebine
|
||||
Comment[sv]=Resultatet av att dela innehåll
|
||||
Comment[ta]=எதையோ பகிர்ந்ததன் விளைவு
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
File=neochatconfig.kcfg
|
||||
ClassName=NeoChatConfig
|
||||
Mutators=true
|
||||
DefaultValueGetters=true
|
||||
GenerateProperties=true
|
||||
ParentInConstructor=true
|
||||
Singleton=true
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <Quotient/connectiondata.h>
|
||||
#include <Quotient/csapi/content-repo.h>
|
||||
#include <Quotient/csapi/profile.h>
|
||||
#include <Quotient/csapi/versions.h>
|
||||
@@ -148,6 +149,17 @@ void NeoChatConnection::connectSignals()
|
||||
});
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
|
||||
connect(this, &Connection::refreshTokenChanged, this, [this]() {
|
||||
QKeychain::WritePasswordJob job(qAppName());
|
||||
job.setAutoDelete(true);
|
||||
job.setKey(userId());
|
||||
job.setBinaryData(connectionData()->refreshToken().toLatin1());
|
||||
QEventLoop loop;
|
||||
connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
|
||||
job.start();
|
||||
loop.exec();
|
||||
});
|
||||
}
|
||||
|
||||
int NeoChatConnection::badgeNotificationCount() const
|
||||
@@ -574,4 +586,21 @@ LinkPreviewer *NeoChatConnection::previewerForLink(const QUrl &link)
|
||||
return previewer;
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
KeyImport::Error NeoChatConnection::exportMegolmSessions(const QString &passphrase, const QString &path)
|
||||
{
|
||||
KeyImport keyImport;
|
||||
auto result = keyImport.exportKeys(passphrase, this);
|
||||
if (!result.has_value()) {
|
||||
return result.error();
|
||||
}
|
||||
QUrl url(path);
|
||||
QFile file(url.toLocalFile());
|
||||
file.open(QFile::WriteOnly);
|
||||
file.write(result.value());
|
||||
file.close();
|
||||
return KeyImport::Success;
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "moc_neochatconnection.cpp"
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <QCoroTask>
|
||||
#include <Quotient/connection.h>
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
#include <Quotient/keyimport.h>
|
||||
#endif
|
||||
|
||||
#include "models/threepidmodel.h"
|
||||
|
||||
class LinkPreviewer;
|
||||
@@ -169,6 +173,10 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE QString accountDataJsonString(const QString &type) const;
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
Q_INVOKABLE Quotient::KeyImport::Error exportMegolmSessions(const QString &passphrase, const QString &path);
|
||||
#endif
|
||||
|
||||
qsizetype directChatNotifications() const;
|
||||
bool directChatsHaveHighlightNotifications() const;
|
||||
qsizetype homeNotifications() const;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <QMimeDatabase>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
#include <Quotient/events/eventcontent.h>
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/quotient_common.h>
|
||||
#include <qcoro/qcorosignal.h>
|
||||
@@ -77,16 +76,11 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
||||
const auto m_event = evtIt->viewAs<RoomEvent>();
|
||||
QString mxcUrl;
|
||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (event->has<EventContent::FileContentBase>()) {
|
||||
mxcUrl = event->get<EventContent::FileContentBase>()->url().toString();
|
||||
#else
|
||||
if (event->hasFileContent()) {
|
||||
mxcUrl = event->content()->fileInfo()->url().toString();
|
||||
#endif
|
||||
}
|
||||
} else if (auto event = eventCast<const Quotient::StickerEvent>(m_event)) {
|
||||
mxcUrl = event->image().url().toString();
|
||||
mxcUrl = event->image().fileInfo()->url().toString();
|
||||
}
|
||||
if (mxcUrl.isEmpty()) {
|
||||
return;
|
||||
@@ -140,11 +134,7 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
||||
auto showNotification = [this, roomMemberEvent] {
|
||||
QImage avatar_image;
|
||||
if (roomMemberEvent && !member(roomMemberEvent->senderId()).avatarUrl().isEmpty()) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
avatar_image = member(roomMemberEvent->senderId()).avatar(128, 128, {});
|
||||
#else
|
||||
avatar_image = memberAvatar(roomMemberEvent->senderId()).get(this->connection(), 128, [] {});
|
||||
#endif
|
||||
} else {
|
||||
qWarning() << "using this room's avatar";
|
||||
avatar_image = avatar(128);
|
||||
@@ -261,11 +251,7 @@ QCoro::Task<void> NeoChatRoom::doUploadFile(QUrl url, QString body)
|
||||
auto mime = QMimeDatabase().mimeTypeForUrl(url);
|
||||
url.setScheme("file"_ls);
|
||||
QFileInfo fileInfo(url.isLocalFile() ? url.toLocalFile() : url.toString());
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
EventContent::FileContentBase *content;
|
||||
#else
|
||||
EventContent::TypedBase *content;
|
||||
#endif
|
||||
if (mime.name().startsWith("image/"_ls)) {
|
||||
QImage image(url.toLocalFile());
|
||||
content = new EventContent::ImageContent(url, fileInfo.size(), mime, image.size(), fileInfo.fileName());
|
||||
@@ -280,11 +266,7 @@ QCoro::Task<void> NeoChatRoom::doUploadFile(QUrl url, QString body)
|
||||
} else {
|
||||
content = new EventContent::FileContent(url, fileInfo.size(), mime, fileInfo.fileName());
|
||||
}
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
QString txnId = postFile(body.isEmpty() ? url.fileName() : body, std::unique_ptr<EventContent::FileContentBase>(content));
|
||||
#else
|
||||
QString txnId = postFile(body.isEmpty() ? url.fileName() : body, content);
|
||||
#endif
|
||||
setHasFileUploading(true);
|
||||
connect(this, &Room::fileTransferCompleted, [this, txnId](const QString &id, FileSourceInfo) {
|
||||
if (id == txnId) {
|
||||
@@ -410,13 +392,8 @@ bool NeoChatRoom::lastEventIsSpoiler() const
|
||||
{
|
||||
if (auto event = lastEvent()) {
|
||||
if (auto e = eventCast<const RoomMessageEvent>(event)) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (e->has<EventContent::TextContent>() && e->content() && e->mimeType().name() == "text/html"_ls) {
|
||||
auto htmlBody = e->get<EventContent::TextContent>()->body;
|
||||
#else
|
||||
if (e->hasTextContent() && e->content() && e->mimeType().name() == "text/html"_ls) {
|
||||
auto htmlBody = static_cast<const Quotient::EventContent::TextContent *>(e->content())->body;
|
||||
#endif
|
||||
return htmlBody.contains("data-mx-spoiler"_ls);
|
||||
}
|
||||
}
|
||||
@@ -1246,11 +1223,7 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
||||
for (const auto &i : roomRuleArray) {
|
||||
QJsonObject roomRule = i.toObject();
|
||||
if (roomRule["rule_id"_ls] == id()) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
connection()->callApi<DeletePushRuleJob>("room"_ls, id());
|
||||
#else
|
||||
connection()->callApi<DeletePushRuleJob>(QLatin1String("global"), "room"_ls, id());
|
||||
#endif
|
||||
connection()->callApi<DeletePushRuleJob>("global"_ls, "room"_ls, id());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1261,11 +1234,7 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
||||
for (const auto &i : overrideRuleArray) {
|
||||
QJsonObject overrideRule = i.toObject();
|
||||
if (overrideRule["rule_id"_ls] == id()) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
connection()->callApi<DeletePushRuleJob>("override"_ls, id());
|
||||
#else
|
||||
connection()->callApi<DeletePushRuleJob>("global"_ls, "override"_ls, id());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1301,17 +1270,9 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
||||
const QList<PushCondition> conditions = {pushCondition};
|
||||
|
||||
// Add new override rule and make sure it's enabled
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
auto job = connection()->callApi<SetPushRuleJob>("override"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||
#else
|
||||
auto job = connection()->callApi<SetPushRuleJob>("global"_ls, "override"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||
#endif
|
||||
connect(job, &BaseJob::success, this, [this]() {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("override"_ls, id(), true);
|
||||
#else
|
||||
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("global"_ls, "override"_ls, id(), true);
|
||||
#endif
|
||||
connect(enableJob, &BaseJob::success, this, [this]() {
|
||||
m_pushNotificationStateUpdating = false;
|
||||
});
|
||||
@@ -1335,17 +1296,9 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
||||
// No conditions for a room rule
|
||||
const QList<PushCondition> conditions;
|
||||
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
auto setJob = connection()->callApi<SetPushRuleJob>("room"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||
#else
|
||||
auto setJob = connection()->callApi<SetPushRuleJob>("global"_ls, "room"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||
#endif
|
||||
connect(setJob, &BaseJob::success, this, [this]() {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("room"_ls, id(), true);
|
||||
#else
|
||||
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("global"_ls, "room"_ls, id(), true);
|
||||
#endif
|
||||
connect(enableJob, &BaseJob::success, this, [this]() {
|
||||
m_pushNotificationStateUpdating = false;
|
||||
});
|
||||
@@ -1374,17 +1327,9 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
||||
const QList<PushCondition> conditions;
|
||||
|
||||
// Add new room rule and make sure enabled
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
auto setJob = connection()->callApi<SetPushRuleJob>("room"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||
#else
|
||||
auto setJob = connection()->callApi<SetPushRuleJob>("global"_ls, "room"_ls, id(), actions, QString(), QString(), conditions, QString());
|
||||
#endif
|
||||
connect(setJob, &BaseJob::success, this, [this]() {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("room"_ls, id(), true);
|
||||
#else
|
||||
auto enableJob = connection()->callApi<SetPushRuleEnabledJob>("global"_ls, "room"_ls, id(), true);
|
||||
#endif
|
||||
connect(enableJob, &BaseJob::success, this, [this]() {
|
||||
m_pushNotificationStateUpdating = false;
|
||||
});
|
||||
@@ -1475,11 +1420,7 @@ void NeoChatRoom::openEventMediaExternally(const QString &eventId)
|
||||
const auto evtIt = findInTimeline(eventId);
|
||||
if (evtIt != messageEvents().rend() && is<RoomMessageEvent>(**evtIt)) {
|
||||
const auto event = evtIt->viewAs<RoomMessageEvent>();
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (event->has<EventContent::FileContent>()) {
|
||||
#else
|
||||
if (event->hasFileContent()) {
|
||||
#endif
|
||||
const auto transferInfo = cachedFileTransferInfo(event);
|
||||
if (transferInfo.completed()) {
|
||||
UrlHelper helper;
|
||||
@@ -1512,11 +1453,7 @@ void NeoChatRoom::copyEventMedia(const QString &eventId)
|
||||
const auto evtIt = findInTimeline(eventId);
|
||||
if (evtIt != messageEvents().rend() && is<RoomMessageEvent>(**evtIt)) {
|
||||
const auto event = evtIt->viewAs<RoomMessageEvent>();
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (event->has<EventContent::FileContent>()) {
|
||||
#else
|
||||
if (event->hasFileContent()) {
|
||||
#endif
|
||||
const auto transferInfo = fileTransferInfo(eventId);
|
||||
if (transferInfo.completed()) {
|
||||
Clipboard clipboard;
|
||||
@@ -1549,20 +1486,13 @@ FileTransferInfo NeoChatRoom::cachedFileTransferInfo(const Quotient::RoomEvent *
|
||||
QString mxcUrl;
|
||||
int total = 0;
|
||||
if (auto evt = eventCast<const Quotient::RoomMessageEvent>(event)) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
if (evt->has<EventContent::FileContent>()) {
|
||||
const auto fileContent = evt->get<EventContent::FileContent>();
|
||||
#else
|
||||
if (evt->hasFileContent()) {
|
||||
const auto fileContent = evt->content()->fileInfo();
|
||||
#endif
|
||||
|
||||
mxcUrl = fileContent->url().toString();
|
||||
total = fileContent->payloadSize;
|
||||
mxcUrl = evt->content()->fileInfo()->url().toString();
|
||||
total = evt->content()->fileInfo()->payloadSize;
|
||||
}
|
||||
} else if (auto evt = eventCast<const Quotient::StickerEvent>(event)) {
|
||||
mxcUrl = evt->image().url().toString();
|
||||
total = evt->image().payloadSize;
|
||||
mxcUrl = evt->image().fileInfo()->url().toString();
|
||||
total = evt->image().fileInfo()->payloadSize;
|
||||
}
|
||||
|
||||
FileTransferInfo transferInfo = fileTransferInfo(event->id());
|
||||
@@ -1775,13 +1705,9 @@ int NeoChatRoom::maxRoomVersion() const
|
||||
return maxVersion;
|
||||
}
|
||||
|
||||
NeochatRoomMember *NeoChatRoom::directChatRemoteMember()
|
||||
Quotient::RoomMember NeoChatRoom::directChatRemoteMember() const
|
||||
{
|
||||
if (directChatMembers().size() == 0) {
|
||||
qWarning() << "No other member available in this room";
|
||||
return {};
|
||||
}
|
||||
return new NeochatRoomMember(this, directChatMembers()[0].id());
|
||||
return directChatMembers()[0];
|
||||
}
|
||||
|
||||
void NeoChatRoom::sendLocation(float lat, float lon, const QString &description)
|
||||
@@ -1822,6 +1748,7 @@ void NeoChatRoom::downloadEventFromServer(const QString &eventId)
|
||||
connect(job, &BaseJob::success, this, [this, job, eventId] {
|
||||
// The event may have arrived in the meantime so check it's not in the timeline.
|
||||
if (findInTimeline(eventId) != historyEdge()) {
|
||||
Q_EMIT extraEventLoaded(eventId);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
#include "enums/pushrule.h"
|
||||
#include "neochatroommember.h"
|
||||
#include "pollhandler.h"
|
||||
|
||||
namespace Quotient
|
||||
@@ -75,7 +74,7 @@ class NeoChatRoom : public Quotient::Room
|
||||
/**
|
||||
* @brief Get a RoomMember object for the other person in a direct chat.
|
||||
*/
|
||||
Q_PROPERTY(NeochatRoomMember *directChatRemoteMember READ directChatRemoteMember CONSTANT)
|
||||
Q_PROPERTY(Quotient::RoomMember directChatRemoteMember READ directChatRemoteMember CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The Matrix IDs of this room's parents.
|
||||
@@ -320,7 +319,7 @@ public:
|
||||
|
||||
[[nodiscard]] QString avatarMediaId() const;
|
||||
|
||||
NeochatRoomMember *directChatRemoteMember();
|
||||
Quotient::RoomMember directChatRemoteMember() const;
|
||||
|
||||
/**
|
||||
* @brief Whether this room has one or more parent spaces set.
|
||||
|
||||
@@ -131,11 +131,7 @@ void NotificationsManager::processNotificationJob(QPointer<NeoChatConnection> co
|
||||
|
||||
QImage avatar_image;
|
||||
if (!sender.avatarUrl().isEmpty()) {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
avatar_image = room->member(sender.id()).avatar(128, 128, {});
|
||||
#else
|
||||
avatar_image = room->memberAvatar(sender.id()).get(connection, 128, {});
|
||||
#endif
|
||||
} else {
|
||||
avatar_image = room->avatar(128);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ Name[en_GB]=NeoChat
|
||||
Name[eo]=NeoChat
|
||||
Name[es]=NeoChat
|
||||
Name[eu]=NeoChat
|
||||
Name[fa]=نئوچت
|
||||
Name[fi]=NeoChat
|
||||
Name[fr]=NeoChat
|
||||
Name[gl]=NeoChat
|
||||
|
||||
@@ -4,34 +4,9 @@
|
||||
{
|
||||
"Name": "Tobias Fella",
|
||||
"Name[ar]": "توبياس فلة",
|
||||
"Name[ca@valencia]": "Tobias Fella",
|
||||
"Name[ca]": "Tobias Fella",
|
||||
"Name[cs]": "Tobias Fella",
|
||||
"Name[de]": "Tobias Fella",
|
||||
"Name[en_GB]": "Tobias Fella",
|
||||
"Name[eo]": "Tobias Fella",
|
||||
"Name[es]": "Tobias Fella",
|
||||
"Name[eu]": "Tobias Fella",
|
||||
"Name[fi]": "Tobias Fella",
|
||||
"Name[fr]": "Tobias Fella",
|
||||
"Name[gl]": "Tobias Fella",
|
||||
"Name[hu]": "Tobias Fella",
|
||||
"Name[ia]": "Tobias Fella",
|
||||
"Name[it]": "Tobias Fella",
|
||||
"Name[ka]": "Tobias Fella",
|
||||
"Name[lv]": "Tobias Fella",
|
||||
"Name[nl]": "Tobias Fella",
|
||||
"Name[nn]": "Tobias Fella",
|
||||
"Name[pl]": "Tobias Fella",
|
||||
"Name[pt_BR]": "Tobias Fella",
|
||||
"Name[ru]": "Tobias Fella",
|
||||
"Name[sl]": "Tobias Fella",
|
||||
"Name[sv]": "Tobias Fella",
|
||||
"Name[he]": "טוביאס פלה",
|
||||
"Name[ta]": "டோபியாஸ் ஃபெல்லா",
|
||||
"Name[tr]": "Tobias Fella",
|
||||
"Name[uk]": "Tobias Fella",
|
||||
"Name[x-test]": "xxTobias Fellaxx",
|
||||
"Name[zh_TW]": "Tobias Fella"
|
||||
"Name[x-test]": "xxTobias Fellaxx"
|
||||
}
|
||||
],
|
||||
"Category": "Utilities",
|
||||
@@ -40,13 +15,13 @@
|
||||
"Description[ca@valencia]": "Compartix a través de NeoChat",
|
||||
"Description[ca]": "Comparteix a través del NeoChat",
|
||||
"Description[de]": "Über NeoChat teilen",
|
||||
"Description[en_GB]": "Share via NeoChat",
|
||||
"Description[eo]": "Kundividi per NeoChat",
|
||||
"Description[es]": "Compartir mediante NeoChat",
|
||||
"Description[eu]": "Partekatu NeoChat bidez",
|
||||
"Description[fi]": "Jaa NeoChatillä",
|
||||
"Description[fr]": "Partager grâce à NeoChat",
|
||||
"Description[gl]": "Compartir por NeoChat",
|
||||
"Description[he]": "שיתוף דרך NeoChat",
|
||||
"Description[hu]": "Megosztás NeoChatben",
|
||||
"Description[ia]": "Comparti via NeoChat",
|
||||
"Description[it]": "Condividi tramite NeoChat",
|
||||
@@ -55,7 +30,6 @@
|
||||
"Description[nl]": "Delen via NeoChat",
|
||||
"Description[nn]": "Del via NeoChat",
|
||||
"Description[pl]": "Udostępnij przez NeoChat",
|
||||
"Description[pt_BR]": "Compartilhar via NeoChat",
|
||||
"Description[ru]": "Опубликовать в NeoChat",
|
||||
"Description[sl]": "Deli prek NeoChat",
|
||||
"Description[sv]": "Dela via NeoChat",
|
||||
@@ -68,36 +42,9 @@
|
||||
"License": "GPL",
|
||||
"Name": "NeoChat",
|
||||
"Name[ar]": "نيوتشات",
|
||||
"Name[ast]": "NeoChat",
|
||||
"Name[ca@valencia]": "NeoChat",
|
||||
"Name[ca]": "NeoChat",
|
||||
"Name[cs]": "NeoChat",
|
||||
"Name[de]": "NeoChat",
|
||||
"Name[en_GB]": "NeoChat",
|
||||
"Name[eo]": "NeoChat",
|
||||
"Name[es]": "NeoChat",
|
||||
"Name[eu]": "NeoChat",
|
||||
"Name[fa]": "نئوچت",
|
||||
"Name[fi]": "NeoChat",
|
||||
"Name[fr]": "NeoChat",
|
||||
"Name[gl]": "NeoChat",
|
||||
"Name[hu]": "NeoChat",
|
||||
"Name[ia]": "Neochat",
|
||||
"Name[it]": "NeoChat",
|
||||
"Name[ka]": "NeoChat",
|
||||
"Name[lv]": "NeoChat",
|
||||
"Name[nl]": "NeoChat",
|
||||
"Name[nn]": "NeoChat",
|
||||
"Name[pl]": "NeoChat",
|
||||
"Name[pt_BR]": "NeoChat",
|
||||
"Name[ru]": "NeoChat",
|
||||
"Name[sl]": "NeoChat",
|
||||
"Name[sv]": "NeoChat",
|
||||
"Name[ta]": "நியோச்சாட்",
|
||||
"Name[tr]": "NeoChat",
|
||||
"Name[uk]": "NeoChat",
|
||||
"Name[x-test]": "xxNeoChatxx",
|
||||
"Name[zh_TW]": "NeoChat",
|
||||
"X-Purpose-ActionDisplay": "NeoChat"
|
||||
},
|
||||
"X-Purpose-PluginTypes": [
|
||||
|
||||
@@ -19,6 +19,22 @@ QQC2.Menu {
|
||||
|
||||
margins: Kirigami.Units.smallSpacing
|
||||
|
||||
QQC2.MenuItem {
|
||||
text: i18nc("@action:button", "Show QR code")
|
||||
icon.name: "view-barcode-qr-symbolic"
|
||||
onTriggered: {
|
||||
let qrMax = Qt.createComponent('org.kde.neochat', 'QrCodeMaximizeComponent').createObject(QQC2.Overlay.overlay, {
|
||||
text: "https://matrix.to/#/" + root.connection.localUser.id,
|
||||
title: root.connection.localUser.displayName,
|
||||
subtitle: root.connection.localUser.id,
|
||||
avatarSource: root.connection.makeMediaUrl(root.connection.localUser.avatarUrl)
|
||||
});
|
||||
if (typeof root.closeDialog === "function") {
|
||||
root.closeDialog();
|
||||
}
|
||||
qrMax.open();
|
||||
}
|
||||
}
|
||||
QQC2.MenuItem {
|
||||
text: i18n("Edit this account")
|
||||
icon.name: "document-edit"
|
||||
@@ -45,7 +61,7 @@ QQC2.Menu {
|
||||
QQC2.MenuItem {
|
||||
text: i18n("Open developer tools")
|
||||
icon.name: "tools"
|
||||
visible: Config.developerTools
|
||||
visible: NeoChatConfig.developerTools
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'DevtoolsPage'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
@@ -57,7 +73,7 @@ QQC2.Menu {
|
||||
QQC2.MenuItem {
|
||||
text: i18nc("@action:inmenu", "Open Secret Backup")
|
||||
icon.name: "unlock"
|
||||
visible: Config.secretBackup
|
||||
visible: NeoChatConfig.secretBackup
|
||||
onTriggered: root.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UnlockSSSSDialog'), {}, {
|
||||
title: i18nc("@title:window", "Open Key Backup")
|
||||
})
|
||||
|
||||
47
src/qml/AvatarNotification.qml
Normal file
47
src/qml/AvatarNotification.qml
Normal file
@@ -0,0 +1,47 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||
|
||||
KirigamiComponents.Avatar {
|
||||
id: root
|
||||
|
||||
property int notificationCount
|
||||
|
||||
property bool notificationHighlight
|
||||
|
||||
property bool showNotificationLabel
|
||||
|
||||
QQC2.Label {
|
||||
id: notificationCountLabel
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: -Kirigami.Units.smallSpacing
|
||||
anchors.rightMargin: -Kirigami.Units.smallSpacing
|
||||
z: 1
|
||||
width: Math.max(notificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
|
||||
height: Kirigami.Units.iconSizes.smallMedium
|
||||
|
||||
text: root.notificationCount > 0 ? root.notificationCount : ""
|
||||
visible: root.showNotificationLabel
|
||||
color: Kirigami.Theme.textColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
background: Rectangle {
|
||||
visible: true
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
||||
Kirigami.Theme.inherit: false
|
||||
color: root.notificationHighlight ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
|
||||
radius: height / 2
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: notificationCountTextMetrics
|
||||
text: notificationCountLabel.text
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,12 @@ Delegates.RoundedItemDelegate {
|
||||
|
||||
property url source
|
||||
|
||||
property alias notificationCount: avatarNotification.notificationCount
|
||||
|
||||
property alias notificationHighlight: avatarNotification.notificationHighlight
|
||||
|
||||
property alias showNotificationLabel: avatarNotification.showNotificationLabel
|
||||
|
||||
signal contextMenuRequested
|
||||
signal selected
|
||||
|
||||
@@ -41,7 +47,8 @@ Delegates.RoundedItemDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: KirigamiComponents.Avatar {
|
||||
contentItem: AvatarNotification {
|
||||
id: avatarNotification
|
||||
source: root.source
|
||||
name: root.text
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ Loader {
|
||||
* Some common actions shared between menus
|
||||
*/
|
||||
component ViewSourceAction: Kirigami.Action {
|
||||
visible: Config.developerTools
|
||||
visible: NeoChatConfig.developerTools
|
||||
text: i18n("View Source")
|
||||
icon.name: "code-context"
|
||||
onTriggered: RoomManager.viewEventSource(root.eventId)
|
||||
|
||||
@@ -101,13 +101,13 @@ DelegateContextMenu {
|
||||
id: saveAsDialog
|
||||
FileDialog {
|
||||
fileMode: FileDialog.SaveFile
|
||||
folder: Config.lastSaveDirectory.length > 0 ? Config.lastSaveDirectory : StandardPaths.writableLocation(StandardPaths.DownloadLocation)
|
||||
folder: NeoChatConfig.lastSaveDirectory.length > 0 ? NeoChatConfig.lastSaveDirectory : StandardPaths.writableLocation(StandardPaths.DownloadLocation)
|
||||
onAccepted: {
|
||||
if (!currentFile) {
|
||||
return;
|
||||
}
|
||||
Config.lastSaveDirectory = folder;
|
||||
Config.save();
|
||||
NeoChatConfig.lastSaveDirectory = folder;
|
||||
NeoChatConfig.save();
|
||||
currentRoom.downloadFile(eventId, currentFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ QQC2.Control {
|
||||
}
|
||||
},
|
||||
Kirigami.Action {
|
||||
visible: Config.threads && !root.currentRoom.readOnly
|
||||
visible: NeoChatConfig.threads && !root.currentRoom.readOnly
|
||||
text: i18n("Reply in Thread")
|
||||
icon.name: "dialog-messages"
|
||||
onTriggered: {
|
||||
|
||||
@@ -27,10 +27,7 @@ Kirigami.Page {
|
||||
let c = LocationHelper.center(LocationHelper.unite(locationsModel.boundingBox, liveLocationsModel.boundingBox));
|
||||
return QtPositioning.coordinate(c.y, c.x);
|
||||
}
|
||||
map.zoomLevel: {
|
||||
const zoom = LocationHelper.zoomToFit(LocationHelper.unite(locationsModel.boundingBox, liveLocationsModel.boundingBox), mapView.width, mapView.height)
|
||||
return Math.min(Math.max(zoom, map.minimumZoomLevel), map.maximumZoomLevel);
|
||||
}
|
||||
map.zoomLevel: LocationHelper.zoomToFit(LocationHelper.unite(locationsModel.boundingBox, liveLocationsModel.boundingBox), mapView.width, mapView.height)
|
||||
|
||||
MapItemView {
|
||||
Component.onCompleted: mapView.map.addMapItemView(this)
|
||||
|
||||
@@ -18,7 +18,7 @@ Kirigami.ApplicationWindow {
|
||||
readonly property HoverLinkIndicator hoverLinkIndicator: linkIndicator
|
||||
|
||||
|
||||
title: Config.windowTitleFocus ? activeFocusItem + " " + (activeFocusItem ? activeFocusItem.Accessible.name : "") : "NeoChat"
|
||||
title: NeoChatConfig.windowTitleFocus ? activeFocusItem + " " + (activeFocusItem ? activeFocusItem.Accessible.name : "") : "NeoChat"
|
||||
|
||||
minimumWidth: Kirigami.Units.gridUnit * 20
|
||||
minimumHeight: Kirigami.Units.gridUnit * 15
|
||||
@@ -54,6 +54,16 @@ Kirigami.ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
id: pendingOidcConnections
|
||||
target: Controller.pendingOidcConnection
|
||||
function onConnected() {
|
||||
console.warn("loading rooms")
|
||||
root.load();
|
||||
pendingOidcConnections.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.quitAction
|
||||
function onTriggered() {
|
||||
@@ -158,7 +168,7 @@ Kirigami.ApplicationWindow {
|
||||
|
||||
// This is a memory for all user initiated actions on the drawer, i.e. clicking the button
|
||||
// It is used to ensure that user choice is remembered when changing pages and expanding and contracting the window width
|
||||
property bool drawerUserState: Config.autoRoomInfoDrawer
|
||||
property bool drawerUserState: NeoChatConfig.autoRoomInfoDrawer
|
||||
|
||||
connection: root.connection
|
||||
|
||||
@@ -179,7 +189,7 @@ Kirigami.ApplicationWindow {
|
||||
modal: (!root.wideScreen || !enabled)
|
||||
onEnabledChanged: drawerOpen = enabled && !modal
|
||||
onModalChanged: {
|
||||
if (Config.autoRoomInfoDrawer) {
|
||||
if (NeoChatConfig.autoRoomInfoDrawer) {
|
||||
drawerOpen = !modal && drawerUserState;
|
||||
dim = false;
|
||||
}
|
||||
@@ -194,11 +204,11 @@ Kirigami.ApplicationWindow {
|
||||
RoomSettingsView.window = root;
|
||||
NeoChatSettingsView.window = root;
|
||||
NeoChatSettingsView.connection = root.connection;
|
||||
WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);
|
||||
WindowController.setBlur(pageStack, NeoChatConfig.blur && !NeoChatConfig.compactLayout);
|
||||
if (ShareHandler.text && root.connection) {
|
||||
root.handleShare()
|
||||
}
|
||||
if (Config.minimizeToSystemTrayOnStartup && !Kirigami.Settings.isMobile && Controller.supportSystemTray && Config.systemTray) {
|
||||
if (NeoChatConfig.minimizeToSystemTrayOnStartup && !Kirigami.Settings.isMobile && Controller.supportSystemTray && NeoChatConfig.systemTray) {
|
||||
restoreWindowGeometryConnections.enabled = true; // To restore window size and position
|
||||
} else {
|
||||
visible = true;
|
||||
@@ -206,21 +216,21 @@ Kirigami.ApplicationWindow {
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: Config
|
||||
target: NeoChatConfig
|
||||
function onBlurChanged() {
|
||||
WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);
|
||||
WindowController.setBlur(pageStack, NeoChatConfig.blur && !NeoChatConfig.compactLayout);
|
||||
}
|
||||
function onCompactLayoutChanged() {
|
||||
WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);
|
||||
WindowController.setBlur(pageStack, NeoChatConfig.blur && !NeoChatConfig.compactLayout);
|
||||
}
|
||||
}
|
||||
|
||||
// blur effect
|
||||
color: Config.blur && !Config.compactLayout ? "transparent" : Kirigami.Theme.backgroundColor
|
||||
color: NeoChatConfig.blur && !NeoChatConfig.compactLayout ? "transparent" : Kirigami.Theme.backgroundColor
|
||||
|
||||
// we need to apply the translucency effect separately on top of the color
|
||||
background: Rectangle {
|
||||
color: Config.blur && !Config.compactLayout ? Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 1 - Config.transparency) : "transparent"
|
||||
color: NeoChatConfig.blur && !NeoChatConfig.compactLayout ? Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 1 - NeoChatConfig.transparency) : "transparent"
|
||||
}
|
||||
|
||||
Component {
|
||||
|
||||
@@ -119,8 +119,8 @@ Components.AlbumMaximizeComponent {
|
||||
fileMode: Platform.FileDialog.SaveFile
|
||||
folder: root.saveFolder
|
||||
onAccepted: {
|
||||
Config.lastSaveDirectory = folder;
|
||||
Config.save();
|
||||
NeoChatConfig.lastSaveDirectory = folder;
|
||||
NeoChatConfig.save();
|
||||
if (!currentFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -54,13 +54,17 @@ Delegates.RoundedItemDelegate {
|
||||
contentItem: RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
Components.Avatar {
|
||||
AvatarNotification {
|
||||
source: root.avatar ? root.connection.makeMediaUrl("mxc://" + root.avatar) : ""
|
||||
name: root.displayName
|
||||
visible: Config.showAvatarInRoomDrawer
|
||||
implicitHeight: Kirigami.Units.gridUnit + (Config.compactRoomList ? 0 : Kirigami.Units.largeSpacing * 2)
|
||||
visible: NeoChatConfig.showAvatarInRoomDrawer
|
||||
implicitHeight: Kirigami.Units.gridUnit + (NeoChatConfig.compactRoomList ? 0 : Kirigami.Units.largeSpacing * 2)
|
||||
implicitWidth: visible ? implicitHeight : 0
|
||||
|
||||
notificationCount: root.contextNotificationCount
|
||||
notificationHighlight: root.hasHighlightNotifications
|
||||
showNotificationLabel: root.hasNotifications && root.collapsed
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.preferredWidth: height
|
||||
}
|
||||
@@ -91,7 +95,7 @@ Delegates.RoundedItemDelegate {
|
||||
elide: Text.ElideRight
|
||||
font: Kirigami.Theme.smallFont
|
||||
opacity: root.hasNotifications ? 0.9 : 0.7
|
||||
visible: !Config.compactRoomList && text.length > 0
|
||||
visible: !NeoChatConfig.compactRoomList && text.length > 0
|
||||
textFormat: Text.PlainText
|
||||
|
||||
Layout.fillWidth: true
|
||||
@@ -137,7 +141,7 @@ Delegates.RoundedItemDelegate {
|
||||
|
||||
QQC2.Button {
|
||||
id: configButton
|
||||
visible: root.hovered && !Kirigami.Settings.isMobile && !Config.compactRoomList && !root.collapsed && root.showConfigure
|
||||
visible: root.hovered && !Kirigami.Settings.isMobile && !NeoChatConfig.compactRoomList && !root.collapsed && root.showConfigure
|
||||
text: i18n("Configure room")
|
||||
display: QQC2.Button.IconOnly
|
||||
|
||||
@@ -155,14 +159,14 @@ Delegates.RoundedItemDelegate {
|
||||
room: root.currentRoom,
|
||||
connection: root.connection
|
||||
});
|
||||
if (!Kirigami.Settings.isMobile && !Config.compactRoomList) {
|
||||
if (!Kirigami.Settings.isMobile && !NeoChatConfig.compactRoomList) {
|
||||
configButton.visible = true;
|
||||
configButton.down = true;
|
||||
}
|
||||
menu.closed.connect(function () {
|
||||
configButton.down = undefined;
|
||||
configButton.visible = Qt.binding(() => {
|
||||
return root.hovered && !Kirigami.Settings.isMobile && !Config.compactRoomList;
|
||||
return root.hovered && !Kirigami.Settings.isMobile && !NeoChatConfig.compactRoomList;
|
||||
});
|
||||
});
|
||||
menu.open();
|
||||
|
||||
@@ -24,10 +24,10 @@ Kirigami.OverlayDrawer {
|
||||
readonly property int maxWidth: Kirigami.Units.gridUnit * 25
|
||||
readonly property int defaultWidth: Kirigami.Units.gridUnit * 20
|
||||
property int actualWidth: {
|
||||
if (Config.roomDrawerWidth === -1) {
|
||||
if (NeoChatConfig.roomDrawerWidth === -1) {
|
||||
return Kirigami.Units.gridUnit * 20;
|
||||
} else {
|
||||
return Config.roomDrawerWidth;
|
||||
return NeoChatConfig.roomDrawerWidth;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ Kirigami.OverlayDrawer {
|
||||
visible: true
|
||||
onPressed: _lastX = mapToGlobal(mouseX, mouseY).x
|
||||
onReleased: {
|
||||
Config.roomDrawerWidth = root.actualWidth;
|
||||
Config.save();
|
||||
NeoChatConfig.roomDrawerWidth = root.actualWidth;
|
||||
NeoChatConfig.save();
|
||||
}
|
||||
property real _lastX: -1
|
||||
|
||||
@@ -55,9 +55,9 @@ Kirigami.OverlayDrawer {
|
||||
return;
|
||||
}
|
||||
if (Qt.application.layoutDirection === Qt.RightToLeft) {
|
||||
root.actualWidth = Math.min(root.maxWidth, Math.max(root.minWidth, Config.roomDrawerWidth - _lastX + mapToGlobal(mouseX, mouseY).x));
|
||||
root.actualWidth = Math.min(root.maxWidth, Math.max(root.minWidth, NeoChatConfig.roomDrawerWidth - _lastX + mapToGlobal(mouseX, mouseY).x));
|
||||
} else {
|
||||
root.actualWidth = Math.min(root.maxWidth, Math.max(root.minWidth, Config.roomDrawerWidth + _lastX - mapToGlobal(mouseX, mouseY).x));
|
||||
root.actualWidth = Math.min(root.maxWidth, Math.max(root.minWidth, NeoChatConfig.roomDrawerWidth + _lastX - mapToGlobal(mouseX, mouseY).x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
@@ -190,17 +188,11 @@ QQC2.ScrollView {
|
||||
|
||||
focusSequence: "Ctrl+Shift+F"
|
||||
|
||||
onAccepted: userFilterModel.filterText = text
|
||||
onAccepted: sortedMessageEventModel.filterString = text
|
||||
}
|
||||
}
|
||||
|
||||
model: root.room.isDirectChat() ? 0 : userFilterModel
|
||||
|
||||
UserFilterModel {
|
||||
id: userFilterModel
|
||||
sourceModel: RoomManager.userListModel
|
||||
allowEmpty: true
|
||||
}
|
||||
model: root.room.isDirectChat() ? 0 : RoomManager.userListModel
|
||||
|
||||
clip: true
|
||||
focus: true
|
||||
|
||||
@@ -27,7 +27,7 @@ Kirigami.Page {
|
||||
|
||||
required property NeoChatConnection connection
|
||||
|
||||
readonly property bool collapsed: Config.collapsed
|
||||
readonly property bool collapsed: NeoChatConfig.collapsed
|
||||
|
||||
signal search
|
||||
|
||||
@@ -258,7 +258,7 @@ Kirigami.Page {
|
||||
if (_private.currentWidth < _private.collapseWidth && _private.currentWidth + (mouse.x - _lastX) >= _private.collapseWidth) {
|
||||
// Here we get back directly to a more wide mode.
|
||||
_private.currentWidth = _private.defaultWidth;
|
||||
Config.collapsed = false;
|
||||
NeoChatConfig.collapsed = false;
|
||||
} else if (_private.currentWidth >= _private.collapseWidth) {
|
||||
// Increase page width
|
||||
_private.currentWidth = Math.min(_private.defaultWidth, _private.currentWidth + (mouse.x - _lastX));
|
||||
@@ -267,7 +267,7 @@ Kirigami.Page {
|
||||
const tmpWidth = _private.currentWidth - (_lastX - mouse.x);
|
||||
if (tmpWidth < _private.collapseWidth) {
|
||||
_private.currentWidth = Qt.binding(() => _private.collapsedSize);
|
||||
Config.collapsed = true;
|
||||
NeoChatConfig.collapsed = true;
|
||||
} else {
|
||||
_private.currentWidth = tmpWidth;
|
||||
}
|
||||
@@ -324,9 +324,9 @@ Kirigami.Page {
|
||||
*/
|
||||
QtObject {
|
||||
id: _private
|
||||
property int currentWidth: Config.collapsed ? collapsedSize : defaultWidth
|
||||
property int currentWidth: NeoChatConfig.collapsed ? collapsedSize : defaultWidth
|
||||
readonly property int defaultWidth: Kirigami.Units.gridUnit * 17
|
||||
readonly property int collapseWidth: Kirigami.Units.gridUnit * 10
|
||||
readonly property int collapsedSize: Kirigami.Units.gridUnit + (Config.compactRoomList ? 0 : Kirigami.Units.largeSpacing * 2) + Kirigami.Units.largeSpacing * 2 + (scrollView.QQC2.ScrollBar.vertical.visible ? scrollView.QQC2.ScrollBar.vertical.width : 0)
|
||||
readonly property int collapsedSize: Kirigami.Units.gridUnit + (NeoChatConfig.compactRoomList ? 0 : Kirigami.Units.largeSpacing * 2) + Kirigami.Units.largeSpacing * 2 + (scrollView.QQC2.ScrollBar.vertical.visible ? scrollView.QQC2.ScrollBar.vertical.width : 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ Kirigami.Page {
|
||||
sourceComponent: Kirigami.PlaceholderMessage {
|
||||
icon.name: "org.kde.neochat"
|
||||
text: i18n("Welcome to NeoChat")
|
||||
explanation: i18n("Select or join a room to get started")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +170,7 @@ Kirigami.Page {
|
||||
background: Rectangle {
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
color: Config.compactLayout ? Kirigami.Theme.backgroundColor : "transparent"
|
||||
color: NeoChatConfig.compactLayout ? Kirigami.Theme.backgroundColor : "transparent"
|
||||
}
|
||||
|
||||
footer: Loader {
|
||||
|
||||
@@ -86,6 +86,34 @@ QQC2.Control {
|
||||
text: i18n("Home")
|
||||
contentItem: Kirigami.Icon {
|
||||
source: "user-home-symbolic"
|
||||
|
||||
QQC2.Label {
|
||||
id: homeNotificationCountLabel
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: -Kirigami.Units.smallSpacing
|
||||
anchors.rightMargin: -Kirigami.Units.smallSpacing
|
||||
z: 1
|
||||
width: Math.max(homeNotificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
|
||||
height: Kirigami.Units.iconSizes.smallMedium
|
||||
|
||||
text: root.connection.homeNotifications > 0 ? root.connection.homeNotifications : ""
|
||||
visible: root.connection.homeNotifications > 0 && (RoomManager.currentSpace.length > 0 || root.showDirectChats === true)
|
||||
color: Kirigami.Theme.textColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
background: Rectangle {
|
||||
visible: true
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
||||
Kirigami.Theme.inherit: false
|
||||
color: root.connection.homeHaveHighlightNotifications ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
|
||||
radius: height / 2
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: homeNotificationCountTextMetrics
|
||||
text: homeNotificationCountLabel.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
activeFocusOnTab: true
|
||||
@@ -95,33 +123,6 @@ QQC2.Control {
|
||||
RoomManager.currentSpace = "";
|
||||
root.selectionChanged();
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
id: homeNotificationCountLabel
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Kirigami.Units.smallSpacing / 2
|
||||
z: 1
|
||||
width: Math.max(homeNotificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
|
||||
height: Kirigami.Units.iconSizes.smallMedium
|
||||
|
||||
text: root.connection.homeNotifications > 0 ? root.connection.homeNotifications : ""
|
||||
visible: root.connection.homeNotifications > 0 && (RoomManager.currentSpace.length > 0 || root.showDirectChats === true)
|
||||
color: Kirigami.Theme.textColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
background: Rectangle {
|
||||
visible: true
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
||||
Kirigami.Theme.inherit: false
|
||||
color: root.connection.homeHaveHighlightNotifications ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
|
||||
radius: height / 2
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: homeNotificationCountTextMetrics
|
||||
text: homeNotificationCountLabel.text
|
||||
}
|
||||
}
|
||||
}
|
||||
AvatarTabButton {
|
||||
id: directChatButton
|
||||
@@ -134,6 +135,34 @@ QQC2.Control {
|
||||
text: i18nc("@button View all one-on-one chats with your friends.", "Friends")
|
||||
contentItem: Kirigami.Icon {
|
||||
source: "system-users"
|
||||
|
||||
QQC2.Label {
|
||||
id: directChatNotificationCountLabel
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: -Kirigami.Units.smallSpacing
|
||||
anchors.rightMargin: -Kirigami.Units.smallSpacing
|
||||
z: 1
|
||||
width: Math.max(directChatNotificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
|
||||
height: Kirigami.Units.iconSizes.smallMedium
|
||||
|
||||
text: root.connection.directChatNotifications > 0 ? root.connection.directChatNotifications : ""
|
||||
visible: (root.connection.directChatNotifications > 0 || root.connection.directChatInvites) && RoomManager.currentSpace !== "DM"
|
||||
color: Kirigami.Theme.textColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
background: Rectangle {
|
||||
visible: true
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
||||
Kirigami.Theme.inherit: false
|
||||
color: root.connection.directChatsHaveHighlightNotifications ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
|
||||
radius: height / 2
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: directChatNotificationCountTextMetrics
|
||||
text: directChatNotificationCountLabel.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
activeFocusOnTab: true
|
||||
@@ -143,33 +172,6 @@ QQC2.Control {
|
||||
RoomManager.currentSpace = "DM";
|
||||
root.selectionChanged();
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
id: directChatNotificationCountLabel
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Kirigami.Units.smallSpacing / 2
|
||||
z: 1
|
||||
width: Math.max(directChatNotificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
|
||||
height: Kirigami.Units.iconSizes.smallMedium
|
||||
|
||||
text: root.connection.directChatNotifications > 0 ? root.connection.directChatNotifications : ""
|
||||
visible: (root.connection.directChatNotifications > 0 || root.connection.directChatInvites) && RoomManager.currentSpace !== "DM"
|
||||
color: Kirigami.Theme.textColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
background: Rectangle {
|
||||
visible: true
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
||||
Kirigami.Theme.inherit: false
|
||||
color: root.connection.directChatsHaveHighlightNotifications ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
|
||||
radius: height / 2
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: directChatNotificationCountTextMetrics
|
||||
text: directChatNotificationCountLabel.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
@@ -190,6 +192,10 @@ QQC2.Control {
|
||||
text: displayName
|
||||
source: avatar ? root.connection.makeMediaUrl("mxc://" + avatar) : ""
|
||||
|
||||
notificationCount: spaceDelegate.currentRoom.childrenNotificationCount
|
||||
notificationHighlight: spaceDelegate.currentRoom.childrenHaveHighlightNotifications
|
||||
showNotificationLabel: spaceDelegate.currentRoom.childrenNotificationCount > 0 && RoomManager.currentSpace != spaceDelegate.roomId
|
||||
|
||||
activeFocusOnTab: true
|
||||
|
||||
onSelected: {
|
||||
@@ -198,34 +204,6 @@ QQC2.Control {
|
||||
}
|
||||
checked: RoomManager.currentSpace === roomId
|
||||
onContextMenuRequested: root.createContextMenu(currentRoom)
|
||||
|
||||
QQC2.Label {
|
||||
id: notificationCountLabel
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Kirigami.Units.smallSpacing / 2
|
||||
z: 1
|
||||
width: Math.max(notificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
|
||||
height: Kirigami.Units.iconSizes.smallMedium
|
||||
|
||||
text: spaceDelegate.currentRoom.childrenNotificationCount > 0 ? spaceDelegate.currentRoom.childrenNotificationCount : ""
|
||||
visible: spaceDelegate.currentRoom.childrenNotificationCount > 0 && RoomManager.currentSpace != spaceDelegate.roomId
|
||||
color: Kirigami.Theme.textColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
background: Rectangle {
|
||||
visible: true
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
||||
Kirigami.Theme.inherit: false
|
||||
color: spaceDelegate.currentRoom.childrenHaveHighlightNotifications ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor
|
||||
radius: height / 2
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: notificationCountTextMetrics
|
||||
text: notificationCountLabel.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user