Compare commits

..

418 Commits

Author SHA1 Message Date
Joshua Goins
508c2bee46 Don't show "Hide Image" button for really tiny images
Otherwise, it looks really buggy.
2026-01-14 20:42:47 -05:00
l10n daemon script
e1bbbfe4fd GIT_SILENT Sync po/docbooks with svn 2026-01-14 01:44:13 +00:00
Kai Uwe Broulik
7a2211f8e0 RoomPage: Fix selected text and hovered link in context menu
They were not forwarded to the menu.
Also, "isThread" argument is no longer there in the signal.
2026-01-13 17:37:30 -05:00
Azhar Momin
4155e9116a Fix some runtime qml warnings 2026-01-13 17:35:02 -05:00
Azhar Momin
a989ef42b2 Fix pushDialogLayer failing in DelegateContextMenu 2026-01-13 17:35:02 -05:00
Joshua Goins
2babf44b28 Grab the correct room in MessageModel::data
Not all events that are processed in this model belong to the room, e.g.
searching through multiple room versions. Now the model finds the
correct room based on the reported room in the event.

This fixes searching through upgraded rooms, and unblocks searching
through multiple rooms in the future.
2026-01-13 09:25:49 -05:00
Joshua Goins
89e42dbc53 Decrypt when downloading single events from the server
This fixes issues like not being able to view pinned messages in
encrypted rooms.
2026-01-13 07:52:07 -05:00
l10n daemon script
ba57570dbf GIT_SILENT Sync po/docbooks with svn 2026-01-13 09:49:08 +00:00
Joshua Goins
1a500a087b Separate priviled members list, and more useful permissions
Having both the member list and permission controls is troublesome, and
scales the larger your moderation team is. We eventually may want to
manage banned/muted users too so I think it warrants having a new page.
I also moved the search field to the top so it's more accessible.

As for permissions, I tried to improve the UX generally while not
changing it too heavily. First the easy change is to the text, hopefully
the sections should be clearer (especially for "state" events.) The
bigger change here is the new sections, I tried to make it more useful
and organized. Additionally, I added more permissions like sharing live
locations and polls so they're more easily configurable.

One other change is that permissions are visible regardless of whether
you can set them or not, matching Element's behavior.
2026-01-11 21:14:15 -05:00
l10n daemon script
e54955ec0c GIT_SILENT Sync po/docbooks with svn 2026-01-12 01:55:14 +00:00
Joshua Goins
8608b3b62e Remove extra arguments in StateComponent's viewEventMenu call 2026-01-11 18:30:02 -05:00
Joshua Goins
fea0cfbf4e Fix opening message menus for popup windows
We were previously assuming that we always want to parent these menus to
RoomPage, but that only exists on the main window. If you tried to open
the menu for say - the search window - then it would confusingly still
open on the main menu.

Thankfully the way to fix this is simple, by passing a parent QtObject
around.
2026-01-11 18:30:02 -05:00
Joshua Goins
5b6e5a25e5 Allow opening message menus for out-of-room events
These are more common than we thought, good examples are pinned or
searched messages - which are not going to be in the room's history
unless you happen to have them loaded. But currently our message menu
infrastructure expects them to be, since its looked up by the room +
event ID.

To fix this is simple, we now move the job of finding the event to the
caller which may use a model instead. I didn't fix all existing
call-sites yet, mainly the message menu opening one since that was the
most obvious bug. But this opens up the door for other assumptions about
room history to be fixed too.

I had to do a bit of C++ re-jiggering in order to expose useful
functions to QML.
2026-01-11 18:30:02 -05:00
Joshua Goins
58b47b8711 Rename indexforEventId to indexForEventId
This is a simple change to ensure its properly camelCase.
2026-01-11 18:30:02 -05:00
Joshua Goins
b45967508c Fix reply colors being broken if you're faster than the server
This is that bug that causes reply colors to be white, and this error to
print in the log:

qrc:/qt/qml/org/kde/neochat/messagecontent/ReplyComponent.qml:41: TypeError: Cannot read property 'color' of null

The reason why this happens is inside of EventMessageContentModel, it
needs to be able to find the relevant event in the room to fetch the
room member (and then their color.) Dependent on many variables to
align, this can happen easily if you are faster than your server giving
you said events.

But this is an easy fix, we obviously get the event afterwards and just
need to re-evaluate the the author property. I also made sure it falls
back to some color instead of white, which will also quiet the error.
2026-01-11 16:27:44 -05:00
Lorenz Wildberg
2ec1fa92fa fix bug: room settings don't open 2026-01-11 16:08:57 +01:00
l10n daemon script
7602a56594 GIT_SILENT Sync po/docbooks with svn 2026-01-11 01:44:50 +00:00
Joshua Goins
e8da02be7d Add dialog to see who has read this message
You were previously relegated to looking at any avatars or a buggy
tooltip, but suffer no longer! If you tap on a message's read marker, a
dialog will pop up listing the users who have read it.

Uou can also view their profiles from here, etc.
2026-01-10 13:33:23 -05:00
Veres Károly
71c84be4b4 Null check decoded messages in loadPinnedMessage
`decryptMessage` returns null if it fails to decode the passed message. This value
then got fed into `EventHandler::richBody` which logged a warning and cleared `m_pinnedMessage`.
If we instead retain the value as an `EncryptedEvent`, the UI will pin the encrypted event
placeholder instead of hiding the existence of pins.
2026-01-10 14:48:09 +00:00
Veres Károly
b684fb451d Null check pinned messages after decryption when filling PinnedMessageModel.
`room()->decryptMessage()` returns null if the message fails to decode. Since elements of `m_pinnedEvents`
get directly dereferenced in getEventForIndex, storing null values leads to a segfault.
In this case we should retain the EncryptedEvent to let the UI report the error.
2026-01-10 14:48:09 +00:00
Joshua Goins
3a416990ca Make clicking room list section headers more reliable
ListSectionHeader itself is an ItemDelegate, which eats up input events.
We can work around this by also listening to onClicked there too.
2026-01-09 23:01:11 -05:00
Joshua Goins
9b7d16973d Improve the Advanced Room settings page more
I added a separator, made it so the "Copy Room ID to clipboard" button
is always shown, and made it more obvious that "Upgrade Room" prompts
you first.
2026-01-09 23:01:02 -05:00
Joshua Goins
dd59e63574 Refer to user's power level as "Power Level", not "Role"
This is a more accurate name for this.
2026-01-09 23:00:52 -05:00
Joshua Goins
7f7e48dfd4 Fix undefined references in UserDetailDialog
This can happen when viewing non-room profiles.
2026-01-09 23:00:52 -05:00
Joshua Goins
7119132e4b Add a way to add private notes for users on their profile
This is a common feature in other chat applications (like Discord) as a
way to keep track of important information. While there isn't a standard
API for this, we can use account data to store notes.
2026-01-09 21:06:10 -05:00
l10n daemon script
6001cc6d4f GIT_SILENT Sync po/docbooks with svn 2026-01-10 01:53:22 +00:00
l10n daemon script
989f1ad020 GIT_SILENT Sync po/docbooks with svn 2026-01-09 01:43:14 +00:00
l10n daemon script
a119563ba7 GIT_SILENT Sync po/docbooks with svn 2026-01-08 01:44:03 +00:00
Joshua Goins
5e473aae0a Avoid assert check when switching to main profile
This can happen if the user doesn't have an avatar set, as there's an
assert check inside of makeMediaUrl. We can avoid this by checking if
the string is empty before calling that.
2026-01-07 17:05:13 -05:00
Joshua Goins
aa0272a02d Remove some duplicate buttons from the room sidebar
The favorites action doesn't really deserve space here IMO, and that's a
pretty list-specific action you're more likely to do elsewhere.

For the Pinned Messages action that can be opened at the top on desktop,
but we still need it on mobile. So now it's selectively hidden based on
that.
2026-01-07 17:04:23 -05:00
Joshua Goins
fc6f345036 Small notification improvements
Changed a check to use isDirectChat (which is a clearer indication of
what we want.) I also made sure not to show the account name if you only
have one, since that's just useless noise.
2026-01-07 15:42:19 -05:00
l10n daemon script
4fa011c266 GIT_SILENT Sync po/docbooks with svn 2026-01-07 01:44:45 +00:00
l10n daemon script
44ebcc6b9a GIT_SILENT Sync po/docbooks with svn 2026-01-06 01:51:54 +00:00
Joshua Goins
32cc5d4e48 Allow switching between viewing main profiles and room profiles
Since Matrix allows users to have different profiles on a room and
"outside of room" level, it's nice to have the ability to switch between
them on-the-fly.
2026-01-05 09:52:31 -05:00
l10n daemon script
bbc5a92f62 GIT_SILENT Sync po/docbooks with svn 2026-01-05 01:51:29 +00:00
Joshua Goins
4656bf4ee7 Rename "Explore Rooms" dialog to just "Explore"
There's a bit of a discrepancy here, that directory will list both rooms
and spaces. I also removed duplicated titles where applicable.
2026-01-04 17:41:55 -05:00
Azhar Momin
5f7967363f Fix notification count refresh for low-priority and mentions-only rooms 2026-01-04 17:00:52 -05:00
Joshua Goins
a02a04d966 Fix icons on Windows
KirigamiApp currently calls KIconTheme::initTheme too late for Windows,
as a workaround we can go back to calling it ourselves.
2026-01-04 13:48:30 +00:00
l10n daemon script
dffec2f0d5 GIT_SILENT Sync po/docbooks with svn 2026-01-04 01:51:40 +00:00
Veres Károly
68b00b9fc5 Extract the space selection logic from setCurrentRoom and use it for setting lastRoomConfig too.
If a setCurrentRoom call changed the active space at the end of its execution, the new room's ID ended up still being written to the old space's lastRoomConfig.

By extracting this space selection logic into a helper function, we can now calculate this value earlier and use it as the space id when writing lastRoomConfig.
2026-01-03 07:42:56 -05:00
Heiko Becker
aa823c7629 GIT_SILENT Update Appstream for new release
(cherry picked from commit 321561fd89)
2026-01-03 12:14:48 +01:00
l10n daemon script
73b82b69d8 GIT_SILENT Sync po/docbooks with svn 2026-01-03 01:41:59 +00:00
Nate Graham
bd0588ca99 Improve hamburger menu button
- Open the menu right beneath the button
- Use pressed state for the button while the menu is open
- Close the menu when clicking the button again
- Hide the tooltip while the menu is open
2026-01-02 08:07:32 -07:00
l10n daemon script
e5b7601dac GIT_SILENT Sync po/docbooks with svn 2026-01-02 01:51:12 +00:00
l10n daemon script
f4ac3346f5 GIT_SILENT Sync po/docbooks with svn 2026-01-01 01:40:42 +00:00
Azhar Momin
58ea229b67 Add a button to cycle through unread highlights
BUG: 465095
2025-12-31 13:57:35 +00:00
l10n daemon script
fd44ff972a GIT_SILENT Sync po/docbooks with svn 2025-12-31 01:51:00 +00:00
l10n daemon script
c5eb63da15 GIT_SILENT Sync po/docbooks with svn 2025-12-30 01:49:46 +00:00
l10n daemon script
79acceb29a 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-12-30 01:34:45 +00:00
l10n daemon script
8022a7aae5 GIT_SILENT Sync po/docbooks with svn 2025-12-29 01:42:10 +00:00
l10n daemon script
d26862dfb6 GIT_SILENT Sync po/docbooks with svn 2025-12-28 01:39:52 +00:00
l10n daemon script
c25c095c1b GIT_SILENT Sync po/docbooks with svn 2025-12-27 01:40:07 +00:00
Tobias Fella
e09e4fb7dc Modernize PropertyChanges 2025-12-26 18:58:28 +00:00
Tobias Fella
657c8a0dcd Fixes for v12 power levels 2025-12-26 18:30:46 +00:00
l10n daemon script
82f54b4f2c GIT_SILENT Sync po/docbooks with svn 2025-12-26 01:41:25 +00:00
Joshua Goins
9e7cd0eb09 Overhaul user profile UI
Our previous iteration is hitting some limitations as the power of
profiles on Matrix grows. For example, where do we put common rooms or
extra profile fields?

I re-arranged everything to group similar actions together - instead of
throwing it all into one big list. We basically trade vertical for
horizontal space, and gives us more headroom for extra fields when we
want to add more.
2025-12-25 12:34:21 -05:00
Joshua Goins
9bdb9b6a7c Improve wording around the various link preview options
In my opinion, the significance of these options was still not super
clear. It wasn't obvious that the setting under Appearance will disable
them globally, and that the per-room option was discoverable.

Another change was using the term "Link preview" instead of "URL
preview", which is more common verbiage in chat applications other than
Element.
2025-12-25 12:33:00 -05:00
Joshua Goins
fa4a93140d Fix not being able to view the DM's profile
This button was broken for a while, so now that's fixed.
2025-12-24 18:46:06 -05:00
Joshua Goins
a41966ecd7 Adjust text for the ignore user option in invitiations
We use the "ignore" terminology elsewhere, so saying "block" here is
kinda weird. I also made it clearer that this also rejects the invite,
in case that wasn't obvious.
2025-12-24 18:43:12 -05:00
Joshua Goins
edcf81f0da Add a few ways to open profile for inviting users
I recieved a few invites from unknown users, and I had no idea who they
were/came from. I could check their profile to see if we had any rooms
in common, but there was no way to actually check their profile!

This makes it possible to open their profile by clicking their avatar
(like elsewhere) but also adds a button to make it even more obvious.
2025-12-24 18:42:26 -05:00
l10n daemon script
0a98c732e0 GIT_SILENT Sync po/docbooks with svn 2025-12-24 01:50:27 +00:00
l10n daemon script
d734e114a6 GIT_SILENT Sync po/docbooks with svn 2025-12-23 01:41:20 +00:00
Joshua Goins
946e505a24 Add missing supportsMatrixSpecVersion QML function
This was rebased out or something in 197d7ce8e8
2025-12-22 16:31:42 -05:00
Joshua Goins
092980c0ff Shorten room subtitles for direct chats
Direct chats by their very nature is between two users, and you usually
can keep track of who said what. There's no point in including the
sender's name here, and most other chat applications exclude it for this
reason (including Element X.)
2025-12-22 15:02:24 -05:00
Joshua Goins
6fcb1cc1e3 Fix assumption about unstable feature reporting
The key can be in the unstable features list, but it can be false. This
stops some features showing up and hitting API that isn't actually
implemented.
2025-12-22 07:26:27 -05:00
Olivier De Cannière
c572480d10 cmake: Explicitly link in neochatplugin and use qt_* cmake commands
The QML code of the various QML modules is compiled to C++ by
qmlcachegen and then compiled into the final binary. Unfortunately, in
the current setup, the plugin for the org.kde.neochat QML module is not
linked into the app because qmlimportscanner can't find usages of it. No
generated C++ code for that module is then run and the QML is
interpreted from its bytecode instead.

This is likely due to the project not following recommended QML
structure and because it is not using qt_* variants of certain cmake
commands. It should not be necessary to link against the plugins
manually.

As a quick fix, link against the plugin explicitly and use qt_*
variants of cmake commands to help qmlimportscanner and to pick up the
existing C++ code.
2025-12-22 11:11:35 +01:00
Joshua Goins
531df7a3b2 Send beautiful red ❤️'s when quick reacting
I wondered for a while (and could tell) when people were using NeoChat
because they would react with cold, monochrome hearts. Let's add more
color to our world!
2025-12-21 13:57:59 -05:00
Veres Károly
706f1f7836 Fix missing escape sequence in /shrug command
Before the fix, the upper arm _ characters in the command's output would be parsed as Markdown italic formatting around the (ツ).
2025-12-21 15:03:00 +01:00
l10n daemon script
45c5806c5a GIT_SILENT Sync po/docbooks with svn 2025-12-19 01:42:17 +00:00
l10n daemon script
abf37c90cb GIT_SILENT Sync po/docbooks with svn 2025-12-18 01:41:35 +00:00
Jimmy Bergström
5d9508b165 Hide quick format bar when selection is cleared
I noticed a bit of an annoying behavior with the quick format bar. Whenever I make a typo when writing a message I usually go back with a ctrl+shift+arrow selection to correct it, and that of course triggers the bar, but then there wasn't any obvious way to make it go away (other than pressing backspace or delete, [ChatBar.qml#L339](https://invent.kde.org/network/neochat/-/blob/master/src/chatbar/ChatBar.qml?ref_type=heads#L339)), so it'd always be stuck visible until I clicked somewhere with my mouse, which I found somewhat annoying.

This intends to fix that, by hiding it whenever the selection is cleared, which seems like a reasonable and expected behavior to me. I would also be okay with making it dismissable with escape, if this current suggestion has some unintended side-effect that I've missed.

I also think that this should in practice "solve" this bug https://bugs.kde.org/show_bug.cgi?id=511590 but I didn't link it directly as their suggested solution is smarter placement of the bar.
2025-12-17 21:33:08 +00:00
l10n daemon script
f5a652f7a1 GIT_SILENT Sync po/docbooks with svn 2025-12-16 14:25:07 +00:00
Paul Brown
f165cb1f10 Removed list of supporters, as we now have a less annoying way of showing them on the apps website. 2025-12-14 16:41:47 +00:00
l10n daemon script
11132c3e89 GIT_SILENT Sync po/docbooks with svn 2025-12-14 01:40:32 +00:00
l10n daemon script
089ab2009a GIT_SILENT Sync po/docbooks with svn 2025-12-13 01:44:24 +00:00
l10n daemon script
6ebedc5dfc GIT_SILENT Sync po/docbooks with svn 2025-12-12 01:42:29 +00:00
Nicolas Fella
e5b2fb7316 Guard against currentIndex being -1 in NeochatMaximizeComponent
Sometimes the ListView's currentIndex is -1, which results in us
querying data() for an invalid index, causing an assert in Qt model
code

BUG: 513104
2025-12-11 18:01:19 +01:00
l10n daemon script
5da3aff607 GIT_SILENT Sync po/docbooks with svn 2025-12-10 01:47:45 +00:00
l10n daemon script
3fa46d52fe GIT_SILENT Sync po/docbooks with svn 2025-12-09 01:41:23 +00:00
Justin Zobel
ece9811d82 CI - Flatpak - Remove Kirigami Addons as it is now in the runtime 2025-12-08 19:55:46 +10:30
Justin Zobel
b543b1c2f9 CI - Flatpak - Use -DCMAKE_POLICY_VERSION_MINIMUM=3.5 for olm 2025-12-08 19:55:46 +10:30
Justin Zobel
5065bccb64 CI - Flatpak - Fix broken sha256sum 2025-12-08 19:55:46 +10:30
Justin Zobel
10c3125668 CI - Flatpak - Fix broken sha256sums 2025-12-08 19:55:46 +10:30
Justin Zobel
73019cfea2 CI - Flatpak - Update Runtime to 6.10 2025-12-08 19:55:46 +10:30
Justin Zobel
4bc6a88acf CI - Flatpak - Update Versions 2025-12-08 19:55:46 +10:30
l10n daemon script
56a49cfc50 GIT_SILENT Sync po/docbooks with svn 2025-12-08 01:41:51 +00:00
Nate Graham
c50380b448 Tell room page header message to fill the width
Otherwise it scrunches up as small as possible and breaks the layout.
2025-12-07 09:27:58 -05:00
l10n daemon script
673a8c8bc8 GIT_SILENT Sync po/docbooks with svn 2025-12-07 01:45:09 +00:00
l10n daemon script
c298c348a4 GIT_SILENT Sync po/docbooks with svn 2025-12-04 01:45:11 +00:00
Heiko Becker
2fcd535aeb GIT_SILENT Update Appstream for new release
(cherry picked from commit 0e4b52ee62)
2025-12-04 00:20:05 +01:00
l10n daemon script
8f5e68de5d GIT_SILENT Sync po/docbooks with svn 2025-12-03 01:45:04 +00:00
l10n daemon script
5fe36a9057 GIT_SILENT Sync po/docbooks with svn 2025-12-02 01:41:01 +00:00
l10n daemon script
8d8c6444e0 GIT_SILENT Sync po/docbooks with svn 2025-12-01 01:44:51 +00:00
Joshua Goins
03d5955c8d Add support for reporting rooms and users
This was added in recent Matrix versions, and a desperately needed
feature.
2025-11-30 14:01:13 -05:00
Joshua Goins
197d7ce8e8 Add a new function to check supported Matrix spec in QML 2025-11-30 14:01:13 -05:00
l10n daemon script
efbf6d96d4 GIT_SILENT Sync po/docbooks with svn 2025-11-30 01:45:44 +00:00
l10n daemon script
b8cd3c69c2 GIT_SILENT Sync po/docbooks with svn 2025-11-29 01:42:47 +00:00
renner 03
1da24191f0 Fix krunner integration with Flatpak 2025-11-28 09:38:51 -05:00
l10n daemon script
2ecc567792 GIT_SILENT Sync po/docbooks with svn 2025-11-28 01:41:47 +00:00
l10n daemon script
66e1fe067d GIT_SILENT Sync po/docbooks with svn 2025-11-27 01:43:17 +00:00
Nate Graham
d08f014def Rephrase a British English wording for the base string
"X has put Y out of the room" is a British English style sentence. Which
is fine, but I'm using en_US, not en_UK.

