Compare commits
77 Commits
release/24
...
work/thiag
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edee51a08a | ||
|
|
2a8cd74ab1 | ||
|
|
63bc7055c2 | ||
|
|
1cca9733d6 | ||
|
|
1104da5e2c | ||
|
|
3a9718c09d | ||
|
|
55362c5573 | ||
|
|
0bba2299b3 | ||
|
|
45685af9e9 | ||
|
|
6c416a9338 | ||
|
|
1b0027e1d2 | ||
|
|
2409adf516 | ||
|
|
554801dfe4 | ||
|
|
20c23917e9 | ||
|
|
ef953b7574 | ||
|
|
6b79795229 | ||
|
|
9cb7ec2348 | ||
|
|
437c981d30 | ||
|
|
0334cae4c8 | ||
|
|
24c405d747 | ||
|
|
a3f5962809 | ||
|
|
0deb7495f0 | ||
|
|
d34f89fc4b | ||
|
|
a909ed498f | ||
|
|
16f4e17e8f | ||
|
|
0e9592a96c | ||
|
|
704ee6a53a | ||
|
|
5b9afbce9a | ||
|
|
396cc8e8ef | ||
|
|
bf776b5c06 | ||
|
|
be319f88d3 | ||
|
|
af40d555d4 | ||
|
|
f802dbe686 | ||
|
|
2379e3d83b | ||
|
|
9e90ac0412 | ||
|
|
c27948ca3c | ||
|
|
c3b9d664df | ||
|
|
31ef0a5223 | ||
|
|
14c58acea1 | ||
|
|
5dae20603e | ||
|
|
3f6fa94289 | ||
|
|
117615a8b0 | ||
|
|
4a52773c7d | ||
|
|
edfee495c6 | ||
|
|
7d112df7c6 | ||
|
|
9acaaade45 | ||
|
|
aaca28dbf6 | ||
|
|
d4ef5f9d4d | ||
|
|
2095dea801 | ||
|
|
a36f7ef10d | ||
|
|
9874962ee3 | ||
|
|
4b08022075 | ||
|
|
dc3db3aec4 | ||
|
|
0568c2a93d | ||
|
|
7ab0a6fc9e | ||
|
|
d6b780762e | ||
|
|
5ef66b5cf6 | ||
|
|
19e8cd5e48 | ||
|
|
df5117892f | ||
|
|
aaa4216f55 | ||
|
|
85ee5084b6 | ||
|
|
bb9ce117de | ||
|
|
00c5aa26bb | ||
|
|
bae4de227c | ||
|
|
253f891c5a | ||
|
|
6966159062 | ||
|
|
07d3b80c3e | ||
|
|
a41d0f3214 | ||
|
|
1ee15de78b | ||
|
|
b044358970 | ||
|
|
d2e11bb3bb | ||
|
|
a55bac899c | ||
|
|
c2380fb8df | ||
|
|
f31c644b13 | ||
|
|
26cd621d0e | ||
|
|
4c58512c54 | ||
|
|
04c1b47660 |
@@ -110,7 +110,7 @@
|
|||||||
{
|
{
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/quotient-im/libQuotient.git",
|
"url": "https://github.com/quotient-im/libQuotient.git",
|
||||||
"tag": "0.9.2",
|
"branch": "dev",
|
||||||
"disable-submodules": true
|
"disable-submodules": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ Dependencies:
|
|||||||
'frameworks/kquickcharts': '@latest-kf6'
|
'frameworks/kquickcharts': '@latest-kf6'
|
||||||
'frameworks/knotifications': '@latest-kf6'
|
'frameworks/knotifications': '@latest-kf6'
|
||||||
'frameworks/kcolorscheme': '@latest-kf6'
|
'frameworks/kcolorscheme': '@latest-kf6'
|
||||||
'frameworks/kiconthemes': '@latest-kf6'
|
|
||||||
'libraries/kquickimageeditor': '@latest-kf6'
|
'libraries/kquickimageeditor': '@latest-kf6'
|
||||||
'frameworks/sonnet': '@latest-kf6'
|
'frameworks/sonnet': '@latest-kf6'
|
||||||
'frameworks/prison': '@latest-kf6'
|
'frameworks/prison': '@latest-kf6'
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
# KDE Applications version, managed by release script.
|
# KDE Applications version, managed by release script.
|
||||||
set(RELEASE_SERVICE_VERSION_MAJOR "24")
|
set(RELEASE_SERVICE_VERSION_MAJOR "25")
|
||||||
set(RELEASE_SERVICE_VERSION_MINOR "12")
|
set(RELEASE_SERVICE_VERSION_MINOR "03")
|
||||||
set(RELEASE_SERVICE_VERSION_MICRO "3")
|
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
||||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||||
|
|
||||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||||
@@ -66,7 +66,7 @@ if (QT_KNOWN_POLICY_QTP0004)
|
|||||||
qt_policy(SET QTP0004 NEW)
|
qt_policy(SET QTP0004 NEW)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels IconThemes ColorScheme)
|
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels ColorScheme)
|
||||||
set_package_properties(KF6 PROPERTIES
|
set_package_properties(KF6 PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
PURPOSE "Basic application components"
|
PURPOSE "Basic application components"
|
||||||
@@ -94,7 +94,7 @@ else()
|
|||||||
set_package_properties(KF6QQC2DesktopStyle PROPERTIES
|
set_package_properties(KF6QQC2DesktopStyle PROPERTIES
|
||||||
TYPE RUNTIME
|
TYPE RUNTIME
|
||||||
)
|
)
|
||||||
ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0)
|
ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0 REQUIRED)
|
||||||
|
|
||||||
find_package(ICU 61.0 COMPONENTS uc)
|
find_package(ICU 61.0 COMPONENTS uc)
|
||||||
set_package_properties(ICU PROPERTIES
|
set_package_properties(ICU PROPERTIES
|
||||||
@@ -123,13 +123,12 @@ set_package_properties(cmark PROPERTIES
|
|||||||
PURPOSE "Convert markdown to html"
|
PURPOSE "Convert markdown to html"
|
||||||
)
|
)
|
||||||
|
|
||||||
ecm_find_qmlmodule(org.kde.kquickimageeditor 1.0)
|
ecm_find_qmlmodule(QtLocation REQUIRED)
|
||||||
ecm_find_qmlmodule(org.kde.kitemmodels 1.0)
|
ecm_find_qmlmodule(org.kde.kitemmodels 1.0 REQUIRED)
|
||||||
ecm_find_qmlmodule(org.kde.quickcharts 1.0)
|
ecm_find_qmlmodule(org.kde.quickcharts 1.0 REQUIRED)
|
||||||
ecm_find_qmlmodule(QtLocation)
|
ecm_find_qmlmodule(org.kde.prison REQUIRED)
|
||||||
ecm_find_qmlmodule(org.kde.prison)
|
|
||||||
|
|
||||||
find_package(KQuickImageEditor COMPONENTS)
|
find_package(KQuickImageEditor REQUIRED COMPONENTS)
|
||||||
set_package_properties(KQuickImageEditor PROPERTIES
|
set_package_properties(KQuickImageEditor PROPERTIES
|
||||||
TYPE REQUIRED
|
TYPE REQUIRED
|
||||||
DESCRIPTION "Simple image editor for QtQuick applications"
|
DESCRIPTION "Simple image editor for QtQuick applications"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.6.0'
|
classpath 'com.android.tools.build:gradle:7.4.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ ecm_add_test(
|
|||||||
TEST_NAME neochatroomtest
|
TEST_NAME neochatroomtest
|
||||||
)
|
)
|
||||||
|
|
||||||
# ecm_add_test(
|
ecm_add_test(
|
||||||
# texthandlertest.cpp
|
texthandlertest.cpp
|
||||||
# LINK_LIBRARIES neochat Qt::Test
|
LINK_LIBRARIES neochat Qt::Test
|
||||||
# TEST_NAME texthandlertest
|
TEST_NAME texthandlertest
|
||||||
# )
|
)
|
||||||
|
|
||||||
ecm_add_test(
|
ecm_add_test(
|
||||||
delegatesizehelpertest.cpp
|
delegatesizehelpertest.cpp
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ private Q_SLOTS:
|
|||||||
void receiveRichEdited();
|
void receiveRichEdited();
|
||||||
void receiveLineSeparator();
|
void receiveLineSeparator();
|
||||||
void receiveRichCodeUrl();
|
void receiveRichCodeUrl();
|
||||||
void receiveRichColor();
|
|
||||||
|
|
||||||
void componentOutput_data();
|
void componentOutput_data();
|
||||||
void componentOutput();
|
void componentOutput();
|
||||||
@@ -521,25 +520,6 @@ void TextHandlerTest::receiveRichCodeUrl()
|
|||||||
QCOMPARE(testTextHandler.handleRecieveRichText(), input);
|
QCOMPARE(testTextHandler.handleRecieveRichText(), input);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextHandlerTest::receiveRichColor()
|
|
||||||
{
|
|
||||||
const QString testInputString = QStringLiteral(
|
|
||||||
"<span data-mx-color=\"#ff00be\">¯</span><span data-mx-color=\"#ff3b1d\">\\</span><span data-mx-color=\"#ffa600\">_</span><span "
|
|
||||||
"data-mx-color=\"#64d200\">(</span><span data-mx-color=\"#00e261\">ツ</span><span data-mx-color=\"#00e7ff\">)</span><span "
|
|
||||||
"data-mx-color=\"#00e1ff\">_</span><span data-mx-color=\"#00bdff\">/</span><span data-mx-color=\"#ff60ff\">¯</span>");
|
|
||||||
const QString testOutputString = QStringLiteral(
|
|
||||||
"<span style=\"color: #ff00be;\">¯</span><span style=\"color: #ff3b1d;\">\\</span><span style=\"color: #ffa600;\">_</span><span style=\"color: "
|
|
||||||
"#64d200;\">(</span><span style=\"color: #00e261;\">ツ</span><span style=\"color: #00e7ff;\">)</span><span style=\"color: #00e1ff;\">_</span><span "
|
|
||||||
"style=\"color: #00bdff;\">/</span><span style=\"color: #ff60ff;\">¯</span>");
|
|
||||||
|
|
||||||
TextHandler testTextHandler;
|
|
||||||
testTextHandler.setData(testInputString);
|
|
||||||
|
|
||||||
qInfo() << testTextHandler.handleRecieveRichText();
|
|
||||||
|
|
||||||
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextHandlerTest::componentOutput_data()
|
void TextHandlerTest::componentOutput_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<QString>("testInputString");
|
QTest::addColumn<QString>("testInputString");
|
||||||
|
|||||||
@@ -54,15 +54,11 @@
|
|||||||
<summary xml:lang="ar">دردش على ماتركس</summary>
|
<summary xml:lang="ar">دردش على ماتركس</summary>
|
||||||
<summary xml:lang="ca">Xat a Matrix</summary>
|
<summary xml:lang="ca">Xat a Matrix</summary>
|
||||||
<summary xml:lang="ca-valencia">Xat a Matrix</summary>
|
<summary xml:lang="ca-valencia">Xat a Matrix</summary>
|
||||||
<summary xml:lang="de">Über Matrix unterhalten</summary>
|
|
||||||
<summary xml:lang="en-GB">Chat on Matrix</summary>
|
<summary xml:lang="en-GB">Chat on Matrix</summary>
|
||||||
<summary xml:lang="eo">Babilo en Matrix</summary>
|
|
||||||
<summary xml:lang="es">Charle en Matrix</summary>
|
<summary xml:lang="es">Charle en Matrix</summary>
|
||||||
<summary xml:lang="eu">Berriketa Matrix-en</summary>
|
<summary xml:lang="eu">Berriketa Matrix-en</summary>
|
||||||
<summary xml:lang="fi">Keskustelu Matrixissä</summary>
|
|
||||||
<summary xml:lang="fr">Discuter sur Matrix</summary>
|
<summary xml:lang="fr">Discuter sur Matrix</summary>
|
||||||
<summary xml:lang="gl">Charlar en Matrix</summary>
|
<summary xml:lang="gl">Charlar en Matrix</summary>
|
||||||
<summary xml:lang="he">התכתבות דרך Matrix</summary>
|
|
||||||
<summary xml:lang="hu">Csevegés Matrixon</summary>
|
<summary xml:lang="hu">Csevegés Matrixon</summary>
|
||||||
<summary xml:lang="ia">Conversation en ditecto sur Matrix</summary>
|
<summary xml:lang="ia">Conversation en ditecto sur Matrix</summary>
|
||||||
<summary xml:lang="it">Chat su Matrix</summary>
|
<summary xml:lang="it">Chat su Matrix</summary>
|
||||||
@@ -71,7 +67,6 @@
|
|||||||
<summary xml:lang="nn">Prat med via Matrix</summary>
|
<summary xml:lang="nn">Prat med via Matrix</summary>
|
||||||
<summary xml:lang="pl">Rozmawiaj na Matriksie</summary>
|
<summary xml:lang="pl">Rozmawiaj na Matriksie</summary>
|
||||||
<summary xml:lang="sl">Klepet na Matrixu</summary>
|
<summary xml:lang="sl">Klepet na Matrixu</summary>
|
||||||
<summary xml:lang="sv">Chatta på Matrix</summary>
|
|
||||||
<summary xml:lang="ta">மேட்ரிக்ஸுக்கான உரையாடல் செயலி</summary>
|
<summary xml:lang="ta">மேட்ரிக்ஸுக்கான உரையாடல் செயலி</summary>
|
||||||
<summary xml:lang="tr">Matrix Üzerinde Sohbet</summary>
|
<summary xml:lang="tr">Matrix Üzerinde Sohbet</summary>
|
||||||
<summary xml:lang="uk">Спілкування у Matrix</summary>
|
<summary xml:lang="uk">Спілкування у Matrix</summary>
|
||||||
@@ -119,7 +114,7 @@
|
|||||||
<p xml:lang="eu">«NeoChat»ek «Matrix» zehaztapenaren ezaugarri guztiak eskaintzen dituen aplikazio bat izan nahi du. Beraz, egungo zehaztapen egonkorrean dagoen guztiaren euskarria du, VoIP, hariak eta muturren artean zifratzeko salbuespen nabarmenekin. Badira beste ez-betetze txikiago batzuk, «Matrix»en zehaztapena etengabe eboluzioan dagoelako, baina azken helburua zehaztapen osoaren euskarria ematea izaten jarraitzen du.</p>
|
<p xml:lang="eu">«NeoChat»ek «Matrix» zehaztapenaren ezaugarri guztiak eskaintzen dituen aplikazio bat izan nahi du. Beraz, egungo zehaztapen egonkorrean dagoen guztiaren euskarria du, VoIP, hariak eta muturren artean zifratzeko salbuespen nabarmenekin. Badira beste ez-betetze txikiago batzuk, «Matrix»en zehaztapena etengabe eboluzioan dagoelako, baina azken helburua zehaztapen osoaren euskarria ematea izaten jarraitzen du.</p>
|
||||||
<p xml:lang="fi">NeoChat pyrkii olemaan Matrix-määritelmän täysominaisuuksinen sovellus, joten se tukee kaikkea nykyisessä vakaassa määritelmässä muutamaa huomattavaa poikkeusta lukuun ottamatta (VoIP, säikeet ja jotkin piirteet päästä päähän -salauksessa). Joitakin pienempiäkin puutteita on Matrix-määritelmän jatkuvan kehityksen vuoksi, mutta lopputavoitteena on tarjota määritelmän täysi tuki.</p>
|
<p xml:lang="fi">NeoChat pyrkii olemaan Matrix-määritelmän täysominaisuuksinen sovellus, joten se tukee kaikkea nykyisessä vakaassa määritelmässä muutamaa huomattavaa poikkeusta lukuun ottamatta (VoIP, säikeet ja jotkin piirteet päästä päähän -salauksessa). Joitakin pienempiäkin puutteita on Matrix-määritelmän jatkuvan kehityksen vuoksi, mutta lopputavoitteena on tarjota määritelmän täysi tuki.</p>
|
||||||
<p xml:lang="fr">L'objectif de NeoChat est d'être une application complète pour le protocole Matrix. En tant que tel, tout dans la spécification stable actuelle avec les exceptions notables de VoIP, les processus et certains aspects du chiffrement de bout en bout sont pris en charge. Il y a quelques autres petites omissions en raison du fait que la spécification du protocole Matrix est en constante évolution. Cependant, l'objectif reste de fournir un soutien éventuel pour l'ensemble de la spécification.</p>
|
<p xml:lang="fr">L'objectif de NeoChat est d'être une application complète pour le protocole Matrix. En tant que tel, tout dans la spécification stable actuelle avec les exceptions notables de VoIP, les processus et certains aspects du chiffrement de bout en bout sont pris en charge. Il y a quelques autres petites omissions en raison du fait que la spécification du protocole Matrix est en constante évolution. Cependant, l'objectif reste de fournir un soutien éventuel pour l'ensemble de la spécification.</p>
|
||||||
<p xml:lang="gl">NeoChat pretende ser unha aplicación completa para a especificación de Matrix. Coas excepcións de VoIP, conversas fiadas e algúns aspectos da cifraxe de extremo a extremo, a versión estábel segue as especificacións. Existen algunhas outras pequenas omisións debido ao feito de que Matrix está en continua evolución pero a intención é fornecer compatibilidade coa especificación completa.</p>
|
<p xml:lang="gl">NeoChat pretende ser unha aplicación completa para a especificación de Matrix. Coas excepcións de VoIP, conversas fiadas e algúns aspectos da cifraxe de extremo a extremo, a versión estábel segue as especificacións. Existen algunhas outras pequenas omisións debido ao feito de que Matrix está en continua evolución pero a intención é implementar a especificación completa.</p>
|
||||||
<p xml:lang="he">NeoChat מתיימר להיות יישום עתיר יכולות לפי מפרט Matrix. כיוון שזה ייעודו, כל מה שבמפרט היציב עם חריגות משמעותיות כגון VoIP, שרשורים ועוד מגוון היבטים של הצפנה מקצה לקצה נתמכים גם הם. יש מספר השמטות קטן עקב העובדה שהמפרט של Matrix ממשיך להתפתח אך המטרה היא להמשיך לספק תמיכה בסופו של דבר לכל המפרט.</p>
|
<p xml:lang="he">NeoChat מתיימר להיות יישום עתיר יכולות לפי מפרט Matrix. כיוון שזה ייעודו, כל מה שבמפרט היציב עם חריגות משמעותיות כגון VoIP, שרשורים ועוד מגוון היבטים של הצפנה מקצה לקצה נתמכים גם הם. יש מספר השמטות קטן עקב העובדה שהמפרט של Matrix ממשיך להתפתח אך המטרה היא להמשיך לספק תמיכה בסופו של דבר לכל המפרט.</p>
|
||||||
<p xml:lang="hu">A NeoChat célja, hogy a Matrix specifikációnak megfelelő teljes funkcionalitású alkalmazás legyen. Mint ilyen, a jelenlegi stabil specifikáció támogatott a VoIP, a szálak és a végpontok közötti titkosítás egyes elemeinek kivételével. Van még néhány kisebb hiányosság annak köszönhetően, hogy a Matrix specifikáció folyamatosan fejlődik, de végső cél a teljes specifikáció megvalósítása.</p>
|
<p xml:lang="hu">A NeoChat célja, hogy a Matrix specifikációnak megfelelő teljes funkcionalitású alkalmazás legyen. Mint ilyen, a jelenlegi stabil specifikáció támogatott a VoIP, a szálak és a végpontok közötti titkosítás egyes elemeinek kivételével. Van még néhány kisebb hiányosság annak köszönhetően, hogy a Matrix specifikáció folyamatosan fejlődik, de végső cél a teljes specifikáció megvalósítása.</p>
|
||||||
<p xml:lang="ia">NeoChat aspira a esser un application plenmente eminente per le specification de Matrix. Tal como omne cosas in le specification currentemente stabile con le exceptiones notabile de VOIP, threads e alcun aspectos del cryptation End-to-End es supportate. Il ha ltere pauc omissiones, debite al facto que le specification de Matrix es in evolution constante ma le aspiration remane a fornir supporto eventual per le integre specification.</p>
|
<p xml:lang="ia">NeoChat aspira a esser un application plenmente eminente per le specification de Matrix. Tal como omne cosas in le specification currentemente stabile con le exceptiones notabile de VOIP, threads e alcun aspectos del cryptation End-to-End es supportate. Il ha ltere pauc omissiones, debite al facto que le specification de Matrix es in evolution constante ma le aspiration remane a fornir supporto eventual per le integre specification.</p>
|
||||||
@@ -293,6 +288,7 @@
|
|||||||
<value key="KDE::windows_store::StoreLogoSquare">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/storelogo-1080x1080.png</value>
|
<value key="KDE::windows_store::StoreLogoSquare">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/storelogo-1080x1080.png</value>
|
||||||
<value key="KDE::windows_store::Icon">https://invent.kde.org/network/neochat/-/raw/master/icons/300-apps-neochat.png</value>
|
<value key="KDE::windows_store::Icon">https://invent.kde.org/network/neochat/-/raw/master/icons/300-apps-neochat.png</value>
|
||||||
<value key="KDE::windows_store::PromotionalArt16x9">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/promoimage-1920x1080.png</value>
|
<value key="KDE::windows_store::PromotionalArt16x9">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/promoimage-1920x1080.png</value>
|
||||||
|
<value key="KDE::supporters">Tanguy Fardet</value>
|
||||||
</custom>
|
</custom>
|
||||||
<launchable type="desktop-id">org.kde.neochat.desktop</launchable>
|
<launchable type="desktop-id">org.kde.neochat.desktop</launchable>
|
||||||
<screenshots>
|
<screenshots>
|
||||||
@@ -447,10 +443,6 @@
|
|||||||
<content_attribute id="social-chat">intense</content_attribute>
|
<content_attribute id="social-chat">intense</content_attribute>
|
||||||
</content_rating>
|
</content_rating>
|
||||||
<releases>
|
<releases>
|
||||||
<release version="24.12.3" date="2025-03-06"/>
|
|
||||||
<release version="24.12.2" date="2025-02-06"/>
|
|
||||||
<release version="24.12.1" date="2025-01-09"/>
|
|
||||||
<release version="24.12.0" date="2024-12-12"/>
|
|
||||||
<release version="24.08.3" date="2024-11-07"/>
|
<release version="24.08.3" date="2024-11-07"/>
|
||||||
<release version="24.08.2" date="2024-10-10"/>
|
<release version="24.08.2" date="2024-10-10"/>
|
||||||
<release version="24.08.1" date="2024-09-12"/>
|
<release version="24.08.1" date="2024-09-12"/>
|
||||||
|
|||||||
@@ -88,32 +88,22 @@ GenericName[x-test]=xxMatrix Clientxx
|
|||||||
GenericName[zh_CN]=Matrix 客户端
|
GenericName[zh_CN]=Matrix 客户端
|
||||||
GenericName[zh_TW]=Matrix 用戶端
|
GenericName[zh_TW]=Matrix 用戶端
|
||||||
Comment=Chat on Matrix
|
Comment=Chat on Matrix
|
||||||
Comment[ar]=دردش على ماتركس
|
|
||||||
Comment[ca]=Xat a Matrix
|
Comment[ca]=Xat a Matrix
|
||||||
Comment[ca@valencia]=Xat a Matrix
|
Comment[ca@valencia]=Xat a Matrix
|
||||||
Comment[de]=Über Matrix unterhalten
|
|
||||||
Comment[en_GB]=Chat on Matrix
|
|
||||||
Comment[eo]=Babilo en Matrix
|
|
||||||
Comment[es]=Chat en Matrix
|
Comment[es]=Chat en Matrix
|
||||||
Comment[eu]=Berriketa Matrix-en
|
Comment[eu]=Berriketa Matrix-en
|
||||||
Comment[fi]=Keskustele Matrixissä
|
|
||||||
Comment[fr]=Clavarder sur Matrix
|
Comment[fr]=Clavarder sur Matrix
|
||||||
Comment[gl]=Charle en Matrix
|
Comment[gl]=Charle en Matrix
|
||||||
Comment[he]=התכתבות דרך Matrix
|
|
||||||
Comment[hu]=Csevegés Matrixon
|
Comment[hu]=Csevegés Matrixon
|
||||||
Comment[ia]=Conversation en ditecto sur Matrix
|
Comment[ia]=Conversation en ditecto sur Matrix
|
||||||
Comment[it]= su Matrix
|
Comment[it]= su Matrix
|
||||||
Comment[ka]=საუბარი Matrix-ზე
|
Comment[ka]=ჩატი Matrix-ზე
|
||||||
Comment[nl]=Chat op Matrix
|
Comment[nl]=Chat op Matrix
|
||||||
Comment[pl]=Rozmawiaj na Matriksie
|
Comment[pl]=Rozmawiaj na Matriksie
|
||||||
Comment[pt_BR]=Bate papo na Matrix
|
|
||||||
Comment[sl]=Klepet na Matrixu
|
Comment[sl]=Klepet na Matrixu
|
||||||
Comment[sv]=Chatta på Matrix
|
Comment[tr]=Matrix Üzerinde Sohbet Et
|
||||||
Comment[ta]=மேட்ரிக்ஸில் உரையாட உதவும்
|
|
||||||
Comment[tr]=Matrix üzerinde sohbet edin
|
|
||||||
Comment[uk]=Спілкування у Matrix
|
Comment[uk]=Спілкування у Matrix
|
||||||
Comment[zh_CN]=在 Matrix 上聊天
|
Comment[x-test]=xxChat on Matrixxx
|
||||||
Comment[zh_TW]=在 Matrix 上聊天
|
|
||||||
MimeType=x-scheme-handler/matrix;
|
MimeType=x-scheme-handler/matrix;
|
||||||
Exec=neochat %u
|
Exec=neochat %u
|
||||||
Terminal=false
|
Terminal=false
|
||||||
|
|||||||
339
po/ar/neochat.po
339
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
310
po/az/neochat.po
310
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
610
po/ca/neochat.po
610
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
310
po/cs/neochat.po
310
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
310
po/da/neochat.po
310
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
659
po/de/neochat.po
659
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
310
po/el/neochat.po
310
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
433
po/eo/neochat.po
433
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
310
po/es/neochat.po
310
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
372
po/eu/neochat.po
372
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
1588
po/fi/neochat.po
1588
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
322
po/fr/neochat.po
322
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
368
po/gl/neochat.po
368
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
310
po/hu/neochat.po
310
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
312
po/ia/neochat.po
312
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
310
po/id/neochat.po
310
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
310
po/ie/neochat.po
310
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
314
po/it/neochat.po
314
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
310
po/ja/neochat.po
310
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
314
po/ka/neochat.po
314
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
310
po/ko/neochat.po
310
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
310
po/lt/neochat.po
310
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
310
po/lv/neochat.po
310
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
318
po/nl/neochat.po
318
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
344
po/nn/neochat.po
344
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
310
po/pa/neochat.po
310
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
322
po/pl/neochat.po
322
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
310
po/pt/neochat.po
310
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
310
po/ru/neochat.po
310
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
919
po/sk/neochat.po
919
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
312
po/sl/neochat.po
312
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
336
po/sv/neochat.po
336
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
337
po/ta/neochat.po
337
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
326
po/tr/neochat.po
326
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
312
po/uk/neochat.po
312
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
|||||||
# SPDX-License-Identifier: CC0-1.0
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
---
|
---
|
||||||
name: neochat
|
name: neochat
|
||||||
base: core24
|
base: core22
|
||||||
adopt-info: neochat
|
adopt-info: neochat
|
||||||
grade: stable
|
grade: stable
|
||||||
confinement: strict
|
confinement: strict
|
||||||
@@ -27,10 +27,6 @@ apps:
|
|||||||
|
|
||||||
compression: lzo
|
compression: lzo
|
||||||
|
|
||||||
package-repositories:
|
|
||||||
- type: apt
|
|
||||||
ppa: ubuntu-toolchain-r/test
|
|
||||||
|
|
||||||
slots:
|
slots:
|
||||||
session-dbus-interface:
|
session-dbus-interface:
|
||||||
interface: dbus
|
interface: dbus
|
||||||
@@ -80,7 +76,6 @@ parts:
|
|||||||
source-depth: 1
|
source-depth: 1
|
||||||
plugin: cmake
|
plugin: cmake
|
||||||
build-environment:
|
build-environment:
|
||||||
- PATH: /snap/bin:${PATH}
|
|
||||||
- PKG_CONFIG_PATH: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET/pkgconfig:$PKG_CONFIG_PATH
|
- PKG_CONFIG_PATH: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET/pkgconfig:$PKG_CONFIG_PATH
|
||||||
cmake-parameters:
|
cmake-parameters:
|
||||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
@@ -97,13 +92,9 @@ parts:
|
|||||||
- olm
|
- olm
|
||||||
- qtkeychain
|
- qtkeychain
|
||||||
source: https://github.com/quotient-im/libQuotient.git
|
source: https://github.com/quotient-im/libQuotient.git
|
||||||
source-tag: 0.9.1
|
source-tag: 0.9.0
|
||||||
source-depth: 1
|
source-depth: 1
|
||||||
plugin: cmake
|
plugin: cmake
|
||||||
build-environment:
|
|
||||||
- PATH: /snap/bin:${PATH}
|
|
||||||
build-snaps:
|
|
||||||
- cmake
|
|
||||||
build-packages:
|
build-packages:
|
||||||
- libssl-dev
|
- libssl-dev
|
||||||
cmake-parameters:
|
cmake-parameters:
|
||||||
@@ -122,10 +113,6 @@ parts:
|
|||||||
source-tag: 'v0.3.0'
|
source-tag: 'v0.3.0'
|
||||||
source-depth: 1
|
source-depth: 1
|
||||||
plugin: cmake
|
plugin: cmake
|
||||||
build-environment:
|
|
||||||
- PATH: /snap/bin:${PATH}
|
|
||||||
- PYTHONPATH: ${CRAFT_STAGE}/lib/python3.12/site-packages:${CRAFT_STAGE}/usr/lib/python3/dist-packages
|
|
||||||
- LD_LIBRARY_PATH: "/snap/mesa-2404/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/kde-qt6-core24-sdk/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libproxy:$LD_LIBRARY_PATH"
|
|
||||||
cmake-parameters:
|
cmake-parameters:
|
||||||
- -DCMAKE_INSTALL_PREFIX=/usr
|
- -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
- -DCMAKE_BUILD_TYPE=Release
|
- -DCMAKE_BUILD_TYPE=Release
|
||||||
@@ -143,12 +130,9 @@ parts:
|
|||||||
- kquickimageeditor
|
- kquickimageeditor
|
||||||
parse-info:
|
parse-info:
|
||||||
- usr/share/metainfo/org.kde.neochat.appdata.xml
|
- usr/share/metainfo/org.kde.neochat.appdata.xml
|
||||||
source: .
|
source: https://invent.kde.org/network/neochat.git
|
||||||
|
source-tag: 'v24.08.1'
|
||||||
plugin: cmake
|
plugin: cmake
|
||||||
build-environment:
|
|
||||||
- PATH: /snap/bin:${PATH}
|
|
||||||
- PYTHONPATH: ${CRAFT_STAGE}/lib/python3.12/site-packages:${CRAFT_STAGE}/usr/lib/python3/dist-packages
|
|
||||||
- LD_LIBRARY_PATH: "/snap/mesa-2404/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/kde-qt6-core24-sdk/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libproxy:$LD_LIBRARY_PATH"
|
|
||||||
build-packages:
|
build-packages:
|
||||||
- cmark
|
- cmark
|
||||||
- libcmark-dev
|
- libcmark-dev
|
||||||
@@ -172,12 +156,3 @@ parts:
|
|||||||
prime:
|
prime:
|
||||||
- usr/lib/*/libcmark.so*
|
- usr/lib/*/libcmark.so*
|
||||||
|
|
||||||
gpu-2404:
|
|
||||||
after: [neochat]
|
|
||||||
source: https://github.com/canonical/gpu-snap.git
|
|
||||||
plugin: dump
|
|
||||||
override-prime: |
|
|
||||||
craftctl default
|
|
||||||
${CRAFT_PART_SRC}/bin/gpu-2404-cleanup mesa-2404
|
|
||||||
prime:
|
|
||||||
- bin/gpu-2404-wrapper
|
|
||||||
|
|||||||
@@ -200,12 +200,6 @@ set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
|||||||
QT_QML_SINGLETON_TYPE TRUE
|
QT_QML_SINGLETON_TYPE TRUE
|
||||||
)
|
)
|
||||||
|
|
||||||
if(ANDROID OR WIN32)
|
|
||||||
set_source_files_properties(qml/ShareActionStub.qml PROPERTIES
|
|
||||||
QT_QML_SOURCE_TYPENAME ShareAction
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat
|
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat
|
||||||
QML_FILES
|
QML_FILES
|
||||||
@@ -317,9 +311,13 @@ if(NOT ANDROID AND NOT WIN32)
|
|||||||
qml/EditMenu.qml
|
qml/EditMenu.qml
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
|
set_source_files_properties(qml/ShareActionStub.qml PROPERTIES
|
||||||
|
QT_RESOURCE_ALIAS qml/ShareAction.qml
|
||||||
|
)
|
||||||
qt_target_qml_sources(neochat QML_FILES qml/ShareActionStub.qml)
|
qt_target_qml_sources(neochat QML_FILES qml/ShareActionStub.qml)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
configure_file(config-neochat.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-neochat.h)
|
configure_file(config-neochat.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-neochat.h)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
@@ -418,7 +416,6 @@ target_link_libraries(neochat PUBLIC
|
|||||||
KF6::ConfigGui
|
KF6::ConfigGui
|
||||||
KF6::CoreAddons
|
KF6::CoreAddons
|
||||||
KF6::SonnetCore
|
KF6::SonnetCore
|
||||||
KF6::IconThemes
|
|
||||||
KF6::ColorScheme
|
KF6::ColorScheme
|
||||||
KF6::ItemModels
|
KF6::ItemModels
|
||||||
QuotientQt6
|
QuotientQt6
|
||||||
@@ -492,7 +489,6 @@ if(ANDROID)
|
|||||||
"network-connect"
|
"network-connect"
|
||||||
"list-remove-user"
|
"list-remove-user"
|
||||||
"org.kde.neochat"
|
"org.kde.neochat"
|
||||||
"org.kde.neochat.tray"
|
|
||||||
"preferences-system-users"
|
"preferences-system-users"
|
||||||
"preferences-desktop-theme-global"
|
"preferences-desktop-theme-global"
|
||||||
"notifications"
|
"notifications"
|
||||||
@@ -530,13 +526,11 @@ if(ANDROID)
|
|||||||
"object-rotate-left"
|
"object-rotate-left"
|
||||||
"object-rotate-right"
|
"object-rotate-right"
|
||||||
"add-subtitle"
|
"add-subtitle"
|
||||||
"security-high"
|
|
||||||
"security-low"
|
"security-low"
|
||||||
"security-low-symbolic"
|
"security-low-symbolic"
|
||||||
"kde"
|
"kde"
|
||||||
"list-remove-symbolic"
|
"list-remove-symbolic"
|
||||||
"edit-delete"
|
"edit-delete"
|
||||||
"user-home-symbolic"
|
|
||||||
)
|
)
|
||||||
ecm_add_android_apk(neochat-app ANDROID_DIR ${CMAKE_SOURCE_DIR}/android)
|
ecm_add_android_apk(neochat-app ANDROID_DIR ${CMAKE_SOURCE_DIR}/android)
|
||||||
else()
|
else()
|
||||||
|
|||||||
@@ -176,14 +176,13 @@ QQC2.Control {
|
|||||||
RowLayout {
|
RowLayout {
|
||||||
QQC2.ScrollView {
|
QQC2.ScrollView {
|
||||||
id: chatBarScrollView
|
id: chatBarScrollView
|
||||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
|
||||||
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
|
||||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
|
||||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumHeight: Kirigami.Units.gridUnit * 8
|
Layout.maximumHeight: Kirigami.Units.gridUnit * 8
|
||||||
Layout.minimumHeight: Kirigami.Units.gridUnit * 3
|
|
||||||
|
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||||
|
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||||
|
Layout.minimumHeight: Kirigami.Units.gridUnit * 2
|
||||||
|
|
||||||
// HACK: This is to stop the ScrollBar flickering on and off as the height is increased
|
// HACK: This is to stop the ScrollBar flickering on and off as the height is increased
|
||||||
QQC2.ScrollBar.vertical.policy: chatBarHeightAnimation.running && implicitHeight <= height ? QQC2.ScrollBar.AlwaysOff : QQC2.ScrollBar.AsNeeded
|
QQC2.ScrollBar.vertical.policy: chatBarHeightAnimation.running && implicitHeight <= height ? QQC2.ScrollBar.AlwaysOff : QQC2.ScrollBar.AsNeeded
|
||||||
@@ -321,11 +320,12 @@ QQC2.Control {
|
|||||||
id: actionsRow
|
id: actionsRow
|
||||||
spacing: 0
|
spacing: 0
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
Layout.bottomMargin: Kirigami.Units.smallSpacing * 4
|
Layout.bottomMargin: Kirigami.Units.smallSpacing * 1.5
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: root.actions
|
model: root.actions
|
||||||
delegate: QQC2.ToolButton {
|
delegate: QQC2.ToolButton {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
icon.name: modelData.isBusy ? "" : (modelData.icon.name.length > 0 ? modelData.icon.name : modelData.icon.source)
|
icon.name: modelData.isBusy ? "" : (modelData.icon.name.length > 0 ? modelData.icon.name : modelData.icon.source)
|
||||||
onClicked: modelData.trigger()
|
onClicked: modelData.trigger()
|
||||||
|
|
||||||
@@ -342,6 +342,7 @@ QQC2.Control {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateSizeHelper {
|
DelegateSizeHelper {
|
||||||
id: chatBarSizeHelper
|
id: chatBarSizeHelper
|
||||||
startBreakpoint: Kirigami.Units.gridUnit * 46
|
startBreakpoint: Kirigami.Units.gridUnit * 46
|
||||||
|
|||||||
@@ -319,8 +319,7 @@ void ChatBarCache::postMessage()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto type = std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result);
|
room->postMessage(text(), sendText, *std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result), replyId(), editId(), threadId());
|
||||||
room->postMessage(text(), sendText, type ? *type : Quotient::RoomMessageEvent::MsgType::Text, replyId(), editId(), threadId());
|
|
||||||
clearCache();
|
clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ void Controller::addConnection(NeoChatConnection *c)
|
|||||||
connect(c, &NeoChatConnection::syncDone, this, [this, c]() {
|
connect(c, &NeoChatConnection::syncDone, this, [this, c]() {
|
||||||
m_notificationsManager.handleNotifications(c);
|
m_notificationsManager.handleNotifications(c);
|
||||||
});
|
});
|
||||||
|
connect(c, &NeoChatConnection::showInviteNotification, &m_notificationsManager, &NotificationsManager::postInviteNotification);
|
||||||
|
|
||||||
c->sync();
|
c->sync();
|
||||||
|
|
||||||
@@ -294,7 +295,7 @@ bool Controller::supportSystemTray() const
|
|||||||
void Controller::setQuitOnLastWindowClosed()
|
void Controller::setQuitOnLastWindowClosed()
|
||||||
{
|
{
|
||||||
#ifndef Q_OS_ANDROID
|
#ifndef Q_OS_ANDROID
|
||||||
if (supportSystemTray() && NeoChatConfig::self()->systemTray()) {
|
if (NeoChatConfig::self()->systemTray()) {
|
||||||
m_trayIcon = new TrayIcon(this);
|
m_trayIcon = new TrayIcon(this);
|
||||||
m_trayIcon->show();
|
m_trayIcon->show();
|
||||||
} else {
|
} else {
|
||||||
@@ -422,14 +423,10 @@ void Controller::setTestMode(bool test)
|
|||||||
|
|
||||||
void Controller::removeConnection(const QString &userId)
|
void Controller::removeConnection(const QString &userId)
|
||||||
{
|
{
|
||||||
// When loadAccessTokenFromKeyChain() fails m_connectionsLoading won't have an
|
|
||||||
// entry for it so we need to check both separately.
|
|
||||||
if (m_accountsLoading.contains(userId)) {
|
|
||||||
m_accountsLoading.removeAll(userId);
|
|
||||||
Q_EMIT accountsLoadingChanged();
|
|
||||||
}
|
|
||||||
if (m_connectionsLoading.contains(userId) && m_connectionsLoading[userId]) {
|
if (m_connectionsLoading.contains(userId) && m_connectionsLoading[userId]) {
|
||||||
auto connection = m_connectionsLoading[userId];
|
auto connection = m_connectionsLoading[userId];
|
||||||
|
m_accountsLoading.removeAll(userId);
|
||||||
|
Q_EMIT accountsLoadingChanged();
|
||||||
SettingsGroup("Accounts"_ls).remove(userId);
|
SettingsGroup("Accounts"_ls).remove(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Window
|
|
||||||
|
|
||||||
import org.kde.kirigami as Kirigami
|
import org.kde.kirigami as Kirigami
|
||||||
import org.kde.kirigamiaddons.formcard as FormCard
|
import org.kde.kirigamiaddons.formcard as FormCard
|
||||||
@@ -38,7 +37,7 @@ FormCard.FormCardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openEventSource(stateKey: string): void {
|
function openEventSource(stateKey: string): void {
|
||||||
root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||||
model: stateKeysModel,
|
model: stateKeysModel,
|
||||||
allowEdit: true,
|
allowEdit: true,
|
||||||
room: root.room,
|
room: root.room,
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ void LoginHelper::init()
|
|||||||
m_connection = new NeoChatConnection();
|
m_connection = new NeoChatConnection();
|
||||||
m_matrixId = QString();
|
m_matrixId = QString();
|
||||||
m_password = QString();
|
m_password = QString();
|
||||||
m_deviceName = QStringLiteral("NeoChat");
|
m_deviceName = QStringLiteral("NeoChat %1 %2 %3 %4")
|
||||||
|
.arg(QSysInfo::machineHostName(), QSysInfo::productType(), QSysInfo::productVersion(), QSysInfo::currentCpuArchitecture());
|
||||||
m_supportsSso = false;
|
m_supportsSso = false;
|
||||||
m_supportsPassword = false;
|
m_supportsPassword = false;
|
||||||
m_ssoUrl = QUrl();
|
m_ssoUrl = QUrl();
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ LoginStep {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
FormCard.FormTextDelegate {
|
FormCard.FormTextDelegate {
|
||||||
textItem.wrapMode: Text.Wrap
|
|
||||||
text: i18n("Please wait while your messages are loaded from the server. This might take a little while.")
|
text: i18n("Please wait while your messages are loaded from the server. This might take a little while.")
|
||||||
}
|
}
|
||||||
FormCard.AbstractFormDelegate {
|
FormCard.AbstractFormDelegate {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ Kirigami.Page {
|
|||||||
|
|
||||||
property bool showExisting: false
|
property bool showExisting: false
|
||||||
property bool _showExisting: showExisting && root.currentStepString === root.initialStep
|
property bool _showExisting: showExisting && root.currentStepString === root.initialStep
|
||||||
property bool showSettings: true
|
|
||||||
property alias currentStep: module.item
|
property alias currentStep: module.item
|
||||||
property string currentStepString: initialStep
|
property string currentStepString: initialStep
|
||||||
property string initialStep: "LoginRegister"
|
property string initialStep: "LoginRegister"
|
||||||
@@ -97,7 +96,7 @@ Kirigami.Page {
|
|||||||
required property string userId
|
required property string userId
|
||||||
required property NeoChatConnection connection
|
required property NeoChatConnection connection
|
||||||
|
|
||||||
text: QmlUtils.escapeString(connection.localUser.displayName)
|
text: connection.localUser.displayName
|
||||||
description: connection.localUser.id
|
description: connection.localUser.id
|
||||||
leadingPadding: Kirigami.Units.largeSpacing
|
leadingPadding: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
@@ -107,6 +106,8 @@ Kirigami.Page {
|
|||||||
}
|
}
|
||||||
leading: KirigamiComponents.Avatar {
|
leading: KirigamiComponents.Avatar {
|
||||||
id: avatar
|
id: avatar
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.topMargin: Kirigami.Units.gridUnit
|
||||||
name: delegate.text
|
name: delegate.text
|
||||||
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
|
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
|
||||||
source: delegate.connection.localUser.avatarUrl.toString().length > 0 ? delegate.connection.makeMediaUrl(delegate.connection.localUser.avatarUrl) : ""
|
source: delegate.connection.localUser.avatarUrl.toString().length > 0 ? delegate.connection.makeMediaUrl(delegate.connection.localUser.avatarUrl) : ""
|
||||||
@@ -266,7 +267,6 @@ Kirigami.Page {
|
|||||||
FormCard.FormCard {
|
FormCard.FormCard {
|
||||||
Layout.topMargin: Kirigami.Units.largeSpacing * 2
|
Layout.topMargin: Kirigami.Units.largeSpacing * 2
|
||||||
maximumWidth: Kirigami.Units.gridUnit * 20
|
maximumWidth: Kirigami.Units.gridUnit * 20
|
||||||
visible: root.showSettings
|
|
||||||
FormCard.FormButtonDelegate {
|
FormCard.FormButtonDelegate {
|
||||||
text: i18nc("@action:button", "Settings")
|
text: i18nc("@action:button", "Settings")
|
||||||
icon.name: "settings-configure"
|
icon.name: "settings-configure"
|
||||||
|
|||||||
@@ -37,7 +37,6 @@
|
|||||||
#include <KCrash>
|
#include <KCrash>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <KIconTheme>
|
|
||||||
#include <KLocalizedContext>
|
#include <KLocalizedContext>
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
|
|
||||||
@@ -102,7 +101,6 @@ Q_DECL_EXPORT
|
|||||||
#endif
|
#endif
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
KIconTheme::initTheme();
|
|
||||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||||
|
|
||||||
#ifdef HAVE_WEBVIEW
|
#ifdef HAVE_WEBVIEW
|
||||||
|
|||||||
@@ -600,19 +600,14 @@ bool ActionsModel::handleQuickEditAction(NeoChatRoom *room, const QString &messa
|
|||||||
} else {
|
} else {
|
||||||
originalString = event->plainBody();
|
originalString = event->plainBody();
|
||||||
}
|
}
|
||||||
QString replaceId = event->id();
|
|
||||||
const auto eventRelation = event->relatesTo();
|
|
||||||
if (eventRelation && eventRelation->type == "m.replace"_L1) {
|
|
||||||
replaceId = eventRelation->eventId;
|
|
||||||
}
|
|
||||||
if (flags == "/g"_L1) {
|
if (flags == "/g"_L1) {
|
||||||
room->postHtmlMessage(messageText, originalString.replace(regex, replacement), event->msgtype(), {}, replaceId);
|
room->postHtmlMessage(messageText, originalString.replace(regex, replacement), event->msgtype(), {}, event->id());
|
||||||
} else {
|
} else {
|
||||||
room->postHtmlMessage(messageText,
|
room->postHtmlMessage(messageText,
|
||||||
originalString.replace(originalString.indexOf(regex), regex.size(), replacement),
|
originalString.replace(originalString.indexOf(regex), regex.size(), replacement),
|
||||||
event->msgtype(),
|
event->msgtype(),
|
||||||
{},
|
{},
|
||||||
replaceId);
|
event->id());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ MessageContentModel::MessageContentModel(NeoChatRoom *room, const QString &event
|
|||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
, m_room(room)
|
, m_room(room)
|
||||||
, m_eventId(eventId)
|
, m_eventId(eventId)
|
||||||
, m_isPending(isPending)
|
, m_currentState(isPending ? Pending : Unknown)
|
||||||
, m_isReply(isReply)
|
, m_isReply(isReply)
|
||||||
{
|
{
|
||||||
initializeModel();
|
initializeModel();
|
||||||
@@ -45,19 +45,27 @@ void MessageContentModel::initializeModel()
|
|||||||
Q_ASSERT(m_room != nullptr);
|
Q_ASSERT(m_room != nullptr);
|
||||||
Q_ASSERT(!m_eventId.isEmpty());
|
Q_ASSERT(!m_eventId.isEmpty());
|
||||||
|
|
||||||
connect(this, &MessageContentModel::eventUnavailable, this, &MessageContentModel::getEvent);
|
connect(m_room, &NeoChatRoom::pendingEventAdded, this, [this]() {
|
||||||
|
if (m_room != nullptr && m_currentState == Unknown) {
|
||||||
|
initializeEvent();
|
||||||
|
updateReplyModel();
|
||||||
|
resetModel();
|
||||||
|
}
|
||||||
|
});
|
||||||
connect(m_room, &NeoChatRoom::pendingEventAboutToMerge, this, [this](Quotient::RoomEvent *serverEvent) {
|
connect(m_room, &NeoChatRoom::pendingEventAboutToMerge, this, [this](Quotient::RoomEvent *serverEvent) {
|
||||||
if (m_room != nullptr) {
|
if (m_room != nullptr) {
|
||||||
if (m_eventId == serverEvent->id() || m_eventId == serverEvent->transactionId()) {
|
if (m_eventId == serverEvent->id() || m_eventId == serverEvent->transactionId()) {
|
||||||
beginResetModel();
|
|
||||||
m_isPending = false;
|
|
||||||
m_eventId = serverEvent->id();
|
m_eventId = serverEvent->id();
|
||||||
initializeEvent();
|
|
||||||
endResetModel();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
connect(m_room, &NeoChatRoom::pendingEventMerged, this, [this]() {
|
||||||
|
if (m_room != nullptr && m_currentState == Pending) {
|
||||||
|
initializeEvent();
|
||||||
|
updateReplyModel();
|
||||||
|
resetModel();
|
||||||
|
}
|
||||||
|
});
|
||||||
connect(m_room, &NeoChatRoom::addedMessages, this, [this](int fromIndex, int toIndex) {
|
connect(m_room, &NeoChatRoom::addedMessages, this, [this](int fromIndex, int toIndex) {
|
||||||
if (m_room != nullptr) {
|
if (m_room != nullptr) {
|
||||||
for (int i = fromIndex; i <= toIndex; i++) {
|
for (int i = fromIndex; i <= toIndex; i++) {
|
||||||
@@ -143,20 +151,33 @@ void MessageContentModel::initializeModel()
|
|||||||
});
|
});
|
||||||
|
|
||||||
initializeEvent();
|
initializeEvent();
|
||||||
updateReplyModel();
|
if (m_currentState == Available || m_currentState == Pending) {
|
||||||
|
updateReplyModel();
|
||||||
|
}
|
||||||
resetModel();
|
resetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageContentModel::initializeEvent()
|
void MessageContentModel::initializeEvent()
|
||||||
{
|
{
|
||||||
const auto event = m_room->getEvent(m_eventId);
|
if (m_currentState == UnAvailable) {
|
||||||
if (event == nullptr) {
|
|
||||||
Q_EMIT eventUnavailable();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto eventResult = m_room->getEvent(m_eventId);
|
||||||
|
if (eventResult.first == nullptr) {
|
||||||
|
if (m_currentState != Pending) {
|
||||||
|
getEvent();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (eventResult.second) {
|
||||||
|
m_currentState = Pending;
|
||||||
|
} else {
|
||||||
|
m_currentState = Available;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_eventSenderObject == nullptr) {
|
if (m_eventSenderObject == nullptr) {
|
||||||
auto senderId = event->senderId();
|
auto senderId = eventResult.first->senderId();
|
||||||
// A pending event might not have a sender ID set yet but in that case it must
|
// A pending event might not have a sender ID set yet but in that case it must
|
||||||
// be the local member.
|
// be the local member.
|
||||||
if (senderId.isEmpty()) {
|
if (senderId.isEmpty()) {
|
||||||
@@ -172,7 +193,6 @@ void MessageContentModel::getEvent()
|
|||||||
Quotient::connectUntil(m_room.get(), &NeoChatRoom::extraEventLoaded, this, [this](const QString &eventId) {
|
Quotient::connectUntil(m_room.get(), &NeoChatRoom::extraEventLoaded, this, [this](const QString &eventId) {
|
||||||
if (m_room != nullptr) {
|
if (m_room != nullptr) {
|
||||||
if (eventId == m_eventId) {
|
if (eventId == m_eventId) {
|
||||||
m_notFound = false;
|
|
||||||
initializeEvent();
|
initializeEvent();
|
||||||
updateReplyModel();
|
updateReplyModel();
|
||||||
resetModel();
|
resetModel();
|
||||||
@@ -184,7 +204,7 @@ void MessageContentModel::getEvent()
|
|||||||
Quotient::connectUntil(m_room.get(), &NeoChatRoom::extraEventNotFound, this, [this](const QString &eventId) {
|
Quotient::connectUntil(m_room.get(), &NeoChatRoom::extraEventNotFound, this, [this](const QString &eventId) {
|
||||||
if (m_room != nullptr) {
|
if (m_room != nullptr) {
|
||||||
if (eventId == m_eventId) {
|
if (eventId == m_eventId) {
|
||||||
m_notFound = true;
|
m_currentState = UnAvailable;
|
||||||
resetModel();
|
resetModel();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -237,7 +257,7 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
|||||||
const auto component = m_components[index.row()];
|
const auto component = m_components[index.row()];
|
||||||
|
|
||||||
const auto event = m_room->getEvent(m_eventId);
|
const auto event = m_room->getEvent(m_eventId);
|
||||||
if (event == nullptr) {
|
if (event.first == nullptr) {
|
||||||
if (role == DisplayRole) {
|
if (role == DisplayRole) {
|
||||||
if (m_isReply) {
|
if (m_isReply) {
|
||||||
return i18n("Loading reply");
|
return i18n("Loading reply");
|
||||||
@@ -252,7 +272,7 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (role == DisplayRole) {
|
if (role == DisplayRole) {
|
||||||
if (m_notFound || m_room->connection()->isIgnored(m_eventSenderId)) {
|
if (m_currentState == UnAvailable || m_room->connection()->isIgnored(m_eventSenderId)) {
|
||||||
Kirigami::Platform::PlatformTheme *theme =
|
Kirigami::Platform::PlatformTheme *theme =
|
||||||
static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
|
static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
|
||||||
|
|
||||||
@@ -276,7 +296,7 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
|||||||
if (!component.content.isEmpty()) {
|
if (!component.content.isEmpty()) {
|
||||||
return component.content;
|
return component.content;
|
||||||
}
|
}
|
||||||
return EventHandler::richBody(m_room, event);
|
return EventHandler::richBody(m_room, event.first);
|
||||||
}
|
}
|
||||||
if (role == ComponentTypeRole) {
|
if (role == ComponentTypeRole) {
|
||||||
return component.type;
|
return component.type;
|
||||||
@@ -285,53 +305,53 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
|||||||
return component.attributes;
|
return component.attributes;
|
||||||
}
|
}
|
||||||
if (role == EventIdRole) {
|
if (role == EventIdRole) {
|
||||||
return EventHandler::id(event);
|
return EventHandler::id(event.first);
|
||||||
}
|
}
|
||||||
if (role == TimeRole) {
|
if (role == TimeRole) {
|
||||||
const auto pendingIt = std::find_if(m_room->pendingEvents().cbegin(), m_room->pendingEvents().cend(), [event](const PendingEventItem &pendingEvent) {
|
const auto pendingIt = std::find_if(m_room->pendingEvents().cbegin(), m_room->pendingEvents().cend(), [event](const PendingEventItem &pendingEvent) {
|
||||||
return event->transactionId() == pendingEvent->transactionId();
|
return event.first->transactionId() == pendingEvent->transactionId();
|
||||||
});
|
});
|
||||||
|
|
||||||
auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated();
|
auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated();
|
||||||
return EventHandler::time(event, m_isPending, lastUpdated);
|
return EventHandler::time(event.first, m_currentState == Pending, lastUpdated);
|
||||||
}
|
}
|
||||||
if (role == TimeStringRole) {
|
if (role == TimeStringRole) {
|
||||||
const auto pendingIt = std::find_if(m_room->pendingEvents().cbegin(), m_room->pendingEvents().cend(), [event](const PendingEventItem &pendingEvent) {
|
const auto pendingIt = std::find_if(m_room->pendingEvents().cbegin(), m_room->pendingEvents().cend(), [event](const PendingEventItem &pendingEvent) {
|
||||||
return event->transactionId() == pendingEvent->transactionId();
|
return event.first->transactionId() == pendingEvent->transactionId();
|
||||||
});
|
});
|
||||||
|
|
||||||
auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated();
|
auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated();
|
||||||
return EventHandler::timeString(event, QStringLiteral("hh:mm"), m_isPending, lastUpdated);
|
return EventHandler::timeString(event.first, QStringLiteral("hh:mm"), m_currentState == Pending, lastUpdated);
|
||||||
}
|
}
|
||||||
if (role == AuthorRole) {
|
if (role == AuthorRole) {
|
||||||
return QVariant::fromValue<NeochatRoomMember *>(m_eventSenderObject.get());
|
return QVariant::fromValue<NeochatRoomMember *>(m_eventSenderObject.get());
|
||||||
}
|
}
|
||||||
if (role == MediaInfoRole) {
|
if (role == MediaInfoRole) {
|
||||||
return EventHandler::mediaInfo(m_room, event);
|
return EventHandler::mediaInfo(m_room, event.first);
|
||||||
}
|
}
|
||||||
if (role == FileTransferInfoRole) {
|
if (role == FileTransferInfoRole) {
|
||||||
return QVariant::fromValue(m_room->cachedFileTransferInfo(event));
|
return QVariant::fromValue(m_room->cachedFileTransferInfo(event.first));
|
||||||
}
|
}
|
||||||
if (role == ItineraryModelRole) {
|
if (role == ItineraryModelRole) {
|
||||||
return QVariant::fromValue<ItineraryModel *>(m_itineraryModel);
|
return QVariant::fromValue<ItineraryModel *>(m_itineraryModel);
|
||||||
}
|
}
|
||||||
if (role == LatitudeRole) {
|
if (role == LatitudeRole) {
|
||||||
return EventHandler::latitude(event);
|
return EventHandler::latitude(event.first);
|
||||||
}
|
}
|
||||||
if (role == LongitudeRole) {
|
if (role == LongitudeRole) {
|
||||||
return EventHandler::longitude(event);
|
return EventHandler::longitude(event.first);
|
||||||
}
|
}
|
||||||
if (role == AssetRole) {
|
if (role == AssetRole) {
|
||||||
return EventHandler::locationAssetType(event);
|
return EventHandler::locationAssetType(event.first);
|
||||||
}
|
}
|
||||||
if (role == PollHandlerRole) {
|
if (role == PollHandlerRole) {
|
||||||
return QVariant::fromValue<PollHandler *>(m_room->poll(m_eventId));
|
return QVariant::fromValue<PollHandler *>(m_room->poll(m_eventId));
|
||||||
}
|
}
|
||||||
if (role == ReplyEventIdRole) {
|
if (role == ReplyEventIdRole) {
|
||||||
return EventHandler::replyId(event);
|
return EventHandler::replyId(event.first);
|
||||||
}
|
}
|
||||||
if (role == ReplyAuthorRole) {
|
if (role == ReplyAuthorRole) {
|
||||||
return QVariant::fromValue(EventHandler::replyAuthor(m_room, event));
|
return QVariant::fromValue(EventHandler::replyAuthor(m_room, event.first));
|
||||||
}
|
}
|
||||||
if (role == ReplyContentModelRole) {
|
if (role == ReplyContentModelRole) {
|
||||||
return QVariant::fromValue<MessageContentModel *>(m_replyModel);
|
return QVariant::fromValue<MessageContentModel *>(m_replyModel);
|
||||||
@@ -387,18 +407,17 @@ QHash<int, QByteArray> MessageContentModel::roleNames() const
|
|||||||
|
|
||||||
void MessageContentModel::resetModel()
|
void MessageContentModel::resetModel()
|
||||||
{
|
{
|
||||||
const auto event = m_room->getEvent(m_eventId);
|
|
||||||
|
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_components.clear();
|
m_components.clear();
|
||||||
|
|
||||||
if (m_room->connection()->isIgnored(m_eventSenderId) || m_notFound) {
|
if (m_room->connection()->isIgnored(m_eventSenderId) || m_currentState == UnAvailable) {
|
||||||
m_components += MessageComponent{MessageComponentType::Text, QString(), {}};
|
m_components += MessageComponent{MessageComponentType::Text, QString(), {}};
|
||||||
endResetModel();
|
endResetModel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event == nullptr) {
|
const auto event = m_room->getEvent(m_eventId);
|
||||||
|
if (event.first == nullptr) {
|
||||||
m_components += MessageComponent{MessageComponentType::Loading, QString(), {}};
|
m_components += MessageComponent{MessageComponentType::Loading, QString(), {}};
|
||||||
endResetModel();
|
endResetModel();
|
||||||
return;
|
return;
|
||||||
@@ -431,19 +450,19 @@ void MessageContentModel::resetContent(bool isEditing, bool isThreading)
|
|||||||
QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEditing, bool isThreading)
|
QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEditing, bool isThreading)
|
||||||
{
|
{
|
||||||
const auto event = m_room->getEvent(m_eventId);
|
const auto event = m_room->getEvent(m_eventId);
|
||||||
if (event == nullptr) {
|
if (event.first == nullptr) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<MessageComponent> newComponents;
|
QList<MessageComponent> newComponents;
|
||||||
|
|
||||||
if (eventCast<const Quotient::RoomMessageEvent>(event)
|
if (eventCast<const Quotient::RoomMessageEvent>(event.first)
|
||||||
&& eventCast<const Quotient::RoomMessageEvent>(event)->rawMsgtype() == QStringLiteral("m.key.verification.request")) {
|
&& eventCast<const Quotient::RoomMessageEvent>(event.first)->rawMsgtype() == QStringLiteral("m.key.verification.request")) {
|
||||||
newComponents += MessageComponent{MessageComponentType::Verification, QString(), {}};
|
newComponents += MessageComponent{MessageComponentType::Verification, QString(), {}};
|
||||||
return newComponents;
|
return newComponents;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->isRedacted()) {
|
if (event.first->isRedacted()) {
|
||||||
newComponents += MessageComponent{MessageComponentType::Text, QString(), {}};
|
newComponents += MessageComponent{MessageComponentType::Text, QString(), {}};
|
||||||
return newComponents;
|
return newComponents;
|
||||||
}
|
}
|
||||||
@@ -455,7 +474,7 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
|
|||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
newComponents += MessageComponent{MessageComponentType::ChatBar, QString(), {}};
|
newComponents += MessageComponent{MessageComponentType::ChatBar, QString(), {}};
|
||||||
} else {
|
} else {
|
||||||
newComponents.append(componentsForType(MessageComponentType::typeForEvent(*event)));
|
newComponents.append(componentsForType(MessageComponentType::typeForEvent(*event.first)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_room->urlPreviewEnabled()) {
|
if (m_room->urlPreviewEnabled()) {
|
||||||
@@ -463,7 +482,7 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the event is already threaded the ThreadModel will handle displaying a chat bar.
|
// If the event is already threaded the ThreadModel will handle displaying a chat bar.
|
||||||
if (isThreading && !EventHandler::isThreaded(event)) {
|
if (isThreading && !EventHandler::isThreaded(event.first)) {
|
||||||
newComponents += MessageComponent{MessageComponentType::ChatBar, QString(), {}};
|
newComponents += MessageComponent{MessageComponentType::ChatBar, QString(), {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,11 +492,11 @@ QList<MessageComponent> MessageContentModel::messageContentComponents(bool isEdi
|
|||||||
void MessageContentModel::updateReplyModel()
|
void MessageContentModel::updateReplyModel()
|
||||||
{
|
{
|
||||||
const auto event = m_room->getEvent(m_eventId);
|
const auto event = m_room->getEvent(m_eventId);
|
||||||
if (event == nullptr || m_isReply) {
|
if (event.first == nullptr || m_isReply) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EventHandler::hasReply(event) || (EventHandler::isThreaded(event) && NeoChatConfig::self()->threads())) {
|
if (!EventHandler::hasReply(event.first) || (EventHandler::isThreaded(event.first) && NeoChatConfig::self()->threads())) {
|
||||||
if (m_replyModel) {
|
if (m_replyModel) {
|
||||||
delete m_replyModel;
|
delete m_replyModel;
|
||||||
}
|
}
|
||||||
@@ -488,7 +507,7 @@ void MessageContentModel::updateReplyModel()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_replyModel = new MessageContentModel(m_room, EventHandler::replyId(event), true, false, this);
|
m_replyModel = new MessageContentModel(m_room, EventHandler::replyId(event.first), true, false, this);
|
||||||
|
|
||||||
connect(m_replyModel, &MessageContentModel::eventUpdated, this, [this]() {
|
connect(m_replyModel, &MessageContentModel::eventUpdated, this, [this]() {
|
||||||
Q_EMIT dataChanged(index(0), index(0), {ReplyAuthorRole});
|
Q_EMIT dataChanged(index(0), index(0), {ReplyAuthorRole});
|
||||||
@@ -498,13 +517,13 @@ void MessageContentModel::updateReplyModel()
|
|||||||
QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentType::Type type)
|
QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentType::Type type)
|
||||||
{
|
{
|
||||||
const auto event = m_room->getEvent(m_eventId);
|
const auto event = m_room->getEvent(m_eventId);
|
||||||
if (event == nullptr) {
|
if (event.first == nullptr) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MessageComponentType::Text: {
|
case MessageComponentType::Text: {
|
||||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event);
|
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first);
|
||||||
auto body = EventHandler::rawMessageBody(*roomMessageEvent);
|
auto body = EventHandler::rawMessageBody(*roomMessageEvent);
|
||||||
return TextHandler().textComponents(body,
|
return TextHandler().textComponents(body,
|
||||||
EventHandler::messageBodyInputFormat(*roomMessageEvent),
|
EventHandler::messageBodyInputFormat(*roomMessageEvent),
|
||||||
@@ -515,11 +534,11 @@ QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentT
|
|||||||
case MessageComponentType::File: {
|
case MessageComponentType::File: {
|
||||||
QList<MessageComponent> components;
|
QList<MessageComponent> components;
|
||||||
components += MessageComponent{MessageComponentType::File, QString(), {}};
|
components += MessageComponent{MessageComponentType::File, QString(), {}};
|
||||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event);
|
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first);
|
||||||
|
|
||||||
if (m_emptyItinerary) {
|
if (m_emptyItinerary) {
|
||||||
if (!m_isReply) {
|
if (!m_isReply) {
|
||||||
auto fileTransferInfo = m_room->cachedFileTransferInfo(event);
|
auto fileTransferInfo = m_room->cachedFileTransferInfo(event.first);
|
||||||
|
|
||||||
#ifndef Q_OS_ANDROID
|
#ifndef Q_OS_ANDROID
|
||||||
Q_ASSERT(roomMessageEvent->content() != nullptr && roomMessageEvent->has<EventContent::FileContent>());
|
Q_ASSERT(roomMessageEvent->content() != nullptr && roomMessageEvent->has<EventContent::FileContent>());
|
||||||
@@ -567,8 +586,8 @@ QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentT
|
|||||||
case MessageComponentType::Image:
|
case MessageComponentType::Image:
|
||||||
case MessageComponentType::Audio:
|
case MessageComponentType::Audio:
|
||||||
case MessageComponentType::Video: {
|
case MessageComponentType::Video: {
|
||||||
if (!event->is<StickerEvent>()) {
|
if (!event.first->is<StickerEvent>()) {
|
||||||
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event);
|
const auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first);
|
||||||
const auto fileContent = roomMessageEvent->get<EventContent::FileContentBase>();
|
const auto fileContent = roomMessageEvent->get<EventContent::FileContentBase>();
|
||||||
if (fileContent != nullptr) {
|
if (fileContent != nullptr) {
|
||||||
const auto fileInfo = fileContent->commonInfo();
|
const auto fileInfo = fileContent->commonInfo();
|
||||||
@@ -660,13 +679,13 @@ void MessageContentModel::closeLinkPreview(int row)
|
|||||||
void MessageContentModel::updateItineraryModel()
|
void MessageContentModel::updateItineraryModel()
|
||||||
{
|
{
|
||||||
const auto event = m_room->getEvent(m_eventId);
|
const auto event = m_room->getEvent(m_eventId);
|
||||||
if (m_room == nullptr || event == nullptr) {
|
if (m_room == nullptr || event.first == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event)) {
|
if (auto roomMessageEvent = eventCast<const Quotient::RoomMessageEvent>(event.first)) {
|
||||||
if (roomMessageEvent->has<EventContent::FileContent>()) {
|
if (roomMessageEvent->has<EventContent::FileContent>()) {
|
||||||
auto filePath = m_room->cachedFileTransferInfo(event).localPath;
|
auto filePath = m_room->cachedFileTransferInfo(event.first).localPath;
|
||||||
if (filePath.isEmpty() && m_itineraryModel != nullptr) {
|
if (filePath.isEmpty() && m_itineraryModel != nullptr) {
|
||||||
delete m_itineraryModel;
|
delete m_itineraryModel;
|
||||||
m_itineraryModel = nullptr;
|
m_itineraryModel = nullptr;
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ class MessageContentModel : public QAbstractListModel
|
|||||||
Q_PROPERTY(bool showAuthor READ showAuthor WRITE setShowAuthor NOTIFY showAuthorChanged)
|
Q_PROPERTY(bool showAuthor READ showAuthor WRITE setShowAuthor NOTIFY showAuthorChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum MessageState {
|
||||||
|
Unknown, /**< The message state is unknown. */
|
||||||
|
Pending, /**< The message is a new pending message which the server has not yet acknowledged. */
|
||||||
|
Available, /**< The message is available and acknowledged by the server. */
|
||||||
|
UnAvailable, /**< The message can't be retrieved either because it doesn't exist or is blocked. */
|
||||||
|
};
|
||||||
|
Q_ENUM(MessageState)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Defines the model roles.
|
* @brief Defines the model roles.
|
||||||
*/
|
*/
|
||||||
@@ -98,7 +106,6 @@ public:
|
|||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void showAuthorChanged();
|
void showAuthorChanged();
|
||||||
void eventUnavailable();
|
|
||||||
void eventUpdated();
|
void eventUpdated();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -107,10 +114,9 @@ private:
|
|||||||
QString m_eventSenderId;
|
QString m_eventSenderId;
|
||||||
std::unique_ptr<NeochatRoomMember> m_eventSenderObject = nullptr;
|
std::unique_ptr<NeochatRoomMember> m_eventSenderObject = nullptr;
|
||||||
|
|
||||||
bool m_isPending;
|
MessageState m_currentState = Unknown;
|
||||||
bool m_showAuthor = true;
|
bool m_showAuthor = true;
|
||||||
bool m_isReply;
|
bool m_isReply;
|
||||||
bool m_notFound = false;
|
|
||||||
|
|
||||||
void initializeModel();
|
void initializeModel();
|
||||||
void initializeEvent();
|
void initializeEvent();
|
||||||
|
|||||||
@@ -160,12 +160,21 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
|||||||
refreshLastUserEvents(i);
|
refreshLastUserEvents(i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
#if Quotient_VERSION_MINOR > 9 || (Quotient_VERSION_MINOR == 9 && Quotient_VERSION_PATCH > 0)
|
||||||
|
connect(m_currentRoom, &Room::pendingEventAdded, this, [this](const Quotient::RoomEvent *event) {
|
||||||
|
m_initialized = true;
|
||||||
|
createEventObjects(event, true);
|
||||||
|
beginInsertRows({}, 0, 0);
|
||||||
|
endInsertRows();
|
||||||
|
});
|
||||||
|
#else
|
||||||
connect(m_currentRoom, &Room::pendingEventAboutToAdd, this, [this](Quotient::RoomEvent *event) {
|
connect(m_currentRoom, &Room::pendingEventAboutToAdd, this, [this](Quotient::RoomEvent *event) {
|
||||||
m_initialized = true;
|
m_initialized = true;
|
||||||
createEventObjects(event);
|
createEventObjects(event, true);
|
||||||
beginInsertRows({}, 0, 0);
|
beginInsertRows({}, 0, 0);
|
||||||
});
|
});
|
||||||
connect(m_currentRoom, &Room::pendingEventAdded, this, &MessageEventModel::endInsertRows);
|
connect(m_currentRoom, &Room::pendingEventAdded, this, &MessageEventModel::endInsertRows);
|
||||||
|
#endif
|
||||||
connect(m_currentRoom, &Room::pendingEventAboutToMerge, this, [this](RoomEvent *, int i) {
|
connect(m_currentRoom, &Room::pendingEventAboutToMerge, this, [this](RoomEvent *, int i) {
|
||||||
Q_EMIT dataChanged(index(i, 0), index(i, 0), {IsPendingRole});
|
Q_EMIT dataChanged(index(i, 0), index(i, 0), {IsPendingRole});
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
@@ -505,8 +514,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
|
|
||||||
if (role == ProgressInfoRole) {
|
if (role == ProgressInfoRole) {
|
||||||
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
||||||
if (e->has<EventContent::FileContent>() || e->has<EventContent::ImageContent>() || e->has<EventContent::VideoContent>()
|
if (e->has<EventContent::FileContent>()) {
|
||||||
|| e->has<EventContent::AudioContent>()) {
|
|
||||||
return QVariant::fromValue(m_currentRoom->cachedFileTransferInfo(&evt));
|
return QVariant::fromValue(m_currentRoom->cachedFileTransferInfo(&evt));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -619,7 +627,7 @@ int MessageEventModel::eventIdToRow(const QString &eventID) const
|
|||||||
return it - m_currentRoom->messageEvents().rbegin() + timelineBaseIndex();
|
return it - m_currentRoom->messageEvents().rbegin() + timelineBaseIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event)
|
void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event, bool isPending)
|
||||||
{
|
{
|
||||||
if (event == nullptr) {
|
if (event == nullptr) {
|
||||||
return;
|
return;
|
||||||
@@ -642,7 +650,7 @@ void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event)
|
|||||||
|
|
||||||
if (!m_contentModels.contains(eventId) && !m_contentModels.contains(event->transactionId())) {
|
if (!m_contentModels.contains(eventId) && !m_contentModels.contains(event->transactionId())) {
|
||||||
if (!event->isStateEvent() || event->matrixType() == QStringLiteral("org.matrix.msc3672.beacon_info")) {
|
if (!event->isStateEvent() || event->matrixType() == QStringLiteral("org.matrix.msc3672.beacon_info")) {
|
||||||
m_contentModels[eventId] = std::unique_ptr<MessageContentModel>(new MessageContentModel(m_currentRoom, eventId));
|
m_contentModels[eventId] = std::unique_ptr<MessageContentModel>(new MessageContentModel(m_currentRoom, eventId, false, isPending));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ private:
|
|||||||
int refreshEventRoles(const QString &eventId, const QList<int> &roles = {});
|
int refreshEventRoles(const QString &eventId, const QList<int> &roles = {});
|
||||||
void moveReadMarker(const QString &toEventId);
|
void moveReadMarker(const QString &toEventId);
|
||||||
|
|
||||||
void createEventObjects(const Quotient::RoomEvent *event);
|
void createEventObjects(const Quotient::RoomEvent *event, bool isPending = false);
|
||||||
// Hack to ensure that we don't call endInsertRows when we haven't called beginInsertRows
|
// Hack to ensure that we don't call endInsertRows when we haven't called beginInsertRows
|
||||||
bool m_initialized = false;
|
bool m_initialized = false;
|
||||||
|
|
||||||
|
|||||||
@@ -289,7 +289,6 @@ Name[ta]=பகிர்
|
|||||||
Name[tr]=Paylaş
|
Name[tr]=Paylaş
|
||||||
Name[uk]=Оприлюднення
|
Name[uk]=Оприлюднення
|
||||||
Name[x-test]=xxSharexx
|
Name[x-test]=xxSharexx
|
||||||
Name[zh_CN]=分享
|
|
||||||
Name[zh_TW]=分享
|
Name[zh_TW]=分享
|
||||||
Comment=The result of sharing a piece of content
|
Comment=The result of sharing a piece of content
|
||||||
Comment[ar]=نتيجة مشاركة محتوى
|
Comment[ar]=نتيجة مشاركة محتوى
|
||||||
@@ -321,6 +320,5 @@ Comment[ta]=எதையோ பகிர்ந்ததன் விளைவ
|
|||||||
Comment[tr]=Bir parça içerik paylaşımının sonucu
|
Comment[tr]=Bir parça içerik paylaşımının sonucu
|
||||||
Comment[uk]=Результат оприлюднення даних
|
Comment[uk]=Результат оприлюднення даних
|
||||||
Comment[x-test]=xxThe result of sharing a piece of contentxx
|
Comment[x-test]=xxThe result of sharing a piece of contentxx
|
||||||
Comment[zh_CN]=分享一个内容得到的结果
|
|
||||||
Comment[zh_TW]=分享一份內容之後的結果
|
Comment[zh_TW]=分享一份內容之後的結果
|
||||||
Action=Popup
|
Action=Popup
|
||||||
|
|||||||
@@ -109,6 +109,10 @@ void NeoChatConnection::connectSignals()
|
|||||||
Q_EMIT homeHaveHighlightNotificationsChanged();
|
Q_EMIT homeHaveHighlightNotificationsChanged();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
connect(this, &NeoChatConnection::invitedRoom, this, [this](Quotient::Room *room) {
|
||||||
|
auto r = dynamic_cast<NeoChatRoom *>(room);
|
||||||
|
connect(r, &NeoChatRoom::showInviteNotification, this, &NeoChatConnection::showInviteNotification);
|
||||||
|
});
|
||||||
connect(this, &NeoChatConnection::leftRoom, this, [this](Room *room, Room *prev) {
|
connect(this, &NeoChatConnection::leftRoom, this, [this](Room *room, Room *prev) {
|
||||||
Q_UNUSED(room)
|
Q_UNUSED(room)
|
||||||
if (prev && prev->isDirectChat()) {
|
if (prev && prev->isDirectChat()) {
|
||||||
|
|||||||
@@ -205,6 +205,11 @@ Q_SIGNALS:
|
|||||||
*/
|
*/
|
||||||
void errorOccured(const QString &error);
|
void errorOccured(const QString &error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Request a notification be shown for an invite to this room.
|
||||||
|
*/
|
||||||
|
void showInviteNotification(NeoChatRoom *room);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_isOnline = true;
|
bool m_isOnline = true;
|
||||||
void setIsOnline(bool isOnline);
|
void setIsOnline(bool isOnline);
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
|||||||
updatePushNotificationState(QStringLiteral("m.push_rules"));
|
updatePushNotificationState(QStringLiteral("m.push_rules"));
|
||||||
|
|
||||||
Q_EMIT canEncryptRoomChanged();
|
Q_EMIT canEncryptRoomChanged();
|
||||||
|
if (this->joinState() == JoinState::Invite) {
|
||||||
|
Q_EMIT showInviteNotification(this);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Qt::SingleShotConnection);
|
Qt::SingleShotConnection);
|
||||||
connect(this, &Room::changed, this, [this] {
|
connect(this, &Room::changed, this, [this] {
|
||||||
@@ -747,10 +750,7 @@ QList<QString> NeoChatRoom::restrictedIds() const
|
|||||||
|
|
||||||
QString NeoChatRoom::historyVisibility() const
|
QString NeoChatRoom::historyVisibility() const
|
||||||
{
|
{
|
||||||
if (auto stateEvent = currentState().get("m.room.history_visibility"_ls)) {
|
return currentState().get("m.room.history_visibility"_ls)->contentJson()["history_visibility"_ls].toString();
|
||||||
return stateEvent->contentJson()["history_visibility"_ls].toString();
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeoChatRoom::setHistoryVisibility(const QString &historyVisibilityRule)
|
void NeoChatRoom::setHistoryVisibility(const QString &historyVisibilityRule)
|
||||||
@@ -1749,25 +1749,31 @@ void NeoChatRoom::downloadEventFromServer(const QString &eventId)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const RoomEvent *NeoChatRoom::getEvent(const QString &eventId) const
|
std::pair<const Quotient::RoomEvent *, bool> NeoChatRoom::getEvent(const QString &eventId) const
|
||||||
{
|
{
|
||||||
if (eventId.isEmpty()) {
|
if (eventId.isEmpty()) {
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
}
|
||||||
const auto timelineIt = findInTimeline(eventId);
|
const auto timelineIt = findInTimeline(eventId);
|
||||||
if (timelineIt != historyEdge()) {
|
if (timelineIt != historyEdge()) {
|
||||||
return timelineIt->get();
|
return std::make_pair(timelineIt->get(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto pendingIt = findPendingEvent(eventId);
|
auto pendingIt = findPendingEvent(eventId);
|
||||||
if (pendingIt != pendingEvents().end()) {
|
if (pendingIt != pendingEvents().end()) {
|
||||||
return pendingIt->event();
|
return std::make_pair(pendingIt->event(), true);
|
||||||
|
}
|
||||||
|
// findPendingEvent() searches by transaction ID, we also need to check event ID.
|
||||||
|
for (const auto &event : pendingEvents()) {
|
||||||
|
if (event->id() == eventId || event->transactionId() == eventId) {
|
||||||
|
return std::make_pair(event.event(), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto extraIt = std::find_if(m_extraEvents.begin(), m_extraEvents.end(), [eventId](const Quotient::event_ptr_tt<Quotient::RoomEvent> &event) {
|
auto extraIt = std::find_if(m_extraEvents.begin(), m_extraEvents.end(), [eventId](const Quotient::event_ptr_tt<Quotient::RoomEvent> &event) {
|
||||||
return event->id() == eventId;
|
return event->id() == eventId;
|
||||||
});
|
});
|
||||||
return extraIt != m_extraEvents.end() ? extraIt->get() : nullptr;
|
return std::make_pair(extraIt != m_extraEvents.end() ? extraIt->get() : nullptr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const RoomEvent *NeoChatRoom::getReplyForEvent(const RoomEvent &event) const
|
const RoomEvent *NeoChatRoom::getReplyForEvent(const RoomEvent &event) const
|
||||||
|
|||||||
@@ -570,7 +570,7 @@ public:
|
|||||||
*
|
*
|
||||||
* The result will be nullptr if not found so needs to be managed.
|
* The result will be nullptr if not found so needs to be managed.
|
||||||
*/
|
*/
|
||||||
const Quotient::RoomEvent *getEvent(const QString &eventId) const;
|
std::pair<const Quotient::RoomEvent *, bool> getEvent(const QString &eventId) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the event that is being replied to. This includes events that were manually loaded using NeoChatRoom::loadReply.
|
* @brief Returns the event that is being replied to. This includes events that were manually loaded using NeoChatRoom::loadReply.
|
||||||
@@ -654,6 +654,14 @@ Q_SIGNALS:
|
|||||||
*/
|
*/
|
||||||
void showMessage(MessageType::Type messageType, const QString &message);
|
void showMessage(MessageType::Type messageType, const QString &message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Request a notification be shown for an invite to this room.
|
||||||
|
*
|
||||||
|
* @note This may later be blocked if there are any rules on where invites can
|
||||||
|
* come from, but this is not NeoChatRoom's responsibility.
|
||||||
|
*/
|
||||||
|
void showInviteNotification(NeoChatRoom *room);
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
/**
|
/**
|
||||||
* @brief Upload a file to the matrix server and post the file to the room.
|
* @brief Upload a file to the matrix server and post the file to the room.
|
||||||
|
|||||||
@@ -127,8 +127,9 @@ void NotificationsManager::processNotificationJob(QPointer<NeoChatConnection> co
|
|||||||
}
|
}
|
||||||
auto sender = room->member(notification["event"_ls]["sender"_ls].toString());
|
auto sender = room->member(notification["event"_ls]["sender"_ls].toString());
|
||||||
|
|
||||||
|
// Don't display notifications for events in invited rooms
|
||||||
|
// This should prevent empty notifications from appearing when they shouldn't
|
||||||
if (room->joinState() == JoinState::Invite) {
|
if (room->joinState() == JoinState::Invite) {
|
||||||
postInviteNotification(qobject_cast<NeoChatRoom *>(room));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ Comment[ru]=Поиск комнат NeoChat
|
|||||||
Comment[sl]=Najdi sobe v NeoChatu
|
Comment[sl]=Najdi sobe v NeoChatu
|
||||||
Comment[sv]=Sök efter rum i NeoChat
|
Comment[sv]=Sök efter rum i NeoChat
|
||||||
Comment[ta]=நியோச்சாட்டில் அரங்குகளை கண்டுபிடிக்கும்
|
Comment[ta]=நியோச்சாட்டில் அரங்குகளை கண்டுபிடிக்கும்
|
||||||
Comment[tr]=NeoChat’te odalar bulun
|
Comment[tr]=NeoChat'te odalar bulun
|
||||||
Comment[uk]=Пошук кімнат у NeoChat
|
Comment[uk]=Пошук кімнат у NeoChat
|
||||||
Comment[x-test]=xxFind rooms in NeoChatxx
|
Comment[x-test]=xxFind rooms in NeoChatxx
|
||||||
Comment[zh_CN]=在 NeoChat 查找聊天室
|
Comment[zh_CN]=在 NeoChat 查找聊天室
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
"Name[nl]": "Tobias Fella",
|
"Name[nl]": "Tobias Fella",
|
||||||
"Name[nn]": "Tobias Fella",
|
"Name[nn]": "Tobias Fella",
|
||||||
"Name[pl]": "Tobias Fella",
|
"Name[pl]": "Tobias Fella",
|
||||||
"Name[pt_BR]": "Tobias Fella",
|
|
||||||
"Name[ru]": "Tobias Fella",
|
"Name[ru]": "Tobias Fella",
|
||||||
"Name[sk]": "Tobias Fella",
|
"Name[sk]": "Tobias Fella",
|
||||||
"Name[sl]": "Tobias Fella",
|
"Name[sl]": "Tobias Fella",
|
||||||
@@ -34,7 +33,6 @@
|
|||||||
"Name[tr]": "Tobias Fella",
|
"Name[tr]": "Tobias Fella",
|
||||||
"Name[uk]": "Tobias Fella",
|
"Name[uk]": "Tobias Fella",
|
||||||
"Name[x-test]": "xxTobias Fellaxx",
|
"Name[x-test]": "xxTobias Fellaxx",
|
||||||
"Name[zh_CN]": "Tobias Fella",
|
|
||||||
"Name[zh_TW]": "Tobias Fella"
|
"Name[zh_TW]": "Tobias Fella"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -61,7 +59,6 @@
|
|||||||
"Description[nl]": "Delen via NeoChat",
|
"Description[nl]": "Delen via NeoChat",
|
||||||
"Description[nn]": "Del via NeoChat",
|
"Description[nn]": "Del via NeoChat",
|
||||||
"Description[pl]": "Udostępnij przez NeoChat",
|
"Description[pl]": "Udostępnij przez NeoChat",
|
||||||
"Description[pt_BR]": "Compartilhar via NeoChat",
|
|
||||||
"Description[ru]": "Опубликовать в NeoChat",
|
"Description[ru]": "Опубликовать в NeoChat",
|
||||||
"Description[sl]": "Deli prek NeoChat",
|
"Description[sl]": "Deli prek NeoChat",
|
||||||
"Description[sv]": "Dela via NeoChat",
|
"Description[sv]": "Dela via NeoChat",
|
||||||
@@ -69,9 +66,8 @@
|
|||||||
"Description[tr]": "NeoChat ile Paylaş",
|
"Description[tr]": "NeoChat ile Paylaş",
|
||||||
"Description[uk]": "Оприлюднити за допомогою NeoChat",
|
"Description[uk]": "Оприлюднити за допомогою NeoChat",
|
||||||
"Description[x-test]": "xxShare via NeoChatxx",
|
"Description[x-test]": "xxShare via NeoChatxx",
|
||||||
"Description[zh_CN]": "通过 NeoChat 分享",
|
|
||||||
"Description[zh_TW]": "透過 NeoChat 分享",
|
"Description[zh_TW]": "透過 NeoChat 分享",
|
||||||
"Icon": "org.kde.neochat.tray",
|
"Icon": "org.kde.neochat",
|
||||||
"License": "GPL",
|
"License": "GPL",
|
||||||
"Name": "NeoChat",
|
"Name": "NeoChat",
|
||||||
"Name[ar]": "نيوتشات",
|
"Name[ar]": "نيوتشات",
|
||||||
@@ -97,7 +93,6 @@
|
|||||||
"Name[nl]": "NeoChat",
|
"Name[nl]": "NeoChat",
|
||||||
"Name[nn]": "NeoChat",
|
"Name[nn]": "NeoChat",
|
||||||
"Name[pl]": "NeoChat",
|
"Name[pl]": "NeoChat",
|
||||||
"Name[pt_BR]": "NeoChat",
|
|
||||||
"Name[ru]": "NeoChat",
|
"Name[ru]": "NeoChat",
|
||||||
"Name[sk]": "NeoChat",
|
"Name[sk]": "NeoChat",
|
||||||
"Name[sl]": "NeoChat",
|
"Name[sl]": "NeoChat",
|
||||||
@@ -106,7 +101,6 @@
|
|||||||
"Name[tr]": "NeoChat",
|
"Name[tr]": "NeoChat",
|
||||||
"Name[uk]": "NeoChat",
|
"Name[uk]": "NeoChat",
|
||||||
"Name[x-test]": "xxNeoChatxx",
|
"Name[x-test]": "xxNeoChatxx",
|
||||||
"Name[zh_CN]": "NeoChat",
|
|
||||||
"Name[zh_TW]": "NeoChat",
|
"Name[zh_TW]": "NeoChat",
|
||||||
"X-Purpose-ActionDisplay": "NeoChat"
|
"X-Purpose-ActionDisplay": "NeoChat"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -27,38 +27,17 @@ Loader {
|
|||||||
Component {
|
Component {
|
||||||
id: regularMenu
|
id: regularMenu
|
||||||
QQC2.Menu {
|
QQC2.Menu {
|
||||||
QQC2.MenuItem {
|
|
||||||
text: room.isFavourite ? i18n("Remove from Favorites") : i18n("Add to Favorites")
|
|
||||||
icon.name: room.isFavourite ? "bookmark-remove" : "bookmark-new"
|
|
||||||
onTriggered: room.isFavourite ? room.removeTag("m.favourite") : room.addTag("m.favourite", 1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.MenuItem {
|
|
||||||
text: room.isLowPriority ? i18n("Reprioritize") : i18n("Deprioritize")
|
|
||||||
icon.name: room.isLowPriority ? "arrow-up-symbolic" : "arrow-down-symbolic"
|
|
||||||
onTriggered: room.isLowPriority ? room.removeTag("m.lowpriority") : room.addTag("m.lowpriority", 1.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.MenuItem {
|
QQC2.MenuItem {
|
||||||
text: i18n("Mark as Read")
|
text: i18n("Mark as Read")
|
||||||
icon.name: "checkmark"
|
icon.name: "checkmark"
|
||||||
|
enabled: room.notificationCount > 0
|
||||||
onTriggered: room.markAllMessagesAsRead()
|
onTriggered: room.markAllMessagesAsRead()
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.MenuItem {
|
QQC2.MenuSeparator {}
|
||||||
text: room.isDirectChat() ? i18nc("@action:inmenu", "Copy user's Matrix ID to Clipboard") : i18nc("@action:inmenu", "Copy Address to Clipboard")
|
|
||||||
icon.name: "edit-copy"
|
|
||||||
onTriggered: if (room.isDirectChat()) {
|
|
||||||
Clipboard.saveText(room.directChatRemoteMember.id);
|
|
||||||
} else if (room.canonicalAlias.length === 0) {
|
|
||||||
Clipboard.saveText(room.id);
|
|
||||||
} else {
|
|
||||||
Clipboard.saveText(room.canonicalAlias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.Menu {
|
QQC2.Menu {
|
||||||
title: i18n("Notification State")
|
title: i18nc("@action:inmenu", "Notifications")
|
||||||
icon.name: "notifications"
|
icon.name: "notifications"
|
||||||
|
|
||||||
QQC2.MenuItem {
|
QQC2.MenuItem {
|
||||||
@@ -107,6 +86,32 @@ Loader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QQC2.MenuItem {
|
||||||
|
text: room.isFavourite ? i18n("Remove from Favorites") : i18n("Add to Favorites")
|
||||||
|
icon.name: room.isFavourite ? "rating" : "rating-unrated"
|
||||||
|
onTriggered: room.isFavourite ? room.removeTag("m.favourite") : room.addTag("m.favourite", 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.MenuItem {
|
||||||
|
text: room.isLowPriority ? i18n("Reprioritize") : i18n("Deprioritize")
|
||||||
|
icon.name: room.isLowPriority ? "arrow-up-symbolic" : "arrow-down-symbolic"
|
||||||
|
onTriggered: room.isLowPriority ? room.removeTag("m.lowpriority") : room.addTag("m.lowpriority", 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.MenuSeparator {}
|
||||||
|
|
||||||
|
QQC2.MenuItem {
|
||||||
|
text: room.isDirectChat() ? i18nc("@action:inmenu", "Copy user's Matrix ID to Clipboard") : i18nc("@action:inmenu", "Copy Address to Clipboard")
|
||||||
|
icon.name: "edit-copy"
|
||||||
|
onTriggered: if (room.isDirectChat()) {
|
||||||
|
Clipboard.saveText(room.directChatRemoteMember.id);
|
||||||
|
} else if (room.canonicalAlias.length === 0) {
|
||||||
|
Clipboard.saveText(room.id);
|
||||||
|
} else {
|
||||||
|
Clipboard.saveText(room.canonicalAlias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QQC2.MenuItem {
|
QQC2.MenuItem {
|
||||||
text: i18nc("@action:inmenu", "Room Settings")
|
text: i18nc("@action:inmenu", "Room Settings")
|
||||||
icon.name: 'settings-configure-symbolic'
|
icon.name: 'settings-configure-symbolic'
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ Loader {
|
|||||||
model: WebShortcutModel {
|
model: WebShortcutModel {
|
||||||
id: webshortcutmodel
|
id: webshortcutmodel
|
||||||
selectedText: root.selectedText.length > 0 ? root.selectedText : root.plainText
|
selectedText: root.selectedText.length > 0 ? root.selectedText : root.plainText
|
||||||
onOpenUrl: url => RoomManager.resolveResource(url.toString())
|
onOpenUrl: RoomManager.resolveResource(url)
|
||||||
}
|
}
|
||||||
delegate: QQC2.MenuItem {
|
delegate: QQC2.MenuItem {
|
||||||
text: model.display
|
text: model.display
|
||||||
|
|||||||
@@ -42,15 +42,19 @@ DelegateContextMenu {
|
|||||||
* Each action will be instantiated as a single line in the menu.
|
* Each action will be instantiated as a single line in the menu.
|
||||||
*/
|
*/
|
||||||
property list<Kirigami.Action> actions: [
|
property list<Kirigami.Action> actions: [
|
||||||
|
DelegateContextMenu.ReplyMessageAction {},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Open Externally")
|
separator: true
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
text: i18nc("@action:inmenu", "Open Image")
|
||||||
icon.name: "document-open"
|
icon.name: "document-open"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
currentRoom.openEventMediaExternally(root.eventId);
|
currentRoom.openEventMediaExternally(root.eventId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Save As")
|
text: i18nc("@action:inmenu", "Save Image…")
|
||||||
icon.name: "document-save"
|
icon.name: "document-save"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
var dialog = saveAsDialog.createObject(QQC2.Overlay.overlay);
|
var dialog = saveAsDialog.createObject(QQC2.Overlay.overlay);
|
||||||
@@ -58,14 +62,16 @@ DelegateContextMenu {
|
|||||||
dialog.open();
|
dialog.open();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DelegateContextMenu.ReplyMessageAction {},
|
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Copy")
|
text: i18nc("@action:inmenu", "Copy Image")
|
||||||
icon.name: "edit-copy"
|
icon.name: "edit-copy"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
currentRoom.copyEventMedia(root.eventId);
|
currentRoom.copyEventMedia(root.eventId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
separator: true
|
||||||
|
},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
visible: author.id === currentRoom.localMember.id || currentRoom.canSendState("redact")
|
visible: author.id === currentRoom.localMember.id || currentRoom.canSendState("redact")
|
||||||
text: i18n("Remove")
|
text: i18n("Remove")
|
||||||
@@ -88,7 +94,13 @@ DelegateContextMenu {
|
|||||||
},
|
},
|
||||||
DelegateContextMenu.ReportMessageAction {},
|
DelegateContextMenu.ReportMessageAction {},
|
||||||
DelegateContextMenu.ShowUserAction {},
|
DelegateContextMenu.ShowUserAction {},
|
||||||
DelegateContextMenu.ViewSourceAction {}
|
Kirigami.Action {
|
||||||
|
separator: true
|
||||||
|
visible: viewSourceAction.visible
|
||||||
|
},
|
||||||
|
DelegateContextMenu.ViewSourceAction {
|
||||||
|
id: viewSourceAction
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let map = Qt.createComponent('org.kde.neochat', 'QrCodeMaximizeComponent').createObject(QQC2.Overlay.overlay, {
|
let map = Qt.createComponent('org.kde.neochat', 'QrCodeMaximizeComponent').createObject(parent, {
|
||||||
text: barcode.content,
|
text: barcode.content,
|
||||||
title: root.room ? root.room.displayName : "",
|
title: root.room ? root.room.displayName : "",
|
||||||
subtitle: root.room ? root.room.id : "",
|
subtitle: root.room ? root.room.id : "",
|
||||||
|
|||||||
@@ -318,13 +318,11 @@ Kirigami.ApplicationWindow {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
function showUserDetail(user, room) {
|
function showUserDetail(user, room) {
|
||||||
const dialog = Qt.createComponent("org.kde.neochat", "UserDetailDialog").createObject(root, {
|
Qt.createComponent("org.kde.neochat", "UserDetailDialog").createObject(root.QQC2.ApplicationWindow.window, {
|
||||||
room: room,
|
room: room,
|
||||||
user: user,
|
user: user,
|
||||||
connection: root.connection,
|
connection: root.connection
|
||||||
});
|
}).open();
|
||||||
dialog.parent = QmlUtils.focusedWindowItem(); // Kirigami Dialogs overwrite the parent, so we need to set it again
|
|
||||||
dialog.open();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function load() {
|
function load() {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ DelegateContextMenu {
|
|||||||
},
|
},
|
||||||
DelegateContextMenu.ReplyMessageAction {},
|
DelegateContextMenu.ReplyMessageAction {},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18nc("@action:inmenu As in 'Forward this message'", "Forward")
|
text: i18nc("@action:inmenu As in 'Forward this message'", "Forward…")
|
||||||
icon.name: "mail-forward-symbolic"
|
icon.name: "mail-forward-symbolic"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
let page = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), {
|
let page = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), {
|
||||||
@@ -59,21 +59,33 @@ DelegateContextMenu {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
separator: true
|
||||||
|
},
|
||||||
DelegateContextMenu.RemoveMessageAction {},
|
DelegateContextMenu.RemoveMessageAction {},
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Copy")
|
text: i18nc("@action:inmenu", "Copy Text")
|
||||||
icon.name: "edit-copy"
|
icon.name: "edit-copy"
|
||||||
onTriggered: Clipboard.saveText(root.selectedText.length > 0 ? root.selectedText : root.plainText)
|
onTriggered: Clipboard.saveText(root.selectedText.length > 0 ? root.selectedText : root.plainText)
|
||||||
},
|
},
|
||||||
DelegateContextMenu.ReportMessageAction {},
|
|
||||||
DelegateContextMenu.ShowUserAction {},
|
|
||||||
DelegateContextMenu.ViewSourceAction {},
|
|
||||||
Kirigami.Action {
|
Kirigami.Action {
|
||||||
text: i18n("Copy Link")
|
text: i18nc("@action:inmenu", "Copy Message Link")
|
||||||
icon.name: "edit-copy"
|
icon.name: "edit-copy"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
Clipboard.saveText("https://matrix.to/#/" + currentRoom.id + "/" + root.eventId);
|
Clipboard.saveText("https://matrix.to/#/" + currentRoom.id + "/" + root.eventId);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Kirigami.Action {
|
||||||
|
separator: true
|
||||||
|
},
|
||||||
|
DelegateContextMenu.ReportMessageAction {},
|
||||||
|
DelegateContextMenu.ShowUserAction {},
|
||||||
|
Kirigami.Action {
|
||||||
|
separator: true
|
||||||
|
visible: viewSourceAction.visible
|
||||||
|
},
|
||||||
|
DelegateContextMenu.ViewSourceAction {
|
||||||
|
id: viewSourceAction
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,20 +29,13 @@ Components.AlbumMaximizeComponent {
|
|||||||
|
|
||||||
readonly property var currentProgressInfo: model.data(model.index(content.currentIndex, 0), MessageEventModel.ProgressInfoRole)
|
readonly property var currentProgressInfo: model.data(model.index(content.currentIndex, 0), MessageEventModel.ProgressInfoRole)
|
||||||
|
|
||||||
onCurrentProgressInfoChanged: () => {
|
|
||||||
if (root.currentProgressInfo) {
|
|
||||||
root.downloadAction.progress = root.currentProgressInfo.progress / root.currentProgressInfo.total * 100.0;
|
|
||||||
} else {
|
|
||||||
root.downloadAction.progress = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Whether the delegate is part of a thread timeline.
|
* @brief Whether the delegate is part of a thread timeline.
|
||||||
*/
|
*/
|
||||||
property bool isThread: false
|
property bool isThread: false
|
||||||
|
|
||||||
downloadAction: Components.DownloadAction {
|
downloadAction: Components.DownloadAction {
|
||||||
|
id: downloadAction
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
currentRoom.downloadFile(root.currentEventId, Core.StandardPaths.writableLocation(Core.StandardPaths.CacheLocation) + "/" + root.currentEventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(root.currentEventId));
|
currentRoom.downloadFile(root.currentEventId, Core.StandardPaths.writableLocation(Core.StandardPaths.CacheLocation) + "/" + root.currentEventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(root.currentEventId));
|
||||||
}
|
}
|
||||||
@@ -69,11 +62,19 @@ Components.AlbumMaximizeComponent {
|
|||||||
|
|
||||||
function onFileTransferProgress(id, progress, total) {
|
function onFileTransferProgress(id, progress, total) {
|
||||||
if (id == root.currentEventId) {
|
if (id == root.currentEventId) {
|
||||||
root.downloadAction.progress = progress / total * 100.0;
|
downloadAction.progress = progress / total * 100.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: content
|
||||||
|
|
||||||
|
function onCurrentIndexChanged() {
|
||||||
|
downloadAction.progress = currentProgressInfo.progress / currentProgressInfo.total * 100.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
leading: RowLayout {
|
leading: RowLayout {
|
||||||
Components.Avatar {
|
Components.Avatar {
|
||||||
id: userAvatar
|
id: userAvatar
|
||||||
@@ -105,7 +106,7 @@ Components.AlbumMaximizeComponent {
|
|||||||
|
|
||||||
onOpened: forceActiveFocus()
|
onOpened: forceActiveFocus()
|
||||||
|
|
||||||
onItemRightClicked: RoomManager.viewEventMenu(root.currentEventId, root.currentRoom, root.currentAuthor)
|
onItemRightClicked: RoomManager.viewEventMenu(root.currentEventId, root.currentRoom)
|
||||||
|
|
||||||
onSaveItem: {
|
onSaveItem: {
|
||||||
var dialog = saveAsDialog.createObject(QQC2.Overlay.overlay);
|
var dialog = saveAsDialog.createObject(QQC2.Overlay.overlay);
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ Kirigami.SearchDialog {
|
|||||||
onTextChanged: RoomManager.sortFilterRoomListModel.filterText = text
|
onTextChanged: RoomManager.sortFilterRoomListModel.filterText = text
|
||||||
model: RoomManager.sortFilterRoomListModel
|
model: RoomManager.sortFilterRoomListModel
|
||||||
emptyText: i18nc("Placeholder message", "No room found")
|
emptyText: i18nc("Placeholder message", "No room found")
|
||||||
parent: QQC2.Overlay.overlay
|
|
||||||
|
|
||||||
delegate: RoomDelegate {
|
delegate: RoomDelegate {
|
||||||
connection: root.connection
|
connection: root.connection
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ QQC2.ScrollView {
|
|||||||
/**
|
/**
|
||||||
* @brief The title that should be displayed for this component if available.
|
* @brief The title that should be displayed for this component if available.
|
||||||
*/
|
*/
|
||||||
readonly property string title: root.room.isSpace ? i18nc("@action:title", "Space Members") : i18nc("@action:title", "Room information")
|
readonly property string title: root.room.isSpace ? i18nc("@action:title", "Space Members") : i18nc("@action:title", "Room Information")
|
||||||
|
|
||||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||||
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||||
@@ -133,7 +133,7 @@ QQC2.ScrollView {
|
|||||||
Delegates.RoundedItemDelegate {
|
Delegates.RoundedItemDelegate {
|
||||||
id: leaveButton
|
id: leaveButton
|
||||||
icon.name: "arrow-left-symbolic"
|
icon.name: "arrow-left-symbolic"
|
||||||
text: i18nc("@action:button", "Leave this room")
|
text: root.room.isSpace ? i18nc("@action:button", "Leave this space") : i18nc("@action:button", "Leave this room")
|
||||||
activeFocusOnTab: true
|
activeFocusOnTab: true
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ Kirigami.Page {
|
|||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: StandardKey.Cancel
|
sequence: StandardKey.Cancel
|
||||||
onActivated: {
|
onActivated: {
|
||||||
if (!timelineViewLoader.item.atYEnd || !root.currentRoom.partiallyReadStats.empty()) {
|
if (!timelineViewLoader.item.atYEnd || root.currentRoom.hasUnreadMessages) {
|
||||||
timelineViewLoader.item.goToLastMessage();
|
timelineViewLoader.item.goToLastMessage();
|
||||||
root.currentRoom.markAllMessagesAsRead();
|
root.currentRoom.markAllMessagesAsRead();
|
||||||
} else {
|
} else {
|
||||||
@@ -308,7 +308,6 @@ Kirigami.Page {
|
|||||||
NeochatMaximizeComponent {
|
NeochatMaximizeComponent {
|
||||||
currentRoom: root.currentRoom
|
currentRoom: root.currentRoom
|
||||||
model: root.mediaMessageFilterModel
|
model: root.mediaMessageFilterModel
|
||||||
parent: root.QQC2.Overlay.overlay
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ QQC2.Control {
|
|||||||
|
|
||||||
text: i18nc("@button View all one-on-one chats with your friends.", "Friends")
|
text: i18nc("@button View all one-on-one chats with your friends.", "Friends")
|
||||||
contentItem: Kirigami.Icon {
|
contentItem: Kirigami.Icon {
|
||||||
source: "system-users-symbolic"
|
source: "system-users"
|
||||||
|
|
||||||
QQC2.Label {
|
QQC2.Label {
|
||||||
id: directChatNotificationCountLabel
|
id: directChatNotificationCountLabel
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ ColumnLayout {
|
|||||||
onClicked: _private.createRoom(root.currentRoom.id)
|
onClicked: _private.createRoom(root.currentRoom.id)
|
||||||
}
|
}
|
||||||
QQC2.Button {
|
QQC2.Button {
|
||||||
text: i18nc("@button", "Leave the space")
|
text: i18nc("@action:button", "Leave this space")
|
||||||
icon.name: "go-previous"
|
icon.name: "go-previous"
|
||||||
onClicked: RoomManager.leaveRoom(root.currentRoom)
|
onClicked: RoomManager.leaveRoom(root.currentRoom)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ QQC2.ScrollView {
|
|||||||
padding: Kirigami.Units.largeSpacing
|
padding: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
z: 2
|
z: 2
|
||||||
visible: (!root.currentRoom?.partiallyReadStats.empty())
|
visible: (root.currentRoom?.hasUnreadMessages ?? false)
|
||||||
|
|
||||||
text: root.currentRoom.readMarkerLoaded ? i18n("Jump to first unread message") : i18n("Jump to oldest loaded message")
|
text: root.currentRoom.readMarkerLoaded ? i18n("Jump to first unread message") : i18n("Jump to oldest loaded message")
|
||||||
action: Kirigami.Action {
|
action: Kirigami.Action {
|
||||||
@@ -195,7 +195,7 @@ QQC2.ScrollView {
|
|||||||
root.focusChatBar();
|
root.focusChatBar();
|
||||||
}
|
}
|
||||||
goReadMarkerFab.textChanged()
|
goReadMarkerFab.textChanged()
|
||||||
messageListView.goToEvent(root.currentRoom.lastFullyReadEventId);
|
messageListView.goToEvent(root.currentRoom.readMarkerEventId);
|
||||||
}
|
}
|
||||||
icon.name: "go-up"
|
icon.name: "go-up"
|
||||||
shortcut: "Shift+PgUp"
|
shortcut: "Shift+PgUp"
|
||||||
@@ -354,7 +354,7 @@ QQC2.ScrollView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function allUnreadVisible() {
|
function allUnreadVisible() {
|
||||||
let readMarkerRow = eventToIndex(root.currentRoom.lastFullyReadEventId);
|
let readMarkerRow = eventToIndex(root.currentRoom.readMarkerEventId);
|
||||||
if (readMarkerRow >= 0 && readMarkerRow < firstVisibleIndex() && messageListView.atYEnd) {
|
if (readMarkerRow >= 0 && readMarkerRow < firstVisibleIndex() && messageListView.atYEnd) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ Kirigami.Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let map = Qt.createComponent('org.kde.neochat', 'QrCodeMaximizeComponent').createObject(QQC2.Overlay.overlay, {
|
let map = qrMaximizeComponent.createObject(parent, {
|
||||||
text: barcode.content,
|
text: barcode.content,
|
||||||
title: root.room ? root.room.member(root.user.id).displayName : root.user.displayName,
|
title: root.room ? root.room.member(root.user.id).displayName : root.user.displayName,
|
||||||
subtitle: root.user.id,
|
subtitle: root.user.id,
|
||||||
@@ -265,4 +265,8 @@ Kirigami.Dialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Component {
|
||||||
|
id: qrMaximizeComponent
|
||||||
|
QrCodeMaximizeComponent {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ RowLayout {
|
|||||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||||
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||||
Layout.minimumHeight: bottomEdge ? Kirigami.Units.gridUnit * 3 : -1
|
Layout.minimumHeight: bottomEdge ? Kirigami.Units.gridUnit * 2 : -1
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
@@ -91,7 +91,6 @@ RowLayout {
|
|||||||
id: switchUserButton
|
id: switchUserButton
|
||||||
text: i18n("Switch User")
|
text: i18n("Switch User")
|
||||||
icon.name: "system-switch-user"
|
icon.name: "system-switch-user"
|
||||||
shortcut: "Ctrl+U"
|
|
||||||
onTriggered: accountSwitchDialog.createObject(QQC2.Overlay.overlay, {
|
onTriggered: accountSwitchDialog.createObject(QQC2.Overlay.overlay, {
|
||||||
connection: root.connection
|
connection: root.connection
|
||||||
}).open();
|
}).open();
|
||||||
@@ -106,6 +105,11 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
Shortcut {
|
||||||
|
sequence: "Ctrl+U"
|
||||||
|
onActivated: switchUserButton.toggle()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// QQC2.ToolButton {
|
// QQC2.ToolButton {
|
||||||
// Layout.alignment: Qt.AlignRight
|
// Layout.alignment: Qt.AlignRight
|
||||||
|
|||||||
@@ -93,7 +93,10 @@ void Registration::registerAccount()
|
|||||||
auto matrixId = "@%1:%2"_ls.arg(m_username, m_homeserver);
|
auto matrixId = "@%1:%2"_ls.arg(m_username, m_homeserver);
|
||||||
connection->resolveServer(matrixId);
|
connection->resolveServer(matrixId);
|
||||||
|
|
||||||
auto displayName = "NeoChat"_ls;
|
auto displayName = "NeoChat %1 %2 %3 %4"_ls.arg(QSysInfo::machineHostName(),
|
||||||
|
QSysInfo::productType(),
|
||||||
|
QSysInfo::productVersion(),
|
||||||
|
QSysInfo::currentCpuArchitecture());
|
||||||
connection->loginWithPassword(matrixId, m_password, displayName);
|
connection->loginWithPassword(matrixId, m_password, displayName);
|
||||||
|
|
||||||
connect(connection, &Connection::connected, this, [this, displayName, connection] {
|
connect(connection, &Connection::connected, this, [this, displayName, connection] {
|
||||||
|
|||||||
@@ -323,15 +323,7 @@ void RoomManager::visitRoom(Room *r, const QString &eventId)
|
|||||||
|
|
||||||
void RoomManager::joinRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QStringList &viaServers)
|
void RoomManager::joinRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QStringList &viaServers)
|
||||||
{
|
{
|
||||||
QStringList vias = viaServers;
|
auto job = account->joinRoom(roomAliasOrId, viaServers);
|
||||||
|
|
||||||
// If no one gives us a homeserver suggestion, try the server specified in the alias/id.
|
|
||||||
// Otherwise joining a remote room not on our homeserver will fail.
|
|
||||||
if (vias.empty()) {
|
|
||||||
vias.append(roomAliasOrId.mid(roomAliasOrId.lastIndexOf(':'_L1) + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto job = account->joinRoom(roomAliasOrId, vias);
|
|
||||||
connect(
|
connect(
|
||||||
job.get(),
|
job.get(),
|
||||||
&Quotient::BaseJob::finished,
|
&Quotient::BaseJob::finished,
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ FormCard.FormCardPage {
|
|||||||
id: addAccountDelegate
|
id: addAccountDelegate
|
||||||
text: i18n("Add Account")
|
text: i18n("Add Account")
|
||||||
icon.name: "list-add"
|
icon.name: "list-add"
|
||||||
onClicked: root.QQC2.ApplicationWindow.window.pageStack.layers.push(Qt.createComponent('org.kde.neochat.login', 'WelcomePage'), { showSettings: false })
|
onClicked: root.QQC2.ApplicationWindow.window.pageStack.layers.push(Qt.createComponent('org.kde.neochat.login', 'WelcomePage'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -142,6 +142,15 @@ TextHandler::handleRecieveRichText(Qt::TextFormat inputFormat, const NeoChatRoom
|
|||||||
// If the image does not have an explicit width, but has a vertical-align it's most likely an emoticon.
|
// If the image does not have an explicit width, but has a vertical-align it's most likely an emoticon.
|
||||||
// We must do some pre-processing for it to show up nicely in and around text.
|
// We must do some pre-processing for it to show up nicely in and around text.
|
||||||
if (isEmoticon) {
|
if (isEmoticon) {
|
||||||
|
// Remove any pre-existing height
|
||||||
|
extraAttributes.removeIf([](const QString &s) {
|
||||||
|
return s.contains(QStringLiteral("height="));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make sure it's the same height as the rest of the text
|
||||||
|
const QFontMetrics metrics(QGuiApplication::font());
|
||||||
|
extraAttributes.append(QStringLiteral("height=\"%1\"").arg(metrics.height()));
|
||||||
|
|
||||||
// Align it properly
|
// Align it properly
|
||||||
extraAttributes.append(QStringLiteral("style=\"%1\"").arg(customEmojiStyle));
|
extraAttributes.append(QStringLiteral("style=\"%1\"").arg(customEmojiStyle));
|
||||||
}
|
}
|
||||||
@@ -496,7 +505,6 @@ QString TextHandler::cleanAttributes(const QString &tag, const QString &tagStrin
|
|||||||
nextAttribute = tagString.mid(nextAttributeIndex, nextSpaceIndex - nextAttributeIndex);
|
nextAttribute = tagString.mid(nextAttributeIndex, nextSpaceIndex - nextAttributeIndex);
|
||||||
|
|
||||||
if (isAllowedAttribute(tag, getAttributeType(nextAttribute))) {
|
if (isAllowedAttribute(tag, getAttributeType(nextAttribute))) {
|
||||||
QString style;
|
|
||||||
if (tag == QStringLiteral("img") && getAttributeType(nextAttribute) == QStringLiteral("src")) {
|
if (tag == QStringLiteral("img") && getAttributeType(nextAttribute) == QStringLiteral("src")) {
|
||||||
QString attributeData = TextRegex::attributeData.match(getAttributeData(nextAttribute)).captured(1);
|
QString attributeData = TextRegex::attributeData.match(getAttributeData(nextAttribute)).captured(1);
|
||||||
if (isAllowedLink(attributeData, true)) {
|
if (isAllowedLink(attributeData, true)) {
|
||||||
@@ -517,19 +525,9 @@ QString TextHandler::cleanAttributes(const QString &tag, const QString &tagStrin
|
|||||||
if (attributeData == customEmojiStyle) {
|
if (attributeData == customEmojiStyle) {
|
||||||
outputString.append(u' ' + nextAttribute);
|
outputString.append(u' ' + nextAttribute);
|
||||||
}
|
}
|
||||||
} else if (getAttributeType(nextAttribute) == QStringLiteral("data-mx-color")) {
|
|
||||||
const QString attributeData = TextRegex::attributeData.match(getAttributeData(nextAttribute)).captured(1);
|
|
||||||
style.append(u"color: " + attributeData + u';');
|
|
||||||
} else if (getAttributeType(nextAttribute) == QStringLiteral("data-mx-bg-color")) {
|
|
||||||
const QString attributeData = TextRegex::attributeData.match(getAttributeData(nextAttribute)).captured(1);
|
|
||||||
style.append(u"background-color: " + attributeData + u';');
|
|
||||||
} else {
|
} else {
|
||||||
outputString.append(u' ' + nextAttribute);
|
outputString.append(u' ' + nextAttribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!style.isEmpty()) {
|
|
||||||
outputString.append(u" style=\"" + style + u'"');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
nextAttributeIndex = nextSpaceIndex + 1;
|
nextAttributeIndex = nextSpaceIndex + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
#include <Quotient/connection.h>
|
#include <Quotient/connection.h>
|
||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QQuickWindow>
|
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
@@ -38,16 +37,6 @@ QColor QmlUtils::getUserColor(qreal hueF)
|
|||||||
return QColor::fromHslF(hueF, 1, -0.7 * lightness + 0.9, 1);
|
return QColor::fromHslF(hueF, 1, -0.7 * lightness + 0.9, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
QQuickItem *QmlUtils::focusedWindowItem()
|
|
||||||
{
|
|
||||||
const auto window = qobject_cast<QQuickWindow *>(QGuiApplication::focusWindow());
|
|
||||||
if (window) {
|
|
||||||
return window->contentItem();
|
|
||||||
} else {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Utils::isEmoji(const QString &text)
|
bool Utils::isEmoji(const QString &text)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_ICU
|
#ifdef HAVE_ICU
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
#include <QQuickItem>
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
#include <Quotient/user.h>
|
#include <Quotient/user.h>
|
||||||
@@ -35,7 +34,6 @@ public:
|
|||||||
Q_INVOKABLE bool isValidJson(const QByteArray &json);
|
Q_INVOKABLE bool isValidJson(const QByteArray &json);
|
||||||
Q_INVOKABLE QString escapeString(const QString &string);
|
Q_INVOKABLE QString escapeString(const QString &string);
|
||||||
Q_INVOKABLE QColor getUserColor(qreal hueF);
|
Q_INVOKABLE QColor getUserColor(qreal hueF);
|
||||||
Q_INVOKABLE QQuickItem *focusedWindowItem();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QmlUtils() = default;
|
QmlUtils() = default;
|
||||||
|
|||||||
Reference in New Issue
Block a user