Compare commits

...

100 Commits

Author SHA1 Message Date
Tobias Fella
cd70e2b47f Annotate functions in connections and port away from implicit onFoo 2025-08-08 11:16:27 +02:00
Tobias Fella
279b611754 Remove unused prison 2025-08-08 10:42:38 +02:00
Tobias Fella
f7c74a60cd Port away from applicationWindow() 2025-08-08 10:29:48 +02:00
l10n daemon script
3465fc7d39 GIT_SILENT Sync po/docbooks with svn 2025-08-07 19:27:58 +00:00
l10n daemon script
6381f06acb GIT_SILENT made messages (after extraction) 2025-08-07 18:30:01 +00:00
l10n daemon script
a7c7a5c72d GIT_SILENT Sync po/docbooks with svn 2025-08-07 01:42:12 +00:00
l10n daemon script
c8ded65e46 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2025-08-07 01:33:59 +00:00
l10n daemon script
729b46fc71 GIT_SILENT made messages (after extraction) 2025-08-07 00:42:54 +00:00
Tobias Fella
e63e04aa57 Fix QML warning 2025-08-07 00:21:10 +02:00
Tobias Fella
ae88879651 Remove unused kitemmodels imports 2025-08-07 00:19:18 +02:00
Tobias Fella
dfd106258b Fix some warnings around pagestack calls 2025-08-07 00:05:52 +02:00
Tobias Fella
4d29b9fd57 Fix qml warning 2025-08-06 23:53:04 +02:00
Tobias Fella
3de7ad237a Fix qml warning 2025-08-06 23:51:56 +02:00
Tobias Fella
7d4e589894 Remove some unused includes 2025-08-06 23:49:27 +02:00
Tobias Fella
4bbd127fe8 Fix some unqualified access 2025-08-06 23:47:28 +02:00
Tobias Fella
f2a0a66b01 Fix connection 2025-08-06 23:47:28 +02:00
James Graham
143c685045 Fix creating a new thread locally
Fix creating a new thread locally. We need to listen for the newThread signal and update the thread root event mode content
2025-08-06 18:10:03 +01:00
Tobias Fella
b8fa6f0690 Fix font sizes 2025-08-06 15:34:33 +02:00
Joshua Goins
93b6c53c82 Remind the user if they're trying to reply to someone who has left
This happens semi-frequently to me and others - we reply to a user who
has left the room. Sometimes this is useful (for example, bringing up a
previous topic) but in most cases this is accidental, and almost
guaranteed to happen if you turned off join/leave events.

So I added a reminder when replying - which manifests as a small label
in the chatbar - that the user has left and can't be notified of your
reply.
2025-08-06 08:13:49 -04:00
l10n daemon script
6822a1ef08 GIT_SILENT Sync po/docbooks with svn 2025-08-06 01:47:29 +00:00
Justin Zobel
12b7c25395 Flatpak: Update kunifiedpush project ID and URL as it is now in KDE Gear and update the version 2025-08-05 09:30:29 +00:00
Justin Zobel
42c0060122 Flatpak: Upgrade libsecret 2025-08-05 09:30:29 +00:00
Justin Zobel
b30ee55a81 Flatpak: Add cleanup 2025-08-05 09:30:29 +00:00
Tobias Fella
9d2ef838bb Don't crash when trying to open a context menu on a pending event 2025-08-05 09:17:13 +00:00
Tobias Fella
b5351e48dd Add tooltip to "remove server" button 2025-08-05 09:16:27 +00:00
l10n daemon script
7b437d91e1 GIT_SILENT Sync po/docbooks with svn 2025-08-05 01:54:03 +00:00
Nicolas Fella
a9d39353ab Add XDG activation support to rooms runner 2025-08-04 21:49:05 +02:00
Tobias Fella
5f778dbd81 Add some translation contexts 2025-08-04 21:27:28 +02:00
Nate Graham
b01286eae3 plasma-runner-neochat: remove unnecessary minimum letter count
Users generally expect search results to start appearing with the first
character typed; we shouldn't be setting minimum letter counts without
a very good reason. Excessive minimum letter counts are also bad
specifically for Chinese, which is highly information-dense.

Get rid of the minimum letter count.

CCBUG: 490972
2025-08-04 09:18:18 -06:00
Tobias Fella
4688802628 Cleanup CommonRoomsModel
- Remove unused include
- Remove roles that don't make sense
2025-08-04 17:14:32 +02:00
l10n daemon script
0282f2c7aa GIT_SILENT Sync po/docbooks with svn 2025-08-04 01:47:41 +00:00
Tobias Fella
53d0fd1663 Improve spacing in mobile UserInfo 2025-08-03 17:26:39 +02:00
l10n daemon script
24bdb7d651 GIT_SILENT Sync po/docbooks with svn 2025-08-03 01:41:12 +00:00
James Graham
dc32f2f947 Update ChatDocumentHandler so RoomManger is no longer required for saving text
Update `ChatDocumentHandler` so `RoomManger` is no longer required for saving the text in the chatbar between room switches. This is achieved by allowing `ChatDocumentHandler` to get the correct `ChatBarChache` itself rather than having to have it passed from `ChatBar.qml`. This avoids any race conditions.
2025-08-02 10:56:20 +01:00
l10n daemon script
401cf29ca8 GIT_SILENT Sync po/docbooks with svn 2025-08-02 01:39:43 +00:00
Nate Graham
f16dea85ed flatpak: add org.kde.kuiserver talk permission
This lets it use the notification infrastructure, e.g. when uploading or
downloading files.
2025-08-01 18:57:28 -06:00
James Graham
b4e1740cad Separate out a base MessageContentModel.
Separate out a base `MessageContentModel` that can be extended to get the component types from different places. This is used currently for `EventMessageContentModel` but will be used later as part of the rich chat bar.

All display text is now in the text component so it never needs special casing. This also cleans up some of the model parameters so more things come from attributes including location and file data (which was already a qvariantmap anyway).

Also cleaned up the itinerary and file enhancement views,
2025-08-01 12:15:51 +01:00
Tobias Fella
501f14fead Fix account switching on logout 2025-08-01 12:27:35 +02:00
Tobias Fella
d14466451d Fix loading 2025-08-01 12:11:35 +02:00
Tobias Fella
f7cd4bd2fb Don't set initial properties that don't exist 2025-08-01 11:43:09 +02:00
Tobias Fella
7742c6d4b0 Fix common crash during login 2025-08-01 09:26:31 +00:00
l10n daemon script
4f6dd50320 GIT_SILENT Sync po/docbooks with svn 2025-08-01 01:44:41 +00:00
Heiko Becker
92f77860dd GIT_SILENT Update Appstream for new release
(cherry picked from commit 699026fc2f)
2025-08-01 00:54:12 +02:00
l10n daemon script
03035b735d GIT_SILENT Sync po/docbooks with svn 2025-07-31 01:38:18 +00:00
Wang Yu
4e0b295f66 Replace duplicate beginResetModel with endResetModel
The initialize method was calling beginResetModel twice without
a corresponding endResetModel call. This could cause model state
inconsistencies.
2025-07-30 16:31:56 +08:00
l10n daemon script
8cbd3f5e0f GIT_SILENT Sync po/docbooks with svn 2025-07-30 01:40:03 +00:00
Joshua Goins
a0b3e484f5 Send thumbnails when uploading videos
NeoChat will now generate thumbnails from the first frame of the video,
since we are loading it anyway. This makes the experience of exchanging
videos in NeoChat much nicer!
2025-07-29 16:41:33 -04:00
Tobias Fella
8ff83ca6df Cleanup QML code for AccountMenu 2025-07-29 20:16:14 +00:00
Joshua Goins
5fe28cb183 Suggest verifying your own device in Devices settings
This button is currently hidden under the user menu, but it should be
shown more prominently - especially under the Devices settings. The
button under the user menu and this new one is now hidden when your
device is already verified.
2025-07-29 15:02:30 -04:00
l10n daemon script
17af4dfddb GIT_SILENT Sync po/docbooks with svn 2025-07-29 01:40:25 +00:00
Tobias Fella
43c6349359 Add translation context 2025-07-28 22:37:28 +02:00
Tobias Fella
265494ee44 Remove unused import 2025-07-28 22:37:05 +02:00
Tobias Fella
a9e4996191 Remove broken code 2025-07-28 22:35:30 +02:00
Tobias Fella
6e3276826d Fix positioning of mobile accounts popup 2025-07-28 22:35:15 +02:00
Tobias Fella
bfe976c438 Remove some unused includes 2025-07-28 21:50:41 +02:00
James Graham
f288367653 Add a dev setting to allow relations to be sent to any message
Add a dev setting to allow relations to be sent to any message using the right click delegate menu. This shouldn't be a main feature, but since it's technically allowed in matrix this will help any future debugging
2025-07-28 19:47:29 +01:00
Tobias Fella
6082bc89b0 Remove "NeoChat" menu item in global menu
BUG: 504429
2025-07-28 15:24:51 +00:00
l10n daemon script
4d3791250b GIT_SILENT Sync po/docbooks with svn 2025-07-28 01:37:39 +00:00
l10n daemon script
04472dae4f GIT_SILENT Sync po/docbooks with svn 2025-07-27 01:40:53 +00:00
Tobias Fella
aa40fc84ea Adapt to power level changes in room version 12 2025-07-26 22:19:07 +00:00
Tobias Fella
24e43d063a Fix test 2025-07-27 00:18:37 +02:00
l10n daemon script
c5caffcdf9 GIT_SILENT Sync po/docbooks with svn 2025-07-25 01:40:12 +00:00
l10n daemon script
95d334ad86 GIT_SILENT Sync po/docbooks with svn 2025-07-23 01:40:50 +00:00
Tobias Fella
602ac5c55f Fix opening EditStateDialog 2025-07-22 21:02:51 +02:00
l10n daemon script
247423bf83 GIT_SILENT Sync po/docbooks with svn 2025-07-22 01:42:47 +00:00
l10n daemon script
24d35b3eae GIT_SILENT Sync po/docbooks with svn 2025-07-21 01:41:16 +00:00
l10n daemon script
8bcd9f7469 GIT_SILENT Sync po/docbooks with svn 2025-07-20 01:41:17 +00:00
Tobias Fella
edf5d55da4 Prepare for new RoomId format
See MSC4291
2025-07-19 13:45:23 +00:00
l10n daemon script
976af783e2 GIT_SILENT Sync po/docbooks with svn 2025-07-19 06:16:11 +00:00
l10n daemon script
d87954838e GIT_SILENT Sync po/docbooks with svn 2025-07-18 01:38:52 +00:00
Joshua Goins
e757331dce Fix reference to Security & Safety settings on the invite screen
It's now called "Security & Safety", not just "Security". I also added a
note for translators to ensure they keep their strings consistent here.
2025-07-17 19:02:37 -04:00
l10n daemon script
bf4f6f5728 GIT_SILENT Sync po/docbooks with svn 2025-07-17 01:38:43 +00:00
Joshua Goins
c73bc8fc29 Redesign the enable encryption room setting
Clients like Element and ours show the room encryption mode as a toggle,
which in my opinion doesn't make sense. It's an irreversible operation,
so it should be a button!