Re-phrase this to sound a bit more American (which is the linguistic
style we use for the base strings), and then count on the en_UK
translators bringing back the old phrasing for en_UK.
2025-11-25 22:15:45 -07:00
l10n daemon script
63941d6685 GIT_SILENT Sync po/docbooks with svn 2025-11-26 01:41:17 +00:00
Tobias Fella
38523c97c5 Implement drag&drop support for flatpaks
BUG: 495552
2025-11-25 10:27:38 +00:00
l10n daemon script
65c6f4c1d3 GIT_SILENT Sync po/docbooks with svn 2025-11-25 01:41:04 +00:00
Heiko Becker
0cb3fd32f4 Drop unused dependencies
Both KF6Crash and KF6IconThemes aren't used anymore after porting to
KirigamiApp in eab45e761a.
2025-11-24 21:51:55 +01:00
l10n daemon script
b02cb0157e GIT_SILENT Sync po/docbooks with svn 2025-11-24 01:42:36 +00:00
l10n daemon script
ca163fc169 GIT_SILENT Sync po/docbooks with svn 2025-11-23 01:43:18 +00:00
l10n daemon script
5687eb33e1 GIT_SILENT Sync po/docbooks with svn 2025-11-21 01:43:31 +00:00
l10n daemon script
fc795e50b3 GIT_SILENT Sync po/docbooks with svn 2025-11-20 01:41:22 +00:00
l10n daemon script
c4adfe7939 GIT_SILENT Sync po/docbooks with svn 2025-11-19 01:39:53 +00:00
l10n daemon script
ce96232bbd GIT_SILENT Sync po/docbooks with svn 2025-11-18 02:00:53 +00:00
l10n daemon script
95653cf3f7 GIT_SILENT Sync po/docbooks with svn 2025-11-17 13:25:28 +00:00
l10n daemon script
0c11cd331f GIT_SILENT Sync po/docbooks with svn 2025-11-17 01:40:07 +00:00
Tobias Fella
3c8ca0d421 Simplify key backup unlocking
Replaces the separate text fields for security keys and backup passphrases with a single on; internally, both methods are then tried.
2025-11-16 14:21:26 +00:00
l10n daemon script
52f71d5c55 GIT_SILENT Sync po/docbooks with svn 2025-11-16 01:40:16 +00:00
l10n daemon script
00f7dd5175 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-11-16 01:34:16 +00:00
l10n daemon script
52da8415e9 GIT_SILENT Sync po/docbooks with svn 2025-11-15 01:41:08 +00:00
l10n daemon script
953e4cd792 GIT_SILENT Sync po/docbooks with svn 2025-11-14 01:41:28 +00:00
Tobias Fella
ee4b1578b1 Consider highlights when determining whether a room can be marked as read
BUG: 501080
2025-11-13 20:11:06 +00:00
Paul Brown
ae24424a32 Adding anthropy@mastodon.derg.nz to list of supporters 2025-11-13 19:32:43 +00:00
l10n daemon script
51be282824 GIT_SILENT Sync po/docbooks with svn 2025-11-13 01:42:02 +00:00
Tobias Fella
c539dfc352 Fix crash when poll answer has fewer selections than possible
BUG: 511909
2025-11-12 12:29:53 +00:00
Carl Schwan
fe734206df Add Thibault Molleman to the donnors 2025-11-12 11:14:38 +01:00
l10n daemon script
79a49165f1 GIT_SILENT Sync po/docbooks with svn 2025-11-11 01:51:20 +00:00
Tobias Fella
ada5eef088 Remove outdated flatpak font config 2025-11-10 17:27:25 +00:00
Tobias Fella
9cc5778126 Remove secret backup feature flag
There isn't really a point in hiding this behind a flag and it just makes the process even more confusing
2025-11-10 18:04:50 +01:00
l10n daemon script
b129805f94 GIT_SILENT Sync po/docbooks with svn 2025-11-10 01:44:58 +00:00
Joshua Goins
887865c0aa Improve invited room counting
I didn't realize when redoing the tooltip for DMs that directChatInvites
was a boolean, not an integer type. Now it's changed to an integer type,
which fixes the DM invite count.

NeoChat apparently didn't count normal room invites until now either, so
now Home is highlighted in that case. Now it should be harder to miss
these kinds of invites.
2025-11-09 12:03:17 -05:00
Joshua Goins
1336194cf4 Don't send an erroneous SetTypingJob on start-up
You can see this while starting NeoChat, as it always fails because
localMember() isn't valid at this point. This is called during the
ChatBar setup as we're setting up the text, which also happens during
room switch.
2025-11-09 10:55:05 -05:00
Joshua Goins
5a7ae3563e Fix alignment on the room invitation page
I forgot to horizontally center the room alias label, so it was usually
stuck to the left-side. I also don't know why I put the room alias on
top and the room name on the bottom, so those are swapped now too.
2025-11-09 10:54:54 -05:00
Joshua Goins
7c56e24fdc Manually update badge count in initActiveConnection
Without doing this, it's possible to start NeoChat and have no badge
count despite having notable notifications. The reason for this is if
badgeNotificationCount was updated and changed before this function
call, and a call to refreshBadgeNotificationCount doesn't emit the
changed signal.

This is easy to work around by calling updateBadgeNotificationCount
ourselves.
2025-11-09 10:19:55 -05:00
Joshua Goins
e6b9abeca3 Account for pending invites in the badge notification count 2025-11-09 10:18:28 -05:00
l10n daemon script
1b45784a88 GIT_SILENT Sync po/docbooks with svn 2025-11-09 01:48:27 +00:00
Albert Astals Cid
a12b0d5282 GIT_SILENT Upgrade release service version to 26.03.70. 2025-11-06 18:29:06 +01:00
Paul Brown
a6d75f2ff5 Adding Joshua Strobl to list of supporters 2025-11-06 17:01:19 +00:00
l10n daemon script
b8f7f33b9a GIT_SILENT Sync po/docbooks with svn 2025-11-06 16:14:20 +00:00
l10n daemon script
273d962707 GIT_SILENT Sync po/docbooks with svn 2025-11-06 01:40:42 +00:00
Paul Brown
5e8b44fea6 Adding Akseli as supporter 2025-11-05 21:56:17 +00:00
l10n daemon script
be9e2ec7d0 GIT_SILENT Sync po/docbooks with svn 2025-11-05 01:48:44 +00:00
l10n daemon script
ee042cc1a2 GIT_SILENT Sync po/docbooks with svn 2025-11-04 01:47:11 +00:00
Kristen McWilliam
25c0bc131a feat: add font size scaling setting
Adds an option in Appearance Settings to adjust font size scaling,
improving accessibility and user customization.
2025-11-03 10:18:23 -05:00
Paul Brown
7def8c066c Added anonymous donot to list of supporters 2025-11-03 12:50:59 +00:00
l10n daemon script
1ddaf37e52 GIT_SILENT Sync po/docbooks with svn 2025-11-02 01:39:31 +00:00
Heiko Becker
40694f502a Fix version in appstream file
GIT_SILENT
2025-11-01 20:01:46 +01:00
l10n daemon script
d9f4a0a032 GIT_SILENT Sync po/docbooks with svn 2025-11-01 01:44:57 +00:00
l10n daemon script
5e392f3101 GIT_SILENT Sync po/docbooks with svn 2025-10-31 01:39:58 +00:00
Heiko Becker
ce1ac6128e GIT_SILENT Update Appstream for new release
(cherry picked from commit c6fa5a10dd)
2025-10-31 01:23:25 +01:00
l10n daemon script
a9d08a6ee2 GIT_SILENT Sync po/docbooks with svn 2025-10-30 01:41:27 +00:00
Joshua Goins
24d4829ba9 Clarify what is "recent activity" in our room list ordering settings
As seen in the bug report, this setting is a bit confusing. First, it
refers to "message activity" but in reality it does take into account
all events. This is fine in my opinion, so I clarified that point.

Another thing is that it wasn't clear that timeline visibility settings
currently affect the sorting, so I added a tip about that.

Finally I wasn't happy with these two options being called "Activity"
so the old "Activity" setting is now called "Importance". The "Last
Message Activity" setting is now called "Newest Events".

BUG: 508480
FIXED-IN: 25.12.0
2025-10-29 18:18:53 -04:00
l10n daemon script
a121c39b6e GIT_SILENT Sync po/docbooks with svn 2025-10-29 01:41:34 +00:00
Joshua Goins
f009420c20 Improve the design of the room notifications settings
Now it has icons that match what we have in the room menu, and the
duplicate header text is removed.
2025-10-28 12:27:23 -04:00
Joshua Goins
069e0d8f16 Add informational Keyboard Shortcuts settings page
While we don't have customizable keyboard shortcuts (yet!) we have been
asked before for some way to view all of NeoChat's keyboard shortcuts.
2025-10-28 10:29:55 -04:00
Joshua Goins
1f1db11197 Fix plugin on LocationsPage
This lives in the base NeoChat QML module, not the libneochat one.
2025-10-28 09:17:18 -04:00
Joshua Goins
be92e56c3a Show less scary icon for neutral reasons when cancelling verification
For example, accepting a verification on another device shows a giant
red security icon which isn't really suitable. I chucked the
dialog-information icon in for some of the neutral-sounding messages so
this dialog can be a little less intimidating.

BUG: 510421
FIXED-IN: 24.08.3
2025-10-28 09:16:17 -04:00
l10n daemon script
d1fc426513 GIT_SILENT Sync po/docbooks with svn 2025-10-28 01:41:19 +00:00
Joshua Goins
c778ba8b24 Fix jump to previous/next unread room shortcuts
These were dependent on a renamed property and ended up breaking, oops.
2025-10-27 18:09:00 -04:00
Joshua Goins
07fb3160eb Add "Inspect Room Data" button in the room sidebar
I find myself doing the same routine: I want to inspect a room's state,
so I have to go hunting for the Developer Tools button. And then I have
to do a few clicks to even get the correct room, what a waste of time!

So I added a new button to the sidebar to open the Developer Tools for
the current room.
2025-10-27 18:08:51 -04:00
Joshua Goins
7444d68280 Change room sidebar action text
As we've continued to add more and more room actions, I'm not entirely
happy with how they're worded. Some are super short and sweet like
"Verify User" while others refer to the room as if the sidebar wasn't
enough context: "Search in this room".

I redid all of the button's text so they're shorter, like "Search
Messages".
2025-10-27 18:08:42 -04:00
Joshua Goins
1d5536401d Make the CTRL+F shortcut search the current room's messages
Right now there's not an easy way to quickly bring up message search. If
you press CTRL+F (with the room information sidebar *closed*, for some
reason) that brings up the same dialog as CTRL+K which seems redundant.

I assigned that shortcut to the message search dialog instead, which is
makes much more sense in my opinion. I also made sure its disabled in
spaces or when there's not a room open.

BUG: 487270
FIXED-IN: 25.12.0
2025-10-27 18:08:29 -04:00
Joshua Goins
099e996f2f Improve standalone images in link preview and more
We would incorrectly show a "truncate" button for standalone images
which isn't applicable since there's no text. That check has been fixed,
and it doesn't seem to regress normal link previews.

Another is that if there's only an image, our layout would center the
image which looks awkward since almost everything else is left-aligned
in chat. This is also fixed, which notably matches up to Element Web's
behavior.

I added support for the hover link indicator as well. Someone could
maliciously hide it via Markdown but have a legitimate-looking link
preview, for example. You can check that by hovering over the link in
the message itself, but now the link preview is another way to confirm
that!
2025-10-27 18:08:21 -04:00
Joshua Goins
6d9974b2b1 Improve the Jitsi meeting button UX
Widgets support is incredibly useful and so is this button, but it has a
few problems. The most obvious is that it's still enabled even if you
don't actually have the permission to start Jitsi meetings, so I fixed
that. I also made sure it's hidden when viewing spaces too.

Another problem is that you can't easily tell if a meeting is currently
in progress either, nor do we have a good icon for that in Breeze. So I
changed the tooltip and colored the icon in this case.

The final problem I fixed is something not exclusive to NeoChat, but
generally all chat applications with this feature - there's no
confirmation! To stop "butt-dialing" random people or rooms, I added a
prompt before starting or joining a meeting.
2025-10-27 18:08:08 -04:00
James Graham
f65e9f7599 Fix the chatbar so that the icons show on android. Overring the contentItem was what was causing them to disappear. 2025-10-27 16:13:54 -04:00
l10n daemon script
4a39810923 GIT_SILENT Sync po/docbooks with svn 2025-10-26 01:38:47 +00:00
l10n daemon script
6720e2dfaa GIT_SILENT Sync po/docbooks with svn 2025-10-25 01:39:53 +00:00
l10n daemon script
eb1bf2ed2b GIT_SILENT Sync po/docbooks with svn 2025-10-24 01:39:25 +00:00
l10n daemon script
cc26b1b62f GIT_SILENT Sync po/docbooks with svn 2025-10-23 01:38:07 +00:00
l10n daemon script
59c2c88eb6 GIT_SILENT made messages (after extraction) 2025-10-22 00:43:30 +00:00
l10n daemon script
99e8fd16eb GIT_SILENT Sync po/docbooks with svn 2025-10-21 01:39:43 +00:00
l10n daemon script
eaa4bdb0b7 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-10-21 01:34:16 +00:00
l10n daemon script
892b61e6bb GIT_SILENT Sync po/docbooks with svn 2025-10-20 01:41:26 +00:00
Nicolas Fella
6d56b673c4 Don't call direct messages friends
Not every person I communicate with is necessarily my friend

Use a more neutral term

BUG: 480167
2025-10-19 19:42:21 +02:00
l10n daemon script
f59e5a4b3c GIT_SILENT Sync po/docbooks with svn 2025-10-19 01:42:36 +00:00
Nicolas Fella
16e3fd4476 Don't show direct messages in room list when not in a space
2b0251c593 changed the logic to also show direct messages inside a space

However this also causes direct messages to be shown when *not* inside a space, which is undesirable
2025-10-18 15:02:07 +02:00
l10n daemon script
82d0fdefd2 GIT_SILENT Sync po/docbooks with svn 2025-10-18 02:03:22 +00:00
Tobias Fella
cbde14d58b Cleanup DevicesPage 2025-10-17 12:23:58 +00:00
l10n daemon script
f3c37e4304 GIT_SILENT Sync po/docbooks with svn 2025-10-17 01:57:08 +00:00
l10n daemon script
369008cec3 GIT_SILENT Sync po/docbooks with svn 2025-10-16 01:40:30 +00:00
l10n daemon script
1a2af57901 GIT_SILENT Sync po/docbooks with svn 2025-10-15 01:39:22 +00:00
l10n daemon script
dbb41d061c 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-10-15 01:33:59 +00:00
l10n daemon script
d1d3c43c6f GIT_SILENT Sync po/docbooks with svn 2025-10-14 01:40:53 +00:00
Paul Brown
ba2b1529ea Edit org.kde.neochat.appdata.xml 2025-10-13 10:50:10 +00:00
l10n daemon script
b5150f82f0 GIT_SILENT Sync po/docbooks with svn 2025-10-13 01:40:08 +00:00
Tobias Fella
00c81de035 Fix pushing welcome page onto layer
BUG: 510285
2025-10-12 10:58:21 +00:00
Tobias Fella
52d4451932 Add state event texts for some element-specific widget events 2025-10-12 10:49:45 +00:00
l10n daemon script
3efc11b9aa GIT_SILENT Sync po/docbooks with svn 2025-10-12 01:39:29 +00:00
James Graham
5fd9f07664 Some further belt and braces to ensure that we don't crash in the timeline
Some further belt and braces to ensure that we don't crash in the timeline. This does the following:
- Checks all callback inputs
- Makes sure that any objects are cleaned up from incubators when deleting the delegate as it turns out that clear() doesn't do this. Hopefully this stops callbacks into an already deleted parent.

Hopefully helps with https://crash-reports.kde.org/organizations/kde/issues/261627/events/0b6bef500c5641e1924aa0bc2b0c11bd/
2025-10-11 18:11:57 +00:00
l10n daemon script
bec9f36bce GIT_SILENT Sync po/docbooks with svn 2025-10-11 10:19:58 +00:00
l10n daemon script
a631acc0ac GIT_SILENT Sync po/docbooks with svn 2025-10-10 01:38:30 +00:00
l10n daemon script
b729aaf6ee GIT_SILENT Sync po/docbooks with svn 2025-10-09 01:43:02 +00:00
Tobias Fella
94b7fc5cdf Fix delegate menu for state events 2025-10-08 21:50:56 +00:00
Marco Martin
ec5355d86e Push to PageRow on mobile too
This fixes some glitches during animation, which had the background
disappearing when the pop animation was running,
and now uses always the pagerow for viewing the room.

This depends from the new PageRow animation on mobile,
https://invent.kde.org/frameworks/kirigami/-/merge_requests/1925
which makes all applications coherent with an application which
looks a bit more like the one on most android apps.

Also, the swipe back gesture now will work there too
2025-10-08 15:44:46 +00:00
Arno Rehn
1a43d15c6d EventHandler: Acknowledge that non-room-events can have a body as well
Specifically, in the current architecture, these can be EncryptedEvents
which are redactions of previous events. These will have artifical bodies.
2025-10-08 14:01:48 +00:00
James Graham
7356a68f4c Hopefully this stops any crashes around QuickActions.qml and EmojiDialog.qml
Hopefully this stops any crashes around QuickActions.qml and EmojiDialog.qml. Best I can guess this is some race condition where QuickActions are deleted in the time it takes to instnatiate the EmojiDialog popup. I've also rearranged the updateQuickActions function to stop a possible race condition there.

BUG: 509484
2025-10-08 13:07:02 +00:00
Tobias Fella
1070427a0d Try fixing crash in QConcatenateTablesProxyModel 2025-10-08 11:39:39 +00:00
l10n daemon script
196ef535ca GIT_SILENT Sync po/docbooks with svn 2025-10-08 01:40:26 +00:00
Tobias Fella
88fd173829 Show a placeholder message if there are no extensions 2025-10-07 22:43:18 +02:00
Arno Rehn
8b0698c670 Support adding Jitsi and removing widgets 2025-10-07 21:58:34 +02:00
Arno Rehn
5b07a0ff45 Add jitsi button to RoomPage 2025-10-07 21:58:34 +02:00
Arno Rehn
ba1d175d67 Add "Extensions" item to the room info 2025-10-07 21:58:34 +02:00
Arno Rehn
36ce55e892 Add WidgetModel 2025-10-07 21:56:18 +02:00
Arno Rehn
ba4a83d38c Don't switch to global DMs when space contains DM 2025-10-06 14:14:04 +00:00
Arno Rehn
5c49c35b82 Space overview: Fix displayed name of direct chats
When in the space overview page, direct chats were previously only
shown with their room ID. Fix this by getting the actual NeoChatRoom
and its displayName.
2025-10-06 14:14:04 +00:00
Arno Rehn
2b0251c593 Room list: Show direct chats in spaces
Direct Chats can be added to spaces as well. No need to exclude them.
2025-10-06 14:14:04 +00:00
l10n daemon script
3b08d0f382 GIT_SILENT Sync po/docbooks with svn 2025-10-06 01:43:51 +00:00
l10n daemon script
f64e8a3192 GIT_SILENT Sync po/docbooks with svn 2025-10-04 15:09:41 +00:00
Tobias Fella
eab45e761a Use KirigamiApp 2025-10-03 11:32:15 +00:00
l10n daemon script
0f03290c57 GIT_SILENT Sync po/docbooks with svn 2025-10-03 01:42:04 +00:00
Tobias Fella
59f87bb2c2 Set deprecation level 2025-10-02 16:43:27 +02:00
Tobias Fella
9afc10160d Raise compiler settings level to 6.17 2025-10-02 16:19:01 +02:00
Tobias Fella
cfc7f50a1f MessageContentModel: Guard data() against being called when there is no room 2025-10-01 23:48:49 +02:00
Heiko Becker
a2fc00365e GIT_SILENT Update Appstream for new release
(cherry picked from commit debbe8e478)
2025-10-01 00:40:10 +02:00
Joshua Goins
466cfd971d Fix room succession not actually working
I broke this in 409b6da160 making it not
possible to join a succeeding room. I decided to keep the old behavior
of not prompting you to join, because:

1. You are intentionally choosing to tap the button.
2. Room successions aren't random, a privileged user in the room is
creating it and there's already some implicit existing trust

(Also the current join confirmation dialog isn't useful enough yet to
make an actual informed decision anyway.)
2025-09-30 16:05:36 -04:00
Joshua Goins
8e83febb24 Remove some dead code from MessageModel::createEventObjects
This senderId variable doesn't seem to be used in the current iteration
of this function, lets remove it.
2025-09-30 12:07:49 -04:00
l10n daemon script
12072b0a73 GIT_SILENT Sync po/docbooks with svn 2025-09-30 01:47:20 +00:00
l10n daemon script
83b1daac07 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-09-30 01:36:30 +00:00
Arno Rehn
981ea053a6 Pinned messages: Decrypt events before handling them 2025-09-29 18:59:53 +02:00
Arno Rehn
e41fd7d986 PollHandler: Decrypt EncryptedEvents
Related events for a poll will be EncryptedEvents in an encrypted
room. Decrypt them before handling.
2025-09-29 18:59:53 +02:00
Arno Rehn
a88a82ca08 PollHandler: Correctly handle paginated events 2025-09-29 18:59:20 +02:00
Arno Rehn
23dc88df60 PollComponent: Apply word wrapping on long messages
Long messages were not wrapped and would overflow.
Correctly apply word wrapping and align all elements on the top of a
row.
2025-09-29 18:58:57 +02:00
l10n daemon script
520b1daeec GIT_SILENT Sync po/docbooks with svn 2025-09-29 01:41:25 +00:00
l10n daemon script
cbe7ace32d GIT_SILENT Sync po/docbooks with svn 2025-09-28 01:46:45 +00:00
l10n daemon script
2b4471ea91 GIT_SILENT Sync po/docbooks with svn 2025-09-27 01:40:14 +00:00
Joshua Goins
88e1e1dd2a Add the ability to scroll up in the message search window
This wasn't a thing yet, so the search experience compared to something
like Element Web was much worse. Now you can scroll back as far as any
other client can!

