Compare commits

..

1 Commits

Author SHA1 Message Date
Joshua Goins
ea18152fad Visually show reactions while the server is processing them
Unlike messages, reactions do not have a "pending" state. This problem
is more obvious while the server is under heavy load, or your connection
is disconnecting often, etc. This creates a pretty terrible UX as you
try to add an emoji, and nothing obvious happens.

libQuotient gives us the tools to do this, we can take advantage of
this. The main missing component is that we depend on a changed
RoomMessage event for reaction updates, but in the pending queue the
library gives us a ReactionEvent directly. We can process this, and tell
ReactionModel to use this while waiting for the message to be updated.

I did some code cleanup in ReactionModel while I'm touching it as well.
2025-09-07 10:47:34 +02:00
190 changed files with 31275 additions and 58103 deletions

View File

@@ -206,6 +206,14 @@
{ {
"type": "dir", "type": "dir",
"path": "." "path": "."
},
{
"type": "patch",
"path": "patches/0001-Revert-Bump-KF6-dependency-version.patch"
},
{
"type": "patch",
"path": "patches/0001-Revert-Use-new-Kirigami-builtin-column-resize-handle.patch"
} }
] ]
} }

View File

@@ -10,11 +10,11 @@ Dependencies:
'frameworks/ki18n': '@latest-kf6' 'frameworks/ki18n': '@latest-kf6'
'frameworks/kconfig': '@latest-kf6' 'frameworks/kconfig': '@latest-kf6'
'frameworks/syntax-highlighting': '@latest-kf6' 'frameworks/syntax-highlighting': '@latest-kf6'
'frameworks/kiconthemes': '@latest-kf6'
'frameworks/kitemmodels': '@latest-kf6' 'frameworks/kitemmodels': '@latest-kf6'
'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'
@@ -29,6 +29,7 @@ Dependencies:
'frameworks/kio': '@latest-kf6' 'frameworks/kio': '@latest-kf6'
'frameworks/kwindowsystem': '@latest-kf6' 'frameworks/kwindowsystem': '@latest-kf6'
'frameworks/kstatusnotifieritem': '@latest-kf6' 'frameworks/kstatusnotifieritem': '@latest-kf6'
'frameworks/kcrash': '@latest-kf6'
- 'on': ['Linux', 'FreeBSD'] - 'on': ['Linux', 'FreeBSD']
'require': 'require':
'frameworks/kdbusaddons': '@latest-kf6' 'frameworks/kdbusaddons': '@latest-kf6'

View File

@@ -8,14 +8,14 @@ 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 "25") set(RELEASE_SERVICE_VERSION_MAJOR "25")
set(RELEASE_SERVICE_VERSION_MINOR "12") set(RELEASE_SERVICE_VERSION_MINOR "11")
set(RELEASE_SERVICE_VERSION_MICRO "1") 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})
set(KF_MIN_VERSION "6.17") set(KF_MIN_VERSION "6.17")
set(QT_MIN_VERSION "6.9") set(QT_MIN_VERSION "6.8")
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE) find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
@@ -24,7 +24,7 @@ set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(KDE_COMPILERSETTINGS_LEVEL 6.17) set(KDE_COMPILERSETTINGS_LEVEL 6.0)
include(FeatureSummary) include(FeatureSummary)
include(ECMSetupVersion) include(ECMSetupVersion)
@@ -39,7 +39,6 @@ include(ECMCheckOutboundLicense)
include(ECMQtDeclareLoggingCategory) include(ECMQtDeclareLoggingCategory)
include(ECMAddAndroidApk) include(ECMAddAndroidApk)
include(ECMQmlModule) include(ECMQmlModule)
include(ECMDeprecationSettings)
include(GenerateExportHeader) include(GenerateExportHeader)
include(ECMGenerateHeaders) include(ECMGenerateHeaders)
if (NOT ANDROID) if (NOT ANDROID)
@@ -52,8 +51,6 @@ endif()
set(QUOTIENT_FORCE_NAMESPACED_INCLUDES TRUE) set(QUOTIENT_FORCE_NAMESPACED_INCLUDES TRUE)
ecm_set_disabled_deprecation_versions(Qt 6.9.0 KF 6.17.0)
ecm_setup_version(${PROJECT_VERSION} ecm_setup_version(${PROJECT_VERSION}
VARIABLE_PREFIX NEOCHAT VARIABLE_PREFIX NEOCHAT
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
@@ -69,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 ColorScheme IconThemes) find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels IconThemes ColorScheme)
set_package_properties(KF6 PROPERTIES set_package_properties(KF6 PROPERTIES
TYPE REQUIRED TYPE REQUIRED
PURPOSE "Basic application components" PURPOSE "Basic application components"
@@ -78,7 +75,7 @@ set_package_properties(KF6Kirigami PROPERTIES
TYPE REQUIRED TYPE REQUIRED
PURPOSE "Kirigami application UI framework" PURPOSE "Kirigami application UI framework"
) )
find_package(KF6KirigamiAddons 1.10.0 REQUIRED) find_package(KF6KirigamiAddons 1.6.0 REQUIRED)
if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT NEOCHAT_FLATPAK AND NOT NEOCHAT_APPIMAGE) if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT NEOCHAT_FLATPAK AND NOT NEOCHAT_APPIMAGE)
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Purpose) find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Purpose)
@@ -92,7 +89,7 @@ if(ANDROID)
) )
else() else()
find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Widgets) find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Widgets)
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem) find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem Crash)
find_package(KF6SyntaxHighlighting ${KF_MIN_VERSION} REQUIRED) find_package(KF6SyntaxHighlighting ${KF_MIN_VERSION} REQUIRED)
set_package_properties(KF6QQC2DesktopStyle PROPERTIES set_package_properties(KF6QQC2DesktopStyle PROPERTIES
TYPE RUNTIME TYPE RUNTIME
@@ -110,7 +107,7 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED) find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED)
endif() endif()
find_package(QuotientQt6 0.9.3) find_package(QuotientQt6 0.9.1)
set_package_properties(QuotientQt6 PROPERTIES set_package_properties(QuotientQt6 PROPERTIES
TYPE REQUIRED TYPE REQUIRED
DESCRIPTION "Qt wrapper around Matrix API" DESCRIPTION "Qt wrapper around Matrix API"

View File

@@ -63,7 +63,7 @@ void ActionsTest::testActions_data()
QTest::addColumn<std::optional<QString>>("resultText"); QTest::addColumn<std::optional<QString>>("resultText");
QTest::addColumn<std::optional<Quotient::RoomMessageEvent::MsgType>>("type"); QTest::addColumn<std::optional<Quotient::RoomMessageEvent::MsgType>>("type");
QTest::newRow("shrug") << u"/shrug Hello"_s << std::make_optional(u"¯\\\\\\_(ツ)\\_/¯ Hello"_s) QTest::newRow("shrug") << u"/shrug Hello"_s << std::make_optional(u"¯\\\\_(ツ)_/¯ Hello"_s)
<< std::make_optional(Quotient::RoomMessageEvent::MsgType::Text); << std::make_optional(Quotient::RoomMessageEvent::MsgType::Text);
QTest::newRow("lenny") << u"/lenny Hello"_s << std::make_optional(u"( ͡° ͜ʖ ͡°) Hello"_s) << std::make_optional(Quotient::RoomMessageEvent::MsgType::Text); QTest::newRow("lenny") << u"/lenny Hello"_s << std::make_optional(u"( ͡° ͜ʖ ͡°) Hello"_s) << std::make_optional(Quotient::RoomMessageEvent::MsgType::Text);
QTest::newRow("tableflip") << u"/tableflip Hello"_s << std::make_optional(u"(╯°□°)╯︵ ┻━┻ Hello"_s) QTest::newRow("tableflip") << u"/tableflip Hello"_s << std::make_optional(u"(╯°□°)╯︵ ┻━┻ Hello"_s)

View File

@@ -28,7 +28,6 @@ private:
private Q_SLOTS: private Q_SLOTS:
void initTestCase(); void initTestCase();
void testMaximizeMedia(); void testMaximizeMedia();
void testResolveMatrixLinks();
}; };
void RoomManagerTest::initTestCase() void RoomManagerTest::initTestCase()
@@ -129,13 +128,5 @@ void RoomManagerTest::testMaximizeMedia()
QVERIFY(spy[1][0] == 0); QVERIFY(spy[1][0] == 0);
} }
void RoomManagerTest::testResolveMatrixLinks()
{
// Test if resolving a non-joined room will bring up the confirmation dialog.
const QSignalSpy askToJoinSpy(&RoomManager::instance(), &RoomManager::askJoinRoom);
RoomManager::instance().resolveResource(QStringLiteral("matrix:r/testbuild:matrix.org"), QStringLiteral("join"));
QTRY_COMPARE(askToJoinSpy.size(), 1);
}
QTEST_MAIN(RoomManagerTest) QTEST_MAIN(RoomManagerTest)
#include "roommanagertest.moc" #include "roommanagertest.moc"

View File

@@ -4,12 +4,15 @@
#include "server.h" #include "server.h"
#include <QFile> #include <QFile>
#include <QHttpServer>
#include <QHttpServerResponder> #include <QHttpServerResponder>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply> #include <QNetworkReply>
#include <QSslCertificate> #include <QSslCertificate>
#include <QSslKey> #include <QSslKey>
#include <QSslServer>
#include <QUuid> #include <QUuid>
#include <Quotient/networkaccessmanager.h> #include <Quotient/networkaccessmanager.h>

View File

