Compare commits
43 Commits
v24.08.0
...
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 "07")
|
||||
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)
|
||||
|
||||
@@ -36,8 +36,6 @@ private Q_SLOTS:
|
||||
|
||||
void eventId();
|
||||
void nullEventId();
|
||||
void author();
|
||||
void nullAuthor();
|
||||
void authorDisplayName();
|
||||
void nullAuthorDisplayName();
|
||||
void singleLineSidplayName();
|
||||
@@ -96,32 +94,6 @@ void EventHandlerTest::nullEventId()
|
||||
QCOMPARE(noEventHandler.getId(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::author()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
auto author = room->member(event->senderId());
|
||||
EventHandler eventHandler(room, event);
|
||||
|
||||
auto eventHandlerAuthor = eventHandler.getAuthor();
|
||||
|
||||
QCOMPARE(eventHandlerAuthor.isLocalMember(), author.id() == room->localMember().id());
|
||||
QCOMPARE(eventHandlerAuthor.id(), author.id());
|
||||
QCOMPARE(eventHandlerAuthor.displayName(), author.displayName());
|
||||
QCOMPARE(eventHandlerAuthor.avatarUrl(), author.avatarUrl());
|
||||
QCOMPARE(eventHandlerAuthor.avatarMediaId(), author.avatarMediaId());
|
||||
QCOMPARE(eventHandlerAuthor.color(), author.color());
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullAuthor()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getAuthor(), RoomMember());
|
||||
|
||||
EventHandler noEventHandler(room, nullptr);
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_event set to nullptr. Returning empty user.");
|
||||
QCOMPARE(noEventHandler.getAuthor(), RoomMember());
|
||||
}
|
||||
|
||||
void EventHandlerTest::authorDisplayName()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(1).get());
|
||||
@@ -191,6 +163,7 @@ void EventHandlerTest::timeString()
|
||||
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().time(), QLocale::LongFormat));
|
||||
QCOMPARE(eventHandler.getTimeString(true, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
||||
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::LongFormat));
|
||||
QCOMPARE(eventHandler.getTimeString(QStringLiteral("hh:mm")), QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toString(QStringLiteral("hh:mm")));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullTimeString()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<name xml:lang="fi">NeoChat</name>
|
||||
<name xml:lang="fr">NeoChat</name>
|
||||
<name xml:lang="gl">NeoChat</name>
|
||||
<name xml:lang="he">NeoChat</name>
|
||||
<name xml:lang="hu">NeoChat</name>
|
||||
<name xml:lang="ia">Neochat</name>
|
||||
<name xml:lang="id">NeoChat</name>
|
||||
@@ -61,6 +62,7 @@
|
||||
<summary xml:lang="fi">Keskustelu ystäviesi kanssa Matrixissa</summary>
|
||||
<summary xml:lang="fr">Discuter avec vos ami(e)s sur le réseau Matrix</summary>
|
||||
<summary xml:lang="gl">Charle coas súas amizades en Matrix.</summary>
|
||||
<summary xml:lang="he">התכתבות עם החברים שלך ב־matrix</summary>
|
||||
<summary xml:lang="hu">Csevegjen barátaival a matrixon</summary>
|
||||
<summary xml:lang="ia">Starta Conversation con tu amicos sur matrix</summary>
|
||||
<summary xml:lang="it">Conversa con i tuoi contatti su matrix</summary>
|
||||
@@ -89,6 +91,7 @@
|
||||
<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="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>
|
||||
<p xml:lang="it">NeoChat è un'applicazione di chat che ti consente di sfruttare appieno la rete Matrix. Ti fornisce un modo sicuro per inviare messaggi di testo, video e file audio a familiari, colleghi e amici.</p>
|
||||
@@ -114,6 +117,7 @@
|
||||
<p xml:lang="fi">NeoChat pyrkii olemaan Matrix-määritelmän täysominaisuuksinen sovellus, joten se tukee kaikkea nykyisessä vakaassa määritelmässä muutamaa huomattavaa poikkeusta lukuun ottamatta (VoIP, säikeet ja jotkin piirteet päästä päähän -salauksessa). Joitakin pienempiäkin puutteita on Matrix-määritelmän jatkuvan kehityksen vuoksi, mutta lopputavoitteena on tarjota määritelmän täysi tuki.</p>
|
||||
<p xml:lang="fr">L'objectif de NeoChat est d'être une application complète pour le protocole Matrix. En tant que tel, tout dans la spécification stable actuelle avec les exceptions notables de VoIP, les processus et certains aspects du chiffrement de bout en bout sont pris en charge. Il y a quelques autres petites omissions en raison du fait que la spécification du protocole Matrix est en constante évolution. Cependant, l'objectif reste de fournir un soutien éventuel pour l'ensemble de la spécification.</p>
|
||||
<p xml:lang="gl">NeoChat pretende ser unha aplicación completa para a especificación de Matrix. Coas excepcións de VoIP, conversas fiadas e algúns aspectos da cifraxe de extremo a extremo, a versión estábel segue as especificacións. Existen algunhas outras pequenas omisións debido ao feito de que Matrix está en continua evolución pero a intención é implementar a especificación completa.</p>
|
||||
<p xml:lang="he">NeoChat מתיימר להיות יישום עתיר יכולות לפי מפרט Matrix. כיוון שזה ייעודו, כל מה שבמפרט היציב עם חריגות משמעותיות כגון VoIP, שרשורים ועוד מגוון היבטים של הצפנה מקצה לקצה נתמכים גם הם. יש מספר השמטות קטן עקב העובדה שהמפרט של Matrix ממשיך להתפתח אך המטרה היא להמשיך לספק תמיכה בסופו של דבר לכל המפרט.</p>
|
||||
<p xml:lang="hu">A NeoChat célja, hogy a Matrix specifikációnak megfelelő teljes funkcionalitású alkalmazás legyen. Mint ilyen, a jelenlegi stabil specifikáció támogatott a VoIP, a szálak és a végpontok közötti titkosítás egyes elemeinek kivételével. Van még néhány kisebb hiányosság annak köszönhetően, hogy a Matrix specifikáció folyamatosan fejlődik, de végső cél a teljes specifikáció megvalósítása.</p>
|
||||
<p xml:lang="ia">NeoChat aspira a esser un application plenmente eminente per le specification de Matrix. Tal como omne cosas in le specification currentemente stabile con le exceptiones notabile de VOIP, threads e alcun aspectos del cryptation End-to-End es supportate. Il ha ltere pauc omissiones, debite al facto que le specification de Matrix es in evolution constante ma le aspiration remane a fornir supporto eventual per le integre specification.</p>
|
||||
<p xml:lang="it">NeoChat mira ad essere un'applicazione completa per le specifiche Matrix. Pertanto, sono supportati tutti gli elementi dell'attuale specifica stabile con le notevoli eccezioni di VoIP, conversazioni e alcuni aspetti della cifratura end-to-end. Ci sono alcune altre piccole omissioni dovute al fatto che le specifiche Matrix sono in continua evoluzione, ma l'obiettivo rimane quello di fornire un eventuale supporto per l'intera specifica.</p>
|
||||
@@ -142,6 +146,7 @@
|
||||
<p xml:lang="fi">Matrix-määritelmän kehittyessä NeoChat tukee myös monia epävakaita ominaisuuksia. Tällä hetkellä näitä ovat:</p>
|
||||
<p xml:lang="fr">En raison de la nature du développement des spécifications du protocole Matrix, NeoChat prend également en charge de nombreuses fonctionnalités instables. Actuellement, ce sont :</p>
|
||||
<p xml:lang="gl">Debido á natureza do desenvolvemento da especificación de Matrix, NeoChat tamén inclúe varias funcionalidades non estábeis:</p>
|
||||
<p xml:lang="he">מטבע הדברים, הפיתוח של NeoChat תומך במגוון יכולות מפוקפקות כתלות בהתפתחות המפרט הטכני של Matrix. היכולות האלה הן:</p>
|
||||
<p xml:lang="hu">A Matrix specifikáció fejlesztésének jellegéből adódóan a NeoChat számos instabil funkciót is támogat. Jelenleg a következőket:</p>
|
||||
<p xml:lang="ia">Debite al natura del disveloppamento de specification de Matrix NeoChat tamben supporta numerose characteristicas instabile. Currentemente istes es:</p>
|
||||
<p xml:lang="it">A causa della natura dello sviluppo delle specifiche Matrix, NeoChat supporta anche numerose funzionalità instabili. Attualmente queste sono:</p>
|
||||
@@ -172,6 +177,7 @@
|
||||
<li xml:lang="fi">Kyselyt – MSC3381</li>
|
||||
<li xml:lang="fr">Sondages - MSC3381</li>
|
||||
<li xml:lang="gl">Enquisas — MSC3381</li>
|
||||
<li xml:lang="he">סקרים - MSC3381</li>
|
||||
<li xml:lang="hu">Szavazások - MSC3381</li>
|
||||
<li xml:lang="ia">Inquestas - MSC3381</li>
|
||||
<li xml:lang="it">Sondaggi - MSC3381</li>
|
||||
@@ -200,6 +206,7 @@
|
||||
<li xml:lang="fi">Tarrapakkaukset – MSC2545</li>
|
||||
<li xml:lang="fr">Paquets d'auto-collants - MSC2545</li>
|
||||
<li xml:lang="gl">Paquetes de adhesivos — MSC2545</li>
|
||||
<li xml:lang="he">חבילות מדבקות - MSC2545</li>
|
||||
<li xml:lang="hu">Matricacsomagok - MSC2545</li>
|
||||
<li xml:lang="ia">Etiquetta gummate (sticker) -MSC2545</li>
|
||||
<li xml:lang="it">Pacchetti di adesivi - MSC2545</li>
|
||||
@@ -229,6 +236,7 @@
|
||||
<li xml:lang="fi">Sijaintitapahtumat – MSC3488</li>
|
||||
<li xml:lang="fr">Événements de lieu - MSC3488</li>
|
||||
<li xml:lang="gl">Localización de eventos — MSC3488</li>
|
||||
<li xml:lang="he">אירועי מקום - MSC3488</li>
|
||||
<li xml:lang="hu">Események helyadatai - MSC3488</li>
|
||||
<li xml:lang="ia">Eventos de Location - MSC3488</li>
|
||||
<li xml:lang="it">Località eventi - MSC3488</li>
|
||||
@@ -292,6 +300,7 @@
|
||||
<caption xml:lang="fi">Päänäkymä, jossa huoneluettelo, keskustelu ja huoneen tiedot</caption>
|
||||
<caption xml:lang="fr">Vue principale avec la liste des salons ainsi que des informations sur les salons et forums de discussions</caption>
|
||||
<caption xml:lang="gl">Vista principal coa lista de salas, a charla, e información da sala.</caption>
|
||||
<caption xml:lang="he">תצוגה ראשית עם רשימת חדרים, צ׳אט ופרטי חדר</caption>
|
||||
<caption xml:lang="hu">A fő nézet a szobalistával, csevegéssel és szobainformációkkal</caption>
|
||||
<caption xml:lang="ia">Vista principal con lista de sala, chat e information de sala</caption>
|
||||
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
|
||||
@@ -323,6 +332,7 @@
|
||||
<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="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>
|
||||
<caption xml:lang="it">Scopri nuove comunità con Matrix Spaces</caption>
|
||||
@@ -358,6 +368,7 @@
|
||||
<caption xml:lang="fi">Päänäkymä, jossa huoneluettelo, keskustelu ja huoneen tiedot</caption>
|
||||
<caption xml:lang="fr">Vue principale avec la liste des salons ainsi que des informations sur les salons et forums de discussions</caption>
|
||||
<caption xml:lang="gl">Vista principal coa lista de salas, a charla, e información da sala.</caption>
|
||||
<caption xml:lang="he">תצוגה ראשית עם רשימת חדרים, צ׳אט ופרטי חדר</caption>
|
||||
<caption xml:lang="hu">A fő nézet a szobalistával, csevegéssel és szobainformációkkal</caption>
|
||||
<caption xml:lang="ia">Vista principal con lista de sala, chat e information de sala</caption>
|
||||
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
|
||||
@@ -391,6 +402,7 @@
|
||||
<caption xml:lang="fi">Kirjautumisnäkymä</caption>
|
||||
<caption xml:lang="fr">Écran de connexion</caption>
|
||||
<caption xml:lang="gl">Pantalla de identificación.</caption>
|
||||
<caption xml:lang="he">מסך כניסה</caption>
|
||||
<caption xml:lang="hu">Bejelentkező képernyő</caption>
|
||||
<caption xml:lang="ia">Schermo de accesso</caption>
|
||||
<caption xml:lang="it">Schermata di accesso</caption>
|
||||
|
||||
@@ -18,6 +18,7 @@ Name[eu]=NeoChat
|
||||
Name[fi]=NeoChat
|
||||
Name[fr]=NeoChat
|
||||
Name[gl]=NeoChat
|
||||
Name[he]=NeoChat
|
||||
Name[hu]=NeoChat
|
||||
Name[ia]=Neochat
|
||||
Name[id]=NeoChat
|
||||
@@ -59,6 +60,7 @@ GenericName[eu]=Matrix bezeroa
|
||||
GenericName[fi]=Matrix-asiakas
|
||||
GenericName[fr]=Client « Matrix »
|
||||
GenericName[gl]=Cliente de Matrix
|
||||
GenericName[he]=לקוח Matrix
|
||||
GenericName[hu]=Matrix kliens
|
||||
GenericName[ia]=Cliente de Matrice
|
||||
GenericName[id]=Klien Matrix
|
||||
@@ -99,6 +101,7 @@ Comment[eu]=Matrix protokolorako bezeroa
|
||||
Comment[fi]=Asiakas Matrix-yhteyskäytännölle
|
||||
Comment[fr]=Client pour le protocole « Matrix »
|
||||
Comment[gl]=Cliente para o protocolo Matrix.
|
||||
Comment[he]=לקוח לפרוטוקול Matrix
|
||||
Comment[hu]=Kliens a Matrix protokollhoz
|
||||
Comment[ia]=Cliente per le protocollo de Matrix
|
||||
Comment[id]=Klien untuk protokol Matrix
|
||||
|
||||
569
po/ar/neochat.po
569
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
537
po/az/neochat.po
537
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
519
po/ca/neochat.po
519
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
513
po/cs/neochat.po
513
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
524
po/da/neochat.po
524
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
541
po/de/neochat.po
541
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
540
po/el/neochat.po
540
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
509
po/eo/neochat.po
509
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
522
po/es/neochat.po
522
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
520
po/eu/neochat.po
520
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
536
po/fi/neochat.po
536
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
547
po/fr/neochat.po
547
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
5593
po/gl/neochat.po
Normal file
5593
po/gl/neochat.po
Normal file
File diff suppressed because it is too large
Load Diff
881
po/hu/neochat.po
881
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
522
po/ia/neochat.po
522
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
541
po/id/neochat.po
541
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
527
po/ie/neochat.po
527
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
565
po/it/neochat.po
565
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
489
po/ja/neochat.po
489
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
522
po/ka/neochat.po
522
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
534
po/ko/neochat.po
534
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
489
po/lt/neochat.po
489
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
537
po/lv/neochat.po
537
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
522
po/nl/neochat.po
522
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
511
po/nn/neochat.po
511
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
537
po/pa/neochat.po
537
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
520
po/pl/neochat.po
520
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
541
po/pt/neochat.po
541
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
530
po/ru/neochat.po
530
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
537
po/sk/neochat.po
537
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
516
po/sl/neochat.po
516
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
518
po/sv/neochat.po
518
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
546
po/ta/neochat.po
546
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
523
po/tr/neochat.po
523
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
522
po/uk/neochat.po
522
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
@@ -134,6 +134,8 @@ add_library(neochat STATIC
|
||||
jobs/neochatdeletedevicejob.h
|
||||
jobs/neochatchangepasswordjob.cpp
|
||||
jobs/neochatchangepasswordjob.h
|
||||
jobs/neochatgetcommonroomsjob.cpp
|
||||
jobs/neochatgetcommonroomsjob.h
|
||||
mediasizehelper.cpp
|
||||
mediasizehelper.h
|
||||
eventhandler.cpp
|
||||
@@ -188,6 +190,8 @@ add_library(neochat STATIC
|
||||
threepidbindhelper.h
|
||||
models/readmarkermodel.cpp
|
||||
models/readmarkermodel.h
|
||||
neochatroommember.cpp
|
||||
neochatroommember.h
|
||||
)
|
||||
|
||||
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||
@@ -284,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
|
||||
@@ -421,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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -210,7 +217,12 @@ void Controller::invokeLogin()
|
||||
connect(connection, &NeoChatConnection::networkError, this, [this](const QString &error, const QString &, int, int) {
|
||||
Q_EMIT errorOccured(i18n("Network Error: %1", error), {});
|
||||
});
|
||||
connection->assumeIdentity(account.userId(), accessToken);
|
||||
|
||||
if (!account.clientId().isEmpty()) {
|
||||
connection->assumeOidcIdentity(account.userId(), accessToken, account.clientId(), account.tokenEndpoint());
|
||||
} else {
|
||||
connection->assumeIdentity(account.userId(), accessToken);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -409,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.
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,20 +61,6 @@ MessageComponentType::Type EventHandler::messageComponentType() const
|
||||
return MessageComponentType::typeForEvent(*m_event);
|
||||
}
|
||||
|
||||
Quotient::RoomMember EventHandler::getAuthor(bool isPending) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getAuthor called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getAuthor called with m_event set to nullptr. Returning empty user.";
|
||||
return {};
|
||||
}
|
||||
|
||||
return isPending ? m_room->localMember() : m_room->member(m_event->senderId());
|
||||
}
|
||||
|
||||
QString EventHandler::getAuthorDisplayName(bool isPending) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
@@ -157,6 +143,11 @@ QString EventHandler::getTimeString(bool relative, QLocale::FormatType format, b
|
||||
return {};
|
||||
}
|
||||
|
||||
QString EventHandler::getTimeString(const QString &format, bool isPending, const QDateTime &lastUpdated)
|
||||
{
|
||||
return getTime(isPending, lastUpdated).toLocalTime().toString(format);
|
||||
}
|
||||
|
||||
bool EventHandler::isHighlighted()
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
|
||||
@@ -53,25 +53,10 @@ public:
|
||||
*/
|
||||
MessageComponentType::Type messageComponentType() const;
|
||||
|
||||
/**
|
||||
* @brief Get the author of the event in context of the room.
|
||||
*
|
||||
* An empty Quotient::RoomMember will be returned if the EventHandler hasn't had
|
||||
* the room or event initialised.
|
||||
*
|
||||
* @param isPending if the event is pending, i.e. has not been confirmed by
|
||||
* the server.
|
||||
*
|
||||
* @return a Quotient::RoomMember object for the user.
|
||||
*
|
||||
* @sa Quotient::RoomMember
|
||||
*/
|
||||
Quotient::RoomMember getAuthor(bool isPending = false) const;
|
||||
|
||||
/**
|
||||
* @brief Get the display name of the event author.
|
||||
*
|
||||
* This method is separate from getAuthor() and special in that it will return
|
||||
* This method is special in that it will return
|
||||
* the old display name of the author if the current event is one that caused it
|
||||
* to change. This allows for scenarios where the UI wishes to notify that a
|
||||
* user's display name has changed and what it changed from.
|
||||
@@ -113,6 +98,8 @@ public:
|
||||
*/
|
||||
QString getTimeString(bool relative, QLocale::FormatType format = QLocale::ShortFormat, bool isPending = false, QDateTime lastUpdated = {}) const;
|
||||
|
||||
QString getTimeString(const QString &format, bool isPending = false, const QDateTime &lastUpdated = {});
|
||||
|
||||
/**
|
||||
* @brief Whether the event should be highlighted in the timeline.
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
@@ -52,8 +43,11 @@ struct ForeignSSSSHandler {
|
||||
QML_NAMED_ELEMENT(SSSSHandler)
|
||||
};
|
||||
|
||||
struct RoomMemberForeign {
|
||||
#if Quotient_VERSION_MINOR > 8
|
||||
struct ForeignKeyImport {
|
||||
Q_GADGET
|
||||
QML_FOREIGN(Quotient::RoomMember)
|
||||
QML_NAMED_ELEMENT(RoomMember)
|
||||
QML_SINGLETON
|
||||
QML_FOREIGN(Quotient::KeyImport)
|
||||
QML_NAMED_ELEMENT(KeyImport)
|
||||
};
|
||||
#endif
|
||||
|
||||
14
src/jobs/neochatgetcommonroomsjob.cpp
Normal file
14
src/jobs/neochatgetcommonroomsjob.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "neochatgetcommonroomsjob.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
NeochatGetCommonRoomsJob::NeochatGetCommonRoomsJob(const QString &userId)
|
||||
: BaseJob(HttpVerb::Get,
|
||||
QStringLiteral("GetCommonRoomsJob"),
|
||||
QStringLiteral("/_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms").toLatin1(),
|
||||
QUrlQuery({{QStringLiteral("user_id"), userId}}))
|
||||
{
|
||||
}
|
||||
14
src/jobs/neochatgetcommonroomsjob.h
Normal file
14
src/jobs/neochatgetcommonroomsjob.h
Normal file
@@ -0,0 +1,14 @@
|
||||
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/omittable.h>
|
||||
|
||||
// TODO: Upstream to libQuotient
|
||||
class NeochatGetCommonRoomsJob : public Quotient::BaseJob
|
||||
{
|
||||
public:
|
||||
explicit NeochatGetCommonRoomsJob(const QString &userId);
|
||||
};
|
||||
@@ -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"));
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "messagecontentmodel.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroommember.h"
|
||||
|
||||
#include <QImageReader>
|
||||
|
||||
@@ -30,20 +31,20 @@
|
||||
|
||||
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())
|
||||
, m_event(loadEvent<RoomEvent>(event->fullJson()))
|
||||
, m_isPending(isPending)
|
||||
, m_isReply(isReply)
|
||||
{
|
||||
intiializeEvent(event);
|
||||
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)
|
||||
@@ -57,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;
|
||||
}
|
||||
}
|
||||
@@ -81,8 +83,7 @@ void MessageContentModel::initializeModel()
|
||||
if (m_eventId == serverEvent->id()) {
|
||||
beginResetModel();
|
||||
m_isPending = false;
|
||||
m_event = loadEvent<RoomEvent>(serverEvent->fullJson());
|
||||
Q_EMIT eventUpdated();
|
||||
intiializeEvent(serverEvent);
|
||||
endResetModel();
|
||||
}
|
||||
}
|
||||
@@ -91,8 +92,7 @@ void MessageContentModel::initializeModel()
|
||||
if (m_room != nullptr && m_event != nullptr) {
|
||||
if (m_eventId == newEvent->id()) {
|
||||
beginResetModel();
|
||||
m_event = loadEvent<RoomEvent>(newEvent->fullJson());
|
||||
Q_EMIT eventUpdated();
|
||||
intiializeEvent(newEvent);
|
||||
endResetModel();
|
||||
}
|
||||
}
|
||||
@@ -154,6 +154,34 @@ void MessageContentModel::initializeModel()
|
||||
resetModel();
|
||||
}
|
||||
|
||||
void MessageContentModel::intiializeEvent(const QString &eventId)
|
||||
{
|
||||
const auto newEvent = m_room->getEvent(eventId);
|
||||
if (newEvent != nullptr) {
|
||||
intiializeEvent(newEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageContentModel::intiializeEvent(const Quotient::RoomEvent *event)
|
||||
{
|
||||
m_event = loadEvent<RoomEvent>(event->fullJson());
|
||||
// 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()) {
|
||||
senderId = m_room->localMember().id();
|
||||
}
|
||||
if (m_eventSenderObject == nullptr) {
|
||||
m_eventSenderObject = std::unique_ptr<NeochatRoomMember>(new NeochatRoomMember(m_room, senderId));
|
||||
}
|
||||
Q_EMIT eventUpdated();
|
||||
}
|
||||
|
||||
bool MessageContentModel::showAuthor() const
|
||||
{
|
||||
return m_showAuthor;
|
||||
@@ -236,10 +264,10 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
||||
});
|
||||
|
||||
auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated();
|
||||
return eventHandler.getTimeString(false, QLocale::ShortFormat, m_isPending, lastUpdated);
|
||||
return eventHandler.getTimeString(QStringLiteral("hh:mm"), m_isPending, lastUpdated);
|
||||
}
|
||||
if (role == AuthorRole) {
|
||||
return QVariant::fromValue(eventHandler.getAuthor(m_isPending));
|
||||
return QVariant::fromValue<NeochatRoomMember *>(m_eventSenderObject.get());
|
||||
}
|
||||
if (role == MediaInfoRole) {
|
||||
return eventHandler.getMediaInfo();
|
||||
@@ -400,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]() {
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
#include <QAbstractListModel>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/events/roomevent.h>
|
||||
#include <Quotient/room.h>
|
||||
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "eventhandler.h"
|
||||
#include "itinerarymodel.h"
|
||||
#include "neochatroommember.h"
|
||||
|
||||
struct MessageComponent {
|
||||
MessageComponentType::Type type = MessageComponentType::Other;
|
||||
@@ -73,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);
|
||||
@@ -115,6 +121,7 @@ private:
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
QString m_eventId;
|
||||
QString m_eventSenderId;
|
||||
std::unique_ptr<NeochatRoomMember> m_eventSenderObject = nullptr;
|
||||
Quotient::RoomEventPtr m_event;
|
||||
|
||||
bool m_isPending;
|
||||
@@ -122,6 +129,8 @@ private:
|
||||
bool m_isReply;
|
||||
|
||||
void initializeModel();
|
||||
void intiializeEvent(const QString &eventId);
|
||||
void intiializeEvent(const Quotient::RoomEvent *event);
|
||||
|
||||
QList<MessageComponent> m_components;
|
||||
void resetModel();
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "messagecontentmodel.h"
|
||||
#include "models/messagefiltermodel.h"
|
||||
#include "models/reactionmodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatroommember.h"
|
||||
#include "readmarkermodel.h"
|
||||
#include "texthandler.h"
|
||||
|
||||
@@ -86,10 +88,16 @@ 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();
|
||||
|
||||
// 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();
|
||||
@@ -150,8 +158,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
refreshLastUserEvents(i);
|
||||
}
|
||||
});
|
||||
connect(m_currentRoom, &Room::pendingEventAboutToAdd, this, [this] {
|
||||
connect(m_currentRoom, &Room::pendingEventAboutToAdd, this, [this](Quotient::RoomEvent *event) {
|
||||
m_initialized = true;
|
||||
createEventObjects(event);
|
||||
beginInsertRows({}, 0, 0);
|
||||
});
|
||||
connect(m_currentRoom, &Room::pendingEventAdded, this, &MessageEventModel::endInsertRows);
|
||||
@@ -214,22 +223,6 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
});
|
||||
connect(m_currentRoom, &Room::memberNameUpdated, this, [this](RoomMember member) {
|
||||
for (auto it = m_currentRoom->messageEvents().rbegin(); it != m_currentRoom->messageEvents().rend(); ++it) {
|
||||
auto event = it->event();
|
||||
if (event->senderId() == member.id()) {
|
||||
refreshEventRoles(event->id(), {AuthorRole});
|
||||
}
|
||||
}
|
||||
});
|
||||
connect(m_currentRoom, &Room::memberAvatarUpdated, this, [this](RoomMember member) {
|
||||
for (auto it = m_currentRoom->messageEvents().rbegin(); it != m_currentRoom->messageEvents().rend(); ++it) {
|
||||
auto event = it->event();
|
||||
if (event->senderId() == member.id()) {
|
||||
refreshEventRoles(event->id(), {AuthorRole});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
qCDebug(MessageEvent) << "Connected to room" << room->id() << "as" << room->localMember().id();
|
||||
} else {
|
||||
@@ -397,6 +390,8 @@ void MessageEventModel::fetchMore(const QModelIndex &parent)
|
||||
}
|
||||
}
|
||||
|
||||
static NeochatRoomMember *emptyNeochatRoomMember = new NeochatRoomMember;
|
||||
|
||||
QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
{
|
||||
if (!checkIndex(idx, QAbstractItemModel::CheckIndexOption::IndexIsValid)) {
|
||||
@@ -449,13 +444,8 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
@@ -469,7 +459,18 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
if (role == AuthorRole) {
|
||||
return QVariant::fromValue(eventHandler.getAuthor(isPending));
|
||||
QString mId;
|
||||
if (isPending) {
|
||||
mId = m_currentRoom->localMember().id();
|
||||
} else {
|
||||
mId = evt.senderId();
|
||||
}
|
||||
|
||||
if (!m_memberObjects.contains(mId)) {
|
||||
return QVariant::fromValue<NeochatRoomMember *>(emptyNeochatRoomMember);
|
||||
}
|
||||
|
||||
return QVariant::fromValue<NeochatRoomMember *>(m_memberObjects.at(mId).get());
|
||||
}
|
||||
|
||||
if (role == HighlightRole) {
|
||||
@@ -619,6 +620,22 @@ void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event)
|
||||
}
|
||||
|
||||
auto eventId = event->id();
|
||||
auto senderId = 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()) {
|
||||
senderId = m_currentRoom->localMember().id();
|
||||
}
|
||||
|
||||
if (!m_memberObjects.contains(senderId)) {
|
||||
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.
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include "linkpreviewer.h"
|
||||
#include "messagecontentmodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatroommember.h"
|
||||
#include "pollhandler.h"
|
||||
#include "readmarkermodel.h"
|
||||
|
||||
@@ -115,6 +117,8 @@ private:
|
||||
bool movingEvent = false;
|
||||
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;
|
||||
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
#include "messagefiltermodel.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <QVariant>
|
||||
|
||||
#include "enums/delegatetype.h"
|
||||
#include "messagecontentmodel.h"
|
||||
#include "messageeventmodel.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroommember.h"
|
||||
#include "timelinemodel.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "eventhandler.h"
|
||||
#include "models/messagecontentmodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatroommember.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
|
||||
@@ -66,7 +67,17 @@ void SearchModel::search()
|
||||
m_job = job;
|
||||
connect(job, &BaseJob::finished, this, [this, job] {
|
||||
beginResetModel();
|
||||
m_memberObjects.clear();
|
||||
m_result = job->searchCategories().roomEvents;
|
||||
|
||||
if (m_result.has_value()) {
|
||||
for (const auto &result : m_result.value().results) {
|
||||
if (!m_memberObjects.contains(result.result->senderId())) {
|
||||
m_memberObjects[result.result->senderId()] = std::unique_ptr<NeochatRoomMember>(new NeochatRoomMember(m_room, result.result->senderId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
setSearching(false);
|
||||
m_job = nullptr;
|
||||
@@ -83,7 +94,7 @@ QVariant SearchModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
switch (role) {
|
||||
case AuthorRole:
|
||||
return QVariant::fromValue(eventHandler.getAuthor());
|
||||
return QVariant::fromValue<NeochatRoomMember *>(m_memberObjects.at(event.senderId()).get());
|
||||
case ShowSectionRole:
|
||||
if (row == 0) {
|
||||
return true;
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include <Quotient/csapi/search.h>
|
||||
|
||||
#include "neochatroommember.h"
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class Connection;
|
||||
@@ -123,4 +125,6 @@ private:
|
||||
std::optional<Quotient::SearchJob::ResultRoomEvents> m_result = std::nullopt;
|
||||
Quotient::SearchJob *m_job = nullptr;
|
||||
bool m_searching = false;
|
||||
|
||||
std::map<QString, std::unique_ptr<NeochatRoomMember>> m_memberObjects;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ Name[eu]=NeoChat
|
||||
Name[fi]=NeoChat
|
||||
Name[fr]=NeoChat
|
||||
Name[gl]=NeoChat
|
||||
Name[he]=NeoChat
|
||||
Name[hu]=NeoChat
|
||||
Name[ia]=Neochat
|
||||
Name[id]=NeoChat
|
||||
@@ -58,6 +59,7 @@ Comment[eu]=Matrix, deszentralizatutako komunikazio protokolorako, bezero bat
|
||||
Comment[fi]=Hajautetun Matrix-viestintäyhteyskäytännön asiakasohjelma
|
||||
Comment[fr]=Un client pour « Matrix », le protocole décentralisé de communications.
|
||||
Comment[gl]=Un cliente para Matrix, o protocolo de comunicación descentralizada.
|
||||
Comment[he]=לקוח ל־matrix, פרוטוקול התקשורת המבוזר
|
||||
Comment[hu]=Kliens a matrixhoz, a decentralizált kommunikációs protokollhoz
|
||||
Comment[ia]=Un cliente per Matrix, le protocollo de communication decentralisate
|
||||
Comment[id]=Sebuah klien untuk matrix, protokol komunikasi terdecentralisasi
|
||||
@@ -102,6 +104,7 @@ Name[eu]=Mezu berria
|
||||
Name[fi]=Uusi viesti
|
||||
Name[fr]=Nouveau message
|
||||
Name[gl]=Nova mensaxe
|
||||
Name[he]=הודעה חדשה
|
||||
Name[hu]=Új üzenet
|
||||
Name[ia]=Nove message
|
||||
Name[id]=Pesan baru
|
||||
@@ -142,6 +145,7 @@ Comment[eu]=Mezu berri bat dago
|
||||
Comment[fi]=Saapui uusi viesti
|
||||
Comment[fr]=Il y a un nouveau message
|
||||
Comment[gl]=Hai unha nova mensaxe.
|
||||
Comment[he]=יש הודעה חדשה
|
||||
Comment[hu]=Új üzenet érkezett
|
||||
Comment[ia]=Il ha un nove message
|
||||
Comment[id]=Ada pesan baru
|
||||
@@ -186,6 +190,7 @@ Name[eu]=Gonbidapen berria
|
||||
Name[fi]=Uusi kutsu
|
||||
Name[fr]=Nouvelle invitation
|
||||
Name[gl]=Novo convite
|
||||
Name[he]=הזמנה חדשה
|
||||
Name[hu]=Új meghívó
|
||||
Name[ia]=Nove invitation
|
||||
Name[id]=Undangan Baru
|
||||
@@ -225,6 +230,7 @@ Comment[eu]=Gela baterako gonbidapen berri bat dago
|
||||
Comment[fi]=Uusi kutsu huoneeseen
|
||||
Comment[fr]=Il y a une nouvelle invitation dans un salon.
|
||||
Comment[gl]=Tes un novo convite para unha sala.
|
||||
Comment[he]=יש הזמנה חדשה לחדר
|
||||
Comment[hu]=Új meghívó érkezett egy szobába
|
||||
Comment[ia]=Il ha un nove invitation a un sala
|
||||
Comment[id]=Ada undangan baru ke sebuah ruangan
|
||||
@@ -263,6 +269,7 @@ Name[es]=Compartir
|
||||
Name[eu]=Partekatu
|
||||
Name[fi]=Jaa
|
||||
Name[fr]=Partager
|
||||
Name[he]=שיתוף
|
||||
Name[hu]=Megosztás
|
||||
Name[ia]=Comparti
|
||||
Name[it]=Condivisione
|
||||
@@ -271,6 +278,7 @@ Name[lv]=Kopīgot
|
||||
Name[nl]=Gedeelde
|
||||
Name[nn]=Del
|
||||
Name[pl]=Udostępnij
|
||||
Name[pt_BR]=Compartilhar
|
||||
Name[sl]=Deli
|
||||
Name[sv]=Dela
|
||||
Name[ta]=பகிர்
|
||||
@@ -288,6 +296,7 @@ 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[he]=תוצאת שיתוף פיסת תוכן
|
||||
Comment[hu]=Tartalom megosztásának eredménye
|
||||
Comment[ia]=Le exito de compartir un pecietta de contento
|
||||
Comment[it]=Il risultato della condivisione di un contenuto
|
||||
@@ -296,6 +305,7 @@ Comment[lv]=Satura kopīgošanas rezultāts
|
||||
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[sl]=Rezultat deljenega kosa vsebine
|
||||
Comment[sv]=Resultatet av att dela innehåll
|
||||
Comment[ta]=எதையோ பகிர்ந்ததன் விளைவு
|
||||
|
||||
@@ -187,5 +187,11 @@
|
||||
<default>false</default>
|
||||
</entry>
|
||||
</group>
|
||||
<group name="Security">
|
||||
<entry name="RejectUnknownInvites" type="bool">
|
||||
<label>Reject unknown invites</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
</group>
|
||||
</kcfg>
|
||||
|
||||
|
||||
@@ -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,8 +23,10 @@
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <Quotient/connectiondata.h>
|
||||
#include <Quotient/csapi/content-repo.h>
|
||||
#include <Quotient/csapi/profile.h>
|
||||
#include <Quotient/csapi/versions.h>
|
||||
#include <Quotient/database.h>
|
||||
#include <Quotient/jobs/downloadfilejob.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
@@ -132,6 +134,32 @@ void NeoChatConnection::connectSignals()
|
||||
Q_EMIT homeNotificationsChanged();
|
||||
Q_EMIT homeHaveHighlightNotificationsChanged();
|
||||
});
|
||||
|
||||
// Fetch unstable features
|
||||
// TODO: Expose unstableFeatures() in libQuotient
|
||||
connect(
|
||||
this,
|
||||
&Connection::connected,
|
||||
this,
|
||||
[this] {
|
||||
auto job = callApi<GetVersionsJob>(BackgroundRequest);
|
||||
connect(job, &GetVersionsJob::success, this, [this, job] {
|
||||
m_canCheckMutualRooms = job->unstableFeatures().contains("uk.half-shot.msc2666.query_mutual_rooms"_ls);
|
||||
Q_EMIT canCheckMutualRoomsChanged();
|
||||
});
|
||||
},
|
||||
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
|
||||
@@ -200,6 +228,11 @@ QVariantList NeoChatConnection::getSupportedRoomVersions() const
|
||||
return supportedRoomVersions;
|
||||
}
|
||||
|
||||
bool NeoChatConnection::canCheckMutualRooms() const
|
||||
{
|
||||
return m_canCheckMutualRooms;
|
||||
}
|
||||
|
||||
void NeoChatConnection::changePassword(const QString ¤tPassword, const QString &newPassword)
|
||||
{
|
||||
auto job = callApi<NeochatChangePasswordJob>(newPassword, false);
|
||||
@@ -553,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;
|
||||
@@ -79,6 +83,11 @@ class NeoChatConnection : public Quotient::Connection
|
||||
*/
|
||||
Q_PROPERTY(bool isOnline READ isOnline WRITE setIsOnline NOTIFY isOnlineChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the server supports querying a user's mutual rooms.
|
||||
*/
|
||||
Q_PROPERTY(bool canCheckMutualRooms READ canCheckMutualRooms NOTIFY canCheckMutualRoomsChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the status after an attempt to change the password on an account.
|
||||
@@ -95,6 +104,7 @@ public:
|
||||
|
||||
Q_INVOKABLE void logout(bool serverSideLogout);
|
||||
Q_INVOKABLE QVariantList getSupportedRoomVersions() const;
|
||||
bool canCheckMutualRooms() const;
|
||||
|
||||
/**
|
||||
* @brief Change the password for an account.
|
||||
@@ -163,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;
|
||||
@@ -196,6 +210,7 @@ Q_SIGNALS:
|
||||
void passwordStatus(NeoChatConnection::PasswordStatus status);
|
||||
void userConsentRequired(QUrl url);
|
||||
void badgeNotificationCountChanged(NeoChatConnection *connection, int count);
|
||||
void canCheckMutualRoomsChanged();
|
||||
|
||||
private:
|
||||
bool m_isOnline = true;
|
||||
@@ -208,4 +223,6 @@ private:
|
||||
int m_badgeNotificationCount = 0;
|
||||
|
||||
QHash<QUrl, LinkPreviewer *> m_linkPreviewers;
|
||||
|
||||
bool m_canCheckMutualRooms = false;
|
||||
};
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "events/joinrulesevent.h"
|
||||
#include "events/pollevent.h"
|
||||
#include "filetransferpseudojob.h"
|
||||
#include "jobs/neochatgetcommonroomsjob.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "notificationsmanager.h"
|
||||
#include "roomlastmessageprovider.h"
|
||||
@@ -129,14 +130,38 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
||||
return;
|
||||
}
|
||||
auto roomMemberEvent = currentState().get<RoomMemberEvent>(localMember().id());
|
||||
QImage avatar_image;
|
||||
if (roomMemberEvent && !member(roomMemberEvent->senderId()).avatarUrl().isEmpty()) {
|
||||
avatar_image = memberAvatar(roomMemberEvent->senderId()).get(this->connection(), 128, [] {});
|
||||
|
||||
auto showNotification = [this, roomMemberEvent] {
|
||||
QImage avatar_image;
|
||||
if (roomMemberEvent && !member(roomMemberEvent->senderId()).avatarUrl().isEmpty()) {
|
||||
avatar_image = memberAvatar(roomMemberEvent->senderId()).get(this->connection(), 128, [] {});
|
||||
} else {
|
||||
qWarning() << "using this room's avatar";
|
||||
avatar_image = avatar(128);
|
||||
}
|
||||
|
||||
NotificationsManager::instance().postInviteNotification(this,
|
||||
displayName(),
|
||||
member(roomMemberEvent->senderId()).htmlSafeDisplayName(),
|
||||
avatar_image);
|
||||
};
|
||||
|
||||
if (NeoChatConfig::rejectUnknownInvites()) {
|
||||
auto job = this->connection()->callApi<NeochatGetCommonRoomsJob>(roomMemberEvent->senderId());
|
||||
connect(job, &BaseJob::result, this, [this, job, roomMemberEvent, showNotification] {
|
||||
QJsonObject replyData = job->jsonData();
|
||||
if (replyData.contains(QStringLiteral("joined"))) {
|
||||
const bool inAnyOfOurRooms = !replyData[QStringLiteral("joined")].toArray().isEmpty();
|
||||
if (inAnyOfOurRooms) {
|
||||
showNotification();
|
||||
} else {
|
||||
leaveRoom();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
qWarning() << "using this room's avatar";
|
||||
avatar_image = avatar(128);
|
||||
showNotification();
|
||||
}
|
||||
NotificationsManager::instance().postInviteNotification(this, displayName(), member(roomMemberEvent->senderId()).htmlSafeDisplayName(), avatar_image);
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
connect(this, &Room::changed, this, [this] {
|
||||
@@ -1313,7 +1338,6 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
||||
|
||||
m_currentPushNotificationState = state;
|
||||
Q_EMIT pushNotificationStateChanged(m_currentPushNotificationState);
|
||||
|
||||
}
|
||||
|
||||
void NeoChatRoom::updatePushNotificationState(QString type)
|
||||
@@ -1724,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;
|
||||
}
|
||||
|
||||
@@ -1743,6 +1768,11 @@ const RoomEvent *NeoChatRoom::getEvent(const QString &eventId) const
|
||||
return timelineIt->get();
|
||||
}
|
||||
|
||||
const auto pendingIt = findPendingEvent(eventId);
|
||||
if (pendingIt != pendingEvents().end()) {
|
||||
return pendingIt->event();
|
||||
}
|
||||
|
||||
auto extraIt = std::find_if(m_extraEvents.begin(), m_extraEvents.end(), [eventId](const Quotient::event_ptr_tt<Quotient::RoomEvent> &event) {
|
||||
return event->id() == eventId;
|
||||
});
|
||||
|
||||
166
src/neochatroommember.cpp
Normal file
166
src/neochatroommember.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
// 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
|
||||
|
||||
#include "neochatroommember.h"
|
||||
|
||||
#include "neochatroom.h"
|
||||
|
||||
NeochatRoomMember::NeochatRoomMember(NeoChatRoom *room, const QString &memberId)
|
||||
: m_room(room)
|
||||
, m_memberId(memberId)
|
||||
{
|
||||
Q_ASSERT(!m_memberId.isEmpty());
|
||||
|
||||
if (m_room != nullptr) {
|
||||
connect(m_room, &NeoChatRoom::memberNameUpdated, this, [this](Quotient::RoomMember member) {
|
||||
if (member.id() == m_memberId) {
|
||||
Q_EMIT displayNameUpdated();
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::memberAvatarUpdated, this, [this](Quotient::RoomMember member) {
|
||||
if (member.id() == m_memberId) {
|
||||
Q_EMIT avatarUpdated();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::id() const
|
||||
{
|
||||
return m_memberId;
|
||||
}
|
||||
|
||||
Quotient::Uri NeochatRoomMember::uri() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).uri();
|
||||
}
|
||||
|
||||
bool NeochatRoomMember::isLocalMember() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).isLocalMember();
|
||||
}
|
||||
|
||||
Quotient::Membership NeochatRoomMember::membershipState() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return Quotient::Membership::Leave;
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).membershipState();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::name() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return id();
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).name();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::displayName() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return id();
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).displayName();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::htmlSafeDisplayName() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return id();
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).htmlSafeDisplayName();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::fullName() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return id();
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).fullName();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::htmlSafeFullName() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return id();
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).htmlSafeFullName();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::disambiguatedName() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return id();
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).disambiguatedName();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::htmlSafeDisambiguatedName() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return id();
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).htmlSafeDisambiguatedName();
|
||||
}
|
||||
|
||||
int NeochatRoomMember::hue() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).hue();
|
||||
}
|
||||
|
||||
qreal NeochatRoomMember::hueF() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).hueF();
|
||||
}
|
||||
|
||||
QColor NeochatRoomMember::color() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).color();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::avatarMediaId() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).avatarMediaId();
|
||||
}
|
||||
|
||||
QUrl NeochatRoomMember::avatarUrl() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).avatarUrl();
|
||||
}
|
||||
83
src/neochatroommember.h
Normal file
83
src/neochatroommember.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
#include <Quotient/roommember.h>
|
||||
#include <Quotient/uri.h>
|
||||
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
* @class NeochatRoomMember
|
||||
*
|
||||
* This class is a shim around RoomMember that can be safety passed to QML.
|
||||
*
|
||||
* Because RoomMember has an internal pointer to a RoomMemberEvent it is
|
||||
* designed to be created used then quickly discarded as the stste event is changed
|
||||
* every time the member updates. Passing these to QML which will hold onto them
|
||||
* can lead to accessing an already deleted Quotient::RoomMemberEvent relatively easily.
|
||||
*
|
||||
* This class instead holds a member ID and can therefore always safely create and
|
||||
* access Quotient::RoomMember objects while being used as long as needed by QML.
|
||||
*
|
||||
* @note This is only needed to pass to QML if only accessing in CPP RoomMmeber can
|
||||
* be used safely.
|
||||
*
|
||||
* @note The interface is the same as Quotient::RoomMember.
|
||||
*
|
||||
* @sa Quotient::RoomMember
|
||||
*/
|
||||
class NeochatRoomMember : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
Q_PROPERTY(QString id READ id CONSTANT)
|
||||
Q_PROPERTY(Quotient::Uri uri READ uri CONSTANT)
|
||||
Q_PROPERTY(bool isLocalMember READ isLocalMember CONSTANT)
|
||||
Q_PROPERTY(QString displayName READ displayName NOTIFY displayNameUpdated)
|
||||
Q_PROPERTY(QString htmlSafeDisplayName READ htmlSafeDisplayName NOTIFY displayNameUpdated)
|
||||
Q_PROPERTY(QString fullName READ fullName NOTIFY displayNameUpdated)
|
||||
Q_PROPERTY(QString htmlSafeFullName READ htmlSafeFullName NOTIFY displayNameUpdated)
|
||||
Q_PROPERTY(QString disambiguatedName READ disambiguatedName NOTIFY displayNameUpdated)
|
||||
Q_PROPERTY(QString htmlSafeDisambiguatedName READ htmlSafeDisambiguatedName NOTIFY displayNameUpdated)
|
||||
Q_PROPERTY(int hue READ hue CONSTANT)
|
||||
Q_PROPERTY(qreal hueF READ hueF CONSTANT)
|
||||
Q_PROPERTY(QColor color READ color CONSTANT)
|
||||
Q_PROPERTY(QUrl avatarUrl READ avatarUrl NOTIFY avatarUpdated)
|
||||
|
||||
public:
|
||||
NeochatRoomMember() = default;
|
||||
explicit NeochatRoomMember(NeoChatRoom *room, const QString &memberId);
|
||||
|
||||
QString id() const;
|
||||
Quotient::Uri uri() const;
|
||||
bool isLocalMember() const;
|
||||
Quotient::Membership membershipState() const;
|
||||
QString name() const;
|
||||
QString displayName() const;
|
||||
QString htmlSafeDisplayName() const;
|
||||
QString fullName() const;
|
||||
QString htmlSafeFullName() const;
|
||||
QString disambiguatedName() const;
|
||||
QString htmlSafeDisambiguatedName() const;
|
||||
int hue() const;
|
||||
qreal hueF() const;
|
||||
QColor color() const;
|
||||
QString avatarMediaId() const;
|
||||
QUrl avatarUrl() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void displayNameUpdated();
|
||||
void avatarUpdated();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
const QString m_memberId = QString();
|
||||
};
|
||||
@@ -110,6 +110,12 @@ void NotificationsManager::processNotificationJob(QPointer<NeoChatConnection> co
|
||||
}
|
||||
auto sender = room->member(notification["event"_ls]["sender"_ls].toString());
|
||||
|
||||
// Don't display notifications for events in invited rooms
|
||||
// This should prevent empty notifications from appearing when they shouldn't
|
||||
if (room->joinState() == JoinState::Invite) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString body;
|
||||
if (notification["event"_ls]["type"_ls].toString() == "org.matrix.msc3381.poll.start"_ls) {
|
||||
body = notification["event"_ls]["content"_ls]["org.matrix.msc3381.poll.start"_ls]["question"_ls]["body"_ls].toString();
|
||||
@@ -243,7 +249,6 @@ void NotificationsManager::postInviteNotification(NeoChatRoom *rawRoom, const QS
|
||||
notification->setText(i18n("%1 invited you to a room", sender));
|
||||
notification->setTitle(title);
|
||||
notification->setPixmap(createNotificationImage(icon, nullptr));
|
||||
notification->setFlags(KNotification::Persistent);
|
||||
auto defaultAction = notification->addDefaultAction(i18n("Open this invitation in NeoChat"));
|
||||
connect(defaultAction, &KNotificationAction::activated, this, [notification, room]() {
|
||||
if (!room) {
|
||||
|
||||
@@ -17,6 +17,7 @@ Name[eu]=NeoChat
|
||||
Name[fi]=NeoChat
|
||||
Name[fr]=NeoChat
|
||||
Name[gl]=NeoChat
|
||||
Name[he]=NeoChat
|
||||
Name[hu]=NeoChat
|
||||
Name[ia]=Neochat
|
||||
Name[id]=NeoChat
|
||||
@@ -57,6 +58,7 @@ Comment[eu]=Bilatu gelak NeoChat-en
|
||||
Comment[fi]=Etsi huoneita NeoChatissä
|
||||
Comment[fr]=Trouver des salons dans NeoChat
|
||||
Comment[gl]=Atopa salas en NeoChat.
|
||||
Comment[he]=איתור חדרים ב־NeoChat
|
||||
Comment[hu]=Szobák keresése a NeoChatben
|
||||
Comment[ia]=Trova salas in NeoChat
|
||||
Comment[id]=Cari ruangan di NeoChat
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{
|
||||
"Name": "Tobias Fella",
|
||||
"Name[ar]": "توبياس فلة",
|
||||
"Name[he]": "טוביאס פלה",
|
||||
"Name[ta]": "டோபியாஸ் ஃபெல்லா",
|
||||
"Name[x-test]": "xxTobias Fellaxx"
|
||||
}
|
||||
@@ -20,6 +21,7 @@
|
||||
"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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ Components.AbstractMaximizeComponent {
|
||||
/**
|
||||
* @brief The message author.
|
||||
*/
|
||||
property RoomMember author
|
||||
property NeochatRoomMember author
|
||||
|
||||
/**
|
||||
* @brief The timestamp of the message.
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -15,7 +15,10 @@ Kirigami.PlaceholderMessage {
|
||||
required property NeoChatRoom currentRoom
|
||||
|
||||
text: i18n("Accept this invitation?")
|
||||
explanation: root.currentRoom.connection.canCheckMutualRooms ? i18n("You can reject invitations from unknown users under Security settings.") : ""
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
QQC2.Button {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: i18nc("@action:button The thing being rejected is an invitation to chat", "Reject and ignore user")
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -143,12 +143,12 @@ QQC2.ScrollView {
|
||||
footer: Item {
|
||||
z: 3
|
||||
width: root.width
|
||||
visible: !Config.blur
|
||||
visible: !NeoChatConfig.blur
|
||||
|
||||
SectionDelegate {
|
||||
id: sectionDelegate
|
||||
anchors.leftMargin: state === "alignLeft" ? Kirigami.Units.largeSpacing : 0
|
||||
state: Config.compactLayout ? "alignLeft" : "alignCenter"
|
||||
state: NeoChatConfig.compactLayout ? "alignLeft" : "alignCenter"
|
||||
// Align left when in compact mode and center when using bubbles
|
||||
states: [
|
||||
State {
|
||||
@@ -171,7 +171,7 @@ QQC2.ScrollView {
|
||||
|
||||
width: messageListView.sectionBannerItem ? messageListView.sectionBannerItem.contentItem.width : 0
|
||||
labelText: messageListView.sectionBannerItem ? messageListView.sectionBannerItem.ListView.section : ""
|
||||
colorSet: Config.compactLayout ? Kirigami.Theme.View : Kirigami.Theme.Window
|
||||
colorSet: NeoChatConfig.compactLayout ? Kirigami.Theme.View : Kirigami.Theme.Window
|
||||
}
|
||||
}
|
||||
footerPositioning: ListView.OverlayHeader
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatroommember.h"
|
||||
#include "spacehierarchycache.h"
|
||||
#include "urlhelper.h"
|
||||
|
||||
@@ -184,7 +185,7 @@ void RoomManager::maximizeMedia(int index)
|
||||
Q_EMIT showMaximizedMedia(index);
|
||||
}
|
||||
|
||||
void RoomManager::maximizeCode(const RoomMember &author, const QDateTime &time, const QString &codeText, const QString &language)
|
||||
void RoomManager::maximizeCode(NeochatRoomMember *author, const QDateTime &time, const QString &codeText, const QString &language)
|
||||
{
|
||||
if (codeText.isEmpty()) {
|
||||
return;
|
||||
@@ -202,14 +203,14 @@ void RoomManager::viewEventSource(const QString &eventId)
|
||||
Q_EMIT showEventSource(eventId);
|
||||
}
|
||||
|
||||
void RoomManager::viewEventMenu(const QString &eventId, NeoChatRoom *room, const QString &selectedText)
|
||||
void RoomManager::viewEventMenu(const QString &eventId, NeoChatRoom *room, NeochatRoomMember *sender, const QString &selectedText)
|
||||
{
|
||||
const auto &event = **room->findInTimeline(eventId);
|
||||
const auto eventHandler = EventHandler(room, &event);
|
||||
|
||||
if (eventHandler.getMediaInfo().contains("mimeType"_ls)) {
|
||||
Q_EMIT showFileMenu(eventId,
|
||||
eventHandler.getAuthor(),
|
||||
sender,
|
||||
eventHandler.messageComponentType(),
|
||||
eventHandler.getPlainBody(),
|
||||
eventHandler.getMediaInfo()["mimeType"_ls].toString(),
|
||||
@@ -217,12 +218,7 @@ void RoomManager::viewEventMenu(const QString &eventId, NeoChatRoom *room, const
|
||||
return;
|
||||
}
|
||||
|
||||
Q_EMIT showMessageMenu(eventId,
|
||||
eventHandler.getAuthor(),
|
||||
eventHandler.messageComponentType(),
|
||||
eventHandler.getPlainBody(),
|
||||
eventHandler.getRichBody(),
|
||||
selectedText);
|
||||
Q_EMIT showMessageMenu(eventId, sender, eventHandler.messageComponentType(), eventHandler.getPlainBody(), eventHandler.getRichBody(), selectedText);
|
||||
}
|
||||
|
||||
bool RoomManager::hasOpenRoom() const
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "models/sortfilterspacelistmodel.h"
|
||||
#include "models/timelinemodel.h"
|
||||
#include "models/userlistmodel.h"
|
||||
#include "neochatroommember.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
class NeoChatConnection;
|
||||
@@ -216,7 +217,7 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE void maximizeMedia(int index);
|
||||
|
||||
Q_INVOKABLE void maximizeCode(const RoomMember &author, const QDateTime &time, const QString &codeText, const QString &language);
|
||||
Q_INVOKABLE void maximizeCode(NeochatRoomMember *author, const QDateTime &time, const QString &codeText, const QString &language);
|
||||
|
||||
/**
|
||||
* @brief Request that any full screen overlay currently open closes.
|
||||
@@ -231,7 +232,7 @@ public:
|
||||
/**
|
||||
* @brief Show a context menu for the given event.
|
||||
*/
|
||||
Q_INVOKABLE void viewEventMenu(const QString &eventId, NeoChatRoom *room, const QString &selectedText = {});
|
||||
Q_INVOKABLE void viewEventMenu(const QString &eventId, NeoChatRoom *room, NeochatRoomMember *sender, const QString &selectedText = {});
|
||||
|
||||
ChatDocumentHandler *chatDocumentHandler() const;
|
||||
void setChatDocumentHandler(ChatDocumentHandler *handler);
|
||||
@@ -287,7 +288,7 @@ Q_SIGNALS:
|
||||
/**
|
||||
* @brief Request a block of code is shown maximized.
|
||||
*/
|
||||
void showMaximizedCode(const RoomMember &author, const QDateTime &time, const QString &codeText, const QString &language);
|
||||
void showMaximizedCode(NeochatRoomMember *author, const QDateTime &time, const QString &codeText, const QString &language);
|
||||
|
||||
/**
|
||||
* @brief Request that any full screen overlay closes.
|
||||
@@ -303,7 +304,7 @@ Q_SIGNALS:
|
||||
* @brief Request to show a menu for the given event.
|
||||
*/
|
||||
void showMessageMenu(const QString &eventId,
|
||||
const Quotient::RoomMember &author,
|
||||
const NeochatRoomMember *author,
|
||||
MessageComponentType::Type messageComponentType,
|
||||
const QString &plainText,
|
||||
const QString &htmlText,
|
||||
@@ -313,7 +314,7 @@ Q_SIGNALS:
|
||||
* @brief Request to show a menu for the given media event.
|
||||
*/
|
||||
void showFileMenu(const QString &eventId,
|
||||
const Quotient::RoomMember &author,
|
||||
const NeochatRoomMember *author,
|
||||
MessageComponentType::Type messageComponentType,
|
||||
const QString &plainText,
|
||||
const QString &mimeType,
|
||||
|
||||
@@ -98,23 +98,25 @@ FormCard.FormCardPage {
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18n("User information")
|
||||
title: i18n("User Information")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: name
|
||||
label: i18n("Name:")
|
||||
label: i18n("Display Name:")
|
||||
text: root.connection ? root.connection.localUser.displayName : ""
|
||||
}
|
||||
FormCard.FormDelegateSeparator {}
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: accountLabel
|
||||
label: i18n("Label:")
|
||||
placeholderText: i18n("Work")
|
||||
text: root.connection ? root.connection.label : ""
|
||||
}
|
||||
FormCard.FormDelegateSeparator {}
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18nc("@action:button", "QR code for account")
|
||||
text: i18nc("@action:button", "Show QR Code")
|
||||
icon.name: "view-barcode-qr-symbolic"
|
||||
onClicked: {
|
||||
let qrMax = Qt.createComponent('org.kde.neochat', 'QrCodeMaximizeComponent').createObject(QQC2.Overlay.overlay, {
|
||||
text: "https://matrix.to/#/" + root.connection.localUser.id,
|
||||
@@ -131,6 +133,7 @@ FormCard.FormCardPage {
|
||||
FormCard.FormDelegateSeparator {}
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18n("Save")
|
||||
icon.name: "document-save-symbolic"
|
||||
onClicked: {
|
||||
if (!root.connection.setAvatar(avatar.source)) {
|
||||
showPassiveNotification("The Avatar could not be set");
|
||||
@@ -186,6 +189,7 @@ FormCard.FormCardPage {
|
||||
FormCard.FormDelegateSeparator {}
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18n("Save")
|
||||
icon.name: "document-save-symbolic"
|
||||
enabled: currentPassword.text.length > 0 && newPassword.text.length > 0 && confirmPassword.text.length > 0
|
||||
onClicked: {
|
||||
if (newPassword.text === confirmPassword.text) {
|
||||
@@ -202,7 +206,7 @@ FormCard.FormCardPage {
|
||||
medium: "email"
|
||||
}
|
||||
ThreePIdCard {
|
||||
visible: Config.phone3PId
|
||||
visible: NeoChatConfig.phone3PId
|
||||
connection: root.connection
|
||||
title: i18n("Phone Numbers")
|
||||
medium: "msisdn"
|
||||
@@ -249,6 +253,7 @@ FormCard.FormCardPage {
|
||||
FormCard.FormButtonDelegate {
|
||||
id: deactivateAccountButton
|
||||
text: i18n("Deactivate Account")
|
||||
icon.name: "trash-empty-symbolic"
|
||||
onClicked: {
|
||||
const component = Qt.createComponent('org.kde.neochat', 'ConfirmDeactivateAccountDialog');
|
||||
const dialog = component.createObject(QQC2.ApplicationWindow.window, {
|
||||
|
||||
@@ -41,8 +41,8 @@ FormCard.FormCardPage {
|
||||
KirigamiComponents.Avatar {
|
||||
color: "#4a5bcc"
|
||||
Layout.alignment: Qt.AlignTop
|
||||
visible: Config.showAvatarInTimeline
|
||||
Layout.preferredWidth: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing * 2 : 0
|
||||
visible: NeoChatConfig.showAvatarInTimeline
|
||||
Layout.preferredWidth: NeoChatConfig.showAvatarInTimeline ? Kirigami.Units.largeSpacing * 2 : 0
|
||||
Layout.preferredHeight: Kirigami.Units.largeSpacing * 2
|
||||
}
|
||||
QQC2.Control {
|
||||
@@ -78,8 +78,8 @@ FormCard.FormCardPage {
|
||||
KirigamiComponents.Avatar {
|
||||
color: "#9f244b"
|
||||
Layout.alignment: Qt.AlignTop
|
||||
visible: Config.showAvatarInTimeline
|
||||
Layout.preferredWidth: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing * 2 : 0
|
||||
visible: NeoChatConfig.showAvatarInTimeline
|
||||
Layout.preferredWidth: NeoChatConfig.showAvatarInTimeline ? Kirigami.Units.largeSpacing * 2 : 0
|
||||
Layout.preferredHeight: Kirigami.Units.largeSpacing * 2
|
||||
}
|
||||
QQC2.Control {
|
||||
@@ -113,13 +113,13 @@ FormCard.FormCardPage {
|
||||
]
|
||||
|
||||
text: i18n("Bubbles")
|
||||
checked: !Config.compactLayout
|
||||
checked: !NeoChatConfig.compactLayout
|
||||
QQC2.ButtonGroup.group: themeGroup
|
||||
enabled: !Config.isCompactLayoutImmutable
|
||||
enabled: !NeoChatConfig.isCompactLayoutImmutable
|
||||
|
||||
onToggled: {
|
||||
Config.compactLayout = !checked;
|
||||
Config.save();
|
||||
NeoChatConfig.compactLayout = !checked;
|
||||
NeoChatConfig.save();
|
||||
}
|
||||
}
|
||||
ThemeRadioButton {
|
||||
@@ -131,8 +131,8 @@ FormCard.FormCardPage {
|
||||
KirigamiComponents.Avatar {
|
||||
color: "#4a5bcc"
|
||||
Layout.alignment: Qt.AlignTop
|
||||
visible: Config.showAvatarInTimeline
|
||||
Layout.preferredWidth: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing * 2 : 0
|
||||
visible: NeoChatConfig.showAvatarInTimeline
|
||||
Layout.preferredWidth: NeoChatConfig.showAvatarInTimeline ? Kirigami.Units.largeSpacing * 2 : 0
|
||||
Layout.preferredHeight: Kirigami.Units.largeSpacing * 2
|
||||
}
|
||||
ColumnLayout {
|
||||
@@ -158,8 +158,8 @@ FormCard.FormCardPage {
|
||||
KirigamiComponents.Avatar {
|
||||
color: "#9f244b"
|
||||
Layout.alignment: Qt.AlignTop
|
||||
visible: Config.showAvatarInTimeline
|
||||
Layout.preferredWidth: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing * 2 : 0
|
||||
visible: NeoChatConfig.showAvatarInTimeline
|
||||
Layout.preferredWidth: NeoChatConfig.showAvatarInTimeline ? Kirigami.Units.largeSpacing * 2 : 0
|
||||
Layout.preferredHeight: Kirigami.Units.largeSpacing * 2
|
||||
}
|
||||
ColumnLayout {
|
||||
@@ -182,13 +182,13 @@ FormCard.FormCardPage {
|
||||
}
|
||||
]
|
||||
text: i18n("Compact")
|
||||
checked: Config.compactLayout
|
||||
checked: NeoChatConfig.compactLayout
|
||||
QQC2.ButtonGroup.group: themeGroup
|
||||
enabled: !Config.isCompactLayoutImmutable
|
||||
enabled: !NeoChatConfig.isCompactLayoutImmutable
|
||||
|
||||
onToggled: {
|
||||
Config.compactLayout = checked;
|
||||
Config.save();
|
||||
NeoChatConfig.compactLayout = checked;
|
||||
NeoChatConfig.save();
|
||||
}
|
||||
}
|
||||
Item {
|
||||
@@ -204,10 +204,10 @@ FormCard.FormCardPage {
|
||||
FormCard.FormCheckDelegate {
|
||||
id: compactRoomListDelegate
|
||||
text: i18n("Use compact room list")
|
||||
checked: Config.compactRoomList
|
||||
checked: NeoChatConfig.compactRoomList
|
||||
onToggled: {
|
||||
Config.compactRoomList = checked;
|
||||
Config.save();
|
||||
NeoChatConfig.compactRoomList = checked;
|
||||
NeoChatConfig.save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,11 +231,11 @@ FormCard.FormCardPage {
|
||||
id: hasWindowSystemDelegate
|
||||
visible: WindowController.hasWindowSystem
|
||||
text: i18n("Use transparent chat page")
|
||||
enabled: !Config.compactLayout && !Config.isBlurImmutable
|
||||
checked: Config.blur
|
||||
enabled: !NeoChatConfig.compactLayout && !NeoChatConfig.isBlurImmutable
|
||||
checked: NeoChatConfig.blur
|
||||
onToggled: {
|
||||
Config.blur = checked;
|
||||
Config.save();
|
||||
NeoChatConfig.blur = checked;
|
||||
NeoChatConfig.save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,8 +246,8 @@ FormCard.FormCardPage {
|
||||
|
||||
FormCard.AbstractFormDelegate {
|
||||
id: transparencyDelegate
|
||||
visible: WindowController.hasWindowSystem && Config.blur
|
||||
enabled: !Config.isTransparancyImmutable
|
||||
visible: WindowController.hasWindowSystem && NeoChatConfig.blur
|
||||
enabled: !NeoChatConfig.isTransparancyImmutable
|
||||
background: Item {}
|
||||
contentItem: ColumnLayout {
|
||||
QQC2.Label {
|
||||
@@ -255,14 +255,14 @@ FormCard.FormCardPage {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.Slider {
|
||||
enabled: !Config.compactLayout && Config.blur
|
||||
enabled: !NeoChatConfig.compactLayout && NeoChatConfig.blur
|
||||
from: 0
|
||||
to: 1
|
||||
stepSize: 0.05
|
||||
value: Config.transparency
|
||||
value: NeoChatConfig.transparency
|
||||
onMoved: {
|
||||
Config.transparency = value;
|
||||
Config.save();
|
||||
NeoChatConfig.transparency = value;
|
||||
NeoChatConfig.save();
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -273,7 +273,7 @@ FormCard.FormCardPage {
|
||||
QQC2.ToolTip.text: i18n("Only enabled if the transparent chat page is enabled.")
|
||||
}
|
||||
QQC2.Label {
|
||||
text: Math.round(Config.transparency * 100) + "%"
|
||||
text: Math.round(NeoChatConfig.transparency * 100) + "%"
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
@@ -288,11 +288,11 @@ FormCard.FormCardPage {
|
||||
FormCard.FormCheckDelegate {
|
||||
id: showLocalMessagesOnRightDelegate
|
||||
text: i18n("Show your messages on the right")
|
||||
checked: Config.showLocalMessagesOnRight
|
||||
enabled: !Config.isShowLocalMessagesOnRightImmutable && !Config.compactLayout
|
||||
checked: NeoChatConfig.showLocalMessagesOnRight
|
||||
enabled: !NeoChatConfig.isShowLocalMessagesOnRightImmutable && !NeoChatConfig.compactLayout
|
||||
onToggled: {
|
||||
Config.showLocalMessagesOnRight = checked;
|
||||
Config.save();
|
||||
NeoChatConfig.showLocalMessagesOnRight = checked;
|
||||
NeoChatConfig.save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,10 +304,10 @@ FormCard.FormCardPage {
|
||||
FormCard.FormCheckDelegate {
|
||||
id: showLinkPreviewDelegate
|
||||
text: i18n("Show links preview in the chat messages")
|
||||
checked: Config.showLinkPreview
|
||||
checked: NeoChatConfig.showLinkPreview
|
||||
onToggled: {
|
||||
Config.showLinkPreview = checked;
|
||||
Config.save();
|
||||
NeoChatConfig.showLinkPreview = checked;
|
||||
NeoChatConfig.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -318,21 +318,21 @@ FormCard.FormCardPage {
|
||||
FormCard.FormCard {
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18n("In chat")
|
||||
checked: Config.showAvatarInTimeline
|
||||
checked: NeoChatConfig.showAvatarInTimeline
|
||||
onToggled: {
|
||||
Config.showAvatarInTimeline = checked;
|
||||
Config.save();
|
||||
NeoChatConfig.showAvatarInTimeline = checked;
|
||||
NeoChatConfig.save();
|
||||
}
|
||||
enabled: !Config.isShowAvatarInTimelineImmutable
|
||||
enabled: !NeoChatConfig.isShowAvatarInTimelineImmutable
|
||||
}
|
||||
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18n("In sidebar")
|
||||
checked: Config.showAvatarInRoomDrawer
|
||||
enabled: !Config.isShowAvatarInRoomDrawerImmutable
|
||||
checked: NeoChatConfig.showAvatarInRoomDrawer
|
||||
enabled: !NeoChatConfig.isShowAvatarInRoomDrawerImmutable
|
||||
onToggled: {
|
||||
Config.showAvatarInRoomDrawer = checked;
|
||||
Config.save();
|
||||
NeoChatConfig.showAvatarInRoomDrawer = checked;
|
||||
NeoChatConfig.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user