I had to do a bit of refactoring to stop resetting the model all the
time, and append instead. Otherwise, it was quite straightforward to
implement.
2025-09-26 17:49:22 -04:00
Joshua Goins
94ea1305b2 Fix some miscellaneous weirdness around push notifications
For some reason, I *stopped* the timer if we get a push message - which
makes *no* sense. It meant that NeoChat kept running in the background
for basically no reason.

We can also make the NotificationsManager::postPushNotification function
static and avoid creating some singletons.
2025-09-26 15:14:05 -04:00
Joshua Goins
960377838d Replace KDBusService usage with QDBusConnection when D-Bus activated
This is lighter and more reliable when it comes to being activated by
KUnifiedPush. We also don't need all of the features of KDBusService
here.
2025-09-26 15:14:05 -04:00
Joshua Goins
a48e8662d6 Remove useless translated string from dbus-activated CLI option
It never gets shown, so let's not waste our translators time.
2025-09-26 15:12:56 -04:00
l10n daemon script
9c90a38efc GIT_SILENT Sync po/docbooks with svn 2025-09-26 01:39:23 +00:00
l10n daemon script
8696a24127 GIT_SILENT Sync po/docbooks with svn 2025-09-25 01:40:55 +00:00
l10n daemon script
f2d1b4c1e1 GIT_SILENT Sync po/docbooks with svn 2025-09-24 01:40:47 +00:00
Laurent Montel
4cdc2b5e58 GIT_SILENT: Don't duplicate headers between .h/.cpp 2025-09-22 04:43:02 +00:00
l10n daemon script
e26f02d9e2 GIT_SILENT Sync po/docbooks with svn 2025-09-22 01:38:51 +00:00
l10n daemon script
5d83fe9a1d GIT_SILENT Sync po/docbooks with svn 2025-09-21 01:41:04 +00:00
l10n daemon script
6a506f237a GIT_SILENT Sync po/docbooks with svn 2025-09-20 01:41:10 +00:00
l10n daemon script
3a5a0153d8 GIT_SILENT Sync po/docbooks with svn 2025-09-18 01:40:38 +00:00
l10n daemon script
27519b8788 GIT_SILENT Sync po/docbooks with svn 2025-09-17 01:39:11 +00:00
l10n daemon script
dd45fe16a5 GIT_SILENT Sync po/docbooks with svn 2025-09-16 01:41:59 +00:00
Vlad Zahorodnii
161815acff Fix inserting UserListModel items without beginInsertRows()
If an item is added, the corresponding code should be wrapped with
beginInsertRows() and endInsertRows(), otherwise proxy or filter models
can end up with corrupted internal state.

m_members.insert() in refreshMember() should be unnecessary because
if pos is not the same as m_members.size(), then it means there's already
a member.id() item in the member list.
2025-09-15 18:04:29 +00:00
Tobias Fella
438edf2155 Fix qml warnings in ImageEditorPage 2025-09-15 16:13:49 +02:00
Tobias Fella
6607a4b72c Fix qml warnings in ChatBar 2025-09-15 16:11:20 +02:00
Tobias Fella
8f0c4ab133 Fix warning in FullScreenMap 2025-09-15 16:06:58 +02:00
Tobias Fella
78e5cd51cd remove unused imports 2025-09-15 16:05:22 +02:00
Tobias Fella
f5da44655e Fix qml warnings in SpaceDrawer 2025-09-15 16:03:38 +02:00
Tobias Fella
a91863c60d Fix qml warnings in AccountSwitchDialog 2025-09-15 14:39:40 +02:00
Tobias Fella
29e3a09aba Fix qml warning 2025-09-15 14:29:55 +02:00
Tobias Fella
a59952f189 Fix qml warnings in AccountData 2025-09-15 14:24:42 +02:00
Tobias Fella
b169da25ab Fix qml warnings in RoomSortParameterDialog 2025-09-15 14:21:47 +02:00
Tobias Fella
833e357d70 Fix qml warnings in login module 2025-09-15 14:15:38 +02:00
Tobias Fella
f9bf2b8f7a Fix warnings in NeoChatMaximizeComponent 2025-09-15 14:06:17 +02:00
Tobias Fella
7b27579f2d Fix qml warnings in ExploreRoomsPage 2025-09-15 13:54:43 +02:00
Tobias Fella
edb9d9f54e Remove unused import 2025-09-15 13:51:23 +02:00
Tobias Fella
abf9a486d0 Fix qml warnings in RoomData 2025-09-15 13:49:08 +02:00
Tobias Fella
b0bc19c055 Fix qml warnings in RoomInformation 2025-09-15 13:41:32 +02:00
Tobias Fella
71776ef275 Fix qml warnings in TypingPane 2025-09-15 13:30:55 +02:00
Tobias Fella
8dc7d1d39d Fix most qml warnings in StateKeys 2025-09-15 13:25:41 +02:00
Tobias Fella
adb9e59503 Fix qml warnings 2025-09-15 13:21:17 +02:00
Tobias Fella
518044e34a Fix qml warning 2025-09-15 13:17:32 +02:00
Tobias Fella
d6d747bb99 Fix qml warnings in EmoticonFormCard 2025-09-15 13:15:59 +02:00
l10n daemon script
f390702a7a GIT_SILENT Sync po/docbooks with svn 2025-09-15 01:39:42 +00:00
Tobias Fella
e056360ddd Various fixes for ExportKeysDialog 2025-09-14 18:31:26 +02:00
l10n daemon script
85163791ce GIT_SILENT Sync po/docbooks with svn 2025-09-14 01:39:17 +00:00
l10n daemon script
b5a853bc96 GIT_SILENT Sync po/docbooks with svn 2025-09-13 01:46:27 +00:00
Tobias Fella
e4cd6ce5a6 Add "copy user id" menu to delegate in user search page
BUG: 499792
2025-09-12 11:37:07 +00:00
l10n daemon script
84fa68e3ae GIT_SILENT Sync po/docbooks with svn 2025-09-12 01:38:40 +00:00
Joshua Goins
649dc7a815 Add placeholder message when there's no public rooms at all
I guess we never tested this against a truly empty server yet, as the
"no search" message was empty. This would normally never show up since
the top public rooms are usually listed instead.
2025-09-11 14:09:52 -04:00
Joshua Goins
b87e43660c Use better-fitting icons for "Explore rooms" and "Find your friends"
Only a problem on the room list, which used list-add which is odd as it
doesn't match up with how these are displayed elsewhere.
2025-09-11 11:52:24 -04:00
Joshua Goins
511138e3bb Revert back to normal, non-master KNotifications
It requires KF6.18, and while we could also force that it's probably
easier to wait for the full release.
2025-09-11 17:47:29 +02:00
Volker Krause
5a3c23d000 Fix KNotifications Craft path 2025-09-11 17:22:04 +02:00
Joshua Goins
1036384023 Add recently pinned message to the top of the window
This makes pinned messages actually quite useful, as rooms can stick
persistent and important information for users to see. (For example, the
KWin has crash triage meeting information pinned in their room.)

For simplicity and space reasons, only one line of the generic message
body is shown.
2025-09-11 08:57:22 -04:00
Tobias Fella
e3f618489b Remove flatpak patches 2025-09-11 12:00:44 +00:00
Joshua Goins
cfedd4fdb5 Stop erroring out when setting avatar if you never set it
The code didn't actually check the avatar URLs were different until now.
2025-09-11 07:43:04 -04:00
Joshua Goins
812e5beee6 Allow pressing enter/return on "Add Account" in account switcher dialog
Because "Add Account" is a "fake item" by being the footer, it should be
the null fallback when accessing currentItem.
2025-09-11 07:41:56 -04:00
Joshua Goins
17b632eb78 Add a small margin between the border of media and "Hide Media" button
I thought we had this already, I don't know where it went, but now it's
back.
2025-09-11 13:36:51 +02:00
Joshua Goins
877d5f34f7 Hide "Scan a QR Code" with no camera connected
Also when you disconnect/turn off the camera while the QR scanner is
open, show a better error message instead of freezing the last frame.
2025-09-11 05:15:39 -04:00
Tobias Fella
c93ce52ba1 Remove various outdated ifdefs 2025-09-11 10:47:23 +02:00
l10n daemon script
2504ca3f5c GIT_SILENT Sync po/docbooks with svn 2025-09-11 01:38:19 +00:00
l10n daemon script
5be05032be GIT_SILENT Sync po/docbooks with svn 2025-09-10 01:39:56 +00:00
Joshua Goins
81e5061831 Fix-up QR code scanning actions
I cleaned up how resolving QR codes work, I made sure to test both user
and room links and everything works great.

A more awkward bug I fixed is
a workaround around a quirk in Kirigami.Dialog, but actually regresses
QR codes. Because the QR code scanner is currently an extra window (at
least on desktop) the UserDetailDialog will reparent itself to the
soon-to-be-destroyed scanner window.
2025-09-09 13:35:12 -04:00
Joshua Goins
3b419e18a3 Build KNotifications master in Craft for now
This is needed to make Android notifications work again, as it narrowly
missed 6.18. We can remove it upon the next release.
2025-09-09 10:44:25 -04:00
Joshua Goins
1a1190d7bd Fix URL previews on modern Matrix servers never loading
This was a regression in 5d5295d06d where
the job never connected to the onSuccess function.
2025-09-09 08:51:54 -04:00
Joshua Goins
5b4d3df6ee Make rooms in the room list bold when there's unread messages
This used to be a feature but kinda broke/changed when we switched to
focusing on context-aware or notable events. Basically instead of only
notable events making a room bold in the room list, it's any unread
messages now.
2025-09-09 11:43:25 +02:00
Tobias Fella
f53d47fa28 Require libQuotient 0.9.3 2025-09-09 07:47:39 +00:00
l10n daemon script
67d5711cc4 GIT_SILENT Sync po/docbooks with svn 2025-09-09 02:18:13 +00:00
l10n daemon script
9f51b1e7ab 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-09-09 02:04:03 +00:00
l10n daemon script
5ac296ee71 GIT_SILENT made messages (after extraction) 2025-09-09 00:54:31 +00:00
Joshua Goins
1b8adcc91a Don't import Purpose on Android
This prevents the app from launching on Android (and probably Windows
too) because our module declares a dependency on a non-existent Purpose.
2025-09-08 16:10:12 +02:00
Joshua Goins
1e78209cd7 Fix missing appWindow property for GlobalMenuStub
This was one of the issues with the current Android version.
2025-09-08 15:47:04 +02:00
Tobias Fella
0fd924be7b Fix showing permissions settings for v12 rooms 2025-09-08 08:14:17 +00:00
l10n daemon script
d67cb89505 GIT_SILENT Sync po/docbooks with svn 2025-09-08 02:13:16 +00:00
l10n daemon script
0d0f88e251 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-09-08 01:51:40 +00:00
Joshua Goins
4cbd4a034c Remove incorrect context for "Always allow device verification" 2025-09-07 12:57:08 -04:00
Joshua Goins
e7ed07e386 Show the "Verify This Device" warning when turning on reverification
This is the only way to reverify your device again, so it's useful to
show this in that case.
2025-09-07 12:57:08 -04:00
Joshua Goins
390654fa4a Fix deprecation warning about QHoverEvent::pos
We should be using position().
2025-09-07 12:26:27 -04:00
Joshua Goins
e8830b56d8 Add "Done" button when verification session is finished
It isn't immediately obvious when to close the window, other clients
(e.g. Element Web) has a "Got It" button in addition to the regular
dialog close button.
2025-09-07 12:25:16 -04:00
Tobias Fella
bbf2587908 Don't use raw json for getting reply event id 2025-09-07 16:43:35 +02:00
Joshua Goins
409b6da160 Bring up the room confirmation dialog in more situations
This super helpful and nice dialog went mostly unused, especially if you
only joined links to rooms through matrix.to or within NeoChat itself.

The reason why this never showed up for said links is because we were
greedily overwriting the "action". I don't think the code made solid
sense anyway, so I redone it a bit so there's a clearer
"join_confirmed" our UI uses. I added a test case to showcase this
working.
2025-09-07 08:43:51 -04:00
l10n daemon script
9d8c9853ce GIT_SILENT Sync po/docbooks with svn 2025-09-07 01:40:17 +00:00
Joshua Goins
b3781e0db4 Turn NeoChatConnection::isVerifiedSession into a notifiable property
This should allow the devices page to hide the "Verify this Device"
warning, once you actually complete verification. I also made sure to
update the other consumer - the menu item under the user menu.

Additionally, I also fixed the password field not being automatically
focused when trying to remove a device.
2025-09-06 07:51:38 -04:00
l10n daemon script
fbcb2379fb GIT_SILENT Sync po/docbooks with svn 2025-09-06 01:40:56 +00:00
Tobias Fella
e8ad4a5dee Fixes for logging out the active connection
- Don't crash
- Fall back to WelcomePage
2025-09-05 10:11:43 +00:00
l10n daemon script
6c57c41993 GIT_SILENT Sync po/docbooks with svn 2025-09-05 01:41:21 +00:00
Joshua Goins
ce0014f846 Don't accidentally hide relevant error banners
We use error banners for intermittent errors (failing to join a room),
along with more long-standing/important errors (like going offline.) And
due to the nature of how we manage banner visibility - for example, the
online check could accidentally hide joining rooms errors which is
pretty bad.

To fix this, each error banner is assigned an id. All errors can still
overwrite each other as they could before, but they can only hide
banners of their own kind.
2025-09-04 20:48:56 -04:00
l10n daemon script
e5df412258 GIT_SILENT Sync po/docbooks with svn 2025-09-04 01:41:16 +00:00
Heiko Becker
0c482aa463 GIT_SILENT Update Appstream for new release
(cherry picked from commit 681a3c4036)
2025-09-04 01:31:22 +02:00
Joshua Goins
2df3a21fb5 Show the room name when failing to join it
The previous error message generically said you failed to join a room,
but it doesn't say *which* one!
2025-09-03 10:19:26 -04:00
l10n daemon script
9dd438e8a2 GIT_SILENT Sync po/docbooks with svn 2025-09-03 01:45:00 +00:00
Tobias Fella
cea5006e15 Revert "Use libQuotient qml module"
This reverts commit b80a2f94f4.
2025-09-03 00:00:16 +02:00
Tobias Fella
8ff3b31497 Fix some qml warnings in RoomDelegate 2025-09-02 23:00:15 +02:00
Tobias Fella
c7a9f9f07e Fix qml warnings in NotificationsView 2025-09-02 22:57:39 +02:00
Tobias Fella
1de10a4b87 Fix qml warnings in RoomAdvancedPage 2025-09-02 22:51:45 +02:00
Tobias Fella
d7f19efbd6 Fix qml warnings in MimeComponent 2025-09-02 22:49:27 +02:00
Tobias Fella
a3a28b2472 Fix qml warnings in PolLDelegate 2025-09-02 22:48:19 +02:00
Tobias Fella
919fc8b821 Fix qml warnings in DelegateContextMenu 2025-09-02 20:43:46 +00:00
Tobias Fella
b80a2f94f4 Use libQuotient qml module 2025-09-02 22:36:07 +02:00
Tobias Fella
6d18d1a237 Fix qml warnings 2025-09-02 15:36:21 +02:00
Tobias Fella
a2d64d2955 Fix some qml warnings 2025-09-02 15:06:58 +02:00
l10n daemon script
95617e3210 GIT_SILENT Sync po/docbooks with svn 2025-09-02 01:52:19 +00:00
Tobias Fella
1522f7086e Fix various qmllint warnings 2025-09-01 23:50:17 +02:00
Tobias Fella
14167d197f Fix some qml warnings 2025-09-01 23:33:25 +02:00
Tobias Fella
c377cf05b8 Fix qml warnings 2025-09-01 23:27:21 +02:00
Tobias Fella
4a52bc04fb Fix warning 2025-09-01 23:12:31 +02:00
Tobias Fella
53cd230d16 Remove redundant TapHandlers 2025-09-01 23:09:18 +02:00
Tobias Fella
e7b204b9fd Fix various qmllint warnings 2025-09-01 23:06:46 +02:00
Tobias Fella
4d91ae96e3 Only build required parts of opencv 2025-09-01 15:46:08 +02:00
Tobias Fella
b422d641de Add opencv to flatpak
Dependency of KQuickImageEditor
2025-09-01 14:58:27 +02:00
Tobias Fella
adc23e22cd Fix qml warnings in ExploreComponentMobile 2025-09-01 13:39:13 +02:00
Tobias Fella
a673d97144 Fix qml warnings in ExploreComponent 2025-09-01 13:25:36 +02:00
Tobias Fella
5a71dfcd7a Fix qml warnings in SpaceHomePage 2025-09-01 13:22:05 +02:00
Tobias Fella
5f6c248181 Also disable i18n warnings 2025-09-01 13:12:53 +02:00
Tobias Fella
8e04a5ed2f Fix warnings in InvitationView 2025-09-01 13:10:11 +02:00
Tobias Fella
f45582dc03 Fix warnings and add message contexts in ServerComboBox 2025-09-01 13:06:04 +02:00
Tobias Fella
19cfe118ac Fix warnings in SpaceListContextMenu 2025-09-01 13:03:31 +02:00
Tobias Fella
79b8987d85 Fix UserSearchPage warnings 2025-09-01 13:00:57 +02:00
l10n daemon script
1d9708ffa4 GIT_SILENT Sync po/docbooks with svn 2025-09-01 01:41:21 +00:00
Tobias Fella
c55f30be5b Fix some more warnings around emoji components 2025-08-31 23:01:51 +02:00
Tobias Fella
669a00a7e3 Fix most qml warnings in RoomContextMenu 2025-08-31 22:55:58 +02:00
Tobias Fella
51fe090043 Fix warnings in EmojiPicker 2025-08-31 22:51:30 +02:00
Tobias Fella
cf85d0fc0d Fix some qml warnings 2025-08-31 22:38:24 +02:00
Tobias Fella
c257ac504b Fix warning 2025-08-31 22:35:19 +02:00
Joshua Goins
9412f7e189 Fix compact mode not filling all available space
This seems to be an unintentional change in compact mode caused by 054f87cae2,
where an if check for "straight line" or "fill width" mode ended up
being removed. But this was needed for availableWidth() to return the
correct width for compact mode, otherwise it got weirdly centered and
ended up with a limited width.
2025-08-31 10:03:57 -04:00
Joshua Goins
385fafa51c Re-arrange appearance page to group similar settings together
I re-arranged everything now so it makes more semantic sense, e.g. all
the message layout stuff is together in one card. I also made
bubble-only options hide themselves to reduce the amount of clutter
for compact layout users. Some small grammatical things were changed
like "Show Avatar" -> "Show Avatars".
2025-08-31 10:03:46 -04:00
Joshua Goins
c7e409abe9 Improve the event loading indicator somewhat
I added ellipses so it matches the other loading placeholder we have. I
also removed the spacing in it's layout, because there is more than
enough space inside of BusyIndicator itself that it makes the
additional spacing look odd.
2025-08-30 22:01:19 -04:00
Joshua Goins
98816aedd4 Set the correct palette colors for selections in quotes
This bothered me a for a while, but I finally remembered to change it
now.
2025-08-30 22:01:07 -04:00
Joshua Goins
38c66d1b7d Clip commonly seen labels used for display names to prevent Unicode spam
Unfortunately a pretty common spam/annoyance online is putting "spam"
via abusing Unicode symbols - which is used for multilingual support -
in display names. This typically results in an unreadable word jumble in
our UI, which looks bad.

So I decided to put a stop to that by clipping some labels that could be
used for this crap. I didn't cover *everything* yet, but this at least
prevents these idiots from showing up in the completion menu, the room
member list and as authors for messages.
2025-08-30 22:00:55 -04:00
Joshua Goins
0e01a43aab Ensure we update the avatar item when switching to Compact mode
This fixes an always-reproducible bug by switching from Bubbles (with
"move local messages to the right" enabled) to Compact, and your
avatar is missing until you switch rooms or restart NeoChat.

The various flags to inform the rest of the delegate of the avatar were
returning true, but the actual avatar item was never created when the
option was switched on.
2025-08-30 21:52:33 -04:00
l10n daemon script
ddf67aef94 GIT_SILENT Sync po/docbooks with svn 2025-08-31 01:42:36 +00:00
Joshua Goins
fa8294d4b9 Add action to user detail dialog to search for their messages in room
In other messaging applications (e.g. Discord) this is possible through
text modifiers like "from:@user". We don't support that, and I'm not
super keen on implementing yet-another-parsing-thing, so an action in
the user detail dialog should work for now.