@@ -41,7 +41,6 @@
<name xml:lang="pl">NeoChat</name> <name xml:lang="pl">NeoChat</name>
<name xml:lang="pt">NeoChat</name> <name xml:lang="pt">NeoChat</name>
<name xml:lang="pt-BR">NeoChat</name> <name xml:lang="pt-BR">NeoChat</name>
<name xml:lang="ro">NeoChat</name>
<name xml:lang="ru">NeoChat</name> <name xml:lang="ru">NeoChat</name>
<name xml:lang="sa">नवचैट्</name> <name xml:lang="sa">नवचैट्</name>
<name xml:lang="sk">NeoChat</name> <name xml:lang="sk">NeoChat</name>
@@ -50,6 +49,7 @@
<name xml:lang="ta">நியோச்சாட்</name> <name xml:lang="ta">நியோச்சாட்</name>
<name xml:lang="tr">NeoChat</name> <name xml:lang="tr">NeoChat</name>
<name xml:lang="uk">NeoChat</name> <name xml:lang="uk">NeoChat</name>
<name xml:lang="x-test">xxNeoChatxx</name>
<name xml:lang="zh-CN">NeoChat</name> <name xml:lang="zh-CN">NeoChat</name>
<name xml:lang="zh-TW">NeoChat</name> <name xml:lang="zh-TW">NeoChat</name>
<summary>Chat on Matrix</summary> <summary>Chat on Matrix</summary>
@@ -76,7 +76,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="pt-BR">Bate-papo na Matrix</summary> <summary xml:lang="pt-BR">Bate-papo na Matrix</summary>
<summary xml:lang="ro">Discutați pe Matrix</summary>
<summary xml:lang="ru">Общение в Matrix</summary> <summary xml:lang="ru">Общение в Matrix</summary>
<summary xml:lang="sa">Matrix इत्यत्र गपशपं कुर्वन्तु</summary> <summary xml:lang="sa">Matrix इत्यत्र गपशपं कुर्वन्तु</summary>
<summary xml:lang="sl">Klepet na Matrixu</summary> <summary xml:lang="sl">Klepet na Matrixu</summary>
@@ -84,6 +83,7 @@
<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>
<summary xml:lang="x-test">xxChat on Matrixxx</summary>
<summary xml:lang="zh-TW">在 Matrix 上聊天</summary> <summary xml:lang="zh-TW">在 Matrix 上聊天</summary>
<description> <description>
<p>NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p> <p>NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
@@ -111,13 +111,13 @@
<p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p> <p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p>
<p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p> <p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p>
<p xml:lang="pt-BR">O NeoChat é um aplicativo de bate-papo que permite que você aproveite ao máximo a rede Matrix. Ele oferece uma maneira segura de enviar mensagens de texto, vídeos e arquivos de áudio para sua família, colegas e amigos.</p> <p xml:lang="pt-BR">O NeoChat é um aplicativo de bate-papo que permite que você aproveite ao máximo a rede Matrix. Ele oferece uma maneira segura de enviar mensagens de texto, vídeos e arquivos de áudio para sua família, colegas e amigos.</p>
<p xml:lang="ro">NeoChat e o aplicație de discuții ce vă ajută să profitați din plin de rețeaua Matrix. Aceasta oferă o modalitate sigură de a trimite mesaje textuale, videoclipuri și fișiere audio familiei, colegilor și prietenilor.</p>
<p xml:lang="ru">NeoChat — приложение для общения, предоставляющее все преимущества сети Matrix. С его помощью можно безопасно отправлять текстовые сообщения, видеозаписи и звуковые файлы родственникам, коллегам и друзьям.</p> <p xml:lang="ru">NeoChat — приложение для общения, предоставляющее все преимущества сети Matrix. С его помощью можно безопасно отправлять текстовые сообщения, видеозаписи и звуковые файлы родственникам, коллегам и друзьям.</p>
<p xml:lang="sa">NeoChat इति एकं गपशप-अनुप्रयोगं यत् भवान् Matrix-जालस्य पूर्णं लाभं ग्रहीतुं शक्नोति । एतत् भवन्तं भवतः परिवाराय, सहकारिभ्यः, मित्रेभ्यः च पाठसन्देशान्, भिडियो, श्रव्यसञ्चिकाः च प्रेषयितुं सुरक्षितं मार्गं प्रदाति ।</p> <p xml:lang="sa">NeoChat इति एकं गपशप-अनुप्रयोगं यत् भवान् Matrix-जालस्य पूर्णं लाभं ग्रहीतुं शक्नोति । एतत् भवन्तं भवतः परिवाराय, सहकारिभ्यः, मित्रेभ्यः च पाठसन्देशान्, भिडियो, श्रव्यसञ्चिकाः च प्रेषयितुं सुरक्षितं मार्गं प्रदाति ।</p>
<p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p> <p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p>
<p xml:lang="sv">NeoChat är ett chattprogram som låter dig dra full nytta av Matrix-nätverket. Det ger dig ett säkert sätt att skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner.</p> <p xml:lang="sv">NeoChat är ett chattprogram som låter dig dra full nytta av Matrix-nätverket. Det ger dig ett säkert sätt att skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner.</p>
<p xml:lang="tr">NeoChat, Matrix ağının tüm özelliklerini kullanan bir sohbet uygulamasıdır. Ailenize, arkadaşlarınıza ve iş arkadaşlarınıza metin iletileri, ses ve video dosyaları göndermenin kolay bir yolunu sunar.</p> <p xml:lang="tr">NeoChat, Matrix ağının tüm özelliklerini kullanan bir sohbet uygulamasıdır. Ailenize, arkadaşlarınıza ve iş arkadaşlarınıza metin iletileri, ses ve video dosyaları göndermenin kolay bir yolunu sunar.</p>
<p xml:lang="uk">NeoChat є програмою для спілкування, за допомогою якої ви можете скористатися усіма перевагами мережі Matrix. За її допомогою ви можете безпечно надсилати текстові повідомлення, відео та звукові файли вашим родичам, колегам та друзям.</p> <p xml:lang="uk">NeoChat є програмою для спілкування, за допомогою якої ви можете скористатися усіма перевагами мережі Matrix. За її допомогою ви можете безпечно надсилати текстові повідомлення, відео та звукові файли вашим родичам, колегам та друзям.</p>
<p xml:lang="x-test">xxNeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.xx</p>
<p xml:lang="zh-TW">NeoChat 是一個讓您能夠完全利用 Matrix 網路的聊天應用程式。它讓您安全地傳送文字訊息、影片或音訊檔給家人、同事或朋友等等。</p> <p xml:lang="zh-TW">NeoChat 是一個讓您能夠完全利用 Matrix 網路的聊天應用程式。它讓您安全地傳送文字訊息、影片或音訊檔給家人、同事或朋友等等。</p>
<p>NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p> <p>NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. يوفر نيوتشات كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP و تعدد الخيوط وبعض جوانب التعمية من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار، ولكن يبقى الهدف توفير تطبيق للمواصفات بأكملها.</p> <p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. يوفر نيوتشات كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP و تعدد الخيوط وبعض جوانب التعمية من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار، ولكن يبقى الهدف توفير تطبيق للمواصفات بأكملها.</p>
@@ -145,13 +145,13 @@
<p xml:lang="pl">NeoChat w zamyśle ma być pełnowartościową aplikacją wg wytycznych Matriksa. Z tego powodu, wszystko, co jest obecnie w stabilnych wytycznych z pominięciem VoIP, wątków i niektórych części szyfrowania Użytkownik-do-Użytkownika są obecnie obsługiwane. Pominięto też kilka mniejszych rzeczy ze względu na ciągły rozwój wytycznych Matriksa, lecz celem nadal jest zapewnienie obsługi wszystkich wytycznych.</p> <p xml:lang="pl">NeoChat w zamyśle ma być pełnowartościową aplikacją wg wytycznych Matriksa. Z tego powodu, wszystko, co jest obecnie w stabilnych wytycznych z pominięciem VoIP, wątków i niektórych części szyfrowania Użytkownik-do-Użytkownika są obecnie obsługiwane. Pominięto też kilka mniejszych rzeczy ze względu na ciągły rozwój wytycznych Matriksa, lecz celem nadal jest zapewnienie obsługi wszystkich wytycznych.</p>
<p xml:lang="pt">O NeoChat pretende ser uma aplicação completa para a especificação do Matrix. Como tal, tudo o que existe na especificação estável actual, com as notáveis excepções do VoIP, tópicos e alguns aspectos da Encriptação Ponto-a-Ponto, são suportados. Existem mais algumas omissões, devido ao facto que a norma do Matrix está em constante evolução, mas o objectivo continua a ser oferecer o suporte eventual para a norma por inteiro.</p> <p xml:lang="pt">O NeoChat pretende ser uma aplicação completa para a especificação do Matrix. Como tal, tudo o que existe na especificação estável actual, com as notáveis excepções do VoIP, tópicos e alguns aspectos da Encriptação Ponto-a-Ponto, são suportados. Existem mais algumas omissões, devido ao facto que a norma do Matrix está em constante evolução, mas o objectivo continua a ser oferecer o suporte eventual para a norma por inteiro.</p>
<p xml:lang="pt-BR">O NeoChat pretende ser um aplicativo completo para a especificação Matrix. Dessa forma, tudo na especificação estável atual, com as notáveis exceções de VoIP, tópicos e alguns aspectos da criptografia de ponta a ponta, é suportado. Há algumas outras pequenas omissões devido ao fato de a especificação Matrix estar em constante evolução, mas o objetivo continua sendo fornecer suporte eventual para toda a especificação.</p> <p xml:lang="pt-BR">O NeoChat pretende ser um aplicativo completo para a especificação Matrix. Dessa forma, tudo na especificação estável atual, com as notáveis exceções de VoIP, tópicos e alguns aspectos da criptografia de ponta a ponta, é suportado. Há algumas outras pequenas omissões devido ao fato de a especificação Matrix estar em constante evolução, mas o objetivo continua sendo fornecer suporte eventual para toda a especificação.</p>
<p xml:lang="ro">NeoChat vrea să fie o aplicație completă pentru specificațiile Matrix. Astfel, susține tot ce se găsește acum în specificațiile stabile cu excepția VoIP, a firelor de discuții, și a unor părți din criptarea punct-la-punct. Sunt și câteva omisiuni minore din cauza faptului că specificația Matrix evoluează continuu, dar scopul rămâne acela de a implementa întreaga specificație.</p>
<p xml:lang="ru">Целью создания NeoChat является полноценная реализация программы для спецификации Matrix. Как следствие, реализовано всё в текущей стабильной спецификации (за исключением голосовой интернет-связи, потоков и некоторых аспектов сквозного шифрования). Есть также несколько других незначительных пробелов, обусловленных постоянными изменениями спецификации Matrix. Тем не менее, стоит задача в итоге предоставить полную поддержку спецификации.</p> <p xml:lang="ru">Целью создания NeoChat является полноценная реализация программы для спецификации Matrix. Как следствие, реализовано всё в текущей стабильной спецификации (за исключением голосовой интернет-связи, потоков и некоторых аспектов сквозного шифрования). Есть также несколько других незначительных пробелов, обусловленных постоянными изменениями спецификации Matrix. Тем не менее, стоит задача в итоге предоставить полную поддержку спецификации.</p>
<p xml:lang="sa">NeoChat इत्यस्य उद्देश्यं Matrix विनिर्देशस्य कृते पूर्णतया विशेषतायुक्तः अनुप्रयोगः भवितुम् अस्ति । यथा तथा वर्तमानस्थिरविनिर्देशे सर्वं VoIP इत्यस्य उल्लेखनीयअपवादैः सह, थ्रेड्स तथा च End-to-End Encryption इत्यस्य केचन पक्षाः समर्थिताः सन्ति । अन्ये कतिचन लघु लोपाः सन्ति यतोहि Matrix spec निरन्तरं विकसितः अस्ति परन्तु उद्देश्यं सम्पूर्ण spec कृते अन्ततः समर्थनं प्रदातुं अवशिष्टम् अस्ति</p> <p xml:lang="sa">NeoChat इत्यस्य उद्देश्यं Matrix विनिर्देशस्य कृते पूर्णतया विशेषतायुक्तः अनुप्रयोगः भवितुम् अस्ति । यथा तथा वर्तमानस्थिरविनिर्देशे सर्वं VoIP इत्यस्य उल्लेखनीयअपवादैः सह, थ्रेड्स तथा च End-to-End Encryption इत्यस्य केचन पक्षाः समर्थिताः सन्ति । अन्ये कतिचन लघु लोपाः सन्ति यतोहि Matrix spec निरन्तरं विकसितः अस्ति परन्तु उद्देश्यं सम्पूर्ण spec कृते अन्ततः समर्थनं प्रदातुं अवशिष्टम् अस्ति</p>
<p xml:lang="sl">Neochat cilja, da bi bila popolna aplikacija po specifikaciji Matrixa. Kot takšna vsebuje vse v trenutni stabilni specifikaciji z pomembnimi izjemami pri VoIP, nitih in nekaterih vidikov šifriranja od konca do konca. Obstaja nekaj drugih manjših opustitev zaradi dejstva, da se specifikacija Matrix nenehno razvija, vendar cilj ostaja zagotoviti morebitno podporo celotni specifikaciji.</p> <p xml:lang="sl">Neochat cilja, da bi bila popolna aplikacija po specifikaciji Matrixa. Kot takšna vsebuje vse v trenutni stabilni specifikaciji z pomembnimi izjemami pri VoIP, nitih in nekaterih vidikov šifriranja od konca do konca. Obstaja nekaj drugih manjših opustitev zaradi dejstva, da se specifikacija Matrix nenehno razvija, vendar cilj ostaja zagotoviti morebitno podporo celotni specifikaciji.</p>
<p xml:lang="sv">NeoChat har som mål att vara ett fullständigt program enligt Matrix-specifikationen. Som sådant stöds allt i den nuvarande stabila specifikationen, med de nämnvärda undantagen VoIP, trådar och några aspekter av kryptering hela vägen. Det finns några ytterligare utelämnanden på grund av att Matrix-specifikationen hela tiden utvecklas, men målet förblir att till slut erbjuda stöd för hela specifikationen.</p> <p xml:lang="sv">NeoChat har som mål att vara ett fullständigt program enligt Matrix-specifikationen. Som sådant stöds allt i den nuvarande stabila specifikationen, med de nämnvärda undantagen VoIP, trådar och några aspekter av kryptering hela vägen. Det finns några ytterligare utelämnanden på grund av att Matrix-specifikationen hela tiden utvecklas, men målet förblir att till slut erbjuda stöd för hela specifikationen.</p>
<p xml:lang="tr">NeoChat, Matrix belirtimi için tam özellikli bir uygulama olmayı hedefler. Bu nedenle; VoIP, ileti zincirleri ve Uçtan Uca Şifrelemenin bazı yönleri gibi dikkate değer istisnalar dışında var olan kararlı belirtimdeki her şey desteklenir. Matrix belirtiminin sürekli gelişmesi nedeniyle birkaç küçük eksiklik daha var; ancak amaç tüm belirtim için nihai destek sağlamak olmayı sürdürüyor.</p> <p xml:lang="tr">NeoChat, Matrix belirtimi için tam özellikli bir uygulama olmayı hedefler. Bu nedenle; VoIP, ileti zincirleri ve Uçtan Uca Şifrelemenin bazı yönleri gibi dikkate değer istisnalar dışında var olan kararlı belirtimdeki her şey desteklenir. Matrix belirtiminin sürekli gelişmesi nedeniyle birkaç küçük eksiklik daha var; ancak amaç tüm belirtim için nihai destek sağlamak olmayı sürdürüyor.</p>
<p xml:lang="uk">Метою створення NeoChat є повноцінна реалізація програми для специфікації Matrix. Як наслідок, реалізовано усе у поточній стабільній специфікації, окрім голосового інтернет-зв'язку, потоків та деяких аспектів міжвузлового шифрування. Є також декілька інших незначних прогалин через те, що специфікація Matrix постійно змінюється, але метою лишається повна підтримка специфікації.</p> <p xml:lang="uk">Метою створення NeoChat є повноцінна реалізація програми для специфікації Matrix. Як наслідок, реалізовано усе у поточній стабільній специфікації, окрім голосового інтернет-зв'язку, потоків та деяких аспектів міжвузлового шифрування. Є також декілька інших незначних прогалин через те, що специфікація Matrix постійно змінюється, але метою лишається повна підтримка специфікації.</p>
<p xml:lang="x-test">xxNeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.xx</p>
<p xml:lang="zh-TW">NeoChat 以完整支援 Matrix 標準為目標,因此目前穩定版標準除了 VoIP、對話串與端對端加密的某些部分以外的所有部分都有支援。其他部分還有一些較小的不支援的部分這是因為 Matrix 標準隨時都在改進,但目標仍然時最終提供整個標準的完整支援。</p> <p xml:lang="zh-TW">NeoChat 以完整支援 Matrix 標準為目標,因此目前穩定版標準除了 VoIP、對話串與端對端加密的某些部分以外的所有部分都有支援。其他部分還有一些較小的不支援的部分這是因為 Matrix 標準隨時都在改進,但目標仍然時最終提供整個標準的完整支援。</p>
<p>Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:</p> <p>Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:</p>
<p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يوفر نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p> <p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يوفر نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p>
@@ -179,7 +179,6 @@
<p xml:lang="pl">Ze względu na sposób rozwoju Matriksa, NeoChat obsługuje także kilka niestabilnych możliwości. Obecnie są to:</p> <p xml:lang="pl">Ze względu na sposób rozwoju Matriksa, NeoChat obsługuje także kilka niestabilnych możliwości. Obecnie są to:</p>
<p xml:lang="pt">Devido à natureza do desenvolvimento da especificação do Matrix, o NeoChat também suporta diversas funcionalidades instáveis. De momento são:</p> <p xml:lang="pt">Devido à natureza do desenvolvimento da especificação do Matrix, o NeoChat também suporta diversas funcionalidades instáveis. De momento são:</p>
<p xml:lang="pt-BR">Devido à natureza do desenvolvimento da especificação Matrix, o NeoChat também suporta diversos recursos instáveis. Atualmente, são eles:</p> <p xml:lang="pt-BR">Devido à natureza do desenvolvimento da especificação Matrix, o NeoChat também suporta diversos recursos instáveis. Atualmente, são eles:</p>
<p xml:lang="ro">Datorită modului de dezvoltare a specificațiilor Matrix, NeoChat susține și numeroase caracteristici nestabile. Acum, acestea sunt:</p>
<p xml:lang="ru">В силу природы разработки спецификации Matrix в NeoChat тоже предусмотрена поддержка многочисленных нестабильных возможностей. В текущей версии это следующие возможности:</p> <p xml:lang="ru">В силу природы разработки спецификации Matrix в NeoChat тоже предусмотрена поддержка многочисленных нестабильных возможностей. В текущей версии это следующие возможности:</p>
<p xml:lang="sa">Matrix विनिर्देशविकासस्य प्रकृतेः कारणात् NeoChat अपि अनेकानाम् अस्थिरविशेषतानां समर्थनं करोति । सम्प्रति एते सन्ति :</p> <p xml:lang="sa">Matrix विनिर्देशविकासस्य प्रकृतेः कारणात् NeoChat अपि अनेकानाम् अस्थिरविशेषतानां समर्थनं करोति । सम्प्रति एते सन्ति :</p>
<p xml:lang="sl">Zaradi narave razvoja specifikacije Matrixa NeoChat podpira tudi številne nestabilne zmožnosti. Trenutno so to:</p> <p xml:lang="sl">Zaradi narave razvoja specifikacije Matrixa NeoChat podpira tudi številne nestabilne zmožnosti. Trenutno so to:</p>
@@ -187,6 +186,7 @@
<p xml:lang="ta">மேட்ரிக்ஸு நெறிமுறை வரையறுக்கப்படும் வித‍த்தின் காரணமாக, பல நிலையற்ற அம்சங்களையும் நியோச்சாட் ஆதரிக்கிறது. தற்போது ஆதரிக்கப்படுபவை:</p> <p xml:lang="ta">மேட்ரிக்ஸு நெறிமுறை வரையறுக்கப்படும் வித‍த்தின் காரணமாக, பல நிலையற்ற அம்சங்களையும் நியோச்சாட் ஆதரிக்கிறது. தற்போது ஆதரிக்கப்படுபவை:</p>
<p xml:lang="tr">NeoChat, Matrix belirtimi geliştirmesinin doğası gereği çok sayıda kararsız özelliği de destekler. Şu anda bunlar:</p> <p xml:lang="tr">NeoChat, Matrix belirtimi geliştirmesinin doğası gereği çok sayıda kararsız özelliği de destekler. Şu anda bunlar:</p>
<p xml:lang="uk">Через природу розробки специфікації Matrix, у NeoChat також передбачено підтримку численних нестабільних можливостей. У поточній версії цими можливостями є:</p> <p xml:lang="uk">Через природу розробки специфікації Matrix, у NeoChat також передбачено підтримку численних нестабільних можливостей. У поточній версії цими можливостями є:</p>
<p xml:lang="x-test">xxDue to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:xx</p>
<p xml:lang="zh-TW">由於 Matrix 標準的開發流程的緣故NeoChat 也支援數個非穩定版的功能。目前這些功能是:</p> <p xml:lang="zh-TW">由於 Matrix 標準的開發流程的緣故NeoChat 也支援數個非穩定版的功能。目前這些功能是:</p>
<ul> <ul>
<li>Polls - MSC3381</li> <li>Polls - MSC3381</li>
@@ -214,7 +214,6 @@
<li xml:lang="pl">Ankiety - MSC3381</li> <li xml:lang="pl">Ankiety - MSC3381</li>
<li xml:lang="pt">Inquéritos - MSC3381</li> <li xml:lang="pt">Inquéritos - MSC3381</li>
<li xml:lang="pt-BR">Enquetes - MSC3381</li> <li xml:lang="pt-BR">Enquetes - MSC3381</li>
<li xml:lang="ro">Sondaje - MSC3381</li>
<li xml:lang="ru">Голосования — MSC3381</li> <li xml:lang="ru">Голосования — MSC3381</li>
<li xml:lang="sa">मतदान - MSC3381</li> <li xml:lang="sa">मतदान - MSC3381</li>
<li xml:lang="sl">Polls - MSC3381</li> <li xml:lang="sl">Polls - MSC3381</li>
@@ -222,6 +221,7 @@
<li xml:lang="ta">வாக்கெடுப்புகள் - MSC3381</li> <li xml:lang="ta">வாக்கெடுப்புகள் - MSC3381</li>
<li xml:lang="tr">Anketler — MSC3381</li> <li xml:lang="tr">Anketler — MSC3381</li>
<li xml:lang="uk">Опитування - MSC3381</li> <li xml:lang="uk">Опитування - MSC3381</li>
<li xml:lang="x-test">xxPolls - MSC3381xx</li>
<li xml:lang="zh-TW">投票 - MSC3381</li> <li xml:lang="zh-TW">投票 - MSC3381</li>
<li>Sticker Packs - MSC2545</li> <li>Sticker Packs - MSC2545</li>
<li xml:lang="ar">حزم الملصقات - MSC2545</li> <li xml:lang="ar">حزم الملصقات - MSC2545</li>
@@ -248,7 +248,6 @@
<li xml:lang="pl">Paczki naklejek - MSC2545</li> <li xml:lang="pl">Paczki naklejek - MSC2545</li>
<li xml:lang="pt">Pacotes de Autocolantes - MSC2545</li> <li xml:lang="pt">Pacotes de Autocolantes - MSC2545</li>
<li xml:lang="pt-BR">Pacotes de Stickers - MSC2545</li> <li xml:lang="pt-BR">Pacotes de Stickers - MSC2545</li>
<li xml:lang="ro">Colecții de abțibilduri - MSC2545</li>
<li xml:lang="ru">Наборы стикеров — MSC2545</li> <li xml:lang="ru">Наборы стикеров — MSC2545</li>
<li xml:lang="sa">स्टिकर पैक - MSC2545</li> <li xml:lang="sa">स्टिकर पैक - MSC2545</li>
<li xml:lang="sl">Sticker Packs - MSC2545</li> <li xml:lang="sl">Sticker Packs - MSC2545</li>
@@ -256,6 +255,7 @@
<li xml:lang="ta">ஒட்டி தொகுப்புகள் - MSC2545</li> <li xml:lang="ta">ஒட்டி தொகுப்புகள் - MSC2545</li>
<li xml:lang="tr">Çıkartma Paketleri — MSC2545</li> <li xml:lang="tr">Çıkartma Paketleri — MSC2545</li>
<li xml:lang="uk">Пакунки наліпок - MSC2545</li> <li xml:lang="uk">Пакунки наліпок - MSC2545</li>
<li xml:lang="x-test">xxSticker Packs - MSC2545xx</li>
<li xml:lang="zh-TW">貼圖包 - MSC2545</li> <li xml:lang="zh-TW">貼圖包 - MSC2545</li>
<li>Location Events - MSC3488</li> <li>Location Events - MSC3488</li>
<li xml:lang="ar">موقع الأحداث - MSC3488</li> <li xml:lang="ar">موقع الأحداث - MSC3488</li>
@@ -282,7 +282,6 @@
<li xml:lang="pl">Wydarzenia w miejscach - MSC3488</li> <li xml:lang="pl">Wydarzenia w miejscach - MSC3488</li>
<li xml:lang="pt">Eventos com Localizações - MSC3488</li> <li xml:lang="pt">Eventos com Localizações - MSC3488</li>
<li xml:lang="pt-BR">Localização de eventos - MSC3488</li> <li xml:lang="pt-BR">Localização de eventos - MSC3488</li>
<li xml:lang="ro">Evenimente de amplasare - MSC3488</li>
<li xml:lang="ru">События местоположения — MSC3488</li> <li xml:lang="ru">События местоположения — MSC3488</li>
<li xml:lang="sa">स्थान घटनाएँ - MSC3488</li> <li xml:lang="sa">स्थान घटनाएँ - MSC3488</li>
<li xml:lang="sl">Location Events - MSC3488</li> <li xml:lang="sl">Location Events - MSC3488</li>
@@ -290,6 +289,7 @@
<li xml:lang="ta">இட நிகழ்வுகள் - MSC3488</li> <li xml:lang="ta">இட நிகழ்வுகள் - MSC3488</li>
<li xml:lang="tr">Konum Etkinlikleri — MSC3488</li> <li xml:lang="tr">Konum Etkinlikleri — MSC3488</li>
<li xml:lang="uk">Місцеві зустрічі - MSC3488</li> <li xml:lang="uk">Місцеві зустрічі - MSC3488</li>
<li xml:lang="x-test">xxLocation Events - MSC3488xx</li>
<li xml:lang="zh-TW">位置事件 - MSC3488</li> <li xml:lang="zh-TW">位置事件 - MSC3488</li>
</ul> </ul>
</description> </description>
@@ -320,7 +320,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">Anonymous donor, Akseli</value> <value key="KDE::supporters">Tanguy Fardet;[dabe](https://freeradical.zone/@dabe);[lengau](https://mastodon.world/@lengau);Joshua Strobl;Stuart Turton</value>
</custom> </custom>
<launchable type="desktop-id">org.kde.neochat.desktop</launchable> <launchable type="desktop-id">org.kde.neochat.desktop</launchable>
<screenshots> <screenshots>
@@ -352,7 +352,6 @@
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption> <caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption> <caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
<caption xml:lang="pt-BR">Visão principal com lista de salas, bate-papo e informações sobre as salas</caption> <caption xml:lang="pt-BR">Visão principal com lista de salas, bate-papo e informações sobre as salas</caption>
<caption xml:lang="ro">Vederea principală cu lista de camere, discuție, și informații despre cameră</caption>
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption> <caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
<caption xml:lang="sa">कक्षसूची, गपशपः, कक्षसूचना च सह मुख्यदृश्यम्</caption> <caption xml:lang="sa">कक्षसूची, गपशपः, कक्षसूचना च सह मुख्यदृश्यम्</caption>
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption> <caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
@@ -360,6 +359,7 @@
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption> <caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
<caption xml:lang="tr">Oda listesini, sohbet penceresini ve oda bilgisini gösteren ana görünüm</caption> <caption xml:lang="tr">Oda listesini, sohbet penceresini ve oda bilgisini gösteren ana görünüm</caption>
<caption xml:lang="uk">Головна панель із списком кімнат, спілкуванням та даними щодо кімнати</caption> <caption xml:lang="uk">Головна панель із списком кімнат, спілкуванням та даними щодо кімнати</caption>
<caption xml:lang="x-test">xxMain view with room list, chat, and room informationxx</caption>
<caption xml:lang="zh-TW">主頁面,包含聊天室列表、聊天內容,與聊天室資訊</caption> <caption xml:lang="zh-TW">主頁面,包含聊天室列表、聊天內容,與聊天室資訊</caption>
</screenshot> </screenshot>
<screenshot type="default"> <screenshot type="default">
@@ -389,7 +389,6 @@
<caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption> <caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption>
<caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption> <caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption>
<caption xml:lang="pt-BR">Descubra novas comunidades com os Espaços Matrix</caption> <caption xml:lang="pt-BR">Descubra novas comunidades com os Espaços Matrix</caption>
<caption xml:lang="ro">Descoperiți comunități noi cu Spații Matrix</caption>
<caption xml:lang="ru">Поиск новых сообществ с помощью Matrix Spaces</caption> <caption xml:lang="ru">Поиск новых сообществ с помощью Matrix Spaces</caption>
<caption xml:lang="sa">Matrix Spaces इत्यनेन सह नूतनानां समुदायानाम् अन्वेषणं कुर्वन्तु</caption> <caption xml:lang="sa">Matrix Spaces इत्यनेन सह नूतनानां समुदायानाम् अन्वेषणं कुर्वन्तु</caption>
<caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption> <caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption>
@@ -397,6 +396,7 @@
<caption xml:lang="ta">மேட்ரிக்ஸு இடங்களின் மூலம் புதிய சமூகங்களைக் கண்டுபிடிக்கலாம்</caption> <caption xml:lang="ta">மேட்ரிக்ஸு இடங்களின் மூலம் புதிய சமூகங்களைக் கண்டுபிடிக்கலாம்</caption>
<caption xml:lang="tr">Matrix Alanlar ile yeni topluluklar keşfedin</caption> <caption xml:lang="tr">Matrix Alanlar ile yeni topluluklar keşfedin</caption>
<caption xml:lang="uk">Пошук нових спільнот за допомогою Matrix Spaces</caption> <caption xml:lang="uk">Пошук нових спільнот за допомогою Matrix Spaces</caption>
<caption xml:lang="x-test">xxDiscover new communities with Matrix Spacesxx</caption>
<caption xml:lang="zh-TW">利用 Matrix 聊天空間發現新的社群</caption> <caption xml:lang="zh-TW">利用 Matrix 聊天空間發現新的社群</caption>
</screenshot> </screenshot>
<!-- <!--
@@ -434,7 +434,6 @@
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption> <caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption> <caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
<caption xml:lang="pt-BR">Visão principal com lista de salas, bate-papo e informações sobre as salas</caption> <caption xml:lang="pt-BR">Visão principal com lista de salas, bate-papo e informações sobre as salas</caption>
<caption xml:lang="ro">Vederea principală cu lista de camere, discuție, și informații despre cameră</caption>
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption> <caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
<caption xml:lang="sa">कक्षसूची, गपशपः, कक्षसूचना च सह मुख्यदृश्यम्</caption> <caption xml:lang="sa">कक्षसूची, गपशपः, कक्षसूचना च सह मुख्यदृश्यम्</caption>
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption> <caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
@@ -442,6 +441,7 @@
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption> <caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
<caption xml:lang="tr">Oda listesini, sohbet penceresini ve oda bilgisini gösteren ana görünüm</caption> <caption xml:lang="tr">Oda listesini, sohbet penceresini ve oda bilgisini gösteren ana görünüm</caption>
<caption xml:lang="uk">Головна панель із списком кімнат, спілкуванням та даними щодо кімнати</caption> <caption xml:lang="uk">Головна панель із списком кімнат, спілкуванням та даними щодо кімнати</caption>
<caption xml:lang="x-test">xxMain view with room list, chat, and room informationxx</caption>
<caption xml:lang="zh-TW">主頁面,包含聊天室列表、聊天內容,與聊天室資訊</caption> <caption xml:lang="zh-TW">主頁面,包含聊天室列表、聊天內容,與聊天室資訊</caption>
</screenshot> </screenshot>
<screenshot environment="windows"> <screenshot environment="windows">
@@ -473,7 +473,6 @@
<caption xml:lang="pl">Ekran logowania</caption> <caption xml:lang="pl">Ekran logowania</caption>
<caption xml:lang="pt">Ecrã de autenticação</caption> <caption xml:lang="pt">Ecrã de autenticação</caption>
<caption xml:lang="pt-BR">Tela de login</caption> <caption xml:lang="pt-BR">Tela de login</caption>
<caption xml:lang="ro">Ecran de autentificare</caption>
<caption xml:lang="ru">Окно входа</caption> <caption xml:lang="ru">Окно входа</caption>
<caption xml:lang="sa">लॉगिन् स्क्रीन</caption> <caption xml:lang="sa">लॉगिन् स्क्रीन</caption>
<caption xml:lang="sl">Prijavni zaslon</caption> <caption xml:lang="sl">Prijavni zaslon</caption>
@@ -481,6 +480,7 @@
<caption xml:lang="ta">நுழைவுத் திரை</caption> <caption xml:lang="ta">நுழைவுத் திரை</caption>
<caption xml:lang="tr">Oturum açma ekranı</caption> <caption xml:lang="tr">Oturum açma ekranı</caption>
<caption xml:lang="uk">Вікно входу</caption> <caption xml:lang="uk">Вікно входу</caption>
<caption xml:lang="x-test">xxLogin screenxx</caption>
<caption xml:lang="zh-TW">登入畫面</caption> <caption xml:lang="zh-TW">登入畫面</caption>
</screenshot> </screenshot>
</screenshots> </screenshots>
@@ -488,10 +488,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="25.12.1" date="2026-01-08"/>
<release version="25.12.0" date="2025-12-11"/>
<release version="25.08.3" date="2025-11-06"/>
<release version="25.08.2" date="2025-10-09"/>
<release version="25.08.1" date="2025-09-11"/> <release version="25.08.1" date="2025-09-11"/>
<release version="25.08.0" date="2025-08-14"/> <release version="25.08.0" date="2025-08-14"/>
<release version="25.04.3" date="2025-07-03"/> <release version="25.04.3" date="2025-07-03"/>

