Compare commits
39 Commits
work/tobia
...
work/tobia
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
882ead5715 | ||
|
|
ab4519dedd | ||
|
|
c3fd2428a2 | ||
|
|
fbb4b962fa | ||
|
|
9bf65de649 | ||
|
|
75f069cb7d | ||
|
|
87d50125ab | ||
|
|
dc2cf21cb8 | ||
|
|
bae14ecd35 | ||
|
|
b48c1c3b80 | ||
|
|
0f9eb4beeb | ||
|
|
0ab8624d79 | ||
|
|
e872c934c3 | ||
|
|
c3d5d18aae | ||
|
|
ff5853a850 | ||
|
|
f772906324 | ||
|
|
07eabb2dc1 | ||
|
|
b3c88763a4 | ||
|
|
f7081f8829 | ||
|
|
ceef2167fd | ||
|
|
1dcfd94328 | ||
|
|
a1aa0804e2 | ||
|
|
77176478eb | ||
|
|
bf4ebfa7a8 | ||
|
|
6e7d622b41 | ||
|
|
8398b7d24d | ||
|
|
aef9b7375a | ||
|
|
ba45318b56 | ||
|
|
7d4f8780ad | ||
|
|
b504c990f8 | ||
|
|
e07b876677 | ||
|
|
a0bfd34951 | ||
|
|
b173714bbe | ||
|
|
db4021b601 | ||
|
|
2f46fd1d2c | ||
|
|
1f85f848e2 | ||
|
|
d7e0954e86 | ||
|
|
1671e05d12 | ||
|
|
33c55d1563 |
@@ -85,6 +85,7 @@ if(ANDROID)
|
||||
else()
|
||||
find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Widgets)
|
||||
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem)
|
||||
find_package(KF6SyntaxHighlighting ${KF_MIN_VERSION} REQUIRED)
|
||||
set_package_properties(KF6QQC2DesktopStyle PROPERTIES
|
||||
TYPE RUNTIME
|
||||
)
|
||||
|
||||
@@ -529,6 +529,19 @@ void TextHandlerTest::componentOutput_data()
|
||||
QTest::newRow("inline code single block") << QStringLiteral("<code>https://kde.org</code>")
|
||||
<< QList<MessageComponent>{
|
||||
MessageComponent{MessageComponentType::Text, QStringLiteral("<code>https://kde.org</code>"), {}}};
|
||||
QTest::newRow("long start tag")
|
||||
<< QStringLiteral(
|
||||
"Ah, you mean something like<br/><pre data-md=\"```\"><code class=\"language-qml\"># main.qml\nimport CustomQml\n...\nControls.TextField { id: "
|
||||
"someField }\nCustomQml {\n someTextProperty: someField.text\n}\n</code></pre>Sure you can, it's still local to the same file where you "
|
||||
"defined the id")
|
||||
<< QList<MessageComponent>{
|
||||
MessageComponent{MessageComponentType::Text, QStringLiteral("Ah, you mean something like"), {}},
|
||||
MessageComponent{
|
||||
MessageComponentType::Code,
|
||||
QStringLiteral(
|
||||
"# main.qml\nimport CustomQml\n...\nControls.TextField { id: someField }\nCustomQml {\n someTextProperty: someField.text\n}"),
|
||||
QVariantMap{{QStringLiteral("class"), QStringLiteral("qml")}}},
|
||||
MessageComponent{MessageComponentType::Text, QStringLiteral("Sure you can, it's still local to the same file where you defined the id"), {}}};
|
||||
}
|
||||
|
||||
void TextHandlerTest::componentOutput()
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<name xml:lang="it">NeoChat</name>
|
||||
<name xml:lang="ka">NeoChat</name>
|
||||
<name xml:lang="ko">NeoChat</name>
|
||||
<name xml:lang="lv">NeoChat</name>
|
||||
<name xml:lang="nl">NeoChat</name>
|
||||
<name xml:lang="nn">NeoChat</name>
|
||||
<name xml:lang="pa">ਨਿਓ-ਚੈਟ</name>
|
||||
@@ -64,6 +65,7 @@
|
||||
<summary xml:lang="it">Conversa con i tuoi contatti su matrix</summary>
|
||||
<summary xml:lang="ka">ესაუბრეთ მეგობრებს Matrix-ზე</summary>
|
||||
<summary xml:lang="ko">Matrix를 사용하여 친구들과 대화하기</summary>
|
||||
<summary xml:lang="lv">Tērzējiet ar saviem draugiem „Matrix“ tīklā</summary>
|
||||
<summary xml:lang="nl">Met uw vrienden chatten op matrix</summary>
|
||||
<summary xml:lang="nn">Prat med vennar på Matrix</summary>
|
||||
<summary xml:lang="pl">Rozmawiaj ze swoimi znajomymi w Matriksie</summary>
|
||||
@@ -87,6 +89,7 @@
|
||||
<p xml:lang="ia">NeoChat es un app de conversation que te permitte prender avantage plen del rete Matrix. Il te forni un modo secur de inviar messages de texto, videos e files audio a tui familia, collegas e amicos.</p>
|
||||
<p xml:lang="it">NeoChat è un'applicazione di chat che ti consente di sfruttare appieno la rete Matrix. Ti fornisce un modo sicuro per inviare messaggi di testo, video e file audio a familiari, colleghi e amici.</p>
|
||||
<p xml:lang="ka">NeoChat ჩატის აპია, რომელიც საშუალება გაძლევთ, Matrix-ის ქსელის საშუალებები ბოლომდე გამოიყენოთ. ის გაძლევთ უსაფრთხო გზას, გააგზავნოთ ტექსტური შეტყობინებები, ვიდეოებ და აუდიოფაილები თქვენს ოჯახთან, კოლეგებთან და მეგობრებთან.</p>
|
||||
<p xml:lang="lv">„NeoChat“ ir tērzēšanas programma, kas ļauj pilnvērtīgi izmantot „Matrix“ tīklu. Tā sniedz drošu veidu teksta ziņu, video un audio sūtīšanai ģimenes locekļiem, kolēģiem un draugiem.</p>
|
||||
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
|
||||
<p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p>
|
||||
<p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p>
|
||||
@@ -110,6 +113,7 @@
|
||||
<p xml:lang="it">NeoChat mira ad essere un'applicazione completa per le specifiche Matrix. Pertanto, sono supportati tutti gli elementi dell'attuale specifica stabile con le notevoli eccezioni di VoIP, conversazioni e alcuni aspetti della cifratura end-to-end. Ci sono alcune altre piccole omissioni dovute al fatto che le specifiche Matrix sono in continua evoluzione, ma l'obiettivo rimane quello di fornire un eventuale supporto per l'intera specifica.</p>
|
||||
<p xml:lang="ka">NeoChat მიზნად ისახავს Matrix სპეციფიკაციის სრული განხორციელება ჰქონდეს. როგორც ასეთი, ყველაფერი მიმდინარე სპეციფიკაციიდან, VoIP-ის, ძაფებისა და გამჭოლი დაშიფვრის ზოგიერთი ასპექტის გარდა, მხარდაჭერილია. შეძლება ასევე იყოს მცირე ლაფსუსებიც იმის გამო, რომ Matrix-ის სპეციფიკაცია მუდმივად ვითარდება, მაგრამ ჩვენი მიზანი მისი სრული მხარდაჭერაა.</p>
|
||||
<p xml:lang="ko">NeoChat은 Matrix 표준을 따르는 프로그램을 목표로 합니다. 현재 안정 버전의 표준에서 제공하는 기능의 대부분을 지원하며, VoIP, 스레드, 일부 종단간 암호화와 같은 기능은 아직 지원하지 않습니다. Matrix 표준은 계속하여 진화 중이기 때문에 일부 기능이 빠져 있을 수도 있지만 장기적으로는 전체 표준을 지원하는 것이 목표입니다.</p>
|
||||
<p xml:lang="lv">„NeoChat“ mērķis ir piedāvāt plašas iespējas atbilstoši „Matrix“ specifikācijai. Līdz ar to programma atbalsta visu pašreizējā stabilajā specifikācijā, izņemot VoIP, pavedienus un dažos aspektos galšifrēšanu. Pastāv citas atsevišķas sīkas neieviestas daļas, jo „Matrix“ specifikācija nepārtraukti attīstās, tomēr mērķis ir ar laiku nodrošināt atbalstu pilnai specifikācijai.</p>
|
||||
<p xml:lang="nl">NeoChat richt zich op het volledig bieden van alle mogelijkheden van de Matrix-specificatie. Alles in de huidige stabiele specificatie met merkbare uitzondering van VoIP, gekoppelde discussies en sommige aspecten van eind-tot-eind versleuteling worden ondersteund. Er zijn een paar andere kleinere omissies vanwege het feit dat de Matrix specificatie constant evolueert maar het doel blijft het eventueel bieden van ondersteuning van de gehele specificatie.</p>
|
||||
<p xml:lang="nn">NeoChat har som mål å støtta all funksjonalitet i Matrix-spesifikasjonen. Førebels er alt i den gjeldande stabile spesifikasjonen støtta, med unntak av VoIP, trådar og nokre delar av ende-til-kryptering. Det finst òg andre småting som ikkje er støtta, sidan Matrix-spesifikasjon er i stadig endring, men målet er altså støtte for alt.</p>
|
||||
<p xml:lang="pl">NeoChat w zamyśle ma być pełnowartościową aplikacją wg wytycznych Matriksa. Z tego powodu, wszystko, co jest obecnie w stabilnych wytycznych z pominięciem VoIP, wątków i niektórych części szyfrowania Użytkownik-do-Użytkownika są obecnie obsługiwane. Pominięto też kilka mniejszych rzeczy ze względu na ciągły rozwój wytycznych Matriksa, lecz celem nadal jest zapewnienie obsługi wszystkich wytycznych.</p>
|
||||
@@ -136,6 +140,7 @@
|
||||
<p xml:lang="it">A causa della natura dello sviluppo delle specifiche Matrix, NeoChat supporta anche numerose funzionalità instabili. Attualmente queste sono:</p>
|
||||
<p xml:lang="ka">Matrix-ის სპეციფიკაციის განვითარების ბუნების გამო NeoChat-ს ასევე აქვს უამრავი არასტაბილური ფუნქციაც. ახლა ისინია:</p>
|
||||
<p xml:lang="ko">Matrix 표준 개발의 특징으로 인하여 NeoChat은 일부 실험적인 기능을 지원합니다. 현재 지원하는 기능은 다음과 같습니다.</p>
|
||||
<p xml:lang="lv">„Matrix“ specifikācijas veida dēļ „NeoChat“ attīstība atbalsta arī vairākas nestabilas iespējas, šobrīd šādas ir:</p>
|
||||
<p xml:lang="nl">Vanwege de aard van de ontwikkeling van de Matrix specificatie ondersteunt NeoChat ook talloze onstabiele mogelijkheden. Dit zijn nu:</p>
|
||||
<p xml:lang="nn">På grunn av måten Matrix-spesifikasjonen vert utvikla på, støttar NeoChat òg nokre uferdige funksjonar:</p>
|
||||
<p xml:lang="pl">Ze względu na sposób rozwoju Matriksa, NeoChat obsługuje także kilka niestabilnych możliwości. Obecnie są to:</p>
|
||||
@@ -164,6 +169,7 @@
|
||||
<li xml:lang="it">Sondaggi - MSC3381</li>
|
||||
<li xml:lang="ka">Polls - MSC3381</li>
|
||||
<li xml:lang="ko">투표 - MSC3381</li>
|
||||
<li xml:lang="lv">Aptaujas — MSC3381</li>
|
||||
<li xml:lang="nl">Polls - MSC3381</li>
|
||||
<li xml:lang="nn">Avstemmingar – MSC3381</li>
|
||||
<li xml:lang="pl">Ankiety - MSC3381</li>
|
||||
@@ -191,6 +197,7 @@
|
||||
<li xml:lang="it">Pacchetti di adesivi - MSC2545</li>
|
||||
<li xml:lang="ka">სტიკერების პაკეტები - MSC2545</li>
|
||||
<li xml:lang="ko">스티커 팩 - MSC2545</li>
|
||||
<li xml:lang="lv">Uzlīmju pakas — MSC2545</li>
|
||||
<li xml:lang="nl">Sticker Packs - MSC2545</li>
|
||||
<li xml:lang="nn">Klistremerke-pakkar – MSC2545</li>
|
||||
<li xml:lang="pl">Paczki naklejek - MSC2545</li>
|
||||
@@ -218,6 +225,7 @@
|
||||
<li xml:lang="it">Località eventi - MSC3488</li>
|
||||
<li xml:lang="ka">მდებარეობის მოვლენები - MSC3488</li>
|
||||
<li xml:lang="ko">위치 이벤트 - MSC3488</li>
|
||||
<li xml:lang="lv">Atrašanās vietas notikumi — MSC3488</li>
|
||||
<li xml:lang="nl">Locatie gebeurtenissen - MSC3488</li>
|
||||
<li xml:lang="nn">Posisjonshendingar – MSC3488</li>
|
||||
<li xml:lang="pl">Wydarzenia w miejscach - MSC3488</li>
|
||||
@@ -278,6 +286,7 @@
|
||||
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
|
||||
<caption xml:lang="ka">მთავარი ხედი სურათების სიით, ჩატით და ოთახის ინფორმაციით</caption>
|
||||
<caption xml:lang="ko">대화방 목록, 채팅, 대화방 정보가 표시된 주 보기</caption>
|
||||
<caption xml:lang="lv">Pamata skats ar istabu sarakstu, tērzēšanu un istabas informāciju</caption>
|
||||
<caption xml:lang="nl">Hoofdweergave met lijst met rooms, chat en roominformatie</caption>
|
||||
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
|
||||
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
|
||||
@@ -303,6 +312,7 @@
|
||||
<caption xml:lang="ia">Discoperi nove communitate con Matrix Spaces (Spatios de Matrix)</caption>
|
||||
<caption xml:lang="it">Scopri nuove comunità con Matrix Spaces</caption>
|
||||
<caption xml:lang="ka">აღმოაჩინეთ ახალი საზოგადოებები Matrix Spaces-თან ერთად</caption>
|
||||
<caption xml:lang="lv">Atklājiet jaunas kopienas ar „Matrix“ telpām</caption>
|
||||
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
|
||||
<caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption>
|
||||
<caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption>
|
||||
@@ -335,6 +345,7 @@
|
||||
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
|
||||
<caption xml:lang="ka">მთავარი ხედი სურათების სიით, ჩატით და ოთახის ინფორმაციით</caption>
|
||||
<caption xml:lang="ko">대화방 목록, 채팅, 대화방 정보가 표시된 주 보기</caption>
|
||||
<caption xml:lang="lv">Pamata skats ar istabu sarakstu, tērzēšanu un istabas informāciju</caption>
|
||||
<caption xml:lang="nl">Hoofdweergave met lijst met rooms, chat en roominformatie</caption>
|
||||
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
|
||||
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
|
||||
@@ -365,6 +376,7 @@
|
||||
<caption xml:lang="it">Schermata di accesso</caption>
|
||||
<caption xml:lang="ka">შესვლის ეკრანი</caption>
|
||||
<caption xml:lang="ko">로그인 화면</caption>
|
||||
<caption xml:lang="lv">Ierakstīšanās logs</caption>
|
||||
<caption xml:lang="nl">Aanmeldscherm</caption>
|
||||
<caption xml:lang="nn">Innloggingsbilete</caption>
|
||||
<caption xml:lang="pl">Ekran logowania</caption>
|
||||
|
||||
@@ -26,6 +26,7 @@ Name[it]=NeoChat
|
||||
Name[ka]=NeoChat
|
||||
Name[ko]=NeoChat
|
||||
Name[lt]=NeoChat
|
||||
Name[lv]=NeoChat
|
||||
Name[nl]=NeoChat
|
||||
Name[nn]=NeoChat
|
||||
Name[pa]=ਨਿਓ-ਚੈਟ
|
||||
@@ -66,6 +67,7 @@ GenericName[it]=Client Matrix
|
||||
GenericName[ka]=Matrix -ის კლიენტი
|
||||
GenericName[ko]=Matrix 클라이언트
|
||||
GenericName[lt]=Matrix kliento programa
|
||||
GenericName[lv]=„Matrix“ klients
|
||||
GenericName[nl]=Matrix-client
|
||||
GenericName[nn]=Matrix-klient
|
||||
GenericName[pa]=ਮੈਟਰਿਕਸ ਕਲਾਈਂਟ
|
||||
@@ -105,6 +107,7 @@ Comment[it]=Client per il protocollo Matrix
|
||||
Comment[ka]=კლიენტი Matrix-ის პროტოკოლისთვის
|
||||
Comment[ko]=Matrix 프로토콜용 클라이언트
|
||||
Comment[lt]=Matrix protokolo kliento programa
|
||||
Comment[lv]=Klients „Matrix“ protokolam
|
||||
Comment[nl]=Client voor het Matrix-protocol
|
||||
Comment[nn]=Klient for Matrix-protokollen
|
||||
Comment[pa]=ਮੈਟਰਿਕਸ ਪਰੋਟੋਕਾਲ ਲਈ ਕਲਾਈਂਟ ਹੈ
|
||||
|
||||
3030
po/ar/neochat.po
3030
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
2934
po/ast/neochat.po
2934
po/ast/neochat.po
File diff suppressed because it is too large
Load Diff
3220
po/az/neochat.po
3220
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
3028
po/ca/neochat.po
3028
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2962
po/cs/neochat.po
2962
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
3043
po/da/neochat.po
3043
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
3104
po/de/neochat.po
3104
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
3114
po/el/neochat.po
3114
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
3056
po/en_GB/neochat.po
3056
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
3000
po/eo/neochat.po
3000
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
3026
po/es/neochat.po
3026
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
3005
po/eu/neochat.po
3005
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
3005
po/fi/neochat.po
3005
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
3041
po/fr/neochat.po
3041
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
3009
po/hu/neochat.po
3009
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
3006
po/ia/neochat.po
3006
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
3074
po/id/neochat.po
3074
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
3115
po/ie/neochat.po
3115
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
3036
po/it/neochat.po
3036
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
2934
po/ja/neochat.po
2934
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
2996
po/ka/neochat.po
2996
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
2991
po/ko/neochat.po
2991
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
2934
po/lt/neochat.po
2934
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
5026
po/lv/neochat.po
Normal file
5026
po/lv/neochat.po
Normal file
File diff suppressed because it is too large
Load Diff
3015
po/nl/neochat.po
3015
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
3046
po/nn/neochat.po
3046
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
3178
po/pa/neochat.po
3178
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
3004
po/pl/neochat.po
3004
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
3068
po/pt/neochat.po
3068
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
3220
po/pt_BR/neochat.po
3220
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
3061
po/ru/neochat.po
3061
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
3186
po/sk/neochat.po
3186
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
3001
po/sl/neochat.po
3001
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
3048
po/sv/neochat.po
3048
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
2987
po/ta/neochat.po
2987
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
3019
po/tok/neochat.po
3019
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
3000
po/tr/neochat.po
3000
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
3017
po/uk/neochat.po
3017
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
2953
po/zh_CN/neochat.po
2953
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
2969
po/zh_TW/neochat.po
2969
po/zh_TW/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -126,6 +126,7 @@ add_library(neochat STATIC
|
||||
events/pollevent.cpp
|
||||
pollhandler.cpp
|
||||
utils.h
|
||||
utils.cpp
|
||||
registration.cpp
|
||||
neochatconnection.cpp
|
||||
neochatconnection.h
|
||||
@@ -187,18 +188,12 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/UserInfo.qml
|
||||
qml/UserInfoDesktop.qml
|
||||
qml/RoomPage.qml
|
||||
qml/RoomWindow.qml
|
||||
qml/ExploreRoomsPage.qml
|
||||
qml/ManualRoomDialog.qml
|
||||
qml/ExplorerDelegate.qml
|
||||
qml/InviteUserPage.qml
|
||||
qml/ImageEditorPage.qml
|
||||
qml/WelcomePage.qml
|
||||
qml/General.qml
|
||||
qml/RoomSecurity.qml
|
||||
qml/PushNotification.qml
|
||||
qml/Categories.qml
|
||||
qml/Permissions.qml
|
||||
qml/NeochatMaximizeComponent.qml
|
||||
qml/FancyEffectsContainer.qml
|
||||
qml/TypingPane.qml
|
||||
@@ -245,25 +240,6 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/FileDelegateContextMenu.qml
|
||||
qml/MessageSourceSheet.qml
|
||||
qml/ReportSheet.qml
|
||||
qml/SettingsPage.qml
|
||||
qml/ThemeRadioButton.qml
|
||||
qml/ColorScheme.qml
|
||||
qml/GeneralSettingsPage.qml
|
||||
qml/EmoticonsPage.qml
|
||||
qml/EmoticonEditorPage.qml
|
||||
qml/EmoticonFormCard.qml
|
||||
qml/GlobalNotificationsPage.qml
|
||||
qml/NotificationRuleItem.qml
|
||||
qml/AppearanceSettingsPage.qml
|
||||
qml/AccountsPage.qml
|
||||
qml/AccountEditorPage.qml
|
||||
qml/DevicesPage.qml
|
||||
qml/DeviceDelegate.qml
|
||||
qml/DevicesCard.qml
|
||||
qml/About.qml
|
||||
qml/AboutKDE.qml
|
||||
qml/SonnetConfigPage.qml
|
||||
qml/NetworkProxyPage.qml
|
||||
qml/DevtoolsPage.qml
|
||||
qml/ConfirmEncryptionDialog.qml
|
||||
qml/RemoveSheet.qml
|
||||
@@ -292,7 +268,6 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/SpaceHierarchyDelegate.qml
|
||||
qml/RemoveChildDialog.qml
|
||||
qml/SelectParentDialog.qml
|
||||
qml/Security.qml
|
||||
qml/QrCodeMaximizeComponent.qml
|
||||
qml/SelectSpacesDialog.qml
|
||||
qml/AttachDialog.qml
|
||||
@@ -306,14 +281,18 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/DelegateContextMenu.qml
|
||||
qml/ShareDialog.qml
|
||||
qml/FeatureFlagPage.qml
|
||||
qml/IgnoredUsersDialog.qml
|
||||
qml/AccountData.qml
|
||||
qml/StateKeys.qml
|
||||
qml/UnlockSSSSDialog.qml
|
||||
qml/QrScannerPage.qml
|
||||
qml/JoinRoomDialog.qml
|
||||
qml/ConfirmUrlDialog.qml
|
||||
RESOURCES
|
||||
qml/confetti.png
|
||||
qml/glowdot.png
|
||||
)
|
||||
|
||||
add_subdirectory(settings)
|
||||
add_subdirectory(timeline)
|
||||
|
||||
if(UNIX)
|
||||
@@ -406,7 +385,7 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||
endif()
|
||||
|
||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
|
||||
target_link_libraries(neochat PRIVATE timelineplugin)
|
||||
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin)
|
||||
target_link_libraries(neochat PUBLIC
|
||||
Qt::Core
|
||||
Qt::Quick
|
||||
@@ -532,7 +511,7 @@ if(ANDROID)
|
||||
)
|
||||
ecm_add_android_apk(neochat-app ANDROID_DIR ${CMAKE_SOURCE_DIR}/android)
|
||||
else()
|
||||
target_link_libraries(neochat PUBLIC Qt::Widgets KF6::KIOWidgets)
|
||||
target_link_libraries(neochat PUBLIC Qt::Widgets KF6::KIOWidgets KF6::SyntaxHighlighting)
|
||||
install(FILES neochat.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
|
||||
endif()
|
||||
|
||||
|
||||
@@ -156,6 +156,15 @@ void Controller::addConnection(NeoChatConnection *c)
|
||||
c->saveState();
|
||||
});
|
||||
connect(c, &NeoChatConnection::loggedOut, this, [this, c] {
|
||||
if (accounts().count() > 1) {
|
||||
// Only set the connection if the the account being logged out is currently active
|
||||
if (c == activeConnection()) {
|
||||
setActiveConnection(dynamic_cast<NeoChatConnection *>(accounts().accounts()[0]));
|
||||
}
|
||||
} else {
|
||||
setActiveConnection(nullptr);
|
||||
}
|
||||
|
||||
dropConnection(c);
|
||||
});
|
||||
connect(c, &NeoChatConnection::badgeNotificationCountChanged, this, &Controller::updateBadgeNotificationCount);
|
||||
@@ -377,6 +386,14 @@ AccountRegistry &Controller::accounts()
|
||||
return m_accountRegistry;
|
||||
}
|
||||
|
||||
QString Controller::loadFileContent(const QString &path) const
|
||||
{
|
||||
QUrl url(path);
|
||||
QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString());
|
||||
file.open(QFile::ReadOnly);
|
||||
return QString::fromLatin1(file.readAll());
|
||||
}
|
||||
|
||||
#include "moc_controller.cpp"
|
||||
|
||||
void Controller::setTestMode(bool test)
|
||||
@@ -393,3 +410,12 @@ void Controller::removeConnection(const QString &userId)
|
||||
SettingsGroup("Accounts"_ls).remove(userId);
|
||||
}
|
||||
}
|
||||
|
||||
bool Controller::ssssSupported() const
|
||||
{
|
||||
#if __has_include("Quotient/e2ee/sssshandler.h")
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ class Controller : public QObject
|
||||
|
||||
Q_PROPERTY(QStringList accountsLoading MEMBER m_accountsLoading NOTIFY accountsLoadingChanged)
|
||||
|
||||
Q_PROPERTY(bool ssssSupported READ ssssSupported CONSTANT)
|
||||
|
||||
public:
|
||||
static Controller &instance();
|
||||
static Controller *create(QQmlEngine *engine, QJSEngine *)
|
||||
@@ -92,12 +94,16 @@ public:
|
||||
*/
|
||||
static void listenForNotifications();
|
||||
|
||||
Q_INVOKABLE QString loadFileContent(const QString &path) const;
|
||||
|
||||
Quotient::AccountRegistry &accounts();
|
||||
|
||||
static void setTestMode(bool testMode);
|
||||
|
||||
Q_INVOKABLE void removeConnection(const QString &userId);
|
||||
|
||||
bool ssssSupported() const;
|
||||
|
||||
private:
|
||||
explicit Controller(QObject *parent = nullptr);
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ public:
|
||||
Code, /**< A code section. */
|
||||
Quote, /**< A quote section. */
|
||||
File, /**< A message that is a file. */
|
||||
Itinerary, /**< A preview for a file that can integrate with KDE itinerary.. */
|
||||
Itinerary, /**< A preview for a file that can integrate with KDE itinerary. */
|
||||
Pdf, /**< A preview for a PDF file. */
|
||||
Poll, /**< The initial event for a poll. */
|
||||
Location, /**< A location event. */
|
||||
LiveLocation, /**< The initial event of a shared live location (i.e., the place where this is supposed to be shown in the timeline). */
|
||||
@@ -50,6 +51,7 @@ public:
|
||||
LinkPreview, /**< A preview of a URL in the message. */
|
||||
LinkPreviewLoad, /**< A loading dialog for a link preview. */
|
||||
Edit, /**< A text edit for editing a message. */
|
||||
Verification, /**< A user verification session start message. */
|
||||
Other, /**< Anything that cannot be classified as another type. */
|
||||
};
|
||||
Q_ENUM(Type);
|
||||
|
||||
@@ -656,6 +656,7 @@ QVariantMap EventHandler::getMediaInfoForEvent(const Quotient::RoomEvent *event)
|
||||
|
||||
// Get the file info for the event.
|
||||
const EventContent::FileInfo *fileInfo;
|
||||
bool isSticker = false;
|
||||
if (event->is<RoomMessageEvent>()) {
|
||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event);
|
||||
if (!roomMessageEvent->hasFileContent()) {
|
||||
@@ -665,14 +666,15 @@ QVariantMap EventHandler::getMediaInfoForEvent(const Quotient::RoomEvent *event)
|
||||
} else if (event->is<StickerEvent>()) {
|
||||
auto stickerEvent = eventCast<const StickerEvent>(event);
|
||||
fileInfo = &stickerEvent->image();
|
||||
isSticker = true;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
return getMediaInfoFromFileInfo(fileInfo, eventId);
|
||||
return getMediaInfoFromFileInfo(fileInfo, eventId, false, isSticker);
|
||||
}
|
||||
|
||||
QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail) const
|
||||
QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail, bool isSticker) const
|
||||
{
|
||||
QVariantMap mediaInfo;
|
||||
|
||||
@@ -699,6 +701,8 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo
|
||||
// Add media size if available.
|
||||
mediaInfo["size"_ls] = fileInfo->payloadSize;
|
||||
|
||||
mediaInfo["isSticker"_ls] = isSticker;
|
||||
|
||||
// Add parameter depending on media type.
|
||||
if (mimeType.name().contains(QStringLiteral("image"))) {
|
||||
if (auto castInfo = static_cast<const EventContent::ImageContent *>(fileInfo)) {
|
||||
|
||||
@@ -229,6 +229,7 @@ public:
|
||||
* - width - The width in pixels of the audio media.
|
||||
* - height - The height in pixels of the audio media.
|
||||
* - tempInfo - mediaInfo (with the same properties as this except no tempInfo) for a temporary image while the file downloads.
|
||||
* - isSticker - Whether the image is a sticker or not
|
||||
*/
|
||||
QVariantMap getMediaInfo() const;
|
||||
|
||||
@@ -320,6 +321,7 @@ public:
|
||||
* - width - The width in pixels of the audio media.
|
||||
* - height - The height in pixels of the audio media.
|
||||
* - tempInfo - mediaInfo (with the same properties as this except no tempInfo) for a temporary image while the file downloads.
|
||||
* - isSticker - Whether the image is a sticker or not
|
||||
*/
|
||||
QVariantMap getReplyMediaInfo() const;
|
||||
|
||||
@@ -405,5 +407,6 @@ private:
|
||||
QString getMessageBody(const Quotient::RoomMessageEvent &event, Qt::TextFormat format, bool stripNewlines) const;
|
||||
|
||||
QVariantMap getMediaInfoForEvent(const Quotient::RoomEvent *event) const;
|
||||
QVariantMap getMediaInfoFromFileInfo(const Quotient::EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail = false) const;
|
||||
QVariantMap
|
||||
getMediaInfoFromFileInfo(const Quotient::EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail = false, bool isSticker = false) const;
|
||||
};
|
||||
|
||||
@@ -41,6 +41,12 @@ FileType::~FileType() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
FileType &FileType::instance()
|
||||
{
|
||||
static FileType _instance;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
QMimeType FileType::mimeTypeForName(const QString &nameOrAlias) const
|
||||
{
|
||||
Q_D(const FileType);
|
||||
@@ -113,4 +119,10 @@ QStringList FileType::supportedAnimatedImageFormats() const
|
||||
return d->supportedAnimatedImageFormats;
|
||||
}
|
||||
|
||||
bool FileType::fileHasImage(const QUrl &file) const
|
||||
{
|
||||
const auto mimeType = mimeTypeForFile(file.toString());
|
||||
return mimeType.isValid() && supportedImageFormats().contains(mimeType.preferredSuffix());
|
||||
}
|
||||
|
||||
#include "moc_filetype.cpp"
|
||||
|
||||
@@ -41,8 +41,13 @@ class FileType : public QObject
|
||||
Q_PROPERTY(QStringList supportedAnimatedImageFormats READ supportedAnimatedImageFormats CONSTANT FINAL)
|
||||
|
||||
public:
|
||||
explicit FileType(QObject *parent = nullptr);
|
||||
~FileType();
|
||||
static FileType &instance();
|
||||
static FileType *create(QQmlEngine *engine, QJSEngine *)
|
||||
{
|
||||
engine->setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
|
||||
return &instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a MIME type for nameOrAlias or an invalid one if none found.
|
||||
@@ -120,7 +125,11 @@ public:
|
||||
QStringList supportedImageFormats() const;
|
||||
QStringList supportedAnimatedImageFormats() const;
|
||||
|
||||
bool fileHasImage(const QUrl &file) const;
|
||||
|
||||
private:
|
||||
explicit FileType(QObject *parent = nullptr);
|
||||
|
||||
const QScopedPointer<FileTypePrivate> d_ptr;
|
||||
Q_DECLARE_PRIVATE(FileType)
|
||||
Q_DISABLE_COPY(FileType)
|
||||
|
||||
@@ -35,6 +35,11 @@
|
||||
|
||||
#include "neochat-version.h"
|
||||
|
||||
#include <Quotient/accountregistry.h>
|
||||
#if __has_include("Quotient/e2ee/sssshandler.h")
|
||||
#include <Quotient/e2ee/sssshandler.h>
|
||||
#endif
|
||||
#include <Quotient/keyverificationsession.h>
|
||||
#include <Quotient/networkaccessmanager.h>
|
||||
|
||||
#include "blurhashimageprovider.h"
|
||||
@@ -223,6 +228,7 @@ int main(int argc, char *argv[])
|
||||
KDBusService service(KDBusService::Unique);
|
||||
#endif
|
||||
|
||||
Q_IMPORT_QML_PLUGIN(org_kde_neochat_settingsPlugin)
|
||||
Q_IMPORT_QML_PLUGIN(org_kde_neochat_timelinePlugin)
|
||||
|
||||
qml_register_types_org_kde_neochat();
|
||||
@@ -230,6 +236,9 @@ int main(int argc, char *argv[])
|
||||
qmlRegisterSingletonInstance("org.kde.neochat.accounts", 1, 0, "AccountRegistry", &Controller::instance().accounts());
|
||||
|
||||
qmlRegisterUncreatableType<KeyVerificationSession>("com.github.quotient_im.libquotient", 1, 0, "KeyVerificationSession", {});
|
||||
#if __has_include("Quotient/e2ee/sssshandler.h")
|
||||
qmlRegisterType<SSSSHandler>("com.github.quotient_im.libquotient", 1, 0, "SSSSHandler");
|
||||
#endif
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
|
||||
@@ -26,20 +26,68 @@ QVariant ItineraryModel::data(const QModelIndex &index, int role) const
|
||||
auto data = m_data[row];
|
||||
if (role == NameRole) {
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("TrainReservation")) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("trainNumber")];
|
||||
auto trainName = QStringLiteral("%1 %2").arg(data[QStringLiteral("reservationFor")][QStringLiteral("trainName")].toString(),
|
||||
data[QStringLiteral("reservationFor")][QStringLiteral("trainNumber")].toString());
|
||||
if (trainName.trimmed().isEmpty()) {
|
||||
return QStringLiteral("%1 to %2")
|
||||
.arg(data[QStringLiteral("reservationFor")][QStringLiteral("departureStation")][QStringLiteral("name")].toString(),
|
||||
data[QStringLiteral("reservationFor")][QStringLiteral("arrivalStation")][QStringLiteral("name")].toString());
|
||||
;
|
||||
}
|
||||
return trainName;
|
||||
}
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("LodgingReservation")) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("name")];
|
||||
}
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("FoodEstablishmentReservation")) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("name")];
|
||||
}
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("FlightReservation")) {
|
||||
return QStringLiteral("%1 %2 %3 → %4")
|
||||
.arg(data[QStringLiteral("reservationFor")][QStringLiteral("airline")][QStringLiteral("iataCode")].toString(),
|
||||
data[QStringLiteral("reservationFor")][QStringLiteral("flightNumber")].toString(),
|
||||
data[QStringLiteral("reservationFor")][QStringLiteral("departureAirport")][QStringLiteral("iataCode")].toString(),
|
||||
data[QStringLiteral("reservationFor")][QStringLiteral("arrivalAirport")][QStringLiteral("iataCode")].toString());
|
||||
}
|
||||
}
|
||||
if (role == TypeRole) {
|
||||
return data[QStringLiteral("@type")];
|
||||
}
|
||||
if (role == DepartureStationRole) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("departureStation")][QStringLiteral("name")];
|
||||
if (role == DepartureLocationRole) {
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("TrainReservation")) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("departureStation")][QStringLiteral("name")];
|
||||
}
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("FlightReservation")) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("departureAirport")][QStringLiteral("iataCode")];
|
||||
}
|
||||
}
|
||||
if (role == ArrivalStationRole) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("arrivalStation")][QStringLiteral("name")];
|
||||
if (role == DepartureAddressRole) {
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("TrainReservation")) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("departureStation")][QStringLiteral("address")][QStringLiteral("addressCountry")]
|
||||
.toString();
|
||||
}
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("FlightReservation")) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("departureAirport")][QStringLiteral("address")][QStringLiteral("addressCountry")]
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
if (role == ArrivalLocationRole) {
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("TrainReservation")) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("arrivalStation")][QStringLiteral("name")];
|
||||
}
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("FlightReservation")) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("arrivalAirport")][QStringLiteral("iataCode")];
|
||||
}
|
||||
}
|
||||
if (role == ArrivalAddressRole) {
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("TrainReservation")) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("arrivalStation")][QStringLiteral("address")][QStringLiteral("addressCountry")]
|
||||
.toString();
|
||||
}
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("FlightReservation")) {
|
||||
return data[QStringLiteral("reservationFor")][QStringLiteral("arrivalAirport")][QStringLiteral("address")][QStringLiteral("addressCountry")]
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
if (role == DepartureTimeRole) {
|
||||
const auto &time = data[QStringLiteral("reservationFor")][QStringLiteral("departureTime")];
|
||||
@@ -66,7 +114,16 @@ QVariant ItineraryModel::data(const QModelIndex &index, int role) const
|
||||
addressData[QStringLiteral("addressCountry")].toString());
|
||||
}
|
||||
if (role == StartTimeRole) {
|
||||
auto dateTime = data[QStringLiteral("checkinTime")][QStringLiteral("@value")].toVariant().toDateTime();
|
||||
QDateTime dateTime;
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("LodgingReservation")) {
|
||||
dateTime = data[QStringLiteral("checkinTime")][QStringLiteral("@value")].toVariant().toDateTime();
|
||||
}
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("FoodEstablishmentReservation")) {
|
||||
dateTime = data[QStringLiteral("startTime")][QStringLiteral("@value")].toVariant().toDateTime();
|
||||
}
|
||||
if (data[QStringLiteral("@type")] == QStringLiteral("FlightReservation")) {
|
||||
dateTime = data[QStringLiteral("reservationFor")][QStringLiteral("boardingTime")][QStringLiteral("@value")].toVariant().toDateTime();
|
||||
}
|
||||
return dateTime.toString(QLocale::system().dateTimeFormat(QLocale::ShortFormat));
|
||||
}
|
||||
if (role == EndTimeRole) {
|
||||
@@ -99,8 +156,10 @@ QHash<int, QByteArray> ItineraryModel::roleNames() const
|
||||
return {
|
||||
{NameRole, "name"},
|
||||
{TypeRole, "type"},
|
||||
{DepartureStationRole, "departureStation"},
|
||||
{ArrivalStationRole, "arrivalStation"},
|
||||
{DepartureLocationRole, "departureLocation"},
|
||||
{DepartureAddressRole, "departureAddress"},
|
||||
{ArrivalLocationRole, "arrivalLocation"},
|
||||
{ArrivalAddressRole, "arrivalAddress"},
|
||||
{DepartureTimeRole, "departureTime"},
|
||||
{ArrivalTimeRole, "arrivalTime"},
|
||||
{AddressRole, "address"},
|
||||
@@ -133,6 +192,11 @@ void ItineraryModel::loadData()
|
||||
beginResetModel();
|
||||
m_data = QJsonDocument::fromJson(data).array();
|
||||
endResetModel();
|
||||
|
||||
Q_EMIT loaded();
|
||||
});
|
||||
connect(process, &QProcess::errorOccurred, this, [this]() {
|
||||
Q_EMIT loadErrorOccurred();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,12 @@ public:
|
||||
enum Roles {
|
||||
NameRole = Qt::DisplayRole,
|
||||
TypeRole,
|
||||
DepartureStationRole,
|
||||
ArrivalStationRole,
|
||||
DepartureLocationRole,
|
||||
ArrivalLocationRole,
|
||||
DepartureTimeRole,
|
||||
DepartureAddressRole,
|
||||
ArrivalTimeRole,
|
||||
ArrivalAddressRole,
|
||||
AddressRole,
|
||||
StartTimeRole,
|
||||
EndTimeRole,
|
||||
@@ -44,6 +46,10 @@ public:
|
||||
|
||||
Q_INVOKABLE void sendToItinerary();
|
||||
|
||||
Q_SIGNALS:
|
||||
void loaded();
|
||||
void loadErrorOccurred();
|
||||
|
||||
private:
|
||||
QJsonArray m_data;
|
||||
QString m_path;
|
||||
|
||||
@@ -3,15 +3,25 @@
|
||||
|
||||
#include "messagecontentmodel.h"
|
||||
|
||||
#include <QImageReader>
|
||||
|
||||
#include <Quotient/events/redactionevent.h>
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
#include <Quotient/events/stickerevent.h>
|
||||
#include <Quotient/room.h>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
#include <KSyntaxHighlighting/Definition>
|
||||
#include <KSyntaxHighlighting/Repository>
|
||||
#endif
|
||||
|
||||
#include "chatbarcache.h"
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "eventhandler.h"
|
||||
#include "filetype.h"
|
||||
#include "itinerarymodel.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "neochatroom.h"
|
||||
#include "texthandler.h"
|
||||
@@ -234,41 +244,75 @@ void MessageContentModel::updateComponents(bool isEditing)
|
||||
beginResetModel();
|
||||
m_components.clear();
|
||||
|
||||
EventHandler eventHandler(m_room, m_event);
|
||||
if (eventHandler.hasReply()) {
|
||||
if (m_room->findInTimeline(eventHandler.getReplyId()) == m_room->historyEdge()) {
|
||||
m_components += MessageComponent{MessageComponentType::ReplyLoad, QString(), {}};
|
||||
m_room->loadReply(m_event->id(), eventHandler.getReplyId());
|
||||
} else {
|
||||
m_components += MessageComponent{MessageComponentType::Reply, QString(), {}};
|
||||
}
|
||||
}
|
||||
|
||||
if (isEditing) {
|
||||
m_components += MessageComponent{MessageComponentType::Edit, QString(), {}};
|
||||
} else if (m_event->isRedacted()) {
|
||||
m_components += MessageComponent{MessageComponentType::Text, QString(), {}};
|
||||
if (eventCast<const Quotient::RoomMessageEvent>(m_event)
|
||||
&& eventCast<const Quotient::RoomMessageEvent>(m_event)->rawMsgtype() == QStringLiteral("m.key.verification.request")) {
|
||||
m_components += MessageComponent{MessageComponentType::Verification, QString(), {}};
|
||||
} else {
|
||||
if (eventHandler.messageComponentType() == MessageComponentType::Text) {
|
||||
const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event);
|
||||
auto body = EventHandler::rawMessageBody(*event);
|
||||
m_components.append(TextHandler().textComponents(body, EventHandler::messageBodyInputFormat(*event), m_room, event, event->isReplaced()));
|
||||
} else if (eventHandler.messageComponentType() == MessageComponentType::File) {
|
||||
m_components += MessageComponent{MessageComponentType::File, QString(), {}};
|
||||
updateItineraryModel();
|
||||
if (m_itineraryModel != nullptr) {
|
||||
m_components += MessageComponent{MessageComponentType::Itinerary, QString(), {}};
|
||||
EventHandler eventHandler(m_room, m_event);
|
||||
if (eventHandler.hasReply()) {
|
||||
if (m_room->findInTimeline(eventHandler.getReplyId()) == m_room->historyEdge()) {
|
||||
m_components += MessageComponent{MessageComponentType::ReplyLoad, QString(), {}};
|
||||
m_room->loadReply(m_event->id(), eventHandler.getReplyId());
|
||||
} else {
|
||||
m_components += MessageComponent{MessageComponentType::Reply, QString(), {}};
|
||||
}
|
||||
} else {
|
||||
m_components += MessageComponent{eventHandler.messageComponentType(), QString(), {}};
|
||||
}
|
||||
}
|
||||
|
||||
if (m_linkPreviewer != nullptr) {
|
||||
if (m_linkPreviewer->loaded()) {
|
||||
m_components += MessageComponent{MessageComponentType::LinkPreview, QString(), {}};
|
||||
if (isEditing) {
|
||||
m_components += MessageComponent{MessageComponentType::Edit, QString(), {}};
|
||||
} else if (m_event->isRedacted()) {
|
||||
m_components += MessageComponent{MessageComponentType::Text, QString(), {}};
|
||||
} else {
|
||||
m_components += MessageComponent{MessageComponentType::LinkPreviewLoad, QString(), {}};
|
||||
if (eventHandler.messageComponentType() == MessageComponentType::Text) {
|
||||
const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event);
|
||||
auto body = EventHandler::rawMessageBody(*event);
|
||||
m_components.append(TextHandler().textComponents(body, EventHandler::messageBodyInputFormat(*event), m_room, event, event->isReplaced()));
|
||||
} else if (eventHandler.messageComponentType() == MessageComponentType::File) {
|
||||
m_components += MessageComponent{MessageComponentType::File, QString(), {}};
|
||||
if (m_emptyItinerary) {
|
||||
Quotient::FileTransferInfo fileTransferInfo;
|
||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
if (event->hasFileContent()) {
|
||||
fileTransferInfo = m_room->fileTransferInfo(event->id());
|
||||
}
|
||||
}
|
||||
if (auto event = eventCast<const Quotient::StickerEvent>(m_event)) {
|
||||
fileTransferInfo = m_room->fileTransferInfo(event->id());
|
||||
}
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
KSyntaxHighlighting::Repository repository;
|
||||
const auto definitionForFile = repository.definitionForFileName(fileTransferInfo.localPath.toString());
|
||||
if (definitionForFile.isValid() || QFileInfo(fileTransferInfo.localPath.path()).suffix() == QStringLiteral("txt")) {
|
||||
QFile file(fileTransferInfo.localPath.path());
|
||||
file.open(QIODevice::ReadOnly);
|
||||
m_components += MessageComponent{MessageComponentType::Code,
|
||||
QString::fromStdString(file.readAll().toStdString()),
|
||||
{{QStringLiteral("class"), definitionForFile.name()}}};
|
||||
}
|
||||
#endif
|
||||
|
||||
if (FileType::instance().fileHasImage(fileTransferInfo.localPath)) {
|
||||
QImageReader reader(fileTransferInfo.localPath.path());
|
||||
m_components += MessageComponent{MessageComponentType::Pdf, QString(), {{QStringLiteral("size"), reader.size()}}};
|
||||
}
|
||||
} else {
|
||||
updateItineraryModel();
|
||||
if (m_itineraryModel != nullptr) {
|
||||
m_components += MessageComponent{MessageComponentType::Itinerary, QString(), {}};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_components += MessageComponent{eventHandler.messageComponentType(), QString(), {}};
|
||||
}
|
||||
}
|
||||
|
||||
if (m_linkPreviewer != nullptr) {
|
||||
if (m_linkPreviewer->loaded()) {
|
||||
m_components += MessageComponent{MessageComponentType::LinkPreview, QString(), {}};
|
||||
} else {
|
||||
m_components += MessageComponent{MessageComponentType::LinkPreviewLoad, QString(), {}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,6 +334,20 @@ void MessageContentModel::updateItineraryModel()
|
||||
} else if (!filePath.isEmpty()) {
|
||||
if (m_itineraryModel == nullptr) {
|
||||
m_itineraryModel = new ItineraryModel(this);
|
||||
connect(m_itineraryModel, &ItineraryModel::loaded, this, [this]() {
|
||||
if (m_itineraryModel->rowCount() == 0) {
|
||||
m_itineraryModel->deleteLater();
|
||||
m_itineraryModel = nullptr;
|
||||
m_emptyItinerary = true;
|
||||
updateComponents();
|
||||
}
|
||||
});
|
||||
connect(m_itineraryModel, &ItineraryModel::loadErrorOccurred, this, [this]() {
|
||||
m_itineraryModel->deleteLater();
|
||||
m_itineraryModel = nullptr;
|
||||
m_emptyItinerary = true;
|
||||
updateComponents();
|
||||
});
|
||||
}
|
||||
m_itineraryModel->setPath(filePath.toString());
|
||||
}
|
||||
|
||||
@@ -97,4 +97,5 @@ private:
|
||||
ItineraryModel *m_itineraryModel = nullptr;
|
||||
|
||||
void updateItineraryModel();
|
||||
bool m_emptyItinerary = false;
|
||||
};
|
||||
|
||||
@@ -629,6 +629,10 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
|
||||
int MessageEventModel::eventIdToRow(const QString &eventID) const
|
||||
{
|
||||
if (m_currentRoom == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto it = m_currentRoom->findInTimeline(eventID);
|
||||
if (it == m_currentRoom->historyEdge()) {
|
||||
// qWarning() << "Trying to find inexistent event:" << eventID;
|
||||
|
||||
@@ -113,7 +113,7 @@ private Q_SLOTS:
|
||||
void refreshRow(int row);
|
||||
|
||||
private:
|
||||
NeoChatRoom *m_currentRoom = nullptr;
|
||||
QPointer<NeoChatRoom> m_currentRoom = nullptr;
|
||||
QString lastReadEventId;
|
||||
QPersistentModelIndex m_lastReadEventIndex;
|
||||
int rowBelowInserted = -1;
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
SpaceChildrenModel::SpaceChildrenModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, m_rootItem(new SpaceTreeItem(nullptr))
|
||||
{
|
||||
m_rootItem = new SpaceTreeItem(nullptr);
|
||||
}
|
||||
|
||||
SpaceChildrenModel::~SpaceChildrenModel()
|
||||
@@ -40,6 +40,12 @@ void SpaceChildrenModel::setSpace(NeoChatRoom *space)
|
||||
m_space = space;
|
||||
Q_EMIT spaceChanged();
|
||||
|
||||
refreshModel();
|
||||
|
||||
if (!m_space) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto connection = m_space->connection();
|
||||
connect(connection, &Quotient::Connection::loadedRoomState, this, [this](Quotient::Room *room) {
|
||||
if (m_pendingChildren.contains(room->name())) {
|
||||
@@ -50,8 +56,6 @@ void SpaceChildrenModel::setSpace(NeoChatRoom *space)
|
||||
connect(m_space, &Quotient::Room::changed, this, [this]() {
|
||||
refreshModel();
|
||||
});
|
||||
|
||||
refreshModel();
|
||||
}
|
||||
|
||||
bool SpaceChildrenModel::loading() const
|
||||
@@ -69,6 +73,10 @@ void SpaceChildrenModel::refreshModel()
|
||||
m_currentJobs.clear();
|
||||
|
||||
if (m_space == nullptr) {
|
||||
beginResetModel();
|
||||
delete m_rootItem;
|
||||
m_rootItem = nullptr;
|
||||
endResetModel();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -131,18 +139,18 @@ void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJ
|
||||
insertChildren(job->rooms(), index(insertRow, 0, parent));
|
||||
});
|
||||
}
|
||||
parentItem->insertChild(new SpaceTreeItem(dynamic_cast<NeoChatConnection *>(m_space->connection()),
|
||||
parentItem,
|
||||
children[i].roomId,
|
||||
children[i].name,
|
||||
children[i].canonicalAlias,
|
||||
children[i].topic,
|
||||
children[i].numJoinedMembers,
|
||||
children[i].avatarUrl,
|
||||
children[i].guestCanJoin,
|
||||
children[i].worldReadable,
|
||||
children[i].roomType == QLatin1String("m.space"),
|
||||
std::move(children[i].childrenState)));
|
||||
parentItem->insertChild(std::make_unique<SpaceTreeItem>(dynamic_cast<NeoChatConnection *>(m_space->connection()),
|
||||
parentItem,
|
||||
children[i].roomId,
|
||||
children[i].name,
|
||||
children[i].canonicalAlias,
|
||||
children[i].topic,
|
||||
children[i].numJoinedMembers,
|
||||
children[i].avatarUrl,
|
||||
children[i].guestCanJoin,
|
||||
children[i].worldReadable,
|
||||
children[i].roomType == QLatin1String("m.space"),
|
||||
std::move(children[i].childrenState)));
|
||||
}
|
||||
}
|
||||
endInsertRows();
|
||||
|
||||
@@ -131,7 +131,7 @@ Q_SIGNALS:
|
||||
void loadingChanged();
|
||||
|
||||
private:
|
||||
NeoChatRoom *m_space = nullptr;
|
||||
QPointer<NeoChatRoom> m_space;
|
||||
SpaceTreeItem *m_rootItem;
|
||||
|
||||
bool m_loading = false;
|
||||
|
||||
@@ -32,30 +32,22 @@ SpaceTreeItem::SpaceTreeItem(NeoChatConnection *connection,
|
||||
{
|
||||
}
|
||||
|
||||
SpaceTreeItem::~SpaceTreeItem()
|
||||
{
|
||||
qDeleteAll(m_children);
|
||||
}
|
||||
|
||||
bool SpaceTreeItem::operator==(const SpaceTreeItem &other) const
|
||||
{
|
||||
return m_id == other.id();
|
||||
}
|
||||
|
||||
SpaceTreeItem *SpaceTreeItem::child(int number)
|
||||
SpaceTreeItem *SpaceTreeItem::child(int row)
|
||||
{
|
||||
if (number < 0 || number >= m_children.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
return m_children[number];
|
||||
return row >= 0 && row < childCount() ? m_children.at(row).get() : nullptr;
|
||||
}
|
||||
|
||||
int SpaceTreeItem::childCount() const
|
||||
{
|
||||
return m_children.count();
|
||||
return int(m_children.size());
|
||||
}
|
||||
|
||||
bool SpaceTreeItem::insertChild(SpaceTreeItem *newChild)
|
||||
bool SpaceTreeItem::insertChild(std::unique_ptr<SpaceTreeItem> newChild)
|
||||
{
|
||||
if (newChild == nullptr) {
|
||||
return false;
|
||||
@@ -63,30 +55,39 @@ bool SpaceTreeItem::insertChild(SpaceTreeItem *newChild)
|
||||
|
||||
for (auto it = m_children.begin(), end = m_children.end(); it != end; ++it) {
|
||||
if (*it == newChild) {
|
||||
*it = newChild;
|
||||
*it = std::move(newChild);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
m_children.append(newChild);
|
||||
m_children.push_back(std::move(newChild));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpaceTreeItem::removeChild(int row)
|
||||
{
|
||||
if (row < 0 || row >= m_children.size()) {
|
||||
if (row < 0 || row >= childCount()) {
|
||||
return false;
|
||||
}
|
||||
delete m_children.takeAt(row);
|
||||
m_children.erase(m_children.begin() + row);
|
||||
return true;
|
||||
}
|
||||
|
||||
int SpaceTreeItem::row() const
|
||||
{
|
||||
if (m_parentItem) {
|
||||
return m_parentItem->m_children.indexOf(const_cast<SpaceTreeItem *>(this));
|
||||
if (m_parentItem == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
|
||||
const auto it = std::find_if(m_parentItem->m_children.cbegin(), m_parentItem->m_children.cend(), [this](const std::unique_ptr<SpaceTreeItem> &treeItem) {
|
||||
return treeItem.get() == this;
|
||||
});
|
||||
|
||||
if (it != m_parentItem->m_children.cend()) {
|
||||
return std::distance(m_parentItem->m_children.cbegin(), it);
|
||||
}
|
||||
Q_ASSERT(false); // should not happen
|
||||
return -1;
|
||||
}
|
||||
|
||||
SpaceTreeItem *SpaceTreeItem::parentItem() const
|
||||
|
||||
@@ -33,7 +33,6 @@ public:
|
||||
bool worldReadable = {},
|
||||
bool isSpace = {},
|
||||
Quotient::StateEvents childStates = {});
|
||||
~SpaceTreeItem();
|
||||
|
||||
bool operator==(const SpaceTreeItem &other) const;
|
||||
|
||||
@@ -42,7 +41,7 @@ public:
|
||||
*
|
||||
* Nullptr is returned if there is no child at the given row number.
|
||||
*/
|
||||
SpaceTreeItem *child(int number);
|
||||
SpaceTreeItem *child(int row);
|
||||
|
||||
/**
|
||||
* @brief The number of children this item has.
|
||||
@@ -52,7 +51,7 @@ public:
|
||||
/**
|
||||
* @brief Insert the given child.
|
||||
*/
|
||||
bool insertChild(SpaceTreeItem *newChild);
|
||||
bool insertChild(std::unique_ptr<SpaceTreeItem> newChild);
|
||||
|
||||
/**
|
||||
* @brief Remove the child at the given row number.
|
||||
@@ -151,7 +150,7 @@ public:
|
||||
|
||||
private:
|
||||
NeoChatConnection *m_connection;
|
||||
QList<SpaceTreeItem *> m_children;
|
||||
std::vector<std::unique_ptr<SpaceTreeItem>> m_children;
|
||||
SpaceTreeItem *m_parentItem;
|
||||
|
||||
QString m_id;
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
private:
|
||||
NeoChatRoom *m_room = nullptr;
|
||||
QPointer<NeoChatRoom> m_room = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,7 @@ Name[it]=NeoChat
|
||||
Name[ka]=NeoChat
|
||||
Name[ko]=NeoChat
|
||||
Name[lt]=NeoChat
|
||||
Name[lv]=NeoChat
|
||||
Name[nl]=NeoChat
|
||||
Name[nn]=NeoChat
|
||||
Name[pa]=ਨਿਓ-ਚੈਟ
|
||||
@@ -65,6 +66,7 @@ Comment[it]=Un client per matrix, il protocollo di comunicazione decentralizzato
|
||||
Comment[ka]=კლიენტი Matrix-სთვის, დეცენტრალიზებული კომუნიკაციის პროტოკოლისთვის
|
||||
Comment[ko]=Matrix, 분산 대화 프로토콜 클라이언트
|
||||
Comment[lt]=Matrix decentralizuoto bendravimo protokolo kliento programa
|
||||
Comment[lv]=Klients „Matrix“ protokolam — decentralizētam komunikācijas protokolam
|
||||
Comment[nl]=Een client voor matrix, het gedecentraliseerde communicatieprotocol
|
||||
Comment[nn]=Ein klient for Matrix – protokollen for desentralisert kommunikasjon
|
||||
Comment[pa]=ਮੈਟਰਿਕਸ, ਸਰਬ-ਸਾਂਝੇ ਸੰਚਾਰ ਪਰੋਟੋਕਾਲ, ਲਈ ਕਲਾਈਂਟ ਹੈ
|
||||
@@ -108,6 +110,7 @@ Name[it]=Nuovo messaggio
|
||||
Name[ka]=ახალი შეტყობინება
|
||||
Name[ko]=새 메시지
|
||||
Name[lt]=Nauja žinutė
|
||||
Name[lv]=Jauns ziņojums
|
||||
Name[nl]=Nieuw bericht
|
||||
Name[nn]=Ny melding
|
||||
Name[pa]=ਨਵਾਂ ਸੁਨੇਹਾ
|
||||
@@ -147,6 +150,7 @@ Comment[it]=È presente un nuovo messaggio
|
||||
Comment[ka]=გაქვთ ახალი შეტყობინება
|
||||
Comment[ko]=새 메시지가 있음
|
||||
Comment[lt]=Yra nauja žinutė
|
||||
Comment[lv]=Ir pienācis jauns ziņojums
|
||||
Comment[nl]=Er is een nieuw bericht
|
||||
Comment[nn]=Du har ei ny melding
|
||||
Comment[pa]=ਨਵਾਂ ਸੁਨੇਹਾ ਹੈ
|
||||
@@ -190,6 +194,7 @@ Name[it]=Nuovo invito
|
||||
Name[ka]=ახალი მოსაწვევი
|
||||
Name[ko]=새 초대장
|
||||
Name[lt]=Naujas pakvietimas
|
||||
Name[lv]=Jauns uzaicinājums
|
||||
Name[nl]=Nieuwe uitnodiging
|
||||
Name[nn]=Ny invitasjon
|
||||
Name[pa]=ਨਵਾਂ ਸੱਦਾ
|
||||
@@ -228,6 +233,7 @@ Comment[it]=È presente un nuovo invito a una stanza
|
||||
Comment[ka]=გაქვთ ახალი ოთახის მოსაწვევი
|
||||
Comment[ko]=새로운 대화방 초대장을 받음
|
||||
Comment[lt]=Yra naujas pakvietimas į kambarį
|
||||
Comment[lv]=Istabā ir jauns uzaicinājums
|
||||
Comment[nl]=Er is een nieuwe uitnodiging naar een room
|
||||
Comment[nn]=Du har ein ny invitasjon til eit rom
|
||||
Comment[pa]=ਰੂਮ ਲਈ ਨਵਾਂ ਸੱਦਾ ਹੈ
|
||||
@@ -258,6 +264,7 @@ Name[hu]=Megosztás
|
||||
Name[ia]=Comparti
|
||||
Name[it]=Condivisione
|
||||
Name[ka]=გაზიარება
|
||||
Name[lv]=Kopīgot
|
||||
Name[nl]=Gedeelde
|
||||
Name[pl]=Udostępnij
|
||||
Name[sl]=Deli
|
||||
@@ -277,6 +284,7 @@ Comment[hu]=Tartalom megosztásának eredménye
|
||||
Comment[ia]=Le exito de compartir un pecietta de contento
|
||||
Comment[it]=Il risultato della condivisione di un contenuto
|
||||
Comment[ka]=შემცველობის ნაწილის გაზიარების შედეგი
|
||||
Comment[lv]=Satura kopīgošanas rezultāts
|
||||
Comment[nl]=Het resultaat van het delen van een stukje inhoud
|
||||
Comment[pl]=Wynik udostępniania kawałka treści
|
||||
Comment[sl]=Rezultat deljenega kosa vsebine
|
||||
|
||||
@@ -161,6 +161,10 @@
|
||||
<label>Enable threads</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
<entry name="SecretBackup" type="bool">
|
||||
<label>Enable secret backup</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
</group>
|
||||
</kcfg>
|
||||
|
||||
|
||||
@@ -153,14 +153,6 @@ void NeoChatConnection::logout(bool serverSideLogout)
|
||||
job.start();
|
||||
loop.exec();
|
||||
|
||||
if (Controller::instance().accounts().count() > 1) {
|
||||
// Only set the connection if the the account being logged out is currently active
|
||||
if (this == Controller::instance().activeConnection()) {
|
||||
Controller::instance().setActiveConnection(dynamic_cast<NeoChatConnection *>(Controller::instance().accounts().accounts()[0]));
|
||||
}
|
||||
} else {
|
||||
Controller::instance().setActiveConnection(nullptr);
|
||||
}
|
||||
if (!serverSideLogout) {
|
||||
return;
|
||||
}
|
||||
@@ -344,6 +336,9 @@ void NeoChatConnection::openOrCreateDirectChat(User *user)
|
||||
}
|
||||
}
|
||||
requestDirectChat(user);
|
||||
connectSingleShot(this, &Connection::directChatAvailable, this, [=](auto room) {
|
||||
room->activateEncryption();
|
||||
});
|
||||
}
|
||||
|
||||
qsizetype NeoChatConnection::directChatNotifications() const
|
||||
|
||||
@@ -1298,9 +1298,7 @@ void NeoChatRoom::removeParent(const QString &parentId)
|
||||
if (!currentState().contains("m.space.parent"_ls, parentId)) {
|
||||
return;
|
||||
}
|
||||
if (auto parent = static_cast<NeoChatRoom *>(connection()->room(parentId))) {
|
||||
setState("m.space.parent"_ls, parentId, {});
|
||||
}
|
||||
setState("m.space.parent"_ls, parentId, {});
|
||||
}
|
||||
|
||||
bool NeoChatRoom::isSpace() const
|
||||
|
||||
@@ -57,7 +57,7 @@ void NotificationsManager::processNotificationJob(QPointer<NeoChatConnection> co
|
||||
if (job == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (connection == nullptr) {
|
||||
if (connection == nullptr || !connection->isLoggedIn()) {
|
||||
qWarning() << QStringLiteral("No connection for GetNotificationsJob %1").arg(job->objectName());
|
||||
return;
|
||||
}
|
||||
@@ -150,7 +150,7 @@ void NotificationsManager::processNotificationJob(QPointer<NeoChatConnection> co
|
||||
|
||||
bool NotificationsManager::shouldPostNotification(QPointer<NeoChatConnection> connection, const QJsonValue ¬ification)
|
||||
{
|
||||
if (connection == nullptr) {
|
||||
if (connection == nullptr || !connection->isLoggedIn()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ Name[it]=NeoChat
|
||||
Name[ka]=NeoChat
|
||||
Name[ko]=NeoChat
|
||||
Name[lt]=NeoChat
|
||||
Name[lv]=NeoChat
|
||||
Name[nl]=NeoChat
|
||||
Name[nn]=NeoChat
|
||||
Name[pa]=ਨਿਓ-ਚੈਟ
|
||||
@@ -64,6 +65,7 @@ Comment[it]=Trova stanze in NeoChat
|
||||
Comment[ka]=იპოვე ოთახები NeoChat-ში
|
||||
Comment[ko]=NeoChat에서 대화방 찾기
|
||||
Comment[lt]=Rasti kambarius NeoChat
|
||||
Comment[lv]=Atrast „NeoChat“ istabas
|
||||
Comment[nl]=Rooms zoeken in NeoChat
|
||||
Comment[nn]=Finn rom i NeoChat
|
||||
Comment[pl]=Znajdź pokoje w NeoChat
|
||||
|
||||
@@ -5,53 +5,66 @@
|
||||
"Name": "Tobias Fella",
|
||||
"Name[ca@valencia]": "Tobias Fella",
|
||||
"Name[ca]": "Tobias Fella",
|
||||
"Name[de]": "Tobias Fella",
|
||||
"Name[es]": "Tobias Fella",
|
||||
"Name[eu]": "Tobias Fella",
|
||||
"Name[fr]": "Tobias Fella",
|
||||
"Name[hu]": "Tobias Fella",
|
||||
"Name[ia]": "Tobias Fella",
|
||||
"Name[it]": "Tobias Fella",
|
||||
"Name[ka]": "Tobias Fella",
|
||||
"Name[lv]": "Tobias Fella",
|
||||
"Name[nl]": "Tobias Fella",
|
||||
"Name[pl]": "Tobias Fella",
|
||||
"Name[sl]": "Tobias Fella",
|
||||
"Name[tr]": "Tobias Fella",
|
||||
"Name[uk]": "Tobias Fella",
|
||||
"Name[x-test]": "xxTobias Fellaxx"
|
||||
"Name[x-test]": "xxTobias Fellaxx",
|
||||
"Name[zh_TW]": "Tobias Fella"
|
||||
}
|
||||
],
|
||||
"Category": "Utilities",
|
||||
"Description": "Share via NeoChat",
|
||||
"Description[ca@valencia]": "Compartix a través de NeoChat",
|
||||
"Description[ca]": "Comparteix a través del NeoChat",
|
||||
"Description[de]": "Über NeoChat teilen",
|
||||
"Description[es]": "Compartir mediante NeoChat",
|
||||
"Description[eu]": "Partekatu NeoChat bidez",
|
||||
"Description[fr]": "Partager grâce à NeoChat",
|
||||
"Description[hu]": "Megosztás NeoChatben",
|
||||
"Description[ia]": "Comparti via NeoChat",
|
||||
"Description[it]": "Condividi tramite NeoChat",
|
||||
"Description[ka]": "გააზიარეთ NeoChat-ით",
|
||||
"Description[lv]": "Kopīgot ar „NeoChat“",
|
||||
"Description[nl]": "Delen via NeoChat",
|
||||
"Description[pl]": "Udostępnij przez NeoChat",
|
||||
"Description[sl]": "Deli prek NeoChat",
|
||||
"Description[tr]": "NeoChat ile Paylaş",
|
||||
"Description[uk]": "Оприлюднити за допомогою NeoChat",
|
||||
"Description[x-test]": "xxShare via NeoChatxx",
|
||||
"Description[zh_TW]": "透過 NeoChat 分享",
|
||||
"Icon": "org.kde.neochat",
|
||||
"License": "GPL",
|
||||
"Name": "NeoChat",
|
||||
"Name[ast]": "NeoChat",
|
||||
"Name[ca@valencia]": "NeoChat",
|
||||
"Name[ca]": "NeoChat",
|
||||
"Name[de]": "NeoChat",
|
||||
"Name[es]": "NeoChat",
|
||||
"Name[eu]": "NeoChat",
|
||||
"Name[fr]": "NeoChat",
|
||||
"Name[hu]": "NeoChat",
|
||||
"Name[ia]": "Neochat",
|
||||
"Name[it]": "NeoChat",
|
||||
"Name[ka]": "NeoChat",
|
||||
"Name[lv]": "NeoChat",
|
||||
"Name[nl]": "NeoChat",
|
||||
"Name[pl]": "NeoChat",
|
||||
"Name[sl]": "NeoChat",
|
||||
"Name[tr]": "NeoChat",
|
||||
"Name[uk]": "NeoChat",
|
||||
"Name[x-test]": "xxNeoChatxx",
|
||||
"Name[zh_TW]": "NeoChat",
|
||||
"X-Purpose-ActionDisplay": "NeoChat"
|
||||
},
|
||||
"X-Purpose-PluginTypes": [
|
||||
|
||||
@@ -8,6 +8,7 @@ import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
import org.kde.neochat.config
|
||||
|
||||
QQC2.Menu {
|
||||
@@ -20,7 +21,7 @@ QQC2.Menu {
|
||||
QQC2.MenuItem {
|
||||
text: i18n("Edit this account")
|
||||
icon.name: "document-edit"
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'AccountEditorPage.qml'), {
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'AccountEditorPage.qml'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18n("Account editor")
|
||||
@@ -29,7 +30,7 @@ QQC2.Menu {
|
||||
QQC2.MenuItem {
|
||||
text: i18n("Notification settings")
|
||||
icon.name: "notifications"
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'SettingsPage.qml'), {
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'NeoChatSettings.qml'), {
|
||||
defaultPage: "notifications",
|
||||
connection: root.connection
|
||||
}, {
|
||||
@@ -41,7 +42,7 @@ QQC2.Menu {
|
||||
QQC2.MenuItem {
|
||||
text: i18n("Devices")
|
||||
icon.name: "computer-symbolic"
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'SettingsPage.qml'), {
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'NeoChatSettings.qml'), {
|
||||
defaultPage: "devices",
|
||||
connection: root.connection
|
||||
}, {
|
||||
@@ -62,6 +63,15 @@ QQC2.Menu {
|
||||
height: Kirigami.Units.gridUnit * 42
|
||||
})
|
||||
}
|
||||
QQC2.MenuItem {
|
||||
text: i18nc("@action:inmenu", "Open Secret Backup")
|
||||
icon.name: "unlock"
|
||||
visible: Config.secretBackup
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UnlockSSSSDialog.qml'), {}, {
|
||||
title: i18nc("@title:window", "Open Key Backup")
|
||||
})
|
||||
enabled: Controller.ssssSupported
|
||||
}
|
||||
QQC2.MenuItem {
|
||||
text: i18n("Logout")
|
||||
icon.name: "list-remove-user"
|
||||
|
||||
34
src/qml/ConfirmUrlDialog.qml
Normal file
34
src/qml/ConfirmUrlDialog.qml
Normal file
@@ -0,0 +1,34 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
Kirigami.Dialog {
|
||||
id: root
|
||||
|
||||
property url link
|
||||
|
||||
width: Kirigami.Units.gridUnit * 24
|
||||
height: Kirigami.Units.gridUnit * 8
|
||||
|
||||
title: i18nc("@title", "Open Url")
|
||||
standardButtons: Kirigami.Dialog.Yes | Kirigami.Dialog.No
|
||||
|
||||
contentItem: QQC2.Label {
|
||||
text: i18nc("Do you want to open <link>", "Do you want to open <b>%1</b>?", root.link)
|
||||
wrapMode: QQC2.Label.Wrap
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
Qt.openUrlExternally(root.link);
|
||||
root.close();
|
||||
}
|
||||
onRejected: {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import org.kde.kirigamiaddons.components as KirigamiComponents
|
||||
import org.kde.kirigamiaddons.delegates as Delegates
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
|
||||
/**
|
||||
* Context menu when clicking on a room in the room list
|
||||
@@ -26,18 +27,6 @@ Loader {
|
||||
Component {
|
||||
id: regularMenu
|
||||
QQC2.Menu {
|
||||
QQC2.MenuItem {
|
||||
id: newWindow
|
||||
text: i18n("Open in New Window")
|
||||
icon.name: "window-new"
|
||||
onTriggered: RoomManager.openWindow(room)
|
||||
visible: !Kirigami.Settings.isMobile
|
||||
}
|
||||
|
||||
QQC2.MenuSeparator {
|
||||
visible: newWindow.visible
|
||||
}
|
||||
|
||||
QQC2.MenuItem {
|
||||
text: room.isFavourite ? i18n("Remove from Favorites") : i18n("Add to Favorites")
|
||||
icon.name: room.isFavourite ? "bookmark-remove" : "bookmark-new"
|
||||
@@ -121,7 +110,7 @@ Loader {
|
||||
QQC2.MenuItem {
|
||||
text: i18n("Room Settings")
|
||||
icon.name: "configure"
|
||||
onTriggered: QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'Categories.qml'), {
|
||||
onTriggered: QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'RoomSettings.qml'), {
|
||||
room: room,
|
||||
connection: connection
|
||||
}, {
|
||||
@@ -195,7 +184,7 @@ Loader {
|
||||
QQC2.ToolButton {
|
||||
icon.name: 'settings-configure'
|
||||
onClicked: {
|
||||
QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'Categories.qml'), {
|
||||
QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'RoomSettings.qml'), {
|
||||
room: room,
|
||||
connection: root.connection
|
||||
}, {
|
||||
|
||||
@@ -68,6 +68,16 @@ RowLayout {
|
||||
}
|
||||
}
|
||||
|
||||
property Kirigami.Action scanAction: Kirigami.Action {
|
||||
text: i18n("Scan a QR Code")
|
||||
icon.name: "view-barcode-qr"
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "QrScannerPage.qml"), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18nc("@title", "Scan a QR Code")
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Emitted when the text is changed in the search field.
|
||||
*/
|
||||
@@ -130,6 +140,9 @@ RowLayout {
|
||||
QQC2.MenuItem {
|
||||
action: spaceAction
|
||||
}
|
||||
QQC2.MenuItem {
|
||||
action: scanAction
|
||||
}
|
||||
}
|
||||
}
|
||||
Component {
|
||||
@@ -177,6 +190,11 @@ RowLayout {
|
||||
onClicked: menuRoot.close()
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Delegates.RoundedItemDelegate {
|
||||
action: scanAction
|
||||
onClicked: menuRoot.close()
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ SearchPage {
|
||||
let dialog = manualRoomDialog.createObject(applicationWindow().overlay, {
|
||||
connection: root.connection
|
||||
});
|
||||
dialog.parent = root.Window.window.overlay;
|
||||
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
|
||||
root.roomSelected(roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined);
|
||||
root.closeDialog();
|
||||
|
||||
@@ -23,5 +23,11 @@ FormCard.FormCardPage {
|
||||
|
||||
onToggled: Config.threads = checked
|
||||
}
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check Enable the matrix 'secret backup' feature", "Secret Backup")
|
||||
checked: Config.secretBackup
|
||||
|
||||
onToggled: Config.secretBackup = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import QtQuick.Window
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
import org.kde.neochat.config
|
||||
import org.kde.neochat.accounts
|
||||
|
||||
@@ -28,7 +29,7 @@ Labs.MenuBar {
|
||||
text: i18nc("menu", "Configure NeoChat...")
|
||||
|
||||
shortcut: StandardKey.Preferences
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'SettingsPage.qml'), {
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'NeoChatSettings.qml'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18n("Configure"),
|
||||
|
||||
75
src/qml/JoinRoomDialog.qml
Normal file
75
src/qml/JoinRoomDialog.qml
Normal file
@@ -0,0 +1,75 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.components as KirigamiComponents
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.prison
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Kirigami.Dialog {
|
||||
id: root
|
||||
|
||||
property string room
|
||||
property NeoChatConnection connection
|
||||
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
standardButtons: Kirigami.Dialog.NoButton
|
||||
|
||||
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
|
||||
title: i18nc("@title", "Join Room")
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 0
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
KirigamiComponents.Avatar {
|
||||
id: avatar
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
|
||||
|
||||
name: root.room.slice(1, -1)
|
||||
initialsMode: KirigamiComponents.Avatar.UseInitials
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
level: 1
|
||||
Layout.fillWidth: true
|
||||
font.bold: true
|
||||
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
text: root.room
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18nc("@action:button", "Join room")
|
||||
icon.name: "irc-join-channel"
|
||||
onClicked: {
|
||||
RoomManager.resolveResource(root.room, "join");
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,13 @@ DelegateContextMenu {
|
||||
onTriggered: Clipboard.saveText(root.selectedText.length > 0 ? root.selectedText : root.plainText)
|
||||
},
|
||||
DelegateContextMenu.ReportMessageAction {},
|
||||
Kirigami.Action {
|
||||
text: i18nc("@action:inmenu", "Show User")
|
||||
icon.name: "username-copy"
|
||||
onTriggered: {
|
||||
RoomManager.resolveResource(author.id)
|
||||
}
|
||||
},
|
||||
DelegateContextMenu.ViewSourceAction {},
|
||||
Kirigami.Action {
|
||||
text: i18n("Copy Link")
|
||||
|
||||
58
src/qml/QrScannerPage.qml
Normal file
58
src/qml/QrScannerPage.qml
Normal file
@@ -0,0 +1,58 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtMultimedia
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.prison.scanner as Prison
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Kirigami.Page {
|
||||
id: root
|
||||
|
||||
title: i18nc("@title", "Scan a QR Code")
|
||||
|
||||
required property NeoChatConnection connection
|
||||
padding: 0
|
||||
|
||||
Component.onCompleted: camera.start()
|
||||
|
||||
Connections {
|
||||
target: root.QQC2.ApplicationWindow.window
|
||||
function onClosing() {
|
||||
root.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
VideoOutput {
|
||||
id: viewFinder
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
Prison.VideoScanner {
|
||||
id: scanner
|
||||
property string previousText: ""
|
||||
formats: Prison.Format.QRCode | Prison.Format.Aztec
|
||||
onResultChanged: {
|
||||
if (result.text.length > 0 && result.text != scanner.previousText) {
|
||||
RoomManager.resolveResource(result.text, "");
|
||||
scanner.previousText = result.text;
|
||||
}
|
||||
root.closeDialog();
|
||||
}
|
||||
videoSink: viewFinder.videoSink
|
||||
}
|
||||
|
||||
CaptureSession {
|
||||
camera: Camera {
|
||||
id: camera
|
||||
}
|
||||
imageCapture: ImageCapture {
|
||||
id: imageCapture
|
||||
}
|
||||
videoOutput: viewFinder
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import org.kde.kirigami as Kirigami
|
||||
import org.kde.kitemmodels
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
import org.kde.neochat.config
|
||||
|
||||
Kirigami.OverlayDrawer {
|
||||
@@ -102,7 +103,7 @@ Kirigami.OverlayDrawer {
|
||||
text: i18n("Room settings")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
|
||||
onClicked: QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'Categories.qml'), {
|
||||
onClicked: QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'RoomSettings.qml'), {
|
||||
room: room,
|
||||
connection: root.connection
|
||||
}, {
|
||||
|
||||
@@ -25,8 +25,7 @@ Kirigami.Page {
|
||||
* @note Other objects can access the value but the private function makes sure
|
||||
* that only the internal members can modify it.
|
||||
*/
|
||||
readonly property int currentWidth: _private.currentWidth + spaceListWidth + 1
|
||||
readonly property alias spaceListWidth: spaceDrawer.width
|
||||
readonly property int currentWidth: _private.currentWidth + spaceDrawer.width + 1
|
||||
|
||||
required property NeoChatConnection connection
|
||||
|
||||
@@ -94,7 +93,7 @@ Kirigami.Page {
|
||||
|
||||
SpaceDrawer {
|
||||
id: spaceDrawer
|
||||
Layout.preferredWidth: spaceDrawer.enabled ? Kirigami.Units.gridUnit * 3 : 0
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
|
||||
Layout.fillHeight: true
|
||||
|
||||
connection: root.connection
|
||||
|
||||
@@ -238,9 +238,6 @@ Kirigami.Page {
|
||||
|
||||
Connections {
|
||||
target: RoomManager
|
||||
function onShowUserDetail(user) {
|
||||
root.showUserDetail(user);
|
||||
}
|
||||
|
||||
function onShowEventSource(eventId) {
|
||||
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
|
||||
@@ -286,18 +283,6 @@ Kirigami.Page {
|
||||
}
|
||||
}
|
||||
|
||||
function showUserDetail(user) {
|
||||
userDetailDialog.createObject(QQC2.ApplicationWindow.overlay, {
|
||||
room: root.currentRoom,
|
||||
user: root.currentRoom.getUser(user.id)
|
||||
}).open();
|
||||
}
|
||||
|
||||
Component {
|
||||
id: userDetailDialog
|
||||
UserDetailDialog {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: messageDelegateContextMenu
|
||||
MessageDelegateContextMenu {
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Kirigami.ApplicationWindow {
|
||||
id: root
|
||||
|
||||
required property NeoChatRoom currentRoom
|
||||
required property NeoChatConnection connection
|
||||
|
||||
minimumWidth: Kirigami.Units.gridUnit * 10
|
||||
minimumHeight: Kirigami.Units.gridUnit * 15
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Cancel
|
||||
onActivated: root.close()
|
||||
}
|
||||
pageStack.initialPage: RoomPage {
|
||||
id: roomPage
|
||||
visible: true
|
||||
currentRoom: root.currentRoom
|
||||
disableCancelShortcut: true
|
||||
connection: root.connection
|
||||
|
||||
timelineModel: TimelineModel {
|
||||
room: currentRoom
|
||||
}
|
||||
messageFilterModel: MessageFilterModel {
|
||||
sourceModel: roomPage.messageEventModel
|
||||
}
|
||||
mediaMessageFilterModel: MediaMessageFilterModel {
|
||||
sourceModel: roomPage.messageFilterModel
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentRoomChanged: if (!currentRoom) {
|
||||
root.close();
|
||||
}
|
||||
|
||||
property Item hoverLinkIndicator: QQC2.Control {
|
||||
parent: overlay.parent
|
||||
property string text
|
||||
opacity: linkText.text.length > 0 ? 1 : 0
|
||||
|
||||
z: 20
|
||||
x: 0
|
||||
y: parent.height - implicitHeight
|
||||
contentItem: QQC2.Label {
|
||||
id: linkText
|
||||
text: parent.text.startsWith("https://matrix.to/") ? "" : parent.text
|
||||
}
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
background: Rectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
|
||||
Kirigami.Page {
|
||||
id: root
|
||||
@@ -70,7 +71,7 @@ Kirigami.Page {
|
||||
text: i18nc("@button", "Space settings")
|
||||
icon.name: "settings-configure"
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'Categories.qml'), {
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'RoomSettings.qml'), {
|
||||
room: root.currentRoom,
|
||||
connection: root.currentRoom.connection
|
||||
}, {
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.kde.kirigamiaddons.components as KirigamiComponents
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
|
||||
/**
|
||||
* Context menu when clicking on a room in the room list
|
||||
@@ -45,7 +46,7 @@ Loader {
|
||||
QQC2.MenuItem {
|
||||
text: i18nc("'Space' is a matrix space", "Space Settings")
|
||||
icon.name: 'settings-configure'
|
||||
onTriggered: QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'Categories.qml'), {
|
||||
onTriggered: QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'RoomSettings.qml'), {
|
||||
room: room,
|
||||
connection: connection
|
||||
}, {
|
||||
@@ -130,7 +131,7 @@ Loader {
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18nc("'Space' is a matrix space", "Space Settings")
|
||||
icon.name: 'settings-configure'
|
||||
onClicked: QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'Categories.qml'), {
|
||||
onClicked: QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'RoomSettings.qml'), {
|
||||
room: room,
|
||||
connection: connection
|
||||
}, {
|
||||
|
||||
137
src/qml/UnlockSSSSDialog.qml
Normal file
137
src/qml/UnlockSSSSDialog.qml
Normal file
@@ -0,0 +1,137 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||
|
||||
import com.github.quotient_im.libquotient
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
title: i18nc("@title:window", "Load your encrypted messages")
|
||||
|
||||
topPadding: Kirigami.Units.gridUnit
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
|
||||
header: KirigamiComponents.Banner {
|
||||
id: banner
|
||||
showCloseButton: true
|
||||
visible: false
|
||||
type: Kirigami.MessageType.Error
|
||||
}
|
||||
|
||||
property SSSSHandler ssssHandler: SSSSHandler {
|
||||
id: ssssHandler
|
||||
|
||||
property bool processing: false
|
||||
|
||||
connection: Controller.activeConnection
|
||||
onKeyBackupUnlocked: {
|
||||
ssssHandler.processing = false
|
||||
root.closeDialog()
|
||||
}
|
||||
onError: error => {
|
||||
if (error !== SSSSHandler.WrongKeyError) {
|
||||
banner.text = error
|
||||
banner.visible = true
|
||||
return;
|
||||
}
|
||||
passwordField.clear()
|
||||
ssssHandler.processing = false
|
||||
banner.text = i18nc("@info:status", "The security phrase was not correct.")
|
||||
banner.visible = true
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title", "Unlock using Passphrase")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormTextDelegate {
|
||||
description: i18nc("@info", "If you have a backup passphrase for this account, enter it below.")
|
||||
}
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: passwordField
|
||||
label: i18nc("@label:textbox", "Backup Password:")
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
FormCard.FormButtonDelegate {
|
||||
id: unlockButton
|
||||
text: i18nc("@action:button", "Unlock")
|
||||
icon.name: "unlock"
|
||||
enabled: passwordField.text.length > 0 && !ssssHandler.processing
|
||||
onClicked: {
|
||||
ssssHandler.processing = true
|
||||
banner.visible = false
|
||||
ssssHandler.unlockSSSSWithPassphrase(passwordField.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title", "Unlock using Security Key")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormTextDelegate {
|
||||
description: i18nc("@info", "If you have a security key for this account, enter it below or upload it as a file.")
|
||||
}
|
||||
FormCard.FormTextFieldDelegate {
|
||||
id: securityKeyField
|
||||
label: i18nc("@label:textbox", "Security Key:")
|
||||
echoMode: TextInput.Password
|
||||
}
|
||||
FormCard.FormButtonDelegate {
|
||||
id: uploadSecurityKeyButton
|
||||
text: i18nc("@action:button", "Upload from File")
|
||||
icon.name: "cloud-upload"
|
||||
enabled: !ssssHandler.processing
|
||||
onClicked: {
|
||||
ssssHandler.processing = true
|
||||
openFileDialog.open()
|
||||
}
|
||||
}
|
||||
FormCard.FormButtonDelegate {
|
||||
id: unlockSecurityKeyButton
|
||||
text: i18nc("@action:button", "Unlock")
|
||||
icon.name: "unlock"
|
||||
enabled: securityKeyField.text.length > 0 && !ssssHandler.processing
|
||||
onClicked: {
|
||||
ssssHandler.processing = true
|
||||
ssssHandler.unlockSSSSFromSecurityKey(securityKeyField.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title", "Unlock from Cross-Signing")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormTextDelegate {
|
||||
description: i18nc("@info", "If you have previously verified this device, you can try loading the backup key from other devices by clicking the button below.")
|
||||
}
|
||||
FormCard.FormButtonDelegate {
|
||||
id: unlockCrossSigningButton
|
||||
icon.name: "emblem-shared-symbolic"
|
||||
text: i18nc("@action:button", "Request from other Devices")
|
||||
enabled: !ssssHandler.processing
|
||||
onClicked: {
|
||||
ssssHandler.processing = true
|
||||
ssssHandler.unlockSSSSFromCrossSigning()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property OpenFileDialog openFileDialog: OpenFileDialog {
|
||||
id: openFileDialog
|
||||
onChosen: securityKeyField.text = Controller.loadFileContent(path)
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,13 @@ import org.kde.neochat
|
||||
Kirigami.Dialog {
|
||||
id: root
|
||||
|
||||
// This dialog is sometimes used outside the context of a room, e.g., when scanning a user's QR code.
|
||||
// Make sure that code is prepared to deal with this property being null
|
||||
property NeoChatRoom room
|
||||
property var user
|
||||
|
||||
property NeoChatConnection connection
|
||||
|
||||
parent: applicationWindow().overlay
|
||||
|
||||
leftPadding: 0
|
||||
@@ -102,19 +106,19 @@ Kirigami.Dialog {
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: !root.user.isLocalUser
|
||||
visible: !root.user.isLocalUser && !!root.user.object
|
||||
action: Kirigami.Action {
|
||||
text: room.connection.isIgnored(root.user.object) ? i18n("Unignore this user") : i18n("Ignore this user")
|
||||
text: !!root.user.object && root.connection.isIgnored(root.user.object) ? i18n("Unignore this user") : i18n("Ignore this user")
|
||||
icon.name: "im-invisible-user"
|
||||
onTriggered: {
|
||||
root.close();
|
||||
room.connection.isIgnored(root.user.object) ? room.connection.removeFromIgnoredUsers(root.user.object) : room.connection.addToIgnoredUsers(root.user.object);
|
||||
root.connection.isIgnored(root.user.object) ? root.connection.removeFromIgnoredUsers(root.user.object) : root.connection.addToIgnoredUsers(root.user.object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: !root.user.isLocalUser && room.canSendState("kick") && room.containsUser(root.user.id) && room.getUserPowerLevel(root.user.id) < room.getUserPowerLevel(root.room.connection.localUser.id)
|
||||
visible: root.room && !root.user.isLocalUser && room.canSendState("kick") && room.containsUser(root.user.id) && room.getUserPowerLevel(root.user.id) < room.getUserPowerLevel(root.connection.localUser.id)
|
||||
|
||||
action: Kirigami.Action {
|
||||
text: i18n("Kick this user")
|
||||
@@ -127,10 +131,10 @@ Kirigami.Dialog {
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: !root.user.isLocalUser && room.canSendState("invite") && !room.containsUser(root.user.id)
|
||||
visible: root.room && !root.user.isLocalUser && room.canSendState("invite") && !room.containsUser(root.user.id)
|
||||
|
||||
action: Kirigami.Action {
|
||||
enabled: !room.isUserBanned(root.user.id)
|
||||
enabled: root.room && !root.room.isUserBanned(root.user.id)
|
||||
text: i18n("Invite this user")
|
||||
icon.name: "list-add-user"
|
||||
onTriggered: {
|
||||
@@ -141,7 +145,7 @@ Kirigami.Dialog {
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: !root.user.isLocalUser && room.canSendState("ban") && !room.isUserBanned(root.user.id) && room.getUserPowerLevel(root.user.id) < room.getUserPowerLevel(root.room.connection.localUser.id)
|
||||
visible: root.room && !root.user.isLocalUser && room.canSendState("ban") && !room.isUserBanned(root.user.id) && room.getUserPowerLevel(root.user.id) < room.getUserPowerLevel(root.room.connection.localUser.id)
|
||||
|
||||
action: Kirigami.Action {
|
||||
text: i18n("Ban this user")
|
||||
@@ -161,7 +165,7 @@ Kirigami.Dialog {
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: !root.user.isLocalUser && room.canSendState("ban") && room.isUserBanned(root.user.id)
|
||||
visible: root.room && !root.user.isLocalUser && room.canSendState("ban") && room.isUserBanned(root.user.id)
|
||||
|
||||
action: Kirigami.Action {
|
||||
text: i18n("Unban this user")
|
||||
@@ -175,7 +179,7 @@ Kirigami.Dialog {
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: room.canSendState("m.room.power_levels")
|
||||
visible: root.room && room.canSendState("m.room.power_levels")
|
||||
action: Kirigami.Action {
|
||||
text: i18n("Set user power level")
|
||||
icon.name: "visibility"
|
||||
@@ -199,7 +203,7 @@ Kirigami.Dialog {
|
||||
}
|
||||
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: root.user.isLocalUser || room.canSendState("redact")
|
||||
visible: root.room && (root.user.isLocalUser || room.canSendState("redact"))
|
||||
|
||||
action: Kirigami.Action {
|
||||
text: i18n("Remove recent messages by this user")
|
||||
@@ -221,10 +225,10 @@ Kirigami.Dialog {
|
||||
FormCard.FormButtonDelegate {
|
||||
visible: !root.user.isLocalUser
|
||||
action: Kirigami.Action {
|
||||
text: root.room.connection.directChatExists(root.user.object) ? i18nc("%1 is the name of the user.", "Chat with %1", root.user.escapedDisplayName) : i18n("Invite to private chat")
|
||||
text: root.connection.directChatExists(root.user.object) ? i18nc("%1 is the name of the user.", "Chat with %1", root.user.escapedDisplayName) : i18n("Invite to private chat")
|
||||
icon.name: "document-send"
|
||||
onTriggered: {
|
||||
root.room.connection.openOrCreateDirectChat(root.user.object);
|
||||
root.connection.openOrCreateDirectChat(root.user.object);
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||
import org.kde.kirigamiaddons.delegates as Delegates
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
import org.kde.neochat.config
|
||||
import org.kde.neochat.accounts
|
||||
|
||||
@@ -51,7 +52,7 @@ RowLayout {
|
||||
if (button == Qt.RightButton) {
|
||||
accountMenu.open();
|
||||
} else {
|
||||
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'AccountEditorPage.qml'), {
|
||||
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'AccountEditorPage.qml'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18n("Account editor")
|
||||
@@ -136,7 +137,7 @@ RowLayout {
|
||||
}
|
||||
QQC2.ToolButton {
|
||||
icon.name: "settings-configure"
|
||||
onClicked: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'SettingsPage.qml'), {
|
||||
onClicked: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'NeoChatSettings.qml'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18n("Configure"),
|
||||
|
||||
@@ -101,6 +101,7 @@ SearchPage {
|
||||
let dialog = manualUserDialog.createObject(applicationWindow().overlay, {
|
||||
connection: root.connection
|
||||
});
|
||||
dialog.parent = root.Window.window.overlay;
|
||||
dialog.accepted.connect(() => {
|
||||
root.closeDialog();
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
import org.kde.neochat.accounts
|
||||
|
||||
FormCard.FormCardPage {
|
||||
@@ -216,7 +217,7 @@ FormCard.FormCardPage {
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18nc("@action:button", "Open proxy settings")
|
||||
icon.name: "settings-configure"
|
||||
onClicked: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "NetworkProxyPage.qml"), {}, {
|
||||
onClicked: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat.settings", "NetworkProxyPage.qml"), {}, {
|
||||
title: i18nc("@title:window", "Proxy Settings")
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import QtQuick.Controls as QQC2
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
import org.kde.neochat.config
|
||||
import org.kde.neochat.accounts
|
||||
|
||||
@@ -135,6 +136,17 @@ Kirigami.ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
function onAskJoinRoom(room) {
|
||||
joinRoomDialog.createObject(applicationWindow(), {
|
||||
room: room,
|
||||
connection: root.connection
|
||||
}).open();
|
||||
}
|
||||
|
||||
function onShowUserDetail(user) {
|
||||
root.showUserDetail(user);
|
||||
}
|
||||
|
||||
function onPushSpaceHome(room) {
|
||||
root.spaceHomePage = pageStack.push(Qt.createComponent('org.kde.neochat', 'SpaceHomePage.qml'));
|
||||
root.spaceHomePage.forceActiveFocus();
|
||||
@@ -174,20 +186,16 @@ Kirigami.ApplicationWindow {
|
||||
roomItem.forceActiveFocus();
|
||||
}
|
||||
|
||||
function onOpenRoomInNewWindow(room) {
|
||||
const secondaryWindow = roomWindow.createObject(undefined, {
|
||||
currentRoom: room,
|
||||
connection: root.connection
|
||||
});
|
||||
secondaryWindow.width = root.width - pageStack.get(0).width;
|
||||
secondaryWindow.show();
|
||||
}
|
||||
|
||||
function onAskDirectChatConfirmation(user) {
|
||||
askDirectChatConfirmationComponent.createObject(QQC2.ApplicationWindow.overlay, {
|
||||
user: user
|
||||
}).open();
|
||||
}
|
||||
function onExternalUrl(url) {
|
||||
let dialog = Qt.createComponent("org.kde.neochat", "ConfirmUrlDialog.qml").createObject(applicationWindow());
|
||||
dialog.link = url;
|
||||
dialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
function pushReplaceLayer(page, args) {
|
||||
@@ -213,6 +221,9 @@ Kirigami.ApplicationWindow {
|
||||
|
||||
connection: root.connection
|
||||
|
||||
handleOpenIcon.source: "arrow-right"
|
||||
handleClosedIcon.source: "arrow-left"
|
||||
|
||||
// Connect to the onClicked function of the RoomDrawer handle button
|
||||
Connections {
|
||||
target: contextDrawer.handle.children[0]
|
||||
@@ -396,8 +407,8 @@ Kirigami.ApplicationWindow {
|
||||
}
|
||||
|
||||
Component {
|
||||
id: roomWindow
|
||||
RoomWindow {}
|
||||
id: joinRoomDialog
|
||||
JoinRoomDialog {}
|
||||
}
|
||||
|
||||
Component {
|
||||
@@ -447,7 +458,7 @@ Kirigami.ApplicationWindow {
|
||||
Shortcut {
|
||||
sequence: "Ctrl+Shift+,"
|
||||
onActivated: {
|
||||
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'SettingsPage.qml'), {
|
||||
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'NeoChatSettings.qml'), {
|
||||
connection: root.connection
|
||||
}, {
|
||||
title: i18n("Configure"),
|
||||
@@ -477,4 +488,16 @@ Kirigami.ApplicationWindow {
|
||||
dialog.closeDialog()
|
||||
})
|
||||
}
|
||||
function showUserDetail(user) {
|
||||
userDetailDialog.createObject(root.QQC2.ApplicationWindow.window, {
|
||||
room: RoomManager.currentRoom ? RoomManager.currentRoom : null,
|
||||
user: RoomManager.currentRoom ? RoomManager.currentRoom.getUser(user.id) : QmlUtils.getUser(user),
|
||||
connection: root.connection
|
||||
}).open();
|
||||
}
|
||||
|
||||
Component {
|
||||
id: userDetailDialog
|
||||
UserDetailDialog {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ void RoomManager::resolveResource(const QString &idOrUri, const QString &action)
|
||||
if (!m_connection) {
|
||||
return;
|
||||
}
|
||||
if (!action.isEmpty()) {
|
||||
if (!action.isEmpty() && (uri.type() != Uri::UserId || action != "join"_ls)) {
|
||||
uri.setAction(action);
|
||||
}
|
||||
// TODO we should allow the user to select a connection.
|
||||
@@ -99,7 +99,9 @@ void RoomManager::resolveResource(const QString &idOrUri, const QString &action)
|
||||
|
||||
const auto result = visitResource(m_connection, uri);
|
||||
if (result == Quotient::CouldNotResolve) {
|
||||
Q_EMIT warning(i18n("Room not found"), i18n("There's no room %1 in the room list. Check the spelling and the account.", idOrUri));
|
||||
if (uri.type() == Uri::RoomAlias || uri.type() == Uri::RoomId) {
|
||||
Q_EMIT askJoinRoom(uri.primaryId());
|
||||
}
|
||||
} else { // Invalid cases should have been eliminated earlier
|
||||
Q_ASSERT(result == Quotient::UriResolved);
|
||||
|
||||
@@ -248,12 +250,6 @@ void RoomManager::openRoomForActiveConnection()
|
||||
}
|
||||
}
|
||||
|
||||
void RoomManager::openWindow(NeoChatRoom *room)
|
||||
{
|
||||
// forward the call to QML
|
||||
Q_EMIT openRoomInNewWindow(room);
|
||||
}
|
||||
|
||||
UriResolveResult RoomManager::visitUser(User *user, const QString &action)
|
||||
{
|
||||
if (action == "mention"_ls || action.isEmpty()) {
|
||||
@@ -355,19 +351,7 @@ void RoomManager::knockRoom(Quotient::Connection *account, const QString &roomAl
|
||||
|
||||
bool RoomManager::visitNonMatrix(const QUrl &url)
|
||||
{
|
||||
#ifdef Q_OS_ANDROID
|
||||
if (!QDesktopServices::openUrl(url)) {
|
||||
Q_EMIT warning(i18n("No application for the link"), i18n("Your operating system could not find an application for the link."));
|
||||
}
|
||||
#else
|
||||
auto *job = new KIO::OpenUrlJob(url);
|
||||
connect(job, &KJob::finished, this, [this](KJob *job) {
|
||||
if (job->error()) {
|
||||
Q_EMIT warning(i18n("Could not open URL"), job->errorString());
|
||||
}
|
||||
});
|
||||
job->start();
|
||||
#endif
|
||||
Q_EMIT externalUrl(url);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -148,13 +148,6 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE void loadInitialRoom();
|
||||
|
||||
/**
|
||||
* @brief Open a new window with the given room.
|
||||
*
|
||||
* The open window will have its own message list for the given room.
|
||||
*/
|
||||
Q_INVOKABLE void openWindow(NeoChatRoom *room);
|
||||
|
||||
/**
|
||||
* @brief Leave the room and close it if it is open.
|
||||
*/
|
||||
@@ -215,6 +208,9 @@ public:
|
||||
void setConnection(NeoChatConnection *connection);
|
||||
|
||||
Q_SIGNALS:
|
||||
/** Ask the user whether the room should be joined. */
|
||||
void askJoinRoom(const QString &nameOrId);
|
||||
|
||||
void currentRoomChanged();
|
||||
|
||||
/**
|
||||
@@ -337,10 +333,12 @@ Q_SIGNALS:
|
||||
void directChatsActiveChanged();
|
||||
void lastSpaceIdChanged();
|
||||
|
||||
void externalUrl(const QUrl &url);
|
||||
|
||||
private:
|
||||
void openRoomForActiveConnection();
|
||||
|
||||
NeoChatRoom *m_currentRoom;
|
||||
QPointer<NeoChatRoom> m_currentRoom;
|
||||
NeoChatRoom *m_lastCurrentRoom;
|
||||
QString m_arg;
|
||||
KSharedConfig::Ptr m_config;
|
||||
|
||||
@@ -29,7 +29,7 @@ FormCard.FormCardPage {
|
||||
id: accountDelegate
|
||||
required property NeoChatConnection connection
|
||||
Layout.fillWidth: true
|
||||
onClicked: applicationWindow().pageStack.layers.push(Qt.createComponent('org.kde.neochat', 'AccountEditorPage.qml'), {
|
||||
onClicked: applicationWindow().pageStack.layers.push('AccountEditorPage.qml', {
|
||||
connection: accountDelegate.connection
|
||||
}, {
|
||||
title: i18n("Account editor")
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user