Very useful to sift through large rooms but when you only care about a
specific person's messages (maybe your own?)
2025-08-30 13:19:46 -04:00
James Graham
8ad822fd0b Fix the link preview insert lambda to make sure that it doesn't crash
Fix the link preview insert lambda to make sure that it doesn't crash and also make sure that the system doesn't lock up on link preview load
2025-08-30 12:49:11 +01:00
Tobias Fella
69568c628f Also fix live locations 2025-08-30 11:39:20 +00:00
Tobias Fella
62a770b3e2 Fix showing Location events
- Initialize required properties
- Use event's body as display
2025-08-30 11:39:20 +00:00
Tobias Fella
9b65ae1e66 Cleanup LocationsModel
Use libQuotient's event properties instead of accessing data from raw json
2025-08-30 11:30:28 +00:00
Tobias Fella
5d5295d06d Modernize job handling 2025-08-30 10:23:45 +00:00
l10n daemon script
8f7be9993c GIT_SILENT Sync po/docbooks with svn 2025-08-30 01:43:35 +00:00
Joshua Goins
821d7621e3 Change chat bar text field back to plain text
This was changed in 9ed5224470 but this
ended up causing all sorts of strange formatting issues like the quick
format bar not working, and pastes from other applications that kept the
formatting.
2025-08-29 18:18:01 -04:00
Tobias Fella
91b00a34b7 Simplify code 2025-08-29 22:22:44 +02:00
Tobias Fella
19e74b60a9 Reduce nesting 2025-08-29 22:22:44 +02:00
Tobias Fella
5ee5991c39 Use contentPart instead of contentJson 2025-08-29 22:22:44 +02:00
Tobias Fella
abffeec938 Refactor accountmanager 2025-08-29 17:08:15 +02:00
Tobias Fella
77ac811498 Make delegate take maximum width while editing a message 2025-08-29 15:07:20 +00:00
Tobias Fella
1ba6b840db Bump Qt version dependency 2025-08-29 15:06:57 +00:00
Tobias Fella
bfb640487f Fix connection 2025-08-29 14:21:19 +00:00
Joshua Goins
4b3cc750a1 Remove Kirigami.OverlayZStacking from HoverLinkIndicator
I forgot this is for Popups only, let's just go back to the good
old days of hardcoding a Z value.
2025-08-29 10:06:08 -04:00
Joshua Goins
8bef9713fd Hide Purpose's own "Copy to Clipboard" function
This was added recently to Purpose, and it's not too useful for us.
We already have a bunch of better, more integrated ways to copy
events to the clipboard.
2025-08-29 10:02:50 -04:00
Tobias Fella
889bf9cbc6 Simplify event context menu open
There's no reason to pass the author from QML to C++, we can query the author in the backend
2025-08-29 15:17:19 +02:00
l10n daemon script
003ab4f278 GIT_SILENT Sync po/docbooks with svn 2025-08-29 01:41:59 +00:00
Tobias Fella
a6b9759a31 Unify Delegate context menus
The menus have always been split into a menu for file-like content and text-like content

This split makes some things a bit more complicated then necessary.
2025-08-28 22:43:42 +02:00
Tobias Fella
5c5dcd555b Fix compilation warnings about QFile::open 2025-08-28 15:16:08 +02:00
Tobias Fella
8098fce461 Fix qml name of timeline-memtest 2025-08-28 14:32:54 +02:00
l10n daemon script
227ea231d5 GIT_SILENT Sync po/docbooks with svn 2025-08-28 01:46:47 +00:00
Tobias Fella
35ac1b7a55 Tests: Rewrite server sync handling
Implements #696
2025-08-27 22:59:39 +02:00
l10n daemon script
86be1d82bc GIT_SILENT Sync po/docbooks with svn 2025-08-27 01:44:37 +00:00
Tobias Fella
c6dd82d2ff Don't link kirigami in memorytests 2025-08-26 13:40:25 +02:00
l10n daemon script
2624ed1817 GIT_SILENT Sync po/docbooks with svn 2025-08-26 01:42:10 +00:00
Tobias Fella
8706ee950e Minor fix to StateModel 2025-08-25 15:21:51 +02:00
l10n daemon script
bde27dc826 GIT_SILENT Sync po/docbooks with svn 2025-08-25 01:42:35 +00:00
Tobias Fella
25b4ee2efb Ifdef trayicon a bit more for android 2025-08-24 16:25:52 +02:00
Tobias Fella
e7f6adaa01 Refactor Controller 2025-08-24 16:12:52 +02:00
Tobias Fella
bb3e28bc43 Fix runtime qml warning 2025-08-24 11:56:22 +02:00
Tobias Fella
026db80391 Use logging category for some qDebugs 2025-08-24 10:07:59 +02:00
l10n daemon script
2c09a48c8d GIT_SILENT Sync po/docbooks with svn 2025-08-24 01:40:42 +00:00
Joshua Goins
6e0931e31b Improve search experience somewhat
So I noticed that searching for messages feels "buggy". I dove into the
code and gave it a pretty nice refresh, I know you'll like it once you
try it!

Some of the issues I noticed and are now fixed:
* The search doesn't clear immediately when you clear the text box, it's
delayed like everything else. This also has the knock-on effect of
starting random network requests.
* If you now press the search button (either with a mouse, pen, etc. or
by the keyboard) the search delay timer was still running.
* The "nothing is here" placeholder message appears when the search
timer is running, making you think no messages were found.

These changes also help other places where SearchPage is used, not just
message search.
2025-08-23 17:52:11 -04:00
Joshua Goins
1424b8ce42 Add icons to placeholder messages in search pages 2025-08-23 17:52:11 -04:00
Joshua Goins
f5f151681d Fix HAVE_KUNIFIEDPUSH not applying anymore
This compile-time definition was separated from the code during some
refactor.
2025-08-23 17:23:53 -04:00
Tobias Fella
306d75a9b0 Refactor saved room management and fix closing room when switching account
Relevant fixes hidden behind some refactoring:
- set m_lastRoomConfig per-user while setting the connection
- fold m_lastSpaceConfig into m_lastRoomConfig
- set current room to empty explicitely when there is no room
2025-08-23 23:04:39 +02:00
Tobias Fella
ee33a70bb2 Fix opening "Home space" on startup
There was a mismatch here between the value used in the config ("Home") and the value used in the property (empty string)
2025-08-23 23:04:39 +02:00
Tobias Fella
bd80390daa Improve InviteUserPage 2025-08-23 22:53:42 +02:00
Tobias Fella
0fa490f532 Fix crash on shutdown and simplify code 2025-08-23 22:39:25 +02:00
Tobias Fella
1c7cc45d8e Remove unused member 2025-08-23 12:17:49 +02:00
l10n daemon script
f3288c2e34 GIT_SILENT Sync po/docbooks with svn 2025-08-23 01:42:57 +00:00
Volker Krause
7eff1eec56 Only build the KUnifiedPush client inside a Flatpak
We neither need nor want the distributor nor the host configuration UI
here.

While at it, update to 25.08.0.
2025-08-22 17:34:05 +02:00
Tobias Fella
85c7b1a3fc Enable qmllint on CI 2025-08-22 14:15:55 +00:00
Tobias Fella
5c82c07f06 Fix search menu items 2025-08-22 10:49:39 +02:00
l10n daemon script
fcf125f9ca GIT_SILENT Sync po/docbooks with svn 2025-08-22 01:41:37 +00:00
l10n daemon script
8ae13adbcd GIT_SILENT made messages (after extraction) 2025-08-22 00:42:21 +00:00
Tobias Fella
1e1ad389e3 UserSearchPage: Move "Enter User ID" action into placeholder 2025-08-21 17:50:48 -04:00
Joshua Goins
f58212e8de Improve look of the HoverLinkIndicator and other refactorings
It still looks generally the same, except now:
* It doesn't take up the full width of the window (this was a mistake.)
* It now nicely fades in and out.
* It uses Kirigami's built-in OverlayZStacking object instead of
hardcoding a Z of 20.
* A border is now added which helps make it stand out compared to the
chat bar, which it frequently is on top of.
* The seemingly unused accessibility string is removed.
2025-08-21 17:50:33 -04:00
Tobias Fella
8622087e51 Cleanup Permissions page 2025-08-21 23:20:32 +02:00
Tobias Fella
5fc59b0d66 Cleanup RoomSecurityPage 2025-08-21 23:20:32 +02:00
Tobias Fella
5aeefea5c0 Cleanup RoomGeneralPage 2025-08-21 23:20:32 +02:00
Tobias Fella
52e9c9e8e2 Fix room list placeholders 2025-08-21 23:16:28 +02:00
Tobias Fella
ae69401d57 Fix clicking on a reply to get to the replied-to message 2025-08-21 17:27:45 +02:00
Justin Zobel
2339cad49f Uppercase OK 2025-08-21 22:58:00 +09:30
Tobias Fella
8f6683fd1d Fix most qml warnings in RoomListPage 2025-08-21 09:30:17 +02:00
Tobias Fella
74deb684e1 Don't show AddDirect delegate if we don't have any DMs
In this case, it's nicer to fall back to the placeholder, which has the same action
2025-08-21 09:15:36 +02:00
l10n daemon script
54a918b0cf GIT_SILENT Sync po/docbooks with svn 2025-08-21 01:45:15 +00:00
Tobias Fella
55c9b09b24 Add translation context 2025-08-20 20:19:13 +02:00
James Graham
0d63fce59a Apply all the required styling in cpp
Apply all the required styling to show links, table, spoilers, etc in cpp. This also updates the method of revealing spoilers so now you can click to reveal then click again to hide.
2025-08-20 17:10:44 +01:00
Joshua Goins
4498d4457b Set source size in link preview images
This prevents them from looking super jagged when we scale down a
high-res image into a small (usually ~70px in height) space.
2025-08-20 08:58:37 -04:00
Joshua Goins
83415d202a Handle more states in KeyVerificationDialog
We were specifically missing WAITINGFORKEY and WAITINGFORACCEPT, which
does happen and could be delayed - resulting in a blank screen for a few
seconds.

CCBUG: 508483
2025-08-20 08:58:26 -04:00
Tobias Fella
ed65855cdd Add autotest for server notice handling 2025-08-19 23:09:38 +02:00
Tobias Fella
1477159376 Show banner informing the user about server notices 2025-08-19 23:09:38 +02:00
Tobias Fella
53a88708d6 Only show server notices at the top if they have unread messages 2025-08-19 23:09:13 +02:00
Tobias Fella
8eb8803afd Add room category for server notices 2025-08-19 23:09:10 +02:00
l10n daemon script
20e30982cf GIT_SILENT Sync po/docbooks with svn 2025-08-19 02:05:09 +00:00
Tobias Fella
e13b82f66a Revert patch using features not yet available in flatpak 2025-08-18 17:09:50 +00:00
Tobias Fella
8e50388031 Revert KF6 version bump in flatpak 2025-08-18 17:09:50 +00:00
Tobias Fella
fb21686894 Bump KF6 dependency version 2025-08-18 17:09:50 +00:00
l10n daemon script
62faf0f784 GIT_SILENT Sync po/docbooks with svn 2025-08-18 01:41:27 +00:00
Tobias Fella
be377d9ad8 Update uri for libquotient 2025-08-17 22:01:09 +02:00
291 changed files with 109393 additions and 57796 deletions

View File

@@ -1,2 +1,2 @@
[General]
disableUnqualifiedAccess = "i18nc,xi18nc,i18ncp"
disableUnqualifiedAccess = "i18nc,xi18nc,i18ncp,i18n"

View File