View File

@@ -44,6 +44,7 @@ Name[sv]=NeoChat
Name[ta]=நியோச்சாட் Name[ta]=நியோச்சாட்
Name[tr]=NeoChat Name[tr]=NeoChat
Name[uk]=NeoChat Name[uk]=NeoChat
Name[x-test]=xxNeoChatxx
Name[zh_CN]=NeoChat Name[zh_CN]=NeoChat
Name[zh_TW]=NeoChat Name[zh_TW]=NeoChat
GenericName=Matrix Client GenericName=Matrix Client
@@ -87,6 +88,7 @@ GenericName[sv]=Matrix-klient
GenericName[ta]=Matrix வாங்கி GenericName[ta]=Matrix வாங்கி
GenericName[tr]=Matrix İstemcisi GenericName[tr]=Matrix İstemcisi
GenericName[uk]=Клієнт Matrix GenericName[uk]=Клієнт Matrix
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
@@ -112,7 +114,6 @@ Comment[lv]=Tērzējiet „Matrix“ tīklā
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[pt_BR]=Bate papo na Matrix
Comment[ro]=Discutați pe Matrix
Comment[ru]=Общение в Matrix Comment[ru]=Общение в Matrix
Comment[sa]=Matrix इत्यत्र गपशपं कुर्वन्तु Comment[sa]=Matrix इत्यत्र गपशपं कुर्वन्तु
Comment[sl]=Klepet na Matrixu Comment[sl]=Klepet na Matrixu
@@ -120,6 +121,7 @@ Comment[sv]=Chatta på Matrix
Comment[ta]=மேட்ரிக்ஸில் உரையாட உதவும் Comment[ta]=மேட்ரிக்ஸில் உரையாட உதவும்
Comment[tr]=Matrix üzerinde sohbet edin Comment[tr]=Matrix üzerinde sohbet edin
Comment[uk]=Спілкування у Matrix Comment[uk]=Спілкування у Matrix
Comment[x-test]=xxChat on Matrixxx
Comment[zh_CN]=在 Matrix 上聊天 Comment[zh_CN]=在 Matrix 上聊天
Comment[zh_TW]=在 Matrix 上聊天 Comment[zh_TW]=在 Matrix 上聊天
MimeType=x-scheme-handler/matrix; MimeType=x-scheme-handler/matrix;

View File

@@ -0,0 +1,28 @@
SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
SPDX-License-Identifier: BSD-2-Clause
From dbd1cefd0f07a6942aef450f8f3e082aa3b1cc25 Mon Sep 17 00:00:00 2001
From: Tobias Fella <tobias.fella@kde.org>
Date: Sun, 17 Aug 2025 20:04:04 +0200
Subject: [PATCH] Revert "Bump KF6 dependency version"
This reverts commit 18a6ea98232b3a734905fb18eebba9cf39bf5325.
---
CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 10fe66daa..cd063113d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,7 +14,7 @@ set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
-set(KF_MIN_VERSION "6.17")
+set(KF_MIN_VERSION "6.12")
set(QT_MIN_VERSION "6.8")
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
--
2.50.1

View File