When encryption is already used in the room, the button turns into a
non-interactive card.
2025-07-16 18:23:58 -04:00
Joshua Goins
211a08db68 Add ellipses to various settings actions that have confirm dialogs 2025-07-16 18:23:46 -04:00
Joshua Goins
38987e6d4c Add ellipses to the Report message action
This has a dialog to enter a message associated with the report, so as
suggested by the HIG it should have ellipses.
2025-07-16 18:23:46 -04:00
Joshua Goins
9d76e7e30b Show ellipses for leaving rooms and space actions, and always confirm
The HIG suggests using ellipses for actions that have a confirmation,
and leaving a space or room is one such cases. Otherwise, the user has
no idea if leaving is an immediate, irreversible action.

It turns out there *was* some cases where pressing this button
(especially for spaces) would actually do it without confirmation, which
is now fixed.
2025-07-16 18:23:46 -04:00
Joshua Goins
4c1a8d3657 Show the user's id or a room's canonical alias (if set) in invites
This should prevent the easiest way of masquerading, and provide an \
important identifier for rooms. Note that *only* the room canonical
alias is shown, if it's not set then it's just the display name. This is
intentional as regular users rarely interact with room IDs, but they can
still check it elsewhere in NeoChat.
2025-07-16 18:08:36 -04:00
James Graham
7a5de25885 Further refactor MessageContentModel
Further refactor MessageContentModel. Move away from special casing certian MessageContentTypes. Use forEachComponentOfType more
2025-07-16 18:27:03 +01:00
l10n daemon script
a17aa2c6fa GIT_SILENT Sync po/docbooks with svn 2025-07-16 01:56:47 +00:00
Tobias Fella
207a7876b6 Improve visualization of replies to non-message events 2025-07-15 19:55:35 +00:00
Tobias Fella
4c638a740e Set object ownership for NeoChatRoomMembers 2025-07-15 19:54:24 +00:00
Joshua Goins
0ee89e1b2b Show unable to decrypt events in the room list
These were previously (unintentionally) filtered, but I wanted to add
them because without showing and caching them my DMs look extremely out
of order. And assuming that you get an unexpected "unable to decrypt"
event, you would want that room to "shoot to the top" anyway.
2025-07-15 19:14:54 +00:00
Joshua Goins
4af42a57f4 Fix undefined QML reference in TimelineView
markAllMessagesAsRead() has moved it seems, and this specific case
wasn't changed.
2025-07-15 19:14:41 +00:00
Joshua Goins
34f2c2dabc Stop a room's lastActiveTime from changing because of hidden events
This was just a missed oversight during the refactoring, we need to pass
the hidden filter into NeoChatRoom::lastEvent so it doesn't pick up on
hidden events and push rooms to the top for no discernible reason.

Also, I simplified the function to take out the cached event handling,
because lastEvent is already doing that. The function is const now, too
and has some nicer comments.
2025-07-15 14:44:23 -04:00
l10n daemon script
9ff942915a GIT_SILENT Sync po/docbooks with svn 2025-07-15 01:44:46 +00:00
Tobias Fella
10123abc5b Fix maximizing replied-to media
The previous index-based handling opened the wrong media, as it used the wrong index.
2025-07-14 20:16:34 +00:00
l10n daemon script
ad993d4340 GIT_SILENT Sync po/docbooks with svn 2025-07-14 01:49:19 +00:00
l10n daemon script
ddc0a66d5b GIT_SILENT Sync po/docbooks with svn 2025-07-13 01:40:25 +00:00
l10n daemon script
e8981bdc0f GIT_SILENT Sync po/docbooks with svn 2025-07-12 01:42:05 +00:00
l10n daemon script
c42486a061 GIT_SILENT Sync po/docbooks with svn 2025-07-11 01:40:27 +00:00
l10n daemon script
64d82b8d2a GIT_SILENT Sync po/docbooks with svn 2025-07-10 01:40:11 +00:00
l10n daemon script
677abee890 GIT_SILENT Sync po/docbooks with svn 2025-07-09 01:42:04 +00:00
Joshua Goins
3a25a62350 Refer to global notification settings as "Default Settings" instead
This changes references to "global notification settings" to "default
settings", which should hopefully make it clearer what these actually
are.

Also, a information tip has been added to the settings page to clarify
what these settings do and imply that the per-room settings take
precedence.
2025-07-08 17:17:04 -04:00
l10n daemon script
bc7b480c41 GIT_SILENT Sync po/docbooks with svn 2025-07-08 01:40:16 +00:00
Tobias Fella
d9b495356d Fix roomlist positioning when changing rooms
The room list is supposed to center the new room, but, since the current room as stored in SortFilterRoomTreeModel is only updated after we're jumping to the room, mostly just jumped to the old room.
2025-07-07 18:31:40 +00:00
Tobias Fella
ce82606e6e Fix jumping to replied-to event 2025-07-07 14:20:02 +00:00
l10n daemon script
07837c2e64 GIT_SILENT Sync po/docbooks with svn 2025-07-07 01:38:29 +00:00
Tobias Fella
1738253e6f Don't crash when calling RoomManager::viewEventMenu with empty event id 2025-07-06 23:14:28 +02:00
James Graham
17fa2246da Refine MessageContentModel further
Create some standard functions that should hopefully make it easier to manage the model contents. Used for file stuff for now.
2025-07-06 21:16:17 +01:00
l10n daemon script
4f5e096e7e GIT_SILENT Sync po/docbooks with svn 2025-07-06 01:37:58 +00:00
172 changed files with 31681 additions and 26461 deletions

View File