@@ -2,7 +2,7 @@
"id": "org.kde.neochat",
"branch": "master",
"runtime": "org.kde.Platform",
"runtime-version": "6.9",
"runtime-version": "6.10",
"sdk": "org.kde.Sdk",
"command": "neochat",
"tags": [
@@ -32,17 +32,20 @@
],
"modules": [
{
"name": "kirigamiaddons",
"name": "opencv",
"config-opts": [
"-DBUILD_TESTING=OFF"
"-DBUILD_TESTS=OFF",
"-DWITH_GTK=OFF",
"-DBUILD_LIST=core,imgproc"
],
"buildsystem": "cmake-ninja",
"sources": [
{
"type": "git",
"url": "https://invent.kde.org/libraries/kirigami-addons.git"
"url": "https://github.com/opencv/opencv"
}
]
],
"builddir": true
},
{
"name": "kquickimageeditor",
@@ -62,6 +65,7 @@
"name": "olm",
"buildsystem": "cmake-ninja",
"config-opts": [
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
"-DOLM_TESTS=OFF"
],
"sources": [
@@ -161,11 +165,15 @@
"name": "kunifiedpush",
"buildsystem": "cmake-ninja",
"builddir": true,
"config-opts": [
"-DENABLE_TESTING=OFF",
"-DKUNIFIEDPUSH_CLIENT_ONLY=ON"
],
"sources": [
{
"type": "archive",
"url": "https://download.kde.org/stable/release-service/25.04.3/src/kunifiedpush-25.04.3.tar.xz",
"sha256": "a16ffe4117b14baa02f3b8ae7de9e509a17359c1b67dcd851aef4f3c3661a1df",
"url": "https://download.kde.org/stable/release-service/25.08.3/src/kunifiedpush-25.08.3.tar.xz",
"sha256": "e8c924438d5359f0fa0930ab35111012076e3a0ff4e959d6929595571383320a",
"x-checker-data": {
"type": "anitya",
"project-id": 8763,

View File

@@ -10,11 +10,11 @@ Dependencies:
'frameworks/ki18n': '@latest-kf6'
'frameworks/kconfig': '@latest-kf6'
'frameworks/syntax-highlighting': '@latest-kf6'
'frameworks/kiconthemes': '@latest-kf6'
'frameworks/kitemmodels': '@latest-kf6'
'frameworks/kquickcharts': '@latest-kf6'
'frameworks/knotifications': '@latest-kf6'
'frameworks/kcolorscheme': '@latest-kf6'
'frameworks/kiconthemes': '@latest-kf6'
'libraries/kquickimageeditor': '@latest-kf6'
'frameworks/sonnet': '@latest-kf6'
'frameworks/prison': '@latest-kf6'
@@ -29,7 +29,6 @@ Dependencies:
'frameworks/kio': '@latest-kf6'
'frameworks/kwindowsystem': '@latest-kf6'
'frameworks/kstatusnotifieritem': '@latest-kf6'
'frameworks/kcrash': '@latest-kf6'
- 'on': ['Linux', 'FreeBSD']
'require':
'frameworks/kdbusaddons': '@latest-kf6'
@@ -43,3 +42,4 @@ Dependencies:
Options:
per-test-timeout: 90
require-passing-tests-on: ['Linux', 'Android', 'FreeBSD', 'Windows']
run-qmllint: True

View File

@@ -7,15 +7,15 @@
cmake_minimum_required(VERSION 3.16)
# KDE Applications version, managed by release script.
set(RELEASE_SERVICE_VERSION_MAJOR "25")
set(RELEASE_SERVICE_VERSION_MINOR "11")
set(RELEASE_SERVICE_VERSION_MAJOR "26")
set(RELEASE_SERVICE_VERSION_MINOR "03")
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})
set(KF_MIN_VERSION "6.16")
set(QT_MIN_VERSION "6.5")
set(KF_MIN_VERSION "6.17")
set(QT_MIN_VERSION "6.9")
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
@@ -24,7 +24,7 @@ set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(KDE_COMPILERSETTINGS_LEVEL 6.0)
set(KDE_COMPILERSETTINGS_LEVEL 6.17)
include(FeatureSummary)
include(ECMSetupVersion)
@@ -39,18 +39,17 @@ include(ECMCheckOutboundLicense)
include(ECMQtDeclareLoggingCategory)
include(ECMAddAndroidApk)
include(ECMQmlModule)
include(ECMDeprecationSettings)
include(GenerateExportHeader)
include(ECMGenerateHeaders)
if (NOT ANDROID)
include(KDEClangFormat)
endif()
if(NEOCHAT_FLATPAK)
include(cmake/Flatpak.cmake)
endif()
set(QUOTIENT_FORCE_NAMESPACED_INCLUDES TRUE)
ecm_set_disabled_deprecation_versions(Qt 6.9.0 KF 6.17.0)
ecm_setup_version(${PROJECT_VERSION}
VARIABLE_PREFIX NEOCHAT
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
@@ -66,7 +65,7 @@ if (QT_KNOWN_POLICY_QTP0004)
qt_policy(SET QTP0004 NEW)
endif ()
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels IconThemes ColorScheme)
find_package(KF6 ${KF_MIN_VERSION} COMPONENTS Kirigami I18n Notifications Config CoreAddons Sonnet ItemModels ColorScheme IconThemes)
set_package_properties(KF6 PROPERTIES
TYPE REQUIRED
PURPOSE "Basic application components"
@@ -75,7 +74,7 @@ set_package_properties(KF6Kirigami PROPERTIES
TYPE REQUIRED
PURPOSE "Kirigami application UI framework"
)
find_package(KF6KirigamiAddons 1.6.0 REQUIRED)
find_package(KF6KirigamiAddons 1.10.0 REQUIRED)
if (UNIX AND NOT APPLE AND NOT ANDROID AND NOT NEOCHAT_FLATPAK AND NOT NEOCHAT_APPIMAGE)
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Purpose)
@@ -89,7 +88,7 @@ if(ANDROID)
)
else()
find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Widgets)
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem Crash)
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem)
find_package(KF6SyntaxHighlighting ${KF_MIN_VERSION} REQUIRED)
set_package_properties(KF6QQC2DesktopStyle PROPERTIES
TYPE RUNTIME
@@ -107,7 +106,7 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED)
endif()
find_package(QuotientQt6 0.9.1)
find_package(QuotientQt6 0.9.3)
set_package_properties(QuotientQt6 PROPERTIES
TYPE REQUIRED
DESCRIPTION "Qt wrapper around Matrix API"

View File

@@ -93,6 +93,12 @@ ecm_add_test(
TEST_NAME actionstest
)
ecm_add_test(
servernoticestest.cpp
LINK_LIBRARIES neochat Qt::Test neochat_server
TEST_NAME servernoticestest
)
ecm_add_test(
roommanagertest.cpp
LINK_LIBRARIES neochat Qt::Test neochat_server

View File

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

View File

@@ -43,13 +43,13 @@ void MessageContentModelTest::missingEvent()
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);
QCOMPARE(model1.data(model1.index(0), MessageContentModel::DisplayRole), u"Loading"_s);
auto model2 = EventMessageContentModel(room, u"$153456789:example.org"_s, true);
QCOMPARE(model2.rowCount(), 1);
QCOMPARE(model2.data(model2.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
QCOMPARE(model2.data(model2.index(0), MessageContentModel::DisplayRole), u"Loading reply"_s);
QCOMPARE(model2.data(model2.index(0), MessageContentModel::DisplayRole), u"Loading reply"_s);
room->syncNewEvents(u"test-min-sync.json"_s);
QCOMPARE(model1.rowCount(), 2);

View File

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

View File

@@ -4,15 +4,12 @@
#include "server.h"
#include <QFile>
#include <QHttpServer>
#include <QHttpServerResponder>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkReply>
#include <QSslCertificate>
#include <QSslKey>
#include <QSslServer>
#include <QUuid>
#include <Quotient/networkaccessmanager.h>
@@ -109,113 +106,20 @@ void Server::start()
m_server.route(u"/_matrix/client/v3/rooms/<arg>/invite"_s,
QHttpServerRequest::Method::Post,
[this](const QString &roomId, QHttpServerResponder &responder, const QHttpServerRequest &request) {
m_invitedUsers[roomId] += QJsonDocument::fromJson(request.body()).object()[u"user_id"_s].toString();
Changes changes;
changes.invitations += Changes::InviteUser{
.userId = QJsonDocument::fromJson(request.body()).object()[u"user_id"_s].toString(),
.roomId = roomId,
};
m_state += changes;
responder.write(QJsonDocument(QJsonObject{}), QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/r0/sync"_s, QHttpServerRequest::Method::Get, [this](QHttpServerResponder &responder) {
QMap<QString, QJsonArray> stateEvents;
QMap<QString, QJsonArray> roomAccountData;
for (const auto &roomData : m_roomsToCreate) {
stateEvents[roomData.id] += QJsonObject{
{u"content"_s, QJsonObject{{u"room_version"_s, u"11"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, roomData.id},
{u"sender"_s, roomData.members[0]},
{u"state_key"_s, QString()},
{u"type"_s, u"m.room.create"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
for (const auto &member : roomData.members) {
stateEvents[roomData.id] += QJsonObject{
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"join"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, roomData.id},
{u"sender"_s, member},
{u"state_key"_s, member},
{u"type"_s, u"m.room.member"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
}
QJsonObject tags;
for (const auto &tag : roomData.tags) {
tags[tag] = QJsonObject();
}
roomAccountData[roomData.id] += QJsonObject{{u"type"_s, u"m.tag"_s}, {u"content"_s, QJsonObject{{u"tags"_s, tags}}}};
}
m_roomsToCreate.clear();
for (const auto &roomId : m_invitedUsers.keys()) {
const auto &values = m_invitedUsers[roomId];
for (const auto &value : values) {
stateEvents[roomId] += QJsonObject{
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"invite"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, roomId},
{u"sender"_s, u"@user:localhost:1234"_s},
{u"state_key"_s, value},
{u"type"_s, u"m.room.member"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
}
}
m_invitedUsers.clear();
for (const auto &roomId : m_bannedUsers.keys()) {
const auto &values = m_bannedUsers[roomId];
for (const auto &value : values) {
stateEvents[roomId] += QJsonObject{
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"ban"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, roomId},
{u"sender"_s, u"@user:localhost:1234"_s},
{u"state_key"_s, value},
{u"type"_s, u"m.room.member"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
}
}
m_bannedUsers.clear();
for (const auto &roomId : m_joinedUsers.keys()) {
const auto &values = m_joinedUsers[roomId];
for (const auto &value : values) {
stateEvents[roomId] += QJsonObject{
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"join"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, roomId},
{u"sender"_s, u"@user:localhost:1234"_s},
{u"state_key"_s, value},
{u"type"_s, u"m.room.member"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
}
}
m_joinedUsers.clear();
QJsonObject rooms;
auto keys = stateEvents.keys() + m_events.keys();
for (const auto &roomId : QSet(keys.begin(), keys.end())) {
rooms[roomId] = QJsonObject{
{u"state"_s, QJsonObject{{u"events"_s, stateEvents[roomId]}}},
{u"account_data"_s, QJsonObject{{u"events"_s, roomAccountData[roomId]}}},
{u"timeline"_s, QJsonObject{{u"events"_s, m_events[roomId]}}},
};
}
m_events.clear();
auto json = QJsonObject{{u"rooms"_s, QJsonObject{{u"join"_s, rooms}}}};
responder.write(QJsonDocument(json), QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/r0/sync"_s, QHttpServerRequest::Method::Get, this, &Server::sync);
QSslConfiguration config;
QFile key(QStringLiteral(DATA_DIR) + u"/localhost.key"_s);
key.open(QFile::ReadOnly);
void(key.open(QFile::ReadOnly));
config.setPrivateKey(QSslKey(&key, QSsl::Rsa));
config.setLocalCertificate(QSslCertificate::fromPath(QStringLiteral(DATA_DIR) + u"/localhost.crt"_s).front());
m_sslServer.setSslConfiguration(config);
@@ -229,46 +133,239 @@ void Server::start()
QString Server::createRoom(const QString &matrixId)
{
auto roomId = generateRoomId();
m_roomsToCreate += RoomData{
.members = {matrixId},
.id = roomId,
const auto roomId = generateRoomId();
Changes changes;
changes.newRooms += Changes::NewRoom{
.initialMembers = {matrixId},
.roomId = {roomId},
.tags = {},
};
m_state += changes;
return roomId;
}
void Server::inviteUser(const QString &roomId, const QString &matrixId)
{
m_invitedUsers[roomId] += matrixId;
Changes changes;
changes.invitations += Changes::InviteUser{
.userId = matrixId,
.roomId = roomId,
};
m_state += changes;
}
void Server::banUser(const QString &roomId, const QString &matrixId)
{
m_bannedUsers[roomId] += matrixId;
Changes changes;
changes.bans += Changes::BanUser{
.userId = matrixId,
.roomId = roomId,
};
m_state += changes;
}
void Server::joinUser(const QString &roomId, const QString &matrixId)
{
m_joinedUsers[roomId] += matrixId;
Changes changes;
changes.joins += Changes::JoinUser{
.userId = matrixId,
.roomId = roomId,
};
m_state += changes;
}
QString Server::createServerNoticesRoom(const QString &matrixId)
{
auto roomId = createRoom(matrixId);
m_roomsToCreate.last().tags = {u"m.server_notice"_s};
const auto roomId = generateRoomId();
Changes changes;
changes.newRooms += Changes::NewRoom{
.initialMembers = {matrixId},
.roomId = {roomId},
.tags = {u"m.server_notice"_s},
};
m_state += changes;
return roomId;
}
QString Server::sendEvent(const QString &roomId, const QString &eventType, const QJsonObject &content)
{
Changes changes;
const auto eventId = generateEventId();
m_events[roomId] += QJsonObject{
{u"type"_s, eventType},
{u"content"_s, content},
{u"sender"_s, u"@foo:server.com"_s},
{u"event_id"_s, eventId},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
changes.events += Changes::Event{
.fullJson = QJsonObject{{u"type"_s, eventType},
{u"content"_s, content},
{u"sender"_s, u"@foo:server.com"_s},
{u"event_id"_s, eventId},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, roomId}},
};
m_state += changes;
return eventId;
}
void Server::sync(const QHttpServerRequest &request, QHttpServerResponder &responder)
{
QJsonObject joinRooms;
auto token = request.query().queryItemValue(u"since"_s).toInt();
for (const auto &change : m_state.mid(token)) {
for (const auto &newRoom : change.newRooms) {
QJsonArray stateEvents;
stateEvents += QJsonObject{
{u"content"_s, QJsonObject{{u"room_version"_s, u"11"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, newRoom.roomId},
{u"sender"_s, newRoom.initialMembers[0]},
{u"state_key"_s, QString()},
{u"type"_s, u"m.room.create"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
for (const auto &member : newRoom.initialMembers) {
stateEvents += QJsonObject{
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"join"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, newRoom.roomId},
{u"sender"_s, member},
{u"state_key"_s, member},
{u"type"_s, u"m.room.member"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
}
auto room = QJsonObject{{u"state"_s, QJsonObject{{u"events"_s, stateEvents}}}};
QJsonArray roomAccountData;
QJsonObject tags;
for (const auto &tag : newRoom.tags) {
tags[tag] = QJsonObject();
}
if (!tags.empty()) {
roomAccountData += QJsonObject{{u"type"_s, u"m.tag"_s}, {u"content"_s, QJsonObject{{u"tags"_s, tags}}}};
}
if (roomAccountData.size() > 0) {
room[u"account_data"] = QJsonObject{{u"events"_s, roomAccountData}};
}
joinRooms[newRoom.roomId] = room;
}
}
for (const auto &change : m_state.mid(token)) {
for (const auto &invitation : change.invitations) {
// TODO: The invitation could be for a room we haven't joined yet. Shouldn't be necessary for now, though.
auto stateEvents = joinRooms[invitation.roomId][u"state"_s][u"events"_s].toArray();
stateEvents += QJsonObject{
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"invite"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, invitation.roomId},
{u"sender"_s, u"@user:localhost:1234"_s},
{u"state_key"_s, invitation.userId},
{u"type"_s, u"m.room.member"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
if (joinRooms.contains(invitation.roomId)) {
auto room = joinRooms[invitation.roomId].toObject();
room[u"state"_s] = QJsonObject{{u"events"_s, stateEvents}};
joinRooms[invitation.roomId] = room;
} else {
joinRooms[invitation.roomId] = QJsonObject{{u"state"_s,
QJsonObject{
{u"events"_s, stateEvents},
}}};
}
}
}
for (const auto &change : m_state.mid(token)) {
for (const auto &ban : change.bans) {
// TODO: The ban could be for a room we haven't joined yet. Shouldn't be necessary for now, though.
auto stateEvents = joinRooms[ban.roomId][u"state"_s][u"events"_s].toArray();
stateEvents += QJsonObject{
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"ban"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, ban.roomId},
{u"sender"_s, u"@user:localhost:1234"_s},
{u"state_key"_s, ban.userId},
{u"type"_s, u"m.room.member"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
if (joinRooms.contains(ban.roomId)) {
auto room = joinRooms[ban.roomId].toObject();
room[u"state"_s] = QJsonObject{{u"events"_s, stateEvents}};
joinRooms[ban.roomId] = room;
} else {
joinRooms[ban.roomId] = QJsonObject{{u"state"_s,
QJsonObject{
{u"events"_s, stateEvents},
}}};
}
}
}
for (const auto &change : m_state.mid(token)) {
for (const auto &join : change.joins) {
// TODO: The join could be for a room we haven't joined yet. Shouldn't be necessary for now, though.
auto stateEvents = joinRooms[join.roomId][u"state"_s][u"events"_s].toArray();
stateEvents += QJsonObject{
{u"content"_s, QJsonObject{{u"displayname"_s, u"User"_s}, {u"membership"_s, u"join"_s}}},
{u"event_id"_s, generateEventId()},
{u"origin_server_ts"_s, QDateTime::currentMSecsSinceEpoch()},
{u"room_id"_s, join.roomId},
{u"sender"_s, u"@user:localhost:1234"_s},
{u"state_key"_s, join.userId},
{u"type"_s, u"m.room.member"_s},
{u"unsigned"_s, QJsonObject{{u"age"_s, 1234}}},
};
if (joinRooms.contains(join.roomId)) {
auto room = joinRooms[join.roomId].toObject();
room[u"state"_s] = QJsonObject{{u"events"_s, stateEvents}};
joinRooms[join.roomId] = room;
} else {
joinRooms[join.roomId] = QJsonObject{{u"state"_s,
QJsonObject{
{u"events"_s, stateEvents},
}}};
}
}
}
for (const auto &change : m_state.mid(token)) {
for (const auto &event : change.events) {
// TODO the room might be in a different join state.
auto timeline = joinRooms[event.fullJson[u"room_id"_s].toString()][u"timeline"_s][u"events"_s].toArray();
timeline += event.fullJson;
if (joinRooms.contains(event.fullJson[u"room_id"_s].toString())) {
auto room = joinRooms[event.fullJson[u"room_id"_s].toString()].toObject();
room[u"timeline"_s] = QJsonObject{{u"events"_s, timeline}};
joinRooms[event.fullJson[u"room_id"_s].toString()] = room;
} else {
joinRooms[event.fullJson[u"room_id"_s].toString()] = QJsonObject{
{u"timeline"_s, QJsonObject{{u"events"_s, timeline}}},
};
}
}
}
QJsonObject syncData = {
// {u"account_data"_s, QJsonObject {}},
// {u"presence"_s, QJsonObject {}},
{u"next_batch"_s, QString::number(m_state.size())},
};
QJsonObject rooms;
if (!joinRooms.isEmpty()) {
rooms[u"join"_s] = joinRooms;
}
if (!rooms.empty()) {
syncData[u"rooms"_s] = rooms;
}
qWarning() << syncData;
responder.write(QJsonDocument(syncData), QHttpServerResponder::StatusCode::Ok);
}

View File

@@ -2,16 +2,51 @@
// SPDX-License-Identifier: LGPL-2.0-or-later
#include <QHttpServer>
#include <QJsonObject>
#include <QSslServer>
struct Changes {
struct NewRoom {
QStringList initialMembers;
QString roomId;
QStringList tags;
};
QList<NewRoom> newRooms;
struct InviteUser {
QString userId;
QString roomId;
};
QList<InviteUser> invitations;
struct BanUser {
QString userId;
QString roomId;
};
QList<BanUser> bans;
struct JoinUser {
QString userId;
QString roomId;
};
QList<JoinUser> joins;
struct Event {
QJsonObject fullJson;
};
QList<Event> events;
};
struct RoomData {
QStringList members;
QString id;
QStringList tags;
};
class Server
class Server : public QObject
{
Q_OBJECT
public:
Server();
@@ -37,10 +72,7 @@ private:
QHttpServer m_server;
QSslServer m_sslServer;
QHash<QString, QList<QString>> m_invitedUsers;
QHash<QString, QList<QString>> m_bannedUsers;
QHash<QString, QList<QString>> m_joinedUsers;
void sync(const QHttpServerRequest &request, QHttpServerResponder &responder);
QList<RoomData> m_roomsToCreate;
QMap<QString, QJsonArray> m_events;
QList<Changes> m_state;
};

View File

@@ -0,0 +1,87 @@
// SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QObject>
#include <QSignalSpy>
#include <QTest>
#include <KLocalizedString>
#include <Quotient/connection.h>
#include <Quotient/eventstats.h>
#include <Quotient/quotient_common.h>
#include <Quotient/syncdata.h>
#include "accountmanager.h"
#include "neochatroom.h"
#include "roommanager.h"
#include "server.h"
#include "testutils.h"
using namespace Quotient;
class ServerNoticesTest : public QObject
{
Q_OBJECT
private:
NeoChatConnection *connection = nullptr;
Server server;
private Q_SLOTS:
void initTestCase();
void test();
};
void ServerNoticesTest::initTestCase()
{
Connection::setRoomType<NeoChatRoom>();
server.start();
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
auto accountManager = new AccountManager(true);
QSignalSpy spy(accountManager, &AccountManager::connectionAdded);
connection = dynamic_cast<NeoChatConnection *>(accountManager->accounts()->front());
QVERIFY(connection);
auto roomId = server.createRoom(u"@user:localhost:1234"_s);
RoomManager::instance().setConnection(connection);
QSignalSpy syncSpy(connection, &Connection::syncDone);
// We need to wait for two syncs, as the next one won't have the changes yet
QVERIFY(syncSpy.wait());
QVERIFY(syncSpy.wait());
auto room = dynamic_cast<NeoChatRoom *>(connection->room(roomId));
QVERIFY(room);
}
void ServerNoticesTest::test()
{
auto roomTreeModel = RoomManager::instance().roomTreeModel();
QCOMPARE(roomTreeModel->rowCount(roomTreeModel->index(NeoChatRoomType::ServerNotice, 0)), 0);
auto sortFilterRoomTreeModel = RoomManager::instance().sortFilterRoomTreeModel();
const auto roomId = server.createServerNoticesRoom(u"@user:localhost:1234"_s);
QSignalSpy syncSpy(connection, &Connection::syncDone);
QVERIFY(syncSpy.wait());
QVERIFY(syncSpy.wait());
const auto room = dynamic_cast<NeoChatRoom *>(connection->room(roomId));
QVERIFY(connection->room(roomId)->isServerNoticeRoom());
QCOMPARE(roomTreeModel->rowCount(roomTreeModel->index(NeoChatRoomType::ServerNotice, 0)), 1);
QCOMPARE(sortFilterRoomTreeModel->mapFromSource(roomTreeModel->indexForRoom(room)).parent().row(), 1 /* Below the normal room */);
server.sendEvent(roomId,
u"m.room.message"_s,
QJsonObject{
{u"body"_s, u"Foo"_s},
{u"format"_s, u"org.matrix.custom.html"_s},
{u"formatted_body"_s, u"Foo"_s},
{u"msgtype"_s, u"m.text"_s},
});
QVERIFY(syncSpy.wait());
QVERIFY(syncSpy.wait());
sortFilterRoomTreeModel->invalidate();
QCOMPARE(sortFilterRoomTreeModel->mapFromSource(roomTreeModel->indexForRoom(room)).parent().row(), 0);
room->markAllMessagesAsRead();
QCOMPARE(sortFilterRoomTreeModel->mapFromSource(roomTreeModel->indexForRoom(room)).parent().row(), 1 /* Below the normal room */);
}
QTEST_GUILESS_MAIN(ServerNoticesTest)
#include "servernoticestest.moc"

View File

@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QTest>
#include <Quotient/events/event.h>
#include <Quotient/syncdata.h>
@@ -32,7 +33,7 @@ public:
if (!syncFileName.isEmpty()) {
QFile testSyncFile;
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + syncFileName);
testSyncFile.open(QIODevice::ReadOnly);
Q_UNUSED(testSyncFile.open(QIODevice::ReadOnly));
const auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll());
Quotient::SyncRoomData roomData(id(), Quotient::JoinState::Join, testSyncJson.object());
update(std::move(roomData));
@@ -46,7 +47,7 @@ inline Quotient::event_ptr_tt<EventT> loadEventFromFile(const QString &eventFile
if (!eventFileName.isEmpty()) {
QFile testEventFile;
testEventFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + eventFileName);
testEventFile.open(QIODevice::ReadOnly);
Q_UNUSED(testEventFile.open(QIODevice::ReadOnly));
auto testSyncJson = QJsonDocument::fromJson(testEventFile.readAll()).object();
return Quotient::loadEvent<EventT>(testSyncJson);
}

View File

@@ -34,6 +34,10 @@ private Q_SLOTS:
void stripDisallowedTags();
void stripDisallowedAttributes();
void emptyCodeTags();
void addStyle_data();
void addStyle();
void dontAddStyle_data();
void dontAddStyle();
void sendSimpleStringCase();
void sendSingleParaMarkup();
@@ -71,6 +75,9 @@ private Q_SLOTS:
void componentOutput_data();
void componentOutput();
void updateSpoiler_data();
void updateSpoiler();
};
void TextHandlerTest::initTestCase()
@@ -89,21 +96,26 @@ void TextHandlerTest::initTestCase()
void TextHandlerTest::allowedAttributes()
{
auto theme = static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
const QString testInputString1 = u"<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>"_s;
const QString testOutputString1 = u"<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>"_s;
const QString testOutputString1S = u"<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>"_s;
const QString testOutputString1R = u"<span data-mx-spoiler style=\"color: transparent; background: %1;\"><font color=#FFFFFF>Test</font><span>"_s.arg(
theme->alternateBackgroundColor().name());
// Handle urls where the href has either single (') or double (") quotes.
const QString testInputString2 = u"<a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a>"_s;
const QString testOutputString2 = u"<a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a>"_s;
const QString testOutputString2S = u"<a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a>"_s;
const QString testOutputString2R =
u"<a href=\"https://kde.org\" style=\"text-decoration: none;\">link</a><a href='https://kde.org' style=\"text-decoration: none;\">link</a>"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString1);
QCOMPARE(testTextHandler.handleSendText(), testOutputString1);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString1);
QCOMPARE(testTextHandler.handleSendText(), testOutputString1S);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString1R);
testTextHandler.setData(testInputString2);
QCOMPARE(testTextHandler.handleSendText(), testOutputString2);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString2);
QCOMPARE(testTextHandler.handleSendText(), testOutputString2S);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString2R);
}
void TextHandlerTest::stripDisallowedTags()
@@ -146,6 +158,56 @@ void TextHandlerTest::emptyCodeTags()
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
}
void TextHandlerTest::addStyle_data()
{
QTest::addColumn<QString>("testInputString");
QTest::addColumn<QString>("testOutputString");
QTest::newRow("link") << u"<a href=\"https://kde.org\">link</a>"_s << u"<a href=\"https://kde.org\" style=\"text-decoration: none;\">link</a>"_s;
QTest::newRow("table")
<< u"<table><tr><th>Company</th><th>Contact</th><th>Country</th></tr><tr><td>Alfreds Futterkiste</td><td>Maria Anders</td><td>Germany</td></tr><tr><td>Centro comercial Moctezuma</td><td>Francisco Chang</td><td>Mexico</td></tr></table>"_s
<< u"<table style=\"width: 100%; border-collapse: collapse; border: 1px; border-style: solid;\"><tr><th style=\"border: 1px solid black; padding: 3px;\">Company</th><th style=\"border: 1px solid black; padding: 3px;\">Contact</th><th style=\"border: 1px solid black; padding: 3px;\">Country</th></tr><tr><td style=\"border: 1px solid black; padding: 3px;\">Alfreds Futterkiste</td><td style=\"border: 1px solid black; padding: 3px;\">Maria Anders</td><td style=\"border: 1px solid black; padding: 3px;\">Germany</td></tr><tr><td style=\"border: 1px solid black; padding: 3px;\">Centro comercial Moctezuma</td><td style=\"border: 1px solid black; padding: 3px;\">Francisco Chang</td><td style=\"border: 1px solid black; padding: 3px;\">Mexico</td></tr></table>"_s;
auto theme = static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
QTest::newRow("spoiler") << u"<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>"_s
<< u"<span data-mx-spoiler style=\"color: transparent; background: %1;\"><font color=#FFFFFF>Test</font><span>"_s.arg(
theme->alternateBackgroundColor().name());
}
void TextHandlerTest::addStyle()
{
QFETCH(QString, testInputString);
QFETCH(QString, testOutputString);
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
}
void TextHandlerTest::dontAddStyle_data()
{
QTest::addColumn<QString>("testInputString");
QTest::addColumn<QString>("testOutputString");
QTest::newRow("link") << u"<a href=\"https://kde.org\">link</a>"_s << u"<a href=\"https://kde.org\">link</a>"_s;
QTest::newRow("table")
<< u"<table><tr><th>Company</th><th>Contact</th><th>Country</th></tr><tr><td>Alfreds Futterkiste</td><td>Maria Anders</td><td>Germany</td></tr><tr><td>Centro comercial Moctezuma</td><td>Francisco Chang</td><td>Mexico</td></tr></table>"_s
<< u"<table><tr><th>Company</th><th>Contact</th><th>Country</th></tr><tr><td>Alfreds Futterkiste</td><td>Maria Anders</td><td>Germany</td></tr><tr><td>Centro comercial Moctezuma</td><td>Francisco Chang</td><td>Mexico</td></tr></table>"_s;
QTest::newRow("spoiler") << u"<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>"_s
<< u"<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>"_s;
}
void TextHandlerTest::dontAddStyle()
{
QFETCH(QString, testInputString);
QFETCH(QString, testOutputString);
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
}
void TextHandlerTest::sendSimpleStringCase()
{
const QString testInputString = u"This data should just be left alone."_s;
@@ -338,7 +400,8 @@ void TextHandlerTest::receiveRichInPlainOut()
void TextHandlerTest::receivePlainTextIn()
{
const QString testInputString = u"<plain text in tag bracket>\nTest link https://kde.org."_s;
const QString testOutputStringRich = u"&lt;plain text in tag bracket&gt;<br>Test link <a href=\"https://kde.org\">https://kde.org</a>."_s;
const QString testOutputStringRich =
u"&lt;plain text in tag bracket&gt;<br>Test link <a href=\"https://kde.org\" style=\"text-decoration: none;\">https://kde.org</a>."_s;
QString testOutputStringPlain = u"<plain text in tag bracket>\nTest link https://kde.org."_s;
// Make sure quotes are maintained in a plain string.
@@ -408,7 +471,7 @@ void TextHandlerTest::receivePlainStripMarkup()
void TextHandlerTest::receiveRichUserPill()
{
const QString testInputString = u"<p><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></p>"_s;
const QString testOutputString = u"<b><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></b>"_s;
const QString testOutputString = u"<b><a href=\"https://matrix.to/#/@alice:example.org\" style=\"text-decoration: none;\">@alice:example.org</a></b>"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -460,21 +523,23 @@ void TextHandlerTest::receiveRichPlainUrl_data()
// so we can confirm consistent behaviour for complex urls.
QTest::addRow("link 1")
<< u"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im <a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">Link already rich</a>"_s
<< u"<a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im</a> <a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">Link already rich</a>"_s;
<< u"<a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\" style=\"text-decoration: none;\">https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im</a> <a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\" style=\"text-decoration: none;\">Link already rich</a>"_s;
// Another real case. The linkification wasn't handling it when a single link
// contains what looks like and email. It was broken into 3 but needs to
// be just single link.
QTest::addRow("link 2")
<< u"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/"_s
<< u"<a href=\"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/\">https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/</a>"_s;
<< u"<a href=\"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/\" style=\"text-decoration: none;\">https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/</a>"_s;
QTest::addRow("email") << uR"(email@example.com <a href="mailto:email@example.com">Link already rich</a>)"_s
<< uR"(<a href="mailto:email@example.com">email@example.com</a> <a href="mailto:email@example.com">Link already rich</a>)"_s;
QTest::addRow("email")
<< uR"(email@example.com <a href="mailto:email@example.com">Link already rich</a>)"_s
<< uR"(<a href="mailto:email@example.com" style="text-decoration: none;">email@example.com</a> <a href="mailto:email@example.com" style="text-decoration: none;">Link already rich</a>)"_s;
QTest::addRow("mxid")
<< u"@user:kde.org <a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a>"_s
<< u"<b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a></b>"_s;
QTest::addRow("mxid with prefix") << u"a @user:kde.org b"_s << u"a <b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> b"_s;
<< u"<b><a href=\"https://matrix.to/#/@user:kde.org\" style=\"text-decoration: none;\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\" style=\"text-decoration: none;\">Link already rich</a></b>"_s;
QTest::addRow("mxid with prefix") << u"a @user:kde.org b"_s
<< u"a <b><a href=\"https://matrix.to/#/@user:kde.org\" style=\"text-decoration: none;\">@user:kde.org</a></b> b"_s;
}
/**
@@ -596,5 +661,35 @@ void TextHandlerTest::componentOutput()
QCOMPARE(testTextHandler.textComponents(testInputString), testOutputComponents);
}
void TextHandlerTest::updateSpoiler_data()
{
QTest::addColumn<QString>("testInputString");
QTest::addColumn<QString>("testOutputString");
QTest::addColumn<bool>("spoilerRevealed");
auto theme = static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
QTest::newRow("same length") << u"<span data-mx-spoiler style=\"color: #123456; background: #123456;\">Test<span>"_s
<< u"<span data-mx-spoiler style=\"color: transparent; background: %1;\">Test<span>"_s.arg(
theme->alternateBackgroundColor().name())
<< false;
QTest::newRow("different length") << u"<span data-mx-spoiler style=\"color: short; background: looooooooooong;\">Test<span>"_s
<< u"<span data-mx-spoiler style=\"color: transparent; background: %1;\">Test<span>"_s.arg(
theme->alternateBackgroundColor().name())
<< false;
QTest::newRow("spoiler revealed")
<< u"<span data-mx-spoiler style=\"color: transparent; background: %1;\">Test<span>"_s.arg(theme->alternateBackgroundColor().name())
<< u"<span data-mx-spoiler style=\"color: %1; background: %2;\">Test<span>"_s.arg(theme->textColor().name(), theme->alternateBackgroundColor().name())
<< true;
}
void TextHandlerTest::updateSpoiler()
{
QFETCH(QString, testInputString);
QFETCH(QString, testOutputString);
QFETCH(bool, spoilerRevealed);
QCOMPARE(TextHandler::updateSpoilerText(this, testInputString, spoilerRevealed), testOutputString);
}
QTEST_MAIN(TextHandlerTest)
#include "texthandlertest.moc"

View File

@@ -161,7 +161,7 @@ void TimelineMessageModelTest::pendingEvent()
// different every time.
QFile testSyncFile;
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + u"test-pending-sync.json"_s);
testSyncFile.open(QIODevice::ReadOnly);
QVERIFY(testSyncFile.open(QIODevice::ReadOnly));
auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll());
auto root = testSyncJson.object();
auto timeline = root["timeline"_L1].toObject();
@@ -208,7 +208,7 @@ void TimelineMessageModelTest::idToRow()
auto room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-min-sync.json"_s);
model->setRoom(room);
QCOMPARE(model->indexforEventId(u"$153456789:example.org"_s).row(), 0);
QCOMPARE(model->indexForEventId(u"$153456789:example.org"_s).row(), 0);
}
void TimelineMessageModelTest::cleanup()

View File

@@ -1,14 +0,0 @@
# SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
# SPDX-License-Identifier: BSD-2-Clause
include(GNUInstallDirs)
# Include FontConfig config which uses the Emoji One font from the
# KDE Flatpak SDK.
install(
FILES
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Flatpak/99-noto-mono-color-emoji.conf
DESTINATION
${CMAKE_INSTALL_SYSCONFDIR}/fonts/local.conf
)

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<alias>
<family>serif</family>
<prefer>
<family>Noto Color Emoji</family>
</prefer>
</alias>
<alias>
<family>sans-serif</family>
<prefer>
<family>Noto Color Emoji</family>
</prefer>
</alias>
<alias>
<family>monospace</family>
<prefer>
<family>Noto Color Emoji</family>
</prefer>
</alias>
</fontconfig>

View File

@@ -3,12 +3,12 @@
add_definitions(-DDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}" )
qt_add_executable(timeline-memtest
qt_add_executable(timeline_memtest
main.cpp
)
target_link_libraries(timeline-memtest PRIVATE neochatplugin Timelineplugin)
target_link_libraries(timeline-memtest PUBLIC
target_link_libraries(timeline_memtest PRIVATE neochatplugin Timelineplugin)
target_link_libraries(timeline_memtest PUBLIC
Qt::Core
Qt::Quick
Qt::Qml
@@ -16,14 +16,13 @@ target_link_libraries(timeline-memtest PUBLIC
Qt::QuickControls2
Qt::Widgets
KF6::I18nQml
KF6::Kirigami
QuotientQt6
LibNeoChat
Timeline
)
ecm_add_qml_module(timeline-memtest URI org.kde.neochat.timeline-memtest GENERATE_PLUGIN_SOURCE
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/timeline-memtest
ecm_add_qml_module(timeline_memtest URI org.kde.neochat.timeline_memtest GENERATE_PLUGIN_SOURCE
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/timeline_memtest
QML_FILES
Main.qml
SOURCES

View File

@@ -28,7 +28,7 @@ int main(int argc, char **argv)
engine.rootContext()->setContextProperty(u"memTestTimelineModel"_s, memTestTimelineModel);
engine.rootContext()->setContextProperty(u"messageFilterModel"_s, messageFilterModel);
engine.loadFromModule("org.kde.neochat.timeline-memtest", "Main");
engine.loadFromModule("org.kde.neochat.timeline_memtest", "Main");
return app.exec();
}

View File

@@ -37,7 +37,11 @@ public:
if (!syncFileName.isEmpty()) {
QFile testSyncFile;
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + syncFileName);
testSyncFile.open(QIODevice::ReadOnly);
auto ok = testSyncFile.open(QIODevice::ReadOnly);
if (!ok) {
qWarning() << "Failed to open" << testSyncFile.fileName() << testSyncFile.errorString();
}
auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll()).object();
auto timelineJson = testSyncJson["timeline"_L1].toObject();
timelineJson["events"_L1] = multiplyEvents(timelineJson["events"_L1].toArray(), 100);

View File

@@ -41,6 +41,7 @@
<name xml:lang="pl">NeoChat</name>
<name xml:lang="pt">NeoChat</name>
<name xml:lang="pt-BR">NeoChat</name>
<name xml:lang="ro">NeoChat</name>
<name xml:lang="ru">NeoChat</name>
<name xml:lang="sa">नवचैट्</name>
<name xml:lang="sk">NeoChat</name>
@@ -49,7 +50,6 @@
<name xml:lang="ta">நியோச்சாட்</name>
<name xml:lang="tr">NeoChat</name>
<name xml:lang="uk">NeoChat</name>
<name xml:lang="x-test">xxNeoChatxx</name>
<name xml:lang="zh-CN">NeoChat</name>
<name xml:lang="zh-TW">NeoChat</name>
<summary>Chat on Matrix</summary>
@@ -76,6 +76,7 @@
<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="ro">Discutați pe Matrix</summary>
<summary xml:lang="ru">Общение в Matrix</summary>
<summary xml:lang="sa">Matrix इत्यत्र गपशपं कुर्वन्तु</summary>
<summary xml:lang="sl">Klepet na Matrixu</summary>
@@ -83,7 +84,6 @@
<summary xml:lang="ta">மேட்ரிக்ஸுக்கான உரையாடல் செயலி</summary>
<summary xml:lang="tr">Matrix Üzerinde Sohbet</summary>
<summary xml:lang="uk">Спілкування у Matrix</summary>
<summary xml:lang="x-test">xxChat on Matrixxx</summary>
<summary xml:lang="zh-TW">在 Matrix 上聊天</summary>
<description>
<p>NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
@@ -111,16 +111,16 @@
<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="ro">NeoChat e o aplicație de discuții ce vă ajută să profitați din plin de rețeaua Matrix. Aceasta oferă o modalitate sigură de a trimite mesaje textuale, videoclipuri și fișiere audio familiei, colegilor și prietenilor.</p>
<p xml:lang="ru">NeoChat — приложение для общения, предоставляющее все преимущества сети Matrix. С его помощью можно безопасно отправлять текстовые сообщения, видеозаписи и звуковые файлы родственникам, коллегам и друзьям.</p>
<p xml:lang="sa">NeoChat इति एकं गपशप-अनुप्रयोगं यत् भवान् Matrix-जालस्य पूर्णं लाभं ग्रहीतुं शक्नोति । एतत् भवन्तं भवतः परिवाराय, सहकारिभ्यः, मित्रेभ्यः च पाठसन्देशान्, भिडियो, श्रव्यसञ्चिकाः च प्रेषयितुं सुरक्षितं मार्गं प्रदाति ।</p>
<p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p>
<p xml:lang="sv">NeoChat är ett chattprogram som låter dig dra full nytta av Matrix-nätverket. Det ger dig ett säkert sätt att skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner.</p>
<p xml:lang="tr">NeoChat, Matrix ağının tüm özelliklerini kullanan bir sohbet uygulamasıdır. Ailenize, arkadaşlarınıza ve iş arkadaşlarınıza metin iletileri, ses ve video dosyaları göndermenin kolay bir yolunu sunar.</p>
<p xml:lang="uk">NeoChat є програмою для спілкування, за допомогою якої ви можете скористатися усіма перевагами мережі Matrix. За її допомогою ви можете безпечно надсилати текстові повідомлення, відео та звукові файли вашим родичам, колегам та друзям.</p>
<p xml:lang="x-test">xxNeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.xx</p>
<p xml:lang="zh-TW">NeoChat 是一個讓您能夠完全利用 Matrix 網路的聊天應用程式。它讓您安全地傳送文字訊息、影片或音訊檔給家人、同事或朋友等等。</p>
<p>NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. يوفر نيوتشات كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP و تعدد الخيوط وبعض جوانب التشفير من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار، ولكن يبقى الهدف توفير الدعم النهائي للمواصفات بأكملها.</p>
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. يوفر نيوتشات كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP و تعدد الخيوط وبعض جوانب التعمية من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار، ولكن يبقى الهدف توفير تطبيق للمواصفات بأكملها.</p>
<p xml:lang="ca">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptatge d'extrem a extrem. Hi ha algunes altres omissions més petites a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu segueix sent proporcionar suport eventual per a tota l'especificació.</p>
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
<p xml:lang="de">NeoChat versucht eine vollumfängliche Anwendung für die Spezifikation von Matrix zu sein. Damit wird alles der aktuellen stabilen Spezifikation mit den erwähnenswerten Ausnahmen von VoIP, Diskussionsfäden und ein paar Teilen der Ende-zu-Ende-Verschlüsselung unterstützt. Zudem sind andere kleinere Auslassungen vorhanden, da sich die Matrixspezifikation ständig weiterentwickelt. Nichtsdestotrotz soll letztendlich die gesamte Spezifikation unterstützt werden.</p>
@@ -145,16 +145,16 @@
<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="ro">NeoChat vrea să fie o aplicație completă pentru specificațiile Matrix. Astfel, susține tot ce se găsește acum în specificațiile stabile cu excepția VoIP, a firelor de discuții, și a unor părți din criptarea punct-la-punct. Sunt și câteva omisiuni minore din cauza faptului că specificația Matrix evoluează continuu, dar scopul rămâne acela de a implementa întreaga specificație.</p>
<p xml:lang="ru">Целью создания NeoChat является полноценная реализация программы для спецификации Matrix. Как следствие, реализовано всё в текущей стабильной спецификации (за исключением голосовой интернет-связи, потоков и некоторых аспектов сквозного шифрования). Есть также несколько других незначительных пробелов, обусловленных постоянными изменениями спецификации Matrix. Тем не менее, стоит задача в итоге предоставить полную поддержку спецификации.</p>
<p xml:lang="sa">NeoChat इत्यस्य उद्देश्यं Matrix विनिर्देशस्य कृते पूर्णतया विशेषतायुक्तः अनुप्रयोगः भवितुम् अस्ति । यथा तथा वर्तमानस्थिरविनिर्देशे सर्वं VoIP इत्यस्य उल्लेखनीयअपवादैः सह, थ्रेड्स तथा च End-to-End Encryption इत्यस्य केचन पक्षाः समर्थिताः सन्ति । अन्ये कतिचन लघु लोपाः सन्ति यतोहि Matrix spec निरन्तरं विकसितः अस्ति परन्तु उद्देश्यं सम्पूर्ण spec कृते अन्ततः समर्थनं प्रदातुं अवशिष्टम् अस्ति</p>
<p xml:lang="sl">Neochat cilja, da bi bila popolna aplikacija po specifikaciji Matrixa. Kot takšna vsebuje vse v trenutni stabilni specifikaciji z pomembnimi izjemami pri VoIP, nitih in nekaterih vidikov šifriranja od konca do konca. Obstaja nekaj drugih manjših opustitev zaradi dejstva, da se specifikacija Matrix nenehno razvija, vendar cilj ostaja zagotoviti morebitno podporo celotni specifikaciji.</p>
<p xml:lang="sv">NeoChat har som mål att vara ett fullständigt program enligt Matrix-specifikationen. Som sådant stöds allt i den nuvarande stabila specifikationen, med de nämnvärda undantagen VoIP, trådar och några aspekter av kryptering hela vägen. Det finns några ytterligare utelämnanden på grund av att Matrix-specifikationen hela tiden utvecklas, men målet förblir att till slut erbjuda stöd för hela specifikationen.</p>
<p xml:lang="tr">NeoChat, Matrix belirtimi için tam özellikli bir uygulama olmayı hedefler. Bu nedenle; VoIP, ileti zincirleri ve Uçtan Uca Şifrelemenin bazı yönleri gibi dikkate değer istisnalar dışında var olan kararlı belirtimdeki her şey desteklenir. Matrix belirtiminin sürekli gelişmesi nedeniyle birkaç küçük eksiklik daha var; ancak amaç tüm belirtim için nihai destek sağlamak olmayı sürdürüyor.</p>
<p xml:lang="uk">Метою створення NeoChat є повноцінна реалізація програми для специфікації Matrix. Як наслідок, реалізовано усе у поточній стабільній специфікації, окрім голосового інтернет-зв'язку, потоків та деяких аспектів міжвузлового шифрування. Є також декілька інших незначних прогалин через те, що специфікація Matrix постійно змінюється, але метою лишається повна підтримка специфікації.</p>
<p xml:lang="x-test">xxNeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.xx</p>
<p xml:lang="zh-TW">NeoChat 以完整支援 Matrix 標準為目標,因此目前穩定版標準除了 VoIP、對話串與端對端加密的某些部分以外的所有部分都有支援。其他部分還有一些較小的不支援的部分這是因為 Matrix 標準隨時都在改進,但目標仍然時最終提供整個標準的完整支援。</p>
<p>Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:</p>
<p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يدعم نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p>
<p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يوفر نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p>
<p xml:lang="ca">A causa de la naturalesa del desenvolupament de l'especificació de Matrix, el NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
<p xml:lang="ca-valencia">A causa de la naturalea del desenvolupament de l'especificació de Matrix, NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
<p xml:lang="de">Durch die Weiterentwicklung der Matrix-Spezifikation unterstützt auch NeoChat einige als noch instabil gekennzeichnete Funktionen. Derzeit sind das:</p>
@@ -179,6 +179,7 @@
<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="ro">Datorită modului de dezvoltare a specificațiilor Matrix, NeoChat susține și numeroase caracteristici nestabile. Acum, acestea sunt:</p>
<p xml:lang="ru">В силу природы разработки спецификации Matrix в NeoChat тоже предусмотрена поддержка многочисленных нестабильных возможностей. В текущей версии это следующие возможности:</p>
<p xml:lang="sa">Matrix विनिर्देशविकासस्य प्रकृतेः कारणात् NeoChat अपि अनेकानाम् अस्थिरविशेषतानां समर्थनं करोति । सम्प्रति एते सन्ति :</p>
<p xml:lang="sl">Zaradi narave razvoja specifikacije Matrixa NeoChat podpira tudi številne nestabilne zmožnosti. Trenutno so to:</p>
@@ -186,7 +187,6 @@
<p xml:lang="ta">மேட்ரிக்ஸு நெறிமுறை வரையறுக்கப்படும் வித‍த்தின் காரணமாக, பல நிலையற்ற அம்சங்களையும் நியோச்சாட் ஆதரிக்கிறது. தற்போது ஆதரிக்கப்படுபவை:</p>
<p xml:lang="tr">NeoChat, Matrix belirtimi geliştirmesinin doğası gereği çok sayıda kararsız özelliği de destekler. Şu anda bunlar:</p>
<p xml:lang="uk">Через природу розробки специфікації Matrix, у NeoChat також передбачено підтримку численних нестабільних можливостей. У поточній версії цими можливостями є:</p>
<p xml:lang="x-test">xxDue to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:xx</p>
<p xml:lang="zh-TW">由於 Matrix 標準的開發流程的緣故NeoChat 也支援數個非穩定版的功能。目前這些功能是:</p>
<ul>
<li>Polls - MSC3381</li>
@@ -214,6 +214,7 @@
<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="ro">Sondaje - MSC3381</li>
<li xml:lang="ru">Голосования — MSC3381</li>
<li xml:lang="sa">मतदान - MSC3381</li>
<li xml:lang="sl">Polls - MSC3381</li>
@@ -221,7 +222,6 @@
<li xml:lang="ta">வாக்கெடுப்புகள் - MSC3381</li>
<li xml:lang="tr">Anketler — MSC3381</li>
<li xml:lang="uk">Опитування - MSC3381</li>
<li xml:lang="x-test">xxPolls - MSC3381xx</li>
<li xml:lang="zh-TW">投票 - MSC3381</li>
<li>Sticker Packs - MSC2545</li>
<li xml:lang="ar">حزم الملصقات - MSC2545</li>
@@ -248,6 +248,7 @@
<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="ro">Colecții de abțibilduri - MSC2545</li>
<li xml:lang="ru">Наборы стикеров — MSC2545</li>
<li xml:lang="sa">स्टिकर पैक - MSC2545</li>
<li xml:lang="sl">Sticker Packs - MSC2545</li>
@@ -255,7 +256,6 @@
<li xml:lang="ta">ஒட்டி தொகுப்புகள் - MSC2545</li>
<li xml:lang="tr">Çıkartma Paketleri — MSC2545</li>
<li xml:lang="uk">Пакунки наліпок - MSC2545</li>
<li xml:lang="x-test">xxSticker Packs - MSC2545xx</li>
<li xml:lang="zh-TW">貼圖包 - MSC2545</li>
<li>Location Events - MSC3488</li>
<li xml:lang="ar">موقع الأحداث - MSC3488</li>
@@ -282,6 +282,7 @@
<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="ro">Evenimente de amplasare - MSC3488</li>
<li xml:lang="ru">События местоположения — MSC3488</li>
<li xml:lang="sa">स्थान घटनाएँ - MSC3488</li>
<li xml:lang="sl">Location Events - MSC3488</li>
@@ -289,7 +290,6 @@
<li xml:lang="ta">இட நிகழ்வுகள் - MSC3488</li>
<li xml:lang="tr">Konum Etkinlikleri — MSC3488</li>
<li xml:lang="uk">Місцеві зустрічі - MSC3488</li>
<li xml:lang="x-test">xxLocation Events - MSC3488xx</li>
<li xml:lang="zh-TW">位置事件 - MSC3488</li>
</ul>
</description>
@@ -320,7 +320,6 @@
<value key="KDE::windows_store::StoreLogoSquare">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/storelogo-1080x1080.png</value>
<value key="KDE::windows_store::Icon">https://invent.kde.org/network/neochat/-/raw/master/icons/300-apps-neochat.png</value>
<value key="KDE::windows_store::PromotionalArt16x9">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/promoimage-1920x1080.png</value>
<value key="KDE::supporters">Tanguy Fardet;[dabe](https://freeradical.zone/@dabe);[lengau](https://mastodon.world/@lengau);Joshua Strobl;Stuart Turton</value>
</custom>
<launchable type="desktop-id">org.kde.neochat.desktop</launchable>
<screenshots>
@@ -352,6 +351,7 @@
<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="ro">Vederea principală cu lista de camere, discuție, și informații despre cameră</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>
@@ -359,7 +359,6 @@
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
<caption xml:lang="tr">Oda listesini, sohbet penceresini ve oda bilgisini gösteren ana görünüm</caption>
<caption xml:lang="uk">Головна панель із списком кімнат, спілкуванням та даними щодо кімнати</caption>
<caption xml:lang="x-test">xxMain view with room list, chat, and room informationxx</caption>
<caption xml:lang="zh-TW">主頁面,包含聊天室列表、聊天內容,與聊天室資訊</caption>
</screenshot>
<screenshot type="default">
@@ -389,6 +388,7 @@
<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="ro">Descoperiți comunități noi cu Spații 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>
@@ -396,7 +396,6 @@
<caption xml:lang="ta">மேட்ரிக்ஸு இடங்களின் மூலம் புதிய சமூகங்களைக் கண்டுபிடிக்கலாம்</caption>
<caption xml:lang="tr">Matrix Alanlar ile yeni topluluklar keşfedin</caption>
<caption xml:lang="uk">Пошук нових спільнот за допомогою Matrix Spaces</caption>
<caption xml:lang="x-test">xxDiscover new communities with Matrix Spacesxx</caption>
<caption xml:lang="zh-TW">利用 Matrix 聊天空間發現新的社群</caption>
</screenshot>
<!--
@@ -434,6 +433,7 @@
<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="ro">Vederea principală cu lista de camere, discuție, și informații despre cameră</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>
@@ -441,7 +441,6 @@
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
<caption xml:lang="tr">Oda listesini, sohbet penceresini ve oda bilgisini gösteren ana görünüm</caption>
<caption xml:lang="uk">Головна панель із списком кімнат, спілкуванням та даними щодо кімнати</caption>
<caption xml:lang="x-test">xxMain view with room list, chat, and room informationxx</caption>
<caption xml:lang="zh-TW">主頁面,包含聊天室列表、聊天內容,與聊天室資訊</caption>
</screenshot>
<screenshot environment="windows">
@@ -473,6 +472,7 @@
<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="ro">Ecran de autentificare</caption>
<caption xml:lang="ru">Окно входа</caption>
<caption xml:lang="sa">लॉगिन् स्क्रीन</caption>
<caption xml:lang="sl">Prijavni zaslon</caption>
@@ -480,7 +480,6 @@
<caption xml:lang="ta">நுழைவுத் திரை</caption>
<caption xml:lang="tr">Oturum açma ekranı</caption>
<caption xml:lang="uk">Вікно входу</caption>
<caption xml:lang="x-test">xxLogin screenxx</caption>
<caption xml:lang="zh-TW">登入畫面</caption>
</screenshot>
</screenshots>
@@ -488,6 +487,11 @@
<content_attribute id="social-chat">intense</content_attribute>
</content_rating>
<releases>
<release version="25.12.1" date="2026-01-08"/>
<release version="25.12.0" date="2025-12-11"/>
<release version="25.08.3" date="2025-11-06"/>
<release version="25.08.2" date="2025-10-09"/>
<release version="25.08.1" date="2025-09-11"/>
<release version="25.08.0" date="2025-08-14"/>
<release version="25.04.3" date="2025-07-03"/>
<release version="25.04.2" date="2025-06-05"/>

View File

@@ -44,7 +44,6 @@ Name[sv]=NeoChat
Name[ta]=நியோச்சாட்
Name[tr]=NeoChat
Name[uk]=NeoChat
Name[x-test]=xxNeoChatxx
Name[zh_CN]=NeoChat
Name[zh_TW]=NeoChat
GenericName=Matrix Client
@@ -88,7 +87,6 @@ GenericName[sv]=Matrix-klient
GenericName[ta]=Matrix வாங்கி
GenericName[tr]=Matrix İstemcisi
GenericName[uk]=Клієнт Matrix
GenericName[x-test]=xxMatrix Clientxx
GenericName[zh_CN]=Matrix 客户端
GenericName[zh_TW]=Matrix 用戶端
Comment=Chat on Matrix
@@ -110,10 +108,12 @@ Comment[ia]=Conversation en ditecto sur Matrix
Comment[it]= su Matrix
Comment[ka]=ჩატი Matrix-ზე
Comment[ko]=Matrix에서 대화하기
Comment[lt]=Pokalbiai per Matrix
Comment[lv]=Tērzējiet „Matrix“ tīklā
Comment[nl]=Chat op Matrix
Comment[pl]=Rozmawiaj na Matriksie
Comment[pt_BR]=Bate papo na Matrix
Comment[ro]=Discutați pe Matrix
Comment[ru]=Общение в Matrix
Comment[sa]=Matrix इत्यत्र गपशपं कुर्वन्तु
Comment[sl]=Klepet na Matrixu
@@ -121,7 +121,6 @@ Comment[sv]=Chatta på Matrix
Comment[ta]=மேட்ரிக்ஸில் உரையாட உதவும்
Comment[tr]=Matrix üzerinde sohbet edin
Comment[uk]=Спілкування у Matrix
Comment[x-test]=xxChat on Matrixxx
Comment[zh_CN]=在 Matrix 上聊天
Comment[zh_TW]=在 Matrix 上聊天
MimeType=x-scheme-handler/matrix;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

7146
po/ga/neochat.po Normal file

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

File diff suppressed because it is too large Load Diff

7339
po/ro/neochat.po Normal file

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

@@ -22,7 +22,7 @@ SPDX-License-Identifier: CC-BY-SA-4.0
>carl@carlschwan.eu</email
></author>
<date
>2022-11-01</date>
>20221101</date>
<releaseinfo
>22.09</releaseinfo>
<productname
@@ -111,9 +111,9 @@ SPDX-License-Identifier: CC-BY-SA-4.0
><title
>Telif Hakkı</title>
<para
>Telif hakkı &copy; 2020-2022 Tobias Fella </para>
>Telif hakkı &copy; 20202022 Tobias Fella </para>
<para
>Telif hakkı &copy; 2020-2022 Carl Schwan </para>
>Telif hakkı &copy; 20202022 Carl Schwan </para>
<para
>Lisans: GNU Genel Kamu Lisansa, 3. sürüm veya sonrası &lt;<ulink url="https://www.gnu.org/licenses/gpl-3.0.html"
>https://www.gnu.org/licenses/gpl-3.0.html</ulink

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

@@ -3,7 +3,7 @@
# SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
# SPDX-License-Identifier: BSD-2-Clause
add_library(neochat STATIC
qt_add_library(neochat STATIC
controller.cpp
controller.h
roommanager.cpp
@@ -35,6 +35,8 @@ add_library(neochat STATIC
models/commonroomsmodel.h
texttospeechhelper.h
texttospeechhelper.cpp
models/limitermodel.cpp
models/limitermodel.h
)
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
@@ -48,6 +50,8 @@ if(ANDROID OR WIN32)
set_source_files_properties(qml/GlobalMenuStub.qml PROPERTIES
QT_QML_SOURCE_TYPENAME GlobalMenu
)
else()
set(EXTRA_IMPORTS org.kde.purpose)
endif()
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
@@ -101,10 +105,12 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/ReasonDialog.qml
qml/NewPollDialog.qml
qml/UserMenu.qml
qml/MeetingDialog.qml
qml/SeenByDialog.qml
DEPENDENCIES
QtCore
QtQuick
com.github.quotient_im.libquotient
io.github.quotient_im.libquotient
IMPORTS
org.kde.neochat.libneochat
org.kde.neochat.rooms
@@ -117,8 +123,8 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
org.kde.neochat.login
org.kde.neochat.chatbar
org.kde.config
org.kde.purpose
org.kde.syntaxhighlighting
${EXTRA_IMPORTS}
)
if(NOT ANDROID AND NOT WIN32)
@@ -137,10 +143,17 @@ if(WIN32)
set_target_properties(neochat PROPERTIES OUTPUT_NAME "neochatlib")
endif()
add_executable(neochat-app
qt_add_executable(neochat-app
main.cpp
)
if(ANDROID)
set_target_properties(neochat-app PROPERTIES
OUTPUT_NAME "neochat-app"
PREFIX "lib"
)
endif()
if(TARGET Qt::WebView)
target_link_libraries(neochat-app PUBLIC Qt::WebView)
target_compile_definitions(neochat-app PUBLIC -DHAVE_WEBVIEW)
@@ -150,6 +163,7 @@ target_include_directories(neochat-app PRIVATE ${CMAKE_BINARY_DIR})
target_link_libraries(neochat-app PRIVATE
neochat
KF6::IconThemes
)
ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
@@ -180,7 +194,7 @@ else()
endif()
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models)
target_link_libraries(neochat PRIVATE Loginplugin Roomsplugin RoomInfoplugin MessageContentplugin Timelineplugin Spacesplugin Chatbarplugin Settingsplugin Devtoolsplugin)
target_link_libraries(neochat PRIVATE neochatplugin Loginplugin Roomsplugin RoomInfoplugin MessageContentplugin Timelineplugin Spacesplugin Chatbarplugin Settingsplugin Devtoolsplugin)
target_link_libraries(neochat PUBLIC
LibNeoChat
Timeline
@@ -200,8 +214,9 @@ target_link_libraries(neochat PUBLIC
KF6::ConfigGui
KF6::CoreAddons
KF6::SonnetCore
KF6::IconThemes
KF6::ItemModels
KF6::I18nQml
KirigamiApp
QuotientQt6
Login
Rooms
@@ -209,10 +224,6 @@ target_link_libraries(neochat PUBLIC
Spaces
)
if (TARGET KF6::Crash)
target_link_libraries(neochat PUBLIC KF6::Crash)
endif()
kconfig_target_kcfg_file(neochat FILE neochatconfig.kcfg CLASS_NAME NeoChatConfig MUTATORS GENERATE_PROPERTIES DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR SINGLETON GENERATE_MOC QML_REGISTRATION)
if(NEOCHAT_FLATPAK)
@@ -323,6 +334,7 @@ if(ANDROID)
"kt-restore-defaults-symbolic"
"user-symbolic"
"mark-location-symbolic"
"amarok_playcount"
${KIRIGAMI_ADDONS_ICONS}
)
@@ -342,9 +354,6 @@ if(TARGET KF6::DBusAddons AND NOT WIN32)
endif()
if (TARGET KUnifiedPush)
target_compile_definitions(neochat PUBLIC -DHAVE_KUNIFIEDPUSH)
target_link_libraries(neochat PUBLIC KUnifiedPush)
if (NOT ANDROID)
configure_file(org.kde.neochat.service.in ${CMAKE_CURRENT_BINARY_DIR}/org.kde.neochat.service)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.neochat.service DESTINATION ${KDE_INSTALL_DBUSSERVICEDIR})
@@ -354,7 +363,8 @@ endif()
install(TARGETS neochat-app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins)
# krunner plugin must be the same as the app id for flatpak to export it
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins RENAME org.kde.neochat.desktop)
endif()
if (APPLE)

View File

@@ -17,17 +17,15 @@
#include <Quotient/qt_connection_util.h>
#include <Quotient/settings.h>
#include "accountmanager.h"
#include "enums/roomsortparameter.h"
#include "general_logging.h"
#include "mediasizehelper.h"
#include "models/actionsmodel.h"
#include "models/messagemodel.h"
#include "models/roomlistmodel.h"
#include "models/roomtreemodel.h"
#include "neochatconfig.h"
#include "neochatconnection.h"
#include "neochatroom.h"
#include "notificationsmanager.h"
#include "proxycontroller.h"
#include "roommanager.h"
@@ -37,14 +35,6 @@
#include "trayicon_sni.h"
#endif
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
#ifndef Q_OS_ANDROID
#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusMessage>
#endif
#endif
#ifdef HAVE_KUNIFIEDPUSH
#include <kunifiedpush/connector.h>
#endif
@@ -133,8 +123,10 @@ Controller::Controller(QObject *parent)
connect(NeoChatConfig::self(), &NeoChatConfig::SystemTrayChanged, this, &Controller::setQuitOnLastWindowClosed);
#endif
QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [this] {
connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [this] {
#ifndef Q_OS_ANDROID
delete m_trayIcon;
#endif
NeoChatConfig::self()->save();
});
@@ -200,13 +192,15 @@ void Controller::setAccountManager(AccountManager *manager)
m_accountManager = manager;
if (m_accountManager) {
connect(m_accountManager, &AccountManager::errorOccured, this, &Controller::errorOccured);
connect(m_accountManager, &AccountManager::accountsLoadingChanged, this, &Controller::accountsLoadingChanged);
connect(m_accountManager, &AccountManager::connectionAdded, this, &Controller::initConnection);
connect(m_accountManager, &AccountManager::connectionDropped, this, &Controller::teardownConnection);
connect(m_accountManager, &AccountManager::activeConnectionChanged, this, &Controller::initActiveConnection);
if (!m_accountManager) {
return;
}
connect(m_accountManager, &AccountManager::errorOccured, this, &Controller::errorOccured);
connect(m_accountManager, &AccountManager::accountsLoadingChanged, this, &Controller::accountsLoadingChanged);
connect(m_accountManager, &AccountManager::connectionAdded, this, &Controller::initConnection);
connect(m_accountManager, &AccountManager::connectionDropped, this, &Controller::teardownConnection);
connect(m_accountManager, &AccountManager::activeConnectionChanged, this, &Controller::initActiveConnection);
}
void Controller::initConnection(NeoChatConnection *connection)
@@ -252,7 +246,10 @@ void Controller::initActiveConnection(NeoChatConnection *oldConnection, NeoChatC
if (newConnection) {
connect(newConnection, &NeoChatConnection::errorOccured, this, &Controller::errorOccured);
connect(newConnection, &NeoChatConnection::badgeNotificationCountChanged, this, &Controller::updateBadgeNotificationCount);
// Refresh and update manually, in case we init too late for the badge count to actually change.
newConnection->refreshBadgeNotificationCount();
updateBadgeNotificationCount(newConnection->badgeNotificationCount());
}
Q_EMIT activeConnectionChanged(newConnection);
}
@@ -262,8 +259,8 @@ bool Controller::supportSystemTray() const
#ifdef Q_OS_ANDROID
return false;
#else
auto de = QString::fromLatin1(qgetenv("XDG_CURRENT_DESKTOP"));
return de != u"GNOME"_s && de != u"Pantheon"_s;
QStringList unsupportedPlatforms{u"GNOME"_s, u"Pantheon"_s};
return !unsupportedPlatforms.contains(QString::fromLatin1(qgetenv("XDG_CURRENT_DESKTOP")));
#endif
}
@@ -273,11 +270,8 @@ void Controller::setQuitOnLastWindowClosed()
if (supportSystemTray() && NeoChatConfig::self()->systemTray()) {
m_trayIcon = new TrayIcon(this);
m_trayIcon->show();
} else {
if (m_trayIcon) {
delete m_trayIcon;
m_trayIcon = nullptr;
}
} else if (m_trayIcon) {
delete m_trayIcon;
}
#endif
}
@@ -315,8 +309,7 @@ void Controller::listenForNotifications()
connect(timer, &QTimer::timeout, qGuiApp, &QGuiApplication::quit);
connect(connector, &KUnifiedPush::Connector::messageReceived, [timer](const QByteArray &data) {
instance().m_notificationsManager.postPushNotification(data);
timer->stop();
NotificationsManager::postPushNotification(data);
});
// Wait five seconds to see if we received any messages or this happened to be an erroneous activation.
@@ -334,30 +327,7 @@ void Controller::clearInvitationNotification(const QString &roomId)
void Controller::updateBadgeNotificationCount(int count)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
#ifndef Q_OS_ANDROID
// copied from Telegram desktop
const auto launcherUrl = "application://org.kde.neochat.desktop"_L1;
// Gnome requires that count is a 64bit integer
const qint64 counterSlice = std::min(count, 9999);
QVariantMap dbusUnityProperties;
if (counterSlice > 0) {
dbusUnityProperties["count"_L1] = counterSlice;
dbusUnityProperties["count-visible"_L1] = true;
} else {
dbusUnityProperties["count-visible"_L1] = false;
}
auto signal = QDBusMessage::createSignal("/com/canonical/unity/launcherentry/neochat"_L1, "com.canonical.Unity.LauncherEntry"_L1, "Update"_L1);
signal.setArguments({launcherUrl, dbusUnityProperties});
QDBusConnection::sessionBus().send(signal);
#endif // Q_OS_ANDROID
#else
qGuiApp->setBadgeNumber(count);
#endif // QT_VERSION_CHECK(6, 6, 0)
}
bool Controller::isFlatpak() const
@@ -378,7 +348,10 @@ QString Controller::loadFileContent(const QString &path) const
{
QUrl url(path);
QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString());
file.open(QFile::ReadOnly);
if (!file.open(QFile::ReadOnly)) {
qCWarning(GENERAL) << "Failed to open file" << path;
return {};
}
return QString::fromLatin1(file.readAll());
}

View File

@@ -110,8 +110,9 @@ private:
void initActiveConnection(NeoChatConnection *oldConnection, NeoChatConnection *newConnection);
QPointer<NeoChatConnection> m_connection;
TrayIcon *m_trayIcon = nullptr;
#ifndef Q_OS_ANDROID
QPointer<TrayIcon> m_trayIcon;
#endif
QString m_endpoint;
QStringList m_shownImages;

View File

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

View File

@@ -5,6 +5,7 @@
#include "jobs/neochatgetcommonroomsjob.h"
#include <QGuiApplication>
#include <Quotient/room.h>
using namespace Quotient;
@@ -39,8 +40,22 @@ void CommonRoomsModel::setUserId(const QString &userId)
QVariant CommonRoomsModel::data(const QModelIndex &index, int roleName) const
{
Q_UNUSED(index)
Q_UNUSED(roleName)
auto roomId = m_commonRooms[index.row()];
auto room = connection()->room(roomId);
if (!room) {
return {};
}
switch (roleName) {
case Qt::DisplayRole:
case RoomNameRole:
return room->displayName();
case RoomAvatarRole:
return room->avatarUrl();
case RoomIdRole:
return roomId;
}
return {};
}
@@ -50,6 +65,15 @@ int CommonRoomsModel::rowCount(const QModelIndex &parent) const
return m_commonRooms.size();
}
QHash<int, QByteArray> CommonRoomsModel::roleNames() const
{
return {
{RoomIdRole, "roomId"},
{RoomNameRole, "roomName"},
{RoomAvatarRole, "roomAvatar"},
};
}
void CommonRoomsModel::reload()
{
if (!m_connection || m_userId.isEmpty()) {

View File

@@ -24,7 +24,9 @@ class CommonRoomsModel : public QAbstractListModel
public:
enum Roles {
RoomIdRole = Qt::DisplayRole,
RoomIdRole = Qt::UserRole,
RoomNameRole,
RoomAvatarRole,
};
Q_ENUM(Roles)
@@ -39,6 +41,8 @@ public:
[[nodiscard]] QVariant data(const QModelIndex &index, int roleName) const override;
[[nodiscard]] Q_INVOKABLE int rowCount(const QModelIndex &parent = {}) const override;
QHash<int, QByteArray> roleNames() const override;
Q_SIGNALS:
void connectionChanged();
void userIdChanged();

View File

@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "models/limitermodel.h"
LimiterModel::LimiterModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
connect(this, &QSortFilterProxyModel::rowsInserted, this, &LimiterModel::extraCountChanged);
connect(this, &QSortFilterProxyModel::rowsRemoved, this, &LimiterModel::extraCountChanged);
connect(this, &QSortFilterProxyModel::modelReset, this, &LimiterModel::extraCountChanged);
}
int LimiterModel::maximumCount() const
{
return m_maximumCount;
}
void LimiterModel::setMaximumCount(int maximumCount)
{
if (m_maximumCount != maximumCount) {
m_maximumCount = maximumCount;
Q_EMIT maximumCountChanged();
}
}
int LimiterModel::extraCount() const
{
if (sourceModel()) {
return std::max(sourceModel()->rowCount() - maximumCount(), 0);
}
return 0;
}
bool LimiterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
Q_UNUSED(source_parent)
return source_row < maximumCount();
}
#include "moc_limitermodel.cpp"

View File

@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QQmlEngine>
#include <QSortFilterProxyModel>
/**
* @class LimiterModel
*
* @brief Takes a source QAbstractItemModel model and only displays a desired maximum amount.
*
* Also gives you the remaining (filtered out) items, useful for sticking in a label or somesuch.
*/
class LimiterModel : public QSortFilterProxyModel
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(int maximumCount READ maximumCount WRITE setMaximumCount NOTIFY maximumCountChanged)
Q_PROPERTY(int extraCount READ extraCount NOTIFY extraCountChanged)
public:
explicit LimiterModel(QObject *parent = nullptr);
[[nodiscard]] int maximumCount() const;
void setMaximumCount(int maximumCount);
[[nodiscard]] int extraCount() const;
Q_SIGNALS:
void maximumCountChanged();
void extraCountChanged();
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
private:
int m_maximumCount = 0;
};

View File

@@ -58,7 +58,7 @@ QVariant NotificationsModel::data(const QModelIndex &index, int role) const
QHash<int, QByteArray> NotificationsModel::roleNames() const
{
return {
{TextRole, "text"},
{TextRole, "notificationText"},
{RoomIdRole, "roomId"},
{AuthorName, "authorName"},
{AuthorAvatar, "authorAvatar"},

View File

@@ -42,7 +42,6 @@ Name[sv]=NeoChat
Name[ta]=நியோச்சாட்
Name[tr]=NeoChat
Name[uk]=NeoChat
Name[x-test]=xxNeoChatxx
Name[zh_CN]=NeoChat
Name[zh_TW]=NeoChat
DesktopEntry=org.kde.neochat
@@ -87,7 +86,6 @@ Comment[sv]=En klient för matrix, det decentraliserade kommunikationsprotokolle
Comment[ta]=மையமில்லா தகவல் பரிமாற்ற நெறிமுறையான மேட்ரிக்ஸுக்கான செயலி
Comment[tr]=Merkezi olmayan iletişim protokolü Matrix için bir istemci
Comment[uk]=Клієнт matrix, децентралізованого протоколу обміну даними
Comment[x-test]=xxA client for matrix, the decentralized communication protocolxx
Comment[zh_CN]=分布式通讯协议 Matrix 的客户端
Comment[zh_TW]=去中心化通訊協定 Matrix 的用戶端
@@ -134,7 +132,6 @@ Name[sv]=Nytt meddelande
Name[ta]=புதிய செய்தி
Name[tr]=Yeni İleti
Name[uk]=Нове повідомлення
Name[x-test]=xxNew messagexx
Name[zh_CN]=新消息
Name[zh_TW]=新訊息
Comment=There is a new message
@@ -177,7 +174,6 @@ Comment[sv]=Det finns ett nytt meddelande
Comment[ta]=ஒரு புதிய செய்தி உள்ளது
Comment[tr]=Yeni bir ileti var
Comment[uk]=Надійшло нове повідомлення
Comment[x-test]=xxThere is a new messagexx
Comment[zh_CN]=有新消息
Comment[zh_TW]=有新的訊息
Action=Popup
@@ -215,6 +211,7 @@ Name[pa]=ਨਵਾਂ ਸੱਦਾ
Name[pl]=Nowe zaproszenie
Name[pt]=Novo Convite
Name[pt_BR]=Novo convite
Name[ro]=Invitație nouă
Name[ru]=Новое приглашение
Name[sa]=नवीन आमन्त्रणम्
Name[sl]=Novo povabilo
@@ -222,7 +219,6 @@ Name[sv]=Ny inbjudan
Name[ta]=புதிய அழைப்பிதழ்
Name[tr]=Yeni Davet
Name[uk]=Нове запрошення
Name[x-test]=xxNew Invitationxx
Name[zh_CN]=新邀请
Name[zh_TW]=新邀請
Comment=There is a new invitation to a room
@@ -257,14 +253,14 @@ Comment[pa]=ਰੂਮ ਲਈ ਨਵਾਂ ਸੱਦਾ ਹੈ
Comment[pl]=Dostępna jest nowe zaproszenie do pokoju
Comment[pt]=Existe um novo convite para uma sala
Comment[pt_BR]=Existe um novo convite para uma sala
Comment[ro]=E o nouă invitație la o cameră
Comment[ru]=Доступно новое приглашение в комнату
Comment[sa]=कक्षस्य नूतनं निमन्त्रणम् अस्ति
Comment[sl]=Tam je novo povabilo v sobo
Comment[sv]=Det finns en ny inbjudan till ett rum
Comment[ta]=ஓர் அரங்கிற்கான புதிய அழைப்பிதழ் உள்ளது
Comment[tr]=Bir odaya yeni bir davetiye var
Comment[tr]=Bir odaya yeni bir davet var
Comment[uk]=У кімнаті нове запрошення
Comment[x-test]=xxThere is a new invitation to a roomxx
Comment[zh_CN]=有新的聊天室邀请
Comment[zh_TW]=有新的加入聊天室邀請
Action=Popup
@@ -291,11 +287,13 @@ Name[ia]=Comparti
Name[it]=Condivisione
Name[ka]=გაზიარება
Name[ko]=공유
Name[lt]=Bendrinti
Name[lv]=Kopīgot
Name[nl]=Gedeelde
Name[nn]=Del
Name[pl]=Udostępnij
Name[pt_BR]=Compartilhar
Name[ro]=Partajare
Name[ru]=Публикация
Name[sa]=संविभागः
Name[sl]=Deli
@@ -303,7 +301,6 @@ Name[sv]=Dela
Name[ta]=பகிர்
Name[tr]=Paylaş
Name[uk]=Оприлюднення
Name[x-test]=xxSharexx
Name[zh_CN]=分享
Name[zh_TW]=分享
Comment=The result of sharing a piece of content
@@ -326,11 +323,13 @@ Comment[ia]=Le exito de compartir un pecietta de contento
Comment[it]=Il risultato della condivisione di un contenuto
Comment[ka]=შემცველობის ნაწილის გაზიარების შედეგი
Comment[ko]=콘텐츠 공유 결과
Comment[lt]=Turinio dalies bendrinimo rezultatas
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[ro]=Rezultatul partajării unei bucăți de conținut
Comment[ru]=Результат публикации данных
Comment[sa]=सामग्रीखण्डस्य साझाकरणस्य परिणामः
Comment[sl]=Rezultat deljenega kosa vsebine
@@ -338,7 +337,6 @@ Comment[sv]=Resultatet av att dela innehåll
Comment[ta]=எதையோ பகிர்ந்த‍தன் விளைவு
Comment[tr]=Bir parça içerik paylaşımının sonucu
Comment[uk]=Результат оприлюднення даних
Comment[x-test]=xxThe result of sharing a piece of contentxx
Comment[zh_CN]=分享一个内容得到的结果
Comment[zh_TW]=分享一份內容之後的結果
Action=Popup

View File

@@ -66,6 +66,10 @@
</entry>
</group>
<group name="Timeline">
<entry name="FontScale" type="double">
<label>Scaling factor for font sizes</label>
<default>1.0</default>
</entry>
<entry name="ShowAvatarInTimeline" type="bool">
<label>Show avatar in the timeline</label>
<default>true</default>
@@ -207,10 +211,6 @@
<label>Enable threads</label>
<default>false</default>
</entry>
<entry name="SecretBackup" type="bool">
<label>Enable secret backup</label>
<default>false</default>
</entry>
<entry name="Phone3PId" type="bool">
<label>Enable add phone numbers as 3PIDs</label>
<default>false</default>

View File

@@ -60,9 +60,8 @@ void NotificationsManager::startNotificationJob(QPointer<NeoChatConnection> conn
}
if (!m_connActiveJob.contains(connection->user()->id())) {
auto job = connection->callApi<GetNotificationsJob>();
m_connActiveJob.append(connection->user()->id());
connect(job, &BaseJob::success, this, [this, job, connection]() {
connection->callApi<GetNotificationsJob>().onResult([this, connection](const auto &job) {
m_connActiveJob.removeAll(connection->user()->id());
processNotificationJob(connection, job, !m_oldNotifications.contains(connection->user()->id()));
});
@@ -217,12 +216,12 @@ void NotificationsManager::postNotification(NeoChatRoom *room,
}
});
notification->setTitle(room->displayName());
QString entry;
if (sender == room->displayName()) {
notification->setTitle(sender);
if (room->isDirectChat()) {
entry = text.toHtmlEscaped();
} else {
notification->setTitle(room->displayName());
entry = i18n("%1: %2", sender, text.toHtmlEscaped());
}
@@ -254,7 +253,9 @@ void NotificationsManager::postNotification(NeoChatRoom *room,
notification->setReplyAction(std::move(replyAction));
}
notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
if (Controller::instance().accounts()->rowCount() > 1) {
notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
}
notification->sendEvent();
}
@@ -348,7 +349,9 @@ void NotificationsManager::doPostInviteNotification(QPointer<NeoChatRoom> room)
m_invitations.remove(room->id());
});
notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
if (Controller::instance().accounts()->rowCount() > 1) {
notification->setHint(u"x-kde-origin-name"_s, room->localMember().id());
}
notification->sendEvent();
}
@@ -389,7 +392,7 @@ void NotificationsManager::postPushNotification(const QByteArray &message)
#ifdef HAVE_KIO
auto openAction = notification->addAction(i18n("Open NeoChat"));
connect(openAction, &KNotificationAction::activated, this, [=]() {
connect(openAction, &KNotificationAction::activated, notification, [=]() {
QString properId = roomId;
properId = properId.replace(u"#"_s, QString());
properId = properId.replace(u"!"_s, QString());
@@ -403,8 +406,6 @@ void NotificationsManager::postPushNotification(const QByteArray &message)
connect(notification, &KNotification::closed, qGuiApp, &QGuiApplication::quit);
notification->sendEvent();
m_notifications.insert(roomId, {json["ts"_L1].toVariant().toLongLong(), notification});
} else {
qWarning() << "Skipping unsupported push notification" << type;
}

View File

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

View File

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

View File

@@ -79,7 +79,6 @@ KirigamiComponents.ConvergentContextMenu {
Kirigami.Action {
text: i18nc("@action:inmenu", "Open Secret Backup")
icon.name: "unlock"
visible: NeoChatConfig.secretBackup
onTriggered: root.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UnlockSSSSDialog'), {}, {
title: i18nc("@title:window", "Open Key Backup")
})
@@ -88,7 +87,7 @@ KirigamiComponents.ConvergentContextMenu {
Kirigami.Action {
text: i18nc("@action:inmenu", "Verify This Device")
icon.name: "security-low"
visible: !root.connection.isVerifiedSession()
visible: !root.connection.isVerifiedSession
onTriggered: {
root.connection.startSelfVerification();
const dialog = Qt.createComponent("org.kde.kirigami", "PromptDialog").createObject(QQC2.Overlay.overlay, {

View File

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

View File

@@ -48,7 +48,7 @@ Components.AbstractMaximizeComponent {
implicitWidth: Kirigami.Units.iconSizes.medium
implicitHeight: Kirigami.Units.iconSizes.medium
name: root.author.name ?? root.author.displayName
name: root.author.displayName
source: root.author.avatarUrl
color: root.author.color
}
@@ -57,7 +57,7 @@ Components.AbstractMaximizeComponent {
QQC2.Label {
id: userLabel
text: root.author.name ?? root.author.displayName
text: root.author.displayName
color: root.author.color
font.weight: Font.Bold
elide: Text.ElideRight
@@ -91,6 +91,7 @@ Components.AbstractMaximizeComponent {
color: Kirigami.Theme.textColor
font.family: "monospace"
font.pointSize: Kirigami.Theme.defaultFont.pointSize * NeoChatConfig.fontScale
Kirigami.SpellCheck.enabled: false

View File

@@ -6,16 +6,15 @@ import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.neochat
ColumnLayout {
id: root
property alias emoji: emojiLabel.text
property alias description: descriptionLabel.text
required property string emoji
required property string description
QQC2.Label {
id: emojiLabel
text: root.emoji
Layout.fillWidth: true
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
@@ -25,7 +24,7 @@ ColumnLayout {
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 4
}
QQC2.Label {
id: descriptionLabel
text: root.description
Layout.fillWidth: true
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter

View File

@@ -17,9 +17,6 @@ RowLayout {
Repeater {
id: repeater
delegate: EmojiItem {
emoji: modelData.emoji
description: modelData.description
}
delegate: EmojiItem {}
}
}

View File

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

View File

@@ -22,12 +22,12 @@ Labs.MenuBar {
Labs.MenuItem {
icon.name: "list-add-user"
text: i18nc("@action:inmenu", "Find your Friends")
text: i18nc("@action:inmenu", "Find User")
enabled: root.connection
onTriggered: root.appWindow.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
connection: root.connection
}, {
title: i18nc("@title", "Find your friends")
title: i18nc("@title", "Find User")
})
}
Labs.MenuItem {
@@ -45,14 +45,12 @@ Labs.MenuBar {
}
Labs.MenuItem {
icon.name: "compass-symbolic"
text: i18nc("@action:inmenu", "Explore Rooms")
text: i18nc("@action:inmenu Explore public rooms and spaces", "Explore")
enabled: root.connection
onTriggered: {
let dialog = root.appWindow.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), {
connection: root.connection
}, {
title: i18nc("@title", "Explore Rooms")
});
}, {});
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
RoomManager.resolveResource(roomId.length > 0 ? roomId : alias, isJoined ? "" : "join");
});

View File

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

View File

@@ -3,29 +3,54 @@
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as QQC2
import org.kde.kirigami as Kirigami
QQC2.Control {
RowLayout {
id: root
property string text
visible: !root.text.startsWith("https://matrix.to/") && root.text.length > 0
Kirigami.Theme.colorSet: Kirigami.Theme.View
z: 20
Accessible.ignored: true
contentItem: QQC2.Label {
text: root.text.startsWith("https://matrix.to/") ? "" : root.text
elide: Text.ElideRight
Accessible.description: i18nc("@info screenreader", "The currently selected link")
onTextChanged: {
// This is done so the text doesn't disappear for a split second while in the opacity transition
if (root.text.length > 0) {
urlLabel.text = root.text
}
}
background: Rectangle {
color: Kirigami.Theme.backgroundColor
z: 99
spacing: 0
opacity: (!root.text.startsWith("https://matrix.to/") && root.text.length > 0) ? 1 : 0
visible: opacity > 0
Behavior on opacity {
OpacityAnimator {
duration: Kirigami.Units.shortDuration
easing.type: Easing.InOutQuad
}
}
QQC2.Control {
Kirigami.Theme.colorSet: Kirigami.Theme.View
Accessible.ignored: true
contentItem: QQC2.Label {
id: urlLabel
elide: Text.ElideRight
}
background: Kirigami.ShadowedRectangle {
corners.topRightRadius: Kirigami.Units.cornerRadius
color: Kirigami.Theme.backgroundColor
border {
color: Kirigami.ColorUtils.linearInterpolation(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, Kirigami.Theme.frameContrast)
width: 1
}
}
}
}

View File

@@ -2,6 +2,8 @@
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-2.0-or-later
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
@@ -25,16 +27,17 @@ ColumnLayout {
Layout.fillHeight: true
}
KirigamiComponents.Avatar {
KirigamiComponents.AvatarButton {
id: avatar
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
name: root.invitingMember.displayName
source: NeoChatConfig.hideImages ? undefined : root.invitingMember.avatarUrl
color: root.invitingMember.color
onClicked: RoomManager.resolveResource(root.currentRoom.invitingUserId)
}
Loader {
@@ -52,6 +55,12 @@ ColumnLayout {
Layout.alignment: Qt.AlignHCenter
}
Kirigami.Heading {
text: root.currentRoom.displayName
Layout.alignment: Qt.AlignHCenter
}
Kirigami.SelectableLabel {
Layout.fillWidth: true
font: Kirigami.Theme.smallFont
@@ -59,12 +68,7 @@ ColumnLayout {
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
Layout.alignment: Qt.AlignHCenter
horizontalAlignment: Text.AlignHCenter
}
}
}
@@ -135,8 +139,24 @@ ColumnLayout {
Layout.fillWidth: true
FormCard.FormButtonDelegate {
id: viewProfileDelegate
icon.name: "user-properties-symbolic"
text: i18nc("@action:button View this user's profile", "View %1's Profile", root.invitingMember.displayName)
onClicked: RoomManager.resolveResource(root.currentRoom.invitingUserId)
}
FormCard.FormDelegateSeparator {
above: viewProfileDelegate
below: ignoreUserDelegate
}
FormCard.FormButtonDelegate {
id: ignoreUserDelegate
icon.name: "list-remove-symbolic"
text: i18nc("@action:button Block the user", "Block %1", root.invitingMember.displayName)
text: i18nc("@action:button Ignore the user", "Ignore %1 and Reject Invite", root.invitingMember.displayName)
onClicked: {
root.currentRoom.forget()

View File

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

View File

@@ -4,6 +4,7 @@
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import QtQuick.Window
import QtQml
import org.kde.kirigami as Kirigami
@@ -22,56 +23,63 @@ Kirigami.Page {
name: "cancelled"
when: root.session.state === KeyVerificationSession.CANCELED
PropertyChanges {
target: stateLoader
sourceComponent: verificationCanceled
stateLoader.sourceComponent: verificationCanceled
}
},
State {
name: "waitingForVerification"
when: root.session.state === KeyVerificationSession.WAITINGFORVERIFICATION
PropertyChanges {
target: stateLoader
sourceComponent: emojiSas
stateLoader.sourceComponent: emojiSas
}
},
State {
name: "waitingForReady"
when: root.session.state === KeyVerificationSession.WAITINGFORREADY
PropertyChanges {
target: stateLoader
sourceComponent: message
stateLoader.sourceComponent: message
}
},
State {
name: "incoming"
when: root.session.state === KeyVerificationSession.INCOMING
PropertyChanges {
target: stateLoader
sourceComponent: message
stateLoader.sourceComponent: message
}
},
State {
name: "waitingForKey"
when: root.session.state === KeyVerificationSession.WAITINGFORKEY
PropertyChanges {
stateLoader.sourceComponent: message
}
},
State {
name: "waitingForAccept"
when: root.session.state === KeyVerificationSession.WAITINGFORACCEPT
PropertyChanges {
stateLoader.sourceComponent: message
}
},
State {
name: "waitingForMac"
when: root.session.state === KeyVerificationSession.WAITINGFORMAC
PropertyChanges {
target: stateLoader
sourceComponent: message
stateLoader.sourceComponent: message
}
},
State {
name: "ready"
when: root.session.state === KeyVerificationSession.READY
PropertyChanges {
target: stateLoader
sourceComponent: chooseVerificationComponent
stateLoader.sourceComponent: chooseVerificationComponent
}
},
State {
name: "done"
when: root.session.state === KeyVerificationSession.DONE
PropertyChanges {
target: stateLoader
sourceComponent: message
stateLoader.sourceComponent: message
}
}
]
@@ -127,7 +135,9 @@ Kirigami.Page {
case KeyVerificationSession.WAITINGFORREADY:
case KeyVerificationSession.INCOMING:
case KeyVerificationSession.WAITINGFORMAC:
return "security-medium-symbolic";
case KeyVerificationSession.WAITINGFORKEY:
case KeyVerificationSession.WAITINGFORACCEPT:
return "security-medium-symbolic";
case KeyVerificationSession.DONE:
return "security-high";
default:
@@ -141,13 +151,19 @@ Kirigami.Page {
case KeyVerificationSession.INCOMING:
return i18n("Incoming key verification request from device **%1**", root.session.remoteDeviceId);
case KeyVerificationSession.WAITINGFORMAC:
return i18n("Waiting for other party to send us keys.");
case KeyVerificationSession.WAITINGFORKEY:
return i18n("Waiting for other party to confirm our keys.");
case KeyVerificationSession.WAITINGFORACCEPT:
return i18n("Waiting for other party to verify.");
case KeyVerificationSession.DONE:
return i18n("Successfully verified device **%1**", root.session.remoteDeviceId)
return i18n("Successfully verified device **%1**", root.session.remoteDeviceId);
default:
return "";
}
}
isDone: root.session.state === KeyVerificationSession.DONE
onDone: root.closeDialog()
}
}

View File

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

View File

@@ -21,7 +21,7 @@ Kirigami.Dialog {
/**
* @brief Thrown when a user is selected.
*/
signal userSelected
signal userSelected(string userId)
title: i18nc("@title", "User ID")
@@ -38,7 +38,7 @@ Kirigami.Dialog {
text: i18n("OK")
icon.name: "dialog-ok"
onTriggered: {
root.connection.requestDirectChat(userIdText.text);
root.userSelected(userIdText.text)
root.accept();
}
}

View File

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

View File

@@ -34,8 +34,8 @@ Kirigami.Page {
enabled: root.model
target: root.room
function onChanged(): void {
root.contentJson = model.stateEventContentJson(root.type, root.stateKey);
root.sourceText = model.stateEventJson(root.type, root.stateKey);
root.contentJson = root.model.stateEventContentJson(root.type, root.stateKey);
root.sourceText = root.model.stateEventJson(root.type, root.stateKey);
}
}
@@ -46,8 +46,8 @@ Kirigami.Page {
text: i18nc("@action As in 'edit the state of this room'", "Edit state")
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"), {
enabled: root.room.canSendState(root.type) && (!root.stateKey.startsWith("@") || root.stateKey === root.room.connection.localUserId) && root.type !== "m.room.create"
onTriggered: (root.Kirigami.PageStack.pageStack as Kirigami.PageRow).pushDialogLayer(Qt.createComponent("org.kde.neochat", "EditStateDialog"), {
room: root.room,
type: root.type,
stateKey: root.stateKey,

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