Compare commits

..

1 Commits

Author SHA1 Message Date
Joshua Goins
8e0f0182cb Add Prison dependency to QML modules so Prison is deployed on Windows
Otherwise we get an error because the DLL isn't found.
2025-06-01 04:03:25 +02:00
227 changed files with 32690 additions and 36279 deletions

View File

@@ -2,5 +2,7 @@
; SPDX-License-Identifier: CC0-1.0 ; SPDX-License-Identifier: CC0-1.0
[BlueprintSettings] [BlueprintSettings]
kde/frameworks/extra-cmake-modules.version=master
kde/unreleased/kirigami-addons.version=master
kde/applications/neochat.packageAppx=True kde/applications/neochat.packageAppx=True
libs/qt.qtMajorVersion=6 libs/qt.qtMajorVersion=6

View File

@@ -2,7 +2,7 @@
"id": "org.kde.neochat", "id": "org.kde.neochat",
"branch": "master", "branch": "master",
"runtime": "org.kde.Platform", "runtime": "org.kde.Platform",
"runtime-version": "6.9", "runtime-version": "6.8",
"sdk": "org.kde.Sdk", "sdk": "org.kde.Sdk",
"command": "neochat", "command": "neochat",
"tags": [ "tags": [
@@ -149,6 +149,27 @@
], ],
"builddir": true "builddir": true
}, },
{
"name": "qcoro",
"buildsystem": "cmake-ninja",
"config-opts": [
"-DQCORO_BUILD_EXAMPLES=OFF",
"-DBUILD_TESTING=OFF"
],
"sources": [
{
"type": "archive",
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.11.0.tar.gz",
"sha256": "9942c5b4c533192f6c5954dc6d10178b3829075e6a621b67df73f0a4b74d8297",
"x-checker-data": {
"type": "anitya",
"project-id": 236236,
"stable-only": true,
"url-template": "https://github.com/danvratil/qcoro/archive/refs/tags/v$version.tar.gz"
}
}
]
},
{ {
"name": "kunifiedpush", "name": "kunifiedpush",
"buildsystem": "cmake-ninja", "buildsystem": "cmake-ninja",

View File

@@ -8,8 +8,8 @@ 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 "08") set(RELEASE_SERVICE_VERSION_MINOR "07")
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})

View File

@@ -130,8 +130,7 @@ void EventHandlerTest::timeString()
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1432735824654, QTimeZone(QTimeZone::UTC)).toLocalTime().time(), QLocale::ShortFormat)); QLocale().toString(QDateTime::fromMSecsSinceEpoch(1432735824654, QTimeZone(QTimeZone::UTC)).toLocalTime().time(), QLocale::ShortFormat));
QCOMPARE(EventHandler::timeString(room, event, true), QCOMPARE(EventHandler::timeString(room, event, true),
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1432735824654, QTimeZone(QTimeZone::UTC)).toLocalTime().date(), QLocale::ShortFormat)); format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1432735824654, QTimeZone(QTimeZone::UTC)).toLocalTime().date(), QLocale::ShortFormat));
QCOMPARE(EventHandler::timeString(room, event, u"hh:mm"_s), QCOMPARE(EventHandler::timeString(room, event, u"hh:mm"_s), QDateTime::fromMSecsSinceEpoch(1432735824654, QTimeZone(QTimeZone::UTC)).toString(u"hh:mm"_s));
QDateTime::fromMSecsSinceEpoch(1432735824654, QTimeZone(QTimeZone::LocalTime)).toString(u"hh:mm"_s));
const auto txID = room->postJson("m.room.message"_L1, event->fullJson()); const auto txID = room->postJson("m.room.message"_L1, event->fullJson());
QCOMPARE(room->pendingEvents().size(), 1); QCOMPARE(room->pendingEvents().size(), 1);

View File

@@ -61,7 +61,6 @@ private Q_SLOTS:
void receiveRichStrikethrough(); void receiveRichStrikethrough();
void receiveRichtextIn(); void receiveRichtextIn();
void receiveRichMxcUrl(); void receiveRichMxcUrl();
void receiveRichPlainUrl_data();
void receiveRichPlainUrl(); void receiveRichPlainUrl();
void receiveRichEdited_data(); void receiveRichEdited_data();
void receiveRichEdited(); void receiveRichEdited();
@@ -451,32 +450,6 @@ void TextHandlerTest::receiveRichMxcUrl()
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, room, room->messageEvents().at(0).get()), testOutputString); QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, room, room->messageEvents().at(0).get()), testOutputString);
} }
void TextHandlerTest::receiveRichPlainUrl_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
// This is an actual link that caused trouble which is why it's so long. Keeping
// so we can confirm consistent behaviour for complex urls.
QTest::addRow("link 1")
<< u"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im <a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">Link already rich</a>"_s
<< u"<a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im</a> <a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">Link already rich</a>"_s;
// Another real case. The linkification wasn't handling it when a single link
// contains what looks like and email. It was broken into 3 but needs to
// be just single link.
QTest::addRow("link 2")
<< u"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/"_s
<< u"<a href=\"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/\">https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/</a>"_s;
QTest::addRow("email") << uR"(email@example.com <a href="mailto:email@example.com">Link already rich</a>)"_s
<< uR"(<a href="mailto:email@example.com">email@example.com</a> <a href="mailto:email@example.com">Link already rich</a>)"_s;
QTest::addRow("mxid")
<< u"@user:kde.org <a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a>"_s
<< u"<b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a></b>"_s;
QTest::addRow("mxid with prefix") << u"a @user:kde.org b"_s << u"a <b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> b"_s;
}
/** /**
* For when your rich input string has a plain text url left in. * For when your rich input string has a plain text url left in.
* *
@@ -485,13 +458,46 @@ void TextHandlerTest::receiveRichPlainUrl_data()
*/ */
void TextHandlerTest::receiveRichPlainUrl() void TextHandlerTest::receiveRichPlainUrl()
{ {
QFETCH(QString, input); // This is an actual link that caused trouble which is why it's so long. Keeping
QFETCH(QString, output); // so we can confirm consistent behaviour for complex urls.
const QString testInputStringLink1 =
u"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im <a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">Link already rich</a>"_s;
const QString testOutputStringLink1 =
u"<a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im</a> <a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">Link already rich</a>"_s;
// Another real case. The linkification wasn't handling it when a single link
// contains what looks like and email. It was been broken into 3 but needs to
// be just single link.
const QString testInputStringLink2 = u"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/"_s;
const QString testOutputStringLink2 =
u"<a href=\"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/\">https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/</a>"_s;
QString testInputStringEmail = uR"(email@example.com <a href="mailto:email@example.com">Link already rich</a>)"_s;
QString testOutputStringEmail = uR"(<a href="mailto:email@example.com">email@example.com</a> <a href="mailto:email@example.com">Link already rich</a>)"_s;
QString testInputStringMxId = u"@user:kde.org <a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a>"_s;
QString testOutputStringMxId =
u"<b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a></b>"_s;
QString testInputStringMxIdWithPrefix = u"a @user:kde.org b"_s;
QString testOutputStringMxIdWithPrefix = u"a <b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> b"_s;
TextHandler testTextHandler; TextHandler testTextHandler;
testTextHandler.setData(input); testTextHandler.setData(testInputStringLink1);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), output); QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringLink1);
testTextHandler.setData(testInputStringLink2);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringLink2);
testTextHandler.setData(testInputStringEmail);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringEmail);
testTextHandler.setData(testInputStringMxId);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringMxId);
testTextHandler.setData(testInputStringMxIdWithPrefix);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringMxIdWithPrefix);
} }
void TextHandlerTest::receiveRichEdited_data() void TextHandlerTest::receiveRichEdited_data()

View File

@@ -57,7 +57,7 @@ void TimelineMessageModelTest::switchEmptyRoom()
auto firstRoom = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s); auto firstRoom = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s);
auto secondRoom = new TestUtils::TestRoom(connection, u"#secondRoom:kde.org"_s); auto secondRoom = new TestUtils::TestRoom(connection, u"#secondRoom:kde.org"_s);
QSignalSpy spy(model, SIGNAL(roomChanged(NeoChatRoom *, NeoChatRoom *))); QSignalSpy spy(model, SIGNAL(roomChanged()));
QCOMPARE(model->room(), nullptr); QCOMPARE(model->room(), nullptr);
model->setRoom(firstRoom); model->setRoom(firstRoom);
@@ -77,7 +77,7 @@ void TimelineMessageModelTest::switchSyncedRoom()
auto firstRoom = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s, u"test-messageventmodel-sync.json"_s); auto firstRoom = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s, u"test-messageventmodel-sync.json"_s);
auto secondRoom = new TestUtils::TestRoom(connection, u"#secondRoom:kde.org"_s, u"test-messageventmodel-sync.json"_s); auto secondRoom = new TestUtils::TestRoom(connection, u"#secondRoom:kde.org"_s, u"test-messageventmodel-sync.json"_s);
QSignalSpy spy(model, SIGNAL(roomChanged(NeoChatRoom *, NeoChatRoom *))); QSignalSpy spy(model, SIGNAL(roomChanged()));
QCOMPARE(model->room(), nullptr); QCOMPARE(model->room(), nullptr);
model->setRoom(firstRoom); model->setRoom(firstRoom);
@@ -208,7 +208,7 @@ void TimelineMessageModelTest::idToRow()
auto room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-min-sync.json"_s); auto room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-min-sync.json"_s);
model->setRoom(room); model->setRoom(room);
QCOMPARE(model->indexforEventId(u"$153456789:example.org"_s).row(), 0); QCOMPARE(model->eventIdToRow(u"$153456789:example.org"_s), 0);
} }
void TimelineMessageModelTest::cleanup() void TimelineMessageModelTest::cleanup()

View File