@@ -20,8 +20,16 @@
"--talk-name=org.kde.kwalletd5",
"--talk-name=org.kde.StatusNotifierWatcher",
"--talk-name=org.freedesktop.secrets",
"--talk-name=org.kde.kuiserver",
"--own-name=org.kde.StatusNotifierItem-2-2"
],
"cleanup": [
"/include",
"/lib/*.a",
"/lib/cmake",
"/lib/pkgconfig",
"/share/ndk-modules"
],
"modules": [
{
"name": "kirigamiaddons",
@@ -82,8 +90,8 @@
"sources": [
{
"type": "archive",
"url": "https://download.gnome.org/sources/libsecret/0.21/libsecret-0.21.6.tar.xz",
"sha256": "747b8c175be108c880d3adfb9c3537ea66e520e4ad2dccf5dce58003aeeca090",
"url": "https://download.gnome.org/sources/libsecret/0.21/libsecret-0.21.7.tar.xz",
"sha256": "6b452e4750590a2b5617adc40026f28d2f4903de15f1250e1d1c40bfd68ed55e",
"x-checker-data": {
"type": "gnome",
"name": "libsecret",
@@ -156,13 +164,13 @@
"sources": [
{
"type": "archive",
"url": "https://download.kde.org/stable/kunifiedpush/kunifiedpush-1.0.0.tar.xz",
"sha256": "2ddeba21306d0307114ec50a2c38159ec62359f9fc6cdd58da30a369fbd550cf",
"url": "https://download.kde.org/stable/release-service/25.04.3/src/kunifiedpush-25.04.3.tar.xz",
"sha256": "a16ffe4117b14baa02f3b8ae7de9e509a17359c1b67dcd851aef4f3c3661a1df",
"x-checker-data": {
"type": "anitya",
"project-id": 375055,
"project-id": 8763,
"stable-only": true,
"url-template": "https://download.kde.org/stable/kunifiedpush/kunifiedpush-$version.tar.xz"
"url-template": "https://download.kde.org/stable/release-service/$version/src/kunifiedpush-$version.tar.xz"
}
}
]

View File

@@ -6,6 +6,7 @@
#include <QObject>
#include <QTest>
#include <QSignalSpy>
#include <Quotient/roommember.h>
#include <Quotient/syncdata.h>
#include <qtestcase.h>
@@ -32,6 +33,7 @@ private Q_SLOTS:
void noRoom();
void badParent();
void reply();
void replyMissingUser();
void edit();
void attachment();
};
@@ -102,6 +104,33 @@ void ChatBarCacheTest::reply()
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@example:example.org"_s));
QCOMPARE(chatBarCache->relationMessage(), u"This is an example\ntext message"_s);
QCOMPARE(chatBarCache->attachmentPath(), QString());
QCOMPARE(chatBarCache->relationAuthorIsPresent(), true);
}
void ChatBarCacheTest::replyMissingUser()
{
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
chatBarCache->setText(u"some text"_s);
chatBarCache->setAttachmentPath(u"some/path"_s);
chatBarCache->setReplyId(u"$153456789:example.org"_s);
QCOMPARE(chatBarCache->text(), u"some text"_s);
QCOMPARE(chatBarCache->isReplying(), true);
QCOMPARE(chatBarCache->replyId(), u"$153456789:example.org"_s);
QCOMPARE(chatBarCache->isEditing(), false);
QCOMPARE(chatBarCache->editId(), QString());
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@example:example.org"_s));
QCOMPARE(chatBarCache->relationMessage(), u"This is an example\ntext message"_s);
QCOMPARE(chatBarCache->attachmentPath(), QString());
QCOMPARE(chatBarCache->relationAuthorIsPresent(), true);
QSignalSpy relationAuthorIsPresentSpy(chatBarCache.get(), &ChatBarCache::relationAuthorIsPresentChanged);
// sync again, which will simulate the reply user leaving the room
room->syncNewEvents(u"test-min-sync-extra-sync.json"_s);
QTRY_COMPARE(relationAuthorIsPresentSpy.count(), 1);
QCOMPARE(chatBarCache->relationAuthorIsPresent(), false);
}
void ChatBarCacheTest::edit()

View File

@@ -0,0 +1,20 @@
{
"state": {
"events": [
{
"content": {
"membership": "leave"
},
"event_id": "$1432735824666PhrSA:example.org",
"origin_server_ts": 1432735824666,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"state_key": "@example:example.org",
"type": "m.room.member",
"unsigned": {
"replaces_state": "$143273582443PhrSn:example.org"
}
}
]
}
}

View File

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

View File

@@ -10,7 +10,7 @@
#include <Quotient/roommember.h>
#include <Quotient/syncdata.h>
#include "models/messagecontentmodel.h"
#include "models/eventmessagecontentmodel.h"
#include "neochatconnection.h"
#include "testutils.h"
@@ -39,13 +39,13 @@ void MessageContentModelTest::initTestCase()
void MessageContentModelTest::missingEvent()
{
auto room = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s);
auto model1 = MessageContentModel(room, u"$153456789:example.org"_s);
auto model1 = EventMessageContentModel(room, u"$153456789:example.org"_s);
QCOMPARE(model1.rowCount(), 1);
QCOMPARE(model1.data(model1.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
QCOMPARE(model1.data(model1.index(0), MessageContentModel::DisplayRole), u"Loading"_s);
auto model2 = MessageContentModel(room, u"$153456789:example.org"_s, true);
auto model2 = EventMessageContentModel(room, u"$153456789:example.org"_s, true);
QCOMPARE(model2.rowCount(), 1);
QCOMPARE(model2.data(model2.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);

View File

@@ -9,7 +9,7 @@
#include <Quotient/events/roommessageevent.h>
#include "models/messagecontentmodel.h"
#include "models/eventmessagecontentmodel.h"
#include "testutils.h"
using namespace Quotient;
@@ -21,7 +21,7 @@ class ReactionModelTest : public QObject
private:
Connection *connection = nullptr;
TestUtils::TestRoom *room = nullptr;
MessageContentModel *parentModel;
EventMessageContentModel *parentModel;
private Q_SLOTS:
void initTestCase();
@@ -34,7 +34,7 @@ void ReactionModelTest::initTestCase()
{
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-reactionmodel-sync.json"_s);
parentModel = new MessageContentModel(room, "123456"_L1);
parentModel = new EventMessageContentModel(room, "123456"_L1);
}
void ReactionModelTest::basicReaction()

View File

@@ -75,6 +75,7 @@
<summary xml:lang="nl">Chat op Matrix</summary>
<summary xml:lang="nn">Prat med via Matrix</summary>
<summary xml:lang="pl">Rozmawiaj na Matriksie</summary>
<summary xml:lang="pt-BR">Bate-papo na Matrix</summary>
<summary xml:lang="ru">Общение в Matrix</summary>
<summary xml:lang="sa">Matrix इत्यत्र गपशपं कुर्वन्तु</summary>
<summary xml:lang="sl">Klepet na Matrixu</summary>
@@ -109,6 +110,7 @@
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
<p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p>
<p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p>
<p xml:lang="pt-BR">O NeoChat é um aplicativo de bate-papo que permite que você aproveite ao máximo a rede Matrix. Ele oferece uma maneira segura de enviar mensagens de texto, vídeos e arquivos de áudio para sua família, colegas e amigos.</p>
<p xml:lang="ru">NeoChat — приложение для общения, предоставляющее все преимущества сети Matrix. С его помощью можно безопасно отправлять текстовые сообщения, видеозаписи и звуковые файлы родственникам, коллегам и друзьям.</p>
<p xml:lang="sa">NeoChat इति एकं गपशप-अनुप्रयोगं यत् भवान् Matrix-जालस्य पूर्णं लाभं ग्रहीतुं शक्नोति । एतत् भवन्तं भवतः परिवाराय, सहकारिभ्यः, मित्रेभ्यः च पाठसन्देशान्, भिडियो, श्रव्यसञ्चिकाः च प्रेषयितुं सुरक्षितं मार्गं प्रदाति ।</p>
<p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p>
@@ -142,6 +144,7 @@
<p xml:lang="nn">NeoChat har som mål å støtta all funksjonalitet i Matrix-spesifikasjonen. Førebels er alt i den gjeldande stabile spesifikasjonen støtta, med unntak av VoIP, trådar og nokre delar av ende-til-kryptering. Det finst òg andre småting som ikkje er støtta, sidan Matrix-spesifikasjon er i stadig endring, men målet er altså støtte for alt.</p>
<p xml:lang="pl">NeoChat w zamyśle ma być pełnowartościową aplikacją wg wytycznych Matriksa. Z tego powodu, wszystko, co jest obecnie w stabilnych wytycznych z pominięciem VoIP, wątków i niektórych części szyfrowania Użytkownik-do-Użytkownika są obecnie obsługiwane. Pominięto też kilka mniejszych rzeczy ze względu na ciągły rozwój wytycznych Matriksa, lecz celem nadal jest zapewnienie obsługi wszystkich wytycznych.</p>
<p xml:lang="pt">O NeoChat pretende ser uma aplicação completa para a especificação do Matrix. Como tal, tudo o que existe na especificação estável actual, com as notáveis excepções do VoIP, tópicos e alguns aspectos da Encriptação Ponto-a-Ponto, são suportados. Existem mais algumas omissões, devido ao facto que a norma do Matrix está em constante evolução, mas o objectivo continua a ser oferecer o suporte eventual para a norma por inteiro.</p>
<p xml:lang="pt-BR">O NeoChat pretende ser um aplicativo completo para a especificação Matrix. Dessa forma, tudo na especificação estável atual, com as notáveis exceções de VoIP, tópicos e alguns aspectos da criptografia de ponta a ponta, é suportado. Há algumas outras pequenas omissões devido ao fato de a especificação Matrix estar em constante evolução, mas o objetivo continua sendo fornecer suporte eventual para toda a especificação.</p>
<p xml:lang="ru">Целью создания NeoChat является полноценная реализация программы для спецификации Matrix. Как следствие, реализовано всё в текущей стабильной спецификации (за исключением голосовой интернет-связи, потоков и некоторых аспектов сквозного шифрования). Есть также несколько других незначительных пробелов, обусловленных постоянными изменениями спецификации Matrix. Тем не менее, стоит задача в итоге предоставить полную поддержку спецификации.</p>
<p xml:lang="sa">NeoChat इत्यस्य उद्देश्यं Matrix विनिर्देशस्य कृते पूर्णतया विशेषतायुक्तः अनुप्रयोगः भवितुम् अस्ति । यथा तथा वर्तमानस्थिरविनिर्देशे सर्वं VoIP इत्यस्य उल्लेखनीयअपवादैः सह, थ्रेड्स तथा च End-to-End Encryption इत्यस्य केचन पक्षाः समर्थिताः सन्ति । अन्ये कतिचन लघु लोपाः सन्ति यतोहि Matrix spec निरन्तरं विकसितः अस्ति परन्तु उद्देश्यं सम्पूर्ण spec कृते अन्ततः समर्थनं प्रदातुं अवशिष्टम् अस्ति</p>
<p xml:lang="sl">Neochat cilja, da bi bila popolna aplikacija po specifikaciji Matrixa. Kot takšna vsebuje vse v trenutni stabilni specifikaciji z pomembnimi izjemami pri VoIP, nitih in nekaterih vidikov šifriranja od konca do konca. Obstaja nekaj drugih manjših opustitev zaradi dejstva, da se specifikacija Matrix nenehno razvija, vendar cilj ostaja zagotoviti morebitno podporo celotni specifikaciji.</p>
@@ -175,6 +178,7 @@
<p xml:lang="nn">På grunn av måten Matrix-spesifikasjonen vert utvikla på, støttar NeoChat òg nokre uferdige funksjonar:</p>
<p xml:lang="pl">Ze względu na sposób rozwoju Matriksa, NeoChat obsługuje także kilka niestabilnych możliwości. Obecnie są to:</p>
<p xml:lang="pt">Devido à natureza do desenvolvimento da especificação do Matrix, o NeoChat também suporta diversas funcionalidades instáveis. De momento são:</p>
<p xml:lang="pt-BR">Devido à natureza do desenvolvimento da especificação Matrix, o NeoChat também suporta diversos recursos instáveis. Atualmente, são eles:</p>
<p xml:lang="ru">В силу природы разработки спецификации Matrix в NeoChat тоже предусмотрена поддержка многочисленных нестабильных возможностей. В текущей версии это следующие возможности:</p>
<p xml:lang="sa">Matrix विनिर्देशविकासस्य प्रकृतेः कारणात् NeoChat अपि अनेकानाम् अस्थिरविशेषतानां समर्थनं करोति । सम्प्रति एते सन्ति :</p>
<p xml:lang="sl">Zaradi narave razvoja specifikacije Matrixa NeoChat podpira tudi številne nestabilne zmožnosti. Trenutno so to:</p>
@@ -187,8 +191,8 @@
<ul>
<li>Polls - MSC3381</li>
<li xml:lang="ar">التصويت - MSC3381</li>
<li xml:lang="ca">Enquestes - MSC3381</li>
<li xml:lang="ca-valencia">Enquestes - MSC3381</li>
<li xml:lang="ca">Votacions - MSC3381</li>
<li xml:lang="ca-valencia">Votacions - MSC3381</li>
<li xml:lang="el">Δημοσκοπήσεις - MSC3381</li>
<li xml:lang="en-GB">Polls - MSC3381</li>
<li xml:lang="eo">Enketoj - MSC3381</li>
@@ -209,6 +213,7 @@
<li xml:lang="nn">Avstemmingar  MSC3381</li>
<li xml:lang="pl">Ankiety - MSC3381</li>
<li xml:lang="pt">Inquéritos - MSC3381</li>
<li xml:lang="pt-BR">Enquetes - MSC3381</li>
<li xml:lang="ru">Голосования — MSC3381</li>
<li xml:lang="sa">मतदान - MSC3381</li>
<li xml:lang="sl">Polls - MSC3381</li>
@@ -242,6 +247,7 @@
<li xml:lang="nn">Klistremerke-pakkar  MSC2545</li>
<li xml:lang="pl">Paczki naklejek - MSC2545</li>
<li xml:lang="pt">Pacotes de Autocolantes - MSC2545</li>
<li xml:lang="pt-BR">Pacotes de Stickers - MSC2545</li>
<li xml:lang="ru">Наборы стикеров — MSC2545</li>
<li xml:lang="sa">स्टिकर पैक - MSC2545</li>
<li xml:lang="sl">Sticker Packs - MSC2545</li>
@@ -275,6 +281,7 @@
<li xml:lang="nn">Posisjonshendingar  MSC3488</li>
<li xml:lang="pl">Wydarzenia w miejscach - MSC3488</li>
<li xml:lang="pt">Eventos com Localizações - MSC3488</li>
<li xml:lang="pt-BR">Localização de eventos - MSC3488</li>
<li xml:lang="ru">События местоположения — MSC3488</li>
<li xml:lang="sa">स्थान घटनाएँ - MSC3488</li>
<li xml:lang="sl">Location Events - MSC3488</li>
@@ -344,6 +351,7 @@
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
<caption xml:lang="pt-BR">Visão principal com lista de salas, bate-papo e informações sobre as salas</caption>
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
<caption xml:lang="sa">कक्षसूची, गपशपः, कक्षसूचना च सह मुख्यदृश्यम्</caption>
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
@@ -380,6 +388,7 @@
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
<caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption>
<caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption>
<caption xml:lang="pt-BR">Descubra novas comunidades com os Espaços Matrix</caption>
<caption xml:lang="ru">Поиск новых сообществ с помощью Matrix Spaces</caption>
<caption xml:lang="sa">Matrix Spaces इत्यनेन सह नूतनानां समुदायानाम् अन्वेषणं कुर्वन्तु</caption>
<caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption>
@@ -424,6 +433,7 @@
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
<caption xml:lang="pt-BR">Visão principal com lista de salas, bate-papo e informações sobre as salas</caption>
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
<caption xml:lang="sa">कक्षसूची, गपशपः, कक्षसूचना च सह मुख्यदृश्यम्</caption>
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
@@ -462,6 +472,7 @@
<caption xml:lang="nn">Innloggingsbilete</caption>
<caption xml:lang="pl">Ekran logowania</caption>
<caption xml:lang="pt">Ecrã de autenticação</caption>
<caption xml:lang="pt-BR">Tela de login</caption>
<caption xml:lang="ru">Окно входа</caption>
<caption xml:lang="sa">लॉगिन् स्क्रीन</caption>
<caption xml:lang="sl">Prijavni zaslon</caption>
@@ -477,6 +488,7 @@
<content_attribute id="social-chat">intense</content_attribute>
</content_rating>
<releases>
<release version="25.08.0" date="2025-08-14"/>
<release version="25.04.3" date="2025-07-03"/>
<release version="25.04.2" date="2025-06-05"/>
<release version="25.04.1" date="2025-05-08"/>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,6 @@
#include "controller.h"
#include <Quotient/connection.h>
#include <qt6keychain/keychain.h>
#include <KLocalizedString>
@@ -14,7 +13,6 @@
#include <signal.h>
#include <Quotient/csapi/notifications.h>
#include <Quotient/events/roommemberevent.h>
#include <Quotient/qt_connection_util.h>
#include <Quotient/settings.h>
@@ -24,7 +22,6 @@
#include "mediasizehelper.h"
#include "models/actionsmodel.h"
#include "models/messagemodel.h"
#include "models/pushrulemodel.h"
#include "models/roomlistmodel.h"
#include "models/roomtreemodel.h"
#include "neochatconfig.h"

View File

@@ -7,7 +7,6 @@
#include <QQmlEngine>
#include "neochatconnection.h"
#include "neochatroom.h"
#include <Quotient/events/roommessageevent.h>
#include <Quotient/roommember.h>
@@ -25,11 +24,7 @@ class CommonRoomsModel : public QAbstractListModel
public:
enum Roles {
TextRole = Qt::DisplayRole,
LongitudeRole,
LatitudeRole,
AssetRole,
AuthorRole,
RoomIdRole = Qt::DisplayRole,
};
Q_ENUM(Roles)

View File

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

View File

@@ -189,6 +189,10 @@
<label>Don't hide any events in the timeline</label>
<default>false</default>
</entry>
<entry name="RelateAnyEvent" type="bool">
<label>Send relations to any event, including state events and events normally hidden.</label>
<default>false</default>
</entry>
<entry name="AlwaysVerifyDevice" type="bool">
<label>Always allow device verification</label>
<default>false</default>

View File

@@ -93,4 +93,3 @@ X-Plasma-API=DBus
X-Plasma-DBusRunner-Service=org.kde.neochat
X-Plasma-DBusRunner-Path=/RoomRunner
X-Plasma-Request-Actions-Once=true
X-Plasma-Runner-Min-Letter-Count=3

View File

@@ -1,9 +1,10 @@
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.components as KirigamiComponents
@@ -18,21 +19,17 @@ KirigamiComponents.ConvergentContextMenu {
required property NeoChatConnection connection
required property Kirigami.ApplicationWindow window
QQC2.Action {
Kirigami.Action {
text: i18nc("@action:button", "Show QR Code")
icon.name: "view-barcode-qr-symbolic"
onTriggered: {
let qrMax = Qt.createComponent('org.kde.neochat', 'QrCodeMaximizeComponent').createObject(QQC2.Overlay.overlay, {
(Qt.createComponent('org.kde.neochat', 'QrCodeMaximizeComponent').createObject(QQC2.Overlay.overlay, {
text: "https://matrix.to/#/" + root.connection.localUser.id,
title: root.connection.localUser.displayName,
subtitle: root.connection.localUser.id,
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
avatarSource: root.connection.localUser.avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(root.connection.localUser.avatarUrl) : ""
});
if (typeof root.closeDialog === "function") {
root.closeDialog();
}
qrMax.open();
}) as QrCodeMaximizeComponent).open();
}
}
@@ -40,26 +37,27 @@ KirigamiComponents.ConvergentContextMenu {
text: i18nc("@action:inmenu", "Switch Account")
icon.name: "system-switch-user"
shortcut: "Ctrl+U"
onTriggered: accountSwitchDialog.createObject(QQC2.Overlay.overlay, {
onTriggered: (Qt.createComponent("org.kde.neochat", "AccountSwitchDialog").createObject(QQC2.Overlay.overlay, {
connection: root.connection
}).open();
}) as Kirigami.Dialog).open();
}
QQC2.Action {
text: i18n("Edit This Account")
Kirigami.Action {
text: i18nc("@action:inmenu", "Edit This Account")
icon.name: "document-edit"
onTriggered: NeoChatSettingsView.openWithInitialProperties("accounts", {initialAccount: root.connection});
}
QQC2.Action {
text: i18n("Notification Settings")
Kirigami.Action {
text: i18nc("@action:inmenu", "Notification Settings")
icon.name: "notifications"
onTriggered: {
NeoChatSettingsView.open('notifications');
}
}
QQC2.Action {
text: i18n("Devices")
Kirigami.Action {
text: i18nc("@action:inmenu", "Devices")
icon.name: "computer-symbolic"
onTriggered: {
NeoChatSettingsView.open('devices');
@@ -67,10 +65,10 @@ KirigamiComponents.ConvergentContextMenu {
}
Kirigami.Action {
text: i18n("Open Developer Tools")
text: i18nc("@action:inmenu", "Open Developer Tools")
icon.name: "tools"
visible: NeoChatConfig.developerTools
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'DevtoolsPage'), {
onTriggered: root.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'DevtoolsPage'), {
connection: root.connection
}, {
title: i18nc("@title:window", "Developer Tools"),
@@ -88,9 +86,10 @@ KirigamiComponents.ConvergentContextMenu {
})
}
QQC2.Action {
Kirigami.Action {
text: i18nc("@action:inmenu", "Verify This Device")
icon.name: "security-low"
visible: !root.connection.isVerifiedSession()
onTriggered: {
root.connection.startSelfVerification();
const dialog = Qt.createComponent("org.kde.kirigami", "PromptDialog").createObject(QQC2.Overlay.overlay, {
@@ -99,19 +98,17 @@ KirigamiComponents.ConvergentContextMenu {
standardButtons: Kirigami.Dialog.Ok
})
dialog.open();
root.connection.onNewKeyVerificationSession.connect(() => {
root.connection.newKeyVerificationSession.connect(() => {
dialog.close();
});
}
}
QQC2.Action {
text: i18n("Logout")
Kirigami.Action {
text: i18nc("@action:inmenu", "Logout")
icon.name: "im-kick-user"
onTriggered: confirmLogoutDialogComponent.createObject(root).open()
}
readonly property Component confirmLogoutDialogComponent: ConfirmLogoutDialog {
connection: root.connection
onTriggered: (Qt.createComponent("org.kde.neochat", "ConfirmLogoutDialog").createObject(QQC2.Overlay.overlay, {
connection: root.connection
}) as Kirigami.Dialog).open()
}
}

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
@@ -16,8 +17,6 @@ Kirigami.Dialog {
required property NeoChatConnection connection
parent: applicationWindow().overlay
leftPadding: 0
rightPadding: 0
topPadding: 0
@@ -25,7 +24,7 @@ Kirigami.Dialog {
standardButtons: Kirigami.Dialog.NoButton
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
width: Math.min(QQC2.ApplicationWindow.window.width, Kirigami.Units.gridUnit * 24)
title: i18nc("@title: dialog to switch between logged in accounts", "Switch Account")
onVisibleChanged: if (visible) {
@@ -53,7 +52,7 @@ Kirigami.Dialog {
}
text: i18nc("@button: login to or register a new account.", "Add Account")
contentItem: Delegates.SubtitleContentItem {
itemDelegate: parent
itemDelegate: addDelegate
subtitle: i18n("Log in or create a new account")
labelItem.textFormat: Text.PlainText
subtitleItem.textFormat: Text.PlainText

View File

@@ -33,7 +33,7 @@ ColumnLayout {
}
QQC2.ToolButton {
id: editImageButton
visible: hasImage
visible: root.hasImage
icon.name: "document-edit"
text: i18n("Edit")
display: QQC2.AbstractButton.IconOnly
@@ -46,9 +46,9 @@ ColumnLayout {
}
onClicked: {
let imageEditor = applicationWindow().pageStack.pushDialogLayer(imageEditorPage);
let imageEditor = (Kirigami.PageStack.pageStack as Kirigami.PageRow).pushDialogLayer(imageEditorPage);
imageEditor.newPathChanged.connect(function (newPath) {
applicationWindow().pageStack.layers.pop();
imageEditor.closeDialog();
root.attachmentPath = newPath;
});
}
@@ -61,7 +61,7 @@ ColumnLayout {
action: Kirigami.Action {
text: i18n("Cancel sending attachment")
icon.name: "dialog-close"
onTriggered: attachmentCancelled()
onTriggered: root.attachmentCancelled()
shortcut: "Escape"
}
QQC2.ToolTip.text: text
@@ -75,8 +75,8 @@ ColumnLayout {
asynchronous: true
cache: false // Cache is not needed. Images will rarely be shown repeatedly.
source: hasImage ? root.attachmentPath : ""
visible: hasImage
source: root.hasImage ? root.attachmentPath : ""
visible: root.hasImage
fillMode: Image.PreserveAspectFit
onSourceChanged: {
@@ -114,11 +114,11 @@ ColumnLayout {
id: mimetypeIcon
implicitWidth: Kirigami.Units.iconSizes.smallMedium
implicitHeight: Kirigami.Units.iconSizes.smallMedium
source: attachmentMimetype.iconName
source: root.attachmentMimetype.iconName
}
QQC2.Label {
id: fileLabel
text: baseFileName
text: root.baseFileName
}
}
}

View File

@@ -3,12 +3,9 @@
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as QQC2
import QtQuick.Templates as T
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.delegates as Delegates
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
Delegates.RoundedItemDelegate {
id: root

View File

@@ -8,7 +8,6 @@ import QtQml.Models
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.kitemmodels
import org.kde.neochat

View File

@@ -59,7 +59,7 @@ ApplicationWindow {
Connections {
target: mapView.map
function onCopyrightLinkActivated() {
function onCopyrightLinkActivated(link: string): void {
Qt.openUrlExternally(link);
}
}

View File

@@ -17,23 +17,6 @@ Labs.MenuBar {
required property NeoChatConnection connection
Labs.Menu {
title: i18nc("menu", "NeoChat")
Labs.MenuItem {
enabled: pageStack.layers.currentItem.title !== i18n("Configure NeoChat…")
text: i18nc("menu", "Configure NeoChat…")
shortcut: StandardKey.Preferences
onTriggered: NeoChatSettingsView.open()
}
Labs.MenuItem {
text: i18nc("menu", "Quit NeoChat")
shortcut: StandardKey.Quit
onTriggered: Qt.quit()
}
}
Labs.Menu {
title: i18nc("menu", "File")
@@ -74,6 +57,19 @@ Labs.MenuBar {
});
}
}
Labs.MenuItem {
enabled: pageStack.layers.currentItem.title !== i18n("Configure NeoChat…")
text: i18nc("menu", "Configure NeoChat…")
shortcut: StandardKey.Preferences
onTriggered: NeoChatSettingsView.open()
}
Labs.MenuItem {
text: i18nc("menu", "Quit NeoChat")
shortcut: StandardKey.Quit
onTriggered: Qt.quit()
}
}
EditMenu {
title: i18nc("menu", "Edit")

View File

@@ -52,6 +52,15 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
}
Kirigami.SelectableLabel {
Layout.fillWidth: true
font: Kirigami.Theme.smallFont
textFormat: TextEdit.PlainText
visible: root.currentRoom && root.currentRoom.canonicalAlias
text: root.currentRoom && root.currentRoom.canonicalAlias ? root.currentRoom.canonicalAlias : ""
color: Kirigami.Theme.disabledTextColor
}
Kirigami.Heading {
text: root.currentRoom.displayName
@@ -70,7 +79,14 @@ ColumnLayout {
spacing: Kirigami.Units.smallSpacing
Kirigami.Heading {
text: root.currentRoom.displayName
text: root.invitingMember.displayName
Layout.alignment: Qt.AlignHCenter
}
QQC2.Label {
text: root.invitingMember.id
color: Kirigami.Theme.disabledTextColor
Layout.alignment: Qt.AlignHCenter
}
@@ -159,7 +175,7 @@ ColumnLayout {
QQC2.Label {
color: Kirigami.Theme.disabledTextColor
text: i18nc("@info:label", "You can reject invitations from unknown users under Security settings.")
text: xi18nc("@info:label Ensure you are referring to the same translation used for that settings page", "You can reject invitations from unknown users under the <interface>Security & Safety</interface> settings.")
wrapMode: Text.WordWrap
// + 5 to prevent it from wrapping unnecessarily

View File

@@ -8,7 +8,6 @@ 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
@@ -25,7 +24,7 @@ Kirigami.Dialog {
standardButtons: Kirigami.Dialog.NoButton
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
width: Math.min(QQC2.ApplicationWindow.window.width, Kirigami.Units.gridUnit * 24)
title: i18nc("@title:dialog", "Join Room")
contentItem: ColumnLayout {

View File

@@ -60,21 +60,21 @@ Kirigami.ApplicationWindow {
Connections {
target: LoginHelper
function onLoaded() {
function onLoaded(): void {
root.load();
}
}
Connections {
target: Registration
function onLoaded() {
function onLoaded(): void {
root.load();
}
}
Connections {
target: root.quitAction
function onTriggered() {
function onTriggered(): void {
Qt.quit();
}
}
@@ -98,42 +98,40 @@ Kirigami.ApplicationWindow {
Connections {
target: RoomManager
function onCurrentRoomChanged() {
function onCurrentRoomChanged(): void {
if (RoomManager.currentRoom && pageStack.depth <= 1 && root.initialized && Kirigami.Settings.isMobile) {
let roomPage = pageStack.layers.push(Qt.createComponent('org.kde.neochat', 'RoomPage'), {
connection: root.connection
});
let roomPage = pageStack.layers.push(Qt.createComponent('org.kde.neochat', 'RoomPage'));
roomPage.backRequested.connect(event => {
RoomManager.clearCurrentRoom();
});
}
}
function onAskJoinRoom(room) {
function onAskJoinRoom(room: NeoChatRoom): void {
Qt.createComponent("org.kde.neochat", "JoinRoomDialog").createObject(root, {
room: room,
connection: root.connection
}).open();
}
function onShowUserDetail(user, room) {
function onShowUserDetail(user, room: NeoChatRoom): void {
root.showUserDetail(user, room);
}
function goToEvent(event) {
function goToEvent(event: string): void {
if (event.length > 0) {
roomItem.goToEvent(event);
}
roomItem.forceActiveFocus();
}
function onAskDirectChatConfirmation(user) {
function onAskDirectChatConfirmation(user): void {
Qt.createComponent("org.kde.neochat", "AskDirectChatConfirmation").createObject(this, {
user: user
}).open();
}
function onExternalUrl(url) {
function onExternalUrl(url): void {
let dialog = Qt.createComponent("org.kde.neochat", "ConfirmUrlDialog").createObject(this);
dialog.link = url;
dialog.open();
@@ -338,7 +336,7 @@ Kirigami.ApplicationWindow {
}
}
function handleShare(): void {
const dialog = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), {
const dialog = root.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), {
connection: root.connection
}, {
title: i18nc("@title", "Share"),
@@ -365,9 +363,7 @@ Kirigami.ApplicationWindow {
RoomManager.loadInitialRoom();
if (!Kirigami.Settings.isMobile) {
let roomPage = pageStack.push(Qt.createComponent('org.kde.neochat', 'RoomPage'), {
connection: root.connection
});
let roomPage = pageStack.push(Qt.createComponent('org.kde.neochat', 'RoomPage'));
roomPage.forceActiveFocus();
}

View File

@@ -47,7 +47,7 @@ Kirigami.Page {
icon.name: "document-edit"
visible: root.allowEdit
enabled: room.canSendState(root.type) && (!root.stateKey.startsWith("@") || root.stateKey === root.room.connection.localUserId) && root.type !== "m.room.create"
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "EditStateDialog.qml"), {
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "EditStateDialog"), {
room: root.room,
type: root.type,
stateKey: root.stateKey,

View File

@@ -72,7 +72,7 @@ Components.AlbumMaximizeComponent {
Connections {
target: MediaManager
function onPlaybackStarted() {
function onPlaybackStarted(): void {
if (currentItem.playbackState === MediaPlayer.PlayingState) {
currentItem.pause();
}
@@ -82,7 +82,7 @@ Components.AlbumMaximizeComponent {
Connections {
target: currentRoom
function onFileTransferProgress(id, progress, total) {
function onFileTransferProgress(id: string, progress: int, total: int): void {
if (id == root.currentEventId) {
root.downloadAction.progress = progress / total * 100.0;
}
@@ -130,7 +130,7 @@ Components.AlbumMaximizeComponent {
Connections {
target: RoomManager
function onCloseFullScreen() {
function onCloseFullScreen(): void {
root.close();
}
}

View File

@@ -34,7 +34,7 @@ Kirigami.Dialog {
}
]
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
width: Math.min(QQC2.ApplicationWindow.window.width, Kirigami.Units.gridUnit * 24)
title: i18nc("@title: create new poll in the room", "Create Poll")
contentItem: ColumnLayout {

View File

@@ -22,7 +22,7 @@ Kirigami.Page {
Connections {
target: root.QQC2.ApplicationWindow.window
function onClosing() {
function onClosing(): void {
root.destroy();
}
}

View File

@@ -6,7 +6,6 @@ import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kitemmodels
import org.kde.neochat

View File

@@ -15,8 +15,6 @@ Kirigami.Dialog {
property var connection
parent: applicationWindow().overlay
leftPadding: 0
rightPadding: 0
topPadding: 0
@@ -24,7 +22,7 @@ Kirigami.Dialog {
title: i18nc("@title Join <name of a space>", "Join %1", SpaceHierarchyCache.recommendedSpaceDisplayName)
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
width: Math.min(QQC2.ApplicationWindow.window.width, Kirigami.Units.gridUnit * 24)
contentItem: ColumnLayout {
FormCard.AbstractFormDelegate {

View File

@@ -8,7 +8,6 @@ import QtQuick.Layouts
import QtQuick.Window
import org.kde.kirigami as Kirigami
import org.kde.kitemmodels
import org.kde.neochat
@@ -64,9 +63,9 @@ Kirigami.Page {
actions: [
Kirigami.Action {
visible: Kirigami.Settings.isMobile || !applicationWindow().pageStack.wideMode
visible: Kirigami.Settings.isMobile || !root.Kirigami.PageStack.pageStack.wideMode
icon.name: "view-right-new"
onTriggered: applicationWindow().openRoomDrawer()
onTriggered: (root.QQC2.ApplicationWindow.window as Main).openRoomDrawer()
}
]
@@ -81,9 +80,9 @@ Kirigami.Page {
Connections {
target: root.currentRoom.connection
function onIsOnlineChanged() {
function onIsOnlineChanged(): void {
if (!root.currentRoom.connection.isOnline) {
banner.text = i18n("NeoChat is offline. Please check your network connection.");
banner.text = i18nc("@info:status", "NeoChat is offline. Please check your network connection.");
banner.visible = true;
banner.type = Kirigami.MessageType.Error;
} else {
@@ -138,8 +137,8 @@ Kirigami.Page {
anchors.centerIn: parent
sourceComponent: Kirigami.PlaceholderMessage {
icon.name: "org.kde.neochat"
text: i18n("Welcome to NeoChat")
explanation: i18n("Select or join a room to get started")
text: i18nc("@title", "Welcome to NeoChat")
explanation: i18nc("@info:usagetip", "Select or join a room to get started")
}
}
@@ -163,20 +162,20 @@ Kirigami.Page {
Connections {
target: RoomManager
function onCurrentRoomChanged() {
function onCurrentRoomChanged(): void {
if (root.currentRoom && root.currentRoom.isInvite) {
Controller.clearInvitationNotification(root.currentRoom.id);
}
}
function onGoToEvent(eventId) {
function onGoToEvent(eventId: string): void {
(timelineViewLoader.item as TimelineView).goToEvent(eventId);
}
}
Connections {
target: root.currentRoom.connection
function onJoinedRoom(room, invited) {
function onJoinedRoom(room: NeoChatRoom, invited: NeoChatRoom): void {
if (root.currentRoom.id === invited.id) {
RoomManager.resolveResource(room.id);
}
@@ -196,22 +195,22 @@ Kirigami.Page {
Connections {
target: RoomManager
function onShowMessage(messageType, message) {
function onShowMessage(messageType: Kirigami.MessageType, message: string): void {
banner.text = message;
banner.type = messageType;
banner.visible = true;
}
function onShowEventSource(eventId) {
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
function onShowEventSource(eventId: string): void {
(root.Kirigami.PageStack.pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
sourceText: root.currentRoom.getEventJsonSource(eventId)
}, {
title: i18n("Message Source"),
title: i18nc("@title:dialog", "Message Source"),
width: Kirigami.Units.gridUnit * 25
});
}
function onShowMessageMenu(eventId, author, messageComponentType, plainText, htmlText, selectedText, hoveredLink, isThread) {
function onShowMessageMenu(eventId: string, author, messageComponentType, plainText: string, htmlText: string, selectedText: string, hoveredLink: string, isThread: bool): void {
const contextMenu = messageDelegateContextMenu.createObject(root, {
selectedText: selectedText,
hoveredLink: hoveredLink,

View File

@@ -67,10 +67,14 @@ QQC2.ComboBox {
QQC2.ToolButton {
visible: serverItem.isAddServerDelegate || serverItem.isDeletable
icon.name: serverItem.isAddServerDelegate ? "list-add" : "dialog-close"
text: i18nc("@action:button", "Add new server")
text: serverItem.isAddServerDelegate ? i18nc("@action:button", "Add new server") : i18nc("@action:button", "Remove server")
Accessible.name: text
display: QQC2.AbstractButton.IconOnly
QQC2.ToolTip.text: text
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
onClicked: {
if (root.currentIndex === serverItem.index && serverItem.isDeletable) {
root.currentIndex = 0;
@@ -173,7 +177,7 @@ QQC2.ComboBox {
Connections {
target: serverListModel
function onServerCheckComplete(url, valid) {
function onServerCheckComplete(url: string, valid: bool): void {
if (url == serverUrlField.text && valid) {
serverUrlField.isValidServer = true;
}

View File

@@ -23,8 +23,6 @@ Kirigami.Action {
text: i18n("Share")
tooltip: i18n("Share the selected media")
visible: false
/**
* This property holds the input data for purpose.
*
@@ -60,7 +58,7 @@ Kirigami.Action {
if (id != root.eventId) {
return;
}
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "ShareDialog"), {
pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "ShareDialog"), {
title: root.text,
index: index,
model: root._instantiator.model

View File

@@ -261,7 +261,7 @@ Kirigami.Dialog {
icon.name: "delete"
icon.color: Kirigami.Theme.negativeTextColor
onTriggered: {
let dialog = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ReasonDialog'), {
let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ReasonDialog'), {
title: i18nc("@title:dialog", "Remove Messages"),
placeholder: i18nc("@info:placeholder", "Reason for removing this user's recent messages"),
actionText: i18nc("@action:button 'Remove' as in 'Remove these messages'", "Remove"),

View File

@@ -236,11 +236,18 @@ void RoomManager::resolveResource(Uri uri, const QString &action)
}
}
void RoomManager::maximizeMedia(int index)
void RoomManager::maximizeMedia(const QString &eventId)
{
if (index < -1 || index > m_mediaMessageFilterModel->rowCount()) {
if (eventId.isEmpty()) {
qWarning() << "Tried to open media for empty event id";
return;
}
const auto index = m_mediaMessageFilterModel->getRowForEventId(eventId);
if (index == -1) {
return;
}
Q_EMIT showMaximizedMedia(index);
}
@@ -264,8 +271,17 @@ void RoomManager::viewEventSource(const QString &eventId)
void RoomManager::viewEventMenu(const QString &eventId, NeoChatRoom *room, NeochatRoomMember *sender, const QString &selectedText, const QString &hoveredLink)
{
const auto &event = **room->findInTimeline(eventId);
if (eventId.isEmpty()) {
qWarning() << "Tried to open event menu with empty event id";
return;
}
const auto it = room->findInTimeline(eventId);
if (it == room->historyEdge()) {
// This is probably a pending event
return;
}
const auto &event = **it;
if (EventHandler::mediaInfo(room, &event).contains("mimeType"_L1)) {
Q_EMIT showFileMenu(eventId,
sender,
@@ -358,19 +374,6 @@ void RoomManager::visitRoom(Room *r, const QString &eventId)
if (m_currentRoom && !m_currentRoom->editCache()->editId().isEmpty()) {
m_currentRoom->editCache()->setEditId({});
}
if (m_currentRoom && !m_currentRoom->isSpace() && m_chatDocumentHandler) {
// We're doing these things here because it is critical that they are switched at the same time
if (m_chatDocumentHandler->document()) {
m_currentRoom->mainCache()->setSavedText(m_chatDocumentHandler->document()->textDocument()->toPlainText());
m_chatDocumentHandler->setRoom(room);
if (room) {
m_chatDocumentHandler->document()->textDocument()->setPlainText(room->mainCache()->savedText());
room->mainCache()->setText(room->mainCache()->savedText());
}
} else {
m_chatDocumentHandler->setRoom(room);
}
}
if (!room) {
setCurrentRoom({});
@@ -393,7 +396,9 @@ void RoomManager::joinRoom(Quotient::Connection *account, const QString &roomAli
// If no one gives us a homeserver suggestion, try the server specified in the alias/id.
// Otherwise joining a remote room not on our homeserver will fail.
if (vias.empty()) {
// This is a hack and we're not supposed to do it. With room ids not containing the server going forward, it won't work anymore for new room versions.
// FIXME: Let's keep it around anyway for now, remove it at some point, though
if (vias.empty() && roomAliasOrId.contains(':'_L1)) {
vias.append(roomAliasOrId.mid(roomAliasOrId.lastIndexOf(':'_L1) + 1));
}
@@ -468,18 +473,6 @@ bool RoomManager::visitNonMatrix(const QUrl &url)
return true;
}
ChatDocumentHandler *RoomManager::chatDocumentHandler() const
{
return m_chatDocumentHandler;
}
void RoomManager::setChatDocumentHandler(ChatDocumentHandler *handler)
{
m_chatDocumentHandler = handler;
m_chatDocumentHandler->setRoom(m_currentRoom);
Q_EMIT chatDocumentHandlerChanged();
}
void RoomManager::setConnection(NeoChatConnection *connection)
{
if (m_connection == connection) {

View File

@@ -11,7 +11,6 @@
#include <Quotient/roommember.h>
#include <Quotient/uriresolver.h>
#include "chatdocumenthandler.h"
#include "enums/messagecomponenttype.h"
#include "enums/messagetype.h"
#include "models/mediamessagefiltermodel.h"
@@ -137,13 +136,6 @@ class RoomManager : public QObject, public UriResolverBase
*/
Q_PROPERTY(bool hasOpenRoom READ hasOpenRoom NOTIFY currentRoomChanged)
/**
* @brief The ChatDocumentHandler for the open room.
*
* @sa ChatDocumentHandler
*/
Q_PROPERTY(ChatDocumentHandler *chatDocumentHandler READ chatDocumentHandler WRITE setChatDocumentHandler NOTIFY chatDocumentHandlerChanged)
public:
virtual ~RoomManager();
static RoomManager &instance();
@@ -212,12 +204,8 @@ public:
/**
* @brief Show a media item maximized.
*
* @param index the index to open the maximize delegate model at. This is the
* index in the MediaMessageFilterModel owned by this RoomManager. A value
* of -1 opens a the default item.
*/
Q_INVOKABLE void maximizeMedia(int index);
Q_INVOKABLE void maximizeMedia(const QString &eventId);
Q_INVOKABLE void maximizeCode(NeochatRoomMember *author, const QDateTime &time, const QString &codeText, const QString &language);
@@ -237,9 +225,6 @@ public:
Q_INVOKABLE void
viewEventMenu(const QString &eventId, NeoChatRoom *room, NeochatRoomMember *sender, const QString &selectedText = {}, const QString &hoveredLink = {});
ChatDocumentHandler *chatDocumentHandler() const;
void setChatDocumentHandler(ChatDocumentHandler *handler);
/**
* @brief Set a URL to be loaded as the initial room.
*/
@@ -342,8 +327,6 @@ Q_SIGNALS:
*/
void showMessage(MessageType::Type messageType, const QString &message);
void chatDocumentHandlerChanged();
void connectionChanged();
void directChatsActiveChanged();
@@ -372,7 +355,6 @@ private:
KConfigGroup m_lastRoomConfig;
KConfigGroup m_lastSpaceConfig;
KConfigGroup m_directChatsConfig;
QPointer<ChatDocumentHandler> m_chatDocumentHandler;
RoomListModel *m_roomListModel;
SortFilterRoomListModel *m_sortFilterRoomListModel;

View File

@@ -5,6 +5,8 @@
#include <QDBusMetaType>
#include <KWindowSystem>
#include "controller.h"
#include "models/roomlistmodel.h"
#include "models/sortfilterroomlistmodel.h"
@@ -86,4 +88,9 @@ void Runner::Run(const QString &id, const QString &actionId)
WindowController::instance().showAndRaiseWindow(QString());
}
void Runner::SetActivationToken(const QString &token)
{
KWindowSystem::setCurrentXdgActivationToken(token);
}
#include "moc_runner.cpp"

View File

@@ -190,6 +190,8 @@ public:
*/
Q_SCRIPTABLE void Run(const QString &id, const QString &actionId);
Q_SCRIPTABLE void SetActivationToken(const QString &token);
Q_SIGNALS:
void roomListModelChanged();

View File

@@ -87,7 +87,7 @@ QQC2.Control {
displayHint: Kirigami.DisplayHint.IconOnly
onTriggered: {
let dialog = (Clipboard.hasImage ? attachDialog : openFileDialog).createObject(applicationWindow().overlay);
let dialog = (Clipboard.hasImage ? attachDialog : openFileDialog).createObject(root.QQC2.Overlay.overlay);
dialog.chosen.connect(path => _private.chatBarCache.attachmentPath = path);
dialog.open();
}
@@ -197,6 +197,22 @@ QQC2.Control {
visible: root.currentRoom.mainCache.replyId.length > 0
sourceComponent: replyPane
}
RowLayout {
visible: replyLoader.visible && !root.currentRoom.mainCache.relationAuthorIsPresent
spacing: Kirigami.Units.smallSpacing
Kirigami.Icon {
source: "help-hint-symbolic"
color: Kirigami.Theme.disabledTextColor
Layout.preferredWidth: Kirigami.Units.iconSizes.small
Layout.preferredHeight: Kirigami.Units.iconSizes.small
}
QQC2.Label {
text: i18nc("@info", "The user you're replying to has left the room, and can't be notified.")
color: Kirigami.Theme.disabledTextColor
}
}
Loader {
id: attachLoader
@@ -382,8 +398,6 @@ QQC2.Control {
implicitHeight: replyComponent.implicitHeight
ReplyComponent {
id: replyComponent
replyEventId: _private.chatBarCache.replyId
replyAuthor: _private.chatBarCache.relationAuthor
replyContentModel: ContentProvider.contentModelForEvent(root.currentRoom, _private.chatBarCache.replyId, true)
Message.maxContentWidth: replyLoader.item.width
@@ -424,7 +438,6 @@ QQC2.Control {
QtObject {
id: _private
property ChatBarCache chatBarCache
onChatBarCacheChanged: documentHandler.chatBarCache = chatBarCache
function postMessage() {
_private.chatBarCache.postMessage();
@@ -486,15 +499,14 @@ QQC2.Control {
ChatDocumentHandler {
id: documentHandler
type: ChatBarType.Room
room: root.currentRoom
document: textField.textDocument
cursorPosition: textField.cursorPosition
selectionStart: textField.selectionStart
selectionEnd: textField.selectionEnd
mentionColor: Kirigami.Theme.linkColor
errorColor: Kirigami.Theme.negativeTextColor
Component.onCompleted: {
RoomManager.chatDocumentHandler = documentHandler;
}
}
Component {

View File

@@ -25,7 +25,7 @@ QQC2.Popup {
Connections {
target: RoomManager
function onCurrentRoomChanged() {
function onCurrentRoomChanged(): void {
root.close();
}
}
@@ -65,7 +65,7 @@ QQC2.Popup {
padding: 2
implicitHeight: Kirigami.Units.gridUnit * 20 + 2 * padding
width: Math.min(contentItem.categoryIconSize * 11 + 2 * padding, applicationWindow().width)
width: Math.min(contentItem.categoryIconSize * 11 + 2 * padding, QQC2.ApplicationWindow.window.width)
contentItem: EmojiPicker {
id: emojiPicker
height: 400

View File

@@ -100,7 +100,7 @@ Kirigami.Page {
}
Connections {
target: selectionTool.selectionArea
function onDoubleClicked() {
function onDoubleClicked(): void {
rootEditorView.crop();
}
}

View File

@@ -20,6 +20,13 @@ FormCard.FormCard {
onToggled: NeoChatConfig.showAllEvents = checked
}
FormCard.FormCheckDelegate {
text: i18nc("@option:check", "Allow sending relations to any event in the timeline")
description: i18nc("@info", "This includes state events")
checked: NeoChatConfig.relateAnyEvent
onToggled: NeoChatConfig.relateAnyEvent = checked
}
FormCard.FormCheckDelegate {
id: roomAccountDataVisibleCheck
text: i18nc("@option:check Enable the matrix 'threads' feature", "Always allow device verification")

View File

@@ -7,7 +7,6 @@ import QtQuick.Window
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kitemmodels
import org.kde.neochat

View File

@@ -7,7 +7,6 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kitemmodels
import org.kde.neochat

View File

@@ -7,7 +7,6 @@ import QtQuick.Window
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kitemmodels
import org.kde.neochat

View File

@@ -22,6 +22,7 @@ target_sources(LibNeoChat PRIVATE
texthandler.cpp
urlhelper.cpp
utils.cpp
enums/chatbartype.h
enums/messagecomponenttype.h
enums/messagetype.h
enums/powerlevel.cpp

View File

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

View File

@@ -15,6 +15,18 @@ using namespace Qt::StringLiterals;
ChatBarCache::ChatBarCache(QObject *parent)
: QObject(parent)
{
if (parent == nullptr) {
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
return;
}
auto room = dynamic_cast<NeoChatRoom *>(parent);
if (room == nullptr) {
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
return;
}
connect(room, &NeoChatRoom::memberLeft, this, &ChatBarCache::relationAuthorIsPresentChanged);
connect(room, &NeoChatRoom::memberJoined, this, &ChatBarCache::relationAuthorIsPresentChanged);
connect(this, &ChatBarCache::relationIdChanged, this, &ChatBarCache::relationAuthorIsPresentChanged);
}
QString ChatBarCache::text() const
@@ -137,6 +149,11 @@ Quotient::RoomMember ChatBarCache::relationAuthor() const
return room->member((*room->findInTimeline(m_relationId))->senderId());
}
bool ChatBarCache::relationAuthorIsPresent() const
{
return relationAuthor().membershipState() == Quotient::Membership::Join;
}
QString ChatBarCache::relationMessage() const
{
if (parent() == nullptr) {

View File

@@ -99,6 +99,13 @@ class ChatBarCache : public QObject
*/
Q_PROPERTY(Quotient::RoomMember relationAuthor READ relationAuthor NOTIFY relationIdChanged)
/**
* @brief If the author for the message being replied to is still present in the room.
*
* @sa Quotient::RoomMember
*/
Q_PROPERTY(bool relationAuthorIsPresent READ relationAuthorIsPresent NOTIFY relationAuthorIsPresentChanged)
/**
* @brief The content of the related message.
*
@@ -153,6 +160,7 @@ public:
void setEditId(const QString &editId);
Quotient::RoomMember relationAuthor() const;
bool relationAuthorIsPresent() const;
QString relationMessage() const;
@@ -196,6 +204,7 @@ Q_SIGNALS:
void threadIdChanged(const QString &oldThreadId, const QString &newThreadId);
void attachmentPathChanged();
void mentionAdded(const QString &mention);
void relationAuthorIsPresentChanged();
private:
QString m_text = QString();

View File

@@ -14,6 +14,7 @@
#include <Sonnet/BackgroundChecker>
#include <Sonnet/Settings>
#include "chatbartype.h"
#include "chatdocumenthandler_logging.h"
#include "eventhandler.h"
@@ -67,7 +68,11 @@ public:
if (!room) {
return;
}
auto mentions = handler->chatBarCache()->mentions();
const auto chatchache = handler->chatBarCache();
if (!chatchache) {
return;
}
auto mentions = chatchache->mentions();
mentions->erase(std::remove_if(mentions->begin(),
mentions->end(),
[this](auto &mention) {
@@ -105,18 +110,6 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
, m_highlighter(new SyntaxHighlighter(this))
, m_completionModel(new CompletionModel(this))
{
connect(this, &ChatDocumentHandler::roomChanged, this, [this]() {
m_completionModel->setRoom(m_room);
static QPointer<NeoChatRoom> previousRoom = nullptr;
if (previousRoom) {
disconnect(m_chatBarCache, &ChatBarCache::textChanged, this, nullptr);
}
previousRoom = m_room;
connect(m_chatBarCache, &ChatBarCache::textChanged, this, [this]() {
int start = completionStartIndex();
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
});
});
connect(this, &ChatDocumentHandler::documentChanged, this, [this]() {
if (!m_document) {
m_highlighter->setDocument(nullptr);
@@ -153,6 +146,20 @@ int ChatDocumentHandler::completionStartIndex() const
return start;
}
ChatBarType::Type ChatDocumentHandler::type() const
{
return m_type;
}
void ChatDocumentHandler::setType(ChatBarType::Type type)
{
if (type == m_type) {
return;
}
m_type = type;
Q_EMIT typeChanged();
}
QQuickTextDocument *ChatDocumentHandler::document() const
{
return m_document;
@@ -198,22 +205,36 @@ void ChatDocumentHandler::setRoom(NeoChatRoom *room)
return;
}
if (m_room && m_type != ChatBarType::None) {
m_room->cacheForType(m_type)->disconnect(this);
if (!m_room->isSpace() && m_document && m_type == ChatBarType::Room) {
m_room->mainCache()->setSavedText(document()->textDocument()->toPlainText());
}
}
m_room = room;
m_completionModel->setRoom(m_room);
if (m_room && m_type != ChatBarType::None) {
connect(m_room->cacheForType(m_type), &ChatBarCache::textChanged, this, [this]() {
int start = completionStartIndex();
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
});
if (!m_room->isSpace() && m_document && m_type == ChatBarType::Room) {
document()->textDocument()->setPlainText(room->mainCache()->savedText());
m_room->mainCache()->setText(room->mainCache()->savedText());
}
}
Q_EMIT roomChanged();
}
ChatBarCache *ChatDocumentHandler::chatBarCache() const
{
return m_chatBarCache;
}
void ChatDocumentHandler::setChatBarCache(ChatBarCache *chatBarCache)
{
if (m_chatBarCache == chatBarCache) {
return;
if (!m_room || m_type == ChatBarType::None) {
return nullptr;
}
m_chatBarCache = chatBarCache;
Q_EMIT chatBarCacheChanged();
return m_room->cacheForType(m_type);
}
void ChatDocumentHandler::complete(int index)
@@ -313,20 +334,20 @@ void ChatDocumentHandler::setSelectionEnd(int position)
QString ChatDocumentHandler::getText() const
{
if (!m_chatBarCache) {
qCWarning(ChatDocumentHandling) << "getText called with m_chatBarCache set to nullptr.";
if (!m_room || m_type == ChatBarType::None) {
qCWarning(ChatDocumentHandling) << "getText called with no ChatBarCache available. ChatBarType: " << m_type << " Room: " << m_room;
return {};
}
return m_chatBarCache->text();
return m_room->cacheForType(m_type)->text();
}
void ChatDocumentHandler::pushMention(const Mention mention) const
{
if (!m_chatBarCache) {
qCWarning(ChatDocumentHandling) << "pushMention called with m_chatBarCache set to nullptr.";
if (!m_room || m_type == ChatBarType::None) {
qCWarning(ChatDocumentHandling) << "pushMention called with no ChatBarCache available. ChatBarType: " << m_type << " Room: " << m_room;
return;
}
m_chatBarCache->mentions()->push_back(mention);
m_room->cacheForType(m_type)->mentions()->push_back(mention);
}
QColor ChatDocumentHandler::mentionColor() const
@@ -365,7 +386,7 @@ void ChatDocumentHandler::updateMentions(QQuickTextDocument *document, const QSt
{
setDocument(document);
if (editId.isEmpty() || !m_chatBarCache || !m_room) {
if (editId.isEmpty() || m_type == ChatBarType::None || !m_room) {
return;
}
@@ -374,7 +395,7 @@ void ChatDocumentHandler::updateMentions(QQuickTextDocument *document, const QSt
// Replaces the mentions that are baked into the HTML but plaintext in the original markdown
const QRegularExpression re(uR"lit(<a\shref="https:\/\/matrix.to\/#\/([\S]*)"\s?>([\S]*)<\/a>)lit"_s);
m_chatBarCache->mentions()->clear();
m_room->cacheForType(m_type)->mentions()->clear();
int linkSize = 0;
auto matches = re.globalMatch(EventHandler::rawMessageBody(*roomMessageEvent));

View File

@@ -9,6 +9,7 @@
#include <QTextCursor>
#include "chatbarcache.h"
#include "enums/chatbartype.h"
#include "models/completionmodel.h"
#include "neochatroom.h"
@@ -62,6 +63,11 @@ class ChatDocumentHandler : public QObject
Q_OBJECT
QML_ELEMENT
/**
* @brief The QQuickTextDocument that is being handled.
*/
Q_PROPERTY(ChatBarType::Type type READ type WRITE setType NOTIFY typeChanged)
/**
* @brief The QQuickTextDocument that is being handled.
*/
@@ -95,11 +101,6 @@ class ChatDocumentHandler : public QObject
*/
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
/**
* @brief The cache for the chat bar the text document is being handled for.
*/
Q_PROPERTY(ChatBarCache *chatBarCache READ chatBarCache WRITE setChatBarCache NOTIFY chatBarCacheChanged)
/**
* @brief The color to highlight user mentions.
*/
@@ -113,6 +114,9 @@ class ChatDocumentHandler : public QObject
public:
explicit ChatDocumentHandler(QObject *parent = nullptr);
ChatBarType::Type type() const;
void setType(ChatBarType::Type type);
[[nodiscard]] QQuickTextDocument *document() const;
void setDocument(QQuickTextDocument *document);
@@ -128,8 +132,7 @@ public:
[[nodiscard]] NeoChatRoom *room() const;
void setRoom(NeoChatRoom *room);
[[nodiscard]] ChatBarCache *chatBarCache() const;
void setChatBarCache(ChatBarCache *chatBarCache);
ChatBarCache *chatBarCache() const;
Q_INVOKABLE void complete(int index);
@@ -147,10 +150,10 @@ public:
Q_INVOKABLE void updateMentions(QQuickTextDocument *document, const QString &editId);
Q_SIGNALS:
void typeChanged();
void documentChanged();
void cursorPositionChanged();
void roomChanged();
void chatBarCacheChanged();
void selectionStartChanged();
void selectionEndChanged();
void errorColorChanged();
@@ -159,10 +162,10 @@ Q_SIGNALS:
private:
int completionStartIndex() const;
ChatBarType::Type m_type = ChatBarType::None;
QPointer<QQuickTextDocument> m_document;
QPointer<NeoChatRoom> m_room;
QPointer<ChatBarCache> m_chatBarCache;
QColor m_mentionColor;
QColor m_errorColor;

View File

@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QObject>
#include <QQmlEngine>
/**
* @class ChatBarType
*
* This class is designed to define the ChatBarType enumeration.
*/
class ChatBarType : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
public:
/**
* @brief The type of chatbar.
*/
enum Type {
Room = 0, /**< A standard room chatbar for creating new messages. */
Edit, /**< A chatbar for editing an existing message. */
Thread, /**< A chatbar for creating a new threaded message. */
None, /**< Undefined. */
};
Q_ENUM(Type);
};

View File

@@ -70,13 +70,23 @@ public:
*
* @param event the event to return a type for.
*
* @param isInReply whether this event is to be treated like a replied-to event (i.e., a basic text fallback should be shown if no other type is used)
*
* @sa Type
*/
static Type typeForEvent(const Quotient::RoomEvent &event)
static Type typeForEvent(const Quotient::RoomEvent &event, bool isInReply = false)
{
using namespace Quotient;
if (event.isRedacted()) {
return MessageComponentType::Text;
}
if (const auto e = eventCast<const RoomMessageEvent>(&event)) {
if (e->rawMsgtype() == u"m.key.verification.request"_s) {
return MessageComponentType::Verification;
}
switch (e->msgtype()) {
case MessageEventType::Emote:
return MessageComponentType::Text;
@@ -103,7 +113,8 @@ public:
if (event.matrixType() == u"org.matrix.msc3672.beacon_info"_s) {
return MessageComponentType::LiveLocation;
}
return MessageComponentType::Other;
// In the (unlikely) case that this is a reply to a state event, we do want to show something
return isInReply ? MessageComponentType::Text : MessageComponentType::Other;
}
if (is<const EncryptedEvent>(event)) {
return MessageComponentType::Encrypted;
@@ -116,7 +127,8 @@ public:
return MessageComponentType::Poll;
}
return MessageComponentType::Other;
// In the (unlikely) case that this is a reply to an unusual event, we do want to show something
return isInReply ? MessageComponentType::Text : MessageComponentType::Other;
}
/**

View File

@@ -198,6 +198,10 @@ bool EventHandler::isHidden(const NeoChatRoom *room, const Quotient::RoomEvent *
Qt::TextFormat EventHandler::messageBodyInputFormat(const Quotient::RoomMessageEvent &event)
{
if (event.isRedacted() && !event.isStateEvent()) {
return Qt::RichText;
}
if (event.mimeType().name() == "text/plain"_L1) {
return Qt::PlainText;
} else {
@@ -207,6 +211,11 @@ Qt::TextFormat EventHandler::messageBodyInputFormat(const Quotient::RoomMessageE
QString EventHandler::rawMessageBody(const Quotient::RoomMessageEvent &event)
{
if (event.isRedacted() && !event.isStateEvent()) {
auto reason = event.redactedBecause()->reason();
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>") : i18n("<i>[This message was deleted: %1]</i>", reason.toHtmlEscaped());
}
QString body;
if (event.has<EventContent::FileContent>()) {
@@ -448,6 +457,12 @@ QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent
[](const PollStartEvent &e) {
return e.question();
},
[](const EncryptedEvent &) {
return i18nc("@info In room list", "Encrypted event");
},
[](const ReactionEvent &e) {
return i18nc("[user] reacted with <emoji>", "reacted with %1", e.key());
},
i18n("Unknown event"));
}

View File

@@ -7,12 +7,12 @@
struct MessageComponent {
MessageComponentType::Type type = MessageComponentType::Other;
QString content;
QString display;
QVariantMap attributes;
bool operator==(const MessageComponent &right) const
{
return type == right.type && content == right.content && attributes == right.attributes;
return type == right.type && display == right.display && attributes == right.attributes;
}
bool isEmpty() const

View File

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

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