Compare commits
102 Commits
work/tobia
...
v25.08.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ea6e0425a | ||
|
|
fe55ff19d2 | ||
|
|
c35bdc0592 | ||
|
|
c6fa5a10dd | ||
|
|
f956c33b82 | ||
|
|
0b9295e67e | ||
|
|
0fdade83e0 | ||
|
|
6a25945131 | ||
|
|
218222bc58 | ||
|
|
4300bba804 | ||
|
|
13f766d166 | ||
|
|
fd4e701c51 | ||
|
|
d8ff639374 | ||
|
|
4a50281152 | ||
|
|
acc9289d06 | ||
|
|
235bd21eaf | ||
|
|
c7c1c8fd5c | ||
|
|
fdbee5a508 | ||
|
|
fb58003451 | ||
|
|
debbe8e478 | ||
|
|
0ce86e5a08 | ||
|
|
6935d887c4 | ||
|
|
963346e0f4 | ||
|
|
e37dd88c43 | ||
|
|
2aacb640c8 | ||
|
|
2d63a92702 | ||
|
|
de6731cfda | ||
|
|
7b3c40757c | ||
|
|
87f243ba8b | ||
|
|
192cdc1ff3 | ||
|
|
c72f77f7b6 | ||
|
|
d978f8de50 | ||
|
|
cf216268ab | ||
|
|
f9741a66c4 | ||
|
|
aac3bfda88 | ||
|
|
2722a6f2f0 | ||
|
|
ecf4b85f00 | ||
|
|
ec1413d1ce | ||
|
|
0b7a6df0a3 | ||
|
|
040efa46f9 | ||
|
|
ef4b41e6f8 | ||
|
|
80f81847f4 | ||
|
|
681a3c4036 | ||
|
|
fd27c70b85 | ||
|
|
435124ffe8 | ||
|
|
e2ca698389 | ||
|
|
7e9cfbedc9 | ||
|
|
eeed8a7277 | ||
|
|
aa8c515432 | ||
|
|
ba30014d40 | ||
|
|
d131030d47 | ||
|
|
23a0d91627 | ||
|
|
11d5a37ffe | ||
|
|
256a8e5a1e | ||
|
|
b608921d43 | ||
|
|
45a3984bf9 | ||
|
|
570e0425e9 | ||
|
|
09ed1bd616 | ||
|
|
3201426886 | ||
|
|
96dc83d807 | ||
|
|
ab1fb8ae97 | ||
|
|
c5a4b2a50a | ||
|
|
17fdad3afd | ||
|
|
64bc2691cb | ||
|
|
c7df34a9c8 | ||
|
|
1525c74b10 | ||
|
|
58b85622c5 | ||
|
|
796470d0e0 | ||
|
|
3a43d99575 | ||
|
|
a62798ef1e | ||
|
|
349d0c5f5f | ||
|
|
c917fc0166 | ||
|
|
a4f2d8fca1 | ||
|
|
fd18f88adf | ||
|
|
546694b08e | ||
|
|
699026fc2f | ||
|
|
c61c2d7437 | ||
|
|
56babbc1c5 | ||
|
|
74b3e703c1 | ||
|
|
f620221113 | ||
|
|
896c001430 | ||
|
|
defee77c96 | ||
|
|
4328ab8e89 | ||
|
|
54b081abba | ||
|
|
29686608e1 | ||
|
|
b720ecf29d | ||
|
|
fc859d679a | ||
|
|
3595ad9293 | ||
|
|
73f8ebc54e | ||
|
|
19cf534acd | ||
|
|
9b86088e26 | ||
|
|
a93117fcd6 | ||
|
|
ee20c90498 | ||
|
|
860a2267d5 | ||
|
|
cb9b2648ca | ||
|
|
1e798b6c15 | ||
|
|
124ffba5e0 | ||
|
|
3aaaa610df | ||
|
|
18e883834c | ||
|
|
6acbd2dffd | ||
|
|
dc5c27aa2d | ||
|
|
26774bbe56 |
@@ -8,13 +8,13 @@ cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# KDE Applications version, managed by release script.
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "25")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "11")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "08")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "3")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||
|
||||
set(KF_MIN_VERSION "6.12")
|
||||
set(KF_MIN_VERSION "6.16")
|
||||
set(QT_MIN_VERSION "6.5")
|
||||
|
||||
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
<name xml:lang="pl">NeoChat</name>
|
||||
<name xml:lang="pt">NeoChat</name>
|
||||
<name xml:lang="pt-BR">NeoChat</name>
|
||||
<name xml:lang="ro">NeoChat</name>
|
||||
<name xml:lang="ru">NeoChat</name>
|
||||
<name xml:lang="sa">नवचैट्</name>
|
||||
<name xml:lang="sk">NeoChat</name>
|
||||
@@ -49,7 +50,6 @@
|
||||
<name xml:lang="ta">நியோச்சாட்</name>
|
||||
<name xml:lang="tr">NeoChat</name>
|
||||
<name xml:lang="uk">NeoChat</name>
|
||||
<name xml:lang="x-test">xxNeoChatxx</name>
|
||||
<name xml:lang="zh-CN">NeoChat</name>
|
||||
<name xml:lang="zh-TW">NeoChat</name>
|
||||
<summary>Chat on Matrix</summary>
|
||||
@@ -75,6 +75,8 @@
|
||||
<summary xml:lang="nl">Chat op Matrix</summary>
|
||||
<summary xml:lang="nn">Prat med via Matrix</summary>
|
||||
<summary xml:lang="pl">Rozmawiaj na Matriksie</summary>
|
||||
<summary xml:lang="pt-BR">Bate-papo na Matrix</summary>
|
||||
<summary xml:lang="ro">Discutați pe Matrix</summary>
|
||||
<summary xml:lang="ru">Общение в Matrix</summary>
|
||||
<summary xml:lang="sa">Matrix इत्यत्र गपशपं कुर्वन्तु</summary>
|
||||
<summary xml:lang="sl">Klepet na Matrixu</summary>
|
||||
@@ -82,7 +84,6 @@
|
||||
<summary xml:lang="ta">மேட்ரிக்ஸுக்கான உரையாடல் செயலி</summary>
|
||||
<summary xml:lang="tr">Matrix Üzerinde Sohbet</summary>
|
||||
<summary xml:lang="uk">Спілкування у Matrix</summary>
|
||||
<summary xml:lang="x-test">xxChat on Matrixxx</summary>
|
||||
<summary xml:lang="zh-TW">在 Matrix 上聊天</summary>
|
||||
<description>
|
||||
<p>NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
|
||||
@@ -109,16 +110,17 @@
|
||||
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
|
||||
<p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p>
|
||||
<p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p>
|
||||
<p xml:lang="pt-BR">O NeoChat é um aplicativo de bate-papo que permite que você aproveite ao máximo a rede Matrix. Ele oferece uma maneira segura de enviar mensagens de texto, vídeos e arquivos de áudio para sua família, colegas e amigos.</p>
|
||||
<p xml:lang="ro">NeoChat e o aplicație de discuții ce vă ajută să profitați din plin de rețeaua Matrix. Aceasta oferă o modalitate sigură de a trimite mesaje textuale, videoclipuri și fișiere audio familiei, colegilor și prietenilor.</p>
|
||||
<p xml:lang="ru">NeoChat — приложение для общения, предоставляющее все преимущества сети Matrix. С его помощью можно безопасно отправлять текстовые сообщения, видеозаписи и звуковые файлы родственникам, коллегам и друзьям.</p>
|
||||
<p xml:lang="sa">NeoChat इति एकं गपशप-अनुप्रयोगं यत् भवान् Matrix-जालस्य पूर्णं लाभं ग्रहीतुं शक्नोति । एतत् भवन्तं भवतः परिवाराय, सहकारिभ्यः, मित्रेभ्यः च पाठसन्देशान्, भिडियो, श्रव्यसञ्चिकाः च प्रेषयितुं सुरक्षितं मार्गं प्रदाति ।</p>
|
||||
<p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p>
|
||||
<p xml:lang="sv">NeoChat är ett chattprogram som låter dig dra full nytta av Matrix-nätverket. Det ger dig ett säkert sätt att skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner.</p>
|
||||
<p xml:lang="tr">NeoChat, Matrix ağının tüm özelliklerini kullanan bir sohbet uygulamasıdır. Ailenize, arkadaşlarınıza ve iş arkadaşlarınıza metin iletileri, ses ve video dosyaları göndermenin kolay bir yolunu sunar.</p>
|
||||
<p xml:lang="uk">NeoChat є програмою для спілкування, за допомогою якої ви можете скористатися усіма перевагами мережі Matrix. За її допомогою ви можете безпечно надсилати текстові повідомлення, відео та звукові файли вашим родичам, колегам та друзям.</p>
|
||||
<p xml:lang="x-test">xxNeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.xx</p>
|
||||
<p xml:lang="zh-TW">NeoChat 是一個讓您能夠完全利用 Matrix 網路的聊天應用程式。它讓您安全地傳送文字訊息、影片或音訊檔給家人、同事或朋友等等。</p>
|
||||
<p>NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
|
||||
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. على هذا النحو يتم دعم كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP والخيوط وبعض جوانب التشفير من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار ، ولكن يبقى الهدف توفير الدعم النهائي للمواصفات بأكملها.</p>
|
||||
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. يوفر نيوتشات كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP و تعدد الخيوط وبعض جوانب التعمية من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار، ولكن يبقى الهدف توفير تطبيق للمواصفات بأكملها.</p>
|
||||
<p xml:lang="ca">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptatge d'extrem a extrem. Hi ha algunes altres omissions més petites a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu segueix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
|
||||
<p xml:lang="de">NeoChat versucht eine vollumfängliche Anwendung für die Spezifikation von Matrix zu sein. Damit wird alles der aktuellen stabilen Spezifikation mit den erwähnenswerten Ausnahmen von VoIP, Diskussionsfäden und ein paar Teilen der Ende-zu-Ende-Verschlüsselung unterstützt. Zudem sind andere kleinere Auslassungen vorhanden, da sich die Matrixspezifikation ständig weiterentwickelt. Nichtsdestotrotz soll letztendlich die gesamte Spezifikation unterstützt werden.</p>
|
||||
@@ -142,16 +144,17 @@
|
||||
<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>
|
||||
<p xml:lang="pt">O NeoChat pretende ser uma aplicação completa para a especificação do Matrix. Como tal, tudo o que existe na especificação estável actual, com as notáveis excepções do VoIP, tópicos e alguns aspectos da Encriptação Ponto-a-Ponto, são suportados. Existem mais algumas omissões, devido ao facto que a norma do Matrix está em constante evolução, mas o objectivo continua a ser oferecer o suporte eventual para a norma por inteiro.</p>
|
||||
<p xml:lang="pt-BR">O NeoChat pretende ser um aplicativo completo para a especificação Matrix. Dessa forma, tudo na especificação estável atual, com as notáveis exceções de VoIP, tópicos e alguns aspectos da criptografia de ponta a ponta, é suportado. Há algumas outras pequenas omissões devido ao fato de a especificação Matrix estar em constante evolução, mas o objetivo continua sendo fornecer suporte eventual para toda a especificação.</p>
|
||||
<p xml:lang="ro">NeoChat vrea să fie o aplicație completă pentru specificațiile Matrix. Astfel, susține tot ce se găsește acum în specificațiile stabile cu excepția VoIP, a firelor de discuții, și a unor părți din criptarea punct-la-punct. Sunt și câteva omisiuni minore din cauza faptului că specificația Matrix evoluează continuu, dar scopul rămâne acela de a implementa întreaga specificație.</p>
|
||||
<p xml:lang="ru">Целью создания NeoChat является полноценная реализация программы для спецификации Matrix. Как следствие, реализовано всё в текущей стабильной спецификации (за исключением голосовой интернет-связи, потоков и некоторых аспектов сквозного шифрования). Есть также несколько других незначительных пробелов, обусловленных постоянными изменениями спецификации Matrix. Тем не менее, стоит задача в итоге предоставить полную поддержку спецификации.</p>
|
||||
<p xml:lang="sa">NeoChat इत्यस्य उद्देश्यं Matrix विनिर्देशस्य कृते पूर्णतया विशेषतायुक्तः अनुप्रयोगः भवितुम् अस्ति । यथा तथा वर्तमानस्थिरविनिर्देशे सर्वं VoIP इत्यस्य उल्लेखनीयअपवादैः सह, थ्रेड्स तथा च End-to-End Encryption इत्यस्य केचन पक्षाः समर्थिताः सन्ति । अन्ये कतिचन लघु लोपाः सन्ति यतोहि Matrix spec निरन्तरं विकसितः अस्ति परन्तु उद्देश्यं सम्पूर्ण spec कृते अन्ततः समर्थनं प्रदातुं अवशिष्टम् अस्ति</p>
|
||||
<p xml:lang="sl">Neochat cilja, da bi bila popolna aplikacija po specifikaciji Matrixa. Kot takšna vsebuje vse v trenutni stabilni specifikaciji z pomembnimi izjemami pri VoIP, nitih in nekaterih vidikov šifriranja od konca do konca. Obstaja nekaj drugih manjših opustitev zaradi dejstva, da se specifikacija Matrix nenehno razvija, vendar cilj ostaja zagotoviti morebitno podporo celotni specifikaciji.</p>
|
||||
<p xml:lang="sv">NeoChat har som mål att vara ett fullständigt program enligt Matrix-specifikationen. Som sådant stöds allt i den nuvarande stabila specifikationen, med de nämnvärda undantagen VoIP, trådar och några aspekter av kryptering hela vägen. Det finns några ytterligare utelämnanden på grund av att Matrix-specifikationen hela tiden utvecklas, men målet förblir att till slut erbjuda stöd för hela specifikationen.</p>
|
||||
<p xml:lang="tr">NeoChat, Matrix belirtimi için tam özellikli bir uygulama olmayı hedefler. Bu nedenle; VoIP, ileti zincirleri ve Uçtan Uca Şifreleme’nin bazı yönleri gibi dikkate değer istisnalar dışında var olan kararlı belirtimdeki her şey desteklenir. Matrix belirtiminin sürekli gelişmesi nedeniyle birkaç küçük eksiklik daha var; ancak amaç tüm belirtim için nihai destek sağlamak olmayı sürdürüyor.</p>
|
||||
<p xml:lang="uk">Метою створення NeoChat є повноцінна реалізація програми для специфікації Matrix. Як наслідок, реалізовано усе у поточній стабільній специфікації, окрім голосового інтернет-зв'язку, потоків та деяких аспектів міжвузлового шифрування. Є також декілька інших незначних прогалин через те, що специфікація Matrix постійно змінюється, але метою лишається повна підтримка специфікації.</p>
|
||||
<p xml:lang="x-test">xxNeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.xx</p>
|
||||
<p xml:lang="zh-TW">NeoChat 以完整支援 Matrix 標準為目標,因此目前穩定版標準除了 VoIP、對話串與端對端加密的某些部分以外的所有部分都有支援。其他部分還有一些較小的不支援的部分,這是因為 Matrix 標準隨時都在改進,但目標仍然時最終提供整個標準的完整支援。</p>
|
||||
<p>Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:</p>
|
||||
<p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يدعم نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p>
|
||||
<p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يوفر نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p>
|
||||
<p xml:lang="ca">A causa de la naturalesa del desenvolupament de l'especificació de Matrix, el NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
|
||||
<p xml:lang="ca-valencia">A causa de la naturalea del desenvolupament de l'especificació de Matrix, NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
|
||||
<p xml:lang="de">Durch die Weiterentwicklung der Matrix-Spezifikation unterstützt auch NeoChat einige als noch instabil gekennzeichnete Funktionen. Derzeit sind das:</p>
|
||||
@@ -175,6 +178,8 @@
|
||||
<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>
|
||||
<p xml:lang="pt">Devido à natureza do desenvolvimento da especificação do Matrix, o NeoChat também suporta diversas funcionalidades instáveis. De momento são:</p>
|
||||
<p xml:lang="pt-BR">Devido à natureza do desenvolvimento da especificação Matrix, o NeoChat também suporta diversos recursos instáveis. Atualmente, são eles:</p>
|
||||
<p xml:lang="ro">Datorită modului de dezvoltare a specificațiilor Matrix, NeoChat susține și numeroase caracteristici nestabile. Acum, acestea sunt:</p>
|
||||
<p xml:lang="ru">В силу природы разработки спецификации Matrix в NeoChat тоже предусмотрена поддержка многочисленных нестабильных возможностей. В текущей версии это следующие возможности:</p>
|
||||
<p xml:lang="sa">Matrix विनिर्देशविकासस्य प्रकृतेः कारणात् NeoChat अपि अनेकानाम् अस्थिरविशेषतानां समर्थनं करोति । सम्प्रति एते सन्ति :</p>
|
||||
<p xml:lang="sl">Zaradi narave razvoja specifikacije Matrixa NeoChat podpira tudi številne nestabilne zmožnosti. Trenutno so to:</p>
|
||||
@@ -182,13 +187,12 @@
|
||||
<p xml:lang="ta">மேட்ரிக்ஸு நெறிமுறை வரையறுக்கப்படும் விதத்தின் காரணமாக, பல நிலையற்ற அம்சங்களையும் நியோச்சாட் ஆதரிக்கிறது. தற்போது ஆதரிக்கப்படுபவை:</p>
|
||||
<p xml:lang="tr">NeoChat, Matrix belirtimi geliştirmesinin doğası gereği çok sayıda kararsız özelliği de destekler. Şu anda bunlar:</p>
|
||||
<p xml:lang="uk">Через природу розробки специфікації Matrix, у NeoChat також передбачено підтримку численних нестабільних можливостей. У поточній версії цими можливостями є:</p>
|
||||
<p xml:lang="x-test">xxDue to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:xx</p>
|
||||
<p xml:lang="zh-TW">由於 Matrix 標準的開發流程的緣故,NeoChat 也支援數個非穩定版的功能。目前這些功能是:</p>
|
||||
<ul>
|
||||
<li>Polls - MSC3381</li>
|
||||
<li xml:lang="ar">التصويت - MSC3381</li>
|
||||
<li xml:lang="ca">Enquestes - MSC3381</li>
|
||||
<li xml:lang="ca-valencia">Enquestes - MSC3381</li>
|
||||
<li xml:lang="ca">Votacions - MSC3381</li>
|
||||
<li xml:lang="ca-valencia">Votacions - MSC3381</li>
|
||||
<li xml:lang="el">Δημοσκοπήσεις - MSC3381</li>
|
||||
<li xml:lang="en-GB">Polls - MSC3381</li>
|
||||
<li xml:lang="eo">Enketoj - MSC3381</li>
|
||||
@@ -209,6 +213,8 @@
|
||||
<li xml:lang="nn">Avstemmingar – MSC3381</li>
|
||||
<li xml:lang="pl">Ankiety - MSC3381</li>
|
||||
<li xml:lang="pt">Inquéritos - MSC3381</li>
|
||||
<li xml:lang="pt-BR">Enquetes - MSC3381</li>
|
||||
<li xml:lang="ro">Sondaje - MSC3381</li>
|
||||
<li xml:lang="ru">Голосования — MSC3381</li>
|
||||
<li xml:lang="sa">मतदान - MSC3381</li>
|
||||
<li xml:lang="sl">Polls - MSC3381</li>
|
||||
@@ -216,7 +222,6 @@
|
||||
<li xml:lang="ta">வாக்கெடுப்புகள் - MSC3381</li>
|
||||
<li xml:lang="tr">Anketler — MSC3381</li>
|
||||
<li xml:lang="uk">Опитування - MSC3381</li>
|
||||
<li xml:lang="x-test">xxPolls - MSC3381xx</li>
|
||||
<li xml:lang="zh-TW">投票 - MSC3381</li>
|
||||
<li>Sticker Packs - MSC2545</li>
|
||||
<li xml:lang="ar">حزم الملصقات - MSC2545</li>
|
||||
@@ -242,6 +247,8 @@
|
||||
<li xml:lang="nn">Klistremerke-pakkar – MSC2545</li>
|
||||
<li xml:lang="pl">Paczki naklejek - MSC2545</li>
|
||||
<li xml:lang="pt">Pacotes de Autocolantes - MSC2545</li>
|
||||
<li xml:lang="pt-BR">Pacotes de Stickers - MSC2545</li>
|
||||
<li xml:lang="ro">Colecții de abțibilduri - MSC2545</li>
|
||||
<li xml:lang="ru">Наборы стикеров — MSC2545</li>
|
||||
<li xml:lang="sa">स्टिकर पैक - MSC2545</li>
|
||||
<li xml:lang="sl">Sticker Packs - MSC2545</li>
|
||||
@@ -249,7 +256,6 @@
|
||||
<li xml:lang="ta">ஒட்டி தொகுப்புகள் - MSC2545</li>
|
||||
<li xml:lang="tr">Çıkartma Paketleri — MSC2545</li>
|
||||
<li xml:lang="uk">Пакунки наліпок - MSC2545</li>
|
||||
<li xml:lang="x-test">xxSticker Packs - MSC2545xx</li>
|
||||
<li xml:lang="zh-TW">貼圖包 - MSC2545</li>
|
||||
<li>Location Events - MSC3488</li>
|
||||
<li xml:lang="ar">موقع الأحداث - MSC3488</li>
|
||||
@@ -275,6 +281,8 @@
|
||||
<li xml:lang="nn">Posisjonshendingar – MSC3488</li>
|
||||
<li xml:lang="pl">Wydarzenia w miejscach - MSC3488</li>
|
||||
<li xml:lang="pt">Eventos com Localizações - MSC3488</li>
|
||||
<li xml:lang="pt-BR">Localização de eventos - MSC3488</li>
|
||||
<li xml:lang="ro">Evenimente de amplasare - MSC3488</li>
|
||||
<li xml:lang="ru">События местоположения — MSC3488</li>
|
||||
<li xml:lang="sa">स्थान घटनाएँ - MSC3488</li>
|
||||
<li xml:lang="sl">Location Events - MSC3488</li>
|
||||
@@ -282,7 +290,6 @@
|
||||
<li xml:lang="ta">இட நிகழ்வுகள் - MSC3488</li>
|
||||
<li xml:lang="tr">Konum Etkinlikleri — MSC3488</li>
|
||||
<li xml:lang="uk">Місцеві зустрічі - MSC3488</li>
|
||||
<li xml:lang="x-test">xxLocation Events - MSC3488xx</li>
|
||||
<li xml:lang="zh-TW">位置事件 - MSC3488</li>
|
||||
</ul>
|
||||
</description>
|
||||
@@ -344,6 +351,8 @@
|
||||
<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>
|
||||
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
|
||||
<caption xml:lang="pt-BR">Visão principal com lista de salas, bate-papo e informações sobre as salas</caption>
|
||||
<caption xml:lang="ro">Vederea principală cu lista de camere, discuție, și informații despre cameră</caption>
|
||||
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
|
||||
<caption xml:lang="sa">कक्षसूची, गपशपः, कक्षसूचना च सह मुख्यदृश्यम्</caption>
|
||||
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
|
||||
@@ -351,7 +360,6 @@
|
||||
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
|
||||
<caption xml:lang="tr">Oda listesini, sohbet penceresini ve oda bilgisini gösteren ana görünüm</caption>
|
||||
<caption xml:lang="uk">Головна панель із списком кімнат, спілкуванням та даними щодо кімнати</caption>
|
||||
<caption xml:lang="x-test">xxMain view with room list, chat, and room informationxx</caption>
|
||||
<caption xml:lang="zh-TW">主頁面,包含聊天室列表、聊天內容,與聊天室資訊</caption>
|
||||
</screenshot>
|
||||
<screenshot type="default">
|
||||
@@ -380,6 +388,8 @@
|
||||
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
|
||||
<caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption>
|
||||
<caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption>
|
||||
<caption xml:lang="pt-BR">Descubra novas comunidades com os Espaços Matrix</caption>
|
||||
<caption xml:lang="ro">Descoperiți comunități noi cu Spații Matrix</caption>
|
||||
<caption xml:lang="ru">Поиск новых сообществ с помощью Matrix Spaces</caption>
|
||||
<caption xml:lang="sa">Matrix Spaces इत्यनेन सह नूतनानां समुदायानाम् अन्वेषणं कुर्वन्तु</caption>
|
||||
<caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption>
|
||||
@@ -387,7 +397,6 @@
|
||||
<caption xml:lang="ta">மேட்ரிக்ஸு இடங்களின் மூலம் புதிய சமூகங்களைக் கண்டுபிடிக்கலாம்</caption>
|
||||
<caption xml:lang="tr">Matrix Alanlar ile yeni topluluklar keşfedin</caption>
|
||||
<caption xml:lang="uk">Пошук нових спільнот за допомогою Matrix Spaces</caption>
|
||||
<caption xml:lang="x-test">xxDiscover new communities with Matrix Spacesxx</caption>
|
||||
<caption xml:lang="zh-TW">利用 Matrix 聊天空間發現新的社群</caption>
|
||||
</screenshot>
|
||||
<!--
|
||||
@@ -424,6 +433,8 @@
|
||||
<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>
|
||||
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
|
||||
<caption xml:lang="pt-BR">Visão principal com lista de salas, bate-papo e informações sobre as salas</caption>
|
||||
<caption xml:lang="ro">Vederea principală cu lista de camere, discuție, și informații despre cameră</caption>
|
||||
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
|
||||
<caption xml:lang="sa">कक्षसूची, गपशपः, कक्षसूचना च सह मुख्यदृश्यम्</caption>
|
||||
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
|
||||
@@ -431,7 +442,6 @@
|
||||
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
|
||||
<caption xml:lang="tr">Oda listesini, sohbet penceresini ve oda bilgisini gösteren ana görünüm</caption>
|
||||
<caption xml:lang="uk">Головна панель із списком кімнат, спілкуванням та даними щодо кімнати</caption>
|
||||
<caption xml:lang="x-test">xxMain view with room list, chat, and room informationxx</caption>
|
||||
<caption xml:lang="zh-TW">主頁面,包含聊天室列表、聊天內容,與聊天室資訊</caption>
|
||||
</screenshot>
|
||||
<screenshot environment="windows">
|
||||
@@ -462,6 +472,8 @@
|
||||
<caption xml:lang="nn">Innloggingsbilete</caption>
|
||||
<caption xml:lang="pl">Ekran logowania</caption>
|
||||
<caption xml:lang="pt">Ecrã de autenticação</caption>
|
||||
<caption xml:lang="pt-BR">Tela de login</caption>
|
||||
<caption xml:lang="ro">Ecran de autentificare</caption>
|
||||
<caption xml:lang="ru">Окно входа</caption>
|
||||
<caption xml:lang="sa">लॉगिन् स्क्रीन</caption>
|
||||
<caption xml:lang="sl">Prijavni zaslon</caption>
|
||||
@@ -469,7 +481,6 @@
|
||||
<caption xml:lang="ta">நுழைவுத் திரை</caption>
|
||||
<caption xml:lang="tr">Oturum açma ekranı</caption>
|
||||
<caption xml:lang="uk">Вікно входу</caption>
|
||||
<caption xml:lang="x-test">xxLogin screenxx</caption>
|
||||
<caption xml:lang="zh-TW">登入畫面</caption>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
@@ -477,6 +488,10 @@
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
</content_rating>
|
||||
<releases>
|
||||
<release version="25.08.3" date="2025-11-06"/>
|
||||
<release version="25.08.2" date="2025-10-09"/>
|
||||
<release version="25.08.1" date="2025-09-11"/>
|
||||
<release version="25.08.0" date="2025-08-14"/>
|
||||
<release version="25.04.3" date="2025-07-03"/>
|
||||
<release version="25.04.2" date="2025-06-05"/>
|
||||
<release version="25.04.1" date="2025-05-08"/>
|
||||
|
||||
@@ -44,7 +44,6 @@ Name[sv]=NeoChat
|
||||
Name[ta]=நியோச்சாட்
|
||||
Name[tr]=NeoChat
|
||||
Name[uk]=NeoChat
|
||||
Name[x-test]=xxNeoChatxx
|
||||
Name[zh_CN]=NeoChat
|
||||
Name[zh_TW]=NeoChat
|
||||
GenericName=Matrix Client
|
||||
@@ -88,7 +87,6 @@ GenericName[sv]=Matrix-klient
|
||||
GenericName[ta]=Matrix வாங்கி
|
||||
GenericName[tr]=Matrix İstemcisi
|
||||
GenericName[uk]=Клієнт Matrix
|
||||
GenericName[x-test]=xxMatrix Clientxx
|
||||
GenericName[zh_CN]=Matrix 客户端
|
||||
GenericName[zh_TW]=Matrix 用戶端
|
||||
Comment=Chat on Matrix
|
||||
@@ -114,6 +112,7 @@ Comment[lv]=Tērzējiet „Matrix“ tīklā
|
||||
Comment[nl]=Chat op Matrix
|
||||
Comment[pl]=Rozmawiaj na Matriksie
|
||||
Comment[pt_BR]=Bate papo na Matrix
|
||||
Comment[ro]=Discutați pe Matrix
|
||||
Comment[ru]=Общение в Matrix
|
||||
Comment[sa]=Matrix इत्यत्र गपशपं कुर्वन्तु
|
||||
Comment[sl]=Klepet na Matrixu
|
||||
@@ -121,7 +120,6 @@ Comment[sv]=Chatta på Matrix
|
||||
Comment[ta]=மேட்ரிக்ஸில் உரையாட உதவும்
|
||||
Comment[tr]=Matrix üzerinde sohbet edin
|
||||
Comment[uk]=Спілкування у Matrix
|
||||
Comment[x-test]=xxChat on Matrixxx
|
||||
Comment[zh_CN]=在 Matrix 上聊天
|
||||
Comment[zh_TW]=在 Matrix 上聊天
|
||||
MimeType=x-scheme-handler/matrix;
|
||||
|
||||
551
po/ar/neochat.po
551
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
397
po/az/neochat.po
397
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
417
po/ca/neochat.po
417
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
417
po/cs/neochat.po
417
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
391
po/da/neochat.po
391
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
447
po/de/neochat.po
447
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
417
po/el/neochat.po
417
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
425
po/eo/neochat.po
425
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
401
po/es/neochat.po
401
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
423
po/eu/neochat.po
423
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
494
po/fi/neochat.po
494
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
407
po/fr/neochat.po
407
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
433
po/gl/neochat.po
433
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
414
po/he/neochat.po
414
po/he/neochat.po
File diff suppressed because it is too large
Load Diff
425
po/hi/neochat.po
425
po/hi/neochat.po
File diff suppressed because it is too large
Load Diff
433
po/hu/neochat.po
433
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
509
po/ia/neochat.po
509
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
409
po/id/neochat.po
409
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
398
po/ie/neochat.po
398
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
413
po/it/neochat.po
413
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
365
po/ja/neochat.po
365
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
406
po/ka/neochat.po
406
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
404
po/ko/neochat.po
404
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
1149
po/lt/neochat.po
1149
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
433
po/lv/neochat.po
433
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
410
po/nl/neochat.po
410
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
408
po/nn/neochat.po
408
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
396
po/pa/neochat.po
396
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
911
po/pl/neochat.po
911
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
409
po/pt/neochat.po
409
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
3956
po/pt_BR/neochat.po
3956
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
6548
po/ro/neochat.po
Normal file
6548
po/ro/neochat.po
Normal file
File diff suppressed because it is too large
Load Diff
122
po/ru/docs/neochat/man-neochat.1.docbook
Normal file
122
po/ru/docs/neochat/man-neochat.1.docbook
Normal file
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" ?>
|
||||
<!DOCTYPE refentry PUBLIC "-//KDE//DTD DocBook XML V4.5-Based Variant V1.1//EN" "dtd/kdedbx45.dtd" [
|
||||
<!ENTITY % Russian "INCLUDE">
|
||||
]>
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
|
||||
SPDX-License-Identifier: CC-BY-SA-4.0
|
||||
-->
|
||||
|
||||
<refentry lang="&language;">
|
||||
<refentryinfo>
|
||||
<title
|
||||
>Руководство пользователя NeoChat</title>
|
||||
<author
|
||||
><firstname
|
||||
>Carl</firstname
|
||||
><surname
|
||||
>Schwan</surname
|
||||
> <contrib
|
||||
>man-страница NeoChat.</contrib
|
||||
> <email
|
||||
>carl@carlschwan.eu</email
|
||||
></author>
|
||||
<date
|
||||
>2022-11-01</date>
|
||||
<releaseinfo
|
||||
>22.09</releaseinfo>
|
||||
<productname
|
||||
>NeoChat</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>
|
||||
<command
|
||||
>neochat</command>
|
||||
</refentrytitle>
|
||||
<manvolnum
|
||||
>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname
|
||||
>neochat</refname>
|
||||
<refpurpose
|
||||
>Клиент для взаимодействия с протоколом обмена сообщениями Matrix</refpurpose>
|
||||
</refnamediv>
|
||||
<!-- body begins here -->
|
||||
<refsynopsisdiv id='synopsis'>
|
||||
<cmdsynopsis
|
||||
><command
|
||||
>neochat</command
|
||||
> <arg choice="opt"
|
||||
><replaceable
|
||||
>URI</replaceable
|
||||
></arg
|
||||
> </cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
||||
<refsect1 id="description">
|
||||
<title
|
||||
>Описание</title>
|
||||
<para
|
||||
><command
|
||||
>neochat</command
|
||||
> — приложение для настольных и мобильных устройств, позволяющее общаться в чатах с помощью протокола Matrix. </para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="options"
|
||||
><title
|
||||
>Параметры</title>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term
|
||||
><option
|
||||
>URI</option
|
||||
></term>
|
||||
<listitem>
|
||||
<para
|
||||
>URI-адрес пользователя или комнаты в Matrix, например: matrix:u/user:example.org и matrix:r/root:example.org. NeoChat попытается открыть указанную комнату или беседу. </para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="bug">
|
||||
<title
|
||||
>Отчёты об ошибках</title>
|
||||
<para
|
||||
>Сообщать об ошибках и отправлять предложения по улучшению можно по адресу <ulink url="https://bugs.kde.org/enter_bug.cgi?product=NeoChat&component=General"
|
||||
>https://bugs.kde.org/enter_bug.cgi?product=NeoChat&component=General</ulink
|
||||
></para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title
|
||||
>Смотрите также</title>
|
||||
<simplelist>
|
||||
<member
|
||||
>Список наиболее часто задаваемых вопросов о Matrix <ulink url="https://matrix.org/faq/"
|
||||
>https://matrix.org/faq/</ulink
|
||||
> </member>
|
||||
<member
|
||||
>kf5options(7)</member>
|
||||
<member
|
||||
>qt5options(7)</member>
|
||||
</simplelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="copyright"
|
||||
><title
|
||||
>Авторские права</title>
|
||||
<para
|
||||
>Авторские права © Tobias Fella, 2020–2022 </para>
|
||||
<para
|
||||
>Авторские права © Carl Schwan, 2020–2022 </para>
|
||||
<para
|
||||
>Лицензия: стандартная общественная лицензия GNU версии 3 или любой более поздней версии <<ulink url="https://www.gnu.org/licenses/gpl-3.0.html"
|
||||
>https://www.gnu.org/licenses/gpl-3.0.html</ulink
|
||||
>></para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
870
po/ru/neochat.po
870
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
425
po/sa/neochat.po
425
po/sa/neochat.po
File diff suppressed because it is too large
Load Diff
397
po/sk/neochat.po
397
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
414
po/sl/neochat.po
414
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
464
po/sv/neochat.po
464
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
425
po/ta/neochat.po
425
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
414
po/tr/neochat.po
414
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
404
po/uk/neochat.po
404
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -318,8 +318,7 @@ void Controller::listenForNotifications()
|
||||
connect(timer, &QTimer::timeout, qGuiApp, &QGuiApplication::quit);
|
||||
|
||||
connect(connector, &KUnifiedPush::Connector::messageReceived, [timer](const QByteArray &data) {
|
||||
instance().m_notificationsManager.postPushNotification(data);
|
||||
timer->stop();
|
||||
NotificationsManager::postPushNotification(data);
|
||||
});
|
||||
|
||||
// Wait five seconds to see if we received any messages or this happened to be an erroneous activation.
|
||||
|
||||
@@ -205,7 +205,7 @@ int main(int argc, char *argv[])
|
||||
parser.addOption(testOption);
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
QCommandLineOption dbusActivatedOption(u"dbus-activated"_s, i18n("Internal usage only."));
|
||||
QCommandLineOption dbusActivatedOption(u"dbus-activated"_s);
|
||||
dbusActivatedOption.setFlags(QCommandLineOption::Flag::HiddenFromHelp);
|
||||
parser.addOption(dbusActivatedOption);
|
||||
#endif
|
||||
@@ -219,8 +219,14 @@ int main(int argc, char *argv[])
|
||||
|
||||
#ifdef HAVE_KUNIFIEDPUSH
|
||||
if (parser.isSet(dbusActivatedOption)) {
|
||||
// We want to be replaceable by the main client
|
||||
KDBusService service(KDBusService::Replace);
|
||||
#ifdef HAVE_KDBUSADDONS
|
||||
// We *don't* want to use KDBusService here. I don't know why, but it makes activation super unreliable. We don't really need it anyway.
|
||||
if (!QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.neochat"))) {
|
||||
// Gracefully fail if NeoChat is already running
|
||||
qWarning() << "NeoChat already running, not sending push notifications.";
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_RUNNER
|
||||
// If we are built with KRunner and KUnifiedPush support, we need to do something special.
|
||||
|
||||
@@ -176,7 +176,7 @@ void ServerListModel::initialize()
|
||||
true,
|
||||
false,
|
||||
});
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
#include "moc_serverlistmodel.cpp"
|
||||
|
||||
@@ -42,7 +42,6 @@ Name[sv]=NeoChat
|
||||
Name[ta]=நியோச்சாட்
|
||||
Name[tr]=NeoChat
|
||||
Name[uk]=NeoChat
|
||||
Name[x-test]=xxNeoChatxx
|
||||
Name[zh_CN]=NeoChat
|
||||
Name[zh_TW]=NeoChat
|
||||
DesktopEntry=org.kde.neochat
|
||||
@@ -87,7 +86,6 @@ Comment[sv]=En klient för matrix, det decentraliserade kommunikationsprotokolle
|
||||
Comment[ta]=மையமில்லா தகவல் பரிமாற்ற நெறிமுறையான மேட்ரிக்ஸுக்கான செயலி
|
||||
Comment[tr]=Merkezi olmayan iletişim protokolü Matrix için bir istemci
|
||||
Comment[uk]=Клієнт matrix, децентралізованого протоколу обміну даними
|
||||
Comment[x-test]=xxA client for matrix, the decentralized communication protocolxx
|
||||
Comment[zh_CN]=分布式通讯协议 Matrix 的客户端
|
||||
Comment[zh_TW]=去中心化通訊協定 Matrix 的用戶端
|
||||
|
||||
@@ -134,7 +132,6 @@ Name[sv]=Nytt meddelande
|
||||
Name[ta]=புதிய செய்தி
|
||||
Name[tr]=Yeni İleti
|
||||
Name[uk]=Нове повідомлення
|
||||
Name[x-test]=xxNew messagexx
|
||||
Name[zh_CN]=新消息
|
||||
Name[zh_TW]=新訊息
|
||||
Comment=There is a new message
|
||||
@@ -177,7 +174,6 @@ Comment[sv]=Det finns ett nytt meddelande
|
||||
Comment[ta]=ஒரு புதிய செய்தி உள்ளது
|
||||
Comment[tr]=Yeni bir ileti var
|
||||
Comment[uk]=Надійшло нове повідомлення
|
||||
Comment[x-test]=xxThere is a new messagexx
|
||||
Comment[zh_CN]=有新消息
|
||||
Comment[zh_TW]=有新的訊息
|
||||
Action=Popup
|
||||
@@ -215,6 +211,7 @@ Name[pa]=ਨਵਾਂ ਸੱਦਾ
|
||||
Name[pl]=Nowe zaproszenie
|
||||
Name[pt]=Novo Convite
|
||||
Name[pt_BR]=Novo convite
|
||||
Name[ro]=Invitație nouă
|
||||
Name[ru]=Новое приглашение
|
||||
Name[sa]=नवीन आमन्त्रणम्
|
||||
Name[sl]=Novo povabilo
|
||||
@@ -222,7 +219,6 @@ Name[sv]=Ny inbjudan
|
||||
Name[ta]=புதிய அழைப்பிதழ்
|
||||
Name[tr]=Yeni Davet
|
||||
Name[uk]=Нове запрошення
|
||||
Name[x-test]=xxNew Invitationxx
|
||||
Name[zh_CN]=新邀请
|
||||
Name[zh_TW]=新邀請
|
||||
Comment=There is a new invitation to a room
|
||||
@@ -257,6 +253,7 @@ Comment[pa]=ਰੂਮ ਲਈ ਨਵਾਂ ਸੱਦਾ ਹੈ
|
||||
Comment[pl]=Dostępna jest nowe zaproszenie do pokoju
|
||||
Comment[pt]=Existe um novo convite para uma sala
|
||||
Comment[pt_BR]=Existe um novo convite para uma sala
|
||||
Comment[ro]=E o nouă invitație la o cameră
|
||||
Comment[ru]=Доступно новое приглашение в комнату
|
||||
Comment[sa]=कक्षस्य नूतनं निमन्त्रणम् अस्ति
|
||||
Comment[sl]=Tam je novo povabilo v sobo
|
||||
@@ -264,7 +261,6 @@ Comment[sv]=Det finns en ny inbjudan till ett rum
|
||||
Comment[ta]=ஓர் அரங்கிற்கான புதிய அழைப்பிதழ் உள்ளது
|
||||
Comment[tr]=Bir odaya yeni bir davetiye var
|
||||
Comment[uk]=У кімнаті нове запрошення
|
||||
Comment[x-test]=xxThere is a new invitation to a roomxx
|
||||
Comment[zh_CN]=有新的聊天室邀请
|
||||
Comment[zh_TW]=有新的加入聊天室邀請
|
||||
Action=Popup
|
||||
@@ -296,6 +292,7 @@ Name[nl]=Gedeelde
|
||||
Name[nn]=Del
|
||||
Name[pl]=Udostępnij
|
||||
Name[pt_BR]=Compartilhar
|
||||
Name[ro]=Partajare
|
||||
Name[ru]=Публикация
|
||||
Name[sa]=संविभागः
|
||||
Name[sl]=Deli
|
||||
@@ -303,7 +300,6 @@ Name[sv]=Dela
|
||||
Name[ta]=பகிர்
|
||||
Name[tr]=Paylaş
|
||||
Name[uk]=Оприлюднення
|
||||
Name[x-test]=xxSharexx
|
||||
Name[zh_CN]=分享
|
||||
Name[zh_TW]=分享
|
||||
Comment=The result of sharing a piece of content
|
||||
@@ -331,6 +327,7 @@ Comment[nl]=Het resultaat van het delen van een stukje inhoud
|
||||
Comment[nn]=Resultatet av deling av innhald
|
||||
Comment[pl]=Wynik udostępniania kawałka treści
|
||||
Comment[pt_BR]=O resultado de compartilhar um conteúdo
|
||||
Comment[ro]=Rezultatul partajării unei bucăți de conținut
|
||||
Comment[ru]=Результат публикации данных
|
||||
Comment[sa]=सामग्रीखण्डस्य साझाकरणस्य परिणामः
|
||||
Comment[sl]=Rezultat deljenega kosa vsebine
|
||||
@@ -338,7 +335,6 @@ Comment[sv]=Resultatet av att dela innehåll
|
||||
Comment[ta]=எதையோ பகிர்ந்ததன் விளைவு
|
||||
Comment[tr]=Bir parça içerik paylaşımının sonucu
|
||||
Comment[uk]=Результат оприлюднення даних
|
||||
Comment[x-test]=xxThe result of sharing a piece of contentxx
|
||||
Comment[zh_CN]=分享一个内容得到的结果
|
||||
Comment[zh_TW]=分享一份內容之後的結果
|
||||
Action=Popup
|
||||
|
||||
@@ -389,7 +389,7 @@ void NotificationsManager::postPushNotification(const QByteArray &message)
|
||||
|
||||
#ifdef HAVE_KIO
|
||||
auto openAction = notification->addAction(i18n("Open NeoChat"));
|
||||
connect(openAction, &KNotificationAction::activated, this, [=]() {
|
||||
connect(openAction, &KNotificationAction::activated, notification, [=]() {
|
||||
QString properId = roomId;
|
||||
properId = properId.replace(u"#"_s, QString());
|
||||
properId = properId.replace(u"!"_s, QString());
|
||||
@@ -403,8 +403,6 @@ void NotificationsManager::postPushNotification(const QByteArray &message)
|
||||
connect(notification, &KNotification::closed, qGuiApp, &QGuiApplication::quit);
|
||||
|
||||
notification->sendEvent();
|
||||
|
||||
m_notifications.insert(roomId, {json["ts"_L1].toVariant().toLongLong(), notification});
|
||||
} else {
|
||||
qWarning() << "Skipping unsupported push notification" << type;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
/**
|
||||
* @brief Display a native notification for the given push notification.
|
||||
*/
|
||||
void postPushNotification(const QByteArray &message);
|
||||
static void postPushNotification(const QByteArray &message);
|
||||
|
||||
/**
|
||||
* @brief Handle the notifications for the given connection.
|
||||
|
||||
@@ -43,7 +43,6 @@ Name[sv]=NeoChat
|
||||
Name[ta]=நியோச்சாட்
|
||||
Name[tr]=NeoChat
|
||||
Name[uk]=NeoChat
|
||||
Name[x-test]=xxNeoChatxx
|
||||
Name[zh_CN]=NeoChat
|
||||
Name[zh_TW]=NeoChat
|
||||
Comment=Find rooms in NeoChat
|
||||
@@ -76,6 +75,7 @@ Comment[nn]=Finn rom i NeoChat
|
||||
Comment[pl]=Znajdź pokoje w NeoChat
|
||||
Comment[pt]=Procurar salas no NeoChat
|
||||
Comment[pt_BR]=Encontrar salas no NeoChat
|
||||
Comment[ro]=Găsește camere în NeoChat
|
||||
Comment[ru]=Поиск комнат NeoChat
|
||||
Comment[sa]=NeoChat इत्यत्र कक्ष्याः अन्वेषणं कुर्वन्तु
|
||||
Comment[sl]=Najdi sobe v NeoChatu
|
||||
@@ -83,7 +83,6 @@ Comment[sv]=Sök efter rum i NeoChat
|
||||
Comment[ta]=நியோச்சாட்டில் அரங்குகளை கண்டுபிடிக்கும்
|
||||
Comment[tr]=NeoChat’te odalar bulun
|
||||
Comment[uk]=Пошук кімнат у NeoChat
|
||||
Comment[x-test]=xxFind rooms in NeoChatxx
|
||||
Comment[zh_CN]=在 NeoChat 查找聊天室
|
||||
Comment[zh_TW]=在 NeoChat 尋找聊天室
|
||||
X-KDE-ServiceTypes=Plasma/Runner
|
||||
|
||||
@@ -106,7 +106,7 @@ KirigamiComponents.ConvergentContextMenu {
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
text: i18n("Logout…")
|
||||
text: i18n("Logout")
|
||||
icon.name: "im-kick-user"
|
||||
onTriggered: confirmLogoutDialogComponent.createObject(root).open()
|
||||
}
|
||||
|
||||
@@ -52,15 +52,6 @@ ColumnLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Kirigami.SelectableLabel {
|
||||
Layout.fillWidth: true
|
||||
font: Kirigami.Theme.smallFont
|
||||
textFormat: TextEdit.PlainText
|
||||
visible: root.currentRoom && root.currentRoom.canonicalAlias
|
||||
text: root.currentRoom && root.currentRoom.canonicalAlias ? root.currentRoom.canonicalAlias : ""
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
}
|
||||
|
||||
Kirigami.Heading {
|
||||
text: root.currentRoom.displayName
|
||||
|
||||
@@ -79,14 +70,7 @@ ColumnLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
Kirigami.Heading {
|
||||
text: root.invitingMember.displayName
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
text: root.invitingMember.id
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
text: root.currentRoom.displayName
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
@@ -175,7 +159,7 @@ ColumnLayout {
|
||||
|
||||
QQC2.Label {
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
text: xi18nc("@info:label Ensure you are referring to the same translation used for that settings page", "You can reject invitations from unknown users under the <interface>Security & Safety</interface> settings.")
|
||||
text: i18nc("@info:label", "You can reject invitations from unknown users under Security settings.")
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
// + 5 to prevent it from wrapping unnecessarily
|
||||
|
||||
@@ -47,7 +47,7 @@ Kirigami.Page {
|
||||
icon.name: "document-edit"
|
||||
visible: root.allowEdit
|
||||
enabled: room.canSendState(root.type) && (!root.stateKey.startsWith("@") || root.stateKey === root.room.connection.localUserId) && root.type !== "m.room.create"
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "EditStateDialog"), {
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "EditStateDialog.qml"), {
|
||||
room: root.room,
|
||||
type: root.type,
|
||||
stateKey: root.stateKey,
|
||||
|
||||
@@ -11,7 +11,19 @@ VerificationMessage {
|
||||
|
||||
required property int reason
|
||||
|
||||
icon: "security-low"
|
||||
icon: {
|
||||
switch (root.reason) {
|
||||
case KeyVerificationSession.TIMEOUT:
|
||||
case KeyVerificationSession.REMOTE_TIMEOUT:
|
||||
case KeyVerificationSession.USER:
|
||||
case KeyVerificationSession.REMOTE_USER:
|
||||
case KeyVerificationSession.SESSION_ACCEPTED:
|
||||
case KeyVerificationSession.REMOTE_SESSION_ACCEPTED:
|
||||
return "dialog-information";
|
||||
default:
|
||||
return "security-low";
|
||||
}
|
||||
}
|
||||
text: {
|
||||
switch (root.reason) {
|
||||
case KeyVerificationSession.NONE:
|
||||
|
||||
@@ -236,18 +236,11 @@ void RoomManager::resolveResource(Uri uri, const QString &action)
|
||||
}
|
||||
}
|
||||
|
||||
void RoomManager::maximizeMedia(const QString &eventId)
|
||||
void RoomManager::maximizeMedia(int index)
|
||||
{
|
||||
if (eventId.isEmpty()) {
|
||||
qWarning() << "Tried to open media for empty event id";
|
||||
if (index < -1 || index > m_mediaMessageFilterModel->rowCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto index = m_mediaMessageFilterModel->getRowForEventId(eventId);
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Q_EMIT showMaximizedMedia(index);
|
||||
}
|
||||
|
||||
@@ -271,10 +264,6 @@ void RoomManager::viewEventSource(const QString &eventId)
|
||||
|
||||
void RoomManager::viewEventMenu(const QString &eventId, NeoChatRoom *room, NeochatRoomMember *sender, const QString &selectedText, const QString &hoveredLink)
|
||||
{
|
||||
if (eventId.isEmpty()) {
|
||||
qWarning() << "Tried to open event menu with empty event id";
|
||||
return;
|
||||
}
|
||||
const auto &event = **room->findInTimeline(eventId);
|
||||
|
||||
if (EventHandler::mediaInfo(room, &event).contains("mimeType"_L1)) {
|
||||
|
||||
@@ -212,8 +212,12 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Show a media item maximized.
|
||||
*
|
||||
* @param index the index to open the maximize delegate model at. This is the
|
||||
* index in the MediaMessageFilterModel owned by this RoomManager. A value
|
||||
* of -1 opens a the default item.
|
||||
*/
|
||||
Q_INVOKABLE void maximizeMedia(const QString &eventId);
|
||||
Q_INVOKABLE void maximizeMedia(int index);
|
||||
|
||||
Q_INVOKABLE void maximizeCode(NeochatRoomMember *author, const QDateTime &time, const QString &codeText, const QString &language);
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ void AccountManager::addConnection(NeoChatConnection *connection)
|
||||
});
|
||||
connect(connection, &NeoChatConnection::loggedOut, this, [this, connection] {
|
||||
// Only set the connection if the account being logged out is currently active
|
||||
if (m_accountRegistry->accounts().count() > 1 && connection == activeConnection()) {
|
||||
if (m_accountRegistry->accounts().count() == 1 && connection == activeConnection()) {
|
||||
setActiveConnection(dynamic_cast<NeoChatConnection *>(m_accountRegistry->accounts()[0]));
|
||||
} else {
|
||||
setActiveConnection(nullptr);
|
||||
|
||||
@@ -70,23 +70,13 @@ public:
|
||||
*
|
||||
* @param event the event to return a type for.
|
||||
*
|
||||
* @param isInReply whether this event is to be treated like a replied-to event (i.e., a basic text fallback should be shown if no other type is used)
|
||||
*
|
||||
* @sa Type
|
||||
*/
|
||||
static Type typeForEvent(const Quotient::RoomEvent &event, bool isInReply = false)
|
||||
static Type typeForEvent(const Quotient::RoomEvent &event)
|
||||
{
|
||||
using namespace Quotient;
|
||||
|
||||
if (event.isRedacted()) {
|
||||
return MessageComponentType::Text;
|
||||
}
|
||||
|
||||
if (const auto e = eventCast<const RoomMessageEvent>(&event)) {
|
||||
if (e->rawMsgtype() == u"m.key.verification.request"_s) {
|
||||
return MessageComponentType::Verification;
|
||||
}
|
||||
|
||||
switch (e->msgtype()) {
|
||||
case MessageEventType::Emote:
|
||||
return MessageComponentType::Text;
|
||||
@@ -113,8 +103,7 @@ public:
|
||||
if (event.matrixType() == u"org.matrix.msc3672.beacon_info"_s) {
|
||||
return MessageComponentType::LiveLocation;
|
||||
}
|
||||
// In the (unlikely) case that this is a reply to a state event, we do want to show something
|
||||
return isInReply ? MessageComponentType::Text : MessageComponentType::Other;
|
||||
return MessageComponentType::Other;
|
||||
}
|
||||
if (is<const EncryptedEvent>(event)) {
|
||||
return MessageComponentType::Encrypted;
|
||||
@@ -127,8 +116,7 @@ public:
|
||||
return MessageComponentType::Poll;
|
||||
}
|
||||
|
||||
// In the (unlikely) case that this is a reply to an unusual event, we do want to show something
|
||||
return isInReply ? MessageComponentType::Text : MessageComponentType::Other;
|
||||
return MessageComponentType::Other;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -448,12 +448,6 @@ QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent
|
||||
[](const PollStartEvent &e) {
|
||||
return e.question();
|
||||
},
|
||||
[](const EncryptedEvent &) {
|
||||
return i18nc("@info In room list", "Encrypted event");
|
||||
},
|
||||
[](const ReactionEvent &e) {
|
||||
return i18nc("[user] reacted with <emoji>", "reacted with %1", e.key());
|
||||
},
|
||||
i18n("Unknown event"));
|
||||
}
|
||||
|
||||
|
||||
@@ -100,10 +100,6 @@ QVariant UserListModel::data(const QModelIndex &index, int role) const
|
||||
return plEvent->powerLevelForUser(memberId);
|
||||
}
|
||||
if (role == PowerLevelStringRole) {
|
||||
if (m_currentRoom->roomCreatorHasUltimatePowerLevel() && m_currentRoom->isCreator(memberId)) {
|
||||
return i18nc("@info the person that created this room", "Creator");
|
||||
}
|
||||
|
||||
auto pl = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
|
||||
// User might not in the room yet, in this case pl can be nullptr.
|
||||
// e.g. When invited but user not accepted or denied the invitation.
|
||||
@@ -140,9 +136,13 @@ bool UserListModel::event(QEvent *event)
|
||||
|
||||
void UserListModel::memberJoined(const Quotient::RoomMember &member)
|
||||
{
|
||||
auto pos = findUserPos(member);
|
||||
beginInsertRows(QModelIndex(), pos, pos);
|
||||
m_members.insert(pos, member.id());
|
||||
if (m_members.contains(member.id())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int row = m_members.size();
|
||||
beginInsertRows(QModelIndex(), row, row);
|
||||
m_members.append(member.id());
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
@@ -162,8 +162,6 @@ void UserListModel::refreshMember(const Quotient::RoomMember &member, const QLis
|
||||
{
|
||||
auto pos = findUserPos(member);
|
||||
if (pos != m_members.size()) {
|
||||
// The update will have changed the state event so we need to insert the updated member object.
|
||||
m_members.insert(pos, member.id());
|
||||
Q_EMIT dataChanged(index(pos), index(pos), roles);
|
||||
} else {
|
||||
qWarning() << "Trying to access a room member not in the user list";
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <QMediaPlayer>
|
||||
#include <QMimeDatabase>
|
||||
#include <QTemporaryFile>
|
||||
#include <QVideoFrame>
|
||||
#include <QVideoSink>
|
||||
|
||||
#include <Quotient/events/eventcontent.h>
|
||||
#include <Quotient/events/eventrelation.h>
|
||||
@@ -245,11 +247,37 @@ QCoro::Task<void> NeoChatRoom::doUploadFile(QUrl url, QString body, std::optiona
|
||||
} else if (mime.name().startsWith("audio/"_L1)) {
|
||||
content = new EventContent::AudioContent(url, fileInfo.size(), mime, fileInfo.fileName());
|
||||
} else if (mime.name().startsWith("video/"_L1)) {
|
||||
QVideoSink sink;
|
||||
|
||||
QMediaPlayer player;
|
||||
player.setSource(url);
|
||||
player.setVideoSink(&sink);
|
||||
co_await qCoro(&player, &QMediaPlayer::mediaStatusChanged);
|
||||
auto resolution = player.metaData().value(QMediaMetaData::Resolution).toSize();
|
||||
|
||||
// Get the first video frame to use as a thumbnail.
|
||||
player.play();
|
||||
co_await qCoro(&player, &QMediaPlayer::positionChanged);
|
||||
|
||||
QTemporaryFile file;
|
||||
file.setFileTemplate(QStringLiteral("XXXXXX.jpg"));
|
||||
file.open();
|
||||
|
||||
const auto thumbnailImage = sink.videoFrame().toImage();
|
||||
Q_UNUSED(thumbnailImage.save(file.fileName()))
|
||||
player.stop(); // We have to delay the stop() because it will invalidate our image
|
||||
|
||||
const auto thumbnailFileInfo = QFileInfo(file.fileName());
|
||||
|
||||
// Upload the thumbnail
|
||||
const auto job = connection()->uploadFile(thumbnailFileInfo.absoluteFilePath());
|
||||
co_await qCoro(job.get(), &BaseJob::finished);
|
||||
|
||||
const auto resolution = player.metaData().value(QMediaMetaData::Resolution).toSize();
|
||||
content = new EventContent::VideoContent(url, fileInfo.size(), mime, resolution, fileInfo.fileName());
|
||||
content->thumbnail = EventContent::Thumbnail(job->contentUri(),
|
||||
thumbnailFileInfo.size(),
|
||||
QMimeDatabase().mimeTypeForName(QStringLiteral("image/jpeg")),
|
||||
thumbnailImage.size());
|
||||
} else {
|
||||
content = new EventContent::FileContent(url, fileInfo.size(), mime, fileInfo.fileName());
|
||||
}
|
||||
@@ -359,14 +387,9 @@ const RoomEvent *NeoChatRoom::lastEvent(std::function<bool(const RoomEvent *)> f
|
||||
if (auto lastEvent = eventCast<const RoomMessageEvent>(event)) {
|
||||
return lastEvent;
|
||||
}
|
||||
|
||||
if (auto lastEvent = eventCast<const PollStartEvent>(event)) {
|
||||
return lastEvent;
|
||||
}
|
||||
|
||||
if (auto lastEvent = eventCast<const EncryptedEvent>(event)) {
|
||||
return lastEvent;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_cachedEvent != nullptr) {
|
||||
@@ -446,19 +469,20 @@ void NeoChatRoom::onRedaction(const RoomEvent &prevEvent, const RoomEvent & /*af
|
||||
}
|
||||
}
|
||||
|
||||
QDateTime NeoChatRoom::lastActiveTime() const
|
||||
QDateTime NeoChatRoom::lastActiveTime()
|
||||
{
|
||||
// Find the last relevant event:
|
||||
if (const auto event = lastEvent(m_hiddenFilter)) {
|
||||
if (timelineSize() == 0) {
|
||||
if (m_cachedEvent != nullptr) {
|
||||
return m_cachedEvent->originTimestamp();
|
||||
}
|
||||
return QDateTime();
|
||||
}
|
||||
|
||||
if (auto event = lastEvent()) {
|
||||
return event->originTimestamp();
|
||||
}
|
||||
|
||||
// If nothing is loaded yet, and there is no cached event:
|
||||
if (timelineSize() == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// No message found, take last event:
|
||||
// no message found, take last event
|
||||
return messageEvents().rbegin()->get()->originTimestamp();
|
||||
}
|
||||
|
||||
@@ -536,9 +560,6 @@ bool NeoChatRoom::containsUser(const QString &userID) const
|
||||
|
||||
bool NeoChatRoom::canSendEvent(const QString &eventType) const
|
||||
{
|
||||
if (roomCreatorHasUltimatePowerLevel() && isCreator(localMember().id())) {
|
||||
return true;
|
||||
}
|
||||
auto plEvent = currentState().get<RoomPowerLevelsEvent>();
|
||||
if (!plEvent) {
|
||||
return false;
|
||||
@@ -551,9 +572,6 @@ bool NeoChatRoom::canSendEvent(const QString &eventType) const
|
||||
|
||||
bool NeoChatRoom::canSendState(const QString &eventType) const
|
||||
{
|
||||
if (roomCreatorHasUltimatePowerLevel() && isCreator(localMember().id())) {
|
||||
return true;
|
||||
}
|
||||
auto plEvent = currentState().get<RoomPowerLevelsEvent>();
|
||||
if (!plEvent) {
|
||||
return false;
|
||||
@@ -1261,33 +1279,36 @@ void NeoChatRoom::openEventMediaExternally(const QString &eventId)
|
||||
void NeoChatRoom::copyEventMedia(const QString &eventId)
|
||||
{
|
||||
const auto evtIt = findInTimeline(eventId);
|
||||
if (evtIt != messageEvents().rend() && is<RoomMessageEvent>(**evtIt)) {
|
||||
const auto event = evtIt->viewAs<RoomMessageEvent>();
|
||||
if (event->has<EventContent::FileContent>()) {
|
||||
const auto transferInfo = fileTransferInfo(eventId);
|
||||
if (transferInfo.completed()) {
|
||||
Clipboard clipboard;
|
||||
clipboard.setImage(transferInfo.localPath);
|
||||
} else {
|
||||
downloadFile(eventId,
|
||||
QUrl(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u'/'
|
||||
+ event->id().replace(u':', u'_').replace(u'/', u'_').replace(u'+', u'_') + fileNameToDownload(eventId)));
|
||||
connect(
|
||||
this,
|
||||
&Room::fileTransferCompleted,
|
||||
this,
|
||||
[this, eventId](QString id, QUrl localFile, FileSourceInfo fileMetadata) {
|
||||
Q_UNUSED(localFile);
|
||||
Q_UNUSED(fileMetadata);
|
||||
if (id == eventId) {
|
||||
auto transferInfo = fileTransferInfo(eventId);
|
||||
Clipboard clipboard;
|
||||
clipboard.setImage(transferInfo.localPath);
|
||||
}
|
||||
},
|
||||
static_cast<Qt::ConnectionType>(Qt::SingleShotConnection));
|
||||
}
|
||||
}
|
||||
|
||||
if (evtIt == messageEvents().rend() || !is<RoomMessageEvent>(**evtIt)) {
|
||||
return;
|
||||
}
|
||||
const auto event = evtIt->viewAs<RoomMessageEvent>();
|
||||
if (!event->has<EventContent::FileContentBase>()) {
|
||||
return;
|
||||
}
|
||||
const auto transferInfo = fileTransferInfo(eventId);
|
||||
if (transferInfo.completed()) {
|
||||
Clipboard clipboard;
|
||||
clipboard.setImage(transferInfo.localPath);
|
||||
} else {
|
||||
downloadFile(eventId,
|
||||
QUrl(u"file:"_s + QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u'/'
|
||||
+ event->id().replace(u':', u'_').replace(u'/', u'_').replace(u'+', u'_') + fileNameToDownload(eventId)));
|
||||
connect(
|
||||
this,
|
||||
&Room::fileTransferCompleted,
|
||||
this,
|
||||
[this, eventId](QString id, QUrl localFile, FileSourceInfo fileMetadata) {
|
||||
Q_UNUSED(localFile);
|
||||
Q_UNUSED(fileMetadata);
|
||||
if (id == eventId) {
|
||||
auto transferInfo = fileTransferInfo(eventId);
|
||||
Clipboard clipboard;
|
||||
clipboard.setImage(transferInfo.localPath);
|
||||
}
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1680,10 +1701,6 @@ void NeoChatRoom::setRoomState(const QString &type, const QString &stateKey, con
|
||||
|
||||
NeochatRoomMember *NeoChatRoom::qmlSafeMember(const QString &memberId)
|
||||
{
|
||||
if (memberId.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!m_memberObjects.contains(memberId)) {
|
||||
auto member = m_memberObjects.emplace(memberId, std::make_unique<NeochatRoomMember>(this, memberId)).first->second.get();
|
||||
QQmlEngine::setObjectOwnership(member, QQmlEngine::CppOwnership);
|
||||
@@ -1747,20 +1764,4 @@ void NeoChatRoom::setHiddenFilter(std::function<bool(const Quotient::RoomEvent *
|
||||
NeoChatRoom::m_hiddenFilter = hiddenFilter;
|
||||
}
|
||||
|
||||
bool NeoChatRoom::roomCreatorHasUltimatePowerLevel() const
|
||||
{
|
||||
bool ok = false;
|
||||
auto version = this->version().toInt(&ok);
|
||||
// This is terrible. For non-numeric room versions, I don't think there's a way of knowing whether they're pre- or post hydra.
|
||||
// We just assume they are. Shouldn't matter for normal users anyway.
|
||||
return !ok || version > 11;
|
||||
}
|
||||
|
||||
bool NeoChatRoom::isCreator(const QString &userId) const
|
||||
{
|
||||
auto createEvent = currentState().get<RoomCreateEvent>();
|
||||
return roomCreatorHasUltimatePowerLevel() && createEvent
|
||||
&& (createEvent->senderId() == userId || createEvent->contentPart<QStringList>(u"additional_creators"_s).contains(userId));
|
||||
}
|
||||
|
||||
#include "moc_neochatroom.cpp"
|
||||
|
||||
@@ -208,7 +208,7 @@ public:
|
||||
bool visible() const;
|
||||
void setVisible(bool visible);
|
||||
|
||||
[[nodiscard]] QDateTime lastActiveTime() const;
|
||||
[[nodiscard]] QDateTime lastActiveTime();
|
||||
|
||||
/**
|
||||
* @brief Get the last interesting event.
|
||||
@@ -589,18 +589,6 @@ public:
|
||||
|
||||
static void setHiddenFilter(std::function<bool(const Quotient::RoomEvent *)> hiddenFilter);
|
||||
|
||||
/**
|
||||
* @brief Whether this room has a room version where the creator is treated as having an ultimate power level
|
||||
*
|
||||
* For unusual room versions, this information might be wrong.
|
||||
*/
|
||||
bool roomCreatorHasUltimatePowerLevel() const;
|
||||
|
||||
/**
|
||||
* @brief Whether this user is considered a creator of this room. Only applies to post-v12 rooms.
|
||||
*/
|
||||
bool isCreator(const QString &userId) const;
|
||||
|
||||
private:
|
||||
bool m_visible = false;
|
||||
|
||||
|
||||
@@ -570,9 +570,8 @@ QVariantMap TextHandler::getAttributes(const QString &tag, const QString &tagStr
|
||||
QList<MessageComponent>
|
||||
TextHandler::textComponents(QString string, Qt::TextFormat inputFormat, const NeoChatRoom *room, const Quotient::RoomEvent *event, bool isEdited)
|
||||
{
|
||||
if (string.trimmed().isEmpty() && event->is<Quotient::RoomMessageEvent>()
|
||||
&& !eventCast<const Quotient::RoomMessageEvent>(event)->has<Quotient::EventContent::FileContentBase>()) {
|
||||
return {MessageComponent{MessageComponentType::Text, i18n("<i>This event does not have any content.</i>"), {}}};
|
||||
if (string.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Strip mx-reply if present.
|
||||
@@ -591,7 +590,7 @@ TextHandler::textComponents(QString string, Qt::TextFormat inputFormat, const Ne
|
||||
string = string.trimmed();
|
||||
|
||||
if (event != nullptr && room != nullptr) {
|
||||
if (auto e = eventCast<const Quotient::RoomMessageEvent>(event); e && e->msgtype() == Quotient::MessageEventType::Emote && components.size() == 1) {
|
||||
if (auto e = eventCast<const Quotient::RoomMessageEvent>(event); e->msgtype() == Quotient::MessageEventType::Emote && components.size() == 1) {
|
||||
if (components[0].type == MessageComponentType::Text) {
|
||||
components[0].content = emoteString(room, event) + components[0].content;
|
||||
} else {
|
||||
|
||||
@@ -85,6 +85,15 @@ void LoginHelper::init()
|
||||
account.sync();
|
||||
m_accountManager->addConnection(m_connection);
|
||||
m_accountManager->setActiveConnection(m_connection);
|
||||
disconnect(m_connection, nullptr, this, nullptr);
|
||||
connect(
|
||||
m_connection.get(),
|
||||
&NeoChatConnection::syncDone,
|
||||
this,
|
||||
[this]() {
|
||||
Q_EMIT loaded();
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
m_connection = nullptr;
|
||||
});
|
||||
connect(m_connection, &NeoChatConnection::networkError, this, [this](QString error, const QString &, int, int) {
|
||||
@@ -105,15 +114,6 @@ void LoginHelper::init()
|
||||
connect(m_connection, &NeoChatConnection::resolveError, this, [this](QString error) {
|
||||
Q_EMIT m_connection->errorOccured(i18n("Network Error: %1", std::move(error)));
|
||||
});
|
||||
|
||||
connect(
|
||||
m_connection.get(),
|
||||
&NeoChatConnection::syncDone,
|
||||
this,
|
||||
[this]() {
|
||||
Q_EMIT loaded();
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
}
|
||||
|
||||
void LoginHelper::setHomeserverReachable(bool reachable)
|
||||
|
||||
@@ -158,7 +158,12 @@ Item {
|
||||
}
|
||||
root.Message.timeline.interactive = false;
|
||||
if (!root.mediaInfo.isSticker) {
|
||||
RoomManager.maximizeMedia(root.eventId);
|
||||
// We need to make sure the index is that of the MediaMessageFilterModel.
|
||||
if (root.Message.timeline.model instanceof MessageFilterModel) {
|
||||
RoomManager.maximizeMedia(RoomManager.mediaMessageFilterModel.getRowForSourceItem(root.Message.index));
|
||||
} else {
|
||||
RoomManager.maximizeMedia(root.Message.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ RowLayout {
|
||||
id: contentRepeater
|
||||
model: root.replyContentModel
|
||||
delegate: ReplyMessageComponentChooser {
|
||||
onReplyClicked: RoomManager.goToEvent(root.replyEventId)
|
||||
onReplyClicked: root.Message.timeline.goToEvent(root.replyEventId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ RowLayout {
|
||||
}
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onTapped: RoomManager.goToEvent(root.replyEventId)
|
||||
onTapped: root.Message.timeline.goToEvent(root.replyEventId)
|
||||
}
|
||||
QtObject {
|
||||
id: _private
|
||||
|
||||
@@ -385,7 +385,12 @@ Video {
|
||||
onTriggered: {
|
||||
root.Message.timeline.interactive = false;
|
||||
root.pause();
|
||||
RoomManager.maximizeMedia(root.eventId);
|
||||
// We need to make sure the index is that of the MediaMessageFilterModel.
|
||||
if (root.Message.timeline.model instanceof MessageFilterModel) {
|
||||
RoomManager.maximizeMedia(RoomManager.mediaMessageFilterModel.getRowForSourceItem(root.Message.index));
|
||||
} else {
|
||||
RoomManager.maximizeMedia(root.Message.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "chatbarcache.h"
|
||||
#include "contentprovider.h"
|
||||
#include "filetype.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "models/reactionmodel.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
@@ -56,6 +57,7 @@ void MessageContentModel::initializeModel()
|
||||
connect(m_room, &NeoChatRoom::pendingEventAdded, this, [this]() {
|
||||
if (m_room != nullptr && m_currentState == Unknown) {
|
||||
initializeEvent();
|
||||
updateReplyModel();
|
||||
resetModel();
|
||||
}
|
||||
});
|
||||
@@ -69,6 +71,7 @@ void MessageContentModel::initializeModel()
|
||||
connect(m_room, &NeoChatRoom::pendingEventMerged, this, [this]() {
|
||||
if (m_room != nullptr && m_currentState == Pending) {
|
||||
initializeEvent();
|
||||
updateReplyModel();
|
||||
resetModel();
|
||||
}
|
||||
});
|
||||
@@ -77,6 +80,7 @@ void MessageContentModel::initializeModel()
|
||||
for (int i = fromIndex; i <= toIndex; i++) {
|
||||
if (m_room->findInTimeline(i)->event()->id() == m_eventId) {
|
||||
initializeEvent();
|
||||
updateReplyModel();
|
||||
resetModel();
|
||||
}
|
||||
}
|
||||
@@ -92,26 +96,22 @@ void MessageContentModel::initializeModel()
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::newFileTransfer, this, [this](const QString &eventId) {
|
||||
if (eventId == m_eventId) {
|
||||
forEachComponentOfType({MessageComponentType::File, MessageComponentType::Audio, MessageComponentType::Image, MessageComponentType::Video},
|
||||
m_fileInfoFunction);
|
||||
updateFileInfo();
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::fileTransferProgress, this, [this](const QString &eventId) {
|
||||
if (eventId == m_eventId) {
|
||||
forEachComponentOfType({MessageComponentType::File, MessageComponentType::Audio, MessageComponentType::Image, MessageComponentType::Video},
|
||||
m_fileInfoFunction);
|
||||
updateFileInfo();
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::fileTransferCompleted, this, [this](const QString &eventId) {
|
||||
if (m_room != nullptr && eventId == m_eventId) {
|
||||
forEachComponentOfType({MessageComponentType::File, MessageComponentType::Audio, MessageComponentType::Image, MessageComponentType::Video},
|
||||
m_fileInfoFunction);
|
||||
updateFileInfo();
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::fileTransferFailed, this, [this](const QString &eventId, const QString &errorMessage) {
|
||||
if (eventId == m_eventId) {
|
||||
forEachComponentOfType({MessageComponentType::File, MessageComponentType::Audio, MessageComponentType::Image, MessageComponentType::Video},
|
||||
m_fileInfoFunction);
|
||||
updateFileInfo();
|
||||
if (errorMessage.isEmpty()) {
|
||||
Q_EMIT m_room->showMessage(MessageType::Error, i18nc("@info", "Failed to download file."));
|
||||
} else {
|
||||
@@ -149,6 +149,7 @@ void MessageContentModel::initializeModel()
|
||||
});
|
||||
|
||||
connect(this, &MessageContentModel::threadsEnabledChanged, this, [this]() {
|
||||
updateReplyModel();
|
||||
resetModel();
|
||||
});
|
||||
connect(m_room, &Room::updatedEvent, this, [this](const QString &eventId) {
|
||||
@@ -158,9 +159,23 @@ void MessageContentModel::initializeModel()
|
||||
});
|
||||
|
||||
initializeEvent();
|
||||
if (m_currentState == Available || m_currentState == Pending) {
|
||||
updateReplyModel();
|
||||
}
|
||||
resetModel();
|
||||
}
|
||||
|
||||
void MessageContentModel::updateFileInfo()
|
||||
{
|
||||
for (auto it = m_components.cbegin(); it != m_components.cend(); it++) {
|
||||
const auto currentIndex = it - m_components.cbegin();
|
||||
if (m_components.at(currentIndex).type == MessageComponentType::File || m_components.at(currentIndex).type == MessageComponentType::Audio
|
||||
|| m_components.at(currentIndex).type == MessageComponentType::Image || m_components.at(currentIndex).type == MessageComponentType::Video) {
|
||||
Q_EMIT dataChanged(index(currentIndex), index(currentIndex), {FileTransferInfoRole});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageContentModel::initializeEvent()
|
||||
{
|
||||
if (m_currentState == UnAvailable) {
|
||||
@@ -188,6 +203,7 @@ void MessageContentModel::getEvent()
|
||||
if (m_room != nullptr) {
|
||||
if (eventId == m_eventId) {
|
||||
initializeEvent();
|
||||
updateReplyModel();
|
||||
resetModel();
|
||||
return true;
|
||||
}
|
||||
@@ -411,38 +427,6 @@ QHash<int, QByteArray> MessageContentModel::roleNamesStatic()
|
||||
return roles;
|
||||
}
|
||||
|
||||
bool MessageContentModel::hasComponentType(MessageComponentType::Type type)
|
||||
{
|
||||
return std::find_if(m_components.cbegin(),
|
||||
m_components.cend(),
|
||||
[type](const MessageComponent &component) {
|
||||
return component.type == type;
|
||||
})
|
||||
!= m_components.cend();
|
||||
}
|
||||
|
||||
void MessageContentModel::forEachComponentOfType(MessageComponentType::Type type,
|
||||
std::function<MessageContentModel::ComponentIt(MessageContentModel::ComponentIt)> function)
|
||||
{
|
||||
auto it = m_components.begin();
|
||||
while ((it = std::find_if(it,
|
||||
m_components.end(),
|
||||
[type](const MessageComponent &component) {
|
||||
return component.type == type;
|
||||
}))
|
||||
!= m_components.end()) {
|
||||
it = function(it);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageContentModel::forEachComponentOfType(QList<MessageComponentType::Type> types,
|
||||
std::function<MessageContentModel::ComponentIt(MessageContentModel::ComponentIt)> function)
|
||||
{
|
||||
for (const auto &type : types) {
|
||||
forEachComponentOfType(type, function);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageContentModel::resetModel()
|
||||
{
|
||||
beginResetModel();
|
||||
@@ -466,11 +450,6 @@ void MessageContentModel::resetModel()
|
||||
m_components += messageContentComponents();
|
||||
endResetModel();
|
||||
|
||||
if (m_room->urlPreviewEnabled()) {
|
||||
forEachComponentOfType({MessageComponentType::Text, MessageComponentType::Quote}, m_linkPreviewFunction);
|
||||
}
|
||||
|
||||
updateReplyModel();
|
||||
updateReactionModel();
|
||||
}
|
||||
|
||||
@@ -489,11 +468,6 @@ void MessageContentModel::resetContent(bool isEditing, bool isThreading)
|
||||
m_components += newComponents;
|
||||
endInsertRows();
|
||||
|
||||
if (m_room->urlPreviewEnabled()) {
|
||||
forEachComponentOfType({MessageComponentType::Text, MessageComponentType::Quote}, m_linkPreviewFunction);
|
||||
}
|
||||
|
||||
updateReplyModel();
|
||||
updateReactionModel();
|
||||
}
|
||||
|
||||
@@ -506,13 +480,31 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
|
||||
|
||||
QList<MessageComponent> newComponents;
|
||||
|
||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first);
|
||||
if (roomMessageEvent && roomMessageEvent->rawMsgtype() == u"m.key.verification.request"_s) {
|
||||
newComponents += MessageComponent{MessageComponentType::Verification, QString(), {}};
|
||||
return newComponents;
|
||||
}
|
||||
|
||||
if (event.first->isRedacted()) {
|
||||
newComponents += MessageComponent{MessageComponentType::Text, QString(), {}};
|
||||
return newComponents;
|
||||
}
|
||||
|
||||
if (m_replyModel != nullptr) {
|
||||
newComponents += MessageComponent{MessageComponentType::Reply, QString(), {}};
|
||||
}
|
||||
|
||||
if (isEditing) {
|
||||
newComponents += MessageComponent{MessageComponentType::ChatBar, QString(), {}};
|
||||
} else {
|
||||
newComponents.append(componentsForType(MessageComponentType::typeForEvent(*event.first, m_isReply)));
|
||||
newComponents.append(componentsForType(MessageComponentType::typeForEvent(*event.first)));
|
||||
}
|
||||
|
||||
if (m_room->urlPreviewEnabled()) {
|
||||
newComponents = addLinkPreviews(newComponents);
|
||||
}
|
||||
|
||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first);
|
||||
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 1)
|
||||
if (m_threadsEnabled && roomMessageEvent && (roomMessageEvent->isThreaded() || m_room->threads().contains(roomMessageEvent->id()))
|
||||
&& roomMessageEvent->id() == roomMessageEvent->threadRootEventId()) {
|
||||
@@ -548,8 +540,7 @@ void MessageContentModel::updateReplyModel()
|
||||
}
|
||||
if (!roomMessageEvent->isReply(m_threadsEnabled) || (roomMessageEvent->isThreaded() && m_threadsEnabled)) {
|
||||
if (m_replyModel) {
|
||||
m_replyModel->disconnect(this);
|
||||
m_replyModel->deleteLater();
|
||||
delete m_replyModel;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -563,24 +554,6 @@ void MessageContentModel::updateReplyModel()
|
||||
connect(m_replyModel, &MessageContentModel::eventUpdated, this, [this]() {
|
||||
Q_EMIT dataChanged(index(0), index(0), {ReplyAuthorRole});
|
||||
});
|
||||
|
||||
bool hasModel = hasComponentType(MessageComponentType::Reply);
|
||||
if (m_replyModel && !hasModel) {
|
||||
int insertRow = 0;
|
||||
if (m_components.first().type == MessageComponentType::Author) {
|
||||
insertRow = 1;
|
||||
}
|
||||
beginInsertRows({}, insertRow, insertRow);
|
||||
m_components.insert(insertRow, MessageComponent{MessageComponentType::Reply, QString(), {}});
|
||||
} else if (!m_replyModel && hasModel) {
|
||||
int removeRow = 0;
|
||||
if (m_components.first().type == MessageComponentType::Author) {
|
||||
removeRow = 1;
|
||||
}
|
||||
beginRemoveRows({}, removeRow, removeRow);
|
||||
m_components.removeAt(removeRow);
|
||||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
|
||||
QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentType::Type type)
|
||||
@@ -591,26 +564,22 @@ QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentT
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case MessageComponentType::Verification: {
|
||||
return {MessageComponent{MessageComponentType::Verification, QString(), {}}};
|
||||
}
|
||||
case MessageComponentType::Text: {
|
||||
if (const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first)) {
|
||||
return TextHandler().textComponents(EventHandler::rawMessageBody(*roomMessageEvent),
|
||||
EventHandler::messageBodyInputFormat(*roomMessageEvent),
|
||||
m_room,
|
||||
roomMessageEvent,
|
||||
roomMessageEvent->isReplaced());
|
||||
} else {
|
||||
return TextHandler().textComponents(EventHandler::plainBody(m_room, event.first), Qt::TextFormat::PlainText, m_room, event.first, false);
|
||||
}
|
||||
|
||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first);
|
||||
return TextHandler().textComponents(EventHandler::rawMessageBody(*roomMessageEvent),
|
||||
EventHandler::messageBodyInputFormat(*roomMessageEvent),
|
||||
m_room,
|
||||
roomMessageEvent,
|
||||
roomMessageEvent->isReplaced());
|
||||
auto body = EventHandler::rawMessageBody(*roomMessageEvent);
|
||||
if (body.trimmed().isEmpty()) {
|
||||
return TextHandler().textComponents(i18n("<i>This event does not have any content.</i>"),
|
||||
Qt::TextFormat::RichText,
|
||||
m_room,
|
||||
roomMessageEvent,
|
||||
roomMessageEvent->isReplaced());
|
||||
} else {
|
||||
return TextHandler().textComponents(body,
|
||||
EventHandler::messageBodyInputFormat(*roomMessageEvent),
|
||||
m_room,
|
||||
roomMessageEvent,
|
||||
roomMessageEvent->isReplaced());
|
||||
}
|
||||
}
|
||||
case MessageComponentType::File: {
|
||||
QList<MessageComponent> components;
|
||||
@@ -701,20 +670,42 @@ MessageComponent MessageContentModel::linkPreviewComponent(const QUrl &link)
|
||||
}
|
||||
if (linkPreviewer->loaded()) {
|
||||
return MessageComponent{MessageComponentType::LinkPreview, QString(), {{"link"_L1, link}}};
|
||||
}
|
||||
connect(linkPreviewer, &LinkPreviewer::loadedChanged, this, [this, link]() {
|
||||
const auto linkPreviewer = dynamic_cast<NeoChatConnection *>(m_room->connection())->previewerForLink(link);
|
||||
if (linkPreviewer != nullptr && linkPreviewer->loaded()) {
|
||||
forEachComponentOfType(MessageComponentType::LinkPreviewLoad, [this, link](ComponentIt it) {
|
||||
if (it->attributes["link"_L1].toUrl() == link) {
|
||||
it->type = MessageComponentType::LinkPreview;
|
||||
Q_EMIT dataChanged(index(it - m_components.begin()), index(it - m_components.begin()), {ComponentTypeRole});
|
||||
} else {
|
||||
connect(linkPreviewer, &LinkPreviewer::loadedChanged, this, [this, link]() {
|
||||
const auto linkPreviewer = dynamic_cast<NeoChatConnection *>(m_room->connection())->previewerForLink(link);
|
||||
if (linkPreviewer != nullptr && linkPreviewer->loaded()) {
|
||||
for (auto it = m_components.begin(); it != m_components.end(); it++) {
|
||||
if (it->attributes["link"_L1].toUrl() == link) {
|
||||
it->type = MessageComponentType::LinkPreview;
|
||||
Q_EMIT dataChanged(index(it - m_components.begin()), index(it - m_components.begin()), {ComponentTypeRole});
|
||||
}
|
||||
}
|
||||
return it;
|
||||
});
|
||||
}
|
||||
});
|
||||
return MessageComponent{MessageComponentType::LinkPreviewLoad, QString(), {{"link"_L1, link}}};
|
||||
}
|
||||
}
|
||||
|
||||
QList<MessageComponent> MessageContentModel::addLinkPreviews(QList<MessageComponent> inputComponents)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < inputComponents.size()) {
|
||||
const auto component = inputComponents.at(i);
|
||||
if (component.type == MessageComponentType::Text || component.type == MessageComponentType::Quote) {
|
||||
if (LinkPreviewer::hasPreviewableLinks(component.content)) {
|
||||
const auto links = LinkPreviewer::linkPreviews(component.content);
|
||||
for (qsizetype j = 0; j < links.size(); ++j) {
|
||||
const auto linkPreview = linkPreviewComponent(links[j]);
|
||||
if (!m_removedLinkPreviews.contains(links[j]) && !linkPreview.isEmpty()) {
|
||||
inputComponents.insert(i + j + 1, linkPreview);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
return MessageComponent{MessageComponentType::LinkPreviewLoad, QString(), {{"link"_L1, link}}};
|
||||
i++;
|
||||
}
|
||||
|
||||
return inputComponents;
|
||||
}
|
||||
|
||||
void MessageContentModel::closeLinkPreview(int row)
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <Quotient/events/roomevent.h>
|
||||
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "messagecomponent.h"
|
||||
#include "models/itinerarymodel.h"
|
||||
#include "models/reactionmodel.h"
|
||||
@@ -133,35 +132,9 @@ private:
|
||||
void initializeModel();
|
||||
void initializeEvent();
|
||||
void getEvent();
|
||||
|
||||
using ComponentIt = QList<MessageComponent>::iterator;
|
||||
void updateFileInfo();
|
||||
|
||||
QList<MessageComponent> m_components;
|
||||
bool hasComponentType(MessageComponentType::Type type);
|
||||
void forEachComponentOfType(MessageComponentType::Type type, std::function<ComponentIt(ComponentIt)> function);
|
||||
void forEachComponentOfType(QList<MessageComponentType::Type> types, std::function<ComponentIt(ComponentIt)> function);
|
||||
|
||||
std::function<ComponentIt(const ComponentIt &)> m_fileInfoFunction = [this](ComponentIt it) {
|
||||
Q_EMIT dataChanged(index(it - m_components.begin()), index(it - m_components.begin()), {MessageContentModel::FileTransferInfoRole});
|
||||
return ++it;
|
||||
};
|
||||
std::function<ComponentIt(const ComponentIt &)> m_linkPreviewFunction = [this](ComponentIt it) {
|
||||
bool previewAdded = false;
|
||||
if (LinkPreviewer::hasPreviewableLinks(it->content)) {
|
||||
const auto links = LinkPreviewer::linkPreviews(it->content);
|
||||
for (qsizetype j = 0; j < links.size(); ++j) {
|
||||
const auto linkPreview = linkPreviewComponent(links[j]);
|
||||
if (!m_removedLinkPreviews.contains(links[j]) && !linkPreview.isEmpty()) {
|
||||
beginInsertRows({}, std::distance(m_components.begin(), it) + j + 1, std::distance(m_components.begin(), it) + j + 1);
|
||||
it = m_components.insert(it + j + 1, linkPreview);
|
||||
previewAdded = true;
|
||||
endInsertRows();
|
||||
}
|
||||
};
|
||||
}
|
||||
return previewAdded ? it : ++it;
|
||||
};
|
||||
|
||||
void resetModel();
|
||||
void resetContent(bool isEditing = false, bool isThreading = false);
|
||||
QList<MessageComponent> messageContentComponents(bool isEditing = false, bool isThreading = false);
|
||||
@@ -174,6 +147,7 @@ private:
|
||||
|
||||
QList<MessageComponent> componentsForType(MessageComponentType::Type type);
|
||||
MessageComponent linkPreviewComponent(const QUrl &link);
|
||||
QList<MessageComponent> addLinkPreviews(QList<MessageComponent> inputComponents);
|
||||
|
||||
QList<QUrl> m_removedLinkPreviews;
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"Name[nl]": "Tobias Fella",
|
||||
"Name[nn]": "Tobias Fella",
|
||||
"Name[pl]": "Tobias Fella",
|
||||
"Name[pt_BR]": "Tobias Fella",
|
||||
"Name[ro]": "Tobias Fella",
|
||||
"Name[ru]": "Tobias Fella",
|
||||
"Name[sa]": "टोबियास फेला",
|
||||
"Name[sk]": "Tobias Fella",
|
||||
@@ -35,7 +37,6 @@
|
||||
"Name[ta]": "டோபியாஸ் ஃபெல்லா",
|
||||
"Name[tr]": "Tobias Fella",
|
||||
"Name[uk]": "Tobias Fella",
|
||||
"Name[x-test]": "xxTobias Fellaxx",
|
||||
"Name[zh_CN]": "Tobias Fella",
|
||||
"Name[zh_TW]": "Tobias Fella"
|
||||
}
|
||||
@@ -65,6 +66,8 @@
|
||||
"Description[nl]": "Delen via NeoChat",
|
||||
"Description[nn]": "Del via NeoChat",
|
||||
"Description[pl]": "Udostępnij przez NeoChat",
|
||||
"Description[pt_BR]": "Compartilhar via NeoChat",
|
||||
"Description[ro]": "Partajează prin NeoChat",
|
||||
"Description[ru]": "Опубликовать в NeoChat",
|
||||
"Description[sa]": "NeoChat मार्गेण साझां कुर्वन्तु",
|
||||
"Description[sl]": "Deli prek NeoChat",
|
||||
@@ -72,7 +75,6 @@
|
||||
"Description[ta]": "நியோச்சாட் மூலம் பகிர்",
|
||||
"Description[tr]": "NeoChat ile Paylaş",
|
||||
"Description[uk]": "Оприлюднити за допомогою NeoChat",
|
||||
"Description[x-test]": "xxShare via NeoChatxx",
|
||||
"Description[zh_CN]": "通过 NeoChat 分享",
|
||||
"Description[zh_TW]": "透過 NeoChat 分享",
|
||||
"Icon": "org.kde.neochat.tray",
|
||||
@@ -103,6 +105,8 @@
|
||||
"Name[nl]": "NeoChat",
|
||||
"Name[nn]": "NeoChat",
|
||||
"Name[pl]": "NeoChat",
|
||||
"Name[pt_BR]": "NeoChat",
|
||||
"Name[ro]": "NeoChat",
|
||||
"Name[ru]": "NeoChat",
|
||||
"Name[sa]": "नवचैट्",
|
||||
"Name[sk]": "NeoChat",
|
||||
@@ -111,7 +115,6 @@
|
||||
"Name[ta]": "நியோச்சாட்",
|
||||
"Name[tr]": "NeoChat",
|
||||
"Name[uk]": "NeoChat",
|
||||
"Name[x-test]": "xxNeoChatxx",
|
||||
"Name[zh_CN]": "NeoChat",
|
||||
"Name[zh_TW]": "NeoChat",
|
||||
"X-Purpose-ActionDisplay": "NeoChat"
|
||||
|
||||
@@ -153,7 +153,7 @@ QQC2.ScrollView {
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: leaveButton
|
||||
icon.name: "arrow-left-symbolic"
|
||||
text: root.room.isSpace ? i18nc("@action:button", "Leave this space…") : i18nc("@action:button", "Leave this room…")
|
||||
text: root.room.isSpace ? i18nc("@action:button", "Leave this space") : i18nc("@action:button", "Leave this room")
|
||||
activeFocusOnTab: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -58,7 +58,7 @@ KirigamiComponents.ConvergentContextMenu {
|
||||
icon.name: "notifications"
|
||||
|
||||
Kirigami.Action {
|
||||
text: i18nc("@action:inmenu Notification 'Default Settings'", "Default Settings")
|
||||
text: i18n("Follow Global Setting")
|
||||
icon.name: "globe"
|
||||
checkable: true
|
||||
autoExclusive: true
|
||||
@@ -152,7 +152,7 @@ KirigamiComponents.ConvergentContextMenu {
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
text: i18n("Leave Room…")
|
||||
text: i18n("Leave Room")
|
||||
icon.name: "go-previous"
|
||||
onTriggered: {
|
||||
Qt.createComponent('org.kde.neochat', 'ConfirmLeaveDialog').createObject(root.QQC2.ApplicationWindow.window, {
|
||||
|
||||
@@ -77,11 +77,11 @@ Kirigami.Page {
|
||||
}
|
||||
|
||||
function goToNextUnreadRoom() {
|
||||
goToNextRoomFiltered(item => (item && item instanceof RoomDelegate && item.hasUnread));
|
||||
goToNextRoomFiltered(item => (item && item instanceof RoomDelegate && item.hasUnreadMessages));
|
||||
}
|
||||
|
||||
function goToPreviousUnreadRoom() {
|
||||
goToPreviousRoomFiltered(item => (item && item instanceof RoomDelegate && item.hasUnread));
|
||||
goToPreviousRoomFiltered(item => (item && item instanceof RoomDelegate && item.hasUnreadMessages));
|
||||
}
|
||||
|
||||
titleDelegate: Loader {
|
||||
@@ -96,10 +96,7 @@ Kirigami.Page {
|
||||
function onCurrentSpaceChanged() {
|
||||
treeView.expandRecursively();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: RoomManager.sortFilterRoomTreeModel
|
||||
function onCurrentRoomChanged() {
|
||||
treeView.positionViewAtIndex(RoomManager.sortFilterRoomTreeModel.currentRoomIndex(), TableView.AlignVCenter)
|
||||
}
|
||||
|
||||
@@ -70,10 +70,8 @@ KirigamiComponents.ConvergentContextMenu {
|
||||
}
|
||||
|
||||
QQC2.Action {
|
||||
text: i18nc("'Space' is a matrix space", "Leave Space…")
|
||||
text: i18nc("'Space' is a matrix space", "Leave Space")
|
||||
icon.name: "go-previous"
|
||||
onTriggered: Qt.createComponent('org.kde.neochat', 'ConfirmLeaveDialog').createObject(root.QQC2.ApplicationWindow.window, {
|
||||
room: root.room
|
||||
}).open();
|
||||
onTriggered: root.room.forget()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,6 @@ void SortFilterRoomTreeModel::setActiveSpaceId(const QString &spaceId)
|
||||
void SortFilterRoomTreeModel::setCurrentRoom(NeoChatRoom *room)
|
||||
{
|
||||
m_currentRoom = room;
|
||||
Q_EMIT currentRoomChanged();
|
||||
}
|
||||
|
||||
SortFilterRoomTreeModel::Mode SortFilterRoomTreeModel::mode() const
|
||||
|
||||
@@ -104,7 +104,6 @@ Q_SIGNALS:
|
||||
void filterTextChanged();
|
||||
void activeSpaceIdChanged();
|
||||
void modeChanged();
|
||||
void currentRoomChanged();
|
||||
|
||||
private:
|
||||
Mode m_mode = All;
|
||||
|
||||
@@ -252,7 +252,7 @@ FormCard.FormCardPage {
|
||||
FormCard.FormCard {
|
||||
FormCard.FormButtonDelegate {
|
||||
id: deactivateAccountButton
|
||||
text: i18nc("@action:button", "Deactivate Account…")
|
||||
text: i18n("Deactivate Account")
|
||||
icon.name: "trash-empty-symbolic"
|
||||
onClicked: {
|
||||
const component = Qt.createComponent('org.kde.neochat', 'ConfirmDeactivateAccountDialog');
|
||||
|
||||
@@ -85,7 +85,7 @@ FormCard.FormCardPage {
|
||||
}
|
||||
|
||||
QQC2.ToolButton {
|
||||
text: i18n("Logout…")
|
||||
text: i18n("Logout")
|
||||
icon.name: "im-kick-user"
|
||||
onClicked: confirmLogoutDialogComponent.createObject(root.QQC2.Overlay.overlay).open()
|
||||
}
|
||||
|
||||
@@ -45,26 +45,6 @@ FormCard.FormCardPage {
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormCard {
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
FormCard.AbstractFormDelegate {
|
||||
contentItem: RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
Kirigami.Icon {
|
||||
source: "data-information"
|
||||
width: Kirigami.Units.iconSizes.sizeForLabels
|
||||
height: Kirigami.Units.iconSizes.sizeForLabels
|
||||
}
|
||||
QQC2.Label {
|
||||
text: i18nc("@info", "These are the default notification settings for all rooms. You can customize notifications per-room in the room list or room settings.")
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title:group", "Room Notifications")
|
||||
}
|
||||
|
||||
@@ -345,7 +345,7 @@ FormCard.FormCardPage {
|
||||
FormCard.FormCard {
|
||||
FormCard.FormButtonDelegate {
|
||||
icon.name: "kt-restore-defaults-symbolic"
|
||||
text: i18nc("@action:button", "Reset all configuration values to their default…")
|
||||
text: i18nc("@action:button", "Reset all configuration values to their default")
|
||||
onClicked: resetDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ FormCard.FormCardPage {
|
||||
|
||||
FormCard.FormCard {
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("As in the default notification setting", "Default Settings")
|
||||
text: i18n("Follow global setting")
|
||||
checked: room.pushNotificationState === PushNotificationState.Default
|
||||
enabled: room.pushNotificationState !== PushNotificationState.Unknown
|
||||
onToggled: {
|
||||
|
||||
@@ -25,34 +25,13 @@ FormCard.FormCardPage {
|
||||
title: i18nc("@option:check", "Encryption")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.AbstractFormDelegate {
|
||||
visible: room.usesEncryption
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
Kirigami.Icon {
|
||||
source: "lock"
|
||||
width: Kirigami.Units.iconSizes.sizeForLabels
|
||||
height: Kirigami.Units.iconSizes.sizeForLabels
|
||||
}
|
||||
QQC2.Label {
|
||||
text: i18nc("@info", "This room uses encryption.")
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormButtonDelegate {
|
||||
FormCard.FormSwitchDelegate {
|
||||
id: enableEncryptionSwitch
|
||||
|
||||
icon.name: "lock-symbolic"
|
||||
text: i18nc("@action:button Enable encryption in this room", "Enable Encryption…")
|
||||
description: i18nc("@info:description", "Once enabled, encryption cannot be disabled.")
|
||||
text: i18n("Enable encryption")
|
||||
description: i18nc("option:check", "Once enabled, encryption cannot be disabled.")
|
||||
enabled: room.canEncryptRoom
|
||||
visible: !room.usesEncryption
|
||||
|
||||
onClicked: {
|
||||
checked: room.usesEncryption
|
||||
onToggled: if (checked) {
|
||||
let dialog = confirmEncryptionDialog.createObject(QQC2.Overlay.overlay, {
|
||||
room: room
|
||||
});
|
||||
@@ -76,9 +55,9 @@ FormCard.FormCardPage {
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("@option:check", "Space members")
|
||||
description: i18n("Anyone in the selected spaces can find and join.") + (!["8", "9", "10"].includes(room.version) ? `\n${needUpgradeRoom}` : "")
|
||||
description: i18n("Anyone in the selected spaces can find and join.") + (!["8", "9", "10", "11", "12"].includes(room.version) ? `\n${needUpgradeRoom}` : "")
|
||||
checked: room.joinRule === JoinRule.Restricted
|
||||
enabled: room.canSendState("m.room.join_rules") && ["8", "9", "10"].includes(room.version)
|
||||
enabled: room.canSendState("m.room.join_rules") && ["8", "9", "10", "11", "12"].includes(room.version)
|
||||
onCheckedChanged: if (checked && room.joinRule != JoinRule.Restricted) {
|
||||
selectSpacesDialog.createObject(QQC2.Overlay.overlay).open();
|
||||
}
|
||||
@@ -104,10 +83,10 @@ FormCard.FormCardPage {
|
||||
}
|
||||
FormCard.FormRadioDelegate {
|
||||
text: i18nc("@option:check", "Knock")
|
||||
description: i18n("People not in the room need to request an invite to join the room.") + (!["7", "8", "9", "10"].includes(room.version) ? `\n${needUpgradeRoom}` : "")
|
||||
description: i18n("People not in the room need to request an invite to join the room.") + (!["7", "8", "9", "10", "11", "12"].includes(room.version) ? `\n${needUpgradeRoom}` : "")
|
||||
checked: room.joinRule === JoinRule.Knock
|
||||
// https://spec.matrix.org/v1.4/rooms/#feature-matrix
|
||||
enabled: room.canSendState("m.room.join_rules") && ["7", "8", "9", "10"].includes(room.version)
|
||||
enabled: room.canSendState("m.room.join_rules") && ["7", "8", "9", "10", "11", "12"].includes(room.version)
|
||||
onCheckedChanged: if (checked && room.joinRule != JoinRule.Knock) {
|
||||
root.room.joinRule = JoinRule.Knock;
|
||||
}
|
||||
|
||||
@@ -95,11 +95,9 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
QQC2.Button {
|
||||
text: i18nc("@action:button", "Leave this space…")
|
||||
text: i18nc("@action:button", "Leave this space")
|
||||
icon.name: "go-previous"
|
||||
onClicked: Qt.createComponent('org.kde.neochat', 'ConfirmLeaveDialog').createObject(root.QQC2.ApplicationWindow.window, {
|
||||
room: root.room
|
||||
}).open();
|
||||
onClicked: root.room.forget()
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -123,7 +123,7 @@ KirigamiComponents.ConvergentContextMenu {
|
||||
|
||||
|
||||
component ReportMessageAction: Kirigami.Action {
|
||||
text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report…")
|
||||
text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report")
|
||||
icon.name: "dialog-warning-symbolic"
|
||||
visible: !author.isLocalMember
|
||||
onTriggered: {
|
||||
|
||||
@@ -159,7 +159,7 @@ QQC2.ScrollView {
|
||||
|
||||
function onReadMarkerAdded() {
|
||||
if (root.markReadCondition == LibNeoChat.TimelineMarkReadCondition.EntryVisible && messageListView.allUnreadVisible()) {
|
||||
_private.room.markAllMessagesAsRead();
|
||||
root.room.markAllMessagesAsRead();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,14 +85,9 @@ QHash<int, QByteArray> MediaMessageFilterModel::roleNames() const
|
||||
return roles;
|
||||
}
|
||||
|
||||
int MediaMessageFilterModel::getRowForEventId(const QString &eventId) const
|
||||
int MediaMessageFilterModel::getRowForSourceItem(int sourceRow) const
|
||||
{
|
||||
for (auto i = 0; i < rowCount(); i++) {
|
||||
if (data(index(i, 0), MessageModel::EventIdRole).toString() == eventId) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
return mapFromSource(sourceModel()->index(sourceRow, 0)).row();
|
||||
}
|
||||
|
||||
#include "moc_mediamessagefiltermodel.cpp"
|
||||
|
||||
@@ -63,5 +63,5 @@ public:
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
int getRowForEventId(const QString &eventId) const;
|
||||
Q_INVOKABLE int getRowForSourceItem(int sourceRow) const;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user