@@ -49,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>
@@ -74,7 +75,6 @@
<summary xml:lang="nl">Chat op Matrix</summary> <summary xml:lang="nl">Chat op Matrix</summary>
<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="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>
@@ -82,6 +82,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>
@@ -108,16 +109,16 @@
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p> <p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
<p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p> <p xml:lang="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="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>
<p xml:lang="ca">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptatge d'extrem a extrem. Hi ha algunes altres omissions més petites a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu segueix sent proporcionar suport eventual per a tota l'especificació.</p> <p xml:lang="ca">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptatge d'extrem a extrem. Hi ha algunes altres omissions més petites a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu segueix sent proporcionar suport eventual per a tota l'especificació.</p>
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p> <p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
<p xml:lang="de">NeoChat versucht eine vollumfängliche Anwendung für die Spezifikation von Matrix zu sein. Damit wird alles der aktuellen stabilen Spezifikation mit den erwähnenswerten Ausnahmen von VoIP, Diskussionsfäden und ein paar Teilen der Ende-zu-Ende-Verschlüsselung unterstützt. Zudem sind andere kleinere Auslassungen vorhanden, da sich die Matrixspezifikation ständig weiterentwickelt. Nichtsdestotrotz soll letztendlich die gesamte Spezifikation unterstützt werden.</p> <p xml:lang="de">NeoChat versucht eine vollumfängliche Anwendung für die Spezifikation von Matrix zu sein. Damit wird alles der aktuellen stabilen Spezifikation mit den erwähnenswerten Ausnahmen von VoIP, Diskussionsfäden und ein paar Teilen der Ende-zu-Ende-Verschlüsselung unterstützt. Zudem sind andere kleinere Auslassungen vorhanden, da sich die Matrixspezifikation ständig weiterentwickelt. Nichtsdestotrotz soll letztendlich die gesamte Spezifikation unterstützt werden.</p>
@@ -141,16 +142,16 @@
<p xml:lang="nn">NeoChat har som mål å støtta all funksjonalitet i Matrix-spesifikasjonen. Førebels er alt i den gjeldande stabile spesifikasjonen støtta, med unntak av VoIP, trådar og nokre delar av ende-til-kryptering. Det finst òg andre småting som ikkje er støtta, sidan Matrix-spesifikasjon er i stadig endring, men målet er altså støtte for alt.</p> <p xml:lang="nn">NeoChat har som mål å støtta all funksjonalitet i Matrix-spesifikasjonen. Førebels er alt i den gjeldande stabile spesifikasjonen støtta, med unntak av VoIP, trådar og nokre delar av ende-til-kryptering. Det finst òg andre småting som ikkje er støtta, sidan Matrix-spesifikasjon er i stadig endring, men målet er altså støtte for alt.</p>
<p xml:lang="pl">NeoChat w zamyśle ma być pełnowartościową aplikacją wg wytycznych Matriksa. Z tego powodu, wszystko, co jest obecnie w stabilnych wytycznych z pominięciem VoIP, wątków i niektórych części szyfrowania Użytkownik-do-Użytkownika są obecnie obsługiwane. Pominięto też kilka mniejszych rzeczy ze względu na ciągły rozwój wytycznych Matriksa, lecz celem nadal jest zapewnienie obsługi wszystkich wytycznych.</p> <p xml:lang="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="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>
<p xml:lang="ca">A causa de la naturalesa del desenvolupament de l'especificació de Matrix, el NeoChat també implementa nombroses característiques inestables. Actualment són:</p> <p xml:lang="ca">A causa de la naturalesa del desenvolupament de l'especificació de Matrix, el NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
<p xml:lang="ca-valencia">A causa de la naturalea del desenvolupament de l'especificació de Matrix, NeoChat també implementa nombroses característiques inestables. Actualment són:</p> <p xml:lang="ca-valencia">A causa de la naturalea del desenvolupament de l'especificació de Matrix, NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
<p xml:lang="de">Durch die Weiterentwicklung der Matrix-Spezifikation unterstützt auch NeoChat einige als noch instabil gekennzeichnete Funktionen. Derzeit sind das:</p> <p xml:lang="de">Durch die Weiterentwicklung der Matrix-Spezifikation unterstützt auch NeoChat einige als noch instabil gekennzeichnete Funktionen. Derzeit sind das:</p>
@@ -174,7 +175,6 @@
<p xml:lang="nn">På grunn av måten Matrix-spesifikasjonen vert utvikla på, støttar NeoChat òg nokre uferdige funksjonar:</p> <p xml:lang="nn">På grunn av måten Matrix-spesifikasjonen vert utvikla på, støttar NeoChat òg nokre uferdige funksjonar:</p>
<p xml:lang="pl">Ze względu na sposób rozwoju Matriksa, NeoChat obsługuje także kilka niestabilnych możliwości. Obecnie są to:</p> <p xml:lang="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="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>
@@ -182,12 +182,13 @@
<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>
<li xml:lang="ar">التصويت - MSC3381</li> <li xml:lang="ar">التصويت - MSC3381</li>
<li xml:lang="ca">Votacions - MSC3381</li> <li xml:lang="ca">Enquestes - MSC3381</li>
<li xml:lang="ca-valencia">Votacions - MSC3381</li> <li xml:lang="ca-valencia">Enquestes - MSC3381</li>
<li xml:lang="el">Δημοσκοπήσεις - MSC3381</li> <li xml:lang="el">Δημοσκοπήσεις - MSC3381</li>
<li xml:lang="en-GB">Polls - MSC3381</li> <li xml:lang="en-GB">Polls - MSC3381</li>
<li xml:lang="eo">Enketoj - MSC3381</li> <li xml:lang="eo">Enketoj - MSC3381</li>
@@ -208,7 +209,6 @@
<li xml:lang="nn">Avstemmingar  MSC3381</li> <li xml:lang="nn">Avstemmingar  MSC3381</li>
<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="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>
@@ -216,6 +216,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>
@@ -241,7 +242,6 @@
<li xml:lang="nn">Klistremerke-pakkar  MSC2545</li> <li xml:lang="nn">Klistremerke-pakkar  MSC2545</li>
<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="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>
@@ -249,6 +249,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>
@@ -274,7 +275,6 @@
<li xml:lang="nn">Posisjonshendingar  MSC3488</li> <li xml:lang="nn">Posisjonshendingar  MSC3488</li>
<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="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>
@@ -282,6 +282,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>
@@ -343,7 +344,6 @@
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption> <caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption> <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="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>
@@ -351,6 +351,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">
@@ -379,7 +380,6 @@
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption> <caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
<caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption> <caption xml:lang="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="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>
@@ -387,6 +387,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>
<!-- <!--
@@ -423,7 +424,6 @@
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption> <caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption> <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="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>
@@ -431,6 +431,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">
@@ -461,7 +462,6 @@
<caption xml:lang="nn">Innloggingsbilete</caption> <caption xml:lang="nn">Innloggingsbilete</caption>
<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="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>
@@ -469,6 +469,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>
@@ -476,10 +477,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.08.1" date="2025-09-11"/>
<release version="25.08.0" date="2025-08-14"/>
<release version="25.04.3" date="2025-07-03"/>
<release version="25.04.2" date="2025-06-05"/>
<release version="25.04.1" date="2025-05-08"/> <release version="25.04.1" date="2025-05-08"/>
<release version="25.04.0" date="2025-04-17"/> <release version="25.04.0" date="2025-04-17"/>
<release version="24.12.3" date="2025-03-06"/> <release version="24.12.3" date="2025-03-06"/>

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
@@ -119,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;

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 % Russian "INCLUDE">
]>
<!--
SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
SPDX-License-Identifier: CC-BY-SA-4.0
-->
<refentry lang="&language;">
<refentryinfo>
<title
>Руководство пользователя NeoChat</title>
<author
><firstname
>Carl</firstname
><surname
>Schwan</surname
> <contrib
>man-страница NeoChat.</contrib
> <email
>carl@carlschwan.eu</email
></author>
<date
>2022-11-01</date>
<releaseinfo
>22.09</releaseinfo>
<productname
>NeoChat</productname>
</refentryinfo>
<refmeta>
<refentrytitle>
<command
>neochat</command>
</refentrytitle>
<manvolnum
>1</manvolnum>
</refmeta>
<refnamediv>
<refname
>neochat</refname>
<refpurpose
>Клиент для взаимодействия с протоколом обмена сообщениями Matrix</refpurpose>
</refnamediv>
<!-- body begins here -->
<refsynopsisdiv id='synopsis'>
<cmdsynopsis
><command
>neochat</command
> <arg choice="opt"
><replaceable
>URI</replaceable
></arg
> </cmdsynopsis>
</refsynopsisdiv>
<refsect1 id="description">
<title
>Описание</title>
<para
><command
>neochat</command
> — приложение для настольных и мобильных устройств, позволяющее общаться в чатах с помощью протокола Matrix. </para>
</refsect1>
<refsect1 id="options"
><title
>Параметры</title>
<variablelist>
<varlistentry>
<term
><option
>URI</option
></term>
<listitem>
<para
>URI-адрес пользователя или комнаты в Matrix, например: matrix:u/user:example.org и matrix:r/root:example.org. NeoChat попытается открыть указанную комнату или беседу. </para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1 id="bug">
<title
>Отчёты об ошибках</title>
<para
>Сообщать об ошибках и отправлять предложения по улучшению можно по адресу <ulink url="https://bugs.kde.org/enter_bug.cgi?product=NeoChat&amp;component=General"
>https://bugs.kde.org/enter_bug.cgi?product=NeoChat&amp;component=General</ulink
></para>
</refsect1>
<refsect1>
<title
>Смотрите также</title>
<simplelist>
<member
>Список наиболее часто задаваемых вопросов о Matrix <ulink url="https://matrix.org/faq/"
>https://matrix.org/faq/</ulink
> </member>
<member
>kf5options(7)</member>
<member
>qt5options(7)</member>
</simplelist>
</refsect1>
<refsect1 id="copyright"
><title
>Авторские права</title>
<para
>Авторские права &copy; Tobias Fella, 20202022 </para>
<para
>Авторские права &copy; Carl Schwan, 20202022 </para>
<para
>Лицензия: стандартная общественная лицензия GNU версии 3 или любой более поздней версии &lt;<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

View File

@@ -9,7 +9,6 @@ add_subdirectory(libneochat)
add_subdirectory(login) add_subdirectory(login)
add_subdirectory(rooms) add_subdirectory(rooms)
add_subdirectory(roominfo) add_subdirectory(roominfo)
add_subdirectory(messagecontent)
add_subdirectory(timeline) add_subdirectory(timeline)
add_subdirectory(spaces) add_subdirectory(spaces)
add_subdirectory(chatbar) add_subdirectory(chatbar)

View File

@@ -20,6 +20,8 @@ add_library(neochat STATIC
windowcontroller.h windowcontroller.h
models/serverlistmodel.cpp models/serverlistmodel.cpp
models/serverlistmodel.h models/serverlistmodel.h
logger.cpp
logger.h
models/notificationsmodel.cpp models/notificationsmodel.cpp
models/notificationsmodel.h models/notificationsmodel.h
proxycontroller.cpp proxycontroller.cpp
@@ -59,7 +61,9 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/RoomPage.qml qml/RoomPage.qml
qml/ManualRoomDialog.qml qml/ManualRoomDialog.qml
qml/ExplorerDelegate.qml qml/ExplorerDelegate.qml
qml/ImageEditorPage.qml
qml/NeochatMaximizeComponent.qml qml/NeochatMaximizeComponent.qml
qml/TypingPane.qml
qml/QuickSwitcher.qml qml/QuickSwitcher.qml
qml/AttachmentPane.qml qml/AttachmentPane.qml
qml/QuickFormatBar.qml qml/QuickFormatBar.qml
@@ -104,11 +108,12 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
DEPENDENCIES DEPENDENCIES
QtCore QtCore
QtQuick QtQuick
org.kde.prison
org.kde.prison.scanner
IMPORTS IMPORTS
org.kde.neochat.libneochat org.kde.neochat.libneochat
org.kde.neochat.rooms org.kde.neochat.rooms
org.kde.neochat.roominfo org.kde.neochat.roominfo
org.kde.neochat.messagecontent
org.kde.neochat.timeline org.kde.neochat.timeline
org.kde.neochat.spaces org.kde.neochat.spaces
org.kde.neochat.settings org.kde.neochat.settings
@@ -177,7 +182,7 @@ else()
endif() endif()
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models) target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models)
target_link_libraries(neochat PRIVATE Loginplugin Roomsplugin RoomInfoplugin MessageContentplugin Timelineplugin Spacesplugin Chatbarplugin Settingsplugin Devtoolsplugin) target_link_libraries(neochat PRIVATE Loginplugin Roomsplugin RoomInfoplugin Timelineplugin Spacesplugin Chatbarplugin Settingsplugin Devtoolsplugin)
target_link_libraries(neochat PUBLIC target_link_libraries(neochat PUBLIC
LibNeoChat LibNeoChat
Timeline Timeline
@@ -202,7 +207,6 @@ target_link_libraries(neochat PUBLIC
QuotientQt6 QuotientQt6
Login Login
Rooms Rooms
MessageContent
Spaces Spaces
) )
@@ -357,10 +361,3 @@ 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)
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins) install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins)
endif() endif()
if (APPLE)
set_target_properties(neochat-app PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.neochat")
set_target_properties(neochat-app PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "NeoChat")
set_target_properties(neochat-app PROPERTIES MACOSX_BUNDLE_SHORT_VERSION_STRING ${RELEASE_SERVICE_VERSION})
set_target_properties(neochat-app PROPERTIES MACOSX_BUNDLE_BUNDLE_VERSION ${RELEASE_SERVICE_VERSION})
endif ()

View File

@@ -407,9 +407,4 @@ void Controller::markImageShown(const QString &eventId)
m_shownImages.append(eventId); m_shownImages.append(eventId);
} }
void Controller::markImageHidden(const QString &eventId)
{
m_shownImages.removeAll(eventId);
}
#include "moc_controller.cpp" #include "moc_controller.cpp"

View File

@@ -99,7 +99,6 @@ public:
Q_INVOKABLE bool isImageShown(const QString &eventId); Q_INVOKABLE bool isImageShown(const QString &eventId);
Q_INVOKABLE void markImageShown(const QString &eventId); Q_INVOKABLE void markImageShown(const QString &eventId);
Q_INVOKABLE void markImageHidden(const QString &eventId);
private: private:
explicit Controller(QObject *parent = nullptr); explicit Controller(QObject *parent = nullptr);

223
src/app/logger.cpp Normal file
View File

@@ -0,0 +1,223 @@
// SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org>
// SPDX-FileCopyrightText: 2002 Holger Freyther <freyther@kde.org>
// SPDX-FileCopyrightText: 2008 Volker Krause <vkrause@kde.org>
// SPDX-FileCopyrightText: 2023 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "logger.h"
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
#include <QLoggingCategory>
#include <QMutex>
#include <QStandardPaths>
using namespace Qt::StringLiterals;
static QLoggingCategory::CategoryFilter oldCategoryFilter = nullptr;
static QtMessageHandler oldHandler = nullptr;
static bool e2eeDebugEnabled = false;
class FileDebugStream : public QIODevice
{
Q_OBJECT
public:
FileDebugStream()
: mType(QtCriticalMsg)
{
open(WriteOnly);
}
bool isSequential() const override
{
return true;
}
qint64 readData(char *, qint64) override
{
return 0;
}
qint64 readLineData(char *, qint64) override
{
return 0;
}
qint64 writeData(const char *data, qint64 len) override
{
if (!mFileName.isEmpty()) {
QFile outputFile(mFileName);
outputFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Unbuffered);
outputFile.write(data, len);
outputFile.putChar('\n');
outputFile.close();
}
return len;
}
void setFileName(const QString &fileName)
{
mFileName = fileName;
}
void setType(QtMsgType type)
{
mType = type;
}
private:
QString mFileName;
QtMsgType mType;
};
class DebugPrivate
{
public:
DebugPrivate()
: origHandler(nullptr)
{
}
~DebugPrivate()
{
qInstallMessageHandler(origHandler);
file.close();
}
void log(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
QMutexLocker locker(&mutex);
QByteArray buf;
QTextStream str(&buf);
str << QDateTime::currentDateTime().toString(Qt::ISODate) << u" ["_s;
switch (type) {
case QtDebugMsg:
str << u"DEBUG"_s;
break;
case QtInfoMsg:
str << u"INFO "_s;
break;
case QtWarningMsg:
str << u"WARN "_s;
break;
case QtFatalMsg:
str << u"FATAL"_s;
break;
case QtCriticalMsg:
str << u"CRITICAL"_s;
break;
}
str << u"] "_s << context.category << u": "_s;
if (context.file && *context.file && context.line) {
str << context.file << u":"_s << context.line << u": "_s;
}
if (context.function && *context.function) {
str << context.function << u": "_s;
}
str << message << u"\n"_s;
str.flush();
file.write(buf.constData(), buf.size());
file.flush();
if (oldHandler && (!context.category || (strcmp(context.category, "quotient.e2ee") != 0 || e2eeDebugEnabled))) {
oldHandler(type, context, message);
}
}
void setName(const QString &appName)
{
name = appName;
if (file.isOpen()) {
file.close();
}
const auto &filePath = u"%1%2%3"_s.arg(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), QDir::separator(), appName);
QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QDir::separator());
auto entryList = dir.entryList({appName + u".*"_s});
std::sort(entryList.begin(), entryList.end(), [](const auto &left, const auto &right) {
auto leftIndex = left.split(u"."_s).last().toInt();
auto rightIndex = right.split(u"."_s).last().toInt();
return leftIndex > rightIndex;
});
for (const auto &entry : entryList) {
bool ok = false;
const auto index = entry.split(u"."_s).last().toInt(&ok);
if (!ok) {
continue;
}
QFileInfo info(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QDir::separator() + entry);
if (info.exists()) {
QFile file(info.absoluteFilePath());
if (index > 50) {
file.remove();
continue;
}
const auto &newName = u"%1.%2"_s.arg(filePath, QString::number(index + 1));
const auto success = file.copy(newName);
if (success) {
file.remove();
} else {
qFatal("Cannot rename log file '%s' to '%s': %s",
qUtf8Printable(file.fileName()),
qUtf8Printable(newName),
qUtf8Printable(file.errorString()));
}
}
}
QFileInfo finfo(filePath);
if (!finfo.absoluteDir().exists()) {
QDir().mkpath(finfo.absolutePath());
}
file.setFileName(filePath + u".0"_s);
file.open(QIODevice::WriteOnly | QIODevice::Unbuffered);
}
void setOrigHandler(QtMessageHandler origHandler_)
{
origHandler = origHandler_;
}
QMutex mutex;
QFile file;
QString name;
QtMessageHandler origHandler;
QByteArray loggingCategory;
};
Q_GLOBAL_STATIC(DebugPrivate, sInstance)
void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
switch (type) {
case QtDebugMsg:
case QtInfoMsg:
case QtWarningMsg:
case QtCriticalMsg:
sInstance()->log(type, context, message);
break;
case QtFatalMsg:
sInstance()->log(QtInfoMsg, context, message);
}
}
void filter(QLoggingCategory *category)
{
if (qstrcmp(category->categoryName(), "quotient.e2ee") == 0) {
category->setEnabled(QtDebugMsg, true);
} else if (oldCategoryFilter) {
oldCategoryFilter(category);
}
}
void initLogging()
{
e2eeDebugEnabled = QLoggingCategory("quotient.e2ee", QtInfoMsg).isEnabled(QtDebugMsg);
oldCategoryFilter = QLoggingCategory::installFilter(filter);
oldHandler = qInstallMessageHandler(messageHandler);
sInstance->setOrigHandler(oldHandler);
sInstance->setName(u"neochat.log"_s);
}
#include "logger.moc"

9
src/app/logger.h Normal file
View File

@@ -0,0 +1,9 @@
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
/**
* Initlalize logging to file and enables some additional categories, which will only be logged to the file
*/
void initLogging();

View File

@@ -49,6 +49,7 @@
#include "blurhashimageprovider.h" #include "blurhashimageprovider.h"
#include "colorschemer.h" #include "colorschemer.h"
#include "controller.h" #include "controller.h"
#include "logger.h"
#include "login.h" #include "login.h"
#include "registration.h" #include "registration.h"
#include "roommanager.h" #include "roommanager.h"
@@ -137,11 +138,6 @@ int main(int argc, char *argv[])
font.setHintingPreference(QFont::PreferNoHinting); font.setHintingPreference(QFont::PreferNoHinting);
app.setFont(font); app.setFont(font);
#endif #endif
#ifdef Q_OS_MACOS
QApplication::setStyle(u"breeze"_s);
#endif
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat")); KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
QGuiApplication::setOrganizationName("KDE"_L1); QGuiApplication::setOrganizationName("KDE"_L1);
@@ -181,6 +177,8 @@ int main(int argc, char *argv[])
KCrash::initialize(); KCrash::initialize();
#endif #endif
initLogging();
Connection::setEncryptionDefault(true); Connection::setEncryptionDefault(true);
Connection::setDirectChatEncryptionDefault(true); Connection::setDirectChatEncryptionDefault(true);
@@ -197,9 +195,6 @@ int main(int argc, char *argv[])
parser.addPositionalArgument(u"urls"_s, i18n("Supports matrix: url scheme")); parser.addPositionalArgument(u"urls"_s, i18n("Supports matrix: url scheme"));
parser.addOption(QCommandLineOption("ignore-ssl-errors"_L1, i18n("Ignore all SSL Errors, e.g., unsigned certificates."))); parser.addOption(QCommandLineOption("ignore-ssl-errors"_L1, i18n("Ignore all SSL Errors, e.g., unsigned certificates.")));
QCommandLineOption replaceOption({QStringLiteral("replace")}, i18nc("command line description", "Replace an existing instance"));
parser.addOption(replaceOption);
QCommandLineOption testOption("test"_L1, i18n("Only used for autotests")); QCommandLineOption testOption("test"_L1, i18n("Only used for autotests"));
testOption.setFlags(QCommandLineOption::HiddenFromHelp); testOption.setFlags(QCommandLineOption::HiddenFromHelp);
parser.addOption(testOption); parser.addOption(testOption);
@@ -236,7 +231,7 @@ int main(int argc, char *argv[])
#endif #endif
#ifdef HAVE_KDBUSADDONS #ifdef HAVE_KDBUSADDONS
KDBusService service(KDBusService::Unique | (parser.isSet(replaceOption) ? KDBusService::Replace : KDBusService::StartupOption(0))); KDBusService service(KDBusService::Unique);
#endif #endif
const auto accountManager = std::make_unique<AccountManager>(parser.isSet("test"_L1)); const auto accountManager = std::make_unique<AccountManager>(parser.isSet("test"_L1));
@@ -244,6 +239,13 @@ int main(int argc, char *argv[])
LoginHelper::instance().setAccountManager(accountManager.get()); LoginHelper::instance().setAccountManager(accountManager.get());
Registration::instance().setAccountManager(accountManager.get()); Registration::instance().setAccountManager(accountManager.get());
Q_IMPORT_QML_PLUGIN(org_kde_neochat_settingsPlugin)
Q_IMPORT_QML_PLUGIN(org_kde_neochat_roomsPlugin)
Q_IMPORT_QML_PLUGIN(org_kde_neochat_timelinePlugin)
Q_IMPORT_QML_PLUGIN(org_kde_neochat_devtoolsPlugin)
Q_IMPORT_QML_PLUGIN(org_kde_neochat_loginPlugin)
Q_IMPORT_QML_PLUGIN(org_kde_neochat_chatbarPlugin)
qml_register_types_org_kde_neochat(); qml_register_types_org_kde_neochat();
qmlRegisterUncreatableMetaObject(Quotient::staticMetaObject, "Quotient", 1, 0, "JoinRule", u"Access to JoinRule enum only"_s); qmlRegisterUncreatableMetaObject(Quotient::staticMetaObject, "Quotient", 1, 0, "JoinRule", u"Access to JoinRule enum only"_s);

