Compare commits
1 Commits
v24.07.90
...
work/tobia
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd5dd1dd54 |
@@ -9,7 +9,7 @@ cmake_minimum_required(VERSION 3.16)
|
||||
# KDE Applications version, managed by release script.
|
||||
set(RELEASE_SERVICE_VERSION_MAJOR "24")
|
||||
set(RELEASE_SERVICE_VERSION_MINOR "07")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "90")
|
||||
set(RELEASE_SERVICE_VERSION_MICRO "70")
|
||||
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
|
||||
|
||||
@@ -36,6 +36,8 @@ private Q_SLOTS:
|
||||
|
||||
void eventId();
|
||||
void nullEventId();
|
||||
void author();
|
||||
void nullAuthor();
|
||||
void authorDisplayName();
|
||||
void nullAuthorDisplayName();
|
||||
void singleLineSidplayName();
|
||||
@@ -73,6 +75,8 @@ private Q_SLOTS:
|
||||
void nullThread();
|
||||
void location();
|
||||
void nullLocation();
|
||||
void readMarkers();
|
||||
void nullReadMarkers();
|
||||
};
|
||||
|
||||
void EventHandlerTest::initTestCase()
|
||||
@@ -94,6 +98,32 @@ void EventHandlerTest::nullEventId()
|
||||
QCOMPARE(noEventHandler.getId(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::author()
|
||||
{
|
||||
auto event = room->messageEvents().at(0).get();
|
||||
auto author = room->member(event->senderId());
|
||||
EventHandler eventHandler(room, event);
|
||||
|
||||
auto eventHandlerAuthor = eventHandler.getAuthor();
|
||||
|
||||
QCOMPARE(eventHandlerAuthor.isLocalMember(), author.id() == room->localMember().id());
|
||||
QCOMPARE(eventHandlerAuthor.id(), author.id());
|
||||
QCOMPARE(eventHandlerAuthor.displayName(), author.displayName());
|
||||
QCOMPARE(eventHandlerAuthor.avatarUrl(), author.avatarUrl());
|
||||
QCOMPARE(eventHandlerAuthor.avatarMediaId(), author.avatarMediaId());
|
||||
QCOMPARE(eventHandlerAuthor.color(), author.color());
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullAuthor()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getAuthor(), RoomMember());
|
||||
|
||||
EventHandler noEventHandler(room, nullptr);
|
||||
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_event set to nullptr. Returning empty user.");
|
||||
QCOMPARE(noEventHandler.getAuthor(), RoomMember());
|
||||
}
|
||||
|
||||
void EventHandlerTest::authorDisplayName()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(1).get());
|
||||
@@ -163,7 +193,6 @@ void EventHandlerTest::timeString()
|
||||
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().time(), QLocale::LongFormat));
|
||||
QCOMPARE(eventHandler.getTimeString(true, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
|
||||
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::LongFormat));
|
||||
QCOMPARE(eventHandler.getTimeString(QStringLiteral("hh:mm")), QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toString(QStringLiteral("hh:mm")));
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullTimeString()
|
||||
@@ -492,5 +521,59 @@ void EventHandlerTest::nullLocation()
|
||||
QCOMPARE(emptyHandler.getLocationAssetType(), QString());
|
||||
}
|
||||
|
||||
void EventHandlerTest::readMarkers()
|
||||
{
|
||||
EventHandler eventHandler(room, room->messageEvents().at(0).get());
|
||||
QCOMPARE(eventHandler.hasReadMarkers(), true);
|
||||
|
||||
auto readMarkers = eventHandler.getReadMarkers();
|
||||
|
||||
QCOMPARE(readMarkers.size(), 1);
|
||||
QCOMPARE(readMarkers[0].id(), QStringLiteral("@alice:example.org"));
|
||||
|
||||
QCOMPARE(eventHandler.getNumberExcessReadMarkers(), QString());
|
||||
QCOMPARE(eventHandler.getReadMarkersString(), QStringLiteral("1 user: Alice Margatroid"));
|
||||
|
||||
EventHandler eventHandler2(room, room->messageEvents().at(2).get());
|
||||
QCOMPARE(eventHandler2.hasReadMarkers(), true);
|
||||
|
||||
readMarkers = eventHandler2.getReadMarkers();
|
||||
|
||||
QCOMPARE(readMarkers.size(), 5);
|
||||
|
||||
QCOMPARE(eventHandler2.getNumberExcessReadMarkers(), QStringLiteral("+ 1"));
|
||||
// There are no guarantees on the order of the users it will be different every time so don't match the whole string.
|
||||
QCOMPARE(eventHandler2.getReadMarkersString().startsWith(QStringLiteral("6 users:")), true);
|
||||
}
|
||||
|
||||
void EventHandlerTest::nullReadMarkers()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "hasReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.hasReadMarkers(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReadMarkers(), QList<Quotient::RoomMember>());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getNumberExcessReadMarkers called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getNumberExcessReadMarkers(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkersString called with m_room set to nullptr.");
|
||||
QCOMPARE(emptyHandler.getReadMarkersString(), QString());
|
||||
|
||||
EventHandler noEventHandler(room, nullptr);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "hasReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.hasReadMarkers(), false);
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReadMarkers(), QList<Quotient::RoomMember>());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getNumberExcessReadMarkers called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getNumberExcessReadMarkers(), QString());
|
||||
|
||||
QTest::ignoreMessage(QtWarningMsg, "getReadMarkersString called with m_event set to nullptr.");
|
||||
QCOMPARE(noEventHandler.getReadMarkersString(), QString());
|
||||
}
|
||||
|
||||
QTEST_MAIN(EventHandlerTest)
|
||||
#include "eventhandlertest.moc"
|
||||
|
||||
@@ -95,7 +95,6 @@
|
||||
<p xml:lang="ka">NeoChat ჩატის აპია, რომელიც საშუალება გაძლევთ, Matrix-ის ქსელის საშუალებები ბოლომდე გამოიყენოთ. ის გაძლევთ უსაფრთხო გზას, გააგზავნოთ ტექსტური შეტყობინებები, ვიდეოებ და აუდიოფაილები თქვენს ოჯახთან, კოლეგებთან და მეგობრებთან.</p>
|
||||
<p xml:lang="lv">„NeoChat“ ir tērzēšanas programma, kas ļauj pilnvērtīgi izmantot „Matrix“ tīklu. Tā sniedz drošu veidu teksta ziņu, video un audio sūtīšanai ģimenes locekļiem, kolēģiem un draugiem.</p>
|
||||
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
|
||||
<p xml:lang="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="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>
|
||||
@@ -329,7 +328,6 @@
|
||||
<caption xml:lang="ka">აღმოაჩინეთ ახალი საზოგადოებები Matrix Spaces-თან ერთად</caption>
|
||||
<caption xml:lang="lv">Atklājiet jaunas kopienas ar „Matrix“ telpām</caption>
|
||||
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
|
||||
<caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption>
|
||||
<caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption>
|
||||
<caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption>
|
||||
<caption xml:lang="sv">Upptäck nya gemenskaper med Matrix Spaces</caption>
|
||||
|
||||
@@ -18,7 +18,6 @@ Name[eu]=NeoChat
|
||||
Name[fi]=NeoChat
|
||||
Name[fr]=NeoChat
|
||||
Name[gl]=NeoChat
|
||||
Name[he]=NeoChat
|
||||
Name[hu]=NeoChat
|
||||
Name[ia]=Neochat
|
||||
Name[id]=NeoChat
|
||||
@@ -60,7 +59,6 @@ GenericName[eu]=Matrix bezeroa
|
||||
GenericName[fi]=Matrix-asiakas
|
||||
GenericName[fr]=Client « Matrix »
|
||||
GenericName[gl]=Cliente de Matrix
|
||||
GenericName[he]=לקוח Matrix
|
||||
GenericName[hu]=Matrix kliens
|
||||
GenericName[ia]=Cliente de Matrice
|
||||
GenericName[id]=Klien Matrix
|
||||
@@ -101,7 +99,6 @@ Comment[eu]=Matrix protokolorako bezeroa
|
||||
Comment[fi]=Asiakas Matrix-yhteyskäytännölle
|
||||
Comment[fr]=Client pour le protocole « Matrix »
|
||||
Comment[gl]=Cliente para o protocolo Matrix.
|
||||
Comment[he]=לקוח לפרוטוקול Matrix
|
||||
Comment[hu]=Kliens a Matrix protokollhoz
|
||||
Comment[ia]=Cliente per le protocollo de Matrix
|
||||
Comment[id]=Klien untuk protokol Matrix
|
||||
|
||||
421
po/ar/neochat.po
421
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
389
po/az/neochat.po
389
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
547
po/ca/neochat.po
547
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
370
po/cs/neochat.po
370
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
375
po/da/neochat.po
375
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
378
po/de/neochat.po
378
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
382
po/el/neochat.po
382
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
369
po/eo/neochat.po
369
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
378
po/es/neochat.po
378
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
769
po/eu/neochat.po
769
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
376
po/fi/neochat.po
376
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
373
po/fr/neochat.po
373
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
5479
po/gl/neochat.po
5479
po/gl/neochat.po
File diff suppressed because it is too large
Load Diff
725
po/hu/neochat.po
725
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
376
po/ia/neochat.po
376
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
378
po/id/neochat.po
378
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
379
po/ie/neochat.po
379
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
417
po/it/neochat.po
417
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
375
po/ja/neochat.po
375
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
376
po/ka/neochat.po
376
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
374
po/ko/neochat.po
374
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
381
po/lt/neochat.po
381
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
373
po/lv/neochat.po
373
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
372
po/nl/neochat.po
372
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
1094
po/nn/neochat.po
1094
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
385
po/pa/neochat.po
385
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
378
po/pl/neochat.po
378
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
378
po/pt/neochat.po
378
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
378
po/ru/neochat.po
378
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
391
po/sk/neochat.po
391
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
380
po/sl/neochat.po
380
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
378
po/sv/neochat.po
378
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
372
po/ta/neochat.po
372
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
375
po/tr/neochat.po
375
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
382
po/uk/neochat.po
382
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -9,8 +9,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: neochat\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
|
||||
"POT-Creation-Date: 2024-07-30 02:34+0000\n"
|
||||
"PO-Revision-Date: 2024-08-06 05:24+0900\n"
|
||||
"POT-Creation-Date: 2024-07-15 00:38+0000\n"
|
||||
"PO-Revision-Date: 2024-07-08 15:59+0900\n"
|
||||
"Last-Translator: Kisaragi Hiu <mail@kisaragi-hiu.com>\n"
|
||||
"Language-Team: Traditional Chinese <zh-l10n@lists.slat.org>\n"
|
||||
"Language: zh_TW\n"
|
||||
@@ -365,7 +365,7 @@ msgid "Custom"
|
||||
msgstr "自訂"
|
||||
|
||||
#: src/enums/powerlevel.cpp:88 src/models/permissionsmodel.cpp:162
|
||||
#: src/models/userlistmodel.cpp:114
|
||||
#: src/models/userlistmodel.cpp:113
|
||||
#, kde-format
|
||||
msgctxt ""
|
||||
"%1 is the name of the power level, e.g. admin and %2 is the value that "
|
||||
@@ -373,349 +373,367 @@ msgctxt ""
|
||||
msgid "%1 (%2)"
|
||||
msgstr "%1 (%2)"
|
||||
|
||||
#: src/eventhandler.cpp:297 src/eventhandler.cpp:505
|
||||
#: src/models/messagecontentmodel.cpp:230 src/models/messageeventmodel.cpp:438
|
||||
#: src/eventhandler.cpp:304 src/eventhandler.cpp:509
|
||||
#: src/models/messagecontentmodel.cpp:223 src/models/messageeventmodel.cpp:459
|
||||
#, kde-format
|
||||
msgid "<i>[This message was deleted]</i>"
|
||||
msgstr "<i>[此訊息已被刪除]</i>"
|
||||
|
||||
#: src/eventhandler.cpp:297 src/models/messagecontentmodel.cpp:231
|
||||
#: src/models/messageeventmodel.cpp:439
|
||||
#: src/eventhandler.cpp:304 src/models/messagecontentmodel.cpp:224
|
||||
#: src/models/messageeventmodel.cpp:460
|
||||
#, kde-format
|
||||
msgid "<i>[This message was deleted: %1]</i>"
|
||||
msgstr "<i>[此訊息已被刪除:%1]</i>"
|
||||
|
||||
#: src/eventhandler.cpp:331
|
||||
#: src/eventhandler.cpp:335
|
||||
#, kde-format
|
||||
msgid "reinvited %1 to the room"
|
||||
msgstr "已重新邀請 %1 到聊天室裡"
|
||||
|
||||
#: src/eventhandler.cpp:333
|
||||
#: src/eventhandler.cpp:337
|
||||
#, kde-format
|
||||
msgctxt "Optional reason for an invitation"
|
||||
msgid ": %1"
|
||||
msgstr ":%1"
|
||||
|
||||
#: src/eventhandler.cpp:342 src/eventhandler.cpp:529
|
||||
#: src/eventhandler.cpp:346 src/eventhandler.cpp:533
|
||||
#, kde-format
|
||||
msgid "joined the room (repeated)"
|
||||
msgstr "已加入聊天室(重複)"
|
||||
|
||||
#: src/eventhandler.cpp:344
|
||||
#: src/eventhandler.cpp:348
|
||||
#, kde-format
|
||||
msgid "invited %1 to the room"
|
||||
msgstr "已邀請 %1 到聊天室裡"
|
||||
|
||||
#: src/eventhandler.cpp:344 src/eventhandler.cpp:531
|
||||
#: src/eventhandler.cpp:348 src/eventhandler.cpp:535
|
||||
#, kde-format
|
||||
msgid "joined the room"
|
||||
msgstr "已加入聊天室"
|
||||
|
||||
#: src/eventhandler.cpp:348
|
||||
#: src/eventhandler.cpp:352
|
||||
#, kde-format
|
||||
msgid ": %1"
|
||||
msgstr ":%1"
|
||||
|
||||
#: src/eventhandler.cpp:355 src/eventhandler.cpp:539
|
||||
#: src/eventhandler.cpp:359 src/eventhandler.cpp:543
|
||||
#, kde-format
|
||||
msgctxt "their refers to a singular user"
|
||||
msgid "cleared their display name"
|
||||
msgstr "將其顯示名稱清除了"
|
||||
|
||||
#: src/eventhandler.cpp:358
|
||||
#: src/eventhandler.cpp:362
|
||||
#, kde-format
|
||||
msgctxt "their refers to a singular user"
|
||||
msgid "changed their display name to %1"
|
||||
msgstr "將其顯示名稱變更為 %1"
|
||||
|
||||
#: src/eventhandler.cpp:364 src/eventhandler.cpp:546
|
||||
#: src/eventhandler.cpp:368 src/eventhandler.cpp:550
|
||||
#, kde-format
|
||||
msgid " and "
|
||||
msgstr " 和 "
|
||||
|
||||
#: src/eventhandler.cpp:367 src/eventhandler.cpp:549
|
||||
#: src/eventhandler.cpp:371 src/eventhandler.cpp:553
|
||||
#, kde-format
|
||||
msgctxt "their refers to a singular user"
|
||||
msgid "cleared their avatar"
|
||||
msgstr "將其頭貼清除了"
|
||||
|
||||
#: src/eventhandler.cpp:369 src/eventhandler.cpp:551
|
||||
#: src/eventhandler.cpp:373 src/eventhandler.cpp:555
|
||||
#, kde-format
|
||||
msgid "set an avatar"
|
||||
msgstr "設定了頭貼"
|
||||
|
||||
#: src/eventhandler.cpp:371 src/eventhandler.cpp:553
|
||||
#: src/eventhandler.cpp:375 src/eventhandler.cpp:557
|
||||
#, kde-format
|
||||
msgctxt "their refers to a singular user"
|
||||
msgid "updated their avatar"
|
||||
msgstr "更新了他的頭貼"
|
||||
|
||||
#: src/eventhandler.cpp:375 src/eventhandler.cpp:557
|
||||
#: src/eventhandler.cpp:379 src/eventhandler.cpp:561
|
||||
#, kde-format
|
||||
msgctxt "<user> changed nothing"
|
||||
msgid "changed nothing"
|
||||
msgstr "什麼都沒改"
|
||||
|
||||
#: src/eventhandler.cpp:381
|
||||
#: src/eventhandler.cpp:385
|
||||
#, kde-format
|
||||
msgid "withdrew %1's invitation"
|
||||
msgstr "撤回了 %1 的邀請"
|
||||
|
||||
#: src/eventhandler.cpp:381 src/eventhandler.cpp:563
|
||||
#: src/eventhandler.cpp:385 src/eventhandler.cpp:567
|
||||
#, kde-format
|
||||
msgid "rejected the invitation"
|
||||
msgstr "拒絕了邀請"
|
||||
|
||||
#: src/eventhandler.cpp:385
|
||||
#: src/eventhandler.cpp:389
|
||||
#, kde-format
|
||||
msgid "unbanned %1"
|
||||
msgstr "解除了 %1 的封鎖"
|
||||
|
||||
#: src/eventhandler.cpp:385 src/eventhandler.cpp:567
|
||||
#: src/eventhandler.cpp:389 src/eventhandler.cpp:571
|
||||
#, kde-format
|
||||
msgid "self-unbanned"
|
||||
msgstr "已自我解除封鎖"
|
||||
|
||||
#: src/eventhandler.cpp:388
|
||||
#: src/eventhandler.cpp:392
|
||||
#, kde-format
|
||||
msgid "has put %1 out of the room: %2"
|
||||
msgstr "已將 %1 移出聊天室:%2"
|
||||
|
||||
#: src/eventhandler.cpp:389 src/eventhandler.cpp:569
|
||||
#: src/eventhandler.cpp:393 src/eventhandler.cpp:573
|
||||
#, kde-format
|
||||
msgid "left the room"
|
||||
msgstr "已離開聊天室"
|
||||
|
||||
#: src/eventhandler.cpp:393
|
||||
#: src/eventhandler.cpp:397
|
||||
#, kde-format
|
||||
msgid "banned %1 from the room"
|
||||
msgstr "已從聊天室封鎖 %1"
|
||||
|
||||
#: src/eventhandler.cpp:395
|
||||
#: src/eventhandler.cpp:399
|
||||
#, kde-format
|
||||
msgid "banned %1 from the room: %2"
|
||||
msgstr "已從聊天室封鎖 %1:%2"
|
||||
|
||||
#: src/eventhandler.cpp:398 src/eventhandler.cpp:574
|
||||
#: src/eventhandler.cpp:402 src/eventhandler.cpp:578
|
||||
#, kde-format
|
||||
msgid "self-banned from the room"
|
||||
msgstr "已從聊天室自行封鎖"
|
||||
|
||||
#: src/eventhandler.cpp:402 src/eventhandler.cpp:577
|
||||
#: src/eventhandler.cpp:406 src/eventhandler.cpp:581
|
||||
#, kde-format
|
||||
msgid "requested an invite"
|
||||
msgstr "請求了邀請"
|
||||
|
||||
#: src/eventhandler.cpp:402
|
||||
#: src/eventhandler.cpp:406
|
||||
#, kde-format
|
||||
msgid "requested an invite with reason: %1"
|
||||
msgstr "請求了邀請,理由:%1"
|
||||
|
||||
#: src/eventhandler.cpp:406 src/eventhandler.cpp:581
|
||||
#: src/eventhandler.cpp:410 src/eventhandler.cpp:585
|
||||
#, kde-format
|
||||
msgid "made something unknown"
|
||||
msgstr "做了不明的東西"
|
||||
|
||||
#: src/eventhandler.cpp:409 src/eventhandler.cpp:584
|
||||
#: src/eventhandler.cpp:413 src/eventhandler.cpp:588
|
||||
#, kde-format
|
||||
msgid "cleared the room main alias"
|
||||
msgstr "清除了聊天室的主別名"
|
||||
|
||||
#: src/eventhandler.cpp:409
|
||||
#: src/eventhandler.cpp:413
|
||||
#, kde-format
|
||||
msgid "set the room main alias to: %1"
|
||||
msgstr "設定聊天室的主別名為:%1"
|
||||
|
||||
#: src/eventhandler.cpp:412 src/eventhandler.cpp:587
|
||||
#: src/eventhandler.cpp:416 src/eventhandler.cpp:591
|
||||
#, kde-format
|
||||
msgid "cleared the room name"
|
||||
msgstr "清除了聊天室名稱"
|
||||
|
||||
#: src/eventhandler.cpp:412
|
||||
#: src/eventhandler.cpp:416
|
||||
#, kde-format
|
||||
msgid "set the room name to: %1"
|
||||
msgstr "設定聊天室名稱為:%1"
|
||||
|
||||
#: src/eventhandler.cpp:415 src/eventhandler.cpp:590
|
||||
#: src/eventhandler.cpp:419 src/eventhandler.cpp:594
|
||||
#, kde-format
|
||||
msgid "cleared the topic"
|
||||
msgstr "清除了主題"
|
||||
|
||||
#: src/eventhandler.cpp:416
|
||||
#: src/eventhandler.cpp:420
|
||||
#, kde-format
|
||||
msgid "set the topic to: %1"
|
||||
msgstr "設定主題為:%1"
|
||||
|
||||
#: src/eventhandler.cpp:422 src/eventhandler.cpp:593
|
||||
#: src/eventhandler.cpp:426 src/eventhandler.cpp:597
|
||||
#, kde-format
|
||||
msgid "changed the room avatar"
|
||||
msgstr "變更了聊天室頭貼"
|
||||
|
||||
#: src/eventhandler.cpp:425 src/eventhandler.cpp:596
|
||||
#: src/eventhandler.cpp:429 src/eventhandler.cpp:600
|
||||
#, kde-format
|
||||
msgid "activated End-to-End Encryption"
|
||||
msgstr "啟用了端對端加密"
|
||||
|
||||
#: src/eventhandler.cpp:429
|
||||
#: src/eventhandler.cpp:433
|
||||
#, kde-format
|
||||
msgid "upgraded the room to version %1"
|
||||
msgstr "更新了聊天室到版本 %1"
|
||||
|
||||
#: src/eventhandler.cpp:430
|
||||
#: src/eventhandler.cpp:434
|
||||
#, kde-format
|
||||
msgid "created the room, version %1"
|
||||
msgstr "建立了聊天室,版本 %1"
|
||||
|
||||
#: src/eventhandler.cpp:433 src/eventhandler.cpp:602
|
||||
#: src/eventhandler.cpp:437 src/eventhandler.cpp:606
|
||||
#, kde-format
|
||||
msgctxt "'power level' means permission level"
|
||||
msgid "changed the power levels for this room"
|
||||
msgstr "變更了這個聊天室的能力等級"
|
||||
|
||||
#: src/eventhandler.cpp:439 src/eventhandler.cpp:608
|
||||
#: src/eventhandler.cpp:443 src/eventhandler.cpp:612
|
||||
#, kde-format
|
||||
msgid "changed the server access control lists for this room"
|
||||
msgstr "變更了這個聊天室的伺服器存取控制清單 (ACL)"
|
||||
|
||||
#: src/eventhandler.cpp:443
|
||||
#: src/eventhandler.cpp:447
|
||||
#, kde-format
|
||||
msgctxt "[User] added <name> widget"
|
||||
msgid "added %1 widget"
|
||||
msgstr "新增了 %1 元件"
|
||||
|
||||
#: src/eventhandler.cpp:446
|
||||
#: src/eventhandler.cpp:450
|
||||
#, kde-format
|
||||
msgctxt "[User] removed <name> widget"
|
||||
msgid "removed %1 widget"
|
||||
msgstr "移除了 %1 元件"
|
||||
|
||||
#: src/eventhandler.cpp:448
|
||||
#: src/eventhandler.cpp:452
|
||||
#, kde-format
|
||||
msgctxt "[User] configured <name> widget"
|
||||
msgid "configured %1 widget"
|
||||
msgstr "設定了 %1 元件"
|
||||
|
||||
#: src/eventhandler.cpp:451
|
||||
#: src/eventhandler.cpp:455
|
||||
#, kde-format
|
||||
msgid "updated %1 state"
|
||||
msgstr "更新了 %1 狀態"
|
||||
|
||||
#: src/eventhandler.cpp:452
|
||||
#: src/eventhandler.cpp:456
|
||||
#, kde-format
|
||||
msgid "updated %1 state for %2"
|
||||
msgstr "為 %2 更新了 %1 狀態"
|
||||
|
||||
#: src/eventhandler.cpp:457 src/eventhandler.cpp:626
|
||||
#: src/eventhandler.cpp:461 src/eventhandler.cpp:630
|
||||
#, kde-format
|
||||
msgid "Unknown event"
|
||||
msgstr "未知事件"
|
||||
|
||||
#: src/eventhandler.cpp:472
|
||||
#: src/eventhandler.cpp:476
|
||||
#, kde-format
|
||||
msgid "a file"
|
||||
msgstr "一個檔案"
|
||||
|
||||
#: src/eventhandler.cpp:512
|
||||
#: src/eventhandler.cpp:516
|
||||
#, kde-format
|
||||
msgid "sent a message"
|
||||
msgstr "傳送了訊息"
|
||||
|
||||
#: src/eventhandler.cpp:516
|
||||
#: src/eventhandler.cpp:520
|
||||
#, kde-format
|
||||
msgid "sent a sticker"
|
||||
msgstr "傳送了貼圖"
|
||||
|
||||
#: src/eventhandler.cpp:522
|
||||
#: src/eventhandler.cpp:526
|
||||
#, kde-format
|
||||
msgid "reinvited someone to the room"
|
||||
msgstr "已重新邀請某人到聊天室裡"
|
||||
|
||||
#: src/eventhandler.cpp:531
|
||||
#: src/eventhandler.cpp:535
|
||||
#, kde-format
|
||||
msgid "invited someone to the room"
|
||||
msgstr "已邀請某人到聊天室裡"
|
||||
|
||||
#: src/eventhandler.cpp:541
|
||||
#: src/eventhandler.cpp:545
|
||||
#, kde-format
|
||||
msgctxt "their refers to a singular user"
|
||||
msgid "changed their display name"
|
||||
msgstr "變更了他的顯示名稱"
|
||||
|
||||
#: src/eventhandler.cpp:563
|
||||
#: src/eventhandler.cpp:567
|
||||
#, kde-format
|
||||
msgid "withdrew a user's invitation"
|
||||
msgstr "撤回了一個使用者的邀請"
|
||||
|
||||
#: src/eventhandler.cpp:567
|
||||
#: src/eventhandler.cpp:571
|
||||
#, kde-format
|
||||
msgid "unbanned a user"
|
||||
msgstr "已解除封鎖一個使用者"
|
||||
|
||||
#: src/eventhandler.cpp:569
|
||||
#: src/eventhandler.cpp:573
|
||||
#, kde-format
|
||||
msgid "put a user out of the room"
|
||||
msgstr "已將一個使用者移出聊天室"
|
||||
|
||||
#: src/eventhandler.cpp:572
|
||||
#: src/eventhandler.cpp:576
|
||||
#, kde-format
|
||||
msgid "banned a user from the room"
|
||||
msgstr "已從聊天室封鎖一個使用者"
|
||||
|
||||
#: src/eventhandler.cpp:584
|
||||
#: src/eventhandler.cpp:588
|
||||
#, kde-format
|
||||
msgid "set the room main alias"
|
||||
msgstr "設定了聊天室的主別名"
|
||||
|
||||
#: src/eventhandler.cpp:587
|
||||
#: src/eventhandler.cpp:591
|
||||
#, kde-format
|
||||
msgid "set the room name"
|
||||
msgstr "設定了聊天室名稱"
|
||||
|
||||
#: src/eventhandler.cpp:590
|
||||
#: src/eventhandler.cpp:594
|
||||
#, kde-format
|
||||
msgid "set the topic"
|
||||
msgstr "設定了主題"
|
||||
|
||||
#: src/eventhandler.cpp:599
|
||||
#: src/eventhandler.cpp:603
|
||||
#, kde-format
|
||||
msgid "upgraded the room version"
|
||||
msgstr "更新了聊天室版本"
|
||||
|
||||
#: src/eventhandler.cpp:599
|
||||
#: src/eventhandler.cpp:603
|
||||
#, kde-format
|
||||
msgid "created the room"
|
||||
msgstr "建立了聊天室"
|
||||
|
||||
#: src/eventhandler.cpp:605
|
||||
#: src/eventhandler.cpp:609
|
||||
#, kde-format
|
||||
msgid "sent a live location beacon"
|
||||
msgstr "傳送即時位置信標"
|
||||
|
||||
#: src/eventhandler.cpp:612
|
||||
#: src/eventhandler.cpp:616
|
||||
#, kde-format
|
||||
msgid "added a widget"
|
||||
msgstr "新增了一個元件"
|
||||
|
||||
#: src/eventhandler.cpp:615
|
||||
#: src/eventhandler.cpp:619
|
||||
#, kde-format
|
||||
msgid "removed a widget"
|
||||
msgstr "移除了一個元件"
|
||||
|
||||
#: src/eventhandler.cpp:617
|
||||
#: src/eventhandler.cpp:621
|
||||
#, kde-format
|
||||
msgid "configured a widget"
|
||||
msgstr "設定了一個元件"
|
||||
|
||||
#: src/eventhandler.cpp:620
|
||||
#: src/eventhandler.cpp:624
|
||||
#, kde-format
|
||||
msgid "updated the state"
|
||||
msgstr "更新了狀態"
|
||||
|
||||
#: src/eventhandler.cpp:624
|
||||
#: src/eventhandler.cpp:628
|
||||
#, kde-format
|
||||
msgid "started a poll"
|
||||
msgstr "開始了投票"
|
||||
|
||||
#: src/eventhandler.cpp:1035
|
||||
#, kde-format
|
||||
msgid "1 user: "
|
||||
msgid_plural "%1 users: "
|
||||
msgstr[0] "%1 名使用者:"
|
||||
|
||||
#: src/eventhandler.cpp:1040
|
||||
#, kde-format
|
||||
msgctxt "A member who is not in the room has been requested."
|
||||
msgid "unknown member"
|
||||
msgstr "未知成員"
|
||||
|
||||
#: src/eventhandler.cpp:1044
|
||||
#, kde-format
|
||||
msgctxt "list separator"
|
||||
msgid ", "
|
||||
msgstr ", "
|
||||
|
||||
#: src/filetransferpseudojob.cpp:48
|
||||
#, kde-format
|
||||
msgctxt "Job heading, like 'Copying'"
|
||||
@@ -1382,67 +1400,67 @@ msgstr "%1 已被踢出這個聊天室。"
|
||||
msgid "Removes the user from the room"
|
||||
msgstr "從聊天室移除使用者"
|
||||
|
||||
#: src/models/emojimodel.cpp:155 src/models/emojimodel.cpp:213
|
||||
#: src/models/emojimodel.cpp:153 src/models/emojimodel.cpp:211
|
||||
#, kde-format
|
||||
msgctxt "Previously used emojis"
|
||||
msgid "History"
|
||||
msgstr "歷史"
|
||||
|
||||
#: src/models/emojimodel.cpp:160
|
||||
#: src/models/emojimodel.cpp:158
|
||||
#, kde-format
|
||||
msgctxt "'Smileys' is a category of emoji"
|
||||
msgid "Smileys"
|
||||
msgstr "微笑"
|
||||
|
||||
#: src/models/emojimodel.cpp:165
|
||||
#: src/models/emojimodel.cpp:163
|
||||
#, kde-format
|
||||
msgctxt "'People' is a category of emoji"
|
||||
msgid "People"
|
||||
msgstr "人們"
|
||||
|
||||
#: src/models/emojimodel.cpp:170
|
||||
#: src/models/emojimodel.cpp:168
|
||||
#, kde-format
|
||||
msgctxt "'Nature' is a category of emoji"
|
||||
msgid "Nature"
|
||||
msgstr "自然"
|
||||
|
||||
#: src/models/emojimodel.cpp:175
|
||||
#: src/models/emojimodel.cpp:173
|
||||
#, kde-format
|
||||
msgctxt "'Food' is a category of emoji"
|
||||
msgid "Food"
|
||||
msgstr "食物"
|
||||
|
||||
#: src/models/emojimodel.cpp:180
|
||||
#: src/models/emojimodel.cpp:178
|
||||
#, kde-format
|
||||
msgctxt "'Activities' is a category of emoji"
|
||||
msgid "Activities"
|
||||
msgstr "活動"
|
||||
|
||||
#: src/models/emojimodel.cpp:185
|
||||
#: src/models/emojimodel.cpp:183
|
||||
#, kde-format
|
||||
msgctxt "'Travel' is a category of emoji"
|
||||
msgid "Travel"
|
||||
msgstr "旅行"
|
||||
|
||||
#: src/models/emojimodel.cpp:190
|
||||
#: src/models/emojimodel.cpp:188
|
||||
#, kde-format
|
||||
msgctxt "'Objects' is a category of emoji"
|
||||
msgid "Objects"
|
||||
msgstr "物體"
|
||||
|
||||
#: src/models/emojimodel.cpp:195
|
||||
#: src/models/emojimodel.cpp:193
|
||||
#, kde-format
|
||||
msgctxt "'Symbols' is a category of emoji"
|
||||
msgid "Symbols"
|
||||
msgstr "符號"
|
||||
|
||||
#: src/models/emojimodel.cpp:200
|
||||
#: src/models/emojimodel.cpp:198
|
||||
#, kde-format
|
||||
msgctxt "'Flags' is a category of emoji"
|
||||
msgid "Flags"
|
||||
msgstr "旗幟"
|
||||
|
||||
#: src/models/emojimodel.cpp:219
|
||||
#: src/models/emojimodel.cpp:217
|
||||
#, kde-format
|
||||
msgctxt "'Custom' is a category of emoji"
|
||||
msgid "Custom"
|
||||
@@ -1460,45 +1478,45 @@ msgctxt "As in 'The user's own emojis"
|
||||
msgid "Own Emojis"
|
||||
msgstr "自己的表情符號"
|
||||
|
||||
#: src/models/messagecontentmodel.cpp:223
|
||||
#: src/models/messagecontentmodel.cpp:216
|
||||
#: src/timeline/LinkPreviewLoadComponent.qml:66
|
||||
#, kde-format
|
||||
msgid "Loading reply"
|
||||
msgstr "載入回覆中"
|
||||
|
||||
#: src/models/messagefiltermodel.cpp:164
|
||||
#: src/models/messagefiltermodel.cpp:162
|
||||
#, kde-format
|
||||
msgctxt "%1: What's being done; %2: How often it is done."
|
||||
msgid " %1"
|
||||
msgid_plural " %1 %2 times"
|
||||
msgstr[0] " %1 %2 次"
|
||||
|
||||
#: src/models/messagefiltermodel.cpp:168
|
||||
#: src/models/messagefiltermodel.cpp:166
|
||||
#, kde-format
|
||||
msgctxt "n users"
|
||||
msgid " %1 user "
|
||||
msgid_plural " %1 users "
|
||||
msgstr[0] " %1 名使用者 "
|
||||
|
||||
#: src/models/messagefiltermodel.cpp:177
|
||||
#: src/models/messagefiltermodel.cpp:175
|
||||
#, kde-format
|
||||
msgctxt "[action 1], [action 2 and/or action 3]"
|
||||
msgid ", "
|
||||
msgstr ","
|
||||
|
||||
#: src/models/messagefiltermodel.cpp:181
|
||||
#: src/models/messagefiltermodel.cpp:179
|
||||
#, kde-format
|
||||
msgctxt "[action 1, action 2] or [action 3]"
|
||||
msgid " or "
|
||||
msgstr " 或 "
|
||||
|
||||
#: src/models/messagefiltermodel.cpp:181
|
||||
#: src/models/messagefiltermodel.cpp:179
|
||||
#, kde-format
|
||||
msgctxt "[action 1, action 2] and [action 3]"
|
||||
msgid " and "
|
||||
msgstr " 和 "
|
||||
|
||||
#: src/models/messagefiltermodel.cpp:187
|
||||
#: src/models/messagefiltermodel.cpp:185
|
||||
#, kde-format
|
||||
msgctxt ""
|
||||
"userText (%1) is either a Matrix username if a single user sent all the "
|
||||
@@ -1713,121 +1731,103 @@ msgid "%2 reacted with %3"
|
||||
msgid_plural "%2 reacted with %3"
|
||||
msgstr[0] "%2 用 %3 反應"
|
||||
|
||||
#: src/models/readmarkermodel.cpp:110
|
||||
#, kde-format
|
||||
msgid "1 user: "
|
||||
msgid_plural "%1 users: "
|
||||
msgstr[0] "%1 名使用者:"
|
||||
|
||||
#: src/models/readmarkermodel.cpp:115
|
||||
#, kde-format
|
||||
msgctxt "A member who is not in the room has been requested."
|
||||
msgid "unknown member"
|
||||
msgstr "未知成員"
|
||||
|
||||
#: src/models/readmarkermodel.cpp:117
|
||||
#, kde-format
|
||||
msgctxt "list separator"
|
||||
msgid ", "
|
||||
msgstr ", "
|
||||
|
||||
#: src/neochatconnection.cpp:86
|
||||
#: src/neochatconnection.cpp:85
|
||||
#, kde-format
|
||||
msgid "File too large to download."
|
||||
msgstr "要下載的檔案太大了。"
|
||||
|
||||
#: src/neochatconnection.cpp:86
|
||||
#: src/neochatconnection.cpp:85
|
||||
#, kde-format
|
||||
msgid "Contact your matrix server administrator for support."
|
||||
msgstr "請聯絡您的 matrix 伺服器管理員以求支援。"
|
||||
|
||||
#: src/neochatconnection.cpp:317
|
||||
#: src/neochatconnection.cpp:296
|
||||
#, kde-format
|
||||
msgctxt "@info"
|
||||
msgid "No identity server configured"
|
||||
msgstr "未設定身份伺服器"
|
||||
|
||||
#: src/neochatconnection.cpp:348
|
||||
#: src/neochatconnection.cpp:327
|
||||
#, kde-format
|
||||
msgid "Room creation failed: %1"
|
||||
msgstr "聊天室建立失敗:%1"
|
||||
|
||||
#: src/neochatconnection.cpp:385
|
||||
#: src/neochatconnection.cpp:364
|
||||
#, kde-format
|
||||
msgid "Space creation failed: %1"
|
||||
msgstr "聊天空間建立失敗:%1"
|
||||
|
||||
#: src/neochatroom.cpp:1403
|
||||
#: src/neochatroom.cpp:1352
|
||||
#, kde-format
|
||||
msgid "Report sent successfully."
|
||||
msgstr "已成功傳送檢舉"
|
||||
|
||||
#: src/neochatroom.cpp:1724 src/neochatroom.cpp:1732
|
||||
#: src/neochatroom.cpp:1622 src/neochatroom.cpp:1630
|
||||
#, kde-format
|
||||
msgctxt "'Lat' and 'Lon' as in Latitude and Longitude"
|
||||
msgid "Lat: %1, Lon: %2"
|
||||
msgstr "緯度:%2,經度:%1"
|
||||
|
||||
#: src/notificationsmanager.cpp:126 src/notificationsmanager.cpp:330
|
||||
#: src/notificationsmanager.cpp:120 src/notificationsmanager.cpp:325
|
||||
#, kde-format
|
||||
msgid "Encrypted Message"
|
||||
msgstr "已加密訊息"
|
||||
|
||||
#: src/notificationsmanager.cpp:210 src/qml/Main.qml:279
|
||||
#: src/notificationsmanager.cpp:204 src/qml/Main.qml:279
|
||||
#, kde-format
|
||||
msgid "%1: %2"
|
||||
msgstr "%1: %2"
|
||||
|
||||
#: src/notificationsmanager.cpp:216
|
||||
#: src/notificationsmanager.cpp:210
|
||||
#, kde-format
|
||||
msgid "Open NeoChat in this room"
|
||||
msgstr "在這個聊天室開啟 NeoChat"
|
||||
|
||||
#: src/notificationsmanager.cpp:229 src/qml/DelegateContextMenu.qml:98
|
||||
#: src/notificationsmanager.cpp:223 src/qml/DelegateContextMenu.qml:98
|
||||
#: src/qml/HoverActions.qml:111
|
||||
#, kde-format
|
||||
msgid "Reply"
|
||||
msgstr "回覆"
|
||||
|
||||
#: src/notificationsmanager.cpp:230
|
||||
#: src/notificationsmanager.cpp:224
|
||||
#, kde-format
|
||||
msgid "Reply..."
|
||||
msgstr "回覆..."
|
||||
|
||||
#: src/notificationsmanager.cpp:249
|
||||
#: src/notificationsmanager.cpp:243
|
||||
#, kde-format
|
||||
msgid "%1 invited you to a room"
|
||||
msgstr "%1 邀請了您到聊天室"
|
||||
|
||||
#: src/notificationsmanager.cpp:252
|
||||
#: src/notificationsmanager.cpp:247
|
||||
#, kde-format
|
||||
msgid "Open this invitation in NeoChat"
|
||||
msgstr "在 NeoChat 開啟這個邀請"
|
||||
|
||||
#: src/notificationsmanager.cpp:262
|
||||
#: src/notificationsmanager.cpp:257
|
||||
#, kde-format
|
||||
msgctxt "@action:button The thing being accepted is an invitation to chat"
|
||||
msgid "Accept"
|
||||
msgstr "接受"
|
||||
|
||||
#: src/notificationsmanager.cpp:263
|
||||
#: src/notificationsmanager.cpp:258
|
||||
#, kde-format
|
||||
msgctxt "@action:button The thing being rejected is an invitation to chat"
|
||||
msgid "Reject"
|
||||
msgstr "拒絕"
|
||||
|
||||
#: src/notificationsmanager.cpp:264
|
||||
#: src/notificationsmanager.cpp:259
|
||||
#, kde-format
|
||||
msgctxt "@action:button The thing being rejected is an invitation to chat"
|
||||
msgid "Reject and Ignore User"
|
||||
msgstr "拒絕並忽略使用者"
|
||||
|
||||
#: src/notificationsmanager.cpp:323
|
||||
#: src/notificationsmanager.cpp:318
|
||||
#, kde-format
|
||||
msgid "%1 (%2)"
|
||||
msgstr "%1 (%2)"
|
||||
|
||||
#: src/notificationsmanager.cpp:334
|
||||
#: src/notificationsmanager.cpp:329
|
||||
#, kde-format
|
||||
msgid "Open NeoChat"
|
||||
msgstr "開啟 NeoChat"
|
||||
@@ -2751,23 +2751,18 @@ msgstr "鏡射"
|
||||
msgid "Accept this invitation?"
|
||||
msgstr "接收邀請嗎?"
|
||||
|
||||
#: src/qml/InvitationView.qml:18
|
||||
#, kde-format
|
||||
msgid "You can reject invitations from unknown users under Security settings."
|
||||
msgstr "您可以在安全性設定底下設定回絕來自未知使用者的邀請。"
|
||||
|
||||
#: src/qml/InvitationView.qml:24
|
||||
#: src/qml/InvitationView.qml:21
|
||||
#, kde-format
|
||||
msgctxt "@action:button The thing being rejected is an invitation to chat"
|
||||
msgid "Reject and ignore user"
|
||||
msgstr "拒絕並忽略使用者"
|
||||
|
||||
#: src/qml/InvitationView.qml:33
|
||||
#: src/qml/InvitationView.qml:30
|
||||
#, kde-format
|
||||
msgid "Reject"
|
||||
msgstr "拒絕"
|
||||
|
||||
#: src/qml/InvitationView.qml:40 src/qml/KeyVerificationDialog.qml:92
|
||||
#: src/qml/InvitationView.qml:37 src/qml/KeyVerificationDialog.qml:92
|
||||
#, kde-format
|
||||
msgid "Accept"
|
||||
msgstr "接受"
|
||||
@@ -3519,27 +3514,27 @@ msgctxt "'Space' is a matrix space"
|
||||
msgid "Leave Space"
|
||||
msgstr "離開聊天空間"
|
||||
|
||||
#: src/qml/TimelineView.qml:196
|
||||
#: src/qml/TimelineView.qml:195
|
||||
#, kde-format
|
||||
msgid "Jump to first unread message"
|
||||
msgstr "跳到第一個未讀訊息"
|
||||
|
||||
#: src/qml/TimelineView.qml:196
|
||||
#: src/qml/TimelineView.qml:195
|
||||
#, kde-format
|
||||
msgid "Jump to oldest loaded message"
|
||||
msgstr "跳到已載入的最舊的訊息"
|
||||
|
||||
#: src/qml/TimelineView.qml:236
|
||||
#: src/qml/TimelineView.qml:235
|
||||
#, kde-format
|
||||
msgid "Jump to latest message"
|
||||
msgstr "跳到最新訊息"
|
||||
|
||||
#: src/qml/TimelineView.qml:261
|
||||
#: src/qml/TimelineView.qml:260
|
||||
#, kde-format
|
||||
msgid "Drag items here to share them"
|
||||
msgstr "拖曳項目至此來分享它"
|
||||
|
||||
#: src/qml/TimelineView.qml:268
|
||||
#: src/qml/TimelineView.qml:267
|
||||
#, kde-format
|
||||
msgctxt "Message displayed when some users are typing"
|
||||
msgid "%2 is typing"
|
||||
@@ -3930,27 +3925,27 @@ msgstr "繼續"
|
||||
msgid "Working"
|
||||
msgstr "處理中"
|
||||
|
||||
#: src/roommanager.cpp:138
|
||||
#: src/roommanager.cpp:125
|
||||
#, kde-format
|
||||
msgid "Malformed or empty Matrix id"
|
||||
msgstr "不正確或空白的 Matrix ID"
|
||||
|
||||
#: src/roommanager.cpp:138
|
||||
#: src/roommanager.cpp:125
|
||||
#, kde-format
|
||||
msgid "%1 is not a correct Matrix identifier"
|
||||
msgstr "%1 不是一個正確的 Matrix ID"
|
||||
|
||||
#: src/roommanager.cpp:345
|
||||
#: src/roommanager.cpp:337
|
||||
#, kde-format
|
||||
msgid "Failed to join room"
|
||||
msgstr "加入聊天室失敗"
|
||||
|
||||
#: src/roommanager.cpp:377
|
||||
#: src/roommanager.cpp:369
|
||||
#, kde-format
|
||||
msgid "You requested to join '%1'"
|
||||
msgstr "您已請求加入 '%1'"
|
||||
|
||||
#: src/roommanager.cpp:381
|
||||
#: src/roommanager.cpp:373
|
||||
#, kde-format
|
||||
msgid "Failed to request joining room"
|
||||
msgstr "請求加入聊天室失敗"
|
||||
@@ -4349,7 +4344,7 @@ msgid "Keyword…"
|
||||
msgstr "關鍵字…"
|
||||
|
||||
#: src/settings/GlobalNotificationsPage.qml:116
|
||||
#: src/settings/Permissions.qml:386 src/settings/PushNotification.qml:119
|
||||
#: src/settings/Permissions.qml:391 src/settings/PushNotification.qml:119
|
||||
#, kde-format
|
||||
msgid "Add keyword"
|
||||
msgstr "新增關鍵字"
|
||||
@@ -4410,7 +4405,7 @@ msgid "Ignored Users"
|
||||
msgstr "已忽略的使用者"
|
||||
|
||||
#: src/settings/IgnoredUsersDialog.qml:24
|
||||
#: src/settings/NeoChatSecurityPage.qml:35
|
||||
#: src/settings/NeoChatSecurityPage.qml:37
|
||||
#, kde-format
|
||||
msgctxt "@title:group"
|
||||
msgid "Ignored Users"
|
||||
@@ -4540,61 +4535,37 @@ msgstr "安全性"
|
||||
|
||||
#: src/settings/NeoChatSecurityPage.qml:20
|
||||
#, kde-format
|
||||
msgctxt "@title:group"
|
||||
msgid "Invitations"
|
||||
msgstr "邀請"
|
||||
|
||||
#: src/settings/NeoChatSecurityPage.qml:24
|
||||
#, kde-format
|
||||
msgctxt "@option:check"
|
||||
msgid "Reject invitations from unknown users"
|
||||
msgstr "回絕來自未知使用者的邀請"
|
||||
msgctxt "@title"
|
||||
msgid "Keys"
|
||||
msgstr "金鑰"
|
||||
|
||||
#: src/settings/NeoChatSecurityPage.qml:25
|
||||
#, kde-format
|
||||
msgid ""
|
||||
"If enabled, NeoChat will reject invitations from from users you don't share "
|
||||
"a room with."
|
||||
msgstr ""
|
||||
msgid "Device key"
|
||||
msgstr "裝置金鑰"
|
||||
|
||||
#: src/settings/NeoChatSecurityPage.qml:25
|
||||
#: src/settings/NeoChatSecurityPage.qml:29
|
||||
#, kde-format
|
||||
msgid "Your server does not support this setting."
|
||||
msgstr "您的伺服器不支援這個設定。"
|
||||
msgid "Encryption key"
|
||||
msgstr "加密金鑰"
|
||||
|
||||
#: src/settings/NeoChatSecurityPage.qml:39
|
||||
#: src/settings/NeoChatSecurityPage.qml:33
|
||||
#, kde-format
|
||||
msgid "Device id"
|
||||
msgstr "裝置 ID"
|
||||
|
||||
#: src/settings/NeoChatSecurityPage.qml:41
|
||||
#, kde-format
|
||||
msgctxt "@action:button"
|
||||
msgid "Manage ignored users"
|
||||
msgstr "管理已忽略的使用者"
|
||||
|
||||
#: src/settings/NeoChatSecurityPage.qml:41
|
||||
#: src/settings/NeoChatSecurityPage.qml:43
|
||||
#, kde-format
|
||||
msgctxt "@title:window"
|
||||
msgid "Ignored Users"
|
||||
msgstr "已忽略的使用者"
|
||||
|
||||
#: src/settings/NeoChatSecurityPage.qml:46
|
||||
#, kde-format
|
||||
msgctxt "@title"
|
||||
msgid "Keys"
|
||||
msgstr "金鑰"
|
||||
|
||||
#: src/settings/NeoChatSecurityPage.qml:51
|
||||
#, kde-format
|
||||
msgid "Device key"
|
||||
msgstr "裝置金鑰"
|
||||
|
||||
#: src/settings/NeoChatSecurityPage.qml:55
|
||||
#, kde-format
|
||||
msgid "Encryption key"
|
||||
msgstr "加密金鑰"
|
||||
|
||||
#: src/settings/NeoChatSecurityPage.qml:59
|
||||
#, kde-format
|
||||
msgid "Device id"
|
||||
msgstr "裝置 ID"
|
||||
|
||||
#: src/settings/NeoChatSettingsView.qml:22 src/settings/RoomGeneralPage.qml:22
|
||||
#: src/settings/RoomSettingsView.qml:39
|
||||
#, kde-format
|
||||
@@ -4748,27 +4719,27 @@ msgctxt "@label:textbox"
|
||||
msgid "Password:"
|
||||
msgstr "密碼:"
|
||||
|
||||
#: src/settings/Permissions.qml:32
|
||||
#: src/settings/Permissions.qml:37
|
||||
#, kde-format
|
||||
msgid "Privileged Users"
|
||||
msgstr "有權力的使用者"
|
||||
|
||||
#: src/settings/Permissions.qml:233
|
||||
#: src/settings/Permissions.qml:238
|
||||
#, kde-format
|
||||
msgid "Default permissions"
|
||||
msgstr "預設權限"
|
||||
|
||||
#: src/settings/Permissions.qml:273
|
||||
#: src/settings/Permissions.qml:278
|
||||
#, kde-format
|
||||
msgid "Basic permissions"
|
||||
msgstr "基本權限"
|
||||
|
||||
#: src/settings/Permissions.qml:313
|
||||
#: src/settings/Permissions.qml:318
|
||||
#, kde-format
|
||||
msgid "Event permissions"
|
||||
msgstr "事件fm0vu0"
|
||||
|
||||
#: src/settings/Permissions.qml:360
|
||||
#: src/settings/Permissions.qml:365
|
||||
#, kde-format
|
||||
msgid "Event Type…"
|
||||
msgstr "事件類型…"
|
||||
@@ -5236,14 +5207,14 @@ msgid ""
|
||||
"device."
|
||||
msgstr "這個訊息已加密而傳送者並未與本裝置分享金鑰。"
|
||||
|
||||
#: src/timeline/FileComponent.qml:103 src/timeline/FileComponent.qml:186
|
||||
#: src/timeline/FileComponent.qml:107 src/timeline/FileComponent.qml:190
|
||||
#, kde-format
|
||||
msgctxt ""
|
||||
"tooltip for a button on a message; offers ability to download its file"
|
||||
msgid "Download"
|
||||
msgstr "下載"
|
||||
|
||||
#: src/timeline/FileComponent.qml:119 src/timeline/FileComponent.qml:176
|
||||
#: src/timeline/FileComponent.qml:123 src/timeline/FileComponent.qml:180
|
||||
#, kde-format
|
||||
msgctxt ""
|
||||
"tooltip for a button on a message; offers ability to open its downloaded "
|
||||
@@ -5251,13 +5222,13 @@ msgctxt ""
|
||||
msgid "Open File"
|
||||
msgstr "開啟檔案"
|
||||
|
||||
#: src/timeline/FileComponent.qml:134
|
||||
#: src/timeline/FileComponent.qml:138
|
||||
#, kde-format
|
||||
msgctxt "file download progress"
|
||||
msgid "%1 / %2"
|
||||
msgstr "%1 / %2"
|
||||
|
||||
#: src/timeline/FileComponent.qml:139
|
||||
#: src/timeline/FileComponent.qml:143
|
||||
#, kde-format
|
||||
msgctxt ""
|
||||
"tooltip for a button on a message; stops downloading the message's file"
|
||||
@@ -5343,7 +5314,7 @@ msgctxt "as in 'this vote has ended'"
|
||||
msgid "(Ended)"
|
||||
msgstr "(已結束)"
|
||||
|
||||
#: src/timeline/ReadMarkerDelegate.qml:42
|
||||
#: src/timeline/ReadMarkerDelegate.qml:39
|
||||
#, kde-format
|
||||
msgctxt "Relative time since the room was last read"
|
||||
msgid "Last read: %1"
|
||||
@@ -5362,18 +5333,18 @@ msgstr "這是此聊天的開頭。沒有比這更早的歷史訊息。"
|
||||
msgid "Pl. %1"
|
||||
msgstr "第 %1 月台"
|
||||
|
||||
#: src/timeline/VideoComponent.qml:197
|
||||
#: src/timeline/VideoComponent.qml:171
|
||||
#, kde-format
|
||||
msgid "Video"
|
||||
msgstr "影片"
|
||||
|
||||
#: src/timeline/VideoComponent.qml:261
|
||||
#: src/timeline/VideoComponent.qml:235
|
||||
#, kde-format
|
||||
msgctxt "@action:button"
|
||||
msgid "Volume"
|
||||
msgstr "音量"
|
||||
|
||||
#: src/timeline/VideoComponent.qml:345
|
||||
#: src/timeline/VideoComponent.qml:319
|
||||
#, kde-format
|
||||
msgid "Maximize"
|
||||
msgstr "最大化"
|
||||
|
||||
@@ -134,8 +134,6 @@ add_library(neochat STATIC
|
||||
jobs/neochatdeletedevicejob.h
|
||||
jobs/neochatchangepasswordjob.cpp
|
||||
jobs/neochatchangepasswordjob.h
|
||||
jobs/neochatgetcommonroomsjob.cpp
|
||||
jobs/neochatgetcommonroomsjob.h
|
||||
mediasizehelper.cpp
|
||||
mediasizehelper.h
|
||||
eventhandler.cpp
|
||||
@@ -188,10 +186,6 @@ add_library(neochat STATIC
|
||||
models/permissionsmodel.h
|
||||
threepidbindhelper.cpp
|
||||
threepidbindhelper.h
|
||||
models/readmarkermodel.cpp
|
||||
models/readmarkermodel.h
|
||||
neochatroommember.cpp
|
||||
neochatroommember.h
|
||||
)
|
||||
|
||||
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||
@@ -288,6 +282,7 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
|
||||
qml/ConsentDialog.qml
|
||||
qml/AskDirectChatConfirmation.qml
|
||||
qml/HoverLinkIndicator.qml
|
||||
qml/AccountDialog.qml
|
||||
DEPENDENCIES
|
||||
QtCore
|
||||
QtQuick
|
||||
|
||||
@@ -61,6 +61,20 @@ MessageComponentType::Type EventHandler::messageComponentType() const
|
||||
return MessageComponentType::typeForEvent(*m_event);
|
||||
}
|
||||
|
||||
Quotient::RoomMember EventHandler::getAuthor(bool isPending) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getAuthor called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getAuthor called with m_event set to nullptr. Returning empty user.";
|
||||
return {};
|
||||
}
|
||||
|
||||
return isPending ? m_room->localMember() : m_room->member(m_event->senderId());
|
||||
}
|
||||
|
||||
QString EventHandler::getAuthorDisplayName(bool isPending) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
@@ -143,11 +157,6 @@ QString EventHandler::getTimeString(bool relative, QLocale::FormatType format, b
|
||||
return {};
|
||||
}
|
||||
|
||||
QString EventHandler::getTimeString(const QString &format, bool isPending, const QDateTime &lastUpdated)
|
||||
{
|
||||
return getTime(isPending, lastUpdated).toLocalTime().toString(format);
|
||||
}
|
||||
|
||||
bool EventHandler::isHighlighted()
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
@@ -232,19 +241,17 @@ Qt::TextFormat EventHandler::messageBodyInputFormat(const Quotient::RoomMessageE
|
||||
|
||||
QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
|
||||
{
|
||||
QString body;
|
||||
|
||||
if (event.hasFileContent()) {
|
||||
// if filename is given or body is equal to filename,
|
||||
// then body is a caption
|
||||
QString filename = event.content()->fileInfo()->originalName;
|
||||
QString body = event.plainBody();
|
||||
if (filename.isEmpty() || filename == body) {
|
||||
return QString();
|
||||
auto fileCaption = event.content()->fileInfo()->originalName;
|
||||
if (fileCaption.isEmpty()) {
|
||||
fileCaption = event.plainBody();
|
||||
} else if (event.content()->fileInfo()->originalName != event.plainBody()) {
|
||||
fileCaption = event.plainBody() + " | "_ls + fileCaption;
|
||||
}
|
||||
return body;
|
||||
return fileCaption;
|
||||
}
|
||||
|
||||
QString body;
|
||||
if (event.hasTextContent() && event.content()) {
|
||||
body = static_cast<const EventContent::TextContent *>(event.content())->body;
|
||||
} else {
|
||||
@@ -653,30 +660,23 @@ QVariantMap EventHandler::getMediaInfoForEvent(const Quotient::RoomEvent *event)
|
||||
QString eventId = event->id();
|
||||
|
||||
// Get the file info for the event.
|
||||
const EventContent::FileInfo *fileInfo;
|
||||
bool isSticker = false;
|
||||
if (event->is<RoomMessageEvent>()) {
|
||||
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event);
|
||||
if (!roomMessageEvent->hasFileContent()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const EventContent::FileInfo *fileInfo;
|
||||
fileInfo = roomMessageEvent->content()->fileInfo();
|
||||
QVariantMap mediaInfo = getMediaInfoFromFileInfo(fileInfo, eventId, false, false);
|
||||
// if filename isn't specifically given, it is in body
|
||||
// https://spec.matrix.org/latest/client-server-api/#mfile
|
||||
mediaInfo["filename"_ls] = (fileInfo->originalName.isEmpty()) ? roomMessageEvent->plainBody() : fileInfo->originalName;
|
||||
|
||||
return mediaInfo;
|
||||
} else if (event->is<StickerEvent>()) {
|
||||
const EventContent::FileInfo *fileInfo;
|
||||
|
||||
auto stickerEvent = eventCast<const StickerEvent>(event);
|
||||
fileInfo = &stickerEvent->image();
|
||||
|
||||
return getMediaInfoFromFileInfo(fileInfo, eventId, false, true);
|
||||
isSticker = true;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
return getMediaInfoFromFileInfo(fileInfo, eventId, false, isSticker);
|
||||
}
|
||||
|
||||
QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail, bool isSticker) const
|
||||
@@ -952,4 +952,102 @@ QString EventHandler::getLocationAssetType() const
|
||||
return assetType;
|
||||
}
|
||||
|
||||
bool EventHandler::hasReadMarkers() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "hasReadMarkers called with m_room set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "hasReadMarkers called with m_event set to nullptr.";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localMember().id());
|
||||
return userIds.size() > 0;
|
||||
}
|
||||
|
||||
QList<Quotient::RoomMember> EventHandler::getReadMarkers(int maxMarkers) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkers called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkers called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds_temp = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds_temp.remove(m_room->localMember().id());
|
||||
|
||||
auto userIds = userIds_temp.values();
|
||||
if (userIds.count() > maxMarkers) {
|
||||
userIds = userIds.mid(0, maxMarkers);
|
||||
}
|
||||
|
||||
QList<Quotient::RoomMember> users;
|
||||
users.reserve(userIds.size());
|
||||
for (const auto &userId : userIds) {
|
||||
users += m_room->member(userId);
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
QString EventHandler::getNumberExcessReadMarkers(int maxMarkers) const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getNumberExcessReadMarkers called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getNumberExcessReadMarkers called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localMember().id());
|
||||
|
||||
if (userIds.count() > maxMarkers) {
|
||||
return QStringLiteral("+ ") + QString::number(userIds.count() - maxMarkers);
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
QString EventHandler::getReadMarkersString() const
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkersString called with m_room set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
if (m_event == nullptr) {
|
||||
qCWarning(EventHandling) << "getReadMarkersString called with m_event set to nullptr.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto userIds = m_room->userIdsAtEvent(m_event->id());
|
||||
userIds.remove(m_room->localMember().id());
|
||||
|
||||
/**
|
||||
* The string ends up in the form
|
||||
* "x users: user1DisplayName, user2DisplayName, etc."
|
||||
*/
|
||||
QString readMarkersString = i18np("1 user: ", "%1 users: ", userIds.size());
|
||||
for (const auto &userId : userIds) {
|
||||
auto member = m_room->member(userId);
|
||||
QString displayName;
|
||||
if (!m_room->memberIds().contains(userId)) {
|
||||
displayName = i18nc("A member who is not in the room has been requested.", "unknown member");
|
||||
} else {
|
||||
displayName = member.displayName();
|
||||
}
|
||||
readMarkersString += displayName + i18nc("list separator", ", ");
|
||||
}
|
||||
readMarkersString.chop(2);
|
||||
return readMarkersString;
|
||||
}
|
||||
|
||||
#include "moc_eventhandler.cpp"
|
||||
|
||||
@@ -53,10 +53,25 @@ public:
|
||||
*/
|
||||
MessageComponentType::Type messageComponentType() const;
|
||||
|
||||
/**
|
||||
* @brief Get the author of the event in context of the room.
|
||||
*
|
||||
* An empty Quotient::RoomMember will be returned if the EventHandler hasn't had
|
||||
* the room or event initialised.
|
||||
*
|
||||
* @param isPending if the event is pending, i.e. has not been confirmed by
|
||||
* the server.
|
||||
*
|
||||
* @return a Quotient::RoomMember object for the user.
|
||||
*
|
||||
* @sa Quotient::RoomMember
|
||||
*/
|
||||
Quotient::RoomMember getAuthor(bool isPending = false) const;
|
||||
|
||||
/**
|
||||
* @brief Get the display name of the event author.
|
||||
*
|
||||
* This method is special in that it will return
|
||||
* This method is separate from getAuthor() and special in that it will return
|
||||
* the old display name of the author if the current event is one that caused it
|
||||
* to change. This allows for scenarios where the UI wishes to notify that a
|
||||
* user's display name has changed and what it changed from.
|
||||
@@ -98,8 +113,6 @@ public:
|
||||
*/
|
||||
QString getTimeString(bool relative, QLocale::FormatType format = QLocale::ShortFormat, bool isPending = false, QDateTime lastUpdated = {}) const;
|
||||
|
||||
QString getTimeString(const QString &format, bool isPending = false, const QDateTime &lastUpdated = {});
|
||||
|
||||
/**
|
||||
* @brief Whether the event should be highlighted in the timeline.
|
||||
*
|
||||
@@ -329,6 +342,43 @@ public:
|
||||
*/
|
||||
QString getLocationAssetType() const;
|
||||
|
||||
/**
|
||||
* @brief Whether the event has any read marker for other users.
|
||||
*/
|
||||
bool hasReadMarkers() const;
|
||||
|
||||
/**
|
||||
* @brief Returns a list of user read marker for the event.
|
||||
*
|
||||
* @param maxMarkers the maximum number of users to return. Usually the number
|
||||
* of user read makers shown is limited to not clutter the UI.
|
||||
* This needs to be the same as used in getNumberExcessReadMarkers
|
||||
* so that the markers line up with the number displayed, i.e.
|
||||
* the number of users shown plus the excess number will be
|
||||
* the total number of other user read markers at an event.
|
||||
*/
|
||||
QList<Quotient::RoomMember> getReadMarkers(int maxMarkers = 5) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the number of excess user read markers for the event.
|
||||
*
|
||||
* This returns a string in the form "+ x" ready for use in the UI.
|
||||
*
|
||||
* @param maxMarkers the maximum number of markers shown in the UI. This needs to
|
||||
* be the same as used in getReadMarkers so that the value lines
|
||||
* up with the number displayed, i.e. the number of users shown
|
||||
* plus the excess number will be the total number of other user
|
||||
* read markers at an event.
|
||||
*/
|
||||
QString getNumberExcessReadMarkers(int maxMarkers = 5) const;
|
||||
|
||||
/**
|
||||
* @brief Returns a string with the names of the read markers at the event.
|
||||
*
|
||||
* This is in the form "x users: name 1, name 2, ...".
|
||||
*/
|
||||
QString getReadMarkersString() const;
|
||||
|
||||
private:
|
||||
const NeoChatRoom *m_room = nullptr;
|
||||
const Quotient::RoomEvent *m_event = nullptr;
|
||||
|
||||
@@ -51,3 +51,9 @@ struct ForeignSSSSHandler {
|
||||
QML_FOREIGN(Quotient::SSSSHandler)
|
||||
QML_NAMED_ELEMENT(SSSSHandler)
|
||||
};
|
||||
|
||||
struct RoomMemberForeign {
|
||||
Q_GADGET
|
||||
QML_FOREIGN(Quotient::RoomMember)
|
||||
QML_NAMED_ELEMENT(RoomMember)
|
||||
};
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "neochatgetcommonroomsjob.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
NeochatGetCommonRoomsJob::NeochatGetCommonRoomsJob(const QString &userId)
|
||||
: BaseJob(HttpVerb::Get,
|
||||
QStringLiteral("GetCommonRoomsJob"),
|
||||
QStringLiteral("/_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms").toLatin1(),
|
||||
QUrlQuery({{QStringLiteral("user_id"), userId}}))
|
||||
{
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Quotient/jobs/basejob.h>
|
||||
#include <Quotient/omittable.h>
|
||||
|
||||
// TODO: Upstream to libQuotient
|
||||
class NeochatGetCommonRoomsJob : public Quotient::BaseJob
|
||||
{
|
||||
public:
|
||||
explicit NeochatGetCommonRoomsJob(const QString &userId);
|
||||
};
|
||||
@@ -9,16 +9,18 @@
|
||||
#include "customemojimodel.h"
|
||||
#include "emojimodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "roommanager.h"
|
||||
#include "userlistmodel.h"
|
||||
|
||||
CompletionModel::CompletionModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, m_filterModel(new CompletionProxyModel())
|
||||
, m_userListModel(RoomManager::instance().userListModel())
|
||||
, m_userListModel(new UserListModel(this))
|
||||
, m_emojiModel(new QConcatenateTablesProxyModel(this))
|
||||
{
|
||||
connect(this, &CompletionModel::textChanged, this, &CompletionModel::updateCompletion);
|
||||
connect(this, &CompletionModel::roomChanged, this, [this]() {
|
||||
m_userListModel->setRoom(m_room);
|
||||
});
|
||||
m_emojiModel->addSourceModel(&CustomEmojiModel::instance());
|
||||
m_emojiModel->addSourceModel(&EmojiModel::instance());
|
||||
}
|
||||
|
||||
20
src/models/customemojimodel_p.h
Normal file
20
src/models/customemojimodel_p.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// SPDX-FileCopyrightText: 2021 Carson Black <uhhadd@gmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "customemojimodel.h"
|
||||
#include <QRegularExpression>
|
||||
|
||||
class NeoChatConnection;
|
||||
|
||||
struct CustomEmoji {
|
||||
QString name; // with :semicolons:
|
||||
QString url; // mxc://
|
||||
QRegularExpression regexp;
|
||||
};
|
||||
|
||||
struct CustomEmojiModel::Private {
|
||||
QPointer<NeoChatConnection> connection;
|
||||
QList<CustomEmoji> emojies;
|
||||
};
|
||||
@@ -26,7 +26,7 @@ int EmojiModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
int total = 0;
|
||||
for (const auto &category : std::as_const(_emojis)) {
|
||||
for (const auto &category : _emojis) {
|
||||
total += category.count();
|
||||
}
|
||||
return total;
|
||||
@@ -35,7 +35,7 @@ int EmojiModel::rowCount(const QModelIndex &parent) const
|
||||
QVariant EmojiModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
auto row = index.row();
|
||||
for (const auto &category : std::as_const(_emojis)) {
|
||||
for (const auto &category : _emojis) {
|
||||
if (row >= category.count()) {
|
||||
row -= category.count();
|
||||
continue;
|
||||
@@ -79,8 +79,7 @@ QVariantList EmojiModel::filterModelNoCustom(const QString &filter, bool limit)
|
||||
{
|
||||
QVariantList result;
|
||||
|
||||
const auto &values = _emojis.values();
|
||||
for (const auto &e : values) {
|
||||
for (const auto &e : _emojis.values()) {
|
||||
for (const auto &variant : e) {
|
||||
const auto &emoji = qvariant_cast<Emoji>(variant);
|
||||
if (emoji.shortName.contains(filter, Qt::CaseInsensitive)) {
|
||||
@@ -122,8 +121,7 @@ QVariantList EmojiModel::emojis(Category category) const
|
||||
}
|
||||
if (category == HistoryNoCustom) {
|
||||
QVariantList list;
|
||||
const auto &history = emojiHistory();
|
||||
for (const auto &e : history) {
|
||||
for (const auto &e : emojiHistory()) {
|
||||
auto emoji = qvariant_cast<Emoji>(e);
|
||||
if (!emoji.isCustom) {
|
||||
list.append(e);
|
||||
@@ -226,9 +224,8 @@ QVariantList EmojiModel::categoriesWithCustom() const
|
||||
QVariantList EmojiModel::emojiHistory() const
|
||||
{
|
||||
QVariantList list;
|
||||
const auto &lastUsed = lastUsedEmojis();
|
||||
for (const auto &historicEmoji : lastUsed) {
|
||||
for (const auto &emojiCategory : std::as_const(_emojis)) {
|
||||
for (const auto &historicEmoji : lastUsedEmojis()) {
|
||||
for (const auto &emojiCategory : _emojis) {
|
||||
for (const auto &emoji : emojiCategory) {
|
||||
if (qvariant_cast<Emoji>(emoji).shortName == historicEmoji) {
|
||||
list.append(emoji);
|
||||
|
||||
@@ -30,6 +30,40 @@ bool MediaMessageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex
|
||||
|
||||
QVariant MediaMessageFilterModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (role == SourceRole) {
|
||||
if (mapToSource(index).data(MessageEventModel::MediaInfoRole).toMap()[QLatin1String("mimeType")].toString().contains(QLatin1String("image"))) {
|
||||
return mapToSource(index).data(MessageEventModel::MediaInfoRole).toMap()[QStringLiteral("source")].toUrl();
|
||||
} else if (mapToSource(index).data(MessageEventModel::MediaInfoRole).toMap()[QLatin1String("mimeType")].toString().contains(QLatin1String("video"))) {
|
||||
auto progressInfo = mapToSource(index).data(MessageEventModel::ProgressInfoRole).value<Quotient::FileTransferInfo>();
|
||||
|
||||
if (progressInfo.completed()) {
|
||||
return mapToSource(index).data(MessageEventModel::ProgressInfoRole).value<Quotient::FileTransferInfo>().localPath;
|
||||
} else {
|
||||
return QUrl();
|
||||
}
|
||||
} else {
|
||||
return QUrl();
|
||||
}
|
||||
}
|
||||
if (role == TempSourceRole) {
|
||||
return mapToSource(index).data(MessageEventModel::MediaInfoRole).toMap()[QStringLiteral("tempInfo")].toMap()[QStringLiteral("source")].toUrl();
|
||||
}
|
||||
if (role == TypeRole) {
|
||||
if (mapToSource(index).data(MessageEventModel::MediaInfoRole).toMap()[QLatin1String("mimeType")].toString().contains(QLatin1String("image"))) {
|
||||
return MediaType::Image;
|
||||
} else {
|
||||
return MediaType::Video;
|
||||
}
|
||||
}
|
||||
if (role == CaptionRole) {
|
||||
return mapToSource(index).data(Qt::DisplayRole);
|
||||
}
|
||||
if (role == SourceWidthRole) {
|
||||
return mapToSource(index).data(MessageEventModel::MediaInfoRole).toMap()[QStringLiteral("width")].toFloat();
|
||||
}
|
||||
if (role == SourceHeightRole) {
|
||||
return mapToSource(index).data(MessageEventModel::MediaInfoRole).toMap()[QStringLiteral("height")].toFloat();
|
||||
}
|
||||
// We need to catch this one and return true if the next media object was
|
||||
// on a different day.
|
||||
if (role == MessageEventModel::ShowSectionRole) {
|
||||
@@ -46,37 +80,6 @@ QVariant MediaMessageFilterModel::data(const QModelIndex &index, int role) const
|
||||
return QVariant::fromValue<MessageContentModel *>(model);
|
||||
}
|
||||
|
||||
QVariantMap mediaInfo = mapToSource(index).data(MessageEventModel::MediaInfoRole).toMap();
|
||||
|
||||
if (role == TempSourceRole) {
|
||||
return mediaInfo[QStringLiteral("tempInfo")].toMap()[QStringLiteral("source")].toUrl();
|
||||
}
|
||||
if (role == CaptionRole) {
|
||||
return mapToSource(index).data(Qt::DisplayRole);
|
||||
}
|
||||
if (role == SourceWidthRole) {
|
||||
return mediaInfo[QStringLiteral("width")].toFloat();
|
||||
}
|
||||
if (role == SourceHeightRole) {
|
||||
return mediaInfo[QStringLiteral("height")].toFloat();
|
||||
}
|
||||
|
||||
bool isVideo = mediaInfo[QStringLiteral("mimeType")].toString().contains(QStringLiteral("video"));
|
||||
|
||||
if (role == TypeRole) {
|
||||
return (isVideo) ? MediaType::Video : MediaType::Image;
|
||||
}
|
||||
if (role == SourceRole) {
|
||||
if (isVideo) {
|
||||
auto progressInfo = mapToSource(index).data(MessageEventModel::ProgressInfoRole).value<Quotient::FileTransferInfo>();
|
||||
if (progressInfo.completed()) {
|
||||
return mapToSource(index).data(MessageEventModel::ProgressInfoRole).value<Quotient::FileTransferInfo>().localPath;
|
||||
}
|
||||
} else {
|
||||
return mediaInfo[QStringLiteral("source")].toUrl();
|
||||
}
|
||||
}
|
||||
|
||||
return sourceModel()->data(mapToSource(index), role);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "messagecontentmodel.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroommember.h"
|
||||
|
||||
#include <QImageReader>
|
||||
|
||||
@@ -36,10 +35,10 @@ MessageContentModel::MessageContentModel(NeoChatRoom *room, const Quotient::Room
|
||||
, m_room(room)
|
||||
, m_eventId(event != nullptr ? event->id() : QString())
|
||||
, m_eventSenderId(event != nullptr ? event->senderId() : QString())
|
||||
, m_event(loadEvent<RoomEvent>(event->fullJson()))
|
||||
, m_isPending(isPending)
|
||||
, m_isReply(isReply)
|
||||
{
|
||||
intiializeEvent(event);
|
||||
initializeModel();
|
||||
}
|
||||
|
||||
@@ -82,7 +81,8 @@ void MessageContentModel::initializeModel()
|
||||
if (m_eventId == serverEvent->id()) {
|
||||
beginResetModel();
|
||||
m_isPending = false;
|
||||
intiializeEvent(serverEvent);
|
||||
m_event = loadEvent<RoomEvent>(serverEvent->fullJson());
|
||||
Q_EMIT eventUpdated();
|
||||
endResetModel();
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,8 @@ void MessageContentModel::initializeModel()
|
||||
if (m_room != nullptr && m_event != nullptr) {
|
||||
if (m_eventId == newEvent->id()) {
|
||||
beginResetModel();
|
||||
intiializeEvent(newEvent);
|
||||
m_event = loadEvent<RoomEvent>(newEvent->fullJson());
|
||||
Q_EMIT eventUpdated();
|
||||
endResetModel();
|
||||
}
|
||||
}
|
||||
@@ -107,9 +108,24 @@ void MessageContentModel::initializeModel()
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::fileTransferCompleted, this, [this](const QString &eventId) {
|
||||
if (m_room != nullptr && m_event != nullptr && eventId == m_eventId) {
|
||||
if (m_event != nullptr && eventId == m_eventId) {
|
||||
resetContent();
|
||||
Q_EMIT dataChanged(index(0), index(rowCount() - 1), {FileTransferInfoRole});
|
||||
|
||||
QString mxcUrl;
|
||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
if (event->hasFileContent()) {
|
||||
mxcUrl = event->content()->fileInfo()->url().toString();
|
||||
}
|
||||
} else if (auto event = eventCast<const Quotient::StickerEvent>(m_event)) {
|
||||
mxcUrl = event->image().fileInfo()->url().toString();
|
||||
}
|
||||
if (mxcUrl.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
auto localPath = m_room->fileTransferInfo(m_eventId).localPath.toLocalFile();
|
||||
auto config = KSharedConfig::openStateConfig(QStringLiteral("neochatdownloads"))->group(QStringLiteral("downloads"));
|
||||
config.writePathEntry(mxcUrl.mid(6), localPath);
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::fileTransferFailed, this, [this](const QString &eventId) {
|
||||
@@ -153,29 +169,6 @@ void MessageContentModel::initializeModel()
|
||||
resetModel();
|
||||
}
|
||||
|
||||
void MessageContentModel::intiializeEvent(const QString &eventId)
|
||||
{
|
||||
const auto newEvent = m_room->getEvent(eventId);
|
||||
if (newEvent != nullptr) {
|
||||
intiializeEvent(newEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageContentModel::intiializeEvent(const Quotient::RoomEvent *event)
|
||||
{
|
||||
m_event = loadEvent<RoomEvent>(event->fullJson());
|
||||
auto senderId = event->senderId();
|
||||
// A pending event might not have a sender ID set yet but in that case it must
|
||||
// be the local member.
|
||||
if (senderId.isEmpty()) {
|
||||
senderId = m_room->localMember().id();
|
||||
}
|
||||
if (m_eventSenderObject == nullptr) {
|
||||
m_eventSenderObject = std::unique_ptr<NeochatRoomMember>(new NeochatRoomMember(m_room, senderId));
|
||||
}
|
||||
Q_EMIT eventUpdated();
|
||||
}
|
||||
|
||||
bool MessageContentModel::showAuthor() const
|
||||
{
|
||||
return m_showAuthor;
|
||||
@@ -258,16 +251,16 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
||||
});
|
||||
|
||||
auto lastUpdated = pendingIt == m_room->pendingEvents().cend() ? QDateTime() : pendingIt->lastUpdated();
|
||||
return eventHandler.getTimeString(QStringLiteral("hh:mm"), m_isPending, lastUpdated);
|
||||
return eventHandler.getTimeString(false, QLocale::ShortFormat, m_isPending, lastUpdated);
|
||||
}
|
||||
if (role == AuthorRole) {
|
||||
return QVariant::fromValue<NeochatRoomMember *>(m_eventSenderObject.get());
|
||||
return QVariant::fromValue(eventHandler.getAuthor(m_isPending));
|
||||
}
|
||||
if (role == MediaInfoRole) {
|
||||
return eventHandler.getMediaInfo();
|
||||
}
|
||||
if (role == FileTransferInfoRole) {
|
||||
return QVariant::fromValue(m_room->cachedFileTransferInfo(m_event.get()));
|
||||
return QVariant::fromValue(fileInfo());
|
||||
}
|
||||
if (role == ItineraryModelRole) {
|
||||
return QVariant::fromValue<ItineraryModel *>(m_itineraryModel);
|
||||
@@ -444,37 +437,27 @@ QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentT
|
||||
QList<MessageComponent> components;
|
||||
components += MessageComponent{MessageComponentType::File, QString(), {}};
|
||||
const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event);
|
||||
auto body = EventHandler::rawMessageBody(*event);
|
||||
components += TextHandler().textComponents(body, EventHandler::messageBodyInputFormat(*event), m_room, event, event->isReplaced());
|
||||
|
||||
if (m_emptyItinerary) {
|
||||
if (!m_isReply) {
|
||||
auto fileTransferInfo = m_room->cachedFileTransferInfo(m_event.get());
|
||||
auto fileTransferInfo = fileInfo();
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
Q_ASSERT(event->content() != nullptr && event->content()->fileInfo() != nullptr);
|
||||
const QMimeType mimeType = event->content()->fileInfo()->mimeType;
|
||||
if (mimeType.name() == QStringLiteral("text/plain") || mimeType.parentMimeTypes().contains(QStringLiteral("text/plain"))) {
|
||||
QString originalName = event->content()->fileInfo()->originalName;
|
||||
if (originalName.isEmpty()) {
|
||||
originalName = event->plainBody();
|
||||
}
|
||||
KSyntaxHighlighting::Repository repository;
|
||||
KSyntaxHighlighting::Definition definitionForFile = repository.definitionForFileName(originalName);
|
||||
if (!definitionForFile.isValid()) {
|
||||
definitionForFile = repository.definitionForMimeType(mimeType.name());
|
||||
}
|
||||
|
||||
QFile file(fileTransferInfo.localPath.path());
|
||||
file.open(QIODevice::ReadOnly);
|
||||
components += MessageComponent{MessageComponentType::Code,
|
||||
QString::fromStdString(file.readAll().toStdString()),
|
||||
{{QStringLiteral("class"), definitionForFile.name()}}};
|
||||
}
|
||||
KSyntaxHighlighting::Repository repository;
|
||||
const auto definitionForFile = repository.definitionForFileName(fileTransferInfo.localPath.toString());
|
||||
if (definitionForFile.isValid() || QFileInfo(fileTransferInfo.localPath.path()).suffix() == QStringLiteral("txt")) {
|
||||
QFile file(fileTransferInfo.localPath.path());
|
||||
file.open(QIODevice::ReadOnly);
|
||||
components += MessageComponent{MessageComponentType::Code,
|
||||
QString::fromStdString(file.readAll().toStdString()),
|
||||
{{QStringLiteral("class"), definitionForFile.name()}}};
|
||||
}
|
||||
#endif
|
||||
|
||||
if (FileType::instance().fileHasImage(fileTransferInfo.localPath)) {
|
||||
QImageReader reader(fileTransferInfo.localPath.path());
|
||||
components += MessageComponent{MessageComponentType::Pdf, QString(), {{QStringLiteral("size"), reader.size()}}};
|
||||
}
|
||||
if (FileType::instance().fileHasImage(fileTransferInfo.localPath)) {
|
||||
QImageReader reader(fileTransferInfo.localPath.path());
|
||||
components += MessageComponent{MessageComponentType::Pdf, QString(), {{QStringLiteral("size"), reader.size()}}};
|
||||
}
|
||||
} else if (m_itineraryModel != nullptr) {
|
||||
components += MessageComponent{MessageComponentType::Itinerary, QString(), {}};
|
||||
@@ -484,12 +467,9 @@ QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentT
|
||||
} else {
|
||||
updateItineraryModel();
|
||||
}
|
||||
auto body = EventHandler::rawMessageBody(*event);
|
||||
components += TextHandler().textComponents(body, EventHandler::messageBodyInputFormat(*event), m_room, event, event->isReplaced());
|
||||
return components;
|
||||
}
|
||||
case MessageComponentType::Image:
|
||||
case MessageComponentType::Audio:
|
||||
case MessageComponentType::Video: {
|
||||
if (!m_event->is<StickerEvent>()) {
|
||||
const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event);
|
||||
@@ -572,7 +552,7 @@ void MessageContentModel::updateItineraryModel()
|
||||
|
||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
if (event->hasFileContent()) {
|
||||
auto filePath = m_room->cachedFileTransferInfo(m_event.get()).localPath;
|
||||
auto filePath = fileInfo().localPath;
|
||||
if (filePath.isEmpty() && m_itineraryModel != nullptr) {
|
||||
delete m_itineraryModel;
|
||||
m_itineraryModel = nullptr;
|
||||
@@ -600,4 +580,42 @@ void MessageContentModel::updateItineraryModel()
|
||||
}
|
||||
}
|
||||
|
||||
FileTransferInfo MessageContentModel::fileInfo() const
|
||||
{
|
||||
if (m_room == nullptr || m_event == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QString mxcUrl;
|
||||
int total;
|
||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
if (event->hasFileContent()) {
|
||||
mxcUrl = event->content()->fileInfo()->url().toString();
|
||||
total = event->content()->fileInfo()->payloadSize;
|
||||
}
|
||||
} else if (auto event = eventCast<const Quotient::StickerEvent>(m_event)) {
|
||||
mxcUrl = event->image().fileInfo()->url().toString();
|
||||
total = event->image().fileInfo()->payloadSize;
|
||||
}
|
||||
auto config = KSharedConfig::openStateConfig(QStringLiteral("neochatdownloads"))->group(QStringLiteral("downloads"));
|
||||
if (!config.hasKey(mxcUrl.mid(6))) {
|
||||
return m_room->fileTransferInfo(m_eventId);
|
||||
}
|
||||
const auto path = config.readPathEntry(mxcUrl.mid(6), QString());
|
||||
QFileInfo info(path);
|
||||
if (!info.isFile()) {
|
||||
config.deleteEntry(mxcUrl);
|
||||
return m_room->fileTransferInfo(m_eventId);
|
||||
}
|
||||
// TODO: we could check the hash here
|
||||
return FileTransferInfo{
|
||||
.status = FileTransferInfo::Completed,
|
||||
.isUpload = false,
|
||||
.progress = total,
|
||||
.total = total,
|
||||
.localDir = QUrl(info.dir().path()),
|
||||
.localPath = QUrl::fromLocalFile(path),
|
||||
};
|
||||
}
|
||||
|
||||
#include "moc_messagecontentmodel.cpp"
|
||||
|
||||
@@ -6,13 +6,11 @@
|
||||
#include <QAbstractListModel>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/events/roomevent.h>
|
||||
#include <Quotient/room.h>
|
||||
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "eventhandler.h"
|
||||
#include "itinerarymodel.h"
|
||||
#include "neochatroommember.h"
|
||||
|
||||
struct MessageComponent {
|
||||
MessageComponentType::Type type = MessageComponentType::Other;
|
||||
@@ -117,7 +115,6 @@ private:
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
QString m_eventId;
|
||||
QString m_eventSenderId;
|
||||
std::unique_ptr<NeochatRoomMember> m_eventSenderObject = nullptr;
|
||||
Quotient::RoomEventPtr m_event;
|
||||
|
||||
bool m_isPending;
|
||||
@@ -125,8 +122,6 @@ private:
|
||||
bool m_isReply;
|
||||
|
||||
void initializeModel();
|
||||
void intiializeEvent(const QString &eventId);
|
||||
void intiializeEvent(const Quotient::RoomEvent *event);
|
||||
|
||||
QList<MessageComponent> m_components;
|
||||
void resetModel();
|
||||
@@ -146,4 +141,6 @@ private:
|
||||
|
||||
void updateItineraryModel();
|
||||
bool m_emptyItinerary = false;
|
||||
|
||||
Quotient::FileTransferInfo fileInfo() const;
|
||||
};
|
||||
|
||||
@@ -26,9 +26,6 @@
|
||||
#include "messagecontentmodel.h"
|
||||
#include "models/messagefiltermodel.h"
|
||||
#include "models/reactionmodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatroommember.h"
|
||||
#include "readmarkermodel.h"
|
||||
#include "texthandler.h"
|
||||
|
||||
using namespace Quotient;
|
||||
@@ -48,6 +45,8 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
|
||||
roles[ThreadRootRole] = "threadRoot";
|
||||
roles[ShowSectionRole] = "showSection";
|
||||
roles[ReadMarkersRole] = "readMarkers";
|
||||
roles[ExcessReadMarkersRole] = "excessReadMarkers";
|
||||
roles[ReadMarkersStringRole] = "readMarkersString";
|
||||
roles[ShowReadMarkersRole] = "showReadMarkers";
|
||||
roles[ReactionRole] = "reaction";
|
||||
roles[ShowReactionsRole] = "showReactions";
|
||||
@@ -88,14 +87,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
// HACK: Reset the model to a null room first to make sure QML dismantles
|
||||
// last room's objects before the room is actually changed
|
||||
beginResetModel();
|
||||
m_readMarkerModels.clear();
|
||||
m_currentRoom->disconnect(this);
|
||||
m_currentRoom = nullptr;
|
||||
endResetModel();
|
||||
|
||||
// Don't clear the member objects until the model has been fully reset and all
|
||||
// refs cleared.
|
||||
m_memberObjects.clear();
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
@@ -106,7 +100,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
room->setDisplayed();
|
||||
|
||||
for (auto event = m_currentRoom->messageEvents().begin(); event != m_currentRoom->messageEvents().end(); ++event) {
|
||||
createEventObjects(&*event->viewAs<RoomEvent>());
|
||||
if (const auto &roomMessageEvent = &*event->viewAs<RoomMessageEvent>()) {
|
||||
createEventObjects(roomMessageEvent);
|
||||
}
|
||||
if (event->event()->is<PollStartEvent>()) {
|
||||
m_currentRoom->createPollHandler(eventCast<const PollStartEvent>(event->event()));
|
||||
}
|
||||
@@ -119,7 +115,11 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
|
||||
connect(m_currentRoom, &Room::aboutToAddNewMessages, this, [this](RoomEventsRange events) {
|
||||
for (auto &&event : events) {
|
||||
createEventObjects(event.get());
|
||||
const RoomMessageEvent *message = dynamic_cast<RoomMessageEvent *>(event.get());
|
||||
|
||||
if (message != nullptr) {
|
||||
createEventObjects(message);
|
||||
}
|
||||
if (event->is<PollStartEvent>()) {
|
||||
m_currentRoom->createPollHandler(eventCast<const PollStartEvent>(event.get()));
|
||||
}
|
||||
@@ -129,7 +129,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
});
|
||||
connect(m_currentRoom, &Room::aboutToAddHistoricalMessages, this, [this](RoomEventsRange events) {
|
||||
for (auto &event : events) {
|
||||
createEventObjects(event.get());
|
||||
if (const auto &roomMessageEvent = dynamic_cast<RoomMessageEvent *>(event.get())) {
|
||||
createEventObjects(roomMessageEvent);
|
||||
}
|
||||
if (event->is<PollStartEvent>()) {
|
||||
m_currentRoom->createPollHandler(eventCast<const PollStartEvent>(event.get()));
|
||||
}
|
||||
@@ -156,9 +158,8 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
refreshLastUserEvents(i);
|
||||
}
|
||||
});
|
||||
connect(m_currentRoom, &Room::pendingEventAboutToAdd, this, [this](Quotient::RoomEvent *event) {
|
||||
connect(m_currentRoom, &Room::pendingEventAboutToAdd, this, [this] {
|
||||
m_initialized = true;
|
||||
createEventObjects(event);
|
||||
beginInsertRows({}, 0, 0);
|
||||
});
|
||||
connect(m_currentRoom, &Room::pendingEventAdded, this, &MessageEventModel::endInsertRows);
|
||||
@@ -194,7 +195,10 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
moveReadMarker(toEventId);
|
||||
});
|
||||
connect(m_currentRoom, &Room::replacedEvent, this, [this](const RoomEvent *newEvent) {
|
||||
createEventObjects(newEvent);
|
||||
const RoomMessageEvent *message = eventCast<const RoomMessageEvent>(newEvent);
|
||||
if (message != nullptr) {
|
||||
createEventObjects(message);
|
||||
}
|
||||
});
|
||||
connect(m_currentRoom, &Room::updatedEvent, this, [this](const QString &eventId) {
|
||||
if (eventId.isEmpty()) { // How did we get here?
|
||||
@@ -202,7 +206,9 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
}
|
||||
const auto eventIt = m_currentRoom->findInTimeline(eventId);
|
||||
if (eventIt != m_currentRoom->historyEdge()) {
|
||||
createEventObjects(eventIt->event());
|
||||
if (const auto &event = dynamic_cast<const RoomMessageEvent *>(&**eventIt)) {
|
||||
createEventObjects(event);
|
||||
}
|
||||
if (eventIt->event()->is<PollStartEvent>()) {
|
||||
m_currentRoom->createPollHandler(eventCast<const PollStartEvent>(eventIt->event()));
|
||||
}
|
||||
@@ -210,10 +216,11 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
refreshEventRoles(eventId, {Qt::DisplayRole});
|
||||
});
|
||||
connect(m_currentRoom, &Room::changed, this, [this](Room::Changes changes) {
|
||||
if (changes.testFlag(Quotient::Room::Change::Other)) {
|
||||
if (changes & (Room::Change::PartiallyReadStats | Room::Change::UnreadStats | Room::Change::Other | Room::Change::Members)) {
|
||||
// this is slow
|
||||
for (auto it = m_currentRoom->messageEvents().rbegin(); it != m_currentRoom->messageEvents().rend(); ++it) {
|
||||
createEventObjects(it->event());
|
||||
auto event = it->event();
|
||||
refreshEventRoles(event->id(), {ReadMarkersRole, ReadMarkersStringRole, ExcessReadMarkersRole});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -221,6 +228,22 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
});
|
||||
connect(m_currentRoom, &Room::memberNameUpdated, this, [this](RoomMember member) {
|
||||
for (auto it = m_currentRoom->messageEvents().rbegin(); it != m_currentRoom->messageEvents().rend(); ++it) {
|
||||
auto event = it->event();
|
||||
if (event->senderId() == member.id()) {
|
||||
refreshEventRoles(event->id(), {AuthorRole});
|
||||
}
|
||||
}
|
||||
});
|
||||
connect(m_currentRoom, &Room::memberAvatarUpdated, this, [this](RoomMember member) {
|
||||
for (auto it = m_currentRoom->messageEvents().rbegin(); it != m_currentRoom->messageEvents().rend(); ++it) {
|
||||
auto event = it->event();
|
||||
if (event->senderId() == member.id()) {
|
||||
refreshEventRoles(event->id(), {AuthorRole});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
qCDebug(MessageEvent) << "Connected to room" << room->id() << "as" << room->localMember().id();
|
||||
} else {
|
||||
@@ -388,8 +411,6 @@ void MessageEventModel::fetchMore(const QModelIndex &parent)
|
||||
}
|
||||
}
|
||||
|
||||
static NeochatRoomMember *emptyNeochatRoomMember = new NeochatRoomMember;
|
||||
|
||||
QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
{
|
||||
if (!checkIndex(idx, QAbstractItemModel::CheckIndexOption::IndexIsValid)) {
|
||||
@@ -462,18 +483,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
if (role == AuthorRole) {
|
||||
QString mId;
|
||||
if (isPending) {
|
||||
mId = m_currentRoom->localMember().id();
|
||||
} else {
|
||||
mId = evt.senderId();
|
||||
}
|
||||
|
||||
if (!m_memberObjects.contains(mId)) {
|
||||
return QVariant::fromValue<NeochatRoomMember *>(emptyNeochatRoomMember);
|
||||
}
|
||||
|
||||
return QVariant::fromValue<NeochatRoomMember *>(m_memberObjects.at(mId).get());
|
||||
return QVariant::fromValue(eventHandler.getAuthor(isPending));
|
||||
}
|
||||
|
||||
if (role == HighlightRole) {
|
||||
@@ -504,11 +514,11 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
if (role == ProgressInfoRole) {
|
||||
if (auto e = eventCast<const RoomMessageEvent>(&evt)) {
|
||||
if (e->hasFileContent()) {
|
||||
return QVariant::fromValue(m_currentRoom->cachedFileTransferInfo(&evt));
|
||||
return QVariant::fromValue(m_currentRoom->fileTransferInfo(e->id()));
|
||||
}
|
||||
}
|
||||
if (eventCast<const StickerEvent>(&evt)) {
|
||||
return QVariant::fromValue(m_currentRoom->cachedFileTransferInfo(&evt));
|
||||
if (auto e = eventCast<const StickerEvent>(&evt)) {
|
||||
return QVariant::fromValue(m_currentRoom->fileTransferInfo(e->id()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,15 +557,19 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
if (role == ReadMarkersRole) {
|
||||
if (m_readMarkerModels.contains(evt.id())) {
|
||||
return QVariant::fromValue<ReadMarkerModel *>(m_readMarkerModels[evt.id()].get());
|
||||
} else {
|
||||
return QVariantList();
|
||||
}
|
||||
return QVariant::fromValue(eventHandler.getReadMarkers());
|
||||
}
|
||||
|
||||
if (role == ExcessReadMarkersRole) {
|
||||
return eventHandler.getNumberExcessReadMarkers();
|
||||
}
|
||||
|
||||
if (role == ReadMarkersStringRole) {
|
||||
return eventHandler.getReadMarkersString();
|
||||
}
|
||||
|
||||
if (role == ShowReadMarkersRole) {
|
||||
return m_readMarkerModels.contains(evt.id());
|
||||
return eventHandler.hasReadMarkers();
|
||||
}
|
||||
|
||||
if (role == ReactionRole) {
|
||||
@@ -616,71 +630,30 @@ int MessageEventModel::eventIdToRow(const QString &eventID) const
|
||||
return it - m_currentRoom->messageEvents().rbegin() + timelineBaseIndex();
|
||||
}
|
||||
|
||||
void MessageEventModel::createEventObjects(const Quotient::RoomEvent *event)
|
||||
void MessageEventModel::createEventObjects(const Quotient::RoomMessageEvent *event)
|
||||
{
|
||||
if (event == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto eventId = event->id();
|
||||
auto senderId = event->senderId();
|
||||
// A pending event might not have a sender ID set yet but in that case it must
|
||||
// be the local member.
|
||||
if (senderId.isEmpty()) {
|
||||
senderId = m_currentRoom->localMember().id();
|
||||
}
|
||||
|
||||
if (!m_memberObjects.contains(senderId)) {
|
||||
m_memberObjects[senderId] = std::unique_ptr<NeochatRoomMember>(new NeochatRoomMember(m_currentRoom, senderId));
|
||||
}
|
||||
|
||||
// ReadMarkerModel handles updates to add and remove markers, we only need to
|
||||
// ReactionModel handles updates to add and remove reactions, we only need to
|
||||
// handle adding and removing whole models here.
|
||||
if (m_readMarkerModels.contains(eventId)) {
|
||||
if (m_reactionModels.contains(eventId)) {
|
||||
// If a model already exists but now has no reactions remove it
|
||||
if (m_readMarkerModels[eventId]->rowCount() <= 0) {
|
||||
m_readMarkerModels.remove(eventId);
|
||||
if (m_reactionModels[eventId]->rowCount() <= 0) {
|
||||
m_reactionModels.remove(eventId);
|
||||
if (!resetting) {
|
||||
refreshEventRoles(eventId, {ReadMarkersRole, ShowReadMarkersRole});
|
||||
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto memberIds = m_currentRoom->userIdsAtEvent(eventId);
|
||||
memberIds.remove(m_currentRoom->localMember().id());
|
||||
if (memberIds.size() > 0) {
|
||||
if (m_currentRoom->relatedEvents(*event, Quotient::EventRelation::AnnotationType).count() > 0) {
|
||||
// If a model doesn't exist and there are reactions add it.
|
||||
auto newModel = QSharedPointer<ReadMarkerModel>(new ReadMarkerModel(eventId, m_currentRoom));
|
||||
if (newModel->rowCount() > 0) {
|
||||
m_readMarkerModels[eventId] = newModel;
|
||||
if (!resetting) {
|
||||
refreshEventRoles(eventId, {ReadMarkersRole, ShowReadMarkersRole});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto roomEvent = eventCast<const RoomMessageEvent>(event)) {
|
||||
// ReactionModel handles updates to add and remove reactions, we only need to
|
||||
// handle adding and removing whole models here.
|
||||
if (m_reactionModels.contains(eventId)) {
|
||||
// If a model already exists but now has no reactions remove it
|
||||
if (m_reactionModels[eventId]->rowCount() <= 0) {
|
||||
m_reactionModels.remove(eventId);
|
||||
auto reactionModel = QSharedPointer<ReactionModel>(new ReactionModel(event, m_currentRoom));
|
||||
if (reactionModel->rowCount() > 0) {
|
||||
m_reactionModels[eventId] = reactionModel;
|
||||
if (!resetting) {
|
||||
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (m_currentRoom->relatedEvents(*event, Quotient::EventRelation::AnnotationType).count() > 0) {
|
||||
// If a model doesn't exist and there are reactions add it.
|
||||
auto reactionModel = QSharedPointer<ReactionModel>(new ReactionModel(roomEvent, m_currentRoom));
|
||||
if (reactionModel->rowCount() > 0) {
|
||||
m_reactionModels[eventId] = reactionModel;
|
||||
if (!resetting) {
|
||||
refreshEventRoles(eventId, {ReactionRole, ShowReactionsRole});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,7 @@
|
||||
|
||||
#include "linkpreviewer.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatroommember.h"
|
||||
#include "pollhandler.h"
|
||||
#include "readmarkermodel.h"
|
||||
|
||||
class ReactionModel;
|
||||
|
||||
@@ -60,6 +58,8 @@ public:
|
||||
ShowSectionRole, /**< Whether the section header should be shown. */
|
||||
|
||||
ReadMarkersRole, /**< The first 5 other users at the event for read marker tracking. */
|
||||
ExcessReadMarkersRole, /**< The number of other users at the event after the first 5. */
|
||||
ReadMarkersStringRole, /**< String with the display name and mxID of the users at the event. */
|
||||
ShowReadMarkersRole, /**< Whether there are any other user read markers to be shown. */
|
||||
ReactionRole, /**< List model for this event. */
|
||||
ShowReactionsRole, /**< Whether there are any reactions to be shown. */
|
||||
@@ -116,8 +116,6 @@ private:
|
||||
bool movingEvent = false;
|
||||
KFormat m_format;
|
||||
|
||||
std::map<QString, std::unique_ptr<NeochatRoomMember>> m_memberObjects;
|
||||
QMap<QString, QSharedPointer<ReadMarkerModel>> m_readMarkerModels;
|
||||
QMap<QString, QSharedPointer<ReactionModel>> m_reactionModels;
|
||||
|
||||
[[nodiscard]] int timelineBaseIndex() const;
|
||||
@@ -132,7 +130,7 @@ private:
|
||||
int refreshEventRoles(const QString &eventId, const QList<int> &roles = {});
|
||||
void moveReadMarker(const QString &toEventId);
|
||||
|
||||
void createEventObjects(const Quotient::RoomEvent *event);
|
||||
void createEventObjects(const Quotient::RoomMessageEvent *event);
|
||||
// Hack to ensure that we don't call endInsertRows when we haven't called beginInsertRows
|
||||
bool m_initialized = false;
|
||||
|
||||
|
||||
@@ -4,13 +4,11 @@
|
||||
#include "messagefiltermodel.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <QVariant>
|
||||
|
||||
#include "enums/delegatetype.h"
|
||||
#include "messagecontentmodel.h"
|
||||
#include "messageeventmodel.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatroommember.h"
|
||||
#include "timelinemodel.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
@@ -99,7 +99,7 @@ void PushRuleModel::setRules(QList<Quotient::PushRule> rules, PushRuleKind::Kind
|
||||
for (const auto &rule : rules) {
|
||||
QString roomId;
|
||||
if (rule.conditions.size() > 0) {
|
||||
for (const auto &condition : std::as_const(rule.conditions)) {
|
||||
for (const auto &condition : rule.conditions) {
|
||||
if (condition.key == QStringLiteral("room_id")) {
|
||||
roomId = condition.pattern;
|
||||
}
|
||||
@@ -163,7 +163,7 @@ PushRuleSection::Section PushRuleModel::getSection(Quotient::PushRule rule)
|
||||
}
|
||||
// If the rule has push conditions and one is a room ID it is a room only keyword.
|
||||
if (!rule.conditions.isEmpty()) {
|
||||
for (const auto &condition : std::as_const(rule.conditions)) {
|
||||
for (auto condition : rule.conditions) {
|
||||
if (condition.key == QStringLiteral("room_id")) {
|
||||
return PushRuleSection::RoomKeywords;
|
||||
}
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "readmarkermodel.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <Quotient/roommember.h>
|
||||
|
||||
#define MAXMARKERS 5
|
||||
|
||||
ReadMarkerModel::ReadMarkerModel(const QString &eventId, NeoChatRoom *room)
|
||||
: QAbstractListModel(nullptr)
|
||||
, m_room(room)
|
||||
, m_eventId(eventId)
|
||||
{
|
||||
Q_ASSERT(!m_eventId.isEmpty());
|
||||
Q_ASSERT(m_room != nullptr);
|
||||
|
||||
connect(m_room, &NeoChatRoom::changed, this, [this](Quotient::Room::Changes changes) {
|
||||
if (m_room != nullptr && changes.testFlag(Quotient::Room::Change::Other)) {
|
||||
auto memberIds = m_room->userIdsAtEvent(m_eventId).values();
|
||||
if (memberIds == m_markerIds) {
|
||||
return;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
m_markerIds.clear();
|
||||
endResetModel();
|
||||
|
||||
beginResetModel();
|
||||
memberIds.removeAll(m_room->localMember().id());
|
||||
m_markerIds = memberIds;
|
||||
endResetModel();
|
||||
|
||||
Q_EMIT reactionUpdated();
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::memberNameUpdated, this, [this](Quotient::RoomMember member) {
|
||||
if (m_markerIds.contains(member.id())) {
|
||||
const auto memberIndex = index(m_markerIds.indexOf(member.id()));
|
||||
Q_EMIT dataChanged(memberIndex, memberIndex);
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::memberAvatarUpdated, this, [this](Quotient::RoomMember member) {
|
||||
if (m_markerIds.contains(member.id())) {
|
||||
const auto memberIndex = index(m_markerIds.indexOf(member.id()));
|
||||
Q_EMIT dataChanged(memberIndex, memberIndex);
|
||||
}
|
||||
});
|
||||
|
||||
beginResetModel();
|
||||
auto userIds = m_room->userIdsAtEvent(m_eventId);
|
||||
userIds.remove(m_room->localMember().id());
|
||||
m_markerIds = userIds.values();
|
||||
endResetModel();
|
||||
|
||||
Q_EMIT reactionUpdated();
|
||||
}
|
||||
|
||||
QVariant ReadMarkerModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (index.row() >= rowCount()) {
|
||||
qDebug() << "ReactionModel, something's wrong: index.row() >= rowCount()";
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto member = m_room->member(m_markerIds.value(index.row()));
|
||||
|
||||
if (role == DisplayNameRole) {
|
||||
return member.htmlSafeDisplayName();
|
||||
}
|
||||
|
||||
if (role == AvatarUrlRole) {
|
||||
return member.avatarUrl();
|
||||
}
|
||||
|
||||
if (role == ColorRole) {
|
||||
return member.color();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
int ReadMarkerModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return std::min(int(m_markerIds.size()), MAXMARKERS);
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ReadMarkerModel::roleNames() const
|
||||
{
|
||||
return {
|
||||
{DisplayNameRole, "displayName"},
|
||||
{AvatarUrlRole, "avatarUrl"},
|
||||
{ColorRole, "memberColor"},
|
||||
};
|
||||
}
|
||||
|
||||
QString ReadMarkerModel::readMarkersString()
|
||||
{
|
||||
/**
|
||||
* The string ends up in the form
|
||||
* "x users: user1DisplayName, user2DisplayName, etc."
|
||||
*/
|
||||
QString readMarkersString = i18np("1 user: ", "%1 users: ", m_markerIds.size());
|
||||
for (const auto &memberId : m_markerIds) {
|
||||
auto member = m_room->member(memberId);
|
||||
QString displayName = member.htmlSafeDisambiguatedName();
|
||||
if (displayName.isEmpty()) {
|
||||
displayName = i18nc("A member who is not in the room has been requested.", "unknown member");
|
||||
}
|
||||
readMarkersString += displayName + i18nc("list separator", ", ");
|
||||
}
|
||||
readMarkersString.chop(2);
|
||||
return readMarkersString;
|
||||
}
|
||||
|
||||
QString ReadMarkerModel::excessReadMarkersString()
|
||||
{
|
||||
if (m_room == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (m_markerIds.size() > MAXMARKERS) {
|
||||
return QStringLiteral("+ ") + QString::number(m_markerIds.size() - MAXMARKERS);
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_readmarkermodel.cpp"
|
||||
@@ -1,79 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 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 <QAbstractListModel>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
#include "neochatroom.h"
|
||||
|
||||
/**
|
||||
* @class ReadMarkerModel
|
||||
*
|
||||
* This class defines the model for visualising a list of reactions to an event.
|
||||
*/
|
||||
class ReadMarkerModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
/**
|
||||
* @brief Returns a string with the names of the read markers at the event.
|
||||
*
|
||||
* This is in the form "x users: name 1, name 2, ...".
|
||||
*/
|
||||
Q_PROPERTY(QString readMarkersString READ readMarkersString NOTIFY reactionUpdated)
|
||||
|
||||
/**
|
||||
* @brief Returns the number of excess user read markers for the event.
|
||||
*
|
||||
* This returns a string in the form "+ x" ready for use in the UI.
|
||||
*/
|
||||
Q_PROPERTY(QString excessReadMarkersString READ excessReadMarkersString NOTIFY reactionUpdated)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*/
|
||||
enum Roles {
|
||||
DisplayNameRole = Qt::DisplayRole, /**< The display name of the member in the room. */
|
||||
AvatarUrlRole, /**< The avatar for the member in the room. */
|
||||
ColorRole, /**< The color for the member. */
|
||||
};
|
||||
|
||||
explicit ReadMarkerModel(const QString &eventId, NeoChatRoom *room);
|
||||
|
||||
QString readMarkersString();
|
||||
QString excessReadMarkersString();
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
*
|
||||
* @sa QAbstractItemModel::data
|
||||
*/
|
||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
/**
|
||||
* @brief Number of rows in the model.
|
||||
*
|
||||
* @sa QAbstractItemModel::rowCount
|
||||
*/
|
||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
/**
|
||||
* @brief Returns a mapping from Role enum values to role names.
|
||||
*
|
||||
* @sa Roles, QAbstractItemModel::roleNames()
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void reactionUpdated();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
QString m_eventId;
|
||||
QList<QString> m_markerIds;
|
||||
};
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "eventhandler.h"
|
||||
#include "models/messagecontentmodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatroommember.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
|
||||
@@ -67,17 +66,7 @@ void SearchModel::search()
|
||||
m_job = job;
|
||||
connect(job, &BaseJob::finished, this, [this, job] {
|
||||
beginResetModel();
|
||||
m_memberObjects.clear();
|
||||
m_result = job->searchCategories().roomEvents;
|
||||
|
||||
if (m_result.has_value()) {
|
||||
for (const auto &result : m_result.value().results) {
|
||||
if (!m_memberObjects.contains(result.result->senderId())) {
|
||||
m_memberObjects[result.result->senderId()] = std::unique_ptr<NeochatRoomMember>(new NeochatRoomMember(m_room, result.result->senderId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
setSearching(false);
|
||||
m_job = nullptr;
|
||||
@@ -94,7 +83,7 @@ QVariant SearchModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
switch (role) {
|
||||
case AuthorRole:
|
||||
return QVariant::fromValue<NeochatRoomMember *>(m_memberObjects.at(event.senderId()).get());
|
||||
return QVariant::fromValue(eventHandler.getAuthor());
|
||||
case ShowSectionRole:
|
||||
if (row == 0) {
|
||||
return true;
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
|
||||
#include <Quotient/csapi/search.h>
|
||||
|
||||
#include "neochatroommember.h"
|
||||
|
||||
namespace Quotient
|
||||
{
|
||||
class Connection;
|
||||
@@ -125,6 +123,4 @@ private:
|
||||
std::optional<Quotient::SearchJob::ResultRoomEvents> m_result = std::nullopt;
|
||||
Quotient::SearchJob *m_job = nullptr;
|
||||
bool m_searching = false;
|
||||
|
||||
std::map<QString, std::unique_ptr<NeochatRoomMember>> m_memberObjects;
|
||||
};
|
||||
|
||||
@@ -33,7 +33,6 @@ void UserListModel::setRoom(NeoChatRoom *room)
|
||||
m_currentRoom->disconnect(this);
|
||||
m_currentRoom->connection()->disconnect(this);
|
||||
m_currentRoom = nullptr;
|
||||
m_members.clear();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
@@ -57,7 +56,7 @@ void UserListModel::setRoom(NeoChatRoom *room)
|
||||
});
|
||||
}
|
||||
|
||||
m_active = false;
|
||||
refreshAllMembers();
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
@@ -170,6 +169,7 @@ void UserListModel::refreshMember(const Quotient::RoomMember &member, const QLis
|
||||
void UserListModel::refreshAllMembers()
|
||||
{
|
||||
beginResetModel();
|
||||
m_members.clear();
|
||||
|
||||
if (m_currentRoom != nullptr) {
|
||||
m_members = m_currentRoom->joinedMemberIds();
|
||||
@@ -179,17 +179,8 @@ void UserListModel::refreshAllMembers()
|
||||
MemberSorter sorter(m_currentRoom);
|
||||
#endif
|
||||
std::sort(m_members.begin(), m_members.end(), [&sorter, this](const auto &left, const auto &right) {
|
||||
const auto leftPl = m_currentRoom->getUserPowerLevel(left);
|
||||
const auto rightPl = m_currentRoom->getUserPowerLevel(right);
|
||||
if (leftPl > rightPl) {
|
||||
return true;
|
||||
} else if (rightPl > leftPl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return sorter(m_currentRoom->member(left), m_currentRoom->member(right));
|
||||
});
|
||||
|
||||
}
|
||||
endResetModel();
|
||||
Q_EMIT usersRefreshed();
|
||||
@@ -225,14 +216,4 @@ QHash<int, QByteArray> UserListModel::roleNames() const
|
||||
return roles;
|
||||
}
|
||||
|
||||
void UserListModel::activate()
|
||||
{
|
||||
if (m_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_active = true;
|
||||
refreshAllMembers();
|
||||
}
|
||||
|
||||
#include "moc_userlistmodel.cpp"
|
||||
|
||||
@@ -77,8 +77,6 @@ public:
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
void activate();
|
||||
|
||||
Q_SIGNALS:
|
||||
void roomChanged();
|
||||
void usersRefreshed();
|
||||
@@ -96,8 +94,6 @@ private:
|
||||
QPointer<NeoChatRoom> m_currentRoom;
|
||||
QList<QString> m_members;
|
||||
|
||||
bool m_active = false;
|
||||
|
||||
int findUserPos(const Quotient::RoomMember &member) const;
|
||||
[[nodiscard]] int findUserPos(const QString &username) const;
|
||||
};
|
||||
|
||||
@@ -16,7 +16,6 @@ Name[eu]=NeoChat
|
||||
Name[fi]=NeoChat
|
||||
Name[fr]=NeoChat
|
||||
Name[gl]=NeoChat
|
||||
Name[he]=NeoChat
|
||||
Name[hu]=NeoChat
|
||||
Name[ia]=Neochat
|
||||
Name[id]=NeoChat
|
||||
@@ -59,7 +58,6 @@ Comment[eu]=Matrix, deszentralizatutako komunikazio protokolorako, bezero bat
|
||||
Comment[fi]=Hajautetun Matrix-viestintäyhteyskäytännön asiakasohjelma
|
||||
Comment[fr]=Un client pour « Matrix », le protocole décentralisé de communications.
|
||||
Comment[gl]=Un cliente para Matrix, o protocolo de comunicación descentralizada.
|
||||
Comment[he]=לקוח ל־matrix, פרוטוקול התקשורת המבוזר
|
||||
Comment[hu]=Kliens a matrixhoz, a decentralizált kommunikációs protokollhoz
|
||||
Comment[ia]=Un cliente per Matrix, le protocollo de communication decentralisate
|
||||
Comment[id]=Sebuah klien untuk matrix, protokol komunikasi terdecentralisasi
|
||||
@@ -104,7 +102,6 @@ Name[eu]=Mezu berria
|
||||
Name[fi]=Uusi viesti
|
||||
Name[fr]=Nouveau message
|
||||
Name[gl]=Nova mensaxe
|
||||
Name[he]=הודעה חדשה
|
||||
Name[hu]=Új üzenet
|
||||
Name[ia]=Nove message
|
||||
Name[id]=Pesan baru
|
||||
@@ -145,7 +142,6 @@ Comment[eu]=Mezu berri bat dago
|
||||
Comment[fi]=Saapui uusi viesti
|
||||
Comment[fr]=Il y a un nouveau message
|
||||
Comment[gl]=Hai unha nova mensaxe.
|
||||
Comment[he]=יש הודעה חדשה
|
||||
Comment[hu]=Új üzenet érkezett
|
||||
Comment[ia]=Il ha un nove message
|
||||
Comment[id]=Ada pesan baru
|
||||
@@ -190,7 +186,6 @@ Name[eu]=Gonbidapen berria
|
||||
Name[fi]=Uusi kutsu
|
||||
Name[fr]=Nouvelle invitation
|
||||
Name[gl]=Novo convite
|
||||
Name[he]=הזמנה חדשה
|
||||
Name[hu]=Új meghívó
|
||||
Name[ia]=Nove invitation
|
||||
Name[id]=Undangan Baru
|
||||
@@ -230,7 +225,6 @@ Comment[eu]=Gela baterako gonbidapen berri bat dago
|
||||
Comment[fi]=Uusi kutsu huoneeseen
|
||||
Comment[fr]=Il y a une nouvelle invitation dans un salon.
|
||||
Comment[gl]=Tes un novo convite para unha sala.
|
||||
Comment[he]=יש הזמנה חדשה לחדר
|
||||
Comment[hu]=Új meghívó érkezett egy szobába
|
||||
Comment[ia]=Il ha un nove invitation a un sala
|
||||
Comment[id]=Ada undangan baru ke sebuah ruangan
|
||||
@@ -261,7 +255,7 @@ Action=Popup
|
||||
Name=Share
|
||||
Name[ar]=شارك
|
||||
Name[ca]=Compartició
|
||||
Name[ca@valencia]=Compartiu
|
||||
Name[ca@valencia]=Compartició
|
||||
Name[cs]=Sdílet
|
||||
Name[en_GB]=Share
|
||||
Name[eo]=Kundividi
|
||||
@@ -269,7 +263,6 @@ Name[es]=Compartir
|
||||
Name[eu]=Partekatu
|
||||
Name[fi]=Jaa
|
||||
Name[fr]=Partager
|
||||
Name[he]=שיתוף
|
||||
Name[hu]=Megosztás
|
||||
Name[ia]=Comparti
|
||||
Name[it]=Condivisione
|
||||
@@ -278,7 +271,6 @@ Name[lv]=Kopīgot
|
||||
Name[nl]=Gedeelde
|
||||
Name[nn]=Del
|
||||
Name[pl]=Udostępnij
|
||||
Name[pt_BR]=Compartilhar
|
||||
Name[sl]=Deli
|
||||
Name[sv]=Dela
|
||||
Name[ta]=பகிர்
|
||||
@@ -296,7 +288,6 @@ Comment[es]=El resultado de compartir una parte de contenido
|
||||
Comment[eu]=Eduki pieza bat partekatzearen emaitza
|
||||
Comment[fi]=Tulos yhden sisältöosasen jakamisesta
|
||||
Comment[fr]=Le résultat du partage d'une partie de contenu.
|
||||
Comment[he]=תוצאת שיתוף פיסת תוכן
|
||||
Comment[hu]=Tartalom megosztásának eredménye
|
||||
Comment[ia]=Le exito de compartir un pecietta de contento
|
||||
Comment[it]=Il risultato della condivisione di un contenuto
|
||||
@@ -305,7 +296,6 @@ Comment[lv]=Satura kopīgošanas rezultāts
|
||||
Comment[nl]=Het resultaat van het delen van een stukje inhoud
|
||||
Comment[nn]=Resultatet av deling av innhald
|
||||
Comment[pl]=Wynik udostępniania kawałka treści
|
||||
Comment[pt_BR]=O resultado de compartilhar um conteúdo
|
||||
Comment[sl]=Rezultat deljenega kosa vsebine
|
||||
Comment[sv]=Resultatet av att dela innehåll
|
||||
Comment[ta]=எதையோ பகிர்ந்ததன் விளைவு
|
||||
|
||||
@@ -187,11 +187,5 @@
|
||||
<default>false</default>
|
||||
</entry>
|
||||
</group>
|
||||
<group name="Security">
|
||||
<entry name="RejectUnknownInvites" type="bool">
|
||||
<label>Reject unknown invites</label>
|
||||
<default>false</default>
|
||||
</entry>
|
||||
</group>
|
||||
</kcfg>
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
#include <Quotient/csapi/content-repo.h>
|
||||
#include <Quotient/csapi/profile.h>
|
||||
#include <Quotient/csapi/versions.h>
|
||||
#include <Quotient/database.h>
|
||||
#include <Quotient/jobs/downloadfilejob.h>
|
||||
#include <Quotient/qt_connection_util.h>
|
||||
@@ -133,21 +132,6 @@ void NeoChatConnection::connectSignals()
|
||||
Q_EMIT homeNotificationsChanged();
|
||||
Q_EMIT homeHaveHighlightNotificationsChanged();
|
||||
});
|
||||
|
||||
// Fetch unstable features
|
||||
// TODO: Expose unstableFeatures() in libQuotient
|
||||
connect(
|
||||
this,
|
||||
&Connection::connected,
|
||||
this,
|
||||
[this] {
|
||||
auto job = callApi<GetVersionsJob>(BackgroundRequest);
|
||||
connect(job, &GetVersionsJob::success, this, [this, job] {
|
||||
m_canCheckMutualRooms = job->unstableFeatures().contains("uk.half-shot.msc2666.query_mutual_rooms"_ls);
|
||||
Q_EMIT canCheckMutualRoomsChanged();
|
||||
});
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
}
|
||||
|
||||
int NeoChatConnection::badgeNotificationCount() const
|
||||
@@ -216,11 +200,6 @@ QVariantList NeoChatConnection::getSupportedRoomVersions() const
|
||||
return supportedRoomVersions;
|
||||
}
|
||||
|
||||
bool NeoChatConnection::canCheckMutualRooms() const
|
||||
{
|
||||
return m_canCheckMutualRooms;
|
||||
}
|
||||
|
||||
void NeoChatConnection::changePassword(const QString ¤tPassword, const QString &newPassword)
|
||||
{
|
||||
auto job = callApi<NeochatChangePasswordJob>(newPassword, false);
|
||||
|
||||
@@ -79,11 +79,6 @@ class NeoChatConnection : public Quotient::Connection
|
||||
*/
|
||||
Q_PROPERTY(bool isOnline READ isOnline WRITE setIsOnline NOTIFY isOnlineChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the server supports querying a user's mutual rooms.
|
||||
*/
|
||||
Q_PROPERTY(bool canCheckMutualRooms READ canCheckMutualRooms NOTIFY canCheckMutualRoomsChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the status after an attempt to change the password on an account.
|
||||
@@ -100,7 +95,6 @@ public:
|
||||
|
||||
Q_INVOKABLE void logout(bool serverSideLogout);
|
||||
Q_INVOKABLE QVariantList getSupportedRoomVersions() const;
|
||||
bool canCheckMutualRooms() const;
|
||||
|
||||
/**
|
||||
* @brief Change the password for an account.
|
||||
@@ -202,7 +196,6 @@ Q_SIGNALS:
|
||||
void passwordStatus(NeoChatConnection::PasswordStatus status);
|
||||
void userConsentRequired(QUrl url);
|
||||
void badgeNotificationCountChanged(NeoChatConnection *connection, int count);
|
||||
void canCheckMutualRoomsChanged();
|
||||
|
||||
private:
|
||||
bool m_isOnline = true;
|
||||
@@ -215,6 +208,4 @@ private:
|
||||
int m_badgeNotificationCount = 0;
|
||||
|
||||
QHash<QUrl, LinkPreviewer *> m_linkPreviewers;
|
||||
|
||||
bool m_canCheckMutualRooms = false;
|
||||
};
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
#include "events/joinrulesevent.h"
|
||||
#include "events/pollevent.h"
|
||||
#include "filetransferpseudojob.h"
|
||||
#include "jobs/neochatgetcommonroomsjob.h"
|
||||
#include "neochatconfig.h"
|
||||
#include "notificationsmanager.h"
|
||||
#include "roomlastmessageprovider.h"
|
||||
@@ -70,26 +69,6 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
||||
setFileUploadingProgress(0);
|
||||
setHasFileUploading(false);
|
||||
});
|
||||
connect(this, &Room::fileTransferCompleted, this, [this](QString eventId) {
|
||||
const auto evtIt = findInTimeline(eventId);
|
||||
if (evtIt != messageEvents().rend()) {
|
||||
const auto m_event = evtIt->viewAs<RoomEvent>();
|
||||
QString mxcUrl;
|
||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
if (event->hasFileContent()) {
|
||||
mxcUrl = event->content()->fileInfo()->url().toString();
|
||||
}
|
||||
} else if (auto event = eventCast<const Quotient::StickerEvent>(m_event)) {
|
||||
mxcUrl = event->image().fileInfo()->url().toString();
|
||||
}
|
||||
if (mxcUrl.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
auto localPath = this->fileTransferInfo(eventId).localPath.toLocalFile();
|
||||
auto config = KSharedConfig::openStateConfig(QStringLiteral("neochatdownloads"))->group(QStringLiteral("downloads"));
|
||||
config.writePathEntry(mxcUrl.mid(6), localPath);
|
||||
}
|
||||
});
|
||||
|
||||
connect(this, &Room::addedMessages, this, &NeoChatRoom::readMarkerLoadedChanged);
|
||||
connect(this, &Room::aboutToAddHistoricalMessages, this, &NeoChatRoom::cleanupExtraEventRange);
|
||||
@@ -130,38 +109,14 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
|
||||
return;
|
||||
}
|
||||
auto roomMemberEvent = currentState().get<RoomMemberEvent>(localMember().id());
|
||||
|
||||
auto showNotification = [this, roomMemberEvent] {
|
||||
QImage avatar_image;
|
||||
if (roomMemberEvent && !member(roomMemberEvent->senderId()).avatarUrl().isEmpty()) {
|
||||
avatar_image = memberAvatar(roomMemberEvent->senderId()).get(this->connection(), 128, [] {});
|
||||
} else {
|
||||
qWarning() << "using this room's avatar";
|
||||
avatar_image = avatar(128);
|
||||
}
|
||||
|
||||
NotificationsManager::instance().postInviteNotification(this,
|
||||
displayName(),
|
||||
member(roomMemberEvent->senderId()).htmlSafeDisplayName(),
|
||||
avatar_image);
|
||||
};
|
||||
|
||||
if (NeoChatConfig::rejectUnknownInvites()) {
|
||||
auto job = this->connection()->callApi<NeochatGetCommonRoomsJob>(roomMemberEvent->senderId());
|
||||
connect(job, &BaseJob::result, this, [this, job, roomMemberEvent, showNotification] {
|
||||
QJsonObject replyData = job->jsonData();
|
||||
if (replyData.contains(QStringLiteral("joined"))) {
|
||||
const bool inAnyOfOurRooms = !replyData[QStringLiteral("joined")].toArray().isEmpty();
|
||||
if (inAnyOfOurRooms) {
|
||||
showNotification();
|
||||
} else {
|
||||
leaveRoom();
|
||||
}
|
||||
}
|
||||
});
|
||||
QImage avatar_image;
|
||||
if (roomMemberEvent && !member(roomMemberEvent->senderId()).avatarUrl().isEmpty()) {
|
||||
avatar_image = memberAvatar(roomMemberEvent->senderId()).get(this->connection(), 128, [] {});
|
||||
} else {
|
||||
showNotification();
|
||||
qWarning() << "using this room's avatar";
|
||||
avatar_image = avatar(128);
|
||||
}
|
||||
NotificationsManager::instance().postInviteNotification(this, displayName(), member(roomMemberEvent->senderId()).htmlSafeDisplayName(), avatar_image);
|
||||
},
|
||||
Qt::SingleShotConnection);
|
||||
connect(this, &Room::changed, this, [this] {
|
||||
@@ -891,18 +846,11 @@ void NeoChatRoom::setUserPowerLevel(const QString &userID, const int &powerLevel
|
||||
|
||||
int NeoChatRoom::getUserPowerLevel(const QString &userId) const
|
||||
{
|
||||
if (!successorId().isEmpty()) {
|
||||
return 0; // No one can upgrade a room that's already upgraded
|
||||
auto powerLevelEvent = currentState().get<RoomPowerLevelsEvent>();
|
||||
if (!powerLevelEvent) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto &mId = userId.isEmpty() ? connection()->userId() : userId;
|
||||
if (const auto *plEvent = currentState().get<RoomPowerLevelsEvent>()) {
|
||||
return plEvent->powerLevelForUser(mId);
|
||||
}
|
||||
if (const auto *createEvent = creation()) {
|
||||
return createEvent->senderId() == mId ? 100 : 0;
|
||||
}
|
||||
return 0; // That's rather weird but may happen, according to rvdh
|
||||
return powerLevelEvent->powerLevelForUser(userId);
|
||||
}
|
||||
|
||||
QCoro::Task<void> NeoChatRoom::doDeleteMessagesByUser(const QString &user, QString reason)
|
||||
@@ -1338,6 +1286,7 @@ void NeoChatRoom::setPushNotificationState(PushNotificationState::State state)
|
||||
|
||||
m_currentPushNotificationState = state;
|
||||
Q_EMIT pushNotificationStateChanged(m_currentPushNotificationState);
|
||||
|
||||
}
|
||||
|
||||
void NeoChatRoom::updatePushNotificationState(QString type)
|
||||
@@ -1421,7 +1370,7 @@ void NeoChatRoom::openEventMediaExternally(const QString &eventId)
|
||||
if (evtIt != messageEvents().rend() && is<RoomMessageEvent>(**evtIt)) {
|
||||
const auto event = evtIt->viewAs<RoomMessageEvent>();
|
||||
if (event->hasFileContent()) {
|
||||
const auto transferInfo = cachedFileTransferInfo(event);
|
||||
const auto transferInfo = fileTransferInfo(eventId);
|
||||
if (transferInfo.completed()) {
|
||||
UrlHelper helper;
|
||||
helper.openUrl(transferInfo.localPath);
|
||||
@@ -1429,20 +1378,15 @@ void NeoChatRoom::openEventMediaExternally(const QString &eventId)
|
||||
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));
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1462,66 +1406,20 @@ void NeoChatRoom::copyEventMedia(const QString &eventId)
|
||||
downloadFile(eventId,
|
||||
QUrl(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + u'/'
|
||||
+ event->id().replace(u':', u'_').replace(u'/', u'_').replace(u'+', u'_') + fileNameToDownload(eventId)));
|
||||
connect(
|
||||
this,
|
||||
&Room::fileTransferCompleted,
|
||||
this,
|
||||
[this, eventId](QString id, QUrl localFile, FileSourceInfo fileMetadata) {
|
||||
Q_UNUSED(localFile);
|
||||
Q_UNUSED(fileMetadata);
|
||||
if (id == eventId) {
|
||||
auto transferInfo = fileTransferInfo(eventId);
|
||||
Clipboard clipboard;
|
||||
clipboard.setImage(transferInfo.localPath);
|
||||
}
|
||||
},
|
||||
static_cast<Qt::ConnectionType>(Qt::SingleShotConnection));
|
||||
connect(this, &Room::fileTransferCompleted, this, [this, eventId](QString id, QUrl localFile, FileSourceInfo fileMetadata) {
|
||||
Q_UNUSED(localFile);
|
||||
Q_UNUSED(fileMetadata);
|
||||
if (id == eventId) {
|
||||
auto transferInfo = fileTransferInfo(eventId);
|
||||
Clipboard clipboard;
|
||||
clipboard.setImage(transferInfo.localPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileTransferInfo NeoChatRoom::cachedFileTransferInfo(const Quotient::RoomEvent *event) const
|
||||
{
|
||||
QString mxcUrl;
|
||||
int total = 0;
|
||||
if (auto evt = eventCast<const Quotient::RoomMessageEvent>(event)) {
|
||||
if (evt->hasFileContent()) {
|
||||
mxcUrl = evt->content()->fileInfo()->url().toString();
|
||||
total = evt->content()->fileInfo()->payloadSize;
|
||||
}
|
||||
} else if (auto evt = eventCast<const Quotient::StickerEvent>(event)) {
|
||||
mxcUrl = evt->image().fileInfo()->url().toString();
|
||||
total = evt->image().fileInfo()->payloadSize;
|
||||
}
|
||||
|
||||
FileTransferInfo transferInfo = fileTransferInfo(event->id());
|
||||
if (transferInfo.active()) {
|
||||
return transferInfo;
|
||||
}
|
||||
|
||||
auto config = KSharedConfig::openStateConfig(QStringLiteral("neochatdownloads"))->group(QStringLiteral("downloads"));
|
||||
if (!config.hasKey(mxcUrl.mid(6))) {
|
||||
return transferInfo;
|
||||
}
|
||||
|
||||
const auto path = config.readPathEntry(mxcUrl.mid(6), QString());
|
||||
QFileInfo info(path);
|
||||
if (!info.isFile()) {
|
||||
config.deleteEntry(mxcUrl);
|
||||
return transferInfo;
|
||||
}
|
||||
// TODO: we could check the hash here
|
||||
return FileTransferInfo{
|
||||
.status = FileTransferInfo::Completed,
|
||||
.isUpload = false,
|
||||
.progress = total,
|
||||
.total = total,
|
||||
.localDir = QUrl(info.dir().path()),
|
||||
.localPath = QUrl::fromLocalFile(path),
|
||||
};
|
||||
}
|
||||
|
||||
ChatBarCache *NeoChatRoom::mainCache() const
|
||||
{
|
||||
return m_mainCache;
|
||||
@@ -1767,11 +1665,6 @@ const RoomEvent *NeoChatRoom::getEvent(const QString &eventId) const
|
||||
return timelineIt->get();
|
||||
}
|
||||
|
||||
const auto pendingIt = findPendingEvent(eventId);
|
||||
if (pendingIt != pendingEvents().end()) {
|
||||
return pendingIt->event();
|
||||
}
|
||||
|
||||
auto extraIt = std::find_if(m_extraEvents.begin(), m_extraEvents.end(), [eventId](const Quotient::event_ptr_tt<Quotient::RoomEvent> &event) {
|
||||
return event->id() == eventId;
|
||||
});
|
||||
|
||||
@@ -579,14 +579,6 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE QString invitingUserId() const;
|
||||
|
||||
/**
|
||||
* @brief Return the cached file transfer information for the event.
|
||||
*
|
||||
* If we downloaded the file previously, return a struct with Completed status
|
||||
* and the local file path stored in KSharedCOnfig
|
||||
*/
|
||||
Quotient::FileTransferInfo cachedFileTransferInfo(const Quotient::RoomEvent *event) const;
|
||||
|
||||
private:
|
||||
QSet<const Quotient::RoomEvent *> highlights;
|
||||
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "neochatroommember.h"
|
||||
|
||||
#include "neochatroom.h"
|
||||
|
||||
NeochatRoomMember::NeochatRoomMember(NeoChatRoom *room, const QString &memberId)
|
||||
: m_room(room)
|
||||
, m_memberId(memberId)
|
||||
{
|
||||
Q_ASSERT(!m_memberId.isEmpty());
|
||||
|
||||
if (m_room != nullptr) {
|
||||
connect(m_room, &NeoChatRoom::memberNameUpdated, this, [this](Quotient::RoomMember member) {
|
||||
if (member.id() == m_memberId) {
|
||||
Q_EMIT displayNameUpdated();
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::memberAvatarUpdated, this, [this](Quotient::RoomMember member) {
|
||||
if (member.id() == m_memberId) {
|
||||
Q_EMIT avatarUpdated();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::id() const
|
||||
{
|
||||
return m_memberId;
|
||||
}
|
||||
|
||||
Quotient::Uri NeochatRoomMember::uri() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).uri();
|
||||
}
|
||||
|
||||
bool NeochatRoomMember::isLocalMember() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).isLocalMember();
|
||||
}
|
||||
|
||||
Quotient::Membership NeochatRoomMember::membershipState() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return Quotient::Membership::Leave;
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).membershipState();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::name() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return id();
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).name();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::displayName() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return id();
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).displayName();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::htmlSafeDisplayName() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return id();
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).htmlSafeDisplayName();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::fullName() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return id();
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).fullName();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::htmlSafeFullName() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return id();
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).htmlSafeFullName();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::disambiguatedName() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return id();
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).disambiguatedName();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::htmlSafeDisambiguatedName() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return id();
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).htmlSafeDisambiguatedName();
|
||||
}
|
||||
|
||||
int NeochatRoomMember::hue() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).hue();
|
||||
}
|
||||
|
||||
qreal NeochatRoomMember::hueF() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).hueF();
|
||||
}
|
||||
|
||||
QColor NeochatRoomMember::color() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).color();
|
||||
}
|
||||
|
||||
QString NeochatRoomMember::avatarMediaId() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).avatarMediaId();
|
||||
}
|
||||
|
||||
QUrl NeochatRoomMember::avatarUrl() const
|
||||
{
|
||||
if (m_room == nullptr || m_memberId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return m_room->member(m_memberId).avatarUrl();
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2024 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 <QPointer>
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
#include <Quotient/roommember.h>
|
||||
#include <Quotient/uri.h>
|
||||
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
* @class NeochatRoomMember
|
||||
*
|
||||
* This class is a shim around RoomMember that can be safety passed to QML.
|
||||
*
|
||||
* Because RoomMember has an internal pointer to a RoomMemberEvent it is
|
||||
* designed to be created used then quickly discarded as the stste event is changed
|
||||
* every time the member updates. Passing these to QML which will hold onto them
|
||||
* can lead to accessing an already deleted Quotient::RoomMemberEvent relatively easily.
|
||||
*
|
||||
* This class instead holds a member ID and can therefore always safely create and
|
||||
* access Quotient::RoomMember objects while being used as long as needed by QML.
|
||||
*
|
||||
* @note This is only needed to pass to QML if only accessing in CPP RoomMmeber can
|
||||
* be used safely.
|
||||
*
|
||||
* @note The interface is the same as Quotient::RoomMember.
|
||||
*
|
||||
* @sa Quotient::RoomMember
|
||||
*/
|
||||
class NeochatRoomMember : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_UNCREATABLE("")
|
||||
|
||||
Q_PROPERTY(QString id READ id CONSTANT)
|
||||
Q_PROPERTY(Quotient::Uri uri READ uri CONSTANT)
|
||||
Q_PROPERTY(bool isLocalMember READ isLocalMember CONSTANT)
|
||||
Q_PROPERTY(QString displayName READ displayName NOTIFY displayNameUpdated)
|
||||
Q_PROPERTY(QString htmlSafeDisplayName READ htmlSafeDisplayName NOTIFY displayNameUpdated)
|
||||
Q_PROPERTY(QString fullName READ fullName NOTIFY displayNameUpdated)
|
||||
Q_PROPERTY(QString htmlSafeFullName READ htmlSafeFullName NOTIFY displayNameUpdated)
|
||||
Q_PROPERTY(QString disambiguatedName READ disambiguatedName NOTIFY displayNameUpdated)
|
||||
Q_PROPERTY(QString htmlSafeDisambiguatedName READ htmlSafeDisambiguatedName NOTIFY displayNameUpdated)
|
||||
Q_PROPERTY(int hue READ hue CONSTANT)
|
||||
Q_PROPERTY(qreal hueF READ hueF CONSTANT)
|
||||
Q_PROPERTY(QColor color READ color CONSTANT)
|
||||
Q_PROPERTY(QUrl avatarUrl READ avatarUrl NOTIFY avatarUpdated)
|
||||
|
||||
public:
|
||||
NeochatRoomMember() = default;
|
||||
explicit NeochatRoomMember(NeoChatRoom *room, const QString &memberId);
|
||||
|
||||
QString id() const;
|
||||
Quotient::Uri uri() const;
|
||||
bool isLocalMember() const;
|
||||
Quotient::Membership membershipState() const;
|
||||
QString name() const;
|
||||
QString displayName() const;
|
||||
QString htmlSafeDisplayName() const;
|
||||
QString fullName() const;
|
||||
QString htmlSafeFullName() const;
|
||||
QString disambiguatedName() const;
|
||||
QString htmlSafeDisambiguatedName() const;
|
||||
int hue() const;
|
||||
qreal hueF() const;
|
||||
QColor color() const;
|
||||
QString avatarMediaId() const;
|
||||
QUrl avatarUrl() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void displayNameUpdated();
|
||||
void avatarUpdated();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
const QString m_memberId = QString();
|
||||
};
|
||||
@@ -110,12 +110,6 @@ void NotificationsManager::processNotificationJob(QPointer<NeoChatConnection> co
|
||||
}
|
||||
auto sender = room->member(notification["event"_ls]["sender"_ls].toString());
|
||||
|
||||
// Don't display notifications for events in invited rooms
|
||||
// This should prevent empty notifications from appearing when they shouldn't
|
||||
if (room->joinState() == JoinState::Invite) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString body;
|
||||
if (notification["event"_ls]["type"_ls].toString() == "org.matrix.msc3381.poll.start"_ls) {
|
||||
body = notification["event"_ls]["content"_ls]["org.matrix.msc3381.poll.start"_ls]["question"_ls]["body"_ls].toString();
|
||||
@@ -249,6 +243,7 @@ void NotificationsManager::postInviteNotification(NeoChatRoom *rawRoom, const QS
|
||||
notification->setText(i18n("%1 invited you to a room", sender));
|
||||
notification->setTitle(title);
|
||||
notification->setPixmap(createNotificationImage(icon, nullptr));
|
||||
notification->setFlags(KNotification::Persistent);
|
||||
auto defaultAction = notification->addDefaultAction(i18n("Open this invitation in NeoChat"));
|
||||
connect(defaultAction, &KNotificationAction::activated, this, [notification, room]() {
|
||||
if (!room) {
|
||||
|
||||
@@ -17,7 +17,6 @@ Name[eu]=NeoChat
|
||||
Name[fi]=NeoChat
|
||||
Name[fr]=NeoChat
|
||||
Name[gl]=NeoChat
|
||||
Name[he]=NeoChat
|
||||
Name[hu]=NeoChat
|
||||
Name[ia]=Neochat
|
||||
Name[id]=NeoChat
|
||||
@@ -58,7 +57,6 @@ Comment[eu]=Bilatu gelak NeoChat-en
|
||||
Comment[fi]=Etsi huoneita NeoChatissä
|
||||
Comment[fr]=Trouver des salons dans NeoChat
|
||||
Comment[gl]=Atopa salas en NeoChat.
|
||||
Comment[he]=איתור חדרים ב־NeoChat
|
||||
Comment[hu]=Szobák keresése a NeoChatben
|
||||
Comment[ia]=Trova salas in NeoChat
|
||||
Comment[id]=Cari ruangan di NeoChat
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
"Description[nl]": "Delen via NeoChat",
|
||||
"Description[nn]": "Del via NeoChat",
|
||||
"Description[pl]": "Udostępnij przez NeoChat",
|
||||
"Description[pt_BR]": "Compartilhar via NeoChat",
|
||||
"Description[ru]": "Опубликовать в NeoChat",
|
||||
"Description[sl]": "Deli prek NeoChat",
|
||||
"Description[sv]": "Dela via NeoChat",
|
||||
|
||||
84
src/qml/AccountDialog.qml
Normal file
84
src/qml/AccountDialog.qml
Normal file
@@ -0,0 +1,84 @@
|
||||
// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
|
||||
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.components as KirigamiComponents
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
import org.kde.prison
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Kirigami.Dialog {
|
||||
id: root
|
||||
|
||||
property NeoChatConnection connection
|
||||
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
standardButtons: Kirigami.Dialog.NoButton
|
||||
|
||||
width: Math.min(QQC2.ApplicationWindow.window.width, Kirigami.Units.gridUnit * 24)
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
KirigamiComponents.Avatar {
|
||||
id: avatar
|
||||
|
||||
readonly property string mediaId: root.connection.localUser.avatarMediaId
|
||||
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.huge * 2
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.huge * 2
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
name: root.room ? root.room.member(root.user.id).displayName : root.user.displayName
|
||||
source: mediaId ? root.connection.makeMediaUrl("mxc://" + mediaId) : ""
|
||||
color: root.room ? root.room.member(root.user.id).color : QmlUtils.getUserColor(root.user.hueF)
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
font.bold: false
|
||||
font.weight: Font.DemiBold
|
||||
font.pixelSize: 32
|
||||
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.NoWrap
|
||||
text: root.connection.localUser.displayName
|
||||
textFormat: Text.PlainText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Kirigami.SelectableLabel {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||
font.pixelSize: 16
|
||||
textFormat: TextEdit.PlainText
|
||||
text: root.connection.localUser.id
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
FormCard.FormDelegateSeparator {}
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18n("Edit Account")
|
||||
}
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18n("Switch Account")
|
||||
}
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18n("Logout")
|
||||
}
|
||||
|
||||
}
|
||||
Component {
|
||||
id: qrMaximizeComponent
|
||||
QrCodeMaximizeComponent {}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ Components.AbstractMaximizeComponent {
|
||||
/**
|
||||
* @brief The message author.
|
||||
*/
|
||||
property NeochatRoomMember author
|
||||
property RoomMember author
|
||||
|
||||
/**
|
||||
* @brief The timestamp of the message.
|
||||
|
||||
@@ -15,10 +15,7 @@ Kirigami.PlaceholderMessage {
|
||||
required property NeoChatRoom currentRoom
|
||||
|
||||
text: i18n("Accept this invitation?")
|
||||
explanation: root.currentRoom.connection.canCheckMutualRooms ? i18n("You can reject invitations from unknown users under Security settings.") : ""
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
QQC2.Button {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: i18nc("@action:button The thing being rejected is an invitation to chat", "Reject and ignore user")
|
||||
|
||||
@@ -192,7 +192,20 @@ QQC2.ScrollView {
|
||||
}
|
||||
}
|
||||
|
||||
model: root.room.isDirectChat() ? 0 : RoomManager.userListModel
|
||||
KSortFilterProxyModel {
|
||||
id: sortedMessageEventModel
|
||||
|
||||
sourceModel: UserListModel {
|
||||
room: root.room
|
||||
}
|
||||
|
||||
sortRoleName: "powerLevel"
|
||||
sortOrder: Qt.DescendingOrder
|
||||
filterRoleName: "name"
|
||||
filterCaseSensitivity: Qt.CaseInsensitive
|
||||
}
|
||||
|
||||
model: root.room.isDirectChat() ? 0 : sortedMessageEventModel
|
||||
|
||||
clip: true
|
||||
focus: true
|
||||
|
||||
@@ -227,11 +227,6 @@ Kirigami.Page {
|
||||
}
|
||||
}
|
||||
|
||||
footer: Loader {
|
||||
width: parent.width
|
||||
sourceComponent: Kirigami.Settings.isMobile ? exploreComponentMobile : userInfoDesktop
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
@@ -47,6 +47,22 @@ QQC2.Control {
|
||||
width: scrollView.width
|
||||
spacing: 0
|
||||
|
||||
AvatarTabButton {
|
||||
readonly property string mediaId: root.connection.localUser.avatarMediaId
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: width - Kirigami.Units.smallSpacing
|
||||
Layout.maximumHeight: width - Kirigami.Units.smallSpacing
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing / 2
|
||||
Layout.bottomMargin: Kirigami.Units.smallSpacing / 2
|
||||
|
||||
text: root.connection.localUser.displayName
|
||||
source: mediaId ? root.connection.makeMediaUrl("mxc://" + mediaId) : ""
|
||||
onClicked: Qt.createComponent("org.kde.neochat", "AccountDialog").createObject(root, {
|
||||
connection: root.connection
|
||||
}).open()
|
||||
}
|
||||
|
||||
AvatarTabButton {
|
||||
id: notificationsButton
|
||||
|
||||
|
||||
@@ -108,7 +108,6 @@ QQC2.ScrollView {
|
||||
onTriggered: {
|
||||
root.roomChanging = false;
|
||||
markReadIfVisibleTimer.reset();
|
||||
RoomManager.activateUserModel();
|
||||
}
|
||||
}
|
||||
onAtYEndChanged: if (!root.roomChanging) {
|
||||
@@ -191,7 +190,7 @@ QQC2.ScrollView {
|
||||
implicitHeight: Kirigami.Units.gridUnit * 2
|
||||
|
||||
z: 2
|
||||
visible: (root.currentRoom?.hasUnreadMessages ?? false)
|
||||
visible: root.currentRoom && root.currentRoom.hasUnreadMessages
|
||||
|
||||
text: root.currentRoom.readMarkerLoaded ? i18n("Jump to first unread message") : i18n("Jump to oldest loaded message")
|
||||
action: Kirigami.Action {
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "neochatroommember.h"
|
||||
#include "spacehierarchycache.h"
|
||||
#include "urlhelper.h"
|
||||
|
||||
@@ -40,7 +39,6 @@ RoomManager::RoomManager(QObject *parent)
|
||||
, m_timelineModel(new TimelineModel(this))
|
||||
, m_messageFilterModel(new MessageFilterModel(this, m_timelineModel))
|
||||
, m_mediaMessageFilterModel(new MediaMessageFilterModel(this, m_messageFilterModel))
|
||||
, m_userListModel(new UserListModel(this))
|
||||
{
|
||||
m_lastRoomConfig = m_config->group(QStringLiteral("LastOpenRoom"));
|
||||
m_lastSpaceConfig = m_config->group(QStringLiteral("LastOpenSpace"));
|
||||
@@ -48,7 +46,6 @@ RoomManager::RoomManager(QObject *parent)
|
||||
|
||||
connect(this, &RoomManager::currentRoomChanged, this, [this]() {
|
||||
m_timelineModel->setRoom(m_currentRoom);
|
||||
m_userListModel->setRoom(m_currentRoom);
|
||||
});
|
||||
|
||||
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, [this](NeoChatConnection *connection) {
|
||||
@@ -116,16 +113,6 @@ MediaMessageFilterModel *RoomManager::mediaMessageFilterModel() const
|
||||
return m_mediaMessageFilterModel;
|
||||
}
|
||||
|
||||
UserListModel *RoomManager::userListModel() const
|
||||
{
|
||||
return m_userListModel;
|
||||
}
|
||||
|
||||
void RoomManager::activateUserModel()
|
||||
{
|
||||
m_userListModel->activate();
|
||||
}
|
||||
|
||||
UriResolveResult RoomManager::resolveResource(const Uri &uri)
|
||||
{
|
||||
return UriResolverBase::visitResource(m_connection, uri);
|
||||
@@ -185,7 +172,7 @@ void RoomManager::maximizeMedia(int index)
|
||||
Q_EMIT showMaximizedMedia(index);
|
||||
}
|
||||
|
||||
void RoomManager::maximizeCode(NeochatRoomMember *author, const QDateTime &time, const QString &codeText, const QString &language)
|
||||
void RoomManager::maximizeCode(const RoomMember &author, const QDateTime &time, const QString &codeText, const QString &language)
|
||||
{
|
||||
if (codeText.isEmpty()) {
|
||||
return;
|
||||
@@ -203,14 +190,14 @@ void RoomManager::viewEventSource(const QString &eventId)
|
||||
Q_EMIT showEventSource(eventId);
|
||||
}
|
||||
|
||||
void RoomManager::viewEventMenu(const QString &eventId, NeoChatRoom *room, NeochatRoomMember *sender, const QString &selectedText)
|
||||
void RoomManager::viewEventMenu(const QString &eventId, NeoChatRoom *room, const QString &selectedText)
|
||||
{
|
||||
const auto &event = **room->findInTimeline(eventId);
|
||||
const auto eventHandler = EventHandler(room, &event);
|
||||
|
||||
if (eventHandler.getMediaInfo().contains("mimeType"_ls)) {
|
||||
Q_EMIT showFileMenu(eventId,
|
||||
sender,
|
||||
eventHandler.getAuthor(),
|
||||
eventHandler.messageComponentType(),
|
||||
eventHandler.getPlainBody(),
|
||||
eventHandler.getMediaInfo()["mimeType"_ls].toString(),
|
||||
@@ -218,7 +205,12 @@ void RoomManager::viewEventMenu(const QString &eventId, NeoChatRoom *room, Neoch
|
||||
return;
|
||||
}
|
||||
|
||||
Q_EMIT showMessageMenu(eventId, sender, eventHandler.messageComponentType(), eventHandler.getPlainBody(), eventHandler.getRichBody(), selectedText);
|
||||
Q_EMIT showMessageMenu(eventId,
|
||||
eventHandler.getAuthor(),
|
||||
eventHandler.messageComponentType(),
|
||||
eventHandler.getPlainBody(),
|
||||
eventHandler.getRichBody(),
|
||||
selectedText);
|
||||
}
|
||||
|
||||
bool RoomManager::hasOpenRoom() const
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
#include "models/sortfilterroomtreemodel.h"
|
||||
#include "models/sortfilterspacelistmodel.h"
|
||||
#include "models/timelinemodel.h"
|
||||
#include "models/userlistmodel.h"
|
||||
#include "neochatroommember.h"
|
||||
|
||||
class NeoChatRoom;
|
||||
class NeoChatConnection;
|
||||
@@ -122,14 +120,6 @@ class RoomManager : public QObject, public UriResolverBase
|
||||
*/
|
||||
Q_PROPERTY(MediaMessageFilterModel *mediaMessageFilterModel READ mediaMessageFilterModel CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief The UserListModel that should be used for room member visualisation.
|
||||
*
|
||||
* @note Available here so that the room page and drawer both have access to the
|
||||
* same model.
|
||||
*/
|
||||
Q_PROPERTY(UserListModel *userListModel READ userListModel CONSTANT)
|
||||
|
||||
/**
|
||||
* @brief Whether a room is currently open in NeoChat.
|
||||
*
|
||||
@@ -165,9 +155,6 @@ public:
|
||||
MessageFilterModel *messageFilterModel() const;
|
||||
MediaMessageFilterModel *mediaMessageFilterModel() const;
|
||||
|
||||
UserListModel *userListModel() const;
|
||||
Q_INVOKABLE void activateUserModel();
|
||||
|
||||
/**
|
||||
* @brief Resolve the given URI resource.
|
||||
*
|
||||
@@ -217,7 +204,7 @@ public:
|
||||
*/
|
||||
Q_INVOKABLE void maximizeMedia(int index);
|
||||
|
||||
Q_INVOKABLE void maximizeCode(NeochatRoomMember *author, const QDateTime &time, const QString &codeText, const QString &language);
|
||||
Q_INVOKABLE void maximizeCode(const RoomMember &author, const QDateTime &time, const QString &codeText, const QString &language);
|
||||
|
||||
/**
|
||||
* @brief Request that any full screen overlay currently open closes.
|
||||
@@ -232,7 +219,7 @@ public:
|
||||
/**
|
||||
* @brief Show a context menu for the given event.
|
||||
*/
|
||||
Q_INVOKABLE void viewEventMenu(const QString &eventId, NeoChatRoom *room, NeochatRoomMember *sender, const QString &selectedText = {});
|
||||
Q_INVOKABLE void viewEventMenu(const QString &eventId, NeoChatRoom *room, const QString &selectedText = {});
|
||||
|
||||
ChatDocumentHandler *chatDocumentHandler() const;
|
||||
void setChatDocumentHandler(ChatDocumentHandler *handler);
|
||||
@@ -288,7 +275,7 @@ Q_SIGNALS:
|
||||
/**
|
||||
* @brief Request a block of code is shown maximized.
|
||||
*/
|
||||
void showMaximizedCode(NeochatRoomMember *author, const QDateTime &time, const QString &codeText, const QString &language);
|
||||
void showMaximizedCode(const RoomMember &author, const QDateTime &time, const QString &codeText, const QString &language);
|
||||
|
||||
/**
|
||||
* @brief Request that any full screen overlay closes.
|
||||
@@ -304,7 +291,7 @@ Q_SIGNALS:
|
||||
* @brief Request to show a menu for the given event.
|
||||
*/
|
||||
void showMessageMenu(const QString &eventId,
|
||||
const NeochatRoomMember *author,
|
||||
const Quotient::RoomMember &author,
|
||||
MessageComponentType::Type messageComponentType,
|
||||
const QString &plainText,
|
||||
const QString &htmlText,
|
||||
@@ -314,7 +301,7 @@ Q_SIGNALS:
|
||||
* @brief Request to show a menu for the given media event.
|
||||
*/
|
||||
void showFileMenu(const QString &eventId,
|
||||
const NeochatRoomMember *author,
|
||||
const Quotient::RoomMember &author,
|
||||
MessageComponentType::Type messageComponentType,
|
||||
const QString &plainText,
|
||||
const QString &mimeType,
|
||||
@@ -372,9 +359,6 @@ private:
|
||||
TimelineModel *m_timelineModel;
|
||||
MessageFilterModel *m_messageFilterModel;
|
||||
MediaMessageFilterModel *m_mediaMessageFilterModel;
|
||||
|
||||
UserListModel *m_userListModel;
|
||||
|
||||
QPointer<NeoChatConnection> m_connection;
|
||||
|
||||
void setCurrentRoom(const QString &roomId);
|
||||
|
||||
@@ -41,7 +41,7 @@ FormCard.FormCard {
|
||||
width: stickerFlow.width / 4
|
||||
height: width
|
||||
|
||||
onClicked: root.QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(emoticonEditorPage, {
|
||||
onClicked: pageStack.pushDialogLayer(emoticonEditorPage, {
|
||||
description: model.body ?? "",
|
||||
index: model.index,
|
||||
url: model.url,
|
||||
@@ -90,7 +90,7 @@ FormCard.FormCard {
|
||||
width: stickerFlow.width / 4
|
||||
height: width
|
||||
|
||||
onClicked: root.QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(emoticonEditorPage, {
|
||||
onClicked: pageStack.pushDialogLayer(emoticonEditorPage, {
|
||||
description: "",
|
||||
index: -1,
|
||||
url: "",
|
||||
|
||||
@@ -50,7 +50,7 @@ FormCard.FormCardPage {
|
||||
QQC2.ToolButton {
|
||||
text: i18nc("@action:button", "Unignore this user")
|
||||
icon.name: "list-remove-symbolic"
|
||||
onClicked: root.connection.removeFromIgnoredUsers(modelData)
|
||||
onClicked: root.connection.removeFromIgnoredUsers(root.connection.user(modelData))
|
||||
display: QQC2.Button.IconOnly
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
|
||||
@@ -16,32 +16,6 @@ FormCard.FormCardPage {
|
||||
|
||||
title: i18nc("@title", "Security")
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title:group", "Invitations")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormCheckDelegate {
|
||||
text: i18nc("@option:check", "Reject invitations from unknown users")
|
||||
description: connection.canCheckMutualRooms ? i18n("If enabled, NeoChat will reject invitations from from users you don't share a room with.") : i18n("Your server does not support this setting.")
|
||||
checked: Config.rejectUnknownInvites
|
||||
enabled: !Config.isRejectUnknownInvitesImmutable && connection.canCheckMutualRooms
|
||||
onToggled: {
|
||||
Config.rejectUnknownInvites = checked;
|
||||
Config.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title:group", "Ignored Users")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18nc("@action:button", "Manage ignored users")
|
||||
onClicked: root.ApplicationWindow.window.pageStack.push(ignoredUsersDialogComponent, {}, {
|
||||
title: i18nc("@title:window", "Ignored Users")
|
||||
});
|
||||
}
|
||||
}
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title", "Keys")
|
||||
}
|
||||
@@ -59,6 +33,17 @@ FormCard.FormCardPage {
|
||||
description: i18n("Device id")
|
||||
}
|
||||
}
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title:group", "Ignored Users")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
FormCard.FormButtonDelegate {
|
||||
text: i18nc("@action:button", "Manage ignored users")
|
||||
onClicked: pageStack.pushDialogLayer(ignoredUsersDialogComponent, {}, {
|
||||
title: i18nc("@title:window", "Ignored Users")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: ignoredUsersDialogComponent
|
||||
|
||||
@@ -20,6 +20,11 @@ FormCard.FormCardPage {
|
||||
|
||||
title: i18nc('@title:window', 'Permissions')
|
||||
|
||||
property UserListModel userListModel: UserListModel {
|
||||
id: userListModel
|
||||
room: root.room
|
||||
}
|
||||
|
||||
readonly property PowerLevelModel powerLevelModel: PowerLevelModel {
|
||||
showMute: false
|
||||
}
|
||||
@@ -34,7 +39,7 @@ FormCard.FormCardPage {
|
||||
FormCard.FormCard {
|
||||
Repeater {
|
||||
model: KSortFilterProxyModel {
|
||||
sourceModel: RoomManager.userListModel
|
||||
sourceModel: userListModel
|
||||
sortRoleName: "powerLevel"
|
||||
sortOrder: Qt.DescendingOrder
|
||||
filterRowCallback: function (source_row, source_parent) {
|
||||
@@ -153,7 +158,7 @@ FormCard.FormCardPage {
|
||||
|
||||
model: UserFilterModel {
|
||||
id: userListFilterModel
|
||||
sourceModel: RoomManager.userListModel
|
||||
sourceModel: userListModel
|
||||
filterText: userListSearchField.text
|
||||
|
||||
onFilterTextChanged: {
|
||||
|
||||
@@ -28,6 +28,11 @@ ColumnLayout {
|
||||
*/
|
||||
required property string eventId
|
||||
|
||||
/**
|
||||
* @brief The display text of the message.
|
||||
*/
|
||||
required property string display
|
||||
|
||||
/**
|
||||
* @brief The media info for the event.
|
||||
*
|
||||
@@ -39,7 +44,6 @@ ColumnLayout {
|
||||
* - width - The width in pixels of the audio media.
|
||||
* - height - The height in pixels of the audio media.
|
||||
* - tempInfo - mediaInfo (with the same properties as this except no tempInfo) for a temporary image while the file downloads.
|
||||
* - filename - original filename of the media
|
||||
*/
|
||||
required property var mediaInfo
|
||||
|
||||
@@ -134,7 +138,7 @@ ColumnLayout {
|
||||
id: playButton
|
||||
}
|
||||
QQC2.Label {
|
||||
text: root.mediaInfo.filename
|
||||
text: root.display
|
||||
wrapMode: Text.Wrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
@@ -155,7 +159,7 @@ ColumnLayout {
|
||||
from: 0
|
||||
to: audio.duration
|
||||
value: audio.position
|
||||
onMoved: audio.setPosition(value)
|
||||
onMoved: audio.seek(value)
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
|
||||
@@ -60,7 +60,7 @@ RowLayout {
|
||||
horizontalAlignment: Text.AlignRight
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
QQC2.ToolTip.visible: timeHoverHandler.hovered
|
||||
QQC2.ToolTip.text: root.time.toLocaleString(Qt.locale(), Locale.ShortFormat)
|
||||
QQC2.ToolTip.text: root.time.toLocaleString(Qt.locale(), Locale.LongFormat)
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
|
||||
HoverHandler {
|
||||
|
||||
@@ -13,21 +13,20 @@ Flow {
|
||||
property var avatarSize: Kirigami.Units.iconSizes.small
|
||||
property alias model: avatarFlowRepeater.model
|
||||
property string toolTipText
|
||||
property alias excessAvatars: excessAvatarsLabel.text
|
||||
|
||||
spacing: -avatarSize / 2
|
||||
Repeater {
|
||||
id: avatarFlowRepeater
|
||||
delegate: KirigamiComponents.Avatar {
|
||||
required property string displayName
|
||||
required property url avatarUrl
|
||||
required property color memberColor
|
||||
required property var modelData
|
||||
|
||||
implicitWidth: root.avatarSize
|
||||
implicitHeight: root.avatarSize
|
||||
|
||||
name: displayName
|
||||
source: avatarUrl
|
||||
color: memberColor
|
||||
name: modelData.displayName
|
||||
source: modelData.avatarUrl
|
||||
color: modelData.color
|
||||
}
|
||||
}
|
||||
QQC2.Label {
|
||||
@@ -35,9 +34,6 @@ Flow {
|
||||
visible: text !== ""
|
||||
color: Kirigami.Theme.textColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
text: root.model?.excessReadMarkersString ?? ""
|
||||
|
||||
background: Kirigami.ShadowedRectangle {
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
Kirigami.Theme.inherit: false
|
||||
@@ -58,7 +54,7 @@ Flow {
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ToolTip.text: root.model?.readMarkersString ?? ""
|
||||
QQC2.ToolTip.text: toolTipText
|
||||
QQC2.ToolTip.visible: hoverHandler.hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ QQC2.Control {
|
||||
*
|
||||
* @sa Quotient::RoomMember
|
||||
*/
|
||||
required property NeochatRoomMember author
|
||||
required property RoomMember author
|
||||
|
||||
/**
|
||||
* @brief The timestamp of the message.
|
||||
|
||||
@@ -29,6 +29,11 @@ ColumnLayout {
|
||||
*/
|
||||
required property string eventId
|
||||
|
||||
/**
|
||||
* @brief The display text of the message.
|
||||
*/
|
||||
required property string display
|
||||
|
||||
/**
|
||||
* @brief The media info for the event.
|
||||
*
|
||||
@@ -40,7 +45,6 @@ ColumnLayout {
|
||||
* - width - The width in pixels of the audio media.
|
||||
* - height - The height in pixels of the audio media.
|
||||
* - tempInfo - mediaInfo (with the same properties as this except no tempInfo) for a temporary image while the file downloads.
|
||||
* - filename - original filename of the media
|
||||
*/
|
||||
required property var mediaInfo
|
||||
|
||||
@@ -151,7 +155,7 @@ ColumnLayout {
|
||||
spacing: 0
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: root.mediaInfo.filename
|
||||
text: root.display
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ TimelineDelegate {
|
||||
/**
|
||||
* @brief The message author.
|
||||
*/
|
||||
required property NeochatRoomMember author
|
||||
required property RoomMember author
|
||||
|
||||
width: parent?.width
|
||||
rightPadding: Config.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing
|
||||
@@ -88,7 +88,7 @@ TimelineDelegate {
|
||||
QtObject {
|
||||
id: _private
|
||||
function showMessageMenu() {
|
||||
RoomManager.viewEventMenu(root.eventId, root.room, root.author, "");
|
||||
RoomManager.viewEventMenu(root.eventId, root.room, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ TimelineDelegate {
|
||||
*
|
||||
* @sa Quotient::RoomMember
|
||||
*/
|
||||
required property NeochatRoomMember author
|
||||
required property var author
|
||||
|
||||
/**
|
||||
* @brief The model to visualise the content of the message.
|
||||
@@ -82,6 +82,16 @@ TimelineDelegate {
|
||||
*/
|
||||
required property var readMarkers
|
||||
|
||||
/**
|
||||
* @brief String with the display name and matrix ID of the other user read markers.
|
||||
*/
|
||||
required property string readMarkersString
|
||||
|
||||
/**
|
||||
* @brief The number of other users at the event after the first 5.
|
||||
*/
|
||||
required property var excessReadMarkers
|
||||
|
||||
/**
|
||||
* @brief Whether the other user read marker component should be shown.
|
||||
*/
|
||||
@@ -235,7 +245,7 @@ TimelineDelegate {
|
||||
topMargin: Kirigami.Units.smallSpacing
|
||||
}
|
||||
|
||||
visible: (root.contentModel?.showAuthor ?? false) && Config.showAvatarInTimeline && (Config.compactLayout || !_private.showUserMessageOnRight)
|
||||
visible: root.contentModel?.showAuthor && Config.showAvatarInTimeline && (Config.compactLayout || !_private.showUserMessageOnRight)
|
||||
name: root.author.displayName
|
||||
source: root.author.avatarUrl
|
||||
color: root.author.color
|
||||
@@ -332,6 +342,8 @@ TimelineDelegate {
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
visible: root.showReadMarkers
|
||||
model: root.readMarkers
|
||||
toolTipText: root.readMarkersString
|
||||
excessAvatars: root.excessReadMarkers
|
||||
}
|
||||
|
||||
DelegateSizeHelper {
|
||||
@@ -365,7 +377,7 @@ TimelineDelegate {
|
||||
property bool showUserMessageOnRight: Config.showLocalMessagesOnRight && root.author.isLocalMember && !Config.compactLayout && !root.alwaysFillWidth
|
||||
|
||||
function showMessageMenu() {
|
||||
RoomManager.viewEventMenu(root.eventId, root.room, root.author, root.selectedText);
|
||||
RoomManager.viewEventMenu(root.eventId, root.room, root.selectedText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.coreaddons
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
/**
|
||||
@@ -14,14 +13,13 @@ import org.kde.kirigami as Kirigami
|
||||
RowLayout {
|
||||
property alias mimeIconSource: icon.source
|
||||
property alias label: nameLabel.text
|
||||
property string subLabel: ""
|
||||
property int size: 0
|
||||
property int duration: 0
|
||||
property alias subLabel: subLabel.text
|
||||
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
Kirigami.Icon {
|
||||
id: icon
|
||||
|
||||
fallback: "unknown"
|
||||
}
|
||||
ColumnLayout {
|
||||
@@ -34,21 +32,15 @@ RowLayout {
|
||||
id: nameLabel
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: caption.visible ? Qt.AlignLeft | Qt.AlignBottom : Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.alignment: subLabel.visible ? Qt.AlignLeft | Qt.AlignBottom : Qt.AlignLeft | Qt.AlignVCenter
|
||||
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
QQC2.Label {
|
||||
id: caption
|
||||
id: subLabel
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: (subLabel || size || duration || '') && [
|
||||
subLabel,
|
||||
size && Format.formatByteSize(size),
|
||||
duration > 0 && Format.formatDuration(duration),
|
||||
].filter(Boolean).join(" | ")
|
||||
|
||||
elide: Text.ElideRight
|
||||
visible: text.length > 0
|
||||
opacity: 0.7
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user