@@ -0,0 +1,123 @@
SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
SPDX-License-Identifier: BSD-2-Clause
From ca72345b8ee550be2172d8ac5e5dc9e4c2b508c9 Mon Sep 17 00:00:00 2001
From: Tobias Fella <tobias.fella@kde.org>
Date: Sun, 17 Aug 2025 20:00:08 +0200
Subject: [PATCH] Revert "Use new Kirigami builtin column resize handle"
This reverts commit de97275a387abcbca6fcb185bcbd1b69c30f5c66.
---
src/app/qml/Main.qml | 1 -
src/rooms/RoomListPage.qml | 70 +++++++++++++++++++++++++++++---------
2 files changed, 54 insertions(+), 17 deletions(-)
diff --git a/src/app/qml/Main.qml b/src/app/qml/Main.qml
index ea8955674..6eed271c1 100644
--- a/src/app/qml/Main.qml
+++ b/src/app/qml/Main.qml
@@ -45,7 +45,6 @@ Kirigami.ApplicationWindow {
showExisting: true
onConnectionChosen: root.load()
}
- columnView.columnResizeMode: pageStack.wideMode ? Kirigami.ColumnView.DynamicColumns : Kirigami.ColumnView.SingleColumn
globalToolBar.canContainHandles: true
globalToolBar {
style: Kirigami.ApplicationHeaderStyle.ToolBar
diff --git a/src/rooms/RoomListPage.qml b/src/rooms/RoomListPage.qml
index 2ac211fd5..f5586d789 100644
--- a/src/rooms/RoomListPage.qml
+++ b/src/rooms/RoomListPage.qml
@@ -17,22 +17,13 @@ import org.kde.neochat
Kirigami.Page {
id: root
- Kirigami.ColumnView.interactiveResizeEnabled: true
- Kirigami.ColumnView.minimumWidth: _private.collapsedSize + spaceDrawer.width + 1
- Kirigami.ColumnView.maximumWidth: _private.defaultWidth + spaceDrawer.width + 1
- Kirigami.ColumnView.onInteractiveResizingChanged: {
- if (!Kirigami.ColumnView.interactiveResizing && collapsed) {
- Kirigami.ColumnView.preferredWidth = root.Kirigami.ColumnView.minimumWidth;
- }
- }
- Kirigami.ColumnView.preferredWidth: _private.currentWidth + spaceDrawer.width + 1
- Kirigami.ColumnView.onPreferredWidthChanged: {
- if (width > _private.collapseWidth) {
- NeoChatConfig.collapsed = false;
- } else if (Kirigami.ColumnView.interactiveResizing) {
- NeoChatConfig.collapsed = true;
- }
- }
+ /**
+ * @brief The current width of the room list.
+ *
+ * @note Other objects can access the value but the private function makes sure
+ * that only the internal members can modify it.
+ */
+ readonly property int currentWidth: _private.currentWidth + spaceDrawer.width + 1
required property NeoChatConnection connection
@@ -40,6 +31,10 @@ Kirigami.Page {
signal search
+ onCurrentWidthChanged: pageStack.defaultColumnWidth = root.currentWidth
+ Component.onCompleted: pageStack.defaultColumnWidth = root.currentWidth
+
+
onCollapsedChanged: {
if (collapsed) {
RoomManager.sortFilterRoomTreeModel.filterText = "";
@@ -252,6 +247,49 @@ Kirigami.Page {
sourceComponent: Kirigami.Settings.isMobile ? exploreComponentMobile : userInfoDesktop
}
+ MouseArea {
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ parent: applicationWindow().overlay.parent
+
+ x: root.currentWidth - width / 2
+ width: Kirigami.Units.smallSpacing * 2
+ z: root.z + 1
+ enabled: RoomManager.hasOpenRoom && applicationWindow().width >= Kirigami.Units.gridUnit * 35
+ visible: enabled
+ cursorShape: Qt.SplitHCursor
+
+ property int _lastX
+
+ onPressed: mouse => {
+ _lastX = mouse.x;
+ }
+ onPositionChanged: mouse => {
+ if (_lastX == -1) {
+ return;
+ }
+ if (mouse.x > _lastX) {
+ // we moved to the right
+ if (_private.currentWidth < _private.collapseWidth && _private.currentWidth + (mouse.x - _lastX) >= _private.collapseWidth) {
+ // Here we get back directly to a more wide mode.
+ _private.currentWidth = _private.defaultWidth;
+ NeoChatConfig.collapsed = false;
+ } else if (_private.currentWidth >= _private.collapseWidth) {
+ // Increase page width
+ _private.currentWidth = Math.min(_private.defaultWidth, _private.currentWidth + (mouse.x - _lastX));
+ }
+ } else if (mouse.x < _lastX) {
+ const tmpWidth = _private.currentWidth - (_lastX - mouse.x);
+ if (tmpWidth < _private.collapseWidth) {
+ _private.currentWidth = Qt.binding(() => _private.collapsedSize);
+ NeoChatConfig.collapsed = true;
+ } else {
+ _private.currentWidth = tmpWidth;
+ }
+ }
+ }
+ }
+
Component {
id: userInfo
UserInfo {
--
2.50.1

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

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

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

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

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,122 +0,0 @@
<?xml version="1.0" ?>
<!DOCTYPE refentry PUBLIC "-//KDE//DTD DocBook XML V4.5-Based Variant V1.1//EN" "dtd/kdedbx45.dtd" [
<!ENTITY % Brazilian-Portuguese "INCLUDE">
]>
<!--
SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
SPDX-License-Identifier: CC-BY-SA-4.0
-->
<refentry lang="&language;">
<refentryinfo>
<title
>Manual do Usuário do NeoChat</title>
<author
><firstname
>Carl</firstname
><surname
>Schwan</surname
> <contrib
>NeoChat man page.</contrib
> <email
>carl@carlschwan.eu</email
></author>
<date
>01/11/2022</date>
<releaseinfo
>22.09</releaseinfo>
<productname
>NeoChat</productname>
</refentryinfo>
<refmeta>
<refentrytitle>
<command
>neochat</command>
</refentrytitle>
<manvolnum
>1</manvolnum>
</refmeta>
<refnamediv>
<refname
>neochat</refname>
<refpurpose
>Cliente para interação com o protocolo de mensagens Matrix.</refpurpose>
</refnamediv>
<!-- body begins here -->
<refsynopsisdiv id='synopsis'>
<cmdsynopsis
><command
>neochat</command
> <arg choice="opt"
><replaceable
>URI</replaceable
></arg
> </cmdsynopsis>
</refsynopsisdiv>
<refsect1 id="description">
<title
>Descrição</title>
<para
>O <command
>neochat</command
> é um aplicativo de bate-papo para o protocolo Matrix. Ele funciona tanto em computadores quanto em dispositivos móveis. </para>
</refsect1>
<refsect1 id="options"
><title
>Opções</title>
<variablelist>
<varlistentry>
<term
><option
>URI</option
></term>
<listitem>
<para
>O URI da matriz para um usuário ou uma sala. Por exemplo, matrix:u/usuário:exemplo.org e matrix:r/root:exemplo.org. Isso fará com que o NeoChat tente abrir a sala ou conversa especificada. </para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1 id="bug">
<title
>Relatar bugs</title>
<para
>Você pode reportar erros e solicitar novas funcionalidades em <ulink url="https://bugs.kde.org/enter_bug.cgi?product=NeoChat&amp;component=General"
>https://bugs.kde.org/enter_bug.cgi?product=NeoChat&amp;component=General</ulink
></para>
</refsect1>
<refsect1>
<title
>Veja também</title>
<simplelist>
<member
>Lista de perguntas frequentes sobre o Matrix <ulink url="https://matrix.org/faq/"
>https://matrix.org/faq/</ulink
> </member>
<member
>kf5options(7)</member>
<member
>qt5options(7)</member>
</simplelist>
</refsect1>
<refsect1 id="copyright"
><title
>Direitos autorais</title>
<para
>Direitos autorais &copy; 2020-2022 Tobias Fella </para>
<para
>Direitos autorais &copy; 2020-2022 Carl Schwan </para>
<para
>Licença: GNU General Public Versão 3 ou posterior <ulink url="https://www.gnu.org/licenses/gpl-3.0.html"
>https://www.gnu.org/licenses/gpl-3.0.html</ulink
>&gt;</para>
</refsect1>
</refentry>

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

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

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

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

File diff suppressed because it is too large Load Diff

View File

@@ -48,8 +48,6 @@ if(ANDROID OR WIN32)
set_source_files_properties(qml/GlobalMenuStub.qml PROPERTIES set_source_files_properties(qml/GlobalMenuStub.qml PROPERTIES
QT_QML_SOURCE_TYPENAME GlobalMenu QT_QML_SOURCE_TYPENAME GlobalMenu
) )
else()
set(EXTRA_IMPORTS org.kde.purpose)
endif() 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
@@ -103,7 +101,6 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/ReasonDialog.qml qml/ReasonDialog.qml
qml/NewPollDialog.qml qml/NewPollDialog.qml
qml/UserMenu.qml qml/UserMenu.qml
qml/MeetingDialog.qml
DEPENDENCIES DEPENDENCIES
QtCore QtCore
QtQuick QtQuick
@@ -120,8 +117,8 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
org.kde.neochat.login org.kde.neochat.login
org.kde.neochat.chatbar org.kde.neochat.chatbar
org.kde.config org.kde.config
org.kde.purpose
org.kde.syntaxhighlighting org.kde.syntaxhighlighting
${EXTRA_IMPORTS}
) )
if(NOT ANDROID AND NOT WIN32) if(NOT ANDROID AND NOT WIN32)
@@ -153,7 +150,6 @@ target_include_directories(neochat-app PRIVATE ${CMAKE_BINARY_DIR})
target_link_libraries(neochat-app PRIVATE target_link_libraries(neochat-app PRIVATE
neochat neochat
KF6::IconThemes
) )
ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png) ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
@@ -204,9 +200,8 @@ target_link_libraries(neochat PUBLIC
KF6::ConfigGui KF6::ConfigGui
KF6::CoreAddons KF6::CoreAddons
KF6::SonnetCore KF6::SonnetCore
KF6::IconThemes
KF6::ItemModels KF6::ItemModels
KF6::I18nQml
KirigamiApp
QuotientQt6 QuotientQt6
Login Login
Rooms Rooms
@@ -214,6 +209,10 @@ target_link_libraries(neochat PUBLIC
Spaces Spaces
) )
if (TARGET KF6::Crash)
target_link_libraries(neochat PUBLIC KF6::Crash)
endif()
kconfig_target_kcfg_file(neochat FILE neochatconfig.kcfg CLASS_NAME NeoChatConfig MUTATORS GENERATE_PROPERTIES DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR SINGLETON GENERATE_MOC QML_REGISTRATION) kconfig_target_kcfg_file(neochat FILE neochatconfig.kcfg CLASS_NAME NeoChatConfig MUTATORS GENERATE_PROPERTIES DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR SINGLETON GENERATE_MOC QML_REGISTRATION)
if(NEOCHAT_FLATPAK) if(NEOCHAT_FLATPAK)
@@ -324,7 +323,6 @@ if(ANDROID)
"kt-restore-defaults-symbolic" "kt-restore-defaults-symbolic"
"user-symbolic" "user-symbolic"
"mark-location-symbolic" "mark-location-symbolic"
"amarok_playcount"
${KIRIGAMI_ADDONS_ICONS} ${KIRIGAMI_ADDONS_ICONS}
) )
@@ -353,8 +351,7 @@ endif()
install(TARGETS neochat-app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(TARGETS neochat-app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE) if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
# krunner plugin must be the same as the app id for flatpak to export it install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins)
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins RENAME org.kde.neochat.desktop)
endif() endif()
if (APPLE) if (APPLE)

View File

@@ -17,6 +17,7 @@
#include <Quotient/qt_connection_util.h> #include <Quotient/qt_connection_util.h>
#include <Quotient/settings.h> #include <Quotient/settings.h>
#include "accountmanager.h"
#include "enums/roomsortparameter.h" #include "enums/roomsortparameter.h"
#include "general_logging.h" #include "general_logging.h"
#include "mediasizehelper.h" #include "mediasizehelper.h"
@@ -25,7 +26,9 @@
#include "models/roomlistmodel.h" #include "models/roomlistmodel.h"
#include "models/roomtreemodel.h" #include "models/roomtreemodel.h"
#include "neochatconfig.h" #include "neochatconfig.h"
#include "neochatconnection.h"
#include "neochatroom.h" #include "neochatroom.h"
#include "notificationsmanager.h"
#include "proxycontroller.h" #include "proxycontroller.h"
#include "roommanager.h" #include "roommanager.h"
@@ -306,7 +309,8 @@ void Controller::listenForNotifications()
connect(timer, &QTimer::timeout, qGuiApp, &QGuiApplication::quit); connect(timer, &QTimer::timeout, qGuiApp, &QGuiApplication::quit);
connect(connector, &KUnifiedPush::Connector::messageReceived, [timer](const QByteArray &data) { connect(connector, &KUnifiedPush::Connector::messageReceived, [timer](const QByteArray &data) {
NotificationsManager::postPushNotification(data); instance().m_notificationsManager.postPushNotification(data);
timer->stop();
}); });
// Wait five seconds to see if we received any messages or this happened to be an erroneous activation. // Wait five seconds to see if we received any messages or this happened to be an erroneous activation.

View File

@@ -33,10 +33,13 @@
#include <KWindowSystem> #include <KWindowSystem>
#endif #endif
#if __has_include("KCrash")
#include <KCrash>
#endif
#include <KIconTheme> #include <KIconTheme>
#include <KLocalizedQmlContext> #include <KLocalizedContext>
#include <KLocalizedString> #include <KLocalizedString>
#include <KirigamiApp>
#include "neochat-version.h" #include "neochat-version.h"
@@ -101,11 +104,8 @@ Q_DECL_EXPORT
#endif #endif
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QNetworkProxyFactory::setUseSystemConfiguration(true);
// We currently need to do this ourselves,
// KirigamiApp currently called this after constructing the app which breaks icons on Windows.
KIconTheme::initTheme(); KIconTheme::initTheme();
QNetworkProxyFactory::setUseSystemConfiguration(true);
#ifdef HAVE_WEBVIEW #ifdef HAVE_WEBVIEW
QtWebView::initialize(); QtWebView::initialize();
@@ -113,10 +113,24 @@ int main(int argc, char *argv[])
QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi); QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
#endif #endif
KirigamiApp::App app(argc, argv); #ifdef Q_OS_ANDROID
KirigamiApp kirigamiApp; QGuiApplication app(argc, argv);
QQuickStyle::setStyle(u"org.kde.breeze"_s);
#else
QIcon::setFallbackThemeName("breeze"_L1);
QApplication app(argc, argv);
// Default to org.kde.desktop style unless the user forces another style
if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) {
QQuickStyle::setStyle(u"org.kde.desktop"_s);
}
#endif
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
}
QApplication::setStyle(u"breeze"_s); QApplication::setStyle(u"breeze"_s);
QFont font(u"Segoe UI Emoji"_s); QFont font(u"Segoe UI Emoji"_s);
font.setPointSize(10); font.setPointSize(10);
@@ -163,6 +177,10 @@ int main(int argc, char *argv[])
KAboutData::setApplicationData(about); KAboutData::setApplicationData(about);
QGuiApplication::setWindowIcon(QIcon::fromTheme(u"org.kde.neochat"_s)); QGuiApplication::setWindowIcon(QIcon::fromTheme(u"org.kde.neochat"_s));
#if __has_include("KCrash")
KCrash::initialize();
#endif
Connection::setEncryptionDefault(true); Connection::setEncryptionDefault(true);
Connection::setDirectChatEncryptionDefault(true); Connection::setDirectChatEncryptionDefault(true);
@@ -187,7 +205,7 @@ int main(int argc, char *argv[])
parser.addOption(testOption); parser.addOption(testOption);
#ifdef HAVE_KUNIFIEDPUSH #ifdef HAVE_KUNIFIEDPUSH
QCommandLineOption dbusActivatedOption(u"dbus-activated"_s); QCommandLineOption dbusActivatedOption(u"dbus-activated"_s, i18n("Internal usage only."));
dbusActivatedOption.setFlags(QCommandLineOption::Flag::HiddenFromHelp); dbusActivatedOption.setFlags(QCommandLineOption::Flag::HiddenFromHelp);
parser.addOption(dbusActivatedOption); parser.addOption(dbusActivatedOption);
#endif #endif
@@ -201,14 +219,8 @@ int main(int argc, char *argv[])
#ifdef HAVE_KUNIFIEDPUSH #ifdef HAVE_KUNIFIEDPUSH
if (parser.isSet(dbusActivatedOption)) { if (parser.isSet(dbusActivatedOption)) {
#ifdef HAVE_KDBUSADDONS // We want to be replaceable by the main client
// We *don't* want to use KDBusService here. I don't know why, but it makes activation super unreliable. We don't really need it anyway. KDBusService service(KDBusService::Replace);
if (!QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.neochat"))) {
// Gracefully fail if NeoChat is already running
qWarning() << "NeoChat already running, not sending push notifications.";
return 0;
}
#endif
#ifdef HAVE_RUNNER #ifdef HAVE_RUNNER
// If we are built with KRunner and KUnifiedPush support, we need to do something special. // If we are built with KRunner and KUnifiedPush support, we need to do something special.
@@ -267,7 +279,7 @@ int main(int argc, char *argv[])
}); });
#endif #endif
KLocalization::setupLocalizedContext(&engine); engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
engine.setNetworkAccessManagerFactory(new NetworkAccessManagerFactory()); engine.setNetworkAccessManagerFactory(new NetworkAccessManagerFactory());
if (parser.isSet("ignore-ssl-errors"_L1)) { if (parser.isSet("ignore-ssl-errors"_L1)) {
@@ -282,9 +294,7 @@ int main(int argc, char *argv[])
engine.addImageProvider(u"blurhash"_s, new BlurhashImageProvider); engine.addImageProvider(u"blurhash"_s, new BlurhashImageProvider);
if (!kirigamiApp.start("org.kde.neochat", "Main", &engine)) { engine.loadFromModule("org.kde.neochat", "Main");
return -1;
}
if (!parser.positionalArguments().isEmpty() && !parser.isSet("share"_L1)) { if (!parser.positionalArguments().isEmpty() && !parser.isSet("share"_L1)) {
RoomManager::instance().setUrlArgument(parser.positionalArguments()[0]); RoomManager::instance().setUrlArgument(parser.positionalArguments()[0]);

View File

@@ -42,6 +42,7 @@ Name[sv]=NeoChat
Name[ta]=நியோச்சாட் Name[ta]=நியோச்சாட்
Name[tr]=NeoChat Name[tr]=NeoChat
Name[uk]=NeoChat Name[uk]=NeoChat
Name[x-test]=xxNeoChatxx
Name[zh_CN]=NeoChat Name[zh_CN]=NeoChat
Name[zh_TW]=NeoChat Name[zh_TW]=NeoChat
DesktopEntry=org.kde.neochat DesktopEntry=org.kde.neochat
@@ -86,6 +87,7 @@ Comment[sv]=En klient för matrix, det decentraliserade kommunikationsprotokolle
Comment[ta]=மையமில்லா தகவல் பரிமாற்ற நெறிமுறையான மேட்ரிக்ஸுக்கான செயலி Comment[ta]=மையமில்லா தகவல் பரிமாற்ற நெறிமுறையான மேட்ரிக்ஸுக்கான செயலி
Comment[tr]=Merkezi olmayan iletişim protokolü Matrix için bir istemci Comment[tr]=Merkezi olmayan iletişim protokolü Matrix için bir istemci
Comment[uk]=Клієнт matrix, децентралізованого протоколу обміну даними Comment[uk]=Клієнт matrix, децентралізованого протоколу обміну даними
Comment[x-test]=xxA client for matrix, the decentralized communication protocolxx
Comment[zh_CN]=分布式通讯协议 Matrix 的客户端 Comment[zh_CN]=分布式通讯协议 Matrix 的客户端
Comment[zh_TW]=去中心化通訊協定 Matrix 的用戶端 Comment[zh_TW]=去中心化通訊協定 Matrix 的用戶端
@@ -132,6 +134,7 @@ Name[sv]=Nytt meddelande
Name[ta]=புதிய செய்தி Name[ta]=புதிய செய்தி
Name[tr]=Yeni İleti Name[tr]=Yeni İleti
Name[uk]=Нове повідомлення Name[uk]=Нове повідомлення
Name[x-test]=xxNew messagexx
Name[zh_CN]=新消息 Name[zh_CN]=新消息
Name[zh_TW]=新訊息 Name[zh_TW]=新訊息
Comment=There is a new message Comment=There is a new message
@@ -174,6 +177,7 @@ Comment[sv]=Det finns ett nytt meddelande
Comment[ta]=ஒரு புதிய செய்தி உள்ளது Comment[ta]=ஒரு புதிய செய்தி உள்ளது
Comment[tr]=Yeni bir ileti var Comment[tr]=Yeni bir ileti var
Comment[uk]=Надійшло нове повідомлення Comment[uk]=Надійшло нове повідомлення
Comment[x-test]=xxThere is a new messagexx
Comment[zh_CN]=有新消息 Comment[zh_CN]=有新消息
Comment[zh_TW]=有新的訊息 Comment[zh_TW]=有新的訊息
Action=Popup Action=Popup
@@ -211,7 +215,6 @@ Name[pa]=ਨਵਾਂ ਸੱਦਾ
Name[pl]=Nowe zaproszenie Name[pl]=Nowe zaproszenie
Name[pt]=Novo Convite Name[pt]=Novo Convite
Name[pt_BR]=Novo convite Name[pt_BR]=Novo convite
Name[ro]=Invitație nouă
Name[ru]=Новое приглашение Name[ru]=Новое приглашение
Name[sa]=नवीन आमन्त्रणम् Name[sa]=नवीन आमन्त्रणम्
Name[sl]=Novo povabilo Name[sl]=Novo povabilo
@@ -219,6 +222,7 @@ Name[sv]=Ny inbjudan
Name[ta]=புதிய அழைப்பிதழ் Name[ta]=புதிய அழைப்பிதழ்
Name[tr]=Yeni Davet Name[tr]=Yeni Davet
Name[uk]=Нове запрошення Name[uk]=Нове запрошення
Name[x-test]=xxNew Invitationxx
Name[zh_CN]=新邀请 Name[zh_CN]=新邀请
Name[zh_TW]=新邀請 Name[zh_TW]=新邀請
Comment=There is a new invitation to a room Comment=There is a new invitation to a room
@@ -253,14 +257,14 @@ Comment[pa]=ਰੂਮ ਲਈ ਨਵਾਂ ਸੱਦਾ ਹੈ
Comment[pl]=Dostępna jest nowe zaproszenie do pokoju Comment[pl]=Dostępna jest nowe zaproszenie do pokoju
Comment[pt]=Existe um novo convite para uma sala Comment[pt]=Existe um novo convite para uma sala
Comment[pt_BR]=Existe um novo convite para uma sala Comment[pt_BR]=Existe um novo convite para uma sala
Comment[ro]=E o nouă invitație la o cameră
Comment[ru]=Доступно новое приглашение в комнату Comment[ru]=Доступно новое приглашение в комнату
Comment[sa]=कक्षस्य नूतनं निमन्त्रणम् अस्ति Comment[sa]=कक्षस्य नूतनं निमन्त्रणम् अस्ति
Comment[sl]=Tam je novo povabilo v sobo Comment[sl]=Tam je novo povabilo v sobo
Comment[sv]=Det finns en ny inbjudan till ett rum Comment[sv]=Det finns en ny inbjudan till ett rum
Comment[ta]=ஓர் அரங்கிற்கான புதிய அழைப்பிதழ் உள்ளது Comment[ta]=ஓர் அரங்கிற்கான புதிய அழைப்பிதழ் உள்ளது
Comment[tr]=Bir odaya yeni bir davet var Comment[tr]=Bir odaya yeni bir davetiye var
Comment[uk]=У кімнаті нове запрошення Comment[uk]=У кімнаті нове запрошення
Comment[x-test]=xxThere is a new invitation to a roomxx
Comment[zh_CN]=有新的聊天室邀请 Comment[zh_CN]=有新的聊天室邀请
Comment[zh_TW]=有新的加入聊天室邀請 Comment[zh_TW]=有新的加入聊天室邀請
Action=Popup Action=Popup
@@ -292,7 +296,6 @@ Name[nl]=Gedeelde
Name[nn]=Del Name[nn]=Del
Name[pl]=Udostępnij Name[pl]=Udostępnij
Name[pt_BR]=Compartilhar Name[pt_BR]=Compartilhar
Name[ro]=Partajare
Name[ru]=Публикация Name[ru]=Публикация
Name[sa]=संविभागः Name[sa]=संविभागः
Name[sl]=Deli Name[sl]=Deli
@@ -300,6 +303,7 @@ Name[sv]=Dela
Name[ta]=பகிர் Name[ta]=பகிர்
Name[tr]=Paylaş Name[tr]=Paylaş
Name[uk]=Оприлюднення Name[uk]=Оприлюднення
Name[x-test]=xxSharexx
Name[zh_CN]=分享 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
@@ -327,7 +331,6 @@ Comment[nl]=Het resultaat van het delen van een stukje inhoud
Comment[nn]=Resultatet av deling av innhald Comment[nn]=Resultatet av deling av innhald
Comment[pl]=Wynik udostępniania kawałka treści Comment[pl]=Wynik udostępniania kawałka treści
Comment[pt_BR]=O resultado de compartilhar um conteúdo Comment[pt_BR]=O resultado de compartilhar um conteúdo
Comment[ro]=Rezultatul partajării unei bucăți de conținut
Comment[ru]=Результат публикации данных Comment[ru]=Результат публикации данных
Comment[sa]=सामग्रीखण्डस्य साझाकरणस्य परिणामः Comment[sa]=सामग्रीखण्डस्य साझाकरणस्य परिणामः
Comment[sl]=Rezultat deljenega kosa vsebine Comment[sl]=Rezultat deljenega kosa vsebine
@@ -335,6 +338,7 @@ Comment[sv]=Resultatet av att dela innehåll
Comment[ta]=எதையோ பகிர்ந்த‍தன் விளைவு 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[zh_CN]=分享一个内容得到的结果 Comment[zh_CN]=分享一个内容得到的结果
Comment[zh_TW]=分享一份內容之後的結果 Comment[zh_TW]=分享一份內容之後的結果
Action=Popup Action=Popup

View File

@@ -66,10 +66,6 @@
</entry> </entry>
</group> </group>
<group name="Timeline"> <group name="Timeline">
<entry name="FontScale" type="double">
<label>Scaling factor for font sizes</label>
<default>1.0</default>
</entry>
<entry name="ShowAvatarInTimeline" type="bool"> <entry name="ShowAvatarInTimeline" type="bool">
<label>Show avatar in the timeline</label> <label>Show avatar in the timeline</label>
<default>true</default> <default>true</default>

View File

@@ -388,7 +388,7 @@ void NotificationsManager::postPushNotification(const QByteArray &message)
#ifdef HAVE_KIO #ifdef HAVE_KIO
auto openAction = notification->addAction(i18n("Open NeoChat")); auto openAction = notification->addAction(i18n("Open NeoChat"));
connect(openAction, &KNotificationAction::activated, notification, [=]() { connect(openAction, &KNotificationAction::activated, this, [=]() {
QString properId = roomId; QString properId = roomId;
properId = properId.replace(u"#"_s, QString()); properId = properId.replace(u"#"_s, QString());
properId = properId.replace(u"!"_s, QString()); properId = properId.replace(u"!"_s, QString());
@@ -402,6 +402,8 @@ void NotificationsManager::postPushNotification(const QByteArray &message)
connect(notification, &KNotification::closed, qGuiApp, &QGuiApplication::quit); connect(notification, &KNotification::closed, qGuiApp, &QGuiApplication::quit);
notification->sendEvent(); notification->sendEvent();
m_notifications.insert(roomId, {json["ts"_L1].toVariant().toLongLong(), notification});
} else { } else {
qWarning() << "Skipping unsupported push notification" << type; qWarning() << "Skipping unsupported push notification" << type;
} }

View File

@@ -53,7 +53,7 @@ public:
/** /**
* @brief Display a native notification for the given push notification. * @brief Display a native notification for the given push notification.
*/ */
static void postPushNotification(const QByteArray &message); void postPushNotification(const QByteArray &message);
/** /**
* @brief Handle the notifications for the given connection. * @brief Handle the notifications for the given connection.

View File

@@ -43,6 +43,7 @@ Name[sv]=NeoChat
Name[ta]=நியோச்சாட் Name[ta]=நியோச்சாட்
Name[tr]=NeoChat Name[tr]=NeoChat
Name[uk]=NeoChat Name[uk]=NeoChat
Name[x-test]=xxNeoChatxx
Name[zh_CN]=NeoChat Name[zh_CN]=NeoChat
Name[zh_TW]=NeoChat Name[zh_TW]=NeoChat
Comment=Find rooms in NeoChat Comment=Find rooms in NeoChat
@@ -75,7 +76,6 @@ Comment[nn]=Finn rom i NeoChat
Comment[pl]=Znajdź pokoje w NeoChat Comment[pl]=Znajdź pokoje w NeoChat
Comment[pt]=Procurar salas no NeoChat Comment[pt]=Procurar salas no NeoChat
Comment[pt_BR]=Encontrar salas no NeoChat Comment[pt_BR]=Encontrar salas no NeoChat
Comment[ro]=Găsește camere în NeoChat
Comment[ru]=Поиск комнат NeoChat Comment[ru]=Поиск комнат NeoChat
Comment[sa]=NeoChat इत्यत्र कक्ष्याः अन्वेषणं कुर्वन्तु Comment[sa]=NeoChat इत्यत्र कक्ष्याः अन्वेषणं कुर्वन्तु
Comment[sl]=Najdi sobe v NeoChatu Comment[sl]=Najdi sobe v NeoChatu
@@ -83,6 +83,7 @@ Comment[sv]=Sök efter rum i NeoChat
Comment[ta]=நியோச்சாட்டில் அரங்குகளை கண்டுபிடிக்கும் Comment[ta]=நியோச்சாட்டில் அரங்குகளை கண்டுபிடிக்கும்
Comment[tr]=NeoChatte odalar bulun Comment[tr]=NeoChatte odalar bulun
Comment[uk]=Пошук кімнат у NeoChat Comment[uk]=Пошук кімнат у NeoChat
Comment[x-test]=xxFind rooms in NeoChatxx
Comment[zh_CN]=在 NeoChat 查找聊天室 Comment[zh_CN]=在 NeoChat 查找聊天室
Comment[zh_TW]=在 NeoChat 尋找聊天室 Comment[zh_TW]=在 NeoChat 尋找聊天室
X-KDE-ServiceTypes=Plasma/Runner X-KDE-ServiceTypes=Plasma/Runner

View File

@@ -61,7 +61,7 @@ Kirigami.Dialog {
} }
onClicked: { onClicked: {
((root.QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat.login', 'WelcomePage'), {}, { pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.login', 'WelcomePage'), {}, {
title: i18nc("@title:window", "Login") title: i18nc("@title:window", "Login")
}); });
root.close(); root.close();
@@ -95,8 +95,8 @@ Kirigami.Dialog {
accountView.decrementCurrentIndex(); accountView.decrementCurrentIndex();
} }
} }
Keys.onEnterPressed: ((accountView.currentItem ?? accountView.footerItem) as Delegates.RoundedItemDelegate).clicked() Keys.onEnterPressed: (accountView.currentItem as Delegates.RoundedItemDelegate).clicked()
Keys.onReturnPressed: ((accountView.currentItem ?? accountView.footerItem) as Delegates.RoundedItemDelegate).clicked() Keys.onReturnPressed: (accountView.currentItem as Delegates.RoundedItemDelegate).clicked()
onVisibleChanged: { onVisibleChanged: {
for (let i = 0; i < accountView.count; i++) { for (let i = 0; i < accountView.count; i++) {

View File

@@ -91,7 +91,6 @@ Components.AbstractMaximizeComponent {
color: Kirigami.Theme.textColor color: Kirigami.Theme.textColor
font.family: "monospace" font.family: "monospace"
font.pointSize: Kirigami.Theme.defaultFont.pointSize * NeoChatConfig.fontScale
Kirigami.SpellCheck.enabled: false Kirigami.SpellCheck.enabled: false

View File

@@ -17,7 +17,7 @@ ApplicationWindow {
property real longitude: NaN property real longitude: NaN
property string asset property string asset
property var author property var author
property LiveLocationsModel liveLocationModel: null property QtObject liveLocationModel: null
flags: Qt.FramelessWindowHint | Qt.WA_TranslucentBackground flags: Qt.FramelessWindowHint | Qt.WA_TranslucentBackground
visibility: Qt.WindowFullScreen visibility: Qt.WindowFullScreen
@@ -59,7 +59,7 @@ ApplicationWindow {
Connections { Connections {
target: mapView.map target: mapView.map
function onCopyrightLinkActivated(link: string) { function onCopyrightLinkActivated() {
Qt.openUrlExternally(link); Qt.openUrlExternally(link);
} }
} }

View File

@@ -22,12 +22,12 @@ Labs.MenuBar {
Labs.MenuItem { Labs.MenuItem {
icon.name: "list-add-user" icon.name: "list-add-user"
text: i18nc("@action:inmenu", "Find User") text: i18nc("@action:inmenu", "Find your Friends")
enabled: root.connection enabled: root.connection
onTriggered: root.appWindow.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), { onTriggered: root.appWindow.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
connection: root.connection connection: root.connection
}, { }, {
title: i18nc("@title", "Find User") title: i18nc("@title", "Find your friends")
}) })
} }
Labs.MenuItem { Labs.MenuItem {

View File

@@ -3,10 +3,8 @@
import QtQuick import QtQuick
import org.kde.kirigami as Kirigami
import org.kde.neochat import org.kde.neochat
Item { Item {
required property NeoChatConnection connection required property NeoChatConnection connection
required property Kirigami.ApplicationWindow appWindow
} }

View File

@@ -66,7 +66,7 @@ Kirigami.Dialog {
text: i18nc("@action:button", "Join room") text: i18nc("@action:button", "Join room")
icon.name: "irc-join-channel" icon.name: "irc-join-channel"
onClicked: { onClicked: {
RoomManager.resolveResource(root.room, "join_confirmed"); RoomManager.resolveResource(root.room, "join");
root.close(); root.close();
} }
} }

View File

@@ -171,8 +171,7 @@ Kirigami.Page {
return ""; return "";
} }
} }
isDone: root.session.state === KeyVerificationSession.DONE onDone: root.QQC2.Window.window.close()
onDone: root.closeDialog()
} }
} }

View File

@@ -100,8 +100,7 @@ Kirigami.ApplicationWindow {
function onCurrentRoomChanged() { function onCurrentRoomChanged() {
if (RoomManager.currentRoom && root.pageStack.depth <= 1 && root.initialized && Kirigami.Settings.isMobile) { if (RoomManager.currentRoom && root.pageStack.depth <= 1 && root.initialized && Kirigami.Settings.isMobile) {
let roomPage = pageStack.push(Qt.createComponent('org.kde.neochat', 'RoomPage')); let roomPage = root.pageStack.layers.push(Qt.createComponent('org.kde.neochat', 'RoomPage'));
roomPage.forceActiveFocus();
roomPage.backRequested.connect(event => { roomPage.backRequested.connect(event => {
RoomManager.clearCurrentRoom(); RoomManager.clearCurrentRoom();
}); });
@@ -359,11 +358,7 @@ Kirigami.ApplicationWindow {
user: user, user: user,
connection: root.connection, connection: root.connection,
}) as UserDetailDialog; }) as UserDetailDialog;
// FIXME: The reason why we don't want the focusedWindowItem for the room null case (aka QR codes) is because it will parent it to the soon-to-be-destroyed window item. dialog.parent = QmlUtils.focusedWindowItem(); // Kirigami Dialogs overwrite the parent, so we need to set it again
// But this won't be a problem if we turn it into a Kirigami.Dialog or some other in-scene item, which it really should be.
if (room != null) {
dialog.parent = QmlUtils.focusedWindowItem(); // Kirigami Dialogs overwrite the parent, so we need to set it again
}
dialog.open(); dialog.open();
} }

View File

@@ -1,22 +0,0 @@
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-only
import QtQuick
import org.kde.kirigami as Kirigami
Kirigami.PromptDialog {
id: root
required property bool hasExistingMeeting
title: hasExistingMeeting ? i18nc("@title", "Join Meeting") : i18nc("@title", "Start Meeting")
subtitle: hasExistingMeeting ? i18nc("@info:label", "You are about to join a Jitsi meeting in your web browser.") : i18nc("@info:label", "You are about to start a new Jitsi meeting in your web browser.")
standardButtons: Kirigami.Dialog.Cancel
customFooterActions: Kirigami.Action {
icon.name: "camera-video-symbolic"
text: hasExistingMeeting ? i18nc("@action:button Join the Jitsi meeting", "Join") : i18nc("@action:button Start a new Jitsi meeting", "Start")
onTriggered: root.accept()
}
}

View File

@@ -1,8 +1,6 @@
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com> // SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
pragma ComponentBehavior: Bound
import QtCore as Core import QtCore as Core
import QtQuick import QtQuick
import QtQuick.Controls as QQC2 import QtQuick.Controls as QQC2
@@ -23,13 +21,13 @@ Components.AlbumMaximizeComponent {
*/ */
required property NeoChatRoom currentRoom required property NeoChatRoom currentRoom
readonly property string currentEventId: model.data(model.index((content as ListView).currentIndex, 0), TimelineMessageModel.EventIdRole) readonly property string currentEventId: model.data(model.index(content.currentIndex, 0), TimelineMessageModel.EventIdRole)
readonly property var currentAuthor: model.data(model.index((content as ListView).currentIndex, 0), TimelineMessageModel.AuthorRole) readonly property var currentAuthor: model.data(model.index(content.currentIndex, 0), TimelineMessageModel.AuthorRole)
readonly property var currentTime: model.data(model.index((content as ListView).currentIndex, 0), TimelineMessageModel.TimeRole) readonly property var currentTime: model.data(model.index(content.currentIndex, 0), TimelineMessageModel.TimeRole)
readonly property var currentProgressInfo: model.data(model.index((content as ListView).currentIndex, 0), TimelineMessageModel.ProgressInfoRole) readonly property var currentProgressInfo: model.data(model.index(content.currentIndex, 0), TimelineMessageModel.ProgressInfoRole)
actions: [ actions: [
ShareAction { ShareAction {
@@ -61,28 +59,28 @@ Components.AlbumMaximizeComponent {
downloadAction: Components.DownloadAction { downloadAction: Components.DownloadAction {
onTriggered: { onTriggered: {
root.currentRoom.downloadFile(root.currentEventId, Core.StandardPaths.writableLocation(Core.StandardPaths.CacheLocation) + "/" + root.currentEventId.replace(":", "_").replace("/", "_").replace("+", "_") + root.currentRoom.fileNameToDownload(root.currentEventId)); currentRoom.downloadFile(root.currentEventId, Core.StandardPaths.writableLocation(Core.StandardPaths.CacheLocation) + "/" + root.currentEventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(root.currentEventId));
} }
} }
playAction: Kirigami.Action { playAction: Kirigami.Action {
onTriggered: { onTriggered: {
MediaManager.startPlayback(); MediaManager.startPlayback();
(root.currentItem as Components.VideoMaximizeDelegate).play(); currentItem.play();
} }
} }
Connections { Connections {
target: MediaManager target: MediaManager
function onPlaybackStarted() { function onPlaybackStarted() {
if ((root.currentItem as Components.VideoMaximizeDelegate).playbackState === MediaPlayer.PlayingState) { if (currentItem.playbackState === MediaPlayer.PlayingState) {
(root.currentItem as Components.VideoMaximizeDelegate).pause(); currentItem.pause();
} }
} }
} }
Connections { Connections {
target: root.currentRoom target: currentRoom
function onFileTransferProgress(id, progress, total) { function onFileTransferProgress(id, progress, total) {
if (id == root.currentEventId) { if (id == root.currentEventId) {
@@ -125,7 +123,7 @@ Components.AlbumMaximizeComponent {
onItemRightClicked: RoomManager.viewEventMenu(root.currentEventId, root.currentRoom) onItemRightClicked: RoomManager.viewEventMenu(root.currentEventId, root.currentRoom)
onSaveItem: { onSaveItem: {
var dialog = saveAsDialog.createObject(QQC2.Overlay.overlay) as Dialogs.FileDialog; var dialog = saveAsDialog.createObject(QQC2.Overlay.overlay);
dialog.selectedFile = currentRoom.fileNameToDownload(root.currentEventId); dialog.selectedFile = currentRoom.fileNameToDownload(root.currentEventId);
dialog.open(); dialog.open();
} }
@@ -148,7 +146,7 @@ Components.AlbumMaximizeComponent {
if (!selectedFile) { if (!selectedFile) {
return; return;
} }
root.currentRoom.downloadFile(root.currentEventId, selectedFile); currentRoom.downloadFile(root.currentEventId, selectedFile);
} }
} }
} }

View File

@@ -27,26 +27,9 @@ Kirigami.Page {
} }
} }
MediaDevices {
id: devices
}
Rectangle {
anchors.fill: parent
color: Kirigami.Theme.backgroundColor
visible: devices.videoInputs.length === 0
Kirigami.PlaceholderMessage {
text: i18nc("@info", "No Camera Connected")
anchors.centerIn: parent
}
}
VideoOutput { VideoOutput {
id: viewFinder id: viewFinder
anchors.centerIn: parent anchors.centerIn: parent
visible: devices.videoInputs.length > 0
} }
Prison.VideoScanner { Prison.VideoScanner {
@@ -64,8 +47,6 @@ Kirigami.Page {
} }
CaptureSession { CaptureSession {
id: session
camera: Camera { camera: Camera {
id: camera id: camera
} }

View File

@@ -7,7 +7,6 @@ pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Controls as QQC2 import QtQuick.Controls as QQC2
import QtQuick.Window import QtQuick.Window
import QtQuick.Layouts
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
@@ -59,59 +58,11 @@ Kirigami.Page {
*/ */
property MediaMessageFilterModel mediaMessageFilterModel: RoomManager.mediaMessageFilterModel property MediaMessageFilterModel mediaMessageFilterModel: RoomManager.mediaMessageFilterModel
/**
* @brief The WidgetModel to use.
*
* This model has the list of widgets available in the current room.
*
* @note For loading a room in a different window, override this with a new
* WidgetModel.
*
* @sa WidgetModel
*/
property WidgetModel widgetModel: RoomManager.widgetModel
title: root.currentRoom ? root.currentRoom.displayName : "" title: root.currentRoom ? root.currentRoom.displayName : ""
focus: true focus: true
padding: 0 padding: 0
actions: [ actions: [
Kirigami.Action {
id: jitsiMeetingAction
readonly property bool hasExistingMeeting: root.widgetModel.jitsiIndex >= 0
readonly property bool canStartNewMeeting: root.currentRoom.canSendState("im.vector.modular.widgets")
tooltip: {
if (hasExistingMeeting) {
return i18nc("@action:button", "Join Jitsi meeting…");
}
return canStartNewMeeting ? i18nc("@action:button", "Start Jitsi meeting…") : i18nc("@action:button", "You do not have permissions to start Jitsi meetings")
}
icon {
name: "camera-video-symbolic"
color: hasExistingMeeting ? Kirigami.Theme.highlightColor : "transparent"
}
enabled: hasExistingMeeting || canStartNewMeeting
visible: root.currentRoom && !root.currentRoom.isSpace
onTriggered: {
const dialog = Qt.createComponent("org.kde.neochat", "MeetingDialog").createObject(QQC2.Overlay.overlay, { hasExistingMeeting });
dialog.onAccepted.connect(doAction);
dialog.open();
}
function doAction(): void {
let url;
if (!hasExistingMeeting) {
url = root.widgetModel.addJitsiConference();
} else {
let idx = root.widgetModel.index(root.widgetModel.jitsiIndex, 0);
url = root.widgetModel.data(idx, WidgetModel.UrlRole);
}
Qt.openUrlExternally(url);
}
},
Kirigami.Action { Kirigami.Action {
visible: Kirigami.Settings.isMobile || !(root.Kirigami.PageStack.pageStack as Kirigami.PageRow).wideMode visible: Kirigami.Settings.isMobile || !(root.Kirigami.PageStack.pageStack as Kirigami.PageRow).wideMode
icon.name: "view-right-new" icon.name: "view-right-new"
@@ -119,18 +70,6 @@ Kirigami.Page {
} }
] ]
Kirigami.Action {
enabled: root.currentRoom && !root.currentRoom.isSpace
shortcut: "Ctrl+F"
onTriggered: {
((root.QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'RoomSearchPage'), {
room: root.currentRoom
}, {
title: i18nc("@action:title", "Search")
});
}
}
KeyNavigation.left: (root.Kirigami.PageStack.pageStack as Kirigami.PageRow).get(0) KeyNavigation.left: (root.Kirigami.PageStack.pageStack as Kirigami.PageRow).get(0)
onCurrentRoomChanged: { onCurrentRoomChanged: {
@@ -159,87 +98,24 @@ Kirigami.Page {
} }
} }
header: ColumnLayout { header: Kirigami.InlineMessage {
id: headerLayout id: banner
spacing: 0 // Used to keep track of messages so we can hide the right one at the right time
property string messageId
readonly property bool shouldShowPins: root.currentRoom.pinnedMessage.length > 0 && !Kirigami.Settings.isMobile showCloseButton: true
visible: false
position: Kirigami.InlineMessage.Position.Header
QQC2.Control { function show(msgid: string): void {
id: pinControl messageId = msgid;
visible = true;
visible: headerLayout.shouldShowPins
Layout.fillWidth: true
background: Rectangle {
color: Kirigami.Theme.backgroundColor
Kirigami.Theme.colorSet: Kirigami.Theme.View
Kirigami.Theme.inherit: false
}
contentItem: RowLayout {
spacing: Kirigami.Units.smallSpacing
Kirigami.Icon {
source: "pin-symbolic"
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
}
QQC2.Label {
text: root.currentRoom.pinnedMessage
maximumLineCount: 1
elide: Text.ElideRight
onLinkActivated: link => UrlHelper.openUrl(link)
onHoveredLinkChanged: if (hoveredLink.length > 0 && hoveredLink !== "1") {
(QQC2.ApplicationWindow.window as Main).hoverLinkIndicator.text = hoveredLink;
} else {
(QQC2.ApplicationWindow.window as Main).hoverLinkIndicator.text = "";
}
Layout.fillWidth: true
}
}
TapHandler {
onTapped: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'RoomPinnedMessagesPage'), {
room: root.currentRoom
}, {
title: i18nc("@title", "Pinned Messages")
});
}
} }
Kirigami.Separator { function hideIf(msgid: string): void {
visible: headerLayout.shouldShowPins if (messageId == msgid) {
visible = false;
Layout.fillWidth: true
}
Kirigami.InlineMessage {
id: banner
// Used to keep track of messages so we can hide the right one at the right time
property string messageId
showCloseButton: true
visible: false
position: Kirigami.InlineMessage.Position.Header
function show(msgid: string): void {
messageId = msgid;
visible = true;
}
function hideIf(msgid: string): void {
if (messageId == msgid) {
visible = false;
}
} }
} }
} }
@@ -286,6 +162,12 @@ Kirigami.Page {
} }
} }
background: Rectangle {
Kirigami.Theme.colorSet: Kirigami.Theme.View
Kirigami.Theme.inherit: false
color: NeoChatConfig.compactLayout ? Kirigami.Theme.backgroundColor : "transparent"
}
footer: Loader { footer: Loader {
id: chatBarLoader id: chatBarLoader
height: active ? (item as ChatBar).implicitHeight : 0 height: active ? (item as ChatBar).implicitHeight : 0

View File

@@ -30,7 +30,7 @@ SearchPage {
*/ */
required property NeoChatConnection connection required property NeoChatConnection connection
title: i18nc("@action:title", "Find User") title: i18nc("@action:title", "Find Your Friends")
Component.onCompleted: focusSearch() Component.onCompleted: focusSearch()
@@ -53,16 +53,6 @@ SearchPage {
root.closeDialog(); root.closeDialog();
} }
QQC2.ContextMenu.menu: QQC2.Menu {
QQC2.MenuItem {
text: i18nc("@action:inmenu", "Copy User ID")
icon.name: "username-copy"
onTriggered: {
Clipboard.saveText(userDelegate.userId)
}
}
}
contentItem: RowLayout { contentItem: RowLayout {
spacing: Kirigami.Units.smallSpacing spacing: Kirigami.Units.smallSpacing
@@ -81,7 +71,7 @@ SearchPage {
} }
QQC2.Label { QQC2.Label {
visible: userDelegate.directChatExists visible: userDelegate.directChatExists
text: i18nc("@info", "Direct Messages") text: i18nc("@info", "Friends")
textFormat: Text.PlainText textFormat: Text.PlainText
color: Kirigami.Theme.positiveTextColor color: Kirigami.Theme.positiveTextColor
} }

View File

@@ -11,19 +11,7 @@ VerificationMessage {
required property int reason required property int reason
icon: { icon: "security-low"
switch (root.reason) {
case KeyVerificationSession.TIMEOUT:
case KeyVerificationSession.REMOTE_TIMEOUT:
case KeyVerificationSession.USER:
case KeyVerificationSession.REMOTE_USER:
case KeyVerificationSession.SESSION_ACCEPTED:
case KeyVerificationSession.REMOTE_SESSION_ACCEPTED:
return "dialog-information";
default:
return "security-low";
}
}
text: { text: {
switch (root.reason) { switch (root.reason) {
case KeyVerificationSession.NONE: case KeyVerificationSession.NONE:

View File

@@ -13,9 +13,6 @@ ColumnLayout {
required property string icon required property string icon
required property string text required property string text
required property bool isDone
signal done
anchors.fill: parent anchors.fill: parent
@@ -35,14 +32,6 @@ ColumnLayout {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap wrapMode: Text.Wrap
} }
QQC2.Button {
text: i18nc("@action:button Done, we are finished with verification", "Done")
visible: root.isDone
onClicked: root.done()
Layout.topMargin: Kirigami.Units.largeSpacing
Layout.alignment: Qt.AlignHCenter
}
Item { Item {
Layout.fillHeight: true Layout.fillHeight: true
} }

View File

@@ -9,6 +9,8 @@
#include "controller.h" #include "controller.h"
#include "eventhandler.h" #include "eventhandler.h"
#include "models/actionsmodel.h" #include "models/actionsmodel.h"
#include "models/messagefiltermodel.h"
#include "models/sortfilterroomtreemodel.h"
#include "neochatconfig.h" #include "neochatconfig.h"
#include "neochatconnection.h" #include "neochatconnection.h"
#include "neochatroom.h" #include "neochatroom.h"
@@ -41,7 +43,6 @@ RoomManager::RoomManager(QObject *parent)
, m_messageFilterModel(new MessageFilterModel(this, m_timelineModel)) , m_messageFilterModel(new MessageFilterModel(this, m_timelineModel))
, m_mediaMessageFilterModel(new MediaMessageFilterModel(this, m_messageFilterModel)) , m_mediaMessageFilterModel(new MediaMessageFilterModel(this, m_messageFilterModel))
, m_userListModel(new UserListModel(this)) , m_userListModel(new UserListModel(this))
, m_widgetModel(new WidgetModel(this))
{ {
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(UBUNTU_TOUCH) #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(UBUNTU_TOUCH)
m_isMobile = true; m_isMobile = true;
@@ -54,7 +55,6 @@ RoomManager::RoomManager(QObject *parent)
#endif #endif
connect(this, &RoomManager::currentRoomChanged, this, [this]() { connect(this, &RoomManager::currentRoomChanged, this, [this]() {
m_widgetModel->setRoom(m_currentRoom);
m_userListModel->setRoom(m_currentRoom); m_userListModel->setRoom(m_currentRoom);
m_timelineModel->setRoom(m_currentRoom); m_timelineModel->setRoom(m_currentRoom);
m_sortFilterRoomTreeModel->setCurrentRoom(m_currentRoom); m_sortFilterRoomTreeModel->setCurrentRoom(m_currentRoom);
@@ -197,11 +197,6 @@ void RoomManager::activateUserModel()
m_userListModel->activate(); m_userListModel->activate();
} }
WidgetModel *RoomManager::widgetModel() const
{
return m_widgetModel;
}
void RoomManager::resolveResource(const QString &idOrUri, const QString &action) void RoomManager::resolveResource(const QString &idOrUri, const QString &action)
{ {
resolveResource(Uri{idOrUri}, action); resolveResource(Uri{idOrUri}, action);
@@ -214,35 +209,24 @@ void RoomManager::resolveResource(Uri uri, const QString &action)
return; return;
} }
if (action == "qr"_L1) { if (uri.type() == Uri::NonMatrix && action == "qr"_L1) {
if (uri.type() == Uri::NonMatrix) { Q_EMIT externalUrl(uri.toUrl());
Q_EMIT externalUrl(uri.toUrl()); return;
return;
}
if (uri.type() != Uri::UserId) {
uri.setAction(QStringLiteral("join"));
} else {
uri.setAction(action);
}
} }
// For matrix URIs:
if (uri.type() != Uri::NonMatrix) { if (uri.type() != Uri::NonMatrix) {
if (!m_connection) { if (!m_connection) {
return; return;
} }
// Once a join is confirmed, set the action to "join" so it skips the confirmation check. if (!action.isEmpty() && (uri.type() != Uri::UserId || action != "join"_L1)) {
if (action == "join_confirmed"_L1) { uri.setAction(action);
uri.setAction(QStringLiteral("join"));
} }
// TODO we should allow the user to select a connection. // TODO we should allow the user to select a connection.
} }
const auto result = visitResource(m_connection, uri); const auto result = visitResource(m_connection, uri);
// If we are not already in the room:
if (result == Quotient::CouldNotResolve) { if (result == Quotient::CouldNotResolve) {
if ((uri.type() == Uri::RoomAlias || uri.type() == Uri::RoomId) && action == "join"_L1) { if ((uri.type() == Uri::RoomAlias || uri.type() == Uri::RoomId) && action != "no_join"_L1) {
Q_EMIT askJoinRoom(uri.primaryId()); Q_EMIT askJoinRoom(uri.primaryId());
} }
} }
@@ -519,14 +503,7 @@ void RoomManager::setCurrentSpace(const QString &spaceId, bool setRoom)
// This need to happen before the signal so TreeView.expandRecursively() can work nicely. // This need to happen before the signal so TreeView.expandRecursively() can work nicely.
m_sortFilterRoomTreeModel->setActiveSpaceId(m_currentSpaceId); m_sortFilterRoomTreeModel->setActiveSpaceId(m_currentSpaceId);
m_sortFilterRoomTreeModel->setMode(m_currentSpaceId == u"DM"_s ? SortFilterRoomTreeModel::DirectChats : SortFilterRoomTreeModel::Rooms);
if (m_currentSpaceId == u"DM") {
m_sortFilterRoomTreeModel->setMode(SortFilterRoomTreeModel::DirectChats);
} else if (m_currentSpaceId.isEmpty()) {
m_sortFilterRoomTreeModel->setMode(SortFilterRoomTreeModel::Rooms);
} else {
m_sortFilterRoomTreeModel->setMode(SortFilterRoomTreeModel::All);
}
Q_EMIT currentSpaceChanged(); Q_EMIT currentSpaceChanged();
if (m_connection) { if (m_connection) {
@@ -554,45 +531,6 @@ void RoomManager::setCurrentSpace(const QString &spaceId, bool setRoom)
setCurrentRoom({}); setCurrentRoom({});
} }
QString RoomManager::findSpaceIdForCurrentRoom() const
{
if (!m_currentRoom) {
return m_currentSpaceId;
}
if (m_currentRoom->isDirectChat()) {
const auto roomsInSpace = SpaceHierarchyCache::instance().getRoomListForSpace(m_currentSpaceId, false);
if (roomsInSpace.contains(m_currentRoom->id())) {
return m_currentSpaceId;
}
return "DM"_L1;
}
const auto &parentSpaces = SpaceHierarchyCache::instance().parentSpaces(m_currentRoom->id());
if (parentSpaces.contains(m_currentSpaceId)) {
return m_currentSpaceId;
}
static auto config = NeoChatConfig::self();
if (config->allRoomsInHome()) {
return {};
}
if (const auto &parent = m_connection->room(m_currentRoom->canonicalParent())) {
for (const auto &parentParent : SpaceHierarchyCache::instance().parentSpaces(parent->id())) {
if (SpaceHierarchyCache::instance().parentSpaces(parentParent).isEmpty()) {
return parentParent;
}
}
return parent->id();
}
for (const auto &space : parentSpaces) {
if (SpaceHierarchyCache::instance().parentSpaces(space).isEmpty()) {
return space;
}
}
if (m_currentRoom->isSpace()) {
return m_currentSpaceId;
}
return {};
}
void RoomManager::setCurrentRoom(const QString &roomId) void RoomManager::setCurrentRoom(const QString &roomId)
{ {
if (m_currentRoom != nullptr) { if (m_currentRoom != nullptr) {
@@ -610,23 +548,56 @@ void RoomManager::setCurrentRoom(const QString &roomId)
} }
Q_EMIT currentRoomChanged(); Q_EMIT currentRoomChanged();
if (m_connection) {
if (roomId.isEmpty()) {
m_lastRoomConfig.deleteEntry(m_currentSpaceId);
} else {
// We can't have empty keys in KConfig, so name it "Home"
if (m_currentSpaceId.isEmpty()) {
m_lastRoomConfig.writeEntry(u"Home"_s, roomId);
} else {
m_lastRoomConfig.writeEntry(m_currentSpaceId, roomId);
}
}
}
if (roomId.isEmpty()) { if (roomId.isEmpty()) {
m_lastRoomConfig.deleteEntry(m_currentSpaceId);
return; return;
} }
if (m_currentRoom->isSpace()) {
const auto spaceIdForRoom = findSpaceIdForCurrentRoom(); return;
// We can't have empty keys in KConfig, so name it "Home"
if (spaceIdForRoom.isEmpty()) {
m_lastRoomConfig.writeEntry(u"Home"_s, roomId);
} else {
m_lastRoomConfig.writeEntry(spaceIdForRoom, roomId);
} }
if (m_currentRoom->isDirectChat()) {
if (m_currentSpaceId != spaceIdForRoom) { if (m_currentSpaceId != "DM"_L1) {
setCurrentSpace(spaceIdForRoom, false); setCurrentSpace("DM"_L1, false);
}
return;
} }
const auto &parentSpaces = SpaceHierarchyCache::instance().parentSpaces(roomId);
if (parentSpaces.contains(m_currentSpaceId)) {
return;
}
static auto config = NeoChatConfig::self();
if (config->allRoomsInHome()) {
setCurrentSpace({}, false);
return;
}
if (const auto &parent = m_connection->room(m_currentRoom->canonicalParent())) {
for (const auto &parentParent : SpaceHierarchyCache::instance().parentSpaces(parent->id())) {
if (SpaceHierarchyCache::instance().parentSpaces(parentParent).isEmpty()) {
setCurrentSpace(parentParent, false);
return;
}
}
setCurrentSpace(parent->id(), false);
return;
}
for (const auto &space : parentSpaces) {
if (SpaceHierarchyCache::instance().parentSpaces(space).isEmpty()) {
setCurrentSpace(space, false);
return;
}
}
setCurrentSpace({}, false);
} }
void RoomManager::clearCurrentRoom() void RoomManager::clearCurrentRoom()

View File

@@ -22,7 +22,6 @@
#include "models/sortfilterspacelistmodel.h" #include "models/sortfilterspacelistmodel.h"
#include "models/timelinemodel.h" #include "models/timelinemodel.h"
#include "models/userlistmodel.h" #include "models/userlistmodel.h"
#include "models/widgetmodel.h"
#include "neochatroommember.h" #include "neochatroommember.h"
class NeoChatRoom; class NeoChatRoom;
@@ -130,14 +129,6 @@ class RoomManager : public QObject, public UriResolverBase
*/ */
Q_PROPERTY(UserListModel *userListModel READ userListModel CONSTANT) Q_PROPERTY(UserListModel *userListModel READ userListModel CONSTANT)
/**
* @brief The WidgetModel that should be used for room widget visualisation.
*
* @note Available here so that the room page and drawer both have access to the
* same model.
*/
Q_PROPERTY(WidgetModel *widgetModel READ widgetModel CONSTANT)
/** /**
* @brief Whether a room is currently open in NeoChat. * @brief Whether a room is currently open in NeoChat.
* *
@@ -169,8 +160,6 @@ public:
UserListModel *userListModel() const; UserListModel *userListModel() const;
Q_INVOKABLE void activateUserModel(); Q_INVOKABLE void activateUserModel();
WidgetModel *widgetModel() const;
/** /**
* @brief Resolve the given resource. * @brief Resolve the given resource.
* *
@@ -367,21 +356,11 @@ private:
MediaMessageFilterModel *m_mediaMessageFilterModel; MediaMessageFilterModel *m_mediaMessageFilterModel;
UserListModel *m_userListModel; UserListModel *m_userListModel;
WidgetModel *m_widgetModel;
QPointer<NeoChatConnection> m_connection; QPointer<NeoChatConnection> m_connection;
void setCurrentRoom(const QString &roomId); void setCurrentRoom(const QString &roomId);
/**
* @brief Find the most appropriate space for the currently selected room
*
* Should be used to figure out what space to switch to after a room change.
*
* @return The Space ID that the currently set room should be displayed as part of. (or "DM" for DM and "" for Home)
*/
QString findSpaceIdForCurrentRoom() const;
// Space ID, "DM", or empty string // Space ID, "DM", or empty string
void setCurrentSpace(const QString &spaceId, bool setRoom = true); void setCurrentSpace(const QString &spaceId, bool setRoom = true);

View File

@@ -8,6 +8,8 @@
#include <KWindowSystem> #include <KWindowSystem>
#include "controller.h" #include "controller.h"
#include "models/roomlistmodel.h"
#include "models/sortfilterroomlistmodel.h"
#include "roommanager.h" #include "roommanager.h"
#include "windowcontroller.h" #include "windowcontroller.h"

View File

@@ -2,8 +2,6 @@
// SPDX-FileCopyrightText: 2020 Noah Davis <noahadvs@gmail.com> // SPDX-FileCopyrightText: 2020 Noah Davis <noahadvs@gmail.com>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
pragma ComponentBehavior: Bound
import QtCore import QtCore
import QtQuick import QtQuick
import QtQuick.Controls as QQC2 import QtQuick.Controls as QQC2
@@ -89,15 +87,9 @@ QQC2.Control {
displayHint: Kirigami.DisplayHint.IconOnly displayHint: Kirigami.DisplayHint.IconOnly
onTriggered: { onTriggered: {
if (Clipboard.hasImage) { let dialog = (Clipboard.hasImage ? attachDialog : openFileDialog).createObject(root.QQC2.Overlay.overlay);
let dialog = attachDialog.createObject(root.QQC2.Overlay.overlay) as AttachDialog; dialog.chosen.connect(path => _private.chatBarCache.attachmentPath = path);
dialog.chosen.connect(path => _private.chatBarCache.attachmentPath = path); dialog.open();
dialog.open();
} else {
let dialog = openFileDialog.createObject(root.QQC2.Overlay.overlay) as OpenFileDialog;
dialog.chosen.connect(path => _private.chatBarCache.attachmentPath = path);
dialog.open();
}
} }
tooltip: text tooltip: text
@@ -130,9 +122,9 @@ QQC2.Control {
displayHint: QQC2.AbstractButton.IconOnly displayHint: QQC2.AbstractButton.IconOnly
onTriggered: { onTriggered: {
(locationChooser.createObject(QQC2.Overlay.overlay, { locationChooser.createObject(QQC2.Overlay.overlay, {
room: root.currentRoom room: root.currentRoom
}) as LocationChooser).open(); }).open();
} }
tooltip: text tooltip: text
}, },
@@ -144,9 +136,9 @@ QQC2.Control {
displayHint: QQC2.AbstractButton.IconOnly displayHint: QQC2.AbstractButton.IconOnly
onTriggered: { onTriggered: {
(newPollDialog.createObject(QQC2.Overlay.overlay, { newPollDialog.createObject(QQC2.Overlay.overlay, {
room: root.currentRoom room: root.currentRoom
}) as NewPollDialog).open(); }).open();
} }
tooltip: text tooltip: text
}, },
@@ -263,7 +255,6 @@ QQC2.Control {
wrapMode: TextEdit.Wrap wrapMode: TextEdit.Wrap
// This has to stay PlainText or else formatting starts breaking in strange ways // This has to stay PlainText or else formatting starts breaking in strange ways
textFormat: TextEdit.PlainText textFormat: TextEdit.PlainText
font.pointSize: Kirigami.Theme.defaultFont.pointSize * NeoChatConfig.fontScale
Accessible.description: placeholderText Accessible.description: placeholderText
@@ -376,9 +367,7 @@ QQC2.Control {
id: actionDelegate id: actionDelegate
required property BusyAction modelData required property BusyAction modelData
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: if (!pieProgress.visible) { onClicked: modelData.trigger()
modelData.trigger()
}
padding: Kirigami.Units.smallSpacing padding: Kirigami.Units.smallSpacing
@@ -386,9 +375,7 @@ QQC2.Control {
QQC2.ToolTip.text: modelData.tooltip QQC2.ToolTip.text: modelData.tooltip
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
PieProgressBar { contentItem: PieProgressBar {
id: pieProgress
anchors.fill: parent
visible: actionDelegate.modelData.isBusy visible: actionDelegate.modelData.isBusy
progress: root.currentRoom.fileUploadingProgress progress: root.currentRoom.fileUploadingProgress
} }
@@ -415,7 +402,7 @@ QQC2.Control {
ReplyComponent { ReplyComponent {
id: replyComponent id: replyComponent
replyContentModel: ContentProvider.contentModelForEvent(root.currentRoom, _private.chatBarCache.replyId, true) replyContentModel: ContentProvider.contentModelForEvent(root.currentRoom, _private.chatBarCache.replyId, true)
Message.maxContentWidth: (replyLoader.item as Item).width Message.maxContentWidth: replyLoader.item.width
// When the user replies to a message and the preview is loaded, make sure the text field is focused again // When the user replies to a message and the preview is loaded, make sure the text field is focused again
Component.onCompleted: textField.forceActiveFocus(Qt.OtherFocusReason) Component.onCompleted: textField.forceActiveFocus(Qt.OtherFocusReason)

View File

@@ -4,7 +4,6 @@
import QtQuick import QtQuick
import QtQuick.Controls as QQC2 import QtQuick.Controls as QQC2
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.neochat
QQC2.ItemDelegate { QQC2.ItemDelegate {
id: root id: root
@@ -30,7 +29,6 @@ QQC2.ItemDelegate {
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
font.family: "emoji" font.family: "emoji"
font.pointSize: Kirigami.Theme.defaultFont.pointSize * NeoChatConfig.fontScale
Kirigami.Icon { Kirigami.Icon {
width: Kirigami.Units.gridUnit * 0.5 width: Kirigami.Units.gridUnit * 0.5

View File

@@ -148,7 +148,7 @@ ColumnLayout {
id: quickReactions id: quickReactions
Layout.fillWidth: true Layout.fillWidth: true
model: ["👍", "👎", "😄", "🎉", "😕", "❤", "🚀", "👀"] model: ["👍", "👎", "😄", "🎉", "😕", "❤", "🚀", "👀"]
delegate: EmojiDelegate { delegate: EmojiDelegate {
required property string modelData required property string modelData

View File

@@ -3,13 +3,14 @@
import QtQuick import QtQuick
import QtQuick.Controls as QQC2 import QtQuick.Controls as QQC2
import QtQuick.Layouts
import QtCore as Core import QtCore as Core
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import org.kde.kquickimageeditor as KQuickImageEditor import org.kde.kquickimageeditor as KQuickImageEditor
Kirigami.Page { Kirigami.Page {
id: root id: rootEditorView
property bool resizing: false property bool resizing: false
required property string imagePath required property string imagePath
@@ -25,7 +26,7 @@ Kirigami.Page {
function crop() { function crop() {
const ratioX = editImage.paintedWidth / editImage.nativeWidth; const ratioX = editImage.paintedWidth / editImage.nativeWidth;
const ratioY = editImage.paintedHeight / editImage.nativeHeight; const ratioY = editImage.paintedHeight / editImage.nativeHeight;
root.resizing = false; rootEditorView.resizing = false;
imageDoc.crop(selectionTool.selectionX / ratioX, selectionTool.selectionY / ratioY, selectionTool.selectionWidth / ratioX, selectionTool.selectionHeight / ratioY); imageDoc.crop(selectionTool.selectionX / ratioX, selectionTool.selectionY / ratioY, selectionTool.selectionWidth / ratioX, selectionTool.selectionHeight / ratioY);
} }
@@ -42,9 +43,9 @@ Kirigami.Page {
text: i18nc("@action:button Accept image modification", "Accept") text: i18nc("@action:button Accept image modification", "Accept")
icon.name: "dialog-ok" icon.name: "dialog-ok"
onTriggered: { onTriggered: {
let newPath = Core.StandardPaths.writableLocation(Core.StandardPaths.CacheLocation) + "/" + (new Date()).getTime() + "." + root.imagePath.split('.').pop(); let newPath = Core.StandardPaths.writableLocation(Core.StandardPaths.CacheLocation) + "/" + (new Date()).getTime() + "." + imagePath.split('.').pop();
if (imageDoc.saveAs(newPath)) { if (imageDoc.saveAs(newPath)) {
root.newPathChanged(newPath); newPathChanged(newPath);
} else { } else {
msg.type = Kirigami.MessageType.Error; msg.type = Kirigami.MessageType.Error;
msg.text = i18n("Unable to save file. Check if you have the correct permission to edit the cache directory."); msg.text = i18n("Unable to save file. Check if you have the correct permission to edit the cache directory.");
@@ -79,12 +80,12 @@ Kirigami.Page {
KQuickImageEditor.ImageDocument { KQuickImageEditor.ImageDocument {
id: imageDoc id: imageDoc
path: root.imagePath path: rootEditorView.imagePath
} }
KQuickImageEditor.SelectionTool { KQuickImageEditor.SelectionTool {
id: selectionTool id: selectionTool
visible: root.resizing visible: rootEditorView.resizing
width: editImage.paintedWidth width: editImage.paintedWidth
height: editImage.paintedHeight height: editImage.paintedHeight
x: editImage.horizontalPadding x: editImage.horizontalPadding
@@ -100,7 +101,7 @@ Kirigami.Page {
Connections { Connections {
target: selectionTool.selectionArea target: selectionTool.selectionArea
function onDoubleClicked() { function onDoubleClicked() {
root.crop(); rootEditorView.crop();
} }
} }
} }
@@ -118,8 +119,8 @@ Kirigami.Page {
display: QQC2.Button.TextBesideIcon display: QQC2.Button.TextBesideIcon
actions: [ actions: [
Kirigami.Action { Kirigami.Action {
icon.name: root.resizing ? "dialog-cancel" : "transform-crop" icon.name: rootEditorView.resizing ? "dialog-cancel" : "transform-crop"
text: root.resizing ? i18n("Cancel") : i18nc("@action:button Crop an image", "Crop") text: rootEditorView.resizing ? i18n("Cancel") : i18nc("@action:button Crop an image", "Crop")
onTriggered: { onTriggered: {
resizeRectangle.width = editImage.paintedWidth; resizeRectangle.width = editImage.paintedWidth;
resizeRectangle.height = editImage.paintedHeight; resizeRectangle.height = editImage.paintedHeight;
@@ -129,38 +130,38 @@ Kirigami.Page {
resizeRectangle.insideY = 100; resizeRectangle.insideY = 100;
resizeRectangle.insideWidth = 100; resizeRectangle.insideWidth = 100;
resizeRectangle.insideHeight = 100; resizeRectangle.insideHeight = 100;
root.resizing = !root.resizing; rootEditorView.resizing = !rootEditorView.resizing;
} }
}, },
Kirigami.Action { Kirigami.Action {
icon.name: "dialog-ok" icon.name: "dialog-ok"
visible: root.resizing visible: rootEditorView.resizing
text: i18nc("@action:button Crop an image", "Crop") text: i18nc("@action:button Crop an image", "Crop")
onTriggered: root.crop() onTriggered: rootEditorView.crop()
}, },
Kirigami.Action { Kirigami.Action {
icon.name: "object-rotate-left" icon.name: "object-rotate-left"
text: i18nc("@action:button Rotate an image to the left", "Rotate left") text: i18nc("@action:button Rotate an image to the left", "Rotate left")
onTriggered: imageDoc.rotate(-90) onTriggered: imageDoc.rotate(-90)
visible: !root.resizing visible: !rootEditorView.resizing
}, },
Kirigami.Action { Kirigami.Action {
icon.name: "object-rotate-right" icon.name: "object-rotate-right"
text: i18nc("@action:button Rotate an image to the right", "Rotate right") text: i18nc("@action:button Rotate an image to the right", "Rotate right")
onTriggered: imageDoc.rotate(90) onTriggered: imageDoc.rotate(90)
visible: !root.resizing visible: !rootEditorView.resizing
}, },
Kirigami.Action { Kirigami.Action {
icon.name: "object-flip-vertical" icon.name: "object-flip-vertical"
text: i18nc("@action:button Mirror an image vertically", "Flip") text: i18nc("@action:button Mirror an image vertically", "Flip")
onTriggered: imageDoc.mirror(false, true) onTriggered: imageDoc.mirror(false, true)
visible: !root.resizing visible: !rootEditorView.resizing
}, },
Kirigami.Action { Kirigami.Action {
icon.name: "object-flip-horizontal" icon.name: "object-flip-horizontal"
text: i18nc("@action:button Mirror an image horizontally", "Mirror") text: i18nc("@action:button Mirror an image horizontally", "Mirror")
onTriggered: imageDoc.mirror(true, false) onTriggered: imageDoc.mirror(true, false)
visible: !root.resizing visible: !rootEditorView.resizing
} }
] ]
} }

View File

@@ -1,8 +1,6 @@
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later // SPDX-License-Identifier: LGPL-2.0-or-later
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Controls as QQC2 import QtQuick.Controls as QQC2
import QtQuick.Layouts import QtQuick.Layouts
@@ -25,9 +23,8 @@ ColumnLayout {
Repeater { Repeater {
model: root.connection.accountDataEventTypes model: root.connection.accountDataEventTypes
delegate: FormCard.FormButtonDelegate { delegate: FormCard.FormButtonDelegate {
required property string modelData
text: modelData text: modelData
onClicked: (root.Kirigami.PageStack.pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), { onClicked: root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
sourceText: root.connection.accountDataJsonString(modelData) sourceText: root.connection.accountDataJsonString(modelData)
}, { }, {
title: i18nc("@title:window", "Event Source"), title: i18nc("@title:window", "Event Source"),

View File

@@ -17,8 +17,6 @@ ecm_add_qml_module(Devtools GENERATE_PLUGIN_SOURCE
models/statefiltermodel.cpp models/statefiltermodel.cpp
models/statekeysmodel.cpp models/statekeysmodel.cpp
models/statemodel.cpp models/statemodel.cpp
DEPENDENCIES
QtCore
) )
target_include_directories(Devtools PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models) target_include_directories(Devtools PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models)

View File

@@ -29,7 +29,7 @@ FormCard.FormCard {
} }
FormCard.FormCheckDelegate { FormCard.FormCheckDelegate {
id: roomAccountDataVisibleCheck id: roomAccountDataVisibleCheck
text: i18nc("@option:check", "Always allow device verification") text: i18nc("@option:check Enable the matrix 'threads' feature", "Always allow device verification")
description: i18n("Allow the user to start a verification session with devices that were already verified") description: i18n("Allow the user to start a verification session with devices that were already verified")
checked: NeoChatConfig.alwaysVerifyDevice checked: NeoChatConfig.alwaysVerifyDevice

View File

@@ -17,7 +17,6 @@ Kirigami.ScrollablePage {
property NeoChatRoom room property NeoChatRoom room
required property NeoChatConnection connection required property NeoChatConnection connection
property alias currentTabIndex: tabBar.currentIndex
title: i18nc("@title", "Developer Tools") title: i18nc("@title", "Developer Tools")

View File

@@ -1,8 +1,6 @@
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com> // SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Window import QtQuick.Window
@@ -27,7 +25,7 @@ ColumnLayout {
description: i18nc("@info", "Click to choose a room"); description: i18nc("@info", "Click to choose a room");
onClicked: { onClicked: {
let dialog = (root.Kirigami.PageStack.pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), { let dialog = root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), {
connection: root.connection, connection: root.connection,
}, { }, {
title: i18nc("@title:dialog", "Choose Room"), title: i18nc("@title:dialog", "Choose Room"),
@@ -51,9 +49,8 @@ ColumnLayout {
id: roomAccountData id: roomAccountData
model: root.room.accountDataEventTypes model: root.room.accountDataEventTypes
delegate: FormCard.FormButtonDelegate { delegate: FormCard.FormButtonDelegate {
required property string modelData
text: modelData text: modelData
onClicked: (root.Kirigami.PageStack.pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), { onClicked: root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
sourceText: root.room.roomAcountDataJson(text) sourceText: root.room.roomAcountDataJson(text)
}, { }, {
title: i18n("Event Source"), title: i18n("Event Source"),
@@ -77,18 +74,15 @@ ColumnLayout {
} }
delegate: FormCard.FormButtonDelegate { delegate: FormCard.FormButtonDelegate {
required property string type text: model.type
required property int eventCount description: i18ncp("'Event' being some JSON data, not something physically happening.", "%1 event of this type", "%1 events of this type", model.eventCount)
required property string stateKey
text: type
description: i18ncp("'Event' being some JSON data, not something physically happening.", "%1 event of this type", "%1 events of this type", eventCount)
onClicked: { onClicked: {
if (eventCount === 1) { if (model.eventCount === 1) {
root.openEventSource(type, stateKey); openEventSource(model.type, model.stateKey);
} else { } else {
(root.Kirigami.PageStack.pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'StateKeys'), { root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'StateKeys'), {
room: root.room, room: root.room,
eventType: type eventType: model.type
}, { }, {
title: i18nc("'Event' being some JSON data, not something physically happening.", "Event Information") title: i18nc("'Event' being some JSON data, not something physically happening.", "Event Information")
}); });
@@ -98,7 +92,7 @@ ColumnLayout {
} }
} }
function openEventSource(type: string, stateKey: string): void { function openEventSource(type: string, stateKey: string): void {
onClicked: (root.Kirigami.PageStack.pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), { onClicked: root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
model: stateModel, model: stateModel,
allowEdit: true, allowEdit: true,
room: root.room, room: root.room,

View File

@@ -1,9 +1,8 @@
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org> // SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts
import QtQuick.Window import QtQuick.Window
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
@@ -31,15 +30,14 @@ FormCard.FormCardPage {
} }
delegate: FormCard.FormButtonDelegate { delegate: FormCard.FormButtonDelegate {
required property string stateKey text: model.stateKey
text: stateKey onClicked: openEventSource(model.stateKey)
onClicked: root.openEventSource(stateKey)
} }
} }
} }
function openEventSource(stateKey: string): void { function openEventSource(stateKey: string): void {
(Kirigami.PageStack.pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), { root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
model: stateKeysModel, model: stateKeysModel,
allowEdit: true, allowEdit: true,
room: root.room, room: root.room,

View File

@@ -45,8 +45,6 @@ target_sources(LibNeoChat PRIVATE
models/stickermodel.cpp models/stickermodel.cpp
models/userfiltermodel.cpp models/userfiltermodel.cpp
models/userlistmodel.cpp models/userlistmodel.cpp
models/widgetmodel.cpp
models/widgetmodel.h
) )
if (TARGET KF6::KIOWidgets) if (TARGET KF6::KIOWidgets)

View File

@@ -207,10 +207,16 @@ void ChatBarCache::setAttachmentPath(const QString &attachmentPath)
m_attachmentPath = attachmentPath; m_attachmentPath = attachmentPath;
Q_EMIT attachmentPathChanged(); Q_EMIT attachmentPathChanged();
#if (Quotient_VERSION_MINOR < 10 && Quotient_VERSION_PATCH < 3) || Quotient_VERSION_MINOR < 9
m_relationType = None;
const auto oldEventId = std::exchange(m_relationId, QString());
Q_EMIT relationIdChanged(oldEventId, m_relationId);
#else
if (m_relationType == Edit) { if (m_relationType == Edit) {
const auto oldEventId = std::exchange(m_relationId, QString()); const auto oldEventId = std::exchange(m_relationId, QString());
Q_EMIT relationIdChanged(oldEventId, m_relationId); Q_EMIT relationIdChanged(oldEventId, m_relationId);
} }
#endif
} }
void ChatBarCache::clearRelations() void ChatBarCache::clearRelations()

View File

@@ -30,7 +30,7 @@ public:
ServerNotice, /**< Official messages from the server. */ ServerNotice, /**< Official messages from the server. */
Deprioritized, /**< The room is set as low priority. */ Deprioritized, /**< The room is set as low priority. */
Space, /**< The room is a space. */ Space, /**< The room is a space. */
AddDirect, /**< So we can show the add direct message delegate. */ AddDirect, /**< So we can show the add friend delegate. */
TypesCount, /**< Number of different types (this should always be last). */ TypesCount, /**< Number of different types (this should always be last). */
}; };
Q_ENUM(Types); Q_ENUM(Types);
@@ -66,7 +66,7 @@ public:
case NeoChatRoomType::Favorite: case NeoChatRoomType::Favorite:
return i18n("Favorite"); return i18n("Favorite");
case NeoChatRoomType::Direct: case NeoChatRoomType::Direct:
return i18n("Direct Messages"); return i18n("Friends");
case NeoChatRoomType::Normal: case NeoChatRoomType::Normal:
return i18n("Normal"); return i18n("Normal");
case NeoChatRoomType::Deprioritized: case NeoChatRoomType::Deprioritized:

View File

@@ -89,7 +89,7 @@ public:
case Parameter::MostHighlights: case Parameter::MostHighlights:
return i18nc("@info", "Rooms with the most highlighted messages are higher"); return i18nc("@info", "Rooms with the most highlighted messages are higher");
case Parameter::LastActive: case Parameter::LastActive:
return i18nc("@info", "Rooms with newer events are higher"); return i18nc("@info", "Rooms with the newer messages are higher");
default: default:
return {}; return {};
} }

View File

@@ -10,6 +10,7 @@
#include <Quotient/events/encryptionevent.h> #include <Quotient/events/encryptionevent.h>
#include <Quotient/events/event.h> #include <Quotient/events/event.h>
#include <Quotient/events/eventcontent.h>
#include <Quotient/events/reactionevent.h> #include <Quotient/events/reactionevent.h>
#include <Quotient/events/redactionevent.h> #include <Quotient/events/redactionevent.h>
#include <Quotient/events/roomavatarevent.h> #include <Quotient/events/roomavatarevent.h>
@@ -195,25 +196,20 @@ bool EventHandler::isHidden(const NeoChatRoom *room, const Quotient::RoomEvent *
return false; return false;
} }
Qt::TextFormat EventHandler::messageBodyInputFormat(const Quotient::RoomEvent &event) Qt::TextFormat EventHandler::messageBodyInputFormat(const Quotient::RoomMessageEvent &event)
{ {
if (event.isRedacted() && !event.isStateEvent()) { if (event.isRedacted() && !event.isStateEvent()) {
return Qt::RichText; return Qt::RichText;
} }
auto msgEvent = eventCast<const Quotient::RoomMessageEvent>(&event); if (event.mimeType().name() == "text/plain"_L1) {
if (!msgEvent) {
return Qt::PlainText;
}
if (msgEvent->mimeType().name() == "text/plain"_L1) {
return Qt::PlainText; return Qt::PlainText;
} else { } else {
return Qt::RichText; return Qt::RichText;
} }
} }
QString EventHandler::rawMessageBody(const RoomEvent &event) QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
{ {
if (event.isRedacted() && !event.isStateEvent()) { if (event.isRedacted() && !event.isStateEvent()) {
auto reason = event.redactedBecause()->reason(); auto reason = event.redactedBecause()->reason();
@@ -222,26 +218,21 @@ QString EventHandler::rawMessageBody(const RoomEvent &event)
QString body; QString body;
auto msgEvent = eventCast<const Quotient::RoomMessageEvent>(&event); if (event.has<EventContent::FileContent>()) {
if (!msgEvent) {
return body;
}
if (msgEvent->has<EventContent::FileContent>()) {
// if filename is given or body is equal to filename, // if filename is given or body is equal to filename,
// then body is a caption // then body is a caption
QString filename = msgEvent->get<EventContent::FileContent>()->originalName; QString filename = event.get<EventContent::FileContent>()->originalName;
QString body = msgEvent->plainBody(); QString body = event.plainBody();
if (filename.isEmpty() || filename == body) { if (filename.isEmpty() || filename == body) {
return QString(); return QString();
} }
return body; return body;
} }
if (msgEvent->has<EventContent::TextContent>() && msgEvent->content()) { if (event.has<EventContent::TextContent>() && event.content()) {
body = msgEvent->get<EventContent::TextContent>()->body; body = event.get<EventContent::TextContent>()->body;
} else { } else {
body = msgEvent->plainBody(); body = event.plainBody();
} }
return body; return body;
} }
@@ -460,9 +451,6 @@ QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent
return i18nc("[User] joined a [voice/video] call", "joined a call"); return i18nc("[User] joined a [voice/video] call", "joined a call");
} }
} }
if (e.matrixType() == "io.element.integrations.installations"_L1) {
return i18nc("[User] configured an extension", "configured an extension");
}
return e.stateKey().isEmpty() ? i18n("updated %1 state", e.matrixType()) return e.stateKey().isEmpty() ? i18n("updated %1 state", e.matrixType())
: i18n("updated %1 state for %2", e.matrixType(), prettyPrint ? e.stateKey().toHtmlEscaped() : e.stateKey()); : i18n("updated %1 state for %2", e.matrixType(), prettyPrint ? e.stateKey().toHtmlEscaped() : e.stateKey());
}, },
@@ -676,9 +664,6 @@ QString EventHandler::genericBody(const NeoChatRoom *room, const Quotient::RoomE
return i18nc("[User] joined a [voice/video] call", "%1 joined a call", senderString); return i18nc("[User] joined a [voice/video] call", "%1 joined a call", senderString);
} }
} }
if (e.matrixType() == "io.element.integrations.installations"_L1) {
return i18nc("[User] configured an extension", "%1 configured an extension", senderString);
}
return i18n("%1 updated the state", senderString); return i18n("%1 updated the state", senderString);
}, },
[senderString](const PollStartEvent &) { [senderString](const PollStartEvent &) {

View File

@@ -122,15 +122,15 @@ public:
* I.e. if the message has only a body the format will be Qt::PlainText, if it * I.e. if the message has only a body the format will be Qt::PlainText, if it
* has a formatted body it will be Qt::RichText. * has a formatted body it will be Qt::RichText.
*/ */
static Qt::TextFormat messageBodyInputFormat(const Quotient::RoomEvent &event); static Qt::TextFormat messageBodyInputFormat(const Quotient::RoomMessageEvent &event);
/** /**
* @brief Output a string for the message content without any formatting. * @brief Output a string for the room message content without any formatting.
* *
* This is the content of the formatted_body key if present or the body key if * This is the content of the formatted_body key if present or the body key if
* not. * not.
*/ */
static QString rawMessageBody(const Quotient::RoomEvent &event); static QString rawMessageBody(const Quotient::RoomMessageEvent &event);
/** /**
* @brief Output a string for the message content ready for display in a rich text field. * @brief Output a string for the message content ready for display in a rich text field.

Some files were not shown because too many files have changed in this diff Show More