View File

@@ -176,7 +176,7 @@ void ServerListModel::initialize()
true, true,
false, false,
}); });
endResetModel(); beginResetModel();
} }
#include "moc_serverlistmodel.cpp" #include "moc_serverlistmodel.cpp"

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
@@ -218,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
@@ -259,6 +264,7 @@ Comment[sv]=Det finns en ny inbjudan till ett rum
Comment[ta]=ஓர் அரங்கிற்கான புதிய அழைப்பிதழ் உள்ளது Comment[ta]=ஓர் அரங்கிற்கான புதிய அழைப்பிதழ் உள்ளது
Comment[tr]=Bir odaya yeni bir davetiye 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
@@ -297,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
@@ -331,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

@@ -78,12 +78,6 @@
<label>Use a compact room list layout</label> <label>Use a compact room list layout</label>
<default>false</default> <default>false</default>
</entry> </entry>
<entry name="MarkReadCondition" type="Enum">
<label>The sort order for the rooms in the list.</label>
<choices name="::TimelineMarkReadCondition::Condition">
</choices>
<default>2</default>
</entry>
<entry name="ShowStateEvent" type="bool"> <entry name="ShowStateEvent" type="bool">
<label>Show state events in the timeline</label> <label>Show state events in the timeline</label>
<default>true</default> <default>true</default>
@@ -211,10 +205,6 @@
<label>Enable add phone numbers as 3PIDs</label> <label>Enable add phone numbers as 3PIDs</label>
<default>false</default> <default>false</default>
</entry> </entry>
<entry name="Calls" type="bool">
<label>Enable audio and video calling</label>
<default>false</default>
</entry>
</group> </group>
<group name="Security"> <group name="Security">
<entry name="RejectUnknownInvites" type="bool"> <entry name="RejectUnknownInvites" type="bool">

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
@@ -82,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

@@ -36,18 +36,14 @@ KirigamiComponents.ConvergentContextMenu {
} }
} }
Kirigami.Action {
text: i18nc("@action:inmenu", "Switch Account")
icon.name: "system-switch-user"
shortcut: "Ctrl+U"
onTriggered: accountSwitchDialog.createObject(QQC2.Overlay.overlay, {
connection: root.connection
}).open();
}
QQC2.Action { QQC2.Action {
text: i18n("Edit This Account") text: i18n("Edit This Account")
icon.name: "document-edit" icon.name: "document-edit"
onTriggered: NeoChatSettingsView.openWithInitialProperties("accounts", {initialAccount: root.connection}); onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.settings', 'AccountEditorPage'), {
connection: root.connection
}, {
title: i18n("Account editor")
})
} }
QQC2.Action { QQC2.Action {

View File

@@ -16,7 +16,7 @@ ColumnLayout {
id: root id: root
required property NeoChatRoom currentRoom required property NeoChatRoom currentRoom
readonly property var invitingMember: currentRoom.qmlSafeMember(currentRoom.invitingUserId) readonly property var invitingMember: currentRoom.member(currentRoom.invitingUserId)
readonly property string inviteTimestamp: root.currentRoom.inviteTimestamp.toLocaleString(Qt.locale(), Locale.ShortFormat) readonly property string inviteTimestamp: root.currentRoom.inviteTimestamp.toLocaleString(Qt.locale(), Locale.ShortFormat)
spacing: Kirigami.Units.smallSpacing spacing: Kirigami.Units.smallSpacing
@@ -33,7 +33,7 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
name: root.invitingMember.displayName name: root.invitingMember.displayName
source: NeoChatConfig.hideImages ? undefined : root.invitingMember.avatarUrl source: root.invitingMember.avatarUrl
color: root.invitingMember.color color: root.invitingMember.color
} }

View File

@@ -41,7 +41,6 @@ Kirigami.ApplicationWindow {
showExisting: true showExisting: true
onConnectionChosen: root.load() onConnectionChosen: root.load()
} }
columnView.columnResizeMode: pageStack.wideMode ? Kirigami.ColumnView.DynamicColumns : Kirigami.ColumnView.SingleColumn
globalToolBar.canContainHandles: true globalToolBar.canContainHandles: true
globalToolBar { globalToolBar {
style: Kirigami.ApplicationHeaderStyle.ToolBar style: Kirigami.ApplicationHeaderStyle.ToolBar

View File

@@ -139,7 +139,7 @@ Components.AlbumMaximizeComponent {
id: saveAsDialog id: saveAsDialog
Dialogs.FileDialog { Dialogs.FileDialog {
fileMode: Dialogs.FileDialog.SaveFile fileMode: Dialogs.FileDialog.SaveFile
currentFolder: NeoChatConfig.lastSaveDirectory.length > 0 ? NeoChatConfig.lastSaveDirectory : Core.StandardPaths.writableLocation(Core.StandardPaths.DownloadLocation) currentFolder: root.saveFolder
onAccepted: { onAccepted: {
NeoChatConfig.lastSaveDirectory = currentFolder; NeoChatConfig.lastSaveDirectory = currentFolder;
NeoChatConfig.save(); NeoChatConfig.save();

View File

@@ -28,14 +28,6 @@ Kirigami.Page {
placeholderText: root.placeholder placeholderText: root.placeholder
anchors.fill: parent anchors.fill: parent
wrapMode: TextEdit.Wrap wrapMode: TextEdit.Wrap
focus: true
Keys.onReturnPressed: event => {
if (event.modifiers & Qt.ControlModifier) {
root.accepted(reason.text);
root.closeDialog();
}
}
background: Rectangle { background: Rectangle {
color: Kirigami.Theme.backgroundColor color: Kirigami.Theme.backgroundColor
@@ -58,7 +50,6 @@ Kirigami.Page {
} }
} }
QQC2.Button { QQC2.Button {
icon.name: "dialog-cancel-symbolic"
text: i18nc("@action", "Cancel") text: i18nc("@action", "Cancel")
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.RejectRole QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.RejectRole
onClicked: root.closeDialog() onClicked: root.closeDialog()

View File

@@ -11,14 +11,15 @@ import org.kde.kirigami as Kirigami
import org.kde.kitemmodels import org.kde.kitemmodels
import org.kde.neochat import org.kde.neochat
import org.kde.neochat.chatbar
Kirigami.Page { Kirigami.Page {
id: root id: root
/** /// Not readonly because of the separate window view.
* @brief The NeoChatRoom the delegate is being displayed in. property NeoChatRoom currentRoom: RoomManager.currentRoom
*/
readonly property NeoChatRoom currentRoom: RoomManager.currentRoom required property NeoChatConnection connection
/** /**
* @brief The TimelineModel to use. * @brief The TimelineModel to use.
@@ -58,6 +59,11 @@ Kirigami.Page {
*/ */
property MediaMessageFilterModel mediaMessageFilterModel: RoomManager.mediaMessageFilterModel property MediaMessageFilterModel mediaMessageFilterModel: RoomManager.mediaMessageFilterModel
property bool loading: !root.currentRoom || (root.currentRoom.timelineSize === 0 && !root.currentRoom.allHistoryLoaded)
/// Disable cancel shortcut. Used by the separate window since it provides its own cancel implementation.
property bool disableCancelShortcut: false
title: root.currentRoom ? root.currentRoom.displayName : "" title: root.currentRoom ? root.currentRoom.displayName : ""
focus: true focus: true
padding: 0 padding: 0
@@ -80,9 +86,9 @@ Kirigami.Page {
} }
Connections { Connections {
target: root.currentRoom.connection target: root.connection
function onIsOnlineChanged() { function onIsOnlineChanged() {
if (!root.currentRoom.connection.isOnline) { if (!root.connection.isOnline) {
banner.text = i18n("NeoChat is offline. Please check your network connection."); banner.text = i18n("NeoChat is offline. Please check your network connection.");
banner.visible = true; banner.visible = true;
banner.type = Kirigami.MessageType.Error; banner.type = Kirigami.MessageType.Error;
@@ -103,15 +109,18 @@ Kirigami.Page {
Loader { Loader {
id: timelineViewLoader id: timelineViewLoader
anchors.fill: parent anchors.fill: parent
active: root.currentRoom && !root.currentRoom.isInvite && !root.currentRoom.isSpace active: root.currentRoom && !root.currentRoom.isInvite && !root.loading && !root.currentRoom.isSpace
// We need the loader to be active but invisible while the room is loading messages so signals in TimelineView work.
visible: !root.loading
sourceComponent: TimelineView { sourceComponent: TimelineView {
id: timelineView id: timelineView
currentRoom: root.currentRoom
page: root
timelineModel: root.timelineModel
messageFilterModel: root.messageFilterModel messageFilterModel: root.messageFilterModel
compactLayout: NeoChatConfig.compactLayout onFocusChatBar: {
fileDropEnabled: !Controller.isFlatpak if (chatBarLoader.item) {
markReadCondition: NeoChatConfig.markReadCondition chatBarLoader.item.forceActiveFocus();
}
}
} }
} }
@@ -143,6 +152,14 @@ Kirigami.Page {
} }
} }
Loader {
active: root.loading && !invitationLoader.active && RoomManager.currentRoom && !spaceLoader.active
anchors.centerIn: parent
sourceComponent: Kirigami.LoadingPlaceholder {
anchors.centerIn: parent
}
}
background: Rectangle { background: Rectangle {
Kirigami.Theme.colorSet: Kirigami.Theme.View Kirigami.Theme.colorSet: Kirigami.Theme.View
Kirigami.Theme.inherit: false Kirigami.Theme.inherit: false
@@ -157,7 +174,12 @@ Kirigami.Page {
id: chatBar id: chatBar
width: parent.width width: parent.width
currentRoom: root.currentRoom currentRoom: root.currentRoom
connection: root.currentRoom.connection connection: root.connection
onMessageSent: {
if (!timelineViewLoader.item.atYEnd) {
timelineViewLoader.item.goToLastMessage();
}
}
} }
} }
@@ -174,8 +196,21 @@ Kirigami.Page {
} }
} }
Shortcut {
sequence: StandardKey.Cancel
onActivated: {
if (!timelineViewLoader.item.atYEnd || !root.currentRoom.partiallyReadStats.empty()) {
timelineViewLoader.item.goToLastMessage();
root.currentRoom.markAllMessagesAsRead();
} else {
applicationWindow().pageStack.get(0).forceActiveFocus();
}
}
enabled: !root.disableCancelShortcut
}
Connections { Connections {
target: root.currentRoom.connection target: root.connection
function onJoinedRoom(room, invited) { function onJoinedRoom(room, invited) {
if (root.currentRoom.id === invited.id) { if (root.currentRoom.id === invited.id) {
RoomManager.resolveResource(room.id); RoomManager.resolveResource(room.id);
@@ -261,7 +296,7 @@ Kirigami.Page {
id: messageDelegateContextMenu id: messageDelegateContextMenu
MessageDelegateContextMenu { MessageDelegateContextMenu {
room: root.currentRoom room: root.currentRoom
connection: root.currentRoom.connection connection: root.connection
} }
} }
@@ -269,7 +304,7 @@ Kirigami.Page {
id: fileDelegateContextMenu id: fileDelegateContextMenu
FileDelegateContextMenu { FileDelegateContextMenu {
room: root.currentRoom room: root.currentRoom
connection: root.currentRoom.connection connection: root.connection
} }
} }

View File

@@ -80,6 +80,7 @@ Kirigami.Dialog {
text: root.user.id text: root.user.id
elide: Qt.ElideRight elide: Qt.ElideRight
elideWidth: root.availableWidth - avatar.width - qrButton.width - detailRow.spacing * 2 - detailRow.Layout.leftMargin - detailRow.Layout.rightMargin elideWidth: root.availableWidth - avatar.width - qrButton.width - detailRow.spacing * 2 - detailRow.Layout.leftMargin - detailRow.Layout.rightMargin
onElideWidthChanged: console.warn(root.availableWidth, avatar.width, qrButton.width, elideWidth, elidedText)
} }
} }

View File

@@ -59,9 +59,9 @@ RoomManager::RoomManager(QObject *parent)
m_directChatsConfig = m_config->group(u"DirectChatsActive"_s); m_directChatsConfig = m_config->group(u"DirectChatsActive"_s);
connect(this, &RoomManager::currentRoomChanged, this, [this]() { connect(this, &RoomManager::currentRoomChanged, this, [this]() {
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);
m_userListModel->setRoom(m_currentRoom);
}); });
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, [this](NeoChatConnection *connection) { connect(&Controller::instance(), &Controller::activeConnectionChanged, this, [this](NeoChatConnection *connection) {
@@ -96,7 +96,6 @@ RoomManager::RoomManager(QObject *parent)
m_messageFilterModel->invalidate(); m_messageFilterModel->invalidate();
} }
}); });
connect(m_timelineModel->timelineMessageModel(), &MessageModel::modelResetComplete, this, &RoomManager::activateUserModel);
MessageFilterModel::setShowAllEvents(NeoChatConfig::self()->showAllEvents()); MessageFilterModel::setShowAllEvents(NeoChatConfig::self()->showAllEvents());
connect(NeoChatConfig::self(), &NeoChatConfig::ShowAllEventsChanged, this, [this] { connect(NeoChatConfig::self(), &NeoChatConfig::ShowAllEventsChanged, this, [this] {
MessageFilterModel::setShowAllEvents(NeoChatConfig::self()->showAllEvents()); MessageFilterModel::setShowAllEvents(NeoChatConfig::self()->showAllEvents());
@@ -393,9 +392,7 @@ void RoomManager::joinRoom(Quotient::Connection *account, const QString &roomAli
// If no one gives us a homeserver suggestion, try the server specified in the alias/id. // If no one gives us a homeserver suggestion, try the server specified in the alias/id.
// Otherwise joining a remote room not on our homeserver will fail. // Otherwise joining a remote room not on our homeserver will fail.
// This is a hack and we're not supposed to do it. With room ids not containing the server going forward, it won't work anymore for new room versions. if (vias.empty()) {
// FIXME: Let's keep it around anyway for now, remove it at some point, though
if (vias.empty() && roomAliasOrId.contains(':'_L1)) {
vias.append(roomAliasOrId.mid(roomAliasOrId.lastIndexOf(':'_L1) + 1)); vias.append(roomAliasOrId.mid(roomAliasOrId.lastIndexOf(':'_L1) + 1));
} }

View File

@@ -15,5 +15,4 @@ ecm_add_qml_module(Chatbar GENERATE_PLUGIN_SOURCE
EmojiPicker.qml EmojiPicker.qml
EmojiDialog.qml EmojiDialog.qml
EmojiTonesPicker.qml EmojiTonesPicker.qml
ImageEditorPage.qml
) )

View File

@@ -160,6 +160,11 @@ QQC2.Control {
} }
] ]
/**
* @brief A message has been sent from the chat bar.
*/
signal messageSent
spacing: 0 spacing: 0
Kirigami.Theme.colorSet: Kirigami.Theme.View Kirigami.Theme.colorSet: Kirigami.Theme.View
@@ -431,6 +436,7 @@ QQC2.Control {
repeatTimer.stop(); repeatTimer.stop();
root.currentRoom.markAllMessagesAsRead(); root.currentRoom.markAllMessagesAsRead();
textField.clear(); textField.clear();
messageSent();
} }
function formatText(format, selectionStart, selectionEnd) { function formatText(format, selectionStart, selectionEnd) {

View File

@@ -207,7 +207,7 @@ ColumnLayout {
padding: Kirigami.Units.largeSpacing padding: Kirigami.Units.largeSpacing
contentItem: Image { contentItem: Image {
source: model.url source: model.avatarUrl
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
sourceSize.width: width sourceSize.width: width
sourceSize.height: height sourceSize.height: height

View File

@@ -36,13 +36,4 @@ FormCard.FormCard {
NeoChatConfig.save(); NeoChatConfig.save();
} }
} }
FormCard.FormCheckDelegate {
text: i18nc("@option:check Enable the matrix feature for audio and video calling", "Calls")
checked: NeoChatConfig.calls
onToggled: {
NeoChatConfig.calls = checked;
NeoChatConfig.save();
}
}
} }

View File

@@ -28,7 +28,6 @@ target_sources(LibNeoChat PRIVATE
enums/pushrule.h enums/pushrule.h
enums/roomsortparameter.cpp enums/roomsortparameter.cpp
enums/roomsortorder.h enums/roomsortorder.h
enums/timelinemarkreadcondition.h
events/imagepackevent.cpp events/imagepackevent.cpp
events/pollevent.cpp events/pollevent.cpp
jobs/neochatgetcommonroomsjob.cpp jobs/neochatgetcommonroomsjob.cpp
@@ -49,6 +48,11 @@ target_sources(LibNeoChat PRIVATE
ecm_add_qml_module(LibNeoChat GENERATE_PLUGIN_SOURCE ecm_add_qml_module(LibNeoChat GENERATE_PLUGIN_SOURCE
URI org.kde.neochat.libneochat URI org.kde.neochat.libneochat
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/libneochat OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/libneochat
DEPENDENCIES
QtCore
QtQuick
org.kde.prison
org.kde.prison.scanner
QML_FILES QML_FILES
qml/GroupChatDrawerHeader.qml qml/GroupChatDrawerHeader.qml
qml/LocationMapItem.qml qml/LocationMapItem.qml

View File

@@ -173,7 +173,7 @@ void AccountManager::addConnection(NeoChatConnection *connection)
}); });
connect(connection, &NeoChatConnection::loggedOut, this, [this, connection] { connect(connection, &NeoChatConnection::loggedOut, this, [this, connection] {
// Only set the connection if the account being logged out is currently active // Only set the connection if the account being logged out is currently active
if (m_accountRegistry->accounts().count() == 1 && connection == activeConnection()) { if (m_accountRegistry->accounts().count() > 1 && connection == activeConnection()) {
setActiveConnection(dynamic_cast<NeoChatConnection *>(m_accountRegistry->accounts()[0])); setActiveConnection(dynamic_cast<NeoChatConnection *>(m_accountRegistry->accounts()[0]));
} else { } else {
setActiveConnection(nullptr); setActiveConnection(nullptr);

View File

@@ -1,32 +0,0 @@
// SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QObject>
#include <QQmlEngine>
/**
* @class TimelineMarkReadCondition
*
* This class is designed to define the TimelineMarkReadCondition enumeration.
*/
class TimelineMarkReadCondition : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
public:
/**
* @brief The condition for marking messages as read.
*/
enum Condition {
Never = 0, /**< Messages should never be marked automatically. */
Entry, /**< Messages should be marked automatically on entry to the room. */
EntryVisible, /**< Messages should be marked automatically on entry to the room if all messages are visible. */
Exit, /**< Messages should be marked automatically on exiting the room. */
ExitVisible, /**< Messages should be marked automatically on exiting the room if all messages are visible. */
};
Q_ENUM(Condition);
};

View File

@@ -435,13 +435,6 @@ QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent
return i18nc("[User] configured <name> widget", "configured %1 widget", e.contentJson()["name"_L1].toString()); return i18nc("[User] configured <name> widget", "configured %1 widget", e.contentJson()["name"_L1].toString());
}, },
[prettyPrint](const StateEvent &e) { [prettyPrint](const StateEvent &e) {
if (e.matrixType() == "org.matrix.msc3401.call.member"_L1) {
if (e.contentJson().isEmpty()) {
return i18nc("[User] left a [voice/video] call", "left a call");
} else {
return i18nc("[User] joined a [voice/video] call", "joined a call");
}
}
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());
}, },
@@ -641,14 +634,7 @@ QString EventHandler::genericBody(const NeoChatRoom *room, const Quotient::RoomE
} }
return i18n("%1 configured a widget", senderString); return i18n("%1 configured a widget", senderString);
}, },
[senderString](const StateEvent &e) { [senderString](const StateEvent &) {
if (e.matrixType() == "org.matrix.msc3401.call.member"_L1) {
if (e.contentJson().isEmpty()) {
return i18nc("[User] left a [voice/video] call", "%1 left a call", senderString);
} else {
return i18nc("[User] joined a [voice/video] call", "%1 joined a call", senderString);
}
}
return i18n("%1 updated the state", senderString); return i18n("%1 updated the state", senderString);
}, },
[senderString](const PollStartEvent &) { [senderString](const PollStartEvent &) {

View File

@@ -10,7 +10,7 @@ struct MessageComponent {
QString content; QString content;
QVariantMap attributes; QVariantMap attributes;
bool operator==(const MessageComponent &right) const int operator==(const MessageComponent &right) const
{ {
return type == right.type && content == right.content && attributes == right.attributes; return type == right.type && content == right.content && attributes == right.attributes;
} }

View File

@@ -31,7 +31,13 @@ auto leaveRoomLambda = [](const QString &text, NeoChatRoom *room, ChatBarCache *
Q_EMIT room->showMessage(MessageType::Information, i18n("Leaving this room.")); Q_EMIT room->showMessage(MessageType::Information, i18n("Leaving this room."));
room->forget(); room->forget();
} else { } else {
// FIXME: re-add sanity check for roomId/alias QRegularExpression roomRegex(uR"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"_s);
auto regexMatch = roomRegex.match(text);
if (!regexMatch.hasMatch()) {
Q_EMIT room->showMessage(MessageType::Error,
i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
return QString();
}
auto leaving = dynamic_cast<NeoChatRoom *>(room->connection()->room(text)); auto leaving = dynamic_cast<NeoChatRoom *>(room->connection()->room(text));
if (!leaving) { if (!leaving) {
leaving = dynamic_cast<NeoChatRoom *>(room->connection()->roomByAlias(text)); leaving = dynamic_cast<NeoChatRoom *>(room->connection()->roomByAlias(text));
@@ -211,7 +217,13 @@ QList<ActionsModel::Action> actions{
Action{ Action{
u"join"_s, u"join"_s,
[](const QString &text, NeoChatRoom *room, ChatBarCache *) { [](const QString &text, NeoChatRoom *room, ChatBarCache *) {
// FIXME: re-add sanity check for roomId/alias QRegularExpression roomRegex(uR"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"_s);
auto regexMatch = roomRegex.match(text);
if (!regexMatch.hasMatch()) {
Q_EMIT room->showMessage(MessageType::Error,
i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
return QString();
}
auto targetRoom = text.startsWith(QLatin1Char('!')) ? room->connection()->room(text) : room->connection()->roomByAlias(text); auto targetRoom = text.startsWith(QLatin1Char('!')) ? room->connection()->room(text) : room->connection()->roomByAlias(text);
if (targetRoom) { if (targetRoom) {
ActionsModel::instance().resolveResource(targetRoom->id()); ActionsModel::instance().resolveResource(targetRoom->id());
@@ -230,18 +242,25 @@ QList<ActionsModel::Action> actions{
[](const QString &text, NeoChatRoom *room, ChatBarCache *) { [](const QString &text, NeoChatRoom *room, ChatBarCache *) {
auto parts = text.split(u" "_s); auto parts = text.split(u" "_s);
QString roomName = parts[0]; QString roomName = parts[0];
// FIXME: re-add sanity check for roomId/alias QRegularExpression roomRegex(uR"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"_s);
if (const auto targetRoom = text.startsWith(QLatin1Char('!')) ? room->connection()->room(text) : room->connection()->roomByAlias(text)) { auto regexMatch = roomRegex.match(roomName);
if (!regexMatch.hasMatch()) {
Q_EMIT room->showMessage(MessageType::Error,
i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
return QString();
}
auto targetRoom = text.startsWith(QLatin1Char('!')) ? room->connection()->room(text) : room->connection()->roomByAlias(text);
if (targetRoom) {
ActionsModel::instance().resolveResource(targetRoom->id()); ActionsModel::instance().resolveResource(targetRoom->id());
return QString(); return QString();
} }
Q_EMIT room->showMessage(MessageType::Information, i18nc("Knocking room <roomname>.", "Knocking room %1.", text)); Q_EMIT room->showMessage(MessageType::Information, i18nc("Knocking room <roomname>.", "Knocking room %1.", text));
auto connection = dynamic_cast<NeoChatConnection *>(room->connection()); auto connection = dynamic_cast<NeoChatConnection *>(room->connection());
const auto knownServer = roomName.contains(":"_L1) ? QStringList{roomName.mid(roomName.indexOf(":"_L1) + 1)} : QStringList(); const auto knownServer = roomName.mid(roomName.indexOf(":"_L1) + 1);
if (parts.length() >= 2) { if (parts.length() >= 2) {
ActionsModel::instance().knockRoom(connection, roomName, parts[1], knownServer); ActionsModel::instance().knockRoom(connection, roomName, parts[1], QStringList{knownServer});
} else { } else {
ActionsModel::instance().knockRoom(connection, roomName, QString(), knownServer); ActionsModel::instance().knockRoom(connection, roomName, QString(), QStringList{knownServer});
} }
return QString(); return QString();
}, },
@@ -252,7 +271,13 @@ QList<ActionsModel::Action> actions{
Action{ Action{
u"j"_s, u"j"_s,
[](const QString &text, NeoChatRoom *room, ChatBarCache *) { [](const QString &text, NeoChatRoom *room, ChatBarCache *) {
// FIXME: re-add sanity check for roomId/alias QRegularExpression roomRegex(uR"(^[#!][^:]+:\w(?:\w|\.|-)*\.\w+(?::\d{1,5})?)"_s);
auto regexMatch = roomRegex.match(text);
if (!regexMatch.hasMatch()) {
Q_EMIT room->showMessage(MessageType::Error,
i18nc("'<text>' does not look like a room id or alias.", "'%1' does not look like a room id or alias.", text));
return QString();
}
if (room->connection()->room(text) || room->connection()->roomByAlias(text)) { if (room->connection()->room(text) || room->connection()->roomByAlias(text)) {
Q_EMIT room->showMessage(MessageType::Information, i18nc("You are already in room <roomname>.", "You are already in room %1.", text)); Q_EMIT room->showMessage(MessageType::Information, i18nc("You are already in room <roomname>.", "You are already in room %1.", text));
return QString(); return QString();

View File

@@ -1214,73 +1214,66 @@ QByteArray NeoChatRoom::getEventJsonSource(const QString &eventId)
void NeoChatRoom::openEventMediaExternally(const QString &eventId) void NeoChatRoom::openEventMediaExternally(const QString &eventId)
{ {
const auto evtIt = findInTimeline(eventId); const auto evtIt = findInTimeline(eventId);
if (evtIt == messageEvents().rend()) { if (evtIt != messageEvents().rend() && is<RoomMessageEvent>(**evtIt)) {
return; const auto event = evtIt->viewAs<RoomMessageEvent>();
} if (event->has<EventContent::FileContent>()) {
const auto transferInfo = cachedFileTransferInfo(event);
// TODO: Also allow stickers here, once that's fixed in libQuotient if (transferInfo.completed()) {
if (!is<RoomMessageEvent>(**evtIt) || !evtIt->viewAs<RoomMessageEvent>()->has<EventContent::FileContentBase>()) {
return;
}
const auto transferInfo = cachedFileTransferInfo(evtIt->viewAs<RoomEvent>());
if (transferInfo.completed()) {
UrlHelper helper;
helper.openUrl(transferInfo.localPath);
return;
}
downloadFile(eventId,
QUrl(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u'/'
+ evtIt->event()->id().replace(u':', u'_').replace(u'/', u'_').replace(u'+', u'_') + fileNameToDownload(eventId)));
connect(
this,
&Room::fileTransferCompleted,
this,
[this, eventId](QString id, QUrl localFile, FileSourceInfo fileMetadata) {
Q_UNUSED(localFile);
Q_UNUSED(fileMetadata);
if (id == eventId) {
auto transferInfo = fileTransferInfo(eventId);
UrlHelper helper; UrlHelper helper;
helper.openUrl(transferInfo.localPath); helper.openUrl(transferInfo.localPath);
} else {
downloadFile(eventId,
QUrl(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u'/'
+ event->id().replace(u':', u'_').replace(u'/', u'_').replace(u'+', u'_') + fileNameToDownload(eventId)));
connect(
this,
&Room::fileTransferCompleted,
this,
[this, eventId](QString id, QUrl localFile, FileSourceInfo fileMetadata) {
Q_UNUSED(localFile);
Q_UNUSED(fileMetadata);
if (id == eventId) {
auto transferInfo = fileTransferInfo(eventId);
UrlHelper helper;
helper.openUrl(transferInfo.localPath);
}
},
static_cast<Qt::ConnectionType>(Qt::SingleShotConnection));
} }
}, }
static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); }
} }
void NeoChatRoom::copyEventMedia(const QString &eventId) void NeoChatRoom::copyEventMedia(const QString &eventId)
{ {
const auto evtIt = findInTimeline(eventId); const auto evtIt = findInTimeline(eventId);
if (evtIt != messageEvents().rend() && is<RoomMessageEvent>(**evtIt)) {
if (evtIt == messageEvents().rend() || !is<RoomMessageEvent>(**evtIt)) { const auto event = evtIt->viewAs<RoomMessageEvent>();
return; if (event->has<EventContent::FileContent>()) {
} const auto transferInfo = fileTransferInfo(eventId);
const auto event = evtIt->viewAs<RoomMessageEvent>(); if (transferInfo.completed()) {
if (!event->has<EventContent::FileContentBase>()) { Clipboard clipboard;
return; clipboard.setImage(transferInfo.localPath);
} } else {
const auto transferInfo = fileTransferInfo(eventId); downloadFile(eventId,
if (transferInfo.completed()) { QUrl(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u'/'
Clipboard clipboard; + event->id().replace(u':', u'_').replace(u'/', u'_').replace(u'+', u'_') + fileNameToDownload(eventId)));
clipboard.setImage(transferInfo.localPath); connect(
} else { this,
downloadFile(eventId, &Room::fileTransferCompleted,
QUrl(u"file:"_s + QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u'/' this,
+ event->id().replace(u':', u'_').replace(u'/', u'_').replace(u'+', u'_') + fileNameToDownload(eventId))); [this, eventId](QString id, QUrl localFile, FileSourceInfo fileMetadata) {
connect( Q_UNUSED(localFile);
this, Q_UNUSED(fileMetadata);
&Room::fileTransferCompleted, if (id == eventId) {
this, auto transferInfo = fileTransferInfo(eventId);
[this, eventId](QString id, QUrl localFile, FileSourceInfo fileMetadata) { Clipboard clipboard;
Q_UNUSED(localFile); clipboard.setImage(transferInfo.localPath);
Q_UNUSED(fileMetadata); }
if (id == eventId) { },
auto transferInfo = fileTransferInfo(eventId); static_cast<Qt::ConnectionType>(Qt::SingleShotConnection));
Clipboard clipboard; }
clipboard.setImage(transferInfo.localPath); }
}
},
Qt::SingleShotConnection);
} }
} }

View File

@@ -557,7 +557,7 @@ public:
* responsibility of the caller to ensure that they only ask for objects * responsibility of the caller to ensure that they only ask for objects
* for real senders. * for real senders.
*/ */
Q_INVOKABLE NeochatRoomMember *qmlSafeMember(const QString &memberId); NeochatRoomMember *qmlSafeMember(const QString &memberId);
/** /**
* @brief Pin a message in the room. * @brief Pin a message in the room.

View File

@@ -85,15 +85,6 @@ void LoginHelper::init()
account.sync(); account.sync();
m_accountManager->addConnection(m_connection); m_accountManager->addConnection(m_connection);
m_accountManager->setActiveConnection(m_connection); m_accountManager->setActiveConnection(m_connection);
disconnect(m_connection, nullptr, this, nullptr);
connect(
m_connection.get(),
&NeoChatConnection::syncDone,
this,
[this]() {
Q_EMIT loaded();
},
Qt::SingleShotConnection);
m_connection = nullptr; m_connection = nullptr;
}); });
connect(m_connection, &NeoChatConnection::networkError, this, [this](QString error, const QString &, int, int) { connect(m_connection, &NeoChatConnection::networkError, this, [this](QString error, const QString &, int, int) {
@@ -114,6 +105,15 @@ void LoginHelper::init()
connect(m_connection, &NeoChatConnection::resolveError, this, [this](QString error) { connect(m_connection, &NeoChatConnection::resolveError, this, [this](QString error) {
Q_EMIT m_connection->errorOccured(i18n("Network Error: %1", std::move(error))); Q_EMIT m_connection->errorOccured(i18n("Network Error: %1", std::move(error)));
}); });
connect(
m_connection.get(),
&NeoChatConnection::syncDone,
this,
[this]() {
Q_EMIT loaded();
},
Qt::SingleShotConnection);
} }
void LoginHelper::setHomeserverReachable(bool reachable) void LoginHelper::setHomeserverReachable(bool reachable)

View File

@@ -1,107 +0,0 @@
# SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
# SPDX-License-Identifier: BSD-2-Clause
qt_add_library(MessageContent STATIC)
ecm_add_qml_module(MessageContent GENERATE_PLUGIN_SOURCE
URI org.kde.neochat.messagecontent
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/messagecontent
QML_FILES
BaseMessageComponentChooser.qml
MessageComponentChooser.qml
ReplyMessageComponentChooser.qml
AuthorComponent.qml
AudioComponent.qml
ChatBarComponent.qml
CodeComponent.qml
EncryptedComponent.qml
FetchButtonComponent.qml
FileComponent.qml
ImageComponent.qml
ItineraryComponent.qml
ItineraryReservationComponent.qml
JourneySectionStopDelegateLineSegment.qml
TransportIcon.qml
FoodReservationComponent.qml
TrainReservationComponent.qml
FlightReservationComponent.qml
HotelReservationComponent.qml
LinkPreviewComponent.qml
LinkPreviewLoadComponent.qml
LiveLocationComponent.qml
LoadComponent.qml
LocationComponent.qml
MimeComponent.qml
PdfPreviewComponent.qml
PollComponent.qml
QuoteComponent.qml
ReactionComponent.qml
ReplyAuthorComponent.qml
ReplyButtonComponent.qml
ReplyComponent.qml
StateComponent.qml
TextComponent.qml
ThreadBodyComponent.qml
VideoComponent.qml
SOURCES
contentprovider.cpp
mediasizehelper.cpp
pollhandler.cpp
models/itinerarymodel.cpp
models/linemodel.cpp
models/messagecontentmodel.cpp
models/pollanswermodel.cpp
models/reactionmodel.cpp
models/threadmodel.cpp
RESOURCES
images/bike.svg
images/bus.svg
images/cablecar.svg
images/car.svg
images/coach.svg
images/couchettecar.svg
images/elevator.svg
images/escalator.svg
images/ferry.svg
images/flight.svg
images/foodestablishment.svg
images/funicular.svg
images/longdistancetrain.svg
images/rapidtransit.svg
images/seat.svg
images/shuttle.svg
images/sleepingcar.svg
images/stairs.svg
images/subway.svg
images/taxi.svg
images/train.svg
images/tramway.svg
images/transfer.svg
images/wait.svg
images/walk.svg
DEPENDENCIES
QtQuick
)
configure_file(config-neochat.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-neochat.h)
ecm_qt_declare_logging_category(MessageContent
HEADER "messagemodel_logging.h"
IDENTIFIER "Message"
CATEGORY_NAME "org.kde.neochat.messagemodel"
DESCRIPTION "Neochat: messagemodel"
DEFAULT_SEVERITY Info
EXPORT NEOCHAT
)
target_include_directories(MessageContent PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models)
target_link_libraries(MessageContent PRIVATE
Qt::Core
Qt::Quick
Qt::QuickControls2
KF6::Kirigami
LibNeoChat
)
if(NOT ANDROID)
target_link_libraries(MessageContent PUBLIC KF6::SyntaxHighlighting)
endif()

View File

@@ -27,7 +27,6 @@
"Name[nl]": "Tobias Fella", "Name[nl]": "Tobias Fella",
"Name[nn]": "Tobias Fella", "Name[nn]": "Tobias Fella",
"Name[pl]": "Tobias Fella", "Name[pl]": "Tobias Fella",
"Name[pt_BR]": "Tobias Fella",
"Name[ru]": "Tobias Fella", "Name[ru]": "Tobias Fella",
"Name[sa]": "टोबियास फेला", "Name[sa]": "टोबियास फेला",
"Name[sk]": "Tobias Fella", "Name[sk]": "Tobias Fella",
@@ -36,6 +35,7 @@
"Name[ta]": "டோபியாஸ் ஃபெல்லா", "Name[ta]": "டோபியாஸ் ஃபெல்லா",
"Name[tr]": "Tobias Fella", "Name[tr]": "Tobias Fella",
"Name[uk]": "Tobias Fella", "Name[uk]": "Tobias Fella",
"Name[x-test]": "xxTobias Fellaxx",
"Name[zh_CN]": "Tobias Fella", "Name[zh_CN]": "Tobias Fella",
"Name[zh_TW]": "Tobias Fella" "Name[zh_TW]": "Tobias Fella"
} }
@@ -65,7 +65,6 @@
"Description[nl]": "Delen via NeoChat", "Description[nl]": "Delen via NeoChat",
"Description[nn]": "Del via NeoChat", "Description[nn]": "Del via NeoChat",
"Description[pl]": "Udostępnij przez NeoChat", "Description[pl]": "Udostępnij przez NeoChat",
"Description[pt_BR]": "Compartilhar via NeoChat",
"Description[ru]": "Опубликовать в NeoChat", "Description[ru]": "Опубликовать в NeoChat",
"Description[sa]": "NeoChat मार्गेण साझां कुर्वन्तु", "Description[sa]": "NeoChat मार्गेण साझां कुर्वन्तु",
"Description[sl]": "Deli prek NeoChat", "Description[sl]": "Deli prek NeoChat",
@@ -73,6 +72,7 @@
"Description[ta]": "நியோச்சாட் மூலம் பகிர்", "Description[ta]": "நியோச்சாட் மூலம் பகிர்",
"Description[tr]": "NeoChat ile Paylaş", "Description[tr]": "NeoChat ile Paylaş",
"Description[uk]": "Оприлюднити за допомогою NeoChat", "Description[uk]": "Оприлюднити за допомогою NeoChat",
"Description[x-test]": "xxShare via NeoChatxx",
"Description[zh_CN]": "通过 NeoChat 分享", "Description[zh_CN]": "通过 NeoChat 分享",
"Description[zh_TW]": "透過 NeoChat 分享", "Description[zh_TW]": "透過 NeoChat 分享",
"Icon": "org.kde.neochat.tray", "Icon": "org.kde.neochat.tray",
@@ -103,7 +103,6 @@
"Name[nl]": "NeoChat", "Name[nl]": "NeoChat",
"Name[nn]": "NeoChat", "Name[nn]": "NeoChat",
"Name[pl]": "NeoChat", "Name[pl]": "NeoChat",
"Name[pt_BR]": "NeoChat",
"Name[ru]": "NeoChat", "Name[ru]": "NeoChat",
"Name[sa]": "नवचैट्", "Name[sa]": "नवचैट्",
"Name[sk]": "NeoChat", "Name[sk]": "NeoChat",
@@ -112,6 +111,7 @@
"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",
"X-Purpose-ActionDisplay": "NeoChat" "X-Purpose-ActionDisplay": "NeoChat"

View File

@@ -14,10 +14,4 @@ ecm_add_qml_module(RoomInfo GENERATE_PLUGIN_SOURCE
LocationsPage.qml LocationsPage.qml
RoomPinnedMessagesPage.qml RoomPinnedMessagesPage.qml
RoomSearchPage.qml RoomSearchPage.qml
SOURCES
locationhelper.cpp
)
target_link_libraries(RoomInfo PRIVATE
Qt::Core
) )

View File

@@ -17,22 +17,13 @@ import org.kde.neochat
Kirigami.Page { Kirigami.Page {
id: root id: root
Kirigami.ColumnView.interactiveResizeEnabled: true /**
Kirigami.ColumnView.minimumWidth: _private.collapsedSize + spaceDrawer.width + 1 * @brief The current width of the room list.
Kirigami.ColumnView.maximumWidth: _private.defaultWidth + spaceDrawer.width + 1 *
Kirigami.ColumnView.onInteractiveResizingChanged: { * @note Other objects can access the value but the private function makes sure
if (!Kirigami.ColumnView.interactiveResizing && collapsed) { * that only the internal members can modify it.
Kirigami.ColumnView.preferredWidth = root.Kirigami.ColumnView.minimumWidth; */
} readonly property int currentWidth: _private.currentWidth + spaceDrawer.width + 1
}
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;
}
}
required property NeoChatConnection connection required property NeoChatConnection connection
@@ -40,6 +31,10 @@ Kirigami.Page {
signal search signal search
onCurrentWidthChanged: pageStack.defaultColumnWidth = root.currentWidth
Component.onCompleted: pageStack.defaultColumnWidth = root.currentWidth
onCollapsedChanged: { onCollapsedChanged: {
if (collapsed) { if (collapsed) {
RoomManager.sortFilterRoomTreeModel.filterText = ""; RoomManager.sortFilterRoomTreeModel.filterText = "";
@@ -249,6 +244,49 @@ Kirigami.Page {
sourceComponent: Kirigami.Settings.isMobile ? exploreComponentMobile : userInfoDesktop 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 { Component {
id: userInfo id: userInfo
UserInfo { UserInfo {

View File

@@ -38,14 +38,26 @@ QQC2.ItemDelegate {
Keys.onSpacePressed: root.treeView.toggleExpanded(row) Keys.onSpacePressed: root.treeView.toggleExpanded(row)
contentItem: RowLayout { contentItem: RowLayout {
spacing: 0 spacing: Kirigami.Units.largeSpacing
Kirigami.ListSectionHeader {
Kirigami.Heading {
Layout.alignment: Qt.AlignVCenter
visible: !root.collapsed
opacity: 0.7
level: 5
type: Kirigami.Heading.Primary
text: root.collapsed ? "" : model.displayName
elide: Text.ElideRight
// we override the Primary type's font weight (DemiBold) for Bold for contrast with small text
font.weight: Font.Bold
}
Kirigami.Separator {
Layout.fillWidth: true Layout.fillWidth: true
visible: !root.collapsed visible: !root.collapsed
horizontalPadding: 0 Layout.alignment: Qt.AlignVCenter
topPadding: 0
bottomPadding: 0
text: root.collapsed ? "" : root.displayName
} }
QQC2.ToolButton { QQC2.ToolButton {
id: collapseButton id: collapseButton

View File

@@ -95,19 +95,31 @@ RowLayout {
window: QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow window: QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow
} }
} }
QQC2.ToolButton {
display: QQC2.Button.IconOnly
action: Kirigami.Action {
text: i18n("Open Settings")
icon.name: "settings-configure-symbolic"
onTriggered: {
NeoChatSettingsView.open();
}
}
QQC2.ToolTip.text: text Kirigami.ActionToolBar {
QQC2.ToolTip.visible: hovered alignment: Qt.AlignRight
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay display: QQC2.Button.IconOnly
Layout.fillWidth: true
Layout.preferredWidth: maximumContentWidth
actions: [
Kirigami.Action {
text: i18n("Switch User")
icon.name: "system-switch-user"
shortcut: "Ctrl+U"
onTriggered: accountSwitchDialog.createObject(QQC2.Overlay.overlay, {
connection: root.connection
}).open();
},
Kirigami.Action {
text: i18n("Open Settings")
icon.name: "settings-configure-symbolic"
onTriggered: {
NeoChatSettingsView.open();
}
}
]
} }
Component { Component {

View File

@@ -138,7 +138,7 @@ FormCard.FormCardPage {
icon.name: "document-save-symbolic" icon.name: "document-save-symbolic"
onClicked: { onClicked: {
if (!root.connection.setAvatar(avatar.source)) { if (!root.connection.setAvatar(avatar.source)) {
(root.Window.window as Kirigami.ApplicationWindow).showPassiveNotification("The Avatar could not be set"); showPassiveNotification("The Avatar could not be set");
} }
if (root.connection.localUser.displayName !== name.text) { if (root.connection.localUser.displayName !== name.text) {
root.connection.localUser.rename(name.text); root.connection.localUser.rename(name.text);

View File

@@ -134,45 +134,9 @@ FormCard.FormCardPage {
} }
} }
FormCard.FormHeader { FormCard.FormHeader {
title: i18nc("@title", "Timeline") title: i18n("Timeline Events")
} }
FormCard.FormCard { FormCard.FormCard {
FormCard.FormComboBoxDelegate {
id: markAsReadCombo
text: i18n("Mark messages as read when:")
textRole: "name"
valueRole: "value"
model: [
{
name: i18n("Never"),
value: 0
},
{
name: i18nc("@item:inlistbox As in mark messages in the room as read when entering the room", "Entering the room"),
value: 1
},
{
name: i18nc("@item:inlistbox As in mark messages in the room as read when entering the room and all messages are visible on screen", "Entering the room and all unread messages are visible"),
value: 2
},
{
name: i18nc("@item:inlistbox As in mark messages in the room as read when exiting the room", "Exiting the room"),
value: 3
},
{
name: i18nc("@item:inlistbox As in mark messages in the room as read when exiting the room and all messages are visible on screen", "Exiting the room and all unread messages are visible"),
value: 4
}
]
Component.onCompleted: currentIndex = NeoChatConfig.markReadCondition
onCurrentValueChanged: NeoChatConfig.markReadCondition = currentValue
}
FormCard.FormDelegateSeparator {
above: markAsReadCombo
below: showDeletedMessages
}
FormCard.FormCheckDelegate { FormCard.FormCheckDelegate {
id: showDeletedMessages id: showDeletedMessages
text: i18n("Show deleted messages") text: i18n("Show deleted messages")
@@ -345,16 +309,8 @@ FormCard.FormCardPage {
FormCard.FormCard { FormCard.FormCard {
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
icon.name: "kt-restore-defaults-symbolic" icon.name: "kt-restore-defaults-symbolic"
text: i18nc("@action:button", "Reset all configuration values to their default") text: i18nc("@action:button", "Reset All Configuration Values to Their Default")
onClicked: resetDialog.open() onClicked: Controller.revertToDefaultConfig()
} }
} }
Kirigami.PromptDialog {
id: resetDialog
title: i18nc("@title:dialog", "Reset Configuration")
subtitle: i18nc("@info", "Do you really want to reset all options to their default values?")
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
onAccepted: Controller.revertToDefaultConfig()
anchors.centerIn: QQC2.Overlay.overlay
}
} }

View File

@@ -59,7 +59,6 @@ KirigamiSettings.ConfigurationView {
visible: root.connection !== null visible: root.connection !== null
}, },
KirigamiSettings.ConfigurationModule { KirigamiSettings.ConfigurationModule {
id: accountsModule
moduleId: "accounts" moduleId: "accounts"
text: i18n("Accounts") text: i18n("Accounts")
icon.name: "preferences-system-users-symbolic" icon.name: "preferences-system-users-symbolic"
@@ -112,14 +111,4 @@ KirigamiSettings.ConfigurationView {
category: i18nc("@title:group", "About") category: i18nc("@title:group", "About")
} }
] ]
function openWithInitialProperties(defaultModule = '', initialProperties): void {
let module = modules.find(module => module.moduleId == defaultModule) ?? null;
if (module) {
module.initialProperties = () => {
return initialProperties;
}
}
root.open(defaultModule);
}
} }

View File

@@ -55,9 +55,9 @@ FormCard.FormCardPage {
} }
FormCard.FormRadioDelegate { FormCard.FormRadioDelegate {
text: i18nc("@option:check", "Space members") text: i18nc("@option:check", "Space members")
description: i18n("Anyone in the selected spaces can find and join.") + (!["8", "9", "10", "11", "12"].includes(room.version) ? `\n${needUpgradeRoom}` : "") description: i18n("Anyone in the selected spaces can find and join.") + (!["8", "9", "10"].includes(room.version) ? `\n${needUpgradeRoom}` : "")
checked: room.joinRule === JoinRule.Restricted checked: room.joinRule === JoinRule.Restricted
enabled: room.canSendState("m.room.join_rules") && ["8", "9", "10", "11", "12"].includes(room.version) enabled: room.canSendState("m.room.join_rules") && ["8", "9", "10"].includes(room.version)
onCheckedChanged: if (checked && room.joinRule != JoinRule.Restricted) { onCheckedChanged: if (checked && room.joinRule != JoinRule.Restricted) {
selectSpacesDialog.createObject(QQC2.Overlay.overlay).open(); selectSpacesDialog.createObject(QQC2.Overlay.overlay).open();
} }
@@ -83,10 +83,10 @@ FormCard.FormCardPage {
} }
FormCard.FormRadioDelegate { FormCard.FormRadioDelegate {
text: i18nc("@option:check", "Knock") text: i18nc("@option:check", "Knock")
description: i18n("People not in the room need to request an invite to join the room.") + (!["7", "8", "9", "10", "11", "12"].includes(room.version) ? `\n${needUpgradeRoom}` : "") description: i18n("People not in the room need to request an invite to join the room.") + (!["7", "8", "9", "10"].includes(room.version) ? `\n${needUpgradeRoom}` : "")
checked: room.joinRule === JoinRule.Knock checked: room.joinRule === JoinRule.Knock
// https://spec.matrix.org/v1.4/rooms/#feature-matrix // https://spec.matrix.org/v1.4/rooms/#feature-matrix
enabled: room.canSendState("m.room.join_rules") && ["7", "8", "9", "10", "11", "12"].includes(room.version) enabled: room.canSendState("m.room.join_rules") && ["7", "8", "9", "10"].includes(room.version)
onCheckedChanged: if (checked && room.joinRule != JoinRule.Knock) { onCheckedChanged: if (checked && room.joinRule != JoinRule.Knock) {
root.room.joinRule = JoinRule.Knock; root.room.joinRule = JoinRule.Knock;
} }

View File

@@ -18,6 +18,15 @@ using namespace Quotient;
DevicesModel::DevicesModel(QObject *parent) DevicesModel::DevicesModel(QObject *parent)
: QAbstractListModel(parent) : QAbstractListModel(parent)
{ {
connect(m_connection, &Connection::sessionVerified, this, [this](const QString &, const QString &deviceId) {
const auto it = std::find_if(m_devices.begin(), m_devices.end(), [deviceId](const Quotient::Device &device) {
return device.deviceId == deviceId;
});
if (it != m_devices.end()) {
const auto index = this->index(it - m_devices.begin());
Q_EMIT dataChanged(index, index, {Type});
}
});
} }
void DevicesModel::fetchDevices() void DevicesModel::fetchDevices()
@@ -153,15 +162,6 @@ void DevicesModel::setConnection(NeoChatConnection *connection)
disconnect(m_connection, nullptr, this, nullptr); disconnect(m_connection, nullptr, this, nullptr);
} }
m_connection = connection; m_connection = connection;
connect(m_connection, &Connection::sessionVerified, this, [this](const QString &, const QString &deviceId) {
const auto it = std::find_if(m_devices.begin(), m_devices.end(), [deviceId](const Quotient::Device &device) {
return device.deviceId == deviceId;
});
if (it != m_devices.end()) {
const auto index = this->index(it - m_devices.begin());
Q_EMIT dataChanged(index, index, {Type});
}
});
Q_EMIT connectionChanged(); Q_EMIT connectionChanged();
fetchDevices(); fetchDevices();

View File

@@ -9,7 +9,6 @@ import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.delegates as Delegates import org.kde.kirigamiaddons.delegates as Delegates
import org.kde.kirigamiaddons.labs.components as Components import org.kde.kirigamiaddons.labs.components as Components
import org.kde.neochat
import org.kde.neochat.libneochat import org.kde.neochat.libneochat
Item { Item {

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