Compare commits

...

2133 Commits

Author SHA1 Message Date
James Graham
6d2fda878c Fix the Timeline Feezes.
This was caused whe TextComponent was changed from a TextEdit to a TextArea for the rich chat bar. This causes freezing for ..... reasons. Changin it back fixes the freezes.

The upshot of this is we now have to implement placeholder text ourselves. So I copied how it is done in TestArea in QQC2 desktop style with some minor logic changes for us.
2026-02-28 17:06:30 +00:00
l10n daemon script
150b9c4038 GIT_SILENT Sync po/docbooks with svn 2026-02-28 01:48:45 +00:00
James Graham
7550b2b000 Apply 1 suggestion(s) to 1 file(s)
Co-authored-by: Tobias Fella <fella@posteo.de>
2026-02-27 14:15:30 +00:00
James Graham
daa66c4abc Treat all text in a code block as plain, i.e. don't format based on markdown. 2026-02-27 14:15:30 +00:00
James Graham
e0b229e040 Focus the charbar when clicking the reply button. 2026-02-27 13:29:04 +00:00
James Graham
c07db82552 When completing treat all text as plain so don't upgrade to markdown. 2026-02-27 13:06:26 +00:00
l10n daemon script
4c1301da51 GIT_SILENT Sync po/docbooks with svn 2026-02-27 01:49:53 +00:00
l10n daemon script
adf0544eb0 GIT_SILENT made messages (after extraction) 2026-02-27 00:43:33 +00:00
Tobias Fella
44ec1e3529 Cleanup logging in RoomListModel 2026-02-26 23:41:20 +01:00
Tobias Fella
7cfac2151e Cleanup logging in TimelineMessageModel 2026-02-26 23:41:20 +01:00
Tobias Fella
3f577adf34 Cleanup logging in MessageContentModel 2026-02-26 23:41:20 +01:00
Tobias Fella
0375ef99a5 Clean up chatbarcache 2026-02-26 23:41:18 +01:00
James Graham
23c0bdb647 Improve the label for showing the rich text controls and tell users what pressing enter will do. 2026-02-26 19:51:40 +00:00
James Graham
611374d4dd Fix funny behaviour when pasting. basically we should start but not end a cursor edit block. 2026-02-26 19:26:26 +00:00
James Graham
c3ea0db82d Make sure that the clickable area of the chatbar fill the whole space 2026-02-26 17:44:40 +00:00
Tobias Fella
eb58d96d77 Remove duplicate regexes 2026-02-26 16:04:35 +00:00
Tobias Fella
9cad195704 Fix many clazy warnings 2026-02-26 14:52:14 +00:00
James Graham
c08693c508 Fix the parsing of the chat cache so the lists are no longer broken and add tests. 2026-02-26 06:15:42 +00:00
James Graham
7f91c57945 Make sure that when a heading is fully deleted the style is returned to paragraph 2026-02-26 06:08:14 +00:00
l10n daemon script
8b9e470907 GIT_SILENT Sync po/docbooks with svn 2026-02-26 01:48:01 +00:00
Darshan Phaldesai
63ddfb6cb8 SendBar: make overflowWidth bigger.
also fix initial state for rich text button
2026-02-25 22:26:27 +00:00
Darshan Phaldesai
ce84562829 chatbar: make emojidialog dynamically loaded 2026-02-25 22:26:27 +00:00
Darshan Phaldesai
7eda07d788 chatbar: remove redundant code 2026-02-25 22:26:27 +00:00
Darshan Phaldesai
3105fb8aa8 chatbar: move richtext back to main chat 2026-02-25 22:26:27 +00:00
Darshan Phaldesai
e3fdcb8779 chatbar: move emojis back to main panel 2026-02-25 22:26:27 +00:00
leia uwu
594018b188 restore window maximized state on toggle
Otherwise the window will have a maximized size but not maximized state after toggling it from system tray
2026-02-25 14:33:50 +00:00
l10n daemon script
774eb6a505 GIT_SILENT Sync po/docbooks with svn 2026-02-25 01:50:06 +00:00
l10n daemon script
f9a6533ca9 GIT_SILENT made messages (after extraction) 2026-02-25 00:46:42 +00:00
Azhar Momin
c804b9ce14 Send typing notifications from ChatBar again 2026-02-24 22:03:46 +00:00
James Graham
d1acb97fe2 - Make sure that when adding characters before/after a link that it doesn't take the link style
- Make sure that when double clicking a link with a space the whole text is selected
- Make sure that shift selection with arrows works
- Make sure that ctrl left right (word jump) moves across the whole link even if multiple words
2026-02-24 16:54:57 +00:00
James Graham
0a99e90591 Treat links as a single character for the purposes of delete, backspace, left and right.
When backspacing at a link the first key selects the whole link the second deletes. Similar for delete, left and right.
2026-02-24 16:54:56 +00:00
James Graham
c6313d2951 Hook sending messages back up 2026-02-24 16:54:56 +00:00
James Graham
d2d48110cb Use the rich text char format to store mentions rather than a separate structure in ChatBarCache.
This removes mentions from ChatBarCache and instead sets mentions as an anchor using the QTextCursor. Saving and restoring the chatbar text content is then done using QTextDocumentFragments which retain the rich text formatting.
2026-02-24 16:54:48 +00:00
l10n daemon script
a235f39c84 GIT_SILENT Sync po/docbooks with svn 2026-02-24 01:47:14 +00:00
l10n daemon script
8e1303edbe GIT_SILENT made messages (after extraction) 2026-02-24 00:44:31 +00:00
l10n daemon script
f66acb5f78 GIT_SILENT Sync po/docbooks with svn 2026-02-23 01:46:24 +00:00
l10n daemon script
0fe4ae82ea GIT_SILENT made messages (after extraction) 2026-02-23 00:44:25 +00:00
James Graham
8e516422b7 Make sure the default menu of textArea isn't thrown for non editable TextComponents.
Note: I know this is horrible but it's all we got until discussions in QQC2-Desktop-Style are resolved.
2026-02-22 18:07:27 +00:00
l10n daemon script
1dc566f9a9 GIT_SILENT Sync po/docbooks with svn 2026-02-22 01:48:38 +00:00
l10n daemon script
cdfda55ac1 GIT_SILENT made messages (after extraction) 2026-02-22 00:45:12 +00:00
Joshua Goins
a27c8971b9 Fix TimelineEnd/TimelineBeginning models properly this time
Amends 3b00e14a9d with a fix that's more
sensible, as we need to take into account the various checks before
mindlessly inserting roles.
2026-02-21 12:41:55 -05:00
Tobias Fella
b7688d4373 Remove dependency on KStatusNotifierItem
We had two implementations of the system tray icon for no particularly good reason
2026-02-21 17:09:22 +00:00
James Graham
73e6a536db Make sure that we only insert a new block for headers. This makes sure that hitting return in a list just adds the next list item. 2026-02-21 14:42:02 +00:00
Joshua Goins
a60d6c4a60 Add report buttons to spaces
This allows you to report harmful spaces that you already joined in,
similar to the current UX for rooms.

Note that you can't report subspaces or rooms within said spaces yet,
that requires hover actions (or ideally a context menu) that don't exist
yet.

See #707
2026-02-20 23:28:16 -05:00
l10n daemon script
b49ce25246 GIT_SILENT Sync po/docbooks with svn 2026-02-21 01:49:38 +00:00
l10n daemon script
6c52cccf20 GIT_SILENT made messages (after extraction) 2026-02-21 00:44:35 +00:00
Joshua Goins
1eba52bb7a Add a way to block invites from all users
This is a useful safety feature, not just for people under attack but
also if you have no use for this feature. Currently this is gated for
servers that support Matrix CSAPI 1.18.

Fixes #662
2026-02-20 18:57:21 -05:00
Joshua Goins
3b00e14a9d Fix the TimelineEnd delegate not appearing when going back in history
It was possible to make this appear if you switched rooms while you had
all history loaded, but now it should show up naturally.
2026-02-20 18:38:45 -05:00
Joshua Goins
f6abbda1e3 Don't claim this is "the beginning of the chat" if a predecessor exists
This could be percieved as confusing since we show a nice blue banner
saying where to find the previous conversation.
2026-02-20 18:38:45 -05:00
Tobias Fella
2d0fa43f4f Minor refactoring 2026-02-21 00:26:31 +01:00
Tobias Fella
502e300d5f Remove leftover warning 2026-02-21 00:17:54 +01:00
Joshua Goins
b7ea6f265e Fix-up ImageComponent's hide media functionality
The Z-level for the "Hide media" and other overlay controls were lost
during the rich text refactor, and now restored. I also made it so the
blurhash is now used when the media is hidden, and when hiding media it
no longer allows you to jumpscare yourself by maximizing the media when
you probably didn't actually want to.

When the media doesn't have blurhash, it falls back to black.
2026-02-20 16:02:49 -05:00
James Graham
5f0f9135fe Stop pester in console when changing rooms regarding missing authors. 2026-02-20 17:45:28 +00:00
Tobias Fella
b0dce5fd0a Fix TimelineEndDelegate and SuccessorDelegate 2026-02-20 17:28:17 +00:00
Tobias Fella
2c5bef6e96 Close pinned message when leaving room
BUG: 516350
2026-02-20 17:04:04 +00:00
Tobias Fella
aaaaf91248 Don't try opening last room when this room is not available
When switching connection to a connection that is not loaded or where we have left the last room, trying to resolve it will not do anything, causing the room from the previous connection to stay open
2026-02-20 16:08:25 +00:00
Tobias Fella
7e3db19ead Require libQuotient 0.9.5 2026-02-20 15:32:41 +00:00
Tobias Fella
f0ea5d1e90 Disable FreeBSD CI 2026-02-20 15:22:41 +01:00
Tobias Fella
d2a6d8e447 Fix opening QR code 2026-02-20 12:46:58 +01:00
l10n daemon script
57c20d4c5a GIT_SILENT Sync po/docbooks with svn 2026-02-20 02:06:24 +00:00
l10n daemon script
d7d348322f GIT_SILENT made messages (after extraction) 2026-02-20 00:45:13 +00:00
Joshua Goins
ae9b2abdc7 Reduce layout shift when loading mutual rooms in user profiles
Instead of making the visibility of this section in user profiles
dependent on the model, its now checking if you can check mutual rooms
and using a busy indicator. There's also a label for when you have no
rooms in common, which is a rare case (for example, banned or left
users.)
2026-02-19 18:11:03 -05:00
Joshua Goins
3a964bae20 Move more security and safety-relevant options to a better suited page
This includes the "show link previews", and "enable typing
notifications" options. I also re-arranged the Security & Safety page
into more sensible groups, and in preparation for more invite options.
2026-02-19 18:09:16 -05:00
Joshua Goins
4078d3f2dc Remove attach dialog
This was used when you pressed the "Attach file/image" button but had an
image copied to your clipboard - allowing you to select from either
source. This is a weird thing to ask, the button should always prompt
you with a file dialog.

It's still possible to paste an image from your clipboard with CTRL+V,
but there isn't a way to do it via right-click yet.

Fixes #712
2026-02-19 17:17:00 -05:00
Joshua Goins
53989ff4fe Improve messaging around various encryption key options
Element has landed on calling these "recovery keys" and we should do the
same. Since these have had various names throughout the years and in
previous versions of NeoChat, they are still mentioned.

I also renamed "Secret Backup" to "Key Storage" which is also Element
terminology.
2026-02-19 17:10:27 -05:00
Azhar Momin
2d33cbf6b1 Show thread root event instead of latest thread event 2026-02-19 16:34:33 -05:00
Azhar Momin
b42a82a455 Fix segfault in ContentProvider
QCache in ContentProvider handles cleanup for ThreadModel.
Setting NeoChatRoom as parent for ThreadModel caused it to
be deleted outside of QCache control leading to a double-free.
2026-02-19 16:34:33 -05:00
l10n daemon script
99aed0993e GIT_SILENT Sync po/docbooks with svn 2026-02-19 01:51:57 +00:00
l10n daemon script
7ede740aa8 GIT_SILENT made messages (after extraction) 2026-02-19 00:45:31 +00:00
Darshan Phaldesai
7137b60da9 compactLayout: add spacing around messages and chatbar 2026-02-18 12:08:05 -05:00
l10n daemon script
30462ddebb GIT_SILENT Sync po/docbooks with svn 2026-02-18 01:55:53 +00:00
Joshua Goins
428a196f8d Disable send message when there's nothing to actually send
Now there's a way to check if the message content model has any useful
data inside of it.

BUG: 516118
FIXED-IN: 26.04
2026-02-17 12:16:09 -05:00
l10n daemon script
2d2291fd78 GIT_SILENT Sync po/docbooks with svn 2026-02-17 01:47:55 +00:00
l10n daemon script
646f5476c3 GIT_SILENT made messages (after extraction) 2026-02-17 00:44:41 +00:00
Joshua Goins
03382496b9 Fix tooltip and checked state of message overflow button
This was incorrectly labeled as "Format Text" during the rich text
refactor, but this is for attaching stuff to messages.

I also fixed the checked state not resetting when the menu was closed.

BUG: 516117
BUG: 516119
FIXED-IN: 26.04
2026-02-16 19:11:04 -05:00
Tobias Fella
52302f0f5c Fix binding loops 2026-02-17 00:06:58 +01:00
Joshua Goins
b1595a4556 Ensure most of our dialogs have cancel buttons on the right
This is standard for KDE dialogs, but we weren't super consistent about
it. Most of this is porting to DialogButtonBox, and using buttonRole. I
also made sure the close button is hidden in cases where we have a
cancel button already.

I also took the time to clean out some cruft like manually calling
close() and such. I probably missed a lot more spots, but this is a good
improvement.
2026-02-16 16:31:10 -05:00
Joshua Goins
436b3a1008 Use the same spacing in chat bar as we do for messages
This fixes the odd spacing around some components like replies,
especially after d2aa8d672d.
2026-02-16 21:13:17 +00:00
Joshua Goins
44ff2daad2 Remove unwanted padding from TextComponent
This fixes the padding for TextComponent as well, which was needed
post-rich text refactor.
2026-02-16 21:13:17 +00:00
Volker Krause
9f64457521 Optimize compile time of emoji data maps
Compiling the two files including the emoji data now doesn't take
significantly longer anymore than before. Size of the .text section
also drops by more than 1Mb, while runtime or memory cost remains
unchanged.

There's more that could be done here, but not having to wait minutes
for those files to build is worth it on its own already.
2026-02-16 14:38:14 -05:00
James Graham
d2aa8d672d Remove unwanted padding from ReplyComponent 2026-02-16 19:09:39 +00:00
Joshua Goins
942221b59f Don't attempt to navigate to a component that doesn't exist
Check for the size of our component list before trying to access it.

BUG: 516107
FIXED-IN: 26.04
2026-02-16 13:46:25 -05:00
James Graham
7e6b79d5d4 Release threads removing the feature flag.
This mr performs some final cleanup to make sure the threads are sized correctly and it all works with the new chatbar
2026-02-16 11:16:32 -05:00
Darshan Phaldesai
85b731e9fb TypingPane: fix spacing between elements 2026-02-16 11:16:05 -05:00
James Graham
38b3e65618 Make sure that when we post a message we refocus the now empty chatbar 2026-02-16 07:28:54 +00:00
James Graham
89e5a605c4 Basically we were applying the default values onComponentComplete therefore overriding any user value.
This patch applies the default on creation before properties are set so the user can now override.
2026-02-16 06:39:34 +00:00
James Graham
a438173403 Change the behaviour of chatmarkdownhelper to only work when a single character is added.
This is because if pasting a link for example * or _ could be treated as formatting. We now assume that any pasted text is done as plain text and is wanted as is.
2026-02-16 06:17:57 +00:00
l10n daemon script
98a224ebdf GIT_SILENT Sync po/docbooks with svn 2026-02-16 01:48:30 +00:00
l10n daemon script
7b8576e203 GIT_SILENT made messages (after extraction) 2026-02-16 00:45:45 +00:00
Joshua Goins
f1b5ad7392 Allow focusing and navigating the space drawer with a keyboard
CCBUG: 511591
2026-02-15 08:03:37 -05:00
Joshua Goins
78beb9ffff When favoriting/reprioritizng a room, make sure to re-sort the list
This fixes some odd issues like newly favorited rooms being sent to the
bottom of the room list, and so on.
2026-02-15 08:03:37 -05:00
Joshua Goins
431dbf6457 Stop tree view from attempting to expand sections too early
This fixes a consistent bug for me, where the expandRecursively call
(somehow) predates the room list being populated. Which resulted in some
or all sections being collapsed on start-up.
2026-02-15 08:03:37 -05:00
l10n daemon script
e1b267622d GIT_SILENT Sync po/docbooks with svn 2026-02-15 01:53:30 +00:00
James Graham
47a8221f53 Fix rebase issues and restore mic button 2026-02-14 20:12:21 +00:00
James Graham
c554c40b3b Fix markdown helper test 2026-02-14 19:53:05 +00:00
James Graham
0341da5868 Cleanup chatkeyhelp and tests 2026-02-14 19:53:05 +00:00
James Graham
c5457a893f Fix tests 2026-02-14 19:53:05 +00:00
James Graham
9f6853f771 Make sure that char format is properly maintained for multiple formats when swithching to quote and back 2026-02-14 19:53:05 +00:00
James Graham
526cb07840 Note where nestedlisthelper has been changed 2026-02-14 19:53:05 +00:00
James Graham
dcc394677e Restore image editor 2026-02-14 19:53:05 +00:00
James Graham
a9a73ab24d Disallow event menu in edit mode 2026-02-14 19:53:05 +00:00
James Graham
2289dbb3fe Restore placeholder messages 2026-02-14 19:53:05 +00:00
James Graham
7758175334 Don't allow links when rich formatting is diabled 2026-02-14 19:53:05 +00:00
James Graham
c79753716c Don't follow link in the chatbar 2026-02-14 19:53:05 +00:00
James Graham
3aa4a915b1 Fix cancel reply 2026-02-14 19:53:05 +00:00
James Graham
60cf12524f Improve images in the chatbar
- Don't show the hide button
- Shrink them to better fit
- Allow a little more maxh height in the chatbar for attachments
- Make sure that the button states work properly when adding and removing images
2026-02-14 19:53:04 +00:00
James Graham
f02366ee48 Improve file attachments
-Don't show download and save in chatbar
-Add a remove button
-Add missing properties to model
2026-02-14 19:53:04 +00:00
James Graham
13d7f9b322 Make sure that the style menu shows the right style name for code and quote.
Also when in quote mode the valid styles are now transformed to how they look in quote to show they are valid. Clicking quote style again in a quote block will return to paragrpah style from heading now
2026-02-14 19:53:04 +00:00
James Graham
7afce01a23 Fold extra send icons when thin for mobile 2026-02-14 19:53:04 +00:00
James Graham
0d1f5c950d Change link button tooltip text to Edit link when a link already exists 2026-02-14 19:53:04 +00:00
James Graham
690bc0d385 Revert to Paragraph format after a heading 2026-02-14 19:53:04 +00:00
James Graham
c32235ffe3 Improve application of text formats from buttons.
- If the bar is empty they are now applied as normal
- If there is a selection or the cursor is next to a word it is applied to it.
- If there is no selected text and no word it is applied properly to the next char
2026-02-14 19:53:04 +00:00
James Graham
dff6ab66f1 Comment updates 2026-02-14 19:53:03 +00:00
James Graham
80047acf87 Improve the code for completions 2026-02-14 19:52:46 +00:00
James Graham
234d823366 Really reenable editing messages, don't just do half a job. 2026-02-14 19:52:46 +00:00
James Graham
a3cd0c0e8d Fix memory leak 2026-02-14 19:52:46 +00:00
James Graham
cad90d0c4c Reenable message edits 2026-02-14 19:52:46 +00:00
James Graham
6e28ada1a4 Fix room switch so that if there is no saved text the user ends up with an empty chatbar 2026-02-14 19:52:46 +00:00
James Graham
007ebbc003 Fix merge issues 2026-02-14 19:52:46 +00:00
James Graham
289c6c4f20 Fix delete when selecting text 2026-02-14 19:52:45 +00:00
James Graham
39573c1650 Make the scroll bar of the chat bar move properly as you type. 2026-02-14 19:52:45 +00:00
James Graham
b7a329c199 Fix reply component 2026-02-14 19:52:45 +00:00
James Graham
6b318ec754 Update the look of the chatbar to be floating with the rich text controls on top and send buttons inline 2026-02-14 19:52:45 +00:00
James Graham
79de8a792c Simplify and fix chat markdown helper and add additional tests 2026-02-14 19:52:45 +00:00
James Graham
b45ded678e Fix multi line quotes 2026-02-14 19:52:45 +00:00
James Graham
f4cb660422 Disallow formatting when there is an attachment and when adding one if there is rich formatting warn the user it will be removed and remove if they accept. 2026-02-14 19:52:45 +00:00
James Graham
d64e6fc206 Change editing so that going up or down from a code or quote a para will be added if one doesn't exist so that the block can te typed around 2026-02-14 19:52:45 +00:00
James Graham
d0abfe60f9 Restore missing functionality 2026-02-14 19:52:45 +00:00
James Graham
d10fe4a684 Add more tests 2026-02-14 19:52:45 +00:00
James Graham
9ea76ca5d0 Move the remaining functionality of ChatDocumentHandler to ChatTextItemHelper or split into own objects 2026-02-14 19:52:45 +00:00
James Graham
22d7d90cf4 Separate ChatButtonHelper from ChatDocumentHandler 2026-02-14 19:52:45 +00:00
James Graham
45163944d0 Add tests for ChatMarkdownHelper and rework how formats are applied to make it more robust. 2026-02-14 19:52:45 +00:00
James Graham
f31e9062e6 Move ChatDocumentHandler to use QmlTextItemWrapper and create test 2026-02-14 19:52:45 +00:00
James Graham
416d85af3b Spearate completion from ChatDocumentHandler 2026-02-14 19:52:44 +00:00
James Graham
02bed79265 Fix link insertion 2026-02-14 19:52:44 +00:00
James Graham
aadc441686 Add qml test for chatdocumenthandler 2026-02-14 19:52:44 +00:00
James Graham
4db1e1c437 Add automatic markdown formatting 2026-02-14 19:52:44 +00:00
James Graham
11bf741554 Improve the style picker 2026-02-14 19:52:44 +00:00
James Graham
c128450cf5 Add ChatBarMessageContentModel and hook up 2026-02-14 19:52:43 +00:00
Tobias Fella
9cbe9f7280 Start implementing rich-text editor 2026-02-14 19:45:15 +00:00
James Graham
1f723d1fdf Allow replying to messages within a thread 2026-02-14 14:07:48 -05:00
Tobias Fella
b88e27d6d5 Refactor NeoChatRoom a bit 2026-02-14 12:23:50 -05:00
Joshua Goins
4e616d53b2 Fix the emoticon editor page
It wasn't possible to edit the shortcode or description anymore, because
signals weren't hooked up. I also added a separator to help separate
some of these controls visually.
2026-02-14 12:23:31 -05:00
Joshua Goins
57b6dbcbde Don't open room-specific profile in account menu
This should be your main profile, since visually and functionally this
is "outside" the current room.

Since the workaround is a bit estoeric, I added a comment so I remember
why I did this later.
2026-02-14 12:23:20 -05:00
Joshua Goins
49646c63f8 Improve read marker delegate, add button to mark as read
I added an icon to the read marker (to help distinguish it from
text-heavy chat rooms), and fixed up the padding. I also find myself
reaching to right-click rooms often to mark them as read, so why not do
this from the read marker itself?
2026-02-14 12:23:10 -05:00
Joshua Goins
9d2a427619 Improve the search pages, especially in error and searching states
Someone hit a nasty bug while attempting to find a KDE room on the
kde.org server, the error wouldn't come up normally and the dialog would
be blank. That's because that specific placeholder message doesn't
appear until you type something into the search field, which is weird.

There is a few other oddities with SearchPage that I squashed, including
showing the loading placeholder in more appropiate situations.
2026-02-14 11:04:29 -05:00
Tobias Fella
593ad27e8c Implement sending voice messages 2026-02-14 11:04:22 -05:00
Joshua Goins
3d07f723c8 Make member list sorted a bit more efficient
I don't have any hard numbers on what difference this makes, but it's
definitely a positive improvement. I noticed and fixed a few issues that
were made more glaring by recent changes in libQuotient:

1. Room::memberJoined is called during the historical loading or
whatever, when we only need that *after* stuff is settled.
2. We really don't need to sort the room's members immediately - it's
only relevant when UserListModel is used (and I think this was previous
behavior?) So now its done lazily.
3. We do not want to call Room::effectivePowerLevel willy-nilly. It may
become a more expensive lookup, and it's also varying levels of wasteful
depending on which sorting algorithm the STL uses. It doesn't cost much
for us to keep a temporary cache for the lambda function to use.
2026-02-14 09:55:05 -05:00
Joshua Goins
d47a4eb0de Fix weird scrolling behavior in room list
You may have noticed that the room list acts a bit... odd. You usually
can't scroll all the way down with a scrollwheel, it just
stops. It's possible to continue a little bit more with the scrollbar.
And sometimes the scrollbar doesn't know how big it's actually supposed
to be, commonly occuring when switching from a large room list to a
smaller one.

I narrowed it down the same usual problem with views in QtQuick:
variable sized delegates! Using GammaRay I figured out that for the
delegates currently in use they're slightly different: 46 pixels for
regular room delegates, 42 pixels for section headers and the "Find your
Friends" button was also different.

*Technically* TableView (and by extension TreeView) is supposed to allow
variable-sized delegates, and we should be able to advertise that with
rowHeightProvider. I tried a bunch of different solutions and none of
them worked reliably, so I took the usual sledgehammer approach of
making all of the delegates the same size.

This fixes all of the obvious bugs with the room list I could see, with
the one visual downside of making the section headers slightly taller.
But since I spent some time improving the tap targets, this is only a
visual change and not a functional one.

I also made sure to test it in compact mode, and everything shrinks as
expected.
2026-02-14 09:54:42 -05:00
l10n daemon script
11f8407ef7 GIT_SILENT Sync po/docbooks with svn 2026-02-14 01:52:45 +00:00
Joshua Goins
e4c9230c09 Re-settle the timeline view when replying to messages
Or changing the height of the chatbar in other ways, which I'm sure is
going to become more common with our new rich text system.
2026-02-13 19:58:40 -05:00
Joshua Goins
318432d561 Fix undefined reference in Bubble
This tends to spam the logs while loading/unloading timelines.
2026-02-13 19:58:19 -05:00
Joshua Goins
afa699381b Change loading delegate to animated progress bar
Gives a nicer visual indication instead of static text, in my opinion.
2026-02-13 19:58:19 -05:00
l10n daemon script
244ef77c4a GIT_SILENT Sync po/docbooks with svn 2026-02-13 01:47:10 +00:00
Joshua Goins
847db41fb3 Hide rooms with custom defined types in quick switcher
This matches the behavior in other room lists. I also tried to normalize
the constructor with SortFilterRoomTreeModel.
2026-02-12 16:33:22 -05:00
Joshua Goins
115d4e7466 Reduce the tap target of RoomTreeSection
For some reason I don't understand, the ItemDelegate used for these
sections are ginormous. There is quite a bit of padding which confuses
users as its unexpectedly used for the tap area.

I changed it so we only listen for taps inside of the contentItem
itself, which is a more suitable area.
2026-02-12 07:29:45 -05:00
Joshua Goins
346d311909 Replace 👀 with ❤️ in the quick reactions bar
I personally use this emoji more than the other, and have seen it more
in the wild too.
2026-02-12 07:29:33 -05:00
Joshua Goins
cff27ca7db Add user ID to the account editor page
For some reason this was never featured here, and it's always useful to
have another place to check and/or copy this ID.
2026-02-12 07:29:20 -05:00
Joshua Goins
35daae6b5d Replace "Show QR Code" in account menu with "Open Profile" action
When you just want to share or view your own profile, the UX has proven
to be a bit confusing. You could try to scroll to find a previous
message of yours, or hopelessly go down the rabbithole of settings (none
of which provide a copyable user ID currently.) Lets cut down on the
slack by providing a way to instantly open your profile from anywhere.

This replaces the "Show QR Code" action because this is duplicative
within the profile itself.
2026-02-12 07:29:10 -05:00
l10n daemon script
94fb429c47 GIT_SILENT Sync po/docbooks with svn 2026-02-12 01:47:38 +00:00
Joshua Goins
9f4146e5b1 Fix the emoticon editor looking visually broken
We need to set a minimum width/height here since the Image isn't
technically loaded, when creating a new one.
2026-02-11 18:52:21 -05:00
Darshan Phaldesai
a9b4a900c9 Move notification button to UserInfo
Move notifications button to a more appropriate position next to username. It used to live in the room/spaces bar, which doesn't make sense context wise. Also the mobile view moves to a bottom navbar anyway.

This is just a copy-paste of the adjacent settings tool button with appropriate icon and callback.
2026-02-11 08:56:04 -05:00
Joshua Goins
18d7d2f736 Add a way to open settings on mobile again
This is visible on desktop, but wasn't accessible on mobile.
2026-02-11 08:55:08 -05:00
Joshua Goins
be65d506c4 Fix the mobile title sometimes disappearing
I was depending on an implicit connection property from somewhere.
2026-02-11 08:55:08 -05:00
Azhar Momin
f5d726989f Add support for copying & deleting multiple messages at once
BUG: 496458
2026-02-11 08:00:09 -05:00
l10n daemon script
0f634ff795 GIT_SILENT Sync po/docbooks with svn 2026-02-11 01:47:49 +00:00
Darshan Phaldesai
1289194b3f Messages: Make the Date/Timestamps more usable
Previously timestamps were in the right-hand side of the messages which made it very hard to relate timestamps with their corresponding messages. 
Moving them right next to the name makes much better UX wise (and surprisingly didn't make the UI too crowded). I have tested this in dark light and bubbles mode, and it all looks good and comfortable to me.

I have also tweaked how the timestamps are formatted. 
- For messages on the same day, it will skip the date part.
- For recent days, it uses relative timestamp (yesterday, XX:XX) 
- For everything before its shows short form date and time

The tooltip now uses Long Format of Date and Time.
2026-02-10 19:32:36 -05:00
Tobias Fella
8ca1b8b1d3 Adapt to libquotient api changes 2026-02-10 12:48:50 +00:00
Joshua Goins
793f81e733 Focus code and location chooser maximize components too
I did this for images a while back, but not for these. Otherwise you
can't press escape or perform other key navigation functions easily.

BUG: 515462
FIXED-IN: 25.12.2
2026-02-09 21:32:24 -05:00
Tobias Fella
4c31f42144 Fix type name for NeoChatDateTime
QML doesn't want it to start with an uppercase letter
2026-02-09 21:22:23 -05:00
Azhar Momin
6b664b0547 Fix context menu not appearing in room media scroll view
MessageDelegate calls model.findEvent(root.eventId), which existed in
MessageFilterModel but was missing in MediaMessageFilterModel. This adds
a findEvent() implementation to MediaMessageFilterModel so the context
menu works correctly in the media scroll view.
2026-02-09 21:09:37 -05:00
l10n daemon script
44f78353ab GIT_SILENT Sync po/docbooks with svn 2026-02-10 01:45:39 +00:00
l10n daemon script
3b2df95df7 GIT_SILENT Sync po/docbooks with svn 2026-02-09 01:43:37 +00:00
James Graham
8edb248647 Fix use after free in message delegate. We can't delete the incubator in the completed callback because it then returns to the incubator we just deleted. 2026-02-08 18:21:12 +00:00
l10n daemon script
07bc06f4ff GIT_SILENT Sync po/docbooks with svn 2026-02-08 01:44:05 +00:00
l10n daemon script
0d9988013b GIT_SILENT Sync po/docbooks with svn 2026-02-07 01:44:26 +00:00
Tobias Fella
c1720bbaa7 Fix various qml warnings 2026-02-06 14:45:03 +01:00
Tobias Fella
692ce82717 Fix minor warnings 2026-02-06 14:19:08 +01:00
Tobias Fella
ac04fa6a13 Fixes 2026-02-06 14:18:54 +01:00
Tobias Fella
39aaced0f9 Fix unqualified access 2026-02-06 14:17:55 +01:00
Tobias Fella
9887665560 Prevent shadowing 2026-02-06 14:17:20 +01:00
Tobias Fella
c590cb76a0 Use Application.layoutDirection 2026-02-06 14:14:31 +01:00
Tobias Fella
f9b0c56fa0 Prevent shadowing 2026-02-06 14:12:55 +01:00
Tobias Fella
aa7ab6b2ec Fix minor warnings 2026-02-06 14:12:24 +01:00
Tobias Fella
ac6a5663a1 Use required properties 2026-02-06 14:11:23 +01:00
Tobias Fella
f09dc79995 Remove unused import 2026-02-06 14:09:34 +01:00
Tobias Fella
693b2e74fe SupportDialog: Set ComponentBehavior 2026-02-06 14:07:08 +01:00
Tobias Fella
3a54d58516 Fix pageStack warnings 2026-02-06 14:05:23 +01:00
Tobias Fella
6abb117989 Set componentBehavior 2026-02-06 14:03:47 +01:00
Tobias Fella
b2dee6a96b Fix unqualified access 2026-02-06 14:02:41 +01:00
Tobias Fella
1e1ba1dca3 Cast qml object where necessary 2026-02-06 13:58:55 +01:00
Tobias Fella
55676b06c2 QrScannerPage: Prevent shadowing 2026-02-06 13:57:20 +01:00
Tobias Fella
513496cf85 Add translation context 2026-02-06 13:56:26 +01:00
Tobias Fella
69215401dd Use let instead of var 2026-02-06 13:55:45 +01:00
Tobias Fella
d5ec37e1af Fix some unqualified access warnings 2026-02-06 13:53:20 +01:00
Tobias Fella
6a45e2532c Prevent more shadowing 2026-02-06 13:50:40 +01:00
Tobias Fella
322926e31c Remove invalid anchors 2026-02-06 13:49:44 +01:00
Tobias Fella
29592a7f92 Prevent shadowing 2026-02-06 13:48:54 +01:00
Tobias Fella
8fc8baa2d2 Fix a few minor qml warnings 2026-02-06 12:53:07 +01:00
l10n daemon script
de7f2654f4 GIT_SILENT Sync po/docbooks with svn 2026-02-06 01:55:03 +00:00
l10n daemon script
ccf9ed66f8 GIT_SILENT Sync po/docbooks with svn 2026-02-04 01:55:11 +00:00
Azhar Momin
259c60f669 Fix barcode tooltip text
This was missed in e5a48bae01
2026-02-03 20:19:41 +05:30
l10n daemon script
2ca121ede9 GIT_SILENT Sync po/docbooks with svn 2026-02-03 01:50:48 +00:00
l10n daemon script
fba01c17ae GIT_SILENT Sync po/docbooks with svn 2026-02-02 01:46:05 +00:00
Joshua Goins
5de394b4b7 Don't scroll the timeline when reacting to messages
BUG: 515306
FIXED-IN: 25.12.2
2026-02-01 19:01:32 -05:00
Joshua Goins
edd64d9b8f Improve UserListModel performance and other preparations
In a future patch I want to add support for viewing banned/invited
users, and it's also been mentioned that UserListModel is quite slow
too.

The biggest cost is sorting the member list (power level and
alphabetically) and this happened in a few different ways:
* When the member list updated
* The user switches rooms
* Misc events such as the palette changing

But this was pretty inefficient, because internally Quotient::Room keeps
a list of members, and we kept re-sorting that same list. Our
connections were also too broad and despite having signals for members
joining and leaving we just reloaded the entire list anyway.

So my new solution is to keep the list persistently sorted in
NeoChatRoom, and reload that in UserListModel. This model also keeps
track of *all* members - including ones that left - which will be used
for the aforementioned feature. So UserFilterModel now filters out only
the joined members, and that will be configurable in the future.

I also added two new roles to UserListModel for membership and color
respectively (which makes some dead code useful again) and fixed us
overwriting the built-in Qt roles accidentally.
2026-02-01 17:43:53 -05:00
l10n daemon script
5c614e59f2 GIT_SILENT Sync po/docbooks with svn 2026-02-01 01:45:02 +00:00
Joshua Goins
853d48e768 Improve the structure of the welcome page slightly
I don't really like these pages in NeoChat much, there's only a few
buttons, and they really blend together. In an attempt to alleviate
this problem, I did the following:

* Added icons to the Login and Register actions, which does complement
the other buttons on this page.
* Removed the icons from the "Continue" and "Go back" buttons, which did
nothing but add confusing arrows.
* Moved the "Go back" button, fixed the capitalization and moved it to a
separate FormCard.
* Made it so the "Settings" button is only shown on the initial page, to
reduce the amount of UI clutter while logging in.
2026-01-31 13:18:23 -05:00
l10n daemon script
295ecf0f18 GIT_SILENT Sync po/docbooks with svn 2026-01-31 01:43:41 +00:00
l10n daemon script
7c0ab697d7 GIT_SILENT Sync po/docbooks with svn 2026-01-30 01:54:31 +00:00
l10n daemon script
75fceaccea GIT_SILENT Sync po/docbooks with svn 2026-01-29 01:55:50 +00:00
James Graham
275d221f75 Improve time handling in NeoChat
This introduces a new NeoChatDateTime object that wraps a QDateTime. The intent is that it can be passed to QML and has a series of functions that format the QDateTime into the various string representations we need.

This means we only have to send the single object to QML and then the correct string can be grabbed from there, simplifying the backend. It is also easy to add a new representation if needed as a function with a QString output and Q_PROPERTY can be added and then it will be available.
2026-01-28 16:16:40 +00:00
Joshua Goins
72416884d4 Add comment about push rule check in contextAwareNotificationCount()
I thought this was unnecessary as the push rules should take care of it
for us, but that's not entirely true. I added a comment to reflect this
reality.
2026-01-28 15:58:46 +01:00
Tobias Fella
537ce772af Remove leak in ChatDocumentHandler 2026-01-28 12:29:18 +00:00
Tobias Fella
37b8d8d813 Remove unused variables in linkpreviewertest 2026-01-28 12:29:18 +00:00
Tobias Fella
d64e22c270 Fix leak in actionstest 2026-01-28 12:29:18 +00:00
Tobias Fella
fa4533e757 Refactor ChatBarCacheTest 2026-01-28 12:29:18 +00:00
Tobias Fella
8b6f5447e1 Refactor NeoChatRoomTest 2026-01-28 12:29:18 +00:00
Tobias Fella
6dce1564b7 Fix memory leaks 2026-01-28 12:29:18 +00:00
Tobias Fella
7313386903 Enable lsan on CI 2026-01-28 12:29:18 +00:00
l10n daemon script
c0f5db7fd2 GIT_SILENT Sync po/docbooks with svn 2026-01-28 01:46:58 +00:00
Joshua Goins
551d827dee Use correct terminology when leaving space
BUG: 514888
FIXED-IN: 26.04
2026-01-27 12:05:42 -05:00
Joshua Goins
332a822996 Remove single tap to maximize code component
This is just more ergonomic (in my opinion) as you usually want to
select some text from a code block, instead of maximizing it. There's
already an easy-to-access button for maximizing if you want to.

BUG: 499048
FIXED-IN: 25.12.2
2026-01-27 12:05:22 -05:00
Joshua Goins
f145bbe8db Fix transparency blur not applying to the timeline anymore
We still had the default opaque background for RoomPage. I added a
comment too so it isn't removed accidentally in the future.

BUG: 513363
FIXED-IN: 25.12.2
2026-01-27 12:05:03 -05:00
l10n daemon script
381b119ad1 GIT_SILENT Sync po/docbooks with svn 2026-01-27 01:46:07 +00:00
Albert Astals Cid
b64dcdb004 GIT_SILENT Update Appstream for new release
(cherry picked from commit 4c4f406c41)
2026-01-27 02:13:43 +01:00
l10n daemon script
b33f1cf5e1 GIT_SILENT Sync po/docbooks with svn 2026-01-26 01:43:12 +00:00
James Graham
f22cafbce1 Revert "Improve time handling in NeoChat"
This reverts commit 92c58b0ea0.
2026-01-25 13:07:53 +00:00
James Graham
92c58b0ea0 Improve time handling in NeoChat
This introduces a new NeoChatDateTime object that wraps a QDateTime. The intent is that it can be passed to QML and has a series of functions that format the QDateTime into the various string representations we need.

This means we only have to send the single object to QML and then the correct string can be grabbed from there, simplifying the backend. It is also easy to add a new representation if needed as a function with a QString output and Q_PROPERTY can be added and then it will be available.
2026-01-25 13:04:58 +00:00
l10n daemon script
c797ecea3d GIT_SILENT Sync po/docbooks with svn 2026-01-25 01:44:45 +00:00
l10n daemon script
b65590a9b9 GIT_SILENT made messages (after extraction) 2026-01-25 00:44:24 +00:00
Joshua Goins
2bacbe7ac7 Improve the bottom mobile navigation bar
The previous set of actions seems like a random selection, how many
rooms is someone creating to be that important?

I have redone it to have way fewer actions, mostly notification and
settings.
2026-01-24 10:55:36 -05:00
Joshua Goins
74c12e89ea Improve other space button texts
Remove some extra verbiage, ensuring ellipses and so on.

CCBUG: 497044
2026-01-24 10:52:31 -05:00
Joshua Goins
02b95c921d Add ellipses to "Remove" button in space hierarchy
This can be somewhat unclear if NeoChat prompts you (it does!)

CCBUG: 497044
2026-01-24 10:52:31 -05:00
Joshua Goins
7f58ee3793 Improve space suggestions editing
This is confusing to users as there may be two "remove" icons, but one
of them is actually for suggestions.

BUG: 497044
FIXED-IN: 26.04
2026-01-24 10:52:31 -05:00
l10n daemon script
7312bf183d GIT_SILENT Sync po/docbooks with svn 2026-01-24 01:44:54 +00:00
Tobias Fella
40b7853338 Fix binding loop 2026-01-23 23:15:00 +01:00
Azhar Momin
ade5750550 Add a button to mark spaces as read
[BUG: 508122](https://bugs.kde.org/show_bug.cgi?id=508122)
2026-01-23 15:29:51 -05:00
l10n daemon script
fb8ee02e3b GIT_SILENT Sync po/docbooks with svn 2026-01-23 01:45:20 +00:00
Tobias Fella
8b27323488 Extend testing 2026-01-22 23:33:45 +00:00
Tobias Fella
96d24f5c3a Minor fixes to various models 2026-01-22 23:33:45 +00:00
Tobias Fella
45cee495a5 Adapt LineModel to being autotested 2026-01-22 23:33:45 +00:00
Tobias Fella
53dc9c1944 Use getter and setter for property in LiveLocationsModel 2026-01-22 23:33:45 +00:00
Tobias Fella
1fb215dae7 Add test that runs each model with a QAbstractItemModelTester 2026-01-22 23:33:45 +00:00
Tobias Fella
971875c8a2 PowerLevelModel: Use Qt::UserRole for role value
Otherwise it's equal to Qt::DecorationRole, which QAbstractItemModelTester expects to be convertible to certain types
2026-01-22 23:33:45 +00:00
Tobias Fella
9ad64b990d NotificationsModel: Don't crash if connection is nullptr
This can legitimately happen
2026-01-22 23:33:45 +00:00
Tobias Fella
1ceffe6a2e Start adapting to libquotient crypto api changes 2026-01-22 23:26:55 +00:00
l10n daemon script
9810b3dee0 GIT_SILENT Sync po/docbooks with svn 2026-01-22 02:00:17 +00:00
Tobias Fella
76954c162a NotificationsManager: Improve some comments
(as suggested by CLion)
2026-01-21 16:11:07 +00:00
Tobias Fella
0a7978f4f5 NotificationsManager: Improve function parameters 2026-01-21 16:11:07 +00:00
Tobias Fella
bf41e1083d NotificationsManager: Make a function static 2026-01-21 16:11:07 +00:00
Tobias Fella
593f772845 NotificationsManager: Minor refactoring 2026-01-21 16:11:07 +00:00
Tobias Fella
98a277ac63 const auto a few things 2026-01-21 16:11:07 +00:00
Tobias Fella
cfe5182a65 Remove redundant check 2026-01-21 16:11:07 +00:00
Joshua Goins
9ace01f74a Fix closeToYEnd check
The comparison operator was reversed, and this was seen with mark as
read being broken and buttons showing up at the wrong times.
2026-01-21 08:21:56 -05:00
l10n daemon script
4632a9f9bb GIT_SILENT Sync po/docbooks with svn 2026-01-21 01:49:34 +00:00
Azhar Momin
a92587cc50 Fix some typos 2026-01-20 17:42:51 +00:00
Tobias Fella
889b7dd2e6 Fix crash when logging out active connection 2026-01-20 14:40:57 +00:00
Tobias Fella
44fa196a26 Use QPointer to store room in WidgetModel 2026-01-20 14:05:30 +00:00
Tobias Fella
2bc8c6a379 Fix various qml warnings 2026-01-20 13:48:46 +00:00
l10n daemon script
3f1ba8d067 GIT_SILENT Sync po/docbooks with svn 2026-01-20 01:53:20 +00:00
Azhar Momin
a1c9b63d1e Fix emojis being too small in EmojiDelegate
BUG: 514170
2026-01-19 09:40:18 +00:00
l10n daemon script
73fdc72ce7 GIT_SILENT Sync po/docbooks with svn 2026-01-19 01:44:12 +00:00
Tobias Fella
08fc8be09c Cleanup README.md a bit 2026-01-18 22:51:55 +01:00
Carl Schwan
e5a48bae01 UserDetailDialog: Remove double QR code action
Now it's part of the header
2026-01-18 22:08:56 +01:00
Carl Schwan
581f5be410 UserDetailDialog: Improve consistency
Use same spacing and sizing as GroupChatDrawerHeader and add a QR code
to share the contact.

Signed-off-by: Carl Schwan <carlschwan@kde.org>
2026-01-18 21:58:34 +01:00
l10n daemon script
f4e857519b GIT_SILENT Sync po/docbooks with svn 2026-01-18 01:50:53 +00:00
Joshua Goins
93e932c09c Add hack to fix crash when sending long text reactions
This is some bug in Flow (that is really hard to debug, I can't get it
to exit at all) but we can work around it for a minor visual impact. It
seems to me allow the reaction list to become slightly larger, but
that's about it.

BUG: 504344
FIXED-IN: 25.12.2
2026-01-17 15:01:44 -05:00
Joshua Goins
3b8930c2bc Cleanup few remaining atYEnd usages in TimelineView
These were either mistakes or rebase errors, but we should be using
closeToYEnd here.
2026-01-17 13:49:40 -05:00
Joshua Goins
7e34570a05 Improve placeholder text for WidgetsPage
This shortens the text and adds an icon.
2026-01-17 13:49:29 -05:00
Joshua Goins
0b5de13c36 Improve Shared Location messaging
This changes the icon for the shared locations page, adds better
placeholder text and so on.
2026-01-17 13:49:29 -05:00
Joshua Goins
6eb2b2e739 Add hack to fix room sidebar not sticking to the top
This is similar to the TimelineView hacks, but this time its the header
item that's changing height as our room topics and such wrap.
2026-01-17 13:49:18 -05:00
Joshua Goins
be89362fdd Fix left padding for "Rooms" label in the room list
This now emulates the default Kirigami heading behavior now, with the
correct amount of padding.
2026-01-17 13:14:05 -05:00
Joshua Goins
e53c84d30c Fix Quick Switcher not being activatable by Enter/Return key 2026-01-17 13:13:52 -05:00
Joshua Goins
a90c26f566 Don't show the Share action for non-file messages
This only shares files, if you try it on anything else it crashes
NeoChat.
2026-01-17 13:13:43 -05:00
Darshan Phaldesai
c2ae5afa73 ReactionComponent: visual changes to make it look consistent 2026-01-17 12:19:46 -05:00
Joshua Goins
5f20a86b62 Reduce the amount of items in the account menu
The devices entry gone (not even Element has this) and so is the logout
action. The Switch account menu item is moved to the bottom so its
easier to access by mouse. The "Open Secret Backup" is moved under
Security settings where it lives next to the other crypto-related
settings.
2026-01-17 11:03:38 -05:00
l10n daemon script
f2d3c9706e GIT_SILENT Sync po/docbooks with svn 2026-01-17 01:44:32 +00:00
Joshua Goins
39de4d10e4 Add hack around the timeline never settling just right
This is due to some kind of bug in ListView that never resettles
properly for bottom-to-top views. This can arise when the pinned message
is loaded (because that squishes the view) or the window is resized
(because that also resizes the view.)

We can work around it by assuming the following:
1. The RoomPage knows the window is resizing because it gets its height
changed before TimelineView.
2. The first height change can be a marker to position the view at the
beginning.

This fixes the issue for me, I did the following in order to test this:
* Switch between many rooms, especially ones with a pinned message. Now
all of them start at the bottom as they should.
* Resize the window, ensure that if you scrolled it stays around that
position - otherwise it sticks at the bottom.
2026-01-16 18:34:05 -05:00
Joshua Goins
4c37dcf518 Improve reliability of restoring the last space and room (again)
I found that 50% of the time, NeoChat won't restore the last space but
instead get stuck at Home. Even worse, it will overwrite Home's last
opened room with the one from the space - resulting in really buggy
behavior.

The reason why this happens is partly due to the space hierarchy cache
(I think) but that's not the real problem in my opinion. During
setCurrentSpace, we needlessly update the last space & room config
despite us being the ones already reading it.

In addition to that I also refactored this code a bit to be more
consolidated and readable.
2026-01-16 18:14:07 -05:00
Joshua Goins
3c77711417 Add hack around atYEnd
This fixes the annoying "I just scrolled down to the bottom, how come
NeoChat doesn't think I did?"

From what I can tell this is also ListView bug (or something caused by
our style/Kirigami) that creates cases like contentY being -643.2 (for a
ListView of height 643) thus that's not "at Y's end". For our case
though, we don't care and can safely round it.
2026-01-16 17:07:03 -05:00
Yuri Chornoivan
136063bd37 Fix minor typo 2026-01-16 05:29:13 +02:00
l10n daemon script
730a9e97fd GIT_SILENT Sync po/docbooks with svn 2026-01-16 01:53:34 +00:00
Joshua Goins
d5260376d2 Support replying and editing messages directly from room search
There's two parts to making this work mainly:
1. Use getEvent instead of findInTimeline so the related event is
actually found.
2. Close the dialog once a reply relation is found, so you can easily
reply in the chat bar.
2026-01-15 20:13:50 -05:00
Joshua Goins
dc935e09b7 Use countedNotifications instead of our own calculations w/ DMs
This fixes an odd disconnect you can sometimes see when the notification
isn't an invite or a "direct chat notification", which conflicts with
what we use to control the tooltip and visibility.
2026-01-15 16:51:57 -05:00
Joshua Goins
5759f7d82b Add a way to view server support information
This is useful if your server has said information, for matrix.org
includes abuse and administrator e-mails.

See #707
2026-01-15 16:21:03 -05:00
Joshua Goins
ed4b77c184 Remove the three item hamburger menu, re-distribute remaining items
There are only three, somewhat odd menu items remaining in this menu.
(Two if you don't have a camera.)

* Find your Friends, which is already accessible in a few other places
and currently has dubious utility.
* Create a Room, which also is barely used and can be combined with the
Create Space button in the space drawer.
* Scan a QR code, which can be placed in the account menu. I know this
isn't the most ideal place, but I can't think of anything better at the
moment.
2026-01-15 16:18:57 -05:00
Tobias Fella
716ee2e494 When "always allow verifying devices" option is enabled, show a less confusing message in the devices page 2026-01-15 09:12:17 -05:00
Joshua Goins
c15860cac3 Give DelegateContextMenu an actual room
This allows me to hide the "Reply" action for read-only rooms. Don't ask
me how it even worked before, I don't know.
2026-01-15 09:00:35 -05:00
Joshua Goins
f5c991c55c Pass room through the model, not when creating the delegate
This is another thing that enables us to view multiple rooms in a single
timeline. Specifically, this improves the experience in room search
going across room versions and getting a correct readOnly status (for
hiding certain hover actions.)
2026-01-15 09:00:35 -05:00
Joshua Goins
41609749d8 Fix a crash when grabbing relationAuthor
There's a bug in how we're using this function in room search, but we
definitely don't want it to crash. The event is technically not in the
timeline, so we were dereferencing an invalid iterator or whatever.
2026-01-15 09:00:35 -05:00
Joshua Goins
644df80090 Don't show "Configure Web Shortcuts" if there are none
This prevents some weird edge cases where the Configure action is
visible, but nothing is actually searchable - like for images.
2026-01-15 09:00:23 -05:00
Joshua Goins
e3307326ef Close the message menu after selecting a quick reaction
And also ensure the "select an emoji" menu doesn't close the message
menu after *not* choosing an emoji, so it acts more like a submenu.
2026-01-15 09:00:13 -05:00
Joshua Goins
74d4e786d3 Clarify where reports are sent to
Contrary to popular belief (unfortunately) these reports are *only* sent
to your own server, which is then opaquely handled in some unknownable
way.

See #707
2026-01-15 09:00:02 -05:00
Joshua Goins
1e461658b8 Hide "Reply in Thread" message action if we don't have threads enabled 2026-01-15 08:16:41 -05:00
l10n daemon script
f305cb849f GIT_SILENT Sync po/docbooks with svn 2026-01-15 01:52:21 +00: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
l10n daemon script
ab8e2f7573 GIT_SILENT Sync po/docbooks with svn 2025-08-17 01:40:12 +00:00
Darshan Phaldesai
7991429ef4 appstream: fix developer id 2025-08-16 12:28:53 +09:30
l10n daemon script
e3b70a14be GIT_SILENT Sync po/docbooks with svn 2025-08-16 01:42:20 +00:00
Tobias Fella
39abf6b5f3 Start adding tests for RoomManager 2025-08-15 08:10:08 +00:00
l10n daemon script
096842bd3a GIT_SILENT Sync po/docbooks with svn 2025-08-15 01:51:42 +00:00
Tobias Fella
c69db9d375 Improve chatbar actions
Introduce a new type BusyAction that wraps Kirigami.Action with the added isBusy property. This makes QML a bit happier
2025-08-14 20:56:26 +00:00
Tobias Fella
ec36d519b1 Cleanup and fix GlobalMenu 2025-08-14 22:47:42 +02:00
Tobias Fella
45b02ae34e Cleanup buttons
Mostly removing the usage of the action property, since there's no point in using it. Also add some translation contexts and some other minor cleanup
2025-08-14 20:46:46 +00:00
Kai Uwe Broulik
9b763daf52 notificationsmanager: Don't draw room avatar if it is null
Ideally, we painted the avatar with initials here but for now
it's better to not paint anything than an awkward white circle.
2025-08-14 11:28:18 +00:00
Tobias Fella
6e8ed5b341 Fix qml warnings in NewPollDialog 2025-08-14 09:23:04 +00:00
Tobias Fella
63a3c3e58a Fix build with kio 2025-08-14 09:22:16 +00:00
Tobias Fella
aadd9b0189 Fix qmllint warnings in QuickFormatBar 2025-08-14 09:20:28 +00:00
l10n daemon script
39f595e45d GIT_SILENT Sync po/docbooks with svn 2025-08-14 01:46:59 +00:00
Tobias Fella
823b2d3747 Add translation context 2025-08-13 22:13:59 +02:00
Tobias Fella
eb268576da Fix some warnings 2025-08-13 22:13:53 +02:00
Tobias Fella
8e51f3ec8e Fix some type warnings 2025-08-13 21:52:36 +02:00
Tobias Fella
de03e1ce2b Remove unused import 2025-08-13 21:52:22 +02:00
Tobias Fella
21b1258b8d Only allow opening QuickSwitcher if there is an active connection 2025-08-13 21:46:43 +02:00
Tobias Fella
becad8c127 Fix qml warnings in QuickSwitcher 2025-08-13 21:45:15 +02:00
Tobias Fella
4044048352 Add translation context 2025-08-13 21:44:34 +02:00
Tobias Fella
7d6bd7ab4c Add contexts to string 2025-08-13 21:37:05 +02:00
Tobias Fella
209ae00f8f Remove unused imports 2025-08-13 21:36:45 +02:00
Tobias Fella
f64c860453 Register dependency on KSyntaxHighlighting 2025-08-13 21:36:14 +02:00
Tobias Fella
36fccaffe6 Fix warnings 2025-08-13 21:32:45 +02:00
Tobias Fella
13deb2d928 Fix qmllint warnings 2025-08-13 21:26:24 +02:00
Tobias Fella
14fe71b556 Fix most warnings in AccountSwitchDialog 2025-08-13 19:37:21 +02:00
Tobias Fella
ecb900994b Remove unused import 2025-08-13 19:30:14 +02:00
Tobias Fella
55b97b469e Fix some qmllint warnings 2025-08-13 19:20:45 +02:00
Tobias Fella
1d594b492d Remove unused imports 2025-08-13 19:18:26 +02:00
Tobias Fella
3902293de7 Add translation contexts 2025-08-13 19:14:54 +02:00
Tobias Fella
a8bc51667c Fix various warnings and add translation contexts 2025-08-13 19:07:06 +02:00
Tobias Fella
0bcf6e74c0 Fix warnings in SearchPage 2025-08-13 18:56:37 +02:00
Tobias Fella
78b218fef3 Fix some qml type warnings 2025-08-13 17:43:51 +02:00
Tobias Fella
964bcfd5f5 Register dependency on KConfig to qml 2025-08-13 17:41:32 +02:00
Tobias Fella
fc733f9ba1 Fix some unqualified access warnings 2025-08-13 17:41:23 +02:00
l10n daemon script
e7e83fa789 GIT_SILENT Sync po/docbooks with svn 2025-08-13 01:41:50 +00:00
Tobias Fella
ef4f11546f Fix copying images 2025-08-12 14:20:57 +00:00
l10n daemon script
648796b9e0 GIT_SILENT Sync po/docbooks with svn 2025-08-12 01:42:57 +00:00
Tobias Fella
3b5da2473d Add missing copyright statement 2025-08-11 23:18:56 +02:00
Tobias Fella
9a04ae3e02 Fix opening files externally 2025-08-11 23:05:57 +02:00
James Graham
9ed5224470 Have ChatDocumentHandler update ChatBarCache text
Have ChatDocumentHandler update ChatBarCache text so all text operations in the ChatBar go through it
2025-08-11 19:33:58 +01:00
James Graham
bc82ceeb5f Simpify the API for ChatDocumentHandler
Simpify the API for ChatDocumentHandler by taking the text item and grabbing everything else needed from there
2025-08-11 19:23:40 +01:00
Tobias Fella
5f7ff209d3 Add .contextProperties.ini
Tells qmlls to ignore i18n context properties
2025-08-11 17:35:02 +02:00
Tobias Fella
35b363fdce Update room versions in security settings
We need to come up with a better way of testing the versions here, but that's for different patch
2025-08-11 10:07:19 +00:00
Tobias Fella
a74931e794 Refactor qml 2025-08-11 12:04:10 +02:00
Tobias Fella
6698bbcf79 Fix warning 2025-08-11 12:04:10 +02:00
Tobias Fella
8c78992b1a Remove broken and duplicate code 2025-08-11 12:04:08 +02:00
l10n daemon script
04e3b88e8c GIT_SILENT Sync po/docbooks with svn 2025-08-11 01:42:20 +00:00
l10n daemon script
9b8b13e98e GIT_SILENT made messages (after extraction) 2025-08-11 00:42:48 +00:00
l10n daemon script
e6dd6aec7f GIT_SILENT Sync po/docbooks with svn 2025-08-10 01:44:51 +00:00
l10n daemon script
c6f0879c9c GIT_SILENT Sync po/docbooks with svn 2025-08-09 01:39:31 +00:00
Joshua Goins
9e7ae37add Partially revert recent RoomMedia change to make it work again
This reverts part of f288367653 which
touches this file. I'm not entirely sure why it was changed, it looks
like a piece of refactoring that isn't complete yet, but MessageDelegate
still depends on a required room property.
2025-08-08 08:39:08 -04:00
Tobias Fella
0c727237ee Remove Edit menu
This menu providers a few text editing related functions, like undo/redo, copy, paste, etc.
As far as I can tell, it never worked in a useful way, though: It operates on the activeFocusItem,
but as soon as one clicks on the menu, this becomes null. It is somewhat useable through shortcuts,
but the text fields have these shortcuts natively.
2025-08-08 08:25:51 -04:00
Joshua Goins
bf66118355 Change "Copy Room Address" action to "Copy Room Link"
This is way more useful for sharing, as you can use this link in and
outside Matrix rooms. It also matches behavior with other clients like
Element Web.

I also cut some places where this shows unnecessarily, such as direct
chats and invite-only private rooms.
2025-08-08 08:25:34 -04:00
Tobias Fella
aacb097650 Add libQuotient qml module dependencies
The module doesn't exist upstream yet; this will just be ignored in that case
2025-08-08 13:42:24 +02:00
Tobias Fella
ce5d60fc5d Fix QML warning 2025-08-08 13:42:00 +02:00
Tobias Fella
96a0b86c33 Fix some unqualified access warnings 2025-08-08 13:39:06 +02:00
Tobias Fella
279b611754 Remove unused prison 2025-08-08 10:42:38 +02:00
Tobias Fella
f7c74a60cd Port away from applicationWindow() 2025-08-08 10:29:48 +02:00
l10n daemon script
3465fc7d39 GIT_SILENT Sync po/docbooks with svn 2025-08-07 19:27:58 +00:00
l10n daemon script
6381f06acb GIT_SILENT made messages (after extraction) 2025-08-07 18:30:01 +00:00
l10n daemon script
a7c7a5c72d GIT_SILENT Sync po/docbooks with svn 2025-08-07 01:42:12 +00:00
l10n daemon script
c8ded65e46 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2025-08-07 01:33:59 +00:00
l10n daemon script
729b46fc71 GIT_SILENT made messages (after extraction) 2025-08-07 00:42:54 +00:00
Tobias Fella
e63e04aa57 Fix QML warning 2025-08-07 00:21:10 +02:00
Tobias Fella
ae88879651 Remove unused kitemmodels imports 2025-08-07 00:19:18 +02:00
Tobias Fella
dfd106258b Fix some warnings around pagestack calls 2025-08-07 00:05:52 +02:00
Tobias Fella
4d29b9fd57 Fix qml warning 2025-08-06 23:53:04 +02:00
Tobias Fella
3de7ad237a Fix qml warning 2025-08-06 23:51:56 +02:00
Tobias Fella
7d4e589894 Remove some unused includes 2025-08-06 23:49:27 +02:00
Tobias Fella
4bbd127fe8 Fix some unqualified access 2025-08-06 23:47:28 +02:00
Tobias Fella
f2a0a66b01 Fix connection 2025-08-06 23:47:28 +02:00
James Graham
143c685045 Fix creating a new thread locally
Fix creating a new thread locally. We need to listen for the newThread signal and update the thread root event mode content
2025-08-06 18:10:03 +01:00
Tobias Fella
b8fa6f0690 Fix font sizes 2025-08-06 15:34:33 +02:00
Joshua Goins
93b6c53c82 Remind the user if they're trying to reply to someone who has left
This happens semi-frequently to me and others - we reply to a user who
has left the room. Sometimes this is useful (for example, bringing up a
previous topic) but in most cases this is accidental, and almost
guaranteed to happen if you turned off join/leave events.

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

Get rid of the minimum letter count.

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

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

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

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

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

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

Also, a information tip has been added to the settings page to clarify
what these settings do and imply that the per-room settings take
precedence.
2025-07-08 17:17:04 -04:00
l10n daemon script
bc7b480c41 GIT_SILENT Sync po/docbooks with svn 2025-07-08 01:40:16 +00:00
Tobias Fella
d9b495356d Fix roomlist positioning when changing rooms
The room list is supposed to center the new room, but, since the current room as stored in SortFilterRoomTreeModel is only updated after we're jumping to the room, mostly just jumped to the old room.
2025-07-07 18:31:40 +00:00
Tobias Fella
ce82606e6e Fix jumping to replied-to event 2025-07-07 14:20:02 +00:00
l10n daemon script
07837c2e64 GIT_SILENT Sync po/docbooks with svn 2025-07-07 01:38:29 +00:00
Tobias Fella
1738253e6f Don't crash when calling RoomManager::viewEventMenu with empty event id 2025-07-06 23:14:28 +02:00
James Graham
17fa2246da Refine MessageContentModel further
Create some standard functions that should hopefully make it easier to manage the model contents. Used for file stuff for now.
2025-07-06 21:16:17 +01:00
l10n daemon script
4f5e096e7e GIT_SILENT Sync po/docbooks with svn 2025-07-06 01:37:58 +00:00
Albert Astals Cid
b125c284bd GIT_SILENT Upgrade release service version to 25.11.70. 2025-07-05 11:59:46 +02:00
l10n daemon script
87213903b1 GIT_SILENT Sync po/docbooks with svn 2025-07-05 01:39:27 +00:00
James Graham
3d9d211d25 Allow the condition for when messages are automatically marked as read to be configurable.
Title this adds a number of options for when messages should be automatically marked as read for the user to choose from.

![image](/uploads/cef95f8c6c77bfdcabb7a8a309bc1fd2/image.png){width=480 height=262}
2025-07-04 14:36:36 +01:00
James Graham
d5d291396d MessageContentModel improvements
Remove the hack for DelegateChooser and minimise the amount of updates when data changes
2025-07-04 10:28:50 +01:00
l10n daemon script
d7cd38b5e5 GIT_SILENT Sync po/docbooks with svn 2025-07-04 01:39:05 +00:00
l10n daemon script
1a25cfc0e3 GIT_SILENT Sync po/docbooks with svn 2025-07-03 07:51:46 +00:00
l10n daemon script
8d9669e033 GIT_SILENT Sync po/docbooks with svn 2025-07-02 11:07:52 +00:00
l10n daemon script
690d2e2b12 GIT_SILENT Sync po/docbooks with svn 2025-07-02 08:01:01 +00:00
l10n daemon script
2208b1b2b3 GIT_SILENT Sync po/docbooks with svn 2025-07-02 01:40:33 +00:00
Joshua Goins
67da272ccd Add feature flag for the new calls feature
This will be experimental for some time, so we need a way to turn it
on/off for future stable builds.
2025-07-01 14:52:46 -04:00
Joshua Goins
171930fb1d Show a message when failing to download a file
And optionally show an error message, if libQuotient gets one. This
helps in certain rare situations where you just *Cannot* download
something. Before, NeoChat would just reset the image or video without
telling you anything but now there's a nice big error message telling
you what's wrong.
2025-07-01 06:53:59 -04:00
l10n daemon script
2bd59fa16f GIT_SILENT Sync po/docbooks with svn 2025-07-01 01:40:14 +00:00
Joshua Goins
d0d0384bdb Improve the look of loading videos
The video thumbnail is no longer hidden while loading. This makes it
look less buggy (like NeoChat forgot what the thumbnail was) while
loading a large video. To help with contrast, a slight background tint
is added.
2025-06-30 17:21:40 -04:00
James Graham
680573de61 Make sure that thread replies aren't hidden when threads are off 2025-06-30 19:34:27 +01:00
Heiko Becker
ba7f866cf5 GIT_SILENT Update Appstream for new release
(cherry picked from commit 7e87d415ae)
2025-06-30 18:57:23 +02:00
Tobias Fella
57a95ae972 Show nicer text for call member events 2025-06-30 17:17:47 +02:00
Marco Martin
de97275a38 Use new Kirigami builtin column resize handle
Use the new Kirigami.ColumnView.interactiveResizeEnabled attached property
which allows to have a built in resize handle for ColumnView pages
removing the need for a custom handle with resize logic

depends from https://invent.kde.org/frameworks/kirigami/-/merge_requests/1795
2025-06-30 11:25:28 +02:00
l10n daemon script
d08181f56d GIT_SILENT Sync po/docbooks with svn 2025-06-30 01:41:18 +00:00
James Graham
f6e8491bf1 Split message content into its own module
This is laying some groundwork for the rich text chatbar.
2025-06-29 12:43:48 +01:00
l10n daemon script
a1447ebd6f GIT_SILENT Sync po/docbooks with svn 2025-06-29 01:44:03 +00:00
l10n daemon script
3b67e4deaf GIT_SILENT Sync po/docbooks with svn 2025-06-28 01:39:59 +00:00
James Graham
e55afd0402 Move ImageEditorPage to Chatbar module 2025-06-27 16:25:57 +01:00
James Graham
7c0710d445 ActionToolbar is no longer needed now there is only 1 button 2025-06-27 16:08:30 +01:00
James Graham
61f9cd41f7 Initial Account
Restore the functionality where clicking on "edit this account" from the AccountMenu opens setting to the account rather than pushing as its own window
2025-06-27 16:08:14 +01:00
l10n daemon script
6c7a7a7be5 GIT_SILENT Sync po/docbooks with svn 2025-06-27 01:41:18 +00:00
James Graham
63125d97c3 Fix hiding down button on timeline when at the bottom 2025-06-26 14:38:16 +01:00
James Graham
cb4e7d4943 Fix room list header when collapsed 2025-06-26 14:34:18 +01:00
l10n daemon script
b9d5ed699e GIT_SILENT Sync po/docbooks with svn 2025-06-26 01:41:04 +00:00
l10n daemon script
75fe5c8970 GIT_SILENT Sync po/docbooks with svn 2025-06-25 01:42:54 +00:00
Aleix Pol
91da2d01b7 UserInfo: Move the switch user action to the menu
It doesn't clutter the main UI.
It allows us to see the keyboard shortcut.
2025-06-23 23:19:05 +00:00
l10n daemon script
4bade72ce4 GIT_SILENT Sync po/docbooks with svn 2025-06-23 01:39:47 +00:00
l10n daemon script
9c05e2feed GIT_SILENT Sync po/docbooks with svn 2025-06-22 01:45:29 +00:00
l10n daemon script
ba94098411 GIT_SILENT Sync po/docbooks with svn 2025-06-21 01:41:18 +00:00
James Graham
43c691160e Update the RoomTreeSection to use standard Kirigami.ListSectionHeader 2025-06-20 07:42:43 +01:00
James Graham
e2daa091e8 Switch section delegate for standard Kirigami.ListSectionHeader
Switch section delegate for standard Kirigami.ListSectionHeader, SectionDelegate is no longer required so removed.
2025-06-20 07:42:14 +01:00
l10n daemon script
f026414b1a GIT_SILENT Sync po/docbooks with svn 2025-06-20 01:39:04 +00:00
l10n daemon script
fd640a4bd0 GIT_SILENT Sync po/docbooks with svn 2025-06-19 01:39:06 +00:00
l10n daemon script
dd433d7f99 GIT_SILENT Sync po/docbooks with svn 2025-06-18 01:39:30 +00:00
James Graham
3183e00acc Fix Use After Free MessageDelegateBase
Make sure that a `MessageDelegateBase` is not used after free by a `MessageObjectIncubator` callback by tracking them and cleaning them up on deletion of a `MessageDelegateBase`
2025-06-17 17:19:38 +01:00
James Graham
1860de12ea RoomPage cleanup
This does some further cleanup of RoomPage, mostly removing all the vestigial bits from when we could have multiple windows. But also stuff is moved to TimelineView where possible.

The loading placeholder is removed as TimelineModel already has this built in.

TimelineView now gets room from it's model. This is to ensure we're always using the same room as it which may not be true momentarily when RoomManager.currentRoom changes as the model does it's own reset sequence. This will prevent some race conditions in future (and which I already hit creating other MRs)
2025-06-17 16:41:38 +01:00
l10n daemon script
277f1ab252 GIT_SILENT Sync po/docbooks with svn 2025-06-17 01:40:15 +00:00
Thiago Sueto
324b332fa6 Add confirmation prompt to reset all configuration
Resetting all configuration is destructive, it should require a confirmation prompt instead of triggering immediately.
2025-06-16 18:00:06 +00:00
James Graham
33d29f6b02 Fix jump to last message button trigger, the list view is no longer root. 2025-06-16 18:04:25 +01:00
l10n daemon script
419aed6375 GIT_SILENT Sync po/docbooks with svn 2025-06-16 01:39:23 +00:00
Aleix Pol
7bb26ac3be main: Add a replace option
Much like many other unique KDE components, add --replace so the running
instance can be replaced by the new one. Useful for development.
2025-06-15 23:07:40 +02:00
Aleix Pol
7654333ec1 ImageComponent: Can't make a qreal undefined
Using -1 because MediaSizeHelper is already handling negative numbers as
such.
2025-06-15 23:07:40 +02:00
Aleix Pol
d3a2da391d flatpak: Use the Qt 6.9 runtime
It's all the fresh!!
2025-06-15 23:07:40 +02:00
Aleix Pol
f19f59c37f EmojiPicker: Fix delegate for stickers
There's no avatarUrl in ImagePacksModel, it's just url.
2025-06-15 23:07:40 +02:00
Aleix Pol
94e53e14a3 UserInfo: Use "Account" naming consistently
We use "Account" in the settings so we better use the same concept
across the UI.
2025-06-15 23:06:15 +02:00
Manuel Alcaraz Zambrano
d2b3788872 Remove QCoro from Flatpak
QCoro is already available on the runtime.
2025-06-15 12:07:24 +02:00
l10n daemon script
8095062db2 GIT_SILENT Sync po/docbooks with svn 2025-06-15 01:40:14 +00:00
Tobias Fella
89beaa9316 Remove stray warning 2025-06-15 00:29:34 +02:00
Carl Schwan
dfaffd043e Cleanup craft config
Don't build master version of some modules
2025-06-14 12:13:31 +00:00
Tobias Fella
a8c200222f Don't parent PollHandler to room
PollHandlers are stored in a QCache, which takes over ownership of the object. At the same time, PollHandler currently relies on its parent being the room.
Somehow, this didn't explode entirely, but only leads to minor problems like crashes on shutdown.
2025-06-14 12:13:16 +00:00
Tobias Fella
598a1c28ac Add button for hiding images and videos 2025-06-14 12:12:01 +00:00
Tobias Fella
b972703f34 Refactor and fix opening file-based content
The relevant fix is checking whether the the event is file-*based* (i.e., image, video, audio, file), not whether it *is* a file
2025-06-14 12:07:56 +00:00
Tobias Fella
d30fdc67c6 Remove file logging
This hasn't proven to be as useful as i had hoped:
- My arcane logic for determining logging categories is apparently broken
- It won't work with the logging by the new crypto sdk
- I never actually ended up looking at my own logs, or anyone else's
- It seems to cause crashes
2025-06-14 11:57:23 +00:00
James Graham
6a5a2e6144 Refactor TimelineView
Refactor TimelineView to make it more reliable and prepare for read marker choice. This is done by creating signalling from the mode when reset which can be used to move the scrollbar to the newest meassage.

Some of the spaghetti is also removed so there is no need for ChatBar and TimelineView to talk directly.

The code to mark messages as read if they are all visible after 10s has been removed infour of just marking as read on entry if all are visible. This is temporary until a follow up providing user options is finished (although it will be one of the options)
2025-06-14 12:16:39 +01:00
Kai Uwe Broulik
235143528c MaximizeComponent: Fix download location
No idea where "saveFolder" was supposed to be coming from but this
makes it use the last location or default to downloads, consistent
with the other save dialogs.
2025-06-14 10:08:12 +02:00
l10n daemon script
33ca2b8d09 GIT_SILENT Sync po/docbooks with svn 2025-06-14 01:40:03 +00:00
Aleix Pol
50176cfafb ImageComponent: Do not set undefined to int
An int property cannot be undefined, make it var.
2025-06-13 15:46:30 +00:00
Aleix Pol
a67fb36a84 SpaceHierarchyDelegate: Fix opening rooms
RoomManager is in org.kde.neochat and needs importing
2025-06-13 15:46:30 +00:00
Aleix Pol
aea9984187 DevicesModel: Do not connect until the value is initialised
When the DevicesModel is open, we get a nullptr instance. I guess it's
obvious that it's null because it hasn't been set yet. Move the connect
to the setter.
2025-06-13 15:46:30 +00:00
Freya Lupen
7a6c234b40 macOS: Set app bundle properties
This fixes the app's name being blank in the system.
2025-06-13 08:25:45 +00:00
Freya Lupen
4cdaa513d3 Use Breeze style on macOS, as on other platforms 2025-06-13 08:25:45 +00:00
l10n daemon script
aaf655ea5b GIT_SILENT Sync po/docbooks with svn 2025-06-13 01:39:47 +00:00
Tobias Fella
207c824ec7 Use QML safe member for invitations
Has the sideeffect of making sure that the avatar colors are the same between the invitation view and the sidebar
2025-06-12 12:49:01 +00:00
Tobias Fella
29abe0bacb Use ImageComponent for replies
This automatically gives us the image hiding safety feature

BUG: 503442
2025-06-12 09:04:52 +00:00
Tobias Fella
d14eda2ca0 Hide avatar of inviting user when images are hidden by default
While this reuses the setting for a slightly different purpose, in practice, these safety feature really belong together and it makes sense to have them both under a single option.
In the future, we might want to rephrase the options description
2025-06-11 22:42:49 +02:00
l10n daemon script
0425cf41d0 GIT_SILENT Sync po/docbooks with svn 2025-06-11 01:45:08 +00:00
l10n daemon script
4ff014f288 GIT_SILENT Sync po/docbooks with svn 2025-06-09 01:42:48 +00:00
l10n daemon script
2e24500866 GIT_SILENT Sync po/docbooks with svn 2025-06-08 01:41:02 +00:00
Joshua Goins
f13a256150 Refactor receiveRichPlainUrl text handler test to be data-based
I was originally going to add another test case to this, turns out I
didn't need to. Might as well ship this anyway, as it will make it
trivial to add more in the future.
2025-06-07 07:30:28 -04:00
l10n daemon script
c958e8cba9 GIT_SILENT Sync po/docbooks with svn 2025-06-06 01:38:09 +00:00
Joshua Goins
7668da68d3 Allow accepting reason dialogs with Ctrl+Enter
The original suggestion was the Enter key, but this won't work well as
reasons can take multiple lines. I also made sure the reason control was
focused by default, and that the "Cancel" button has an icon.

BUG: 500990
2025-06-05 12:03:37 +00:00
Heiko Becker
c478a0c0fc GIT_SILENT Update Appstream for new release
(cherry picked from commit 11ebe5316f)
2025-06-02 23:46:47 +02:00
l10n daemon script
fe60a08817 GIT_SILENT Sync po/docbooks with svn 2025-06-02 01:39:15 +00:00
James Graham
dec5369a8f Actually position the view at the end
It turns out that for whatever reason ListView.posiitionViewAtEnd() ignores any whitespace. This means that when we use the function it goes to the last delegate. This is no good as we have some padding at the bottom to make space for the typing indicator.

So the fix for this is stupid and involves adding a "spacer" delegate to the timeline beginning model which is completely invisible but qml see as a delegate so we can both leave the space and properly position the view at the end.

BUG: 501075
2025-06-01 10:37:30 +01:00
Joshua Goins
5ec0b9393e Fix showPassiveNotification call in Account Editor
I hate this thing, I think the standalone account editor was fine, but
it didn't work when I went through account settings. Now that should be
fixed.
2025-06-01 04:51:18 +02:00
l10n daemon script
3d8f724bb1 GIT_SILENT Sync po/docbooks with svn 2025-06-01 01:44:08 +00:00
Joshua Goins
bc7a663f92 Prevent opening the non-existent "Home" space in your web browser
I carved out an exception for "DM", but we need it for "Home" too.

BUG: 504842
2025-05-30 14:43:10 +02:00
l10n daemon script
90369e927f GIT_SILENT Sync po/docbooks with svn 2025-05-30 01:42:16 +00:00
Joshua Goins
034cad79c7 Fix the code block handling for our custom Markdown syntax
There were some cases that was hit that revealed some mistakes in the
code block checking code, which is now fixed. Basically, we just needed
to make sure the indices were updated at the right times. I also took
some time to clean up some of the comments, and magic numbers used here.

A new test case was added that was failing before in real-world testing.
2025-05-29 07:39:13 -04:00
Joshua Goins
5c8ba7e29e Fix space switching on mobile
We forgot to connect to onTapped on the touchscreen-specific TapHandler.
I also needed to carve out a specific case for DMs so it doesn't try to
resolve the non-existent "DM" resource.
2025-05-29 06:04:11 -04:00
l10n daemon script
bf1dc00338 GIT_SILENT Sync po/docbooks with svn 2025-05-29 01:43:28 +00:00
l10n daemon script
54f7fd08cb GIT_SILENT Sync po/docbooks with svn 2025-05-28 01:41:22 +00:00
l10n daemon script
e7afa07c84 GIT_SILENT Sync po/docbooks with svn 2025-05-27 01:39:20 +00:00
l10n daemon script
e196ef03d6 GIT_SILENT Sync po/docbooks with svn 2025-05-26 01:40:44 +00:00
l10n daemon script
0fa452ca04 GIT_SILENT Sync po/docbooks with svn 2025-05-25 01:53:51 +00:00
Joshua Goins
15b625a3f8 Move certain internal room information to an Advanced settings page
This has (as I've seen) confuse some users because we put this
information front and center. The internal room ID and it's version
isn't relevant for 99% of users, especially since most of them don't
even have permission to touch these.

Instead, let's do what Element smartly does and put it under an Advanced
page you have to intentionally find. This also has the knock-on effect
of propping up the importance of room aliases, which now appear higher
in the general settings.
2025-05-24 17:18:06 -04:00
Joshua Goins
e2427c0683 Improve the general room information form card
We can remove a lot of duplicative labeling here: "Room Information",
"Room Name", "RoomTopic", etc. The "Save" button now looks like a proper
form card element, like it does in the account editor.
2025-05-24 17:16:52 -04:00
Joshua Goins
a0e8039d92 Give NeoChatRoom the hidden filter for last event cache
Otherwise we keep state events and other unwanted events in the cache
(if the user turned these off.)
2025-05-24 17:02:32 -04:00
Joshua Goins
2f87b1f398 Overhaul how NeoChat restores rooms on space switch
I'm pretty unhappy with the current state, almost no other chat
application (including other Matrix clients) work like this. The biggest
annoyance is opening *other* spaces will send you to the Space Home -
which is almost never what you want! Even though our Space Home is
awesome :-)

Now when you switch spaces, the last room in that space is stored and
will be restored. This also allows us simplify some of the
"open the last room" code. I made sure that this works for the two
special spaces "Home" and "Friends" so everyone can take advantage of
this better UX.

Since this is constantly-changing-state and the worst that can happen is
the wrong room opening on launch, I didn't bother migrating the existing
setting. We'll have to kill it eventually anyway when we make it
account-specific!
2025-05-24 16:54:18 -04:00
Joshua Goins
86fd2e8e1e Improve the UX for rooms that don't have a canonical alias
This was discovered none other than Bug Catcher Nate, while in the TWiKS
room. That room doesn't have a canonical alias set (yet) and that
exposed some UX problems around aliases in NeoChat.

First, the non-canonical alias isn't shown in the info drawer despite
being the only alias available. This is something that Element actually
does, and now NeoChat does too.

Second, NeoChat will try to copy the room's internal Matrix ID (which is
not that great) to the clipboard because it looks for the canonical
alias. Surprisingly, Element also does this but now NeoChat doesn't.
2025-05-24 09:55:52 -04:00
Joshua Goins
4167f55ad8 Remove debug check for GlobalMenu
I accidentally included this in the commit, and it passed through
review, oops. Amends 76919a13b8.
2025-05-24 09:44:50 -04:00
Joshua Goins
76919a13b8 Fix GlobalMenu initialization on other platforms
Amends b5fcad3db0, we need a
ShareAction-like stub for GlobalMenu for this to work again.
2025-05-24 10:52:20 +02:00
l10n daemon script
fe0f159490 GIT_SILENT Sync po/docbooks with svn 2025-05-24 01:40:16 +00:00
Tobias Fella
4527a6399e Add QHttpServer-based mock server
This should allow for creating tests more easily than the python-based server, since we can poke at it from C++ code.
The idea is that each test creates the things it needs (rooms in a certain state, etc) programmatically instead of through
the json files we use for the other tests. This allows us to adapt the test data to each test as needed, without having to copy it around a lot.
2025-05-23 09:48:39 +02:00
l10n daemon script
b8ff75c5ce GIT_SILENT Sync po/docbooks with svn 2025-05-23 01:42:24 +00:00
l10n daemon script
049e8af8b2 GIT_SILENT Sync po/docbooks with svn 2025-05-22 01:41:30 +00:00
Joshua Goins
8ce2386cc9 Restore the text in location component 2025-05-21 16:30:52 -04:00
Joshua Goins
5239040d9c Set the spacing for the hover buttons on code component 2025-05-21 16:26:58 -04:00
Joshua Goins
c5eb7578b7 Improve the buttons for the location component
Now they are icon-only, the "Open Externally" icon has changed, and a
 fullscreen button has been added.
2025-05-21 16:26:58 -04:00
Joshua Goins
76ce44230d Get rid of the latitude/longitude text in the LocationComponent
This also fixes this component not loading.
2025-05-21 16:26:58 -04:00
Joshua Goins
4a20371b87 Make sure the MessageContentModel starts at UserRole
If it doesn't, then our roles start filling up the other Qt ones such as
AccessibleDescription. Instead we need to start UserRole (except for
DisplayRole of course.)
2025-05-21 16:26:58 -04:00
Joshua Goins
9a76c30aaf Remove the unused msgTypeToString function
Not sure why it's here, I just came across it and it's unused.
2025-05-21 16:26:58 -04:00
Joshua Goins
9cba57368e Don't refer to everything as an image in the file context menu
Instead, let's change the text based on the message component type.
2025-05-21 16:26:58 -04:00
Joshua Goins
d895f8d771 Fix right-clicking delegates again
I accidentally broke this while trying to dance around the two new
overlapping TapHandlers, but this didn't work. What ended up happening
is that you couldn't right-click non-message delegates anymore (such as
images, files, etc.)

I *still* couldn't figure out how to get overlapping TapHandlers to
work, so I just switched to a MouseArea instead. This makes everything
work as expected now.
2025-05-21 16:26:58 -04:00
Joshua Goins
de8c9f4878 Add padding around file progress indicator, move it to the center
Before it stuck to the top-left corner, and looked bad. Now it's
properly centered inside of the chat bar.
2025-05-21 16:24:34 -04:00
Joshua Goins
f029cf842a Add duration to voice message delegate
Otherwise you have to download the audio file before you can tell how
large it is.
2025-05-21 16:23:17 -04:00
James Graham
6ce9b77b3c Set m_quickActionIncubating and m_compactBackgroundIncubating to avoid heap-use-after-free 2025-05-21 10:08:55 +01:00
l10n daemon script
5a60c6ec67 GIT_SILENT Sync po/docbooks with svn 2025-05-21 01:41:56 +00:00
James Graham
a1ca768711 Use the forget function to leave a room everywhere
Use the forget function to leave a room everywhere this is both for consistency and to reduce dependencies. This way no dependency on RoomManager is required to leave a room and since in all cases they have an object they can just call the function.
2025-05-20 17:35:34 +01:00
l10n daemon script
f0d2c19393 GIT_SILENT Sync po/docbooks with svn 2025-05-20 01:39:59 +00:00
Carl Schwan
67db05a0c3 Adapt API to ConvergentContextMenu change
Use close instead of using internal menuItem
2025-05-19 18:59:45 +02:00
Ritchie Frodomar
05e932b884 Move typing indicators closer to messages
This merge request addresses a minor papercut in NeoChat's layout when using a screen magnifier.

If you are zoomed in on a conversation, typing indicators are generally invisible. This merge request moves them to align with actual messages, making them more visible to screen magnifier users like myself. That way, you can do the text chat equivalent of waiting for someone else to finish talking before you ramble on.

### Before
![Screenshot of NeoChat in a test conversation. A user is typing. The typing pane is very far off to the left, not visible when zoomed in.](https://cdn.acidiclight.dev/images/2025/04/03/31d836b18875.png)

### After
![Screenshot of the typing indicator in a conversation. The typing pane now aligns with the chatbar and messages.](https://cdn.acidiclight.dev/images/2025/04/03/870ace40ce86.png)
2025-05-19 12:07:02 -04:00
l10n daemon script
763713cd32 GIT_SILENT Sync po/docbooks with svn 2025-05-19 01:42:11 +00:00
Joshua Goins
2687448212 Add header to UserMenu for mobile which has the user info
To give a little bit more context, and it also matches the message
context menu which does the same.
2025-05-18 20:00:21 -04:00
Joshua Goins
d059195e92 Add user menu that is opened via right-clicking/long-tapped on avatar
We can un-clutter our message context menu, which we had to share
with user actions. (Even though we only had one so far.) I added one new
user-specific action which allows you to quickly mention the user in
chat. Otherwise you would've had to copy their username or use the
completion menu.

It's convergent on mobile, it still has the hover indicator and it also
is available through the AuthorComponent.

BUG: 486252
2025-05-18 20:00:20 -04:00
James Graham
6ef7acc8e5 Remove any dependencies on App from the spaces module.
Remove any dependencies on App from the spaces module. This requires moving some dialogs either to spaces, or libneochat if they're used more generically.
2025-05-18 14:38:57 +01:00
l10n daemon script
2cb89807ef GIT_SILENT Sync po/docbooks with svn 2025-05-18 01:40:43 +00:00
Joshua Goins
b5fcad3db0 Fix multiple Global menu issues
See individual commits. When backporting, I'll create a separate patchset just for the we-broke-something-bugs without the strings.
2025-05-17 21:21:38 -04:00
Joshua Goins
d3fd441c88 Improve the "Create a Room/Space" and "Select Existing Room" dialog
First of all, these are now all separate dialogs instead of shoving all
of these functions (which are only marginally related) into one single
dialog. We also convert these to in-app Kirigami Dialogs, which look a
bit nicer. I also touched up the UX in some places, such as adding
descriptions which were previously available to translators. I also hid
some not oft used options like setting a topic, which almost nobody does
before creating a room/space.
2025-05-17 21:09:43 -04:00
Joshua Goins
e9568b50fc If the message body is empty, say so
Normally if a malformed event is empty it will just be empty space - but
that looks buggy. Instead, we can add a message saying "This event does
not have any content."

BUG: 494093
2025-05-17 21:09:25 -04:00
James Graham
e7040a518a Create a new module for the room info drawer QML.
Create a new module for the room info drawer QML. This also requires moving some QML to LibNeoChat common with other modules. Finally all QML in roominfo is modifed to not depend on app.
2025-05-17 14:27:38 +01:00
l10n daemon script
495f7194ac GIT_SILENT Sync po/docbooks with svn 2025-05-17 01:40:09 +00:00
Ritchie Frodomar
d31cc486bb Work around startup UI freeze caused by QtTextToSpeech
This merge request works around an annoying startup hang that I introduced when adding text-to-speech to NeoChat. The previous implementation was a QML singleton that used the `TextToSpeech` QML component. Unfortunately that component blocks the UI thread when first loading it, while it connects to speech-dispatcher.

This MR just rewrites that singleton in C++, and moves initialization of QtTextToSpeech to the first time you read a message aloud. It doesn't fix the performance problem, but it at least stops it from affecting startup.

In the future, I'd like to move speech operations to a background thread to completely mitigate the initialization freeze.
2025-05-16 14:27:49 -04:00
l10n daemon script
3ea844496a GIT_SILENT Sync po/docbooks with svn 2025-05-16 01:41:58 +00:00
Nate Graham
7b953d0cc0 Fixup becc127dc4
This isn't a JavaScript list, so a comma between items is actually an
error here.
2025-05-15 15:05:42 -06:00
Joshua Goins
becc127dc4 Add share action to the maximize delegate
Self-explanatory, you know how this works! Enables sharing images out of
NeoChat more easily other Purpose-enabled applications.

BUG: 466382
2025-05-15 15:34:29 -04:00
James Graham
e2742cbf8b Add a button to access all emojis from the message menu
Add a button to access all emojis from the message menu for when that button is not availble in the quick actions

requires libraries/kirigami-addons!362
2025-05-15 13:41:09 -04:00
Joshua Goins
a1513b30cd Check if RoomEvent is null before trying to insert a notification
As seen in the bug report, roomEvent could be null and then we get a
nice and simple null-access error. We should be careful before trying to
use it.

BUG: 502687
2025-05-15 13:39:33 -04:00
Joshua Goins
e2c0316e7d Focus the chat bar when replying to a message
Just a nice UX thing, otherwise you have to manually focus the chat bar
again with a keyboard or mouse.
2025-05-15 13:39:24 -04:00
Joshua Goins
fda84a6aac Allow performing shortcuts while the completion menu is open
This prevents doing actions like zooming in your screen (Meta+Equals)
because it will tripper shortcutOverride and close the menu. Instead, we
shouldn't block shortcuts but still allow closing when Escape is
pressed.

This *does* block all NeoChat shortcuts when the completion menu is
open, but personally I think this is fine. If you have this open, you
really should finish your message first!
2025-05-15 13:39:16 -04:00
Joshua Goins
142312d87d Give completion popup a minimum size when editing an existing message
This was previously fixed to the size of the text area itself, which
could be small. We also have a bug where the text area *itself* is like
one character big, so this helps there too.

The popup is also given a margin of 0 so it is always positioned within
the bounds of the window.

BUG: 503483
2025-05-15 13:39:08 -04:00
Joshua Goins
906fb97259 Fix crash when trying to edit your message on a wide screen
This only seems to happen when the window is maximized, specifically
the height of the window. This seems to be a weird TextArea issue (or
something else in our TimelineView) but until that's found out, a
Qt.callLater will prevent this annoying crash.

BUG: 503846
2025-05-15 13:38:55 -04:00
Joshua Goins
80923a2025 Fix undefined reference to webShortcutModel
This was incorrectly capitalized, so I just went ahead and properly
capitalized it so we stop making this mistake!
2025-05-15 13:38:46 -04:00
l10n daemon script
12882586d3 GIT_SILENT Sync po/docbooks with svn 2025-05-15 01:42:42 +00:00
Joshua Goins
d51e68e9e2 Fix the room list header margin in Kirigami >6.14
They changed how margins behave when there's no navigation buttons in 7b02df40381b28de97fab1dd8d97182778c242df
so we are actually adding more padding ontop of the existing one.
2025-05-14 17:41:45 -04:00
l10n daemon script
7f6e489f20 GIT_SILENT Sync po/docbooks with svn 2025-05-14 01:41:32 +00:00
l10n daemon script
161111bf5c GIT_SILENT Sync po/docbooks with svn 2025-05-13 01:43:24 +00:00
James Graham
5452c51c6a Elide the user ID in UserDetailDialog if it's too long
Elide the user ID in UserDetailDialog if it's too long

BUG: 501483
2025-05-12 19:00:29 +01:00
l10n daemon script
09917d45e3 GIT_SILENT Sync po/docbooks with svn 2025-05-12 01:37:54 +00:00
James Graham
101a8b9ec3 Yeet HoverActions into the sun
Replace HoverActions with an inline action component that appears on hover. There are only actions for reply and react if there is space the overflow button opens the normal message menu.

NOTE: the most recent update changes things slightly, from the images below the buttons are now top aligned because of potentially hige messages. The actions are also now disabled for compact mode as they never really made sense there anyway. The menu now has all options so no one is missing out.

For normal messages

![image](/uploads/b8679eb09c9190404fc84f01e14169af/image.png){width=419 height=138}

When space is limited

![image](/uploads/ecd7c725ea2526689e586a2d786f389e/image.png){width=411 height=130}

User messages

![image](/uploads/767ef09f6650a5fb6abf3a49ef9f9b90/image.png){width=296 height=114}

BUG: 503784
2025-05-11 11:11:52 +01:00
l10n daemon script
a04769baad GIT_SILENT Sync po/docbooks with svn 2025-05-11 01:40:13 +00:00
l10n daemon script
07441df3f6 GIT_SILENT Sync po/docbooks with svn 2025-05-10 01:40:07 +00:00
Joshua Goins
b5465cd8d0 Fix one more ownership issue that causes crashes when switching rooms
I was hitting one more annoying crash with a backtrace inside QCache,
because we didn't set the ownership propetly for one last model. Now I
don't hit it anymore.
2025-05-09 15:09:54 -04:00
James Graham
0a2af02c5f Restore hover actions for the cpp message delegate
Restore hover actions for the cpp message delegate

BUG: 503843
2025-05-09 13:03:23 -04:00
Tobias Fella
3183be460e Implement OIDC registration/login compat 2025-05-09 18:20:21 +02:00
Tobias Fella
beb72dfa48 Actually fix TextHandlerTest 2025-05-09 14:44:44 +02:00
Tobias Fella
60d493e34b Re-enable texthandlertest on CI 2025-05-09 14:44:44 +02:00
Joshua Goins
36a5e8bdb5 Package new "Send a Location" button icon for Android
I forgot to add this in 5d4529ccf9.
2025-05-09 08:44:21 +02:00
l10n daemon script
489e60996a GIT_SILENT Sync po/docbooks with svn 2025-05-09 01:36:18 +00:00
Joshua Goins
3d15e4f061 Use FormCard on the invitation page
It now looks a bit nicer, and I made sure everything scales down to
mobile as well.
2025-05-08 15:48:55 -04:00
Joshua Goins
28c39292f7 Show the time when we were invited on the invite page
Currently, this is something that's missing even in clients like Element
(!?!) I will sometimes miss when invites are sent - like when I'm
sleeping - and then when I start back up NeoChat I have no idea when
they tried to invite me. This can get worse on bigger timescales, like
when you go on vacation for a few days.
2025-05-08 15:48:55 -04:00
Joshua Goins
d7c9bea783 Add spoiler formatting to the quick format bar 2025-05-08 15:39:34 -04:00
Joshua Goins
e232501715 Use the new strikethrough syntax in the quick format bar 2025-05-08 15:39:34 -04:00
Joshua Goins
4757ac11dc Allow striking through text with ~~
Similar to spoilers, some Markdown flavors like GitHub's or Discord's
allow you to strike through text with ~~. Normally, the only way to do
this on most Matrix clients (including Element) is surrounding text with
<del> tags. As expected, no one knows how to do this.

So now NeoChat supports the ~~ syntax. We can reuse the existing spoiler
parser for this after making it generic. I have added new test cases for
this syntax. This does not affect the quick format bar yet.
2025-05-08 15:26:00 -04:00
Joshua Goins
14cbbd394f Differinate between regular text and revealed spoiler text
When you reveal a spoiler, it removes the background from the spoiler
block - effectively making it seem like regular text. There's also a
minor issue that we use the color scheme's textColor as the "spoiler
block" color, and it will usually end up white. Most chat programs have
typically used a dark background for spoiler blocks, even on dark
themes.

I killed two UX issues with one stone, first by making the spoiler block
color fixed (but mixed with the textColor, to ensure it's readable
regardless of Breeze Light/Dark.) And second by only changing the text's
visibility, not the background.
2025-05-08 15:25:37 -04:00
Joshua Goins
5d4529ccf9 Replace the "Send a Location" button icon with a more fitting one
"globe" is super generic, "mark-location" is way more fitting - not only
in name but also in appearance.
2025-05-08 14:42:10 -04:00
Joshua Goins
baa214df0e Don't insert spoilers inside of code blocks
Also add some new test cases to catch these weird situations.
2025-05-08 12:40:10 -04:00
Joshua Goins
6b5996a1bd Add custom syntax for tagging spoilers
Currently the only two ways to spoiler text in your message is either:
* Using the /spoiler command
* Manually typing the data-mx-spoiler span HTML blocks

Neither one is discoverable, or friendly to users really. Instead, we
should extend our existing Markdown-based formatting syntax with one
that can handle spoiler tags. I chose the || syntax to match Discord,
since Element doesn't seem to adopt one.

Unfortunately, CMark does not support custom extensions (see
https://github.com/commonmark/cmark/pull/123) so we have to implement
our own parsing function. New tests are also added for this too.
2025-05-08 12:40:10 -04:00
Joshua Goins
86a43c4f7e Register TextHandler::Type with Q_ENUM
This makes Qt spit out more useful debug messages when printing this
enum.
2025-05-08 12:40:10 -04:00
Joshua Goins
107f3b6616 Make MessageDelegateBase::cleanupIncubator static
This doesn't need to be attached to an instance of MessageDelegateBase,
and avoid various problems when called from a MessageObjectIncubator
callback. (Because it needs access to the "this" pointer.)

Fixes a regularly occurring crash when switching rooms.
2025-05-06 19:33:09 -04:00
Joshua Goins
bcab617b9d Move m_sectionIncubating/m_avatarIncubating/m_readMarkerIncubating set
This should be set *before* we clean up the incubator, because it could
end up accessing invalid data when the incubator is freed. This fixes a
regularly occuring crash when switching rooms.
2025-05-06 19:31:41 -04:00
l10n daemon script
f6c12dc27a GIT_SILENT Sync po/docbooks with svn 2025-05-06 01:37:59 +00:00
Joshua Goins
3e954a24a1 Fix the KDE_CI env var check
This should make it possible to run this test on regular machines again.
2025-05-05 20:39:18 +00:00
Joshua Goins
08dc05c707 Refactor lastEventIsSpoiler, remove lastEvent() call that isn't needed
This is only called after we already get an event with lastEvent() so
doing it again is useless. Instead, we should refactor it to behave like
similar functions (e.g. isEventHighlighted.)
2025-05-05 20:32:22 +00:00
Tobias Fella
8817ca7f2b Return member objects for users that are not actually members
Most of the data in the objects will be empty, but at least a valid matrix id is provided.
This fixes missing data in some places in the UI (e.g., in HiddenDelegate)
2025-05-05 22:19:52 +02:00
Tobias Fella
5262ec4b56 Show event type for hidden events 2025-05-05 20:18:58 +02:00
James Graham
f4799a4287 Rework MessageDelegate in cpp
Rework MessageDelegate to be mostly a cpp class. This allows us to only load the components that are actually needed saving memory.

In testing using memtest it saved ~30% versus the current implementation.
2025-05-05 16:25:40 +01:00
James Graham
97d5be9d81 Make sure that ChatDocumentHandler can handle the document being changed to a nullptr
Make sure that ChatDocumentHandler can handle the document being changed to a nullptr.

BUG: 501950
2025-05-05 15:45:50 +01:00
James Graham
6913a4b447 Force the room list items to update their height when the compactRoomList setting is changed.
Force the room list items to update their height when the compactRoomList setting is changed.

The solution is a bit janky but for whatever reason the height wasn't updating properly which seems to be a qml bug, it sometime happened slow, sometime you would have to turn NeoChat off then on again.

BUG: 494146
2025-05-05 15:02:03 +01:00
James Graham
41e96fca70 Remember the matrixId during login when moving back from password
Remember the matrixId during login when moving back from password

BUG: 489976
2025-05-05 13:48:23 +01:00
James Graham
f6b3f3ac80 Fix the width of the space header.
Fix the width of the space header, the name of currentWidth has changed to available width.
2025-05-05 13:08:32 +01:00
James Graham
db0c423763 Use isOnline from libquotient
Use `isOnline` from libquotient

This require a bump of the min libquotient to 0.9.1
2025-05-05 12:20:26 +01:00
Aleix Pol
df9a7292b9 Fixes a null pointer call
It seems like the case is possible as we already are treating the case
in isUserBanned. Doesn't seem ideal as it shows "" where the username
should be but it's better than a crash.
2025-05-03 16:17:35 +00:00
Heiko Becker
7abca73339 GIT_SILENT Update Appstream for new release
(cherry picked from commit 0a7fccaa5c)
2025-05-03 00:31:40 +02:00
Tobias Fella
30ee914baf Re-enable FreeBSD CI 2025-05-02 20:43:39 +02:00
l10n daemon script
5b835933e6 GIT_SILENT Sync po/docbooks with svn 2025-05-01 01:38:00 +00:00
Kai Uwe Broulik
85f3d3b633 RoomList: Change room on drag enter
With a delay. Allows to drag a file to NeoChat and switch rooms.

In the future we might want to allow dropping a file onto a
room to send it.
2025-04-30 10:57:48 +00:00
l10n daemon script
41d6233f78 GIT_SILENT Sync po/docbooks with svn 2025-04-28 01:37:57 +00:00
Joshua Goins
ccb162cfed Set CppOwnership for managed QObjects returned by Q_INVOKABLES
I'm 99% sure of the recent crashes we've been seeing are double-frees,
the QCache one me and Duha encountered must be one. The QCache is in
charge of the one in ContentProvider, so it will sometimes try to delete
or access something already destroyed by the QML engine.

While I'm at it, I also made sure to check every other Q_INVOKABLE to
ensure we don't hit this elsewhere.

CCBUG: 502747
2025-04-26 11:36:39 -04:00
l10n daemon script
56472f6cfd GIT_SILENT Sync po/docbooks with svn 2025-04-25 01:38:48 +00:00
l10n daemon script
14c26b1663 GIT_SILENT Sync po/docbooks with svn 2025-04-24 01:36:42 +00:00
l10n daemon script
14043f861f GIT_SILENT Sync po/docbooks with svn 2025-04-23 01:38:35 +00:00
l10n daemon script
7761e4e0f7 GIT_SILENT Sync po/docbooks with svn 2025-04-22 01:37:28 +00:00
l10n daemon script
4214b7691f GIT_SILENT Sync po/docbooks with svn 2025-04-21 01:36:58 +00:00
Akseli Lahtinen
62da98af94 ChatBar: set chatBarSizeHelper for compactLayout to root.width
In chatBarSizeHelper, if user is in compact mode and resizes the window,
maxWidth would be -1 and that caused something to go wrong with the
padding calculations.

Instead, just set it to root.width with some padding, so it always is
the width of the ChatBar.
2025-04-20 18:46:32 +03:00
Akseli Lahtinen
0cc14f710d MessageModel: Handle stickers in ContentModelRole
We were ignoring stickers completely when handling them previously, as
it was handled as event and not one with id.

BUG: 501553
2025-04-20 15:49:20 +03:00
l10n daemon script
a148c6e326 GIT_SILENT Sync po/docbooks with svn 2025-04-20 01:36:09 +00:00
Tobias Fella
bb864cac5b Fix crash during logout 2025-04-19 10:26:41 +02:00
l10n daemon script
e465ef6d41 GIT_SILENT Sync po/docbooks with svn 2025-04-19 01:37:38 +00:00
Tobias Fella
1303dcce8f Disable appium tests
Their flakiness makes the CI miserable to use.
2025-04-18 23:19:04 +02:00
James Graham
b6791485c4 Move remaining code to app module
There's still some stuff that could potentially go elsewhere but I think it's enough for now.
2025-04-18 09:26:17 +00:00
l10n daemon script
0708f022bc GIT_SILENT Sync po/docbooks with svn 2025-04-18 01:36:47 +00:00
James Graham
d81478ac97 Move webshortcut model to timeline 2025-04-17 16:53:36 +01:00
James Graham
f57004601d Move more stuff to settings module 2025-04-17 16:41:59 +01:00
Joshua Goins
c104c0b7d0 Remove some action text that explicitly says "to clipboard"
For example, "Copy Address to Clipboard" is redundant - almost every
Copy action on your computer does it to the clipboard. The same is done
for the space action too.
2025-04-17 09:28:52 +00:00
Joshua Goins
50ceddaa7e Improve the "Friends" space notification and tooltip text
If you're a first-time user, the different states on this button may be
a bit confusing. Instead of showing a "plus" sign (meaning an invite) it
will add it to the total notification count. The tooltip text is also
changed to reflect this.
2025-04-17 09:25:17 +00:00
Tobias Fella
23803223bf Remove duplicate mxid regex 2025-04-17 10:15:00 +02:00
Tobias Fella
438d4ca3fd Make mxid regex compatible with localhost mxids
Required for tests
2025-04-17 10:14:59 +02:00
Tobias Fella
d668d2f2de Improve DevtoolsPage
Notably, port away from StackLayout, as it doesn't work well when the individual components have different heights
2025-04-17 10:09:03 +02:00
l10n daemon script
0f704417c6 GIT_SILENT Sync po/docbooks with svn 2025-04-17 01:36:01 +00:00
Tobias Fella
9b8cfafa04 Show a placeholder when permissions are loading 2025-04-16 21:27:00 +02:00
James Graham
195e175186 More qml files to go in timeline module 2025-04-16 19:48:19 +01:00
James Graham
2d5d2c6c06 Move relevant models to devtools 2025-04-16 19:38:13 +01:00
James Graham
e787eaabcd Create a space module 2025-04-16 19:28:54 +01:00
James Graham
4aec891b1f Fix clearing edit ID when an attachment is added
Fix clearing edit ID when an attachment is added. You can reply with an attachment but not edit and attachment simultaneously
2025-04-16 18:28:33 +00:00
l10n daemon script
86f04f8b98 GIT_SILENT Sync po/docbooks with svn 2025-04-16 01:36:50 +00:00
Carl Schwan
cee9c90f55 Use list of breeze icons from Kirigami Addons 2025-04-15 22:38:12 +02:00
Simon Quigley
2ca744c960 Remove TapHandler for CodeMaximizeComponent
This specific TapHandler proves to be a bit trigger-happy; when trying to copy
text, select or de-select text, or generally do more than stare at it without
touching your mouse, it is too easy to minimize the fullscreen component. This
fixes the issue entirely; when tapping on the message in the timeline, it
still shows a maximized message. This only applies to the maximized message.
2025-04-15 20:36:52 +00:00
Tobias Fella
854aa34e52 Don't offer stickers for reactions 2025-04-15 10:53:53 +02:00
l10n daemon script
b8d4ffcedc GIT_SILENT Sync po/docbooks with svn 2025-04-15 01:37:17 +00:00
James Graham
0380de698c Create new rooms module 2025-04-14 19:17:42 +01:00
Tobias Fella
380a52d981 Update copyright year 2025-04-14 17:06:15 +02:00
l10n daemon script
ddc3c11e12 GIT_SILENT Sync po/docbooks with svn 2025-04-14 01:36:37 +00:00
Volker Krause
05e1c2e6f8 Use ECM's more elaborate APK version code logic 2025-04-13 15:49:23 +00:00
James Graham
bffd7fb13d Move ChatDocumentHandler and related includes to LibNeoChat 2025-04-13 16:24:12 +01:00
James Graham
4fe9c76d90 Move location models to LibNeoChat 2025-04-13 15:52:01 +01:00
James Graham
866fee2ea3 Move login and registration to login
To make this work `AccountManager` is split off from controller taking all the code around `AccountRegister` and is added to LibNeoChat as it makes sense to have this functionality there.
2025-04-13 11:23:17 +00:00
James Graham
b9ffe12154 Move locationhelper, linemodel and mediasizehelper to Timeline 2025-04-13 10:11:54 +00:00
James Graham
09cb2bd261 Move the threepid stuff to settings 2025-04-13 10:46:54 +01:00
James Graham
3a4bc18d45 Move more stuff to settings 2025-04-13 10:27:22 +01:00
l10n daemon script
32ee590cef GIT_SILENT Sync po/docbooks with svn 2025-04-13 01:35:50 +00:00
Joshua Goins
5ef4ab0756 Fix two (edited) strings appearing in edited quotes
This is because we only check if the last message component != Text,
because that handles it's own edit strings. Quote components do that
too, so if we don't exclude it there ends up being two (edited) strings
in one message.
2025-04-12 16:59:49 -04:00
l10n daemon script
1e26e740fb GIT_SILENT Sync po/docbooks with svn 2025-04-11 01:35:44 +00:00
Joshua Goins
704505958e Fix multi-line quotes being cut off
This was a mistake in the code that was designed to remove the outside
paragraphs, which seems to be to make way for the quotation marks we
add around the text. Instead of doing that (which turns out, is very
brittle and breaks on multiple paragraphs) insert the quotation marks
*inside* of the paragraph tags.

A test case is added for this as well.
2025-04-10 20:53:00 +00:00
James Graham
b230641600 Move ColorSchemer to settings module 2025-04-10 19:29:30 +01:00
James Graham
3f457774dc Move all timeline relevant models and classes to the module 2025-04-10 18:25:45 +00:00
l10n daemon script
6ff32d0935 GIT_SILENT Sync po/docbooks with svn 2025-04-10 01:36:28 +00:00
l10n daemon script
928b48fd14 GIT_SILENT Sync po/docbooks with svn 2025-04-09 01:36:26 +00:00
l10n daemon script
c040031771 GIT_SILENT Sync po/docbooks with svn 2025-04-08 01:40:09 +00:00
l10n daemon script
324a8ee2e1 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-04-08 01:30:10 +00:00
l10n daemon script
a866c9b924 GIT_SILENT made messages (after extraction) 2025-04-08 00:41:45 +00:00
Heiko Becker
9877962dd0 GIT_SILENT Update Appstream for new release
(cherry picked from commit 4ad1a628b6)
2025-04-07 23:59:31 +02:00
James Graham
aef4f75c33 Move NeoChatConnection and NeoChatRoom to LibNeoChat
Move `NeoChatConnection` and `NeoChatRoom` to `LibNeoChat` along with any required dependencies.
2025-04-07 18:52:15 +00:00
James Graham
8327b4369e Rework so that ActionsModel no longer depends on NeoChatConfig or RoomManager 2025-04-07 14:07:01 +00:00
James Graham
0403ecbd70 Remove the dependency on NeoChatConfig from EventHandler 2025-04-07 12:27:53 +00:00
l10n daemon script
16daca09e8 GIT_SILENT Sync po/docbooks with svn 2025-04-07 01:42:59 +00:00
James Graham
9f60a8957b Migrate forgotten instance of NeoChatRoom::poll() to ContentProvider::handlerForPoll() 2025-04-06 10:47:15 +01:00
l10n daemon script
9a169bbf3d GIT_SILENT Sync po/docbooks with svn 2025-04-06 01:38:05 +00:00
James Graham
8af0ae6263 Move the update mentions function to ChatDocumentHandler
Move the update mentions function to `ChatDocumentHandler`, this means `ChatbarCache` no longer needs to depend on `ChatDocumentHandler`
2025-04-05 20:01:07 +00:00
Scarlett Moore
420baaaf4a snapcraft: Add qml and qt plugins path to env.
Fixes missing qml.
2025-04-05 11:49:34 +00:00
James Graham
c56054a05a Move pollhandler to ContentProvider
Move `PollHandler` to `ContentProvider`, this also breaks the dependency on `PollHandler` for `NeoChatRoom`
2025-04-05 10:08:44 +00:00
James Graham
d9b8b1c8ef Rework NeoChatConnection to remove the dependency on NeoChatConfig
Title
2025-04-05 07:53:26 +00:00
l10n daemon script
73623ec41a GIT_SILENT Sync po/docbooks with svn 2025-04-05 01:38:51 +00:00
James Graham
02e11e8008 Remove the relationEventContentModel function from ChatBarCache
Remove the relationEventContentModel function from ChatBarCache as ContentProvider makes it unneeded. This means turning ContentProvider into a QML singleton.
2025-04-04 12:13:44 +00:00
James Graham
22b3641a07 Cleanup NeoChatRoom includes 2025-04-04 10:36:13 +01:00
James Graham
913b4ea923 Make lastEvent() take a filter function
Make `lastEvent()` take a filter function so we can remove any dependence on `NeochatConfig` from `NeoChatRoom`.
2025-04-04 09:27:04 +00:00
James Graham
88eb2223c5 Create a content provider instance to get message content models from
This means that all content models will now come from the same source to remove duplication across multiple models and `chatbarcaches`.

It also handily breaks the dependency on needing `MessageContentModel` for `NeochatRoom`
2025-04-04 08:42:34 +00:00
l10n daemon script
123d11945e GIT_SILENT Sync po/docbooks with svn 2025-04-04 01:39:54 +00:00
Ritchie Frodomar
7a078b2d34 Add "Read Text Aloud" context menu action for messages
This merge request adds a simple "Read Text Aloud" menu action to the context menu for chat messages. When clicked, the message text will be read aloud using text-to-speech.

The intention behind this change is to make it easier for myself and other low-vision users to use Matrix, without needing to use a system-wide screen reader, in cases where the user still has enough sight left to navigate a computer faster without a screen reader.

I'd eventually like to have it read " said ," so that the message sender gets read aloud as well, but for now it just reads the plaintext message contents.

Another problem, at least on my computer specifically, is that the voice's accent doesn't seem correct. For whatever reason, on my system, messages are read in a Scottish accent which is harder for me to understand. Other apps don't do that, so I'm not sure what's going on there. I do not want to hardcode a specific voice/locale, since I want this feature to work well for everyone and not just me.

@teams/qa Please do break my code! :)  - I've only tested with basic text messages.

@teams/usability Not sure if I put the context menu action in an ideal place, it's grouped in the same area as clipboard actions like "Copy Text."

@teams/localization How could I go about getting author names to be read aloud in a way that's properly translated for other languages? I'm not experienced with i18n.
2025-04-03 19:51:40 +00:00
James Graham
054f87cae2 delegatesizehelper updates
Make `DelegateSizeHelper` take a `QQuickItem` rather than a width value and move `TimelineDelegate` to use it rather than duplicting the code.

This requires the creation of `LibNeoChat` so that both the main and timeline modules can get access to the class.

Note: ideally more stuff will go into `LibNeoChat` but it turns out our dependencies are kinda spaghetti like and so will take some untangling.
2025-04-03 15:24:31 +00:00
l10n daemon script
f026e4e5ed GIT_SILENT Sync po/docbooks with svn 2025-04-03 01:36:43 +00:00
Tobias Fella
b9bd12e451 Fix crash when editing messages 2025-04-02 21:20:30 +02:00
Scarlett Moore
0f75369658 bump olm version, fix build. Add kunifiedpush part. 2025-04-02 06:27:30 -07:00
l10n daemon script
075f2c96b2 GIT_SILENT Sync po/docbooks with svn 2025-04-02 01:37:06 +00:00
l10n daemon script
d3b9aa9808 GIT_SILENT Sync po/docbooks with svn 2025-04-01 01:36:48 +00:00
l10n daemon script
16d42aa6e3 GIT_SILENT Sync po/docbooks with svn 2025-03-31 01:36:40 +00:00
l10n daemon script
e90f038f6a GIT_SILENT Sync po/docbooks with svn 2025-03-30 01:36:16 +00:00
l10n daemon script
bd0c76559b GIT_SILENT Sync po/docbooks with svn 2025-03-29 01:37:19 +00:00
James Graham
42fab806c6 Enable ending polls 2025-03-28 13:41:46 +00:00
James Graham
fadb5725e0 Rework the appearance of poll delegate
![image](/uploads/510e995e15d76ce0566b126a6917a963/image.png){width=541 height=269}
2025-03-28 09:05:39 +00:00
l10n daemon script
8180f111d0 GIT_SILENT Sync po/docbooks with svn 2025-03-28 01:39:03 +00:00
Allen Winter
5c30785d8d CMakeLists.txt - Don't include quiet packages in the feature summary
Only the pkg_config part in Findcmark is quiet, which also shouldn't
put out as a status message.

Fixes:
```
Missing these optional packages:
 * Qt6QmlCompilerPlusPrivateTools (required version >= 6.8.2)
```
2025-03-27 10:01:58 -04:00
l10n daemon script
581a1e7153 GIT_SILENT Sync po/docbooks with svn 2025-03-27 01:39:08 +00:00
l10n daemon script
0b9a8be45d GIT_SILENT Sync po/docbooks with svn 2025-03-26 01:37:58 +00:00
l10n daemon script
40883d9461 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-03-26 01:30:34 +00:00
l10n daemon script
91010c52db GIT_SILENT made messages (after extraction) 2025-03-26 00:42:08 +00:00
Joshua Goins
2546d79f26 LinkPreviewComponent: Fix a few bugs, restore the image preview
We realized that images don't display in link previews anymore, because
QML is terrible and this property is a QUrl, so when we call .length it
silently fails and never loads the image. This is easily fixed by
calling .toString().

There's also another bug where the title ie elided way too greedily, but
we can simplify the elision check and fix the bug at the same time.
No more "Hom..."!
2025-03-25 11:43:55 +00:00
Joshua Goins
c3404936fd Set the pointing hand cursor shape on link previews
Our HIG explicitly says we can do this for links, and these components
are literally giant links. This might also help differentiate these from
similar looking components like quotes for desktop users.
2025-03-25 11:43:00 +00:00
Joshua Goins
ec635d7de3 Elide the hover link indicator
For long links, this ends up going "off" the window (not really of
course.) One good example is Bugzilla filter URLs which are ungodly
huge. So eliding these just makes it look better, browsers do this too.
2025-03-25 11:42:29 +00:00
l10n daemon script
d416270cad GIT_SILENT Sync po/docbooks with svn 2025-03-25 01:37:08 +00:00
Tobias Fella
3fa6dadb43 Fix logout button 2025-03-24 22:52:31 +01:00
l10n daemon script
cee391746f GIT_SILENT Sync po/docbooks with svn 2025-03-24 01:36:50 +00:00
l10n daemon script
8432fa82a8 GIT_SILENT Sync po/docbooks with svn 2025-03-23 01:37:36 +00:00
James Graham
72edfe1112 Make it possible to send images in threads
Title
2025-03-22 18:56:40 +00:00
Tobias Fella
3b2a6800a0 Minor cleanup 2025-03-22 18:55:19 +01:00
Tobias Fella
6b7ad6b1af Remove unneeded property 2025-03-22 18:51:39 +01:00
James Graham
37d77f579a Poll Updates and Send Polls
Fix showing polls and update the events and PollHandler to make them easier to work with.

Add a PollAnswerModel to visualise poll answers.

Enable sending polls.
2025-03-22 16:32:08 +00:00
Tobias Fella
55d68af499 Fix verification 2025-03-22 16:44:29 +01:00
James Graham
1ca4120fd9 Reply to messages with images
Reply to messages with images (and technically any other attachment file type)
2025-03-22 11:16:50 +00:00
l10n daemon script
9164660d19 GIT_SILENT Sync po/docbooks with svn 2025-03-21 01:39:29 +00:00
l10n daemon script
ba78cf050d GIT_SILENT Sync po/docbooks with svn 2025-03-20 01:40:29 +00:00
l10n daemon script
e8d1142a94 GIT_SILENT Sync po/docbooks with svn 2025-03-18 01:40:03 +00:00
l10n daemon script
f40eaf55a3 GIT_SILENT Sync po/docbooks with svn 2025-03-17 01:35:39 +00:00
l10n daemon script
0b85f62858 GIT_SILENT Sync po/docbooks with svn 2025-03-16 01:38:35 +00:00
James Graham
24235174fd Make it so that the menus for individual thread messages can be accessed
This is mostly just for text type messages at the moment but give the framework so that when other message types can be sent in threads they can be added easily
2025-03-15 16:03:27 +00:00
l10n daemon script
4a800f99e1 GIT_SILENT Sync po/docbooks with svn 2025-03-15 01:38:20 +00:00
Nicolas Fella
96e61c8357 Make KUnifiedPush required unless opted out
Quiet dependencies like this are bad since they make it easy to miss the dependency
2025-03-13 21:24:13 +00:00
l10n daemon script
f153e57fdb GIT_SILENT Sync po/docbooks with svn 2025-03-12 01:39:11 +00:00
l10n daemon script
bac93e778e GIT_SILENT Sync po/docbooks with svn 2025-03-11 01:37:29 +00:00
James Graham
2aeed10429 Message attached property
Create Message attached property to propagate parameters like room, timeline, index and maxContentWidth down to the message content avoiding lots of boilerplate
2025-03-10 18:28:42 +00:00
l10n daemon script
ea6ad902a7 GIT_SILENT Sync po/docbooks with svn 2025-03-10 01:41:40 +00:00
James Graham
f9c53ee3b0 ThreePIDModel Updates
There is no need for NeochatConnection to depend on ThreePIdModel and also this means it's not in memory when not needed.

Also a little cleanup to make sure only a single job can run at a time.
2025-03-09 12:31:14 +00:00
l10n daemon script
3f0843647c GIT_SILENT Sync po/docbooks with svn 2025-03-09 01:36:22 +00:00
Albert Astals Cid
a50df870e7 GIT_SILENT Upgrade release service version to 25.07.70. 2025-03-08 19:26:23 +01:00
James Graham
716ae11941 Fix Viewing Encrypted Events
Turns out I forgot that an encrypted event is not a roommessage event so we need to handle this when grabbing the content model for the message.
2025-03-08 14:42:48 +00:00
l10n daemon script
4ff16ff402 GIT_SILENT Sync po/docbooks with svn 2025-03-08 01:43:38 +00:00
l10n daemon script
bd598b9c44 GIT_SILENT Sync po/docbooks with svn 2025-03-07 01:39:06 +00:00
Joshua Goins
f1253e4ede Make joining remote rooms more reliable
When joining remote rooms we have to specify another homeserver (that is in the
room) to help us join. The matrix documentation is a little unclear what to do
in this scenario, so instead of giving up let's at least brute force it with the
server in the alias or room id.

This *does* work and allows my server to join rooms in NeoChat I otherwise
couldn't.

BUG: 487253
CCBUG: 491359
FIXED-IN: 24.12.3
2025-03-06 15:39:32 +00:00
Joshua Goins
79a3da3358 Stop emojis from destroying your message
This is easy to reproduce in the following scenario with a bunch of
half-completed emojis: ":a :a :a :a". Trying to complete anything but the last
one starts replacing parts of the message because it only considers the last
colon to the current completion identifier.

This change fixes that and said scenario can no longer cause a message
massacare. This bug doesn't seem to affect the other completions because their
searching in the string was correct, but I made sure they all share the same
index now.

BUG: 479587
FIXED-IN: 24.12.3
2025-03-06 14:47:27 +00:00
l10n daemon script
5a6bdfbbba GIT_SILENT Sync po/docbooks with svn 2025-03-06 01:39:29 +00:00
Joshua Goins
3d663be506 Move the "Explore rooms" button from the hamburger to the space drawer
I think we should put this feature in a more obvious place (and similar to other
chat applications.) Instead of it being buried underneath a menu, joining
spaces/rooms should have the space promenience as creating them - you're going
to be more likely to access this dialog more anyway.
2025-03-05 22:03:54 +00:00
Joshua Goins
0ada4cdebe Add a dialog explaining what to do next when tapping "Verify this device"
The menu that has this action is now more discoverable, and this menu item is
plagued with a bad UX - when you tap on it nothing obvious happens! To people
not familiar with device verification they will think this is a bug, but in fact
all they need to do is open another verified session on another device.

So now there's a dialog explaining that the next step is to do just that. This
dialog also closes once the verification session starts, but the user has the
option to close it in the meantime.
2025-03-05 22:03:38 +00:00
Balló György
d103de96aa Don't create tray icon if system tray is not supported
This fixes the problem that the tray icon is created in GNOME if it was
enabled in other desktop environment previously.
2025-03-05 20:35:18 +00:00
l10n daemon script
f248b04834 GIT_SILENT Sync po/docbooks with svn 2025-03-05 01:54:04 +00:00
l10n daemon script
9572f20682 GIT_SILENT Sync po/docbooks with svn 2025-03-04 01:37:41 +00:00
Justin Zobel
409cec08fc CI - Flatpak Updates 2025-03-03 02:18:43 +00:00
l10n daemon script
51f330eae9 GIT_SILENT Sync po/docbooks with svn 2025-03-03 01:37:33 +00:00
l10n daemon script
51750267e5 GIT_SILENT Sync po/docbooks with svn 2025-03-02 01:35:42 +00:00
l10n daemon script
99948d5151 GIT_SILENT Sync po/docbooks with svn 2025-03-01 01:48:27 +00:00
l10n daemon script
030726e6fb GIT_SILENT Sync po/docbooks with svn 2025-02-28 01:36:36 +00:00
Heiko Becker
1fad54272f GIT_SILENT Update Appstream for new release
(cherry picked from commit a85af258fe)
2025-02-27 21:59:20 +01:00
James Graham
4af4bfd55f Improve the handling of switching link preivews on and off.
First make sure that the global setting is tied into the room setting, previously it was a bit of a patchwork that worked more by luck than judgement. The two levels of global and room level are properly tied together in a hierarchy.

Add a message in the room when global notifcations re turned off. This has caused confusion in the past when people don't realise there are 2 levels.
2025-02-27 16:37:33 +00:00
l10n daemon script
77cedef5bb GIT_SILENT Sync po/docbooks with svn 2025-02-27 01:35:42 +00:00
Joshua Goins
db36f187dc Don't show the "Settings" button when adding a new account
As much as I like opening Settings while I'm in Settings, this
doesn't make much sense.
2025-02-26 23:59:05 +00:00
Joshua Goins
2861eb9c60 Add new message action to pin and unpin messages in rooms
Self-explanatory, now you can manage pinned messages in NeoChat alone.
2025-02-26 23:53:38 +00:00
l10n daemon script
9811c0d97a GIT_SILENT Sync po/docbooks with svn 2025-02-26 01:37:50 +00:00
l10n daemon script
e9c21373ed GIT_SILENT Sync po/docbooks with svn 2025-02-25 01:35:40 +00:00
l10n daemon script
bda23ec54a GIT_SILENT Sync po/docbooks with svn 2025-02-24 10:28:45 +00:00
l10n daemon script
e23641375b GIT_SILENT Sync po/docbooks with svn 2025-02-24 01:35:59 +00:00
l10n daemon script
024d54345a GIT_SILENT Sync po/docbooks with svn 2025-02-23 01:34:15 +00:00
James Graham
59fd4d3916 Make sure the thread dev setting is actually obeyed 2025-02-22 19:48:56 +00:00
Joshua Goins
88d684b6c1 Don't allow long-pressing on non-touchscreen devices
It isn't the right kind of interaction on a computer with a mouse or
trackpad, it should be relegated to touchscreen only. This should
hopefully cover everything from room list delegates to messages.
2025-02-22 18:50:39 +00:00
Joshua Goins
94fdf777cb Early exit if we're checking mutual rooms with yourself
This is rejected by servers too, so don't even bother doing as it
doesn't make sense.
2025-02-22 13:05:30 -05:00
Joshua Goins
dea70152e4 Improve discoverability of the account menu
I figure that not many users know there's a secret and super useful
account menu. Right-clicking or long-pressing opens this menu.
Additionally, tapping your avatar brings up the "Accounts" settings for
some reason. Worse, there's no indication of any of this functionality
or why we're hiding two separate functions here.

Instead, let's make it a ToolButton but keep the general appearance the
same. That makes it act and feel more like a regular button, and
pressing on it will open the account menu. The shortcut to the accounts
settings is removed, there's plenty of other ways to get there.
2025-02-22 17:31:00 +00:00
Joshua Goins
614caf5ca0 Add ellipses to "Remove" message action
This doesn't apply instantly, it opens a dialog to confirm with you and
optionally add a message. According to the HIG (and just a good idea in
general) it should be marked with ellipses.
2025-02-22 17:30:25 +00:00
Joshua Goins
25dbae37fb Change "Copy Message Link" icon to "link-symbolic"
Instead of sharing the same icon as the "Copy Text" action, this make it
even clearer.
2025-02-22 17:30:12 +00:00
Joshua Goins
e060032e6a Improve the notification setting description
The current text has invited a lot of confusion around how notifications
work in NeoChat, because it mentions "push notifications". Some users
take it to mean that somehow the notifications appear in the background,
but that's only supported if built with KUnifiedPush.

To make it super clear, let's change the description dynamically based
on whether:
1. NeoChat is built with KUnifiedPush support.
2. We were able to connect with the KUnifiedPush daemon and your server
has a push gateway.
2025-02-22 10:33:52 -05:00
l10n daemon script
4725410c0f GIT_SILENT Sync po/docbooks with svn 2025-02-22 01:35:48 +00:00
Scarlett Moore
20488ee400 snapcraft: Move to core24 2025-02-21 04:25:07 -07:00
l10n daemon script
b1c0619af5 GIT_SILENT Sync po/docbooks with svn 2025-02-21 01:35:30 +00:00
l10n daemon script
ade730179a GIT_SILENT Sync po/docbooks with svn 2025-02-20 01:36:40 +00:00
Joshua Goins
9264ad26d6 Make the Notifications window non-modal
Tapping on a notification here doesn't close the window. Additionally,
you need to tap it several times for NeoChat to scroll up in a room.

Considering all of this, it would make more sense for this window to be
non-modal for now so you can have the Notifications window open while
using the main NeoChat window.
2025-02-19 13:21:16 +00:00
Joshua Goins
9020e2c7cb Remove word puzzle in the new invitation page 2025-02-18 20:52:13 -05:00
Joshua Goins
0f51c34b24 Add dedicated invitation subtitle text to rooms
Instead of displaying the message event - or usually nothing at all -
show a label like "user has invited you".
2025-02-19 01:45:02 +00:00
Joshua Goins
f6a427e865 Add user information to the invitation page
Currently the invite page kinda sucks. If someone invites you to a room,
you have no idea who from the UI - which is a safety issue.

Now the invite page shows you who invited you, and it has a slightly
different layout & text for one-on-one chats and room invites.

Also the buttons on this page are improved with fixed capitalization
and icons!
2025-02-19 01:45:02 +00:00
l10n daemon script
9b95930463 GIT_SILENT Sync po/docbooks with svn 2025-02-19 01:37:15 +00:00
l10n daemon script
cb96b4991e GIT_SILENT Sync po/docbooks with svn 2025-02-17 01:34:35 +00:00
l10n daemon script
cde7a51cde GIT_SILENT Sync po/docbooks with svn 2025-02-16 01:38:19 +00:00
l10n daemon script
046d611f56 GIT_SILENT Sync po/docbooks with svn 2025-02-15 01:48:20 +00:00
Albert Astals Cid
d7b3748159 CI: Add linux-qt6-next build 2025-02-13 08:14:42 +00:00
l10n daemon script
188c9fc726 GIT_SILENT Sync po/docbooks with svn 2025-02-13 01:43:18 +00:00
l10n daemon script
dbc735e63b 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-02-13 01:33:08 +00:00
James Graham
8750486f7b Move to upstream JoinRuleEvent 2025-02-12 18:04:59 +00:00
l10n daemon script
6dc4baeeb5 GIT_SILENT Sync po/docbooks with svn 2025-02-12 01:34:39 +00:00
l10n daemon script
ff28828a2e GIT_SILENT Sync po/docbooks with svn 2025-02-11 01:38:11 +00:00
l10n daemon script
e28452dfd1 GIT_SILENT Sync po/docbooks with svn 2025-02-10 01:33:12 +00:00
James Graham
5d7cb5c28f Move the reaction delegate into the bubble
Move the reaction delegate into the bubble so it can be instantiated by the Content model. This aims to make sure we only instantiate it when needed rather than for every event. You can now hover the event to show the ReactionComponent with a button to add a reaction if none are currently present

Added bonus ReactionModel no longer needs an event pointer, the event ID is enough to get reaction from the room so things are less likely to blow up.
2025-02-09 19:07:53 +00:00
James Graham
08b29f7081 Make sure that a blank entry is never added to the message model store
title
2025-02-09 18:18:15 +00:00
l10n daemon script
c9e034b5b3 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-02-09 01:27:43 +00:00
l10n daemon script
d9f0ff466f GIT_SILENT Sync po/docbooks with svn 2025-02-08 01:33:49 +00:00
Joshua Goins
6b4b895102 Fixup higher power levels not being displayed correctly
This is is a fix or 4d1c82a623, I was
coercing integer values to PowerLevel (the enum.)
2025-02-05 20:36:04 +00:00
l10n daemon script
0c7e02e7c9 GIT_SILENT Sync po/docbooks with svn 2025-02-05 01:36:34 +00:00
Joshua Goins
4d1c82a623 Add power level to the user details dialog
The only way to check a user's power level is to haul yourself over to
the member list, which is cumbersome but also hard to parse - especially
if a room has lots of members.

This adds the user's power level to the existing details dialog. For
example, this makes it easier to identify someone as a moderator if they
sent a message in the room.
2025-02-04 20:14:58 +00:00
Joshua Goins
8d33fe6221 StateKeys: Fix opening a specific state key member 2025-02-04 20:13:09 +00:00
l10n daemon script
9d27651411 GIT_SILENT Sync po/docbooks with svn 2025-02-04 01:34:04 +00:00
Joshua Goins
268975bc3b Fix crash when trying to view Security settings in an invited room
This state event doesn't exist (or is inaccessible) to us, and tries to
access nullptr.
2025-02-04 01:02:25 +00:00
James Graham
66343ba11e Fix new MessageModel
Make sure that we initialise the MessageContentModel for nwe and historical events after they have been added to the timeline
2025-02-03 17:16:40 +00:00
l10n daemon script
684cd85a7a GIT_SILENT Sync po/docbooks with svn 2025-02-02 01:38:47 +00:00
Heiko Becker
ef9a80e76f GIT_SILENT Update Appstream for new release
(cherry picked from commit 96b03082e3)
2025-01-31 01:44:04 +01:00
l10n daemon script
fbb5f02379 GIT_SILENT Sync po/docbooks with svn 2025-01-29 01:35:45 +00:00
James Graham
5f4bde96e9 Max Width Threads
Since threads are a conversation where both the local user and others take part always make them span the full available width
2025-01-28 18:13:56 +00:00
l10n daemon script
f8c8a68840 GIT_SILENT Sync po/docbooks with svn 2025-01-28 01:36:42 +00:00
Tobias Fella
bf6f4a951e Mark MessageModel as uncreatable 2025-01-27 16:07:30 +01:00
l10n daemon script
58c9366548 GIT_SILENT Sync po/docbooks with svn 2025-01-27 01:37:35 +00:00
l10n daemon script
f410ecac2b GIT_SILENT Sync po/docbooks with svn 2025-01-26 01:35:04 +00:00
Max Buchholz
1d1a43ade2 Appdata: add display size 2025-01-25 17:57:21 +00:00
James Graham
37adb56233 Thread fetch more button
Changes threads so there is a button to fetch more events. Also adds a separator between the thread root and the rest of the events.
2025-01-25 16:50:29 +00:00
Justin Zobel
aca0669bf6 CI: Add JSON, XML and YML linting 2025-01-25 09:33:05 -05:00
Justin Zobel
b33ab76ff8 YAML formatting 2025-01-25 09:33:04 -05:00
Gary Wang
38a391b7fa CI: add frameworks/kiconthemes to .kde-ci.yaml 2025-01-25 14:14:19 +00:00
Gary Wang
82434fe87c fix: no icon under Windows
See also:

- https://invent.kde.org/frameworks/kiconthemes/-/issues/3
- https://planet.kde.org/christoph-cullmann-2024-05-11-kde-applications-icons/
2025-01-25 14:14:19 +00:00
Tobias Fella
8bf7c36249 Improve verification method choosing 2025-01-25 11:56:10 +01:00
l10n daemon script
cff3557a24 GIT_SILENT Sync po/docbooks with svn 2025-01-25 01:36:39 +00:00
Carl Schwan
2c476c4351 Fix double separator in RoomDrawer 2025-01-24 12:36:50 +00:00
l10n daemon script
82c8ab511d GIT_SILENT Sync po/docbooks with svn 2025-01-24 01:36:28 +00:00
l10n daemon script
486ed6edd2 GIT_SILENT Sync po/docbooks with svn 2025-01-23 01:33:32 +00:00
l10n daemon script
a4b0a9ed36 GIT_SILENT Sync po/docbooks with svn 2025-01-22 01:34:28 +00:00
l10n daemon script
bba9c37ba5 GIT_SILENT Sync po/docbooks with svn 2025-01-21 01:35:12 +00:00
Joshua Goins
febc7d1630 Add UI to set a custom display name for specific rooms
This is the same functionality that /myroomnick does, but it's now
exposed in a much more accessible place in the UI. A new page to the
room settings is added to configure your profile in the room. It's
currently limited to a display name.
2025-01-19 21:33:07 -05:00
l10n daemon script
1ed071949b GIT_SILENT Sync po/docbooks with svn 2025-01-20 01:39:21 +00:00
l10n daemon script
3878c264ef 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-01-20 01:28:38 +00:00
Joshua Goins
21daf0b664 Fix "Configure Web Shortcuts" not doing anything, again 2025-01-19 19:52:37 +00:00
l10n daemon script
5efaa72cea GIT_SILENT Sync po/docbooks with svn 2025-01-19 01:35:00 +00:00
Mark Penner
191cd7cbba add missing icons on Android 2025-01-18 13:00:31 -06:00
Joshua Goins
c583e31b16 Add how many rooms you have in common to the user detail dialog
Eventually this will be expanded into an actual list you can look
through, but this can at least give you an idea the
number of rooms this user shares with you. If the user doesn't share any
rooms with you (e.g. they left) then the label is hidden.
2025-01-18 17:40:26 +00:00
Joshua Goins
590fba7deb Always open the user details dialog in the focused window
This fixes some odd UX where you tap on someone's user in a search or
pinned messages window, but it opens in the NeoChat main window instead.

Fixes #681
2025-01-18 14:41:34 +00:00
Joshua Goins
a8f22003cb Adapt to new libQuotient API changes
postPlainText is gone
2025-01-18 13:28:39 +00:00
l10n daemon script
54596e3fe6 GIT_SILENT Sync po/docbooks with svn 2025-01-18 12:28:26 +00:00
l10n daemon script
2ee4d110a0 GIT_SILENT Sync po/docbooks with svn 2025-01-18 01:41:35 +00:00
James Graham
7a949dccbb Refactor threads
The focus here is to make threads use the standard message content system rather than having a special implementation.

To achieve this the threadroot content model will now get a thread body component which will visualise the thread model with all the other messages. The latest message in the thread will then just ask for the thread root content model and show that.

Note: in order to stop a cyclical dependency with MessageComponentChooser and new base version has been added which is just missing ThreadBodyComponent and and the main version is now inherited from that with ThreadBodyComponent added.
2025-01-17 19:18:44 +00:00
Tobias Fella
111a45ab38 Use ellipsis character instead of ... 2025-01-17 16:50:56 +01:00
l10n daemon script
2bcf59c225 GIT_SILENT Sync po/docbooks with svn 2025-01-17 01:34:40 +00:00
Joshua Goins
d542033125 Don't spam pending invites every time NeoChat is started
Currently the way we show invite notifications is sub-optimal. We did it
during the initial room state load, which meant it shows an invite
notification *every time* you opened NeoChat. This gets annoying very
quickly if you have any pending invitations you don't want to take
action on just yet.

Instead, let's handle this in NotificationsManager directly, and also
remove some scaffolding now that it isn't plumbed through
NeoChatRoom/NeoChatConnection.
2025-01-16 20:35:42 +00:00
Tobias Fella
44c72828e1 Fixes for password changing 2025-01-16 18:21:35 +01:00
l10n daemon script
99d3ee32fa GIT_SILENT Sync po/docbooks with svn 2025-01-16 01:35:29 +00:00
l10n daemon script
7df0ff309e GIT_SILENT Sync po/docbooks with svn 2025-01-15 01:35:33 +00:00
James Graham
856a751fcb Fix Getting Member Objects
It seems that there are no guarantees that we will have a room member event available when a message has arrived especially early on after room load so we should create member object unconditionally and make it the responsibility of the caller to only ask for real senders.

BUG: 498649
2025-01-14 18:54:34 +00:00
l10n daemon script
da99bcae5d GIT_SILENT Sync po/docbooks with svn 2025-01-14 01:36:26 +00:00
Joshua Goins
1b0c6c2847 Add button to view pinned messages in a room
BUG: 497427
2025-01-13 20:48:35 +00:00
Joshua Goins
c315e817b2 Add an option to disable encryption in new chats
Right now NeoChat (or more technically, libQuotient) decides to use
encryption by default in new chats. Some users may not prefer or need
this, so a new option is added under Security to change this behavior.

BUG: 498375
2025-01-13 20:34:00 +00:00
l10n daemon script
6fde07a20d GIT_SILENT Sync po/docbooks with svn 2025-01-13 01:38:22 +00:00
James Graham
8a86159fd7 Fix getting content models for old events in a search model
Fix getting content models for old events in a search model by allowing for calling using the event. This gets past the intial checks and the content model itself can load the event from the server.

Requires network/neochat!2110 to fix the showauthor issue
2025-01-12 15:14:48 +00:00
Joshua Goins
1d532a1fc1 Move showAuthor role to MessageModel, so it's available for all models
This fixes features like the search model, where no message delegates
could be created because it's missing showAuthor.
2025-01-12 13:48:31 +00:00
Tobias Fella
a67ce75924 Improve ssss result reporting 2025-01-12 14:33:42 +01:00
l10n daemon script
b3d845ea32 GIT_SILENT Sync po/docbooks with svn 2025-01-12 01:40:23 +00:00
Carl Schwan
008e12cb42 Port AccountMenu to ConvergentContextMenu 2025-01-11 17:07:17 +00:00
Carl Schwan
ceaed8be51 Simplify ExploreComponent
Remove mobile mode support as this is handled by ExploreComponentMobile
2025-01-11 17:07:17 +00:00
Carl Schwan
f0e0979366 Port DelegateContextMenu to ConvergentContextMenu 2025-01-11 17:07:17 +00:00
Carl Schwan
c43563a804 Port space menu to ConvergentContextMenu 2025-01-11 17:07:17 +00:00
Carl Schwan
8ec3b2d05d Port to ConvergentContextMenu 2025-01-11 17:07:17 +00:00
Tobias Fella
39a95c727f Remove unused functions for getting crypto keys 2025-01-11 17:47:30 +01:00
James Graham
a2f5a585e3 Use the libQuotient function to get the user power level as it is now equivalent 2025-01-11 16:38:20 +00:00
Tobias Fella
aa95bc62bd Use 6.8 flatpak runtime 2025-01-11 15:47:45 +01:00
James Graham
ae7bfa5bcb Move the storage of thread models to the room 2025-01-11 13:22:51 +00:00
James Graham
37de1ec583 Move the storage of MessageContentModels to the room
Move the storage of MessageContentModels to the room in the same manner as memeber objects to prevent duplication but mainly to make the system easier to maintain going forward with things like threads for example. This requires the creation of a MessageContentFilterModel as the same model may be used in multiple places, sometimes with the author showning sometimes not.
2025-01-11 13:16:14 +00:00
l10n daemon script
bb8f0eae1b GIT_SILENT Sync po/docbooks with svn 2025-01-11 01:33:52 +00:00
l10n daemon script
571d3c14c8 GIT_SILENT Sync po/docbooks with svn 2025-01-10 10:24:58 +00:00
Joshua Goins
d7202ae0a7 Implement request for user data erasure
This adds UI for MSC4025 to the account deactivation dialog, if the
server supports it. We also switch away from our
customDeactivateAccountJob to libQuotient's.

Fixes #670.
2025-01-09 16:37:09 -05:00
Carl Schwan
2a9c75e24f Improve handling of DonwloadAction
Set the progress the download action only when the currentProgressInfo
changed, otherwise we sometimes end up in a data race.
2025-01-09 16:41:21 +00:00
Carl Schwan
7231662f94 Fix right clicking on NeoChatMaximizedComponent
The api of RoomManager.viewEventMenu changed and now require also
passing the author.
2025-01-09 16:41:21 +00:00
Carl Schwan
df83927ed7 Expose ProgressInfoRole also for other type of attachments 2025-01-09 16:41:21 +00:00
Carl Schwan
f14dfc5de8 Set explicitely parent in MaximizeComponent
Currently it uses applicationWindow().overlay which works but is
not ideal for multiple reasons:

- This as a tendency to breaks unexpectedly
- It can't be optimized by the qml compiler

So we are trying to move away from these construct everywhere.
2025-01-09 16:41:21 +00:00
l10n daemon script
188d0c9d5c GIT_SILENT Sync po/docbooks with svn 2025-01-09 01:36:14 +00:00
l10n daemon script
8d68c64fdf GIT_SILENT Sync po/docbooks with svn 2025-01-08 01:36:51 +00:00
Carl Schwan
d796ab350e Use Component.onCompleted
as we don't yet depends on KF 6.10
2025-01-07 16:33:31 +01:00
Carl Schwan
334a1b5bef Revert "Require Kirigami master"
This reverts commit 3304ee0985.
2025-01-07 16:32:31 +01:00
Joshua Goins
3304ee0985 Require Kirigami master
This is needed for emptyHelpfulAction, which is only in the currently unreleased KF 6.10.
This should fix the KDE Android Nightly.
2025-01-07 08:17:44 +00:00
l10n daemon script
d7b3523544 GIT_SILENT Sync po/docbooks with svn 2025-01-07 01:35:44 +00:00
l10n daemon script
1a8d346064 GIT_SILENT Sync po/docbooks with svn 2025-01-06 01:37:20 +00:00
James Graham
42f9b36667 Get rid of the m_memberObjects store in MessageModel
Get rid of the m_memberObjects store in MessageModel which was missed in the last mr. Also clean up the code for NeoChatMembers
2025-01-05 18:52:18 +00:00
James Graham
c21e9f2114 Store NeochatRoomMember objects in the room
Store NeochatRoomMember objects in the room so we don't duplicate them unnecessarily. This also adds a visible property for a room which is set true when shown by MessageEventModel and false when not, triggering the deletion of member objects. This mechanism will be used for other object types in the future.
2025-01-05 11:06:42 +00:00
l10n daemon script
3dbe605de8 GIT_SILENT Sync po/docbooks with svn 2025-01-05 01:35:48 +00:00
James Graham
41a6dd6175 Timeline Memory Test App
Create a simple app that puts a number of events into a simple timeline which can be used to verify future memory optimisations

When Neochat is built with tests on you should find the app at ~/kde/usr/bin/timeline-memtest assuming ~/kde is your kdesrc-build directory
2025-01-04 15:29:40 +00:00
l10n daemon script
09af6fe0a7 GIT_SILENT Sync po/docbooks with svn 2025-01-04 01:34:01 +00:00
Kai Uwe Broulik
3b8c3afa3e Support canceling file transfer from pseudo job
Allows to click cancel on the job popup rather than only from
within NeoChat.
2025-01-03 13:56:33 +01:00
Kai Uwe Broulik
7b7f4d264c Handle transfer job being canceled
Set KilledJobError to indicate it was canceled by the user
to avoid a bogus "finished" notification.

Sadly, fileTransferCanceled has been removed from libQuotient
so this lambda botch checking transfer status needs to be done.
2025-01-03 13:55:56 +01:00
Kai Uwe Broulik
c454a4942e MessageDelegateContextMenu: Fix "Edit" action not showing
There's no such thing as Emote or Message anymore, it's all "Text".
Matches IsEditableRole of MessageEventModel.

While at it, also clear threadId, which is what the Edit quick
button also does.
2025-01-03 10:21:36 +00:00
l10n daemon script
44cd52af6c GIT_SILENT Sync po/docbooks with svn 2025-01-03 01:34:59 +00:00
Heiko Becker
0d01339b02 GIT_SILENT Update Appstream for new release
(cherry picked from commit a5b37a78a0)
2025-01-02 15:06:37 +01:00
Carl Schwan
e5b4ca53f8 Use nightly kirigami addons in craft builds 2025-01-02 15:05:36 +01:00
l10n daemon script
703b03b33c GIT_SILENT Sync po/docbooks with svn 2025-01-02 01:33:44 +00:00
l10n daemon script
73cdad66ac GIT_SILENT Sync po/docbooks with svn 2025-01-01 01:34:36 +00:00
James Graham
4b1afdbe2d Fix Thread Chatbars
Since the new thread API was released in 0.9.2 update the if defs to include it. This will solve the double chat bar in thread for anyone on 0.9.2 or above
2024-12-31 16:13:27 +00:00
James Graham
64bfc0f29a Add more detail to the MessageModel documentation
Because when I was making a new one these are the things I forgot
2024-12-31 14:52:49 +00:00
Carl Schwan
28c4c0b48c Use symbolic icon for purpose plugin 2024-12-31 14:05:05 +00:00
l10n daemon script
40ccde9f06 GIT_SILENT Sync po/docbooks with svn 2024-12-31 01:36:00 +00:00
l10n daemon script
baa7d02ba5 GIT_SILENT Sync po/docbooks with svn 2024-12-30 01:41:01 +00:00
Kai Uwe Broulik
a391df1e67 QuickSwitcher: Suggest to explore rooms when there are no search results
My go to place for doing anything with rooms, is the search button.
However, I cannot join/find new rooms from there. Therefore this adds
an "Expore rooms" button as helpful action when there are no matching
rooms.
2024-12-29 18:56:50 +00:00
Kai Uwe Broulik
1f26485208 ChatBar: Remove explicit Keys.onDeletePressed handler
Key-specific handlers, such as Key.onDeletePressed implicitly accept
the event. This means that the entire logic for the delete key must
be reimplemented, and e.g. Ctrl+Delete to delete the previous *word*
was missed.

Since all it has to do is handle the typing notification and format bar,
just use the already existing Keys.onPressed handler (which does *not*
accept the event) and add a case for Delete alongside Backspace.
2024-12-29 11:02:36 +01:00
l10n daemon script
733b72bcbd GIT_SILENT Sync po/docbooks with svn 2024-12-29 01:41:23 +00:00
l10n daemon script
f0816260c5 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"
2024-12-29 01:30:36 +00:00
l10n daemon script
32e9e66e49 GIT_SILENT made messages (after extraction) 2024-12-29 00:41:59 +00:00
James Graham
6e4973cef7 Create a common MessageModel and inherit SearchModel and MessageEventModel from it.
Title
2024-12-28 13:32:26 +00:00
l10n daemon script
4f02472421 GIT_SILENT Sync po/docbooks with svn 2024-12-28 01:45:16 +00:00
l10n daemon script
00ce6be66e 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"
2024-12-28 01:31:38 +00:00
Kai Uwe Broulik
3eaef148fe timeline: Load avatars asynchronously
Speeds up scrolling through the timeline.
2024-12-27 12:16:09 +00:00
Kai Uwe Broulik
c92284ab02 RoomDelegate: Load avatar asynchronously
Speeds up scrolling through the list of rooms.
2024-12-27 12:16:09 +00:00
l10n daemon script
40718d1180 GIT_SILENT Sync po/docbooks with svn 2024-12-27 01:37:22 +00:00
Tobias Fella
ca325cb7bf Add Joshua as Maintainer 2024-12-26 13:52:44 +01:00
l10n daemon script
06e4fc0962 GIT_SILENT Sync po/docbooks with svn 2024-12-26 01:38:44 +00:00
Tobias Fella
8f82ce6ffb Use Quotient's Add3PIDJob 2024-12-25 22:02:10 +01:00
Tobias Fella
138f224109 Use Quotient's job for deleting devices 2024-12-25 21:51:40 +01:00
Tobias Fella
ef8c89b999 Use Quotient's job for password changing 2024-12-25 21:45:01 +01:00
James Graham
b595a2966c Thread alignment
It doesn't make sense for a thread to be on the right or tinted when the local user started it as they contain messages from multiple users. So always make them align left and never tint them.
2024-12-25 19:53:41 +00:00
Tobias Fella
3480c5f067 Remove unused forward declaration 2024-12-25 19:19:37 +01:00
Joshua Goins
a456b10420 Explicitly set the parent in QuickSwitcher
This sets the parent to the overlay, which makes sure it's actually
centered even when the right sidebar is opened.
2024-12-25 15:28:54 +00:00
Jan Rathmann
b7dee707a3 Port away from methods removed in libquotient. This fixes BUG: 497458 2024-12-25 01:58:55 +00:00
l10n daemon script
9f2f0f1375 GIT_SILENT Sync po/docbooks with svn 2024-12-25 01:49:27 +00:00
James Graham
fe7cf0a595 Add button to thread to reply
![image](/uploads/bf2b6857f797480b429db02c5d01a4f7/image.png){width=254 height=147}
2024-12-24 17:14:00 +00:00
James Graham
0f79c04d93 Fix new thread loading
If a thread was created by the local user fetchMore() won't find the pending event that created it so make sure it is found and added.
2024-12-24 16:20:49 +00:00
Tobias Fella
2d3373efbb Add test for invite action 2024-12-24 15:05:19 +01:00
l10n daemon script
f3a96b3562 GIT_SILENT Sync po/docbooks with svn 2024-12-24 01:33:01 +00:00
Tobias Fella
7f79fd95b5 Require passing tests on windows 2024-12-23 15:46:26 +01:00
Nicolas Fella
2eab366a1b Remove unneeded IDs from actions 2024-12-23 11:07:02 +00:00
Nicolas Fella
038a3bb5c8 Drop dead code for switchUserButton
It is never checked
2024-12-23 11:07:02 +00:00
Nicolas Fella
7bef8c99ec [UserInfo] Fix shortcut
Set the keysequence on the action directly

This makes it actually work
2024-12-23 11:07:02 +00:00
Nicolas Fella
5cbae6e8e6 Drop commented out code 2024-12-23 11:07:02 +00:00
l10n daemon script
862c022ec6 GIT_SILENT Sync po/docbooks with svn 2024-12-23 01:32:44 +00:00
Tobias Fella
71349b575b Refactor action definition
- Make an empty message type optional mean "no message"
- Set message type for all actions that send a message through the normal mechanism
- Remove now redundant bool specifying whether a message should be sent
2024-12-22 21:14:20 +01:00
Tobias Fella
2acc08402f Fix rainbowme 2024-12-22 20:35:23 +01:00
Tobias Fella
adf79e1926 Fixup disabling TextHandlerTest 2024-12-22 20:28:17 +01:00
Tobias Fella
d2e5c1d33b Disable TextHandlerTest on CI
It's broken for no apparent reason while working fine locally
2024-12-22 20:17:33 +01:00
Tobias Fella
3fce30f709 Add basic tests for actions 2024-12-22 19:51:07 +01:00
James Graham
6bdb67f504 Update string literals
Since _ls is now deprecated this is removed in favour of L1, I've also taken the oportunity to replace QStringLiteral and QLatin1String with their shortened form while we're at it.

There are also a few instances where the string literal type has been switch, the general rule being to use the one that matches the function type or value being compared to avoid conversions.
2024-12-22 18:23:55 +00:00
James Graham
314f86007e Fix New ThreadModel Messages
ThreadModel was not updated to use pendingEventAdded the same way MessageEventModel was so new messages in an exisiting thread were not being initialised properly. This fixes it to use the updated pendingEventAdded on new enough versions of libQuotient.
2024-12-22 10:11:56 +00:00
James Graham
476edc6ad3 Show thread on latest message
Use the new thread functionality in libQuotient to show the thread on the latest message rather than the root.

Note: to test you need to bump your libquotient minor version to 10 or higher.

Also Note: this reveals some other bugs in how new threads are shown or refreshed when new messages are added, this will be fixed in a later patch as some re-architecting is required
2024-12-22 10:11:37 +00:00
James Graham
a56cafb97e Remove makeMessageTimestamp from MessageEventModel as it is now unused 2024-12-22 10:11:16 +00:00
James Graham
6663b0c257 Custom Room Sort Order
Add the ability to sort rooms by a custom set of parameters.
2024-12-22 10:11:04 +00:00
l10n daemon script
c50d4289c4 GIT_SILENT Sync po/docbooks with svn 2024-12-22 01:33:09 +00:00
l10n daemon script
595080b5c1 GIT_SILENT Sync po/docbooks with svn 2024-12-21 01:45:26 +00:00
l10n daemon script
a9cbd766c8 GIT_SILENT made messages (after extraction) 2024-12-21 00:41:17 +00:00
l10n daemon script
fdfd49105f GIT_SILENT Sync po/docbooks with svn 2024-12-20 01:36:01 +00:00
l10n daemon script
c7afddcfab GIT_SILENT Sync po/docbooks with svn 2024-12-18 01:35:34 +00:00
l10n daemon script
9fe3d4faf0 GIT_SILENT made messages (after extraction) 2024-12-18 00:40:24 +00:00
l10n daemon script
41a827555c GIT_SILENT Sync po/docbooks with svn 2024-12-17 01:38:11 +00:00
l10n daemon script
c9317e4ec5 GIT_SILENT Sync po/docbooks with svn 2024-12-16 02:03:06 +00:00
l10n daemon script
0ae16ccf76 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"
2024-12-16 01:37:31 +00:00
l10n daemon script
73d3b638df GIT_SILENT made messages (after extraction) 2024-12-16 00:42:42 +00:00
Soumyadeep Ghosh
e2b0a105a7 snap: use cmake snap and use . as source
- override the `PATH`
- use gcc from toolchains PPA
2024-12-15 13:05:28 +00:00
l10n daemon script
195864d3ac GIT_SILENT Sync po/docbooks with svn 2024-12-15 02:03:11 +00:00
l10n daemon script
c4aa843038 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"
2024-12-15 01:35:34 +00:00
Joshua Goins
843da2664f Add better support for colored text (and shrugs) from other clients
Some clients - such as Element - can send colored text through <span>,
which fails to display in Qt's rich text parser. So we need to transform
that into CSS styles which is supported by Qt.

Notably this allows you to exchange rainbow shrugs through Matrix, which
is really important. And this means colored backgrounds for text is
supported too, I guess.
2024-12-14 13:46:56 +00:00
Joshua Goins
23eaa6a4c7 Fix web shortcuts not doing anything
This is because we're passing a QUrl into a QString, we need to turn it
into a string explicitly.

BUG: 496434
2024-12-14 09:26:24 +00:00
l10n daemon script
f29bb971e0 GIT_SILENT Sync po/docbooks with svn 2024-12-14 02:06:50 +00:00
l10n daemon script
266123a5e0 GIT_SILENT made messages (after extraction) 2024-12-14 00:49:37 +00:00
Volker Krause
9b0d01619c Update Android Gradle plugin version to 8.6.0
Necessary for building against Android SDK 35.
2024-12-13 17:06:27 +01:00
l10n daemon script
291162f5f0 GIT_SILENT Sync po/docbooks with svn 2024-12-13 01:31:25 +00:00
Thiago Sueto
29820e2ab2 Don't set emoji size to font size
https://invent.kde.org/network/neochat/-/merge_requests/2005 changed the custom emoji height (whose default is 32 on every Matrix client) to match font height (on my machine it becomes 17, my font is 12pt).

It makes emojis unreadable on non-HiDPI resolutions (1366x768, 1920x1080), and even in the MR itself you can see how much detail is lost. This is compounded by some other rendering bug where the emoji image becomes very jagged when downscaled.

That MR however was correct in that:
* we want custom emojis to have a different size than unicode emojis
* we want custom emojis to be centered according to the text (to make better use of line spacing/paddings)
* we don't want the line height to be changed (too much) by custom emojis
* we (probably) want custom emojis to be _displayed_ proportionally to the text

I'm investigating ways to solve this issue. It seems other Matrix clients (and even chat applications like Telegram or Discord) all globally suffer from this issue and have dealt with it in different ways, sometimes masquerading it and sometimes working around it.

For now though, we shouldn't break emoji legibility for our users. Affecting line height by a few pt is a minor issue compared to being almost unable to tell what inline emoji you or your interlocutor is using. Even just the "center emoji with text" thing already makes the line height issue 1/3 less of a problem. Once we improve the emoji rendering so it's more readable, _then_ it would make sense to decrease the emoji height to something like font.height * 1.6 or so.

This does not affect unicode emojis, as far as I can tell those are handled elsewhere. This only affects inline custom emojis.

Illegible on 1366x768 with 100% scaling and font size 11:

![image](/uploads/1d074c78d63aa1f28d9f3d204a656cc7/image.png){width=1025 height=576}

![Screenshot_20241212_144737](/uploads/7ae8e080f383461dcef3320575c05b24/Screenshot_20241212_144737.png)

Legible:

![Screenshot_20241212_150257](/uploads/4f9395bd25025317005aaf1b9a1633c0/Screenshot_20241212_150257.png)

![Screenshot_20241212_150357](/uploads/a5fbb8885d601592591e99c0cc669794/Screenshot_20241212_150357.png)
2024-12-12 18:12:38 +00:00
l10n daemon script
a92a72cebd GIT_SILENT Sync po/docbooks with svn 2024-12-12 01:31:53 +00:00
Kai Uwe Broulik
fe09dc23a6 Main: Set window title to current room name 2024-12-11 17:36:41 +01:00
Tobias Fella
9cd4a7416e Fix crash when sending messages
ECM recently started adding -fhardened, which makes us crash here since we're doing things that aren't valid, but happened to work out fine previously.
2024-12-11 16:05:19 +01:00
l10n daemon script
f6e3210b0d GIT_SILENT Sync po/docbooks with svn 2024-12-11 01:30:44 +00:00
Tobias Fella
16d33eb02c Don't show "This message was deleted" for state events
The result is unexpected and confusing
2024-12-10 12:58:24 +01:00
Tobias Fella
326512697c Show displayname instead of user id for join events 2024-12-10 12:46:54 +01:00
l10n daemon script
f25de891bf GIT_SILENT Sync po/docbooks with svn 2024-12-10 01:34:07 +00:00
l10n daemon script
e785533858 GIT_SILENT Sync po/docbooks with svn 2024-12-09 01:35:58 +00:00
l10n daemon script
0699bc4147 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"
2024-12-09 01:26:04 +00:00
James Graham
d07a8258e9 Room Custom Filter Prep
This is basically prep work for customisable sort orders. The room sort parameters are detached from the room sort model as multiple components will need to access the values. The sorting is then generified.

Some defunct sorting parameters are also removed.
2024-12-08 13:10:00 +00:00
l10n daemon script
d490e65315 GIT_SILENT Sync po/docbooks with svn 2024-12-08 01:30:05 +00:00
Tobias Fella
7a632c9561 Fix janky behavior of room drawer swipes
When not modal, dragging the edge of the room drawer to change its width felt very broken.
This seems to be a collision between Qt's dragging logic and our dragging logic,
so we disable theirs if the drawer is not modal
2024-12-07 12:39:19 +01:00
l10n daemon script
ec4b35fa5f GIT_SILENT Sync po/docbooks with svn 2024-12-07 01:32:24 +00:00
l10n daemon script
b263755629 GIT_SILENT Sync po/docbooks with svn 2024-12-06 01:31:21 +00:00
l10n daemon script
828585a260 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"
2024-12-06 01:25:13 +00:00
l10n daemon script
3b33121dbc GIT_SILENT made messages (after extraction) 2024-12-06 00:40:30 +00:00
Tobias Fella
21484c8184 Convert reuse data to new format 2024-12-05 18:12:07 +01:00
Soumyadeep Ghosh
020385c850 backend: allow users to sort based on last activity
This MR allows an option to prefer the last activity as the most
favorable parameter for sorting.
2024-12-05 16:32:40 +00:00
Kai Uwe Broulik
c585f3d8ae Add "Copy Link Address" context menu
Allows copying just the link address of a hyperlink.
2024-12-05 15:58:22 +00:00
l10n daemon script
3356e6c6cf GIT_SILENT Sync po/docbooks with svn 2024-12-05 01:31:30 +00:00
Joshua Goins
2fa6ad22a3 Expose access token under developer tools
I need this from time to time. For example, debugging an API call or
scripting something with the admin API. This is buried under developer
settings so hopefully no one starts sharing this willy-nilly.

Element Web does something similar, except theirs is hidden under Help &
About.
2024-12-04 21:42:10 +00:00
Joshua Goins
b887519f26 Add missing contexts for the rest of the settings header and page titles 2024-12-04 15:48:43 -05:00
Joshua Goins
b1e54a834c Fix capitalization of labels under General Settings
Buttons should be title case, and form headers should be sentence case.
Also, add an icon to the "Reset to defaults" button.
2024-12-04 15:41:36 -05:00
Joshua Goins
171e62a272 AccountMenu: Fix capitalization of items
This is a menu full of menu items, and should be using title case as
suggested by our HIG.
2024-12-04 15:38:00 -05:00
Joshua Goins
053770c117 snap: update libquotient 2024-12-04 20:05:07 +00:00
Tobias Fella
911f3e1f54 Fix some compilation warnings 2024-12-04 17:31:02 +01:00
l10n daemon script
1494ba95b3 GIT_SILENT Sync po/docbooks with svn 2024-12-04 01:32:09 +00:00
l10n daemon script
c0b00ce146 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"
2024-12-04 01:24:49 +00:00
l10n daemon script
47a08b829e GIT_SILENT made messages (after extraction) 2024-12-04 00:40:08 +00:00
Joshua Goins
953bb60aac Remove explicit quit connection with QQmlApplicationEngine
This is not needed, it already does this by itself.
2024-12-03 22:19:49 +00:00
Tobias Fella
fc4cb31277 Devtools: Use ChooseRoomDialog to select a room for inspection
The combobox has several drawbacks:
- It's not sorted in any meaningful way
- It doesn't have a search
- It doesn't show the icon and last message

This makes it hard to find the intended room in that dialog. The ChooseRoomDialog provides these things for us
2024-12-03 10:07:11 +01:00
l10n daemon script
9876636dbc GIT_SILENT Sync po/docbooks with svn 2024-12-03 01:31:25 +00:00
Heiko Becker
8314ab03bd GIT_SILENT Update Appstream for new release
(cherry picked from commit 1e29eca59a)
2024-12-03 01:10:01 +01:00
Tobias Fella
9d887ba3e7 Remove system information from device display name
BUG: 496901
2024-12-02 15:50:50 +00:00
l10n daemon script
1612a8a960 GIT_SILENT Sync po/docbooks with svn 2024-12-02 01:54:40 +00:00
l10n daemon script
99af210e62 GIT_SILENT made messages (after extraction) 2024-12-02 00:41:39 +00:00
Paul Brown
8fd108cde1 Added Stuart Turton as supporter 2024-12-01 19:09:18 +00:00
James Graham
ca81d35936 Post Message Refactor 2
Remove the need for NeoChat to have overloaded functions for posting messages and just use what quotient gives
2024-12-01 19:05:31 +00:00
James Graham
d65aacac6f postHtml Refactor
Use EventRelation and EventContent to form messages rather than writing custom Json.
2024-12-01 17:19:55 +00:00
l10n daemon script
43f052a363 GIT_SILENT Sync po/docbooks with svn 2024-12-01 01:31:34 +00:00
Paul Brown
0e0a38ffa2 Added supporter Joshua Strobl 2024-11-30 19:33:33 +00:00
Joshua Goins
819586fc4e Fix state keys developer tool page not working
Yet another applicationWindow failure
2024-11-30 16:15:47 +00:00
Joshua Goins
6b8a331428 Add icon for "Open developer tools" under Settings, add separator
It makes it look a little bit nicer, I think.
2024-11-30 16:15:28 +00:00
James Graham
57e7004e05 Fix removeConnection
Check m_accountsLoading and m_connectionsLoading separately for removal as when loadAccessTokenFromKeyChain() fails m_connectionsLoading won't have an entry for it
2024-11-30 15:27:09 +00:00
l10n daemon script
25c95cafe3 GIT_SILENT Sync po/docbooks with svn 2024-11-30 01:30:42 +00:00
l10n daemon script
54be1a8918 GIT_SILENT Sync po/docbooks with svn 2024-11-29 01:34:41 +00:00
l10n daemon script
d4a0573051 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"
2024-11-29 01:25:40 +00:00
Paul Brown
4a96eac67b Update org.kde.neochat.appdata.xml 2024-11-28 09:02:11 +00:00
Ingo Klöcker
2f4134a6d2 Add icon for F-Droid store listing
Our fastlane metadata tooling looks for an icon named like the application
icon specified in AndroidManifest.xml followed by "-playstore.png".
2024-11-27 22:08:34 +01:00
Laurent Montel
4f87dcc0c0 Add missing include moc 2024-11-27 20:44:46 +00:00
Nate Graham
47eba6b720 Make the room list slightly narrower by default
`GridUnit * 17` is 306px, which is quite wide. Given that the focus in
this app is on the content (i.e. the chat view) let's make the sidebar
a 36px narrower to make more room for content.

BUG: 496722
FIXED-IN: 24.12.0
2024-11-27 16:07:06 +00:00
l10n daemon script
cba537d561 GIT_SILENT Sync po/docbooks with svn 2024-11-27 01:31:59 +00:00
l10n daemon script
c9d03cb042 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"
2024-11-27 01:25:14 +00:00
Tobias Fella
e42c002fbd Fix visibility check 2024-11-26 21:58:39 +01:00
Tobias Fella
7c7b073a47 Fix some unqualified access warnings 2024-11-26 21:58:34 +01:00
l10n daemon script
20090d21eb GIT_SILENT Sync po/docbooks with svn 2024-11-26 01:30:45 +00:00
l10n daemon script
b0e69ff4b8 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"
2024-11-26 01:24:17 +00:00
l10n daemon script
552bb0a98b GIT_SILENT made messages (after extraction) 2024-11-26 00:40:17 +00:00
l10n daemon script
19510858af GIT_SILENT Sync po/docbooks with svn 2024-11-25 01:33:01 +00:00
l10n daemon script
44b2f6ee63 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"
2024-11-25 01:24:13 +00:00
l10n daemon script
c5d3002f31 GIT_SILENT made messages (after extraction) 2024-11-25 00:40:24 +00:00
James Graham
2e3659d4ee Don't open the space home page when changing spaces on android
When I change space on android I just want to be able to select a room I'm in.
2024-11-24 13:13:47 +00:00
l10n daemon script
f8a5509a91 GIT_SILENT Sync po/docbooks with svn 2024-11-24 01:32:27 +00:00
James Graham
421f436871 Fix MessageContentModel crash
Make sure we check that the RoomMessageEvent exists before accessing anything
2024-11-23 22:20:05 +00:00
James Graham
a37c9d6cea Make sure space drawer icons are available for android 2024-11-23 17:09:44 +00:00
Tobias Fella
d14d576d99 Implement MSC 4228 Search Redirection
See https://github.com/matrix-org/matrix-spec-proposals/pull/4228 for details.
Since this is tricky to test without server-side support, I have added a basic implementation
to the mock server in appiumtests/login-server.py

1. Start appiumtests/login-server.py
2. Start neochat with "--test --ignore-ssl-errors" options
3. Open "Explore Rooms"
4. Search for the exact string "forbidden"
5. See new error message provided by server
2024-11-23 15:50:12 +01:00
James Graham
9391e44e4b EventHandler Cleanup
Remove functions from eventhandler and replace with now uplifted functions in libquotient
2024-11-23 14:21:13 +00:00
l10n daemon script
a7aebe3a61 GIT_SILENT Sync po/docbooks with svn 2024-11-23 01:29:28 +00:00
Paul Brown
1fca9021a4 Added supporter dabe 2024-11-22 22:04:28 +00:00
Joshua Goins
a39194b2ad Fix ShareActionStub for Windows and Android
Apparently, we are supposed to be setting source file properties for our QML files *before*
the QML module is created. Doing it after seemed to work until Qt 6.8, where it finally
broke. Notably, this makes the Android version work again but might also affect Windows.
2024-11-22 13:44:17 +00:00
l10n daemon script
bff93d9352 GIT_SILENT Sync po/docbooks with svn 2024-11-22 01:32:35 +00:00
l10n daemon script
d76c9cd16d GIT_SILENT Sync po/docbooks with svn 2024-11-21 01:30:26 +00:00
l10n daemon script
14774fe235 GIT_SILENT Sync po/docbooks with svn 2024-11-20 01:31:20 +00:00
l10n daemon script
39046632aa GIT_SILENT Sync po/docbooks with svn 2024-11-19 01:30:50 +00:00
Carl Schwan
fbb2afdb49 Remove layout attached properties
They don't do anything
2024-11-18 12:32:00 +00:00
James Graham
aff0402f71 Make sure the loading text for a new login wraps
Title

BUG: 493869
2024-11-18 08:42:19 +00:00
James Graham
cee9058c77 Fix Sed Edits
Make sure that for multiple sed edits we grab the eventID of the original message not the replacement

BUG: 496313
2024-11-18 08:41:52 +00:00
l10n daemon script
3f922b4c90 GIT_SILENT Sync po/docbooks with svn 2024-11-18 01:34:10 +00:00
l10n daemon script
02d2d31cf3 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"
2024-11-18 01:25:11 +00:00
Thiago Sueto
240cf6a0ed Make height of chatbar, userinfo bar and side tab bar the same
|Current state|With this MR|
|-|-|
|![Screenshot_20241115_171736](/uploads/858a8ca21a6f4024a20f6ba32225aece/Screenshot_20241115_171736.png)|![Screenshot_20241115_171650](/uploads/115f99c7bb2b93a542c42647f9cc25c7/Screenshot_20241115_171650.png)|
2024-11-17 18:47:45 +00:00
Tobias Fella
dcd9ee93de Escape display name in WelcomePage 2024-11-17 19:26:04 +01:00
Joshua Goins
2a8cd74ab1 Fix undefined access when loading stickers in chat
We need a check here, because stickers (and really, any images without a
tempSource) will try to access an undefined object.

This fixes the error:
"qrc:/qt/qml/org/kde/neochat/timeline/ImageComponent.qml:106: TypeError: Cannot read property 'source' of undefined"
2024-11-16 21:30:59 +00:00
Joshua Goins
63bc7055c2 Default to a more sensible sticker size
If we do not set the width/height for stickers (which don't have any)
then the height is okay, but the message has the maximum width which
looks odd.

Instead, let's limit all stickers to 256px and it makes them look much
nicer in chat.
2024-11-16 16:12:56 -05:00
Joshua Goins
1cca9733d6 Add a comment that these are not normal quotation marks
It can be hard to tell depending on which font you're viewing the code
with.
2024-11-16 16:02:12 -05:00
Joshua Goins
1104da5e2c TextHandler: Use the fancy Unicode quotation characters
As per our HIG, we should use these in quotations instead of the normal
quote characters.
2024-11-16 21:01:04 +00:00
Joshua Goins
3a9718c09d Limit the width of a user's QR code
This fixes the lopsided layout in the user details dialog.
2024-11-16 21:00:39 +00:00
Joshua Goins
55362c5573 RoomManager: Unify the resolveResource overloads
Every time I look at how resource resolving works, I always trip over
this unused overload. This behaves *different* than the other overload,
which has special cases for handling invalid Uris.

Now whether you pass in a Uri or a QString, it should behave the same.
2024-11-16 21:00:29 +00:00
Joshua Goins
0bba2299b3 Set the size of custom emoticons to the font height, and fix alignment
Currently custom emojis render weirdly in NeoChat. Not only are they
large, they're also in charge and like to mess up the layout of the
text.

Now that's fixed and they'll take up the same height as the surrounding
text. It's now centered in the text too.
2024-11-16 21:00:11 +00:00
Joshua Goins
45685af9e9 Add icons to the recommended space actions, fix spacing of the items 2024-11-16 20:59:57 +00:00
Joshua Goins
6c416a9338 Fix restoring the last used space on desktop
This was supposed to work, but it's done in the wrong order. We need to
set the current space first, and then select the room - otherwise
it doesn't get restored.
2024-11-16 20:59:45 +00:00
Joshua Goins
1b0027e1d2 Ensure it's not possible for the recommended space avatar to assert 2024-11-16 20:59:02 +00:00
Joshua Goins
2409adf516 Fix avatars not loading in the room completion model 2024-11-16 20:59:02 +00:00
Joshua Goins
554801dfe4 Make sure RoomInformation's source is type url 2024-11-16 20:59:02 +00:00
Joshua Goins
20c23917e9 Remove now unused NeoChatRoomMember::avatarMediaId() 2024-11-16 20:59:02 +00:00
Joshua Goins
ef953b7574 Remove more needless usages of makeMediaUrl
This is only really needed in specific cases, e.g. localUser which isn't
attached to a connection and thus needs a little help. Notes for when
this is needed is added for future readers.
2024-11-16 20:59:02 +00:00
Joshua Goins
6b79795229 Change how room avatars are passed, fix friend avatars not loading
The problem lies in how media URLs work, in this case it the old
NeoChatRoom::avatarMediaId could pass a mxc url *or* a path that can
be put into root.connection.makeMediaUrl. So normal rooms with avatars
loaded, but never friends because room members gave the mxc URL.

Instead, change everything to use avatarMediaUrl which corrects this
issue by always passing a mxc URL to QML. This also removes the need to
call makeMediaUrl.

Fixes #675
2024-11-16 20:59:02 +00:00
Joshua Goins
9cb7ec2348 Add ellipses to the "Forward" message action, because it opens a dialog 2024-11-16 18:53:53 +00:00
Joshua Goins
437c981d30 Don't show the file name underneath the image
This still keeps custom image descriptions, but no longer shows it for
images where it was the same as their filename.
2024-11-16 13:18:46 -05:00
Joshua Goins
0334cae4c8 Change the room alias text color to disabled
It's less important than the title, and this should reduce it's visual
prominence.
2024-11-16 16:40:57 +00:00
Joshua Goins
24c405d747 Add a separator between the report and copy message actions 2024-11-16 11:39:18 -05:00
Joshua Goins
a3f5962809 DelegateContextMenu: Add support for separators in the mobile menu too 2024-11-16 11:30:49 -05:00
Joshua Goins
0deb7495f0 Re-arrange the file and message context menus, add separators
Like the room context menu, this is a jumbled list of actions that could
use some organization. Also, the text for "Copy Text" and "Copy Link" is
clearer.
2024-11-16 11:23:39 -05:00
Joshua Goins
d34f89fc4b DelegateContextMenu: Add support for separator actions 2024-11-16 11:22:37 -05:00
Joshua Goins
a909ed498f Hide the category list in the emoji picker when there is none
This is easy to test if you have no stickers. It should no longer have
a weird empty space above the placeholder message.
2024-11-16 16:03:36 +00:00
Joshua Goins
16f4e17e8f Improve how stickers appear in the emoji picker
First, the fill mode for the sticker images shouldn't stretch them.
Also make sure there is enough padding in the category so the image
doesn't appear larger than the button. Finally, set the source size for
the images so Qt can smooth them out better.
2024-11-16 16:03:24 +00:00
Joshua Goins
0e9592a96c Settings: Use symbolic version of the NeoChat icon
To match the rest of the icons in this sidebar, we can reuse our tray
icon.
2024-11-16 16:03:09 +00:00
Joshua Goins
704ee6a53a Add placeholder icon when there's no emojis or stickers 2024-11-16 10:50:49 -05:00
Joshua Goins
5b9afbce9a Settings: Request symbolic versions of the icons
According to the HIG, we should be using symbolic versions of these
icons at this size. Not that we have symbolic versions for these icons
yet, but it's still safe to do as they'll fall back to the old ones.
2024-11-16 09:48:13 -05:00
Thiago Sueto
396cc8e8ef Make top margin consistent across Neochat settings
This standardizes on the same value used for KirigamiAddons pages like AboutKDE and About, namely largeSpacing * 4.

Now, when switching between settings pages you no longer have settings inconsistently changing heights willy nilly, header notwithstanding.

The only page that's missing is the Spellchecking page, as that needs to be fixed in Kirigami Addons' private Sonnet page.
2024-11-16 14:37:01 +00:00
Thiago Sueto
bf776b5c06 Fix inconsistent wording about leaving current space/room
For rooms, we already say "Leave this room".

When viewing a Space page, we have both "Leave the space" and "Leave this room". The "Leave the space" VS "Leave this space" was bothering me, and the Space page should say "Leave this space" instead of "Leave this room".
2024-11-16 14:36:30 +00:00
Joshua Goins
be319f88d3 "Save As" action should have ellipses
Because you have to interact with the save dialog before doing anything
else.
2024-11-16 14:25:47 +00:00
Joshua Goins
af40d555d4 Improve the layout and function of the room context menu
The room context menu is a jumbled mess of actions, so the first idea of
this commit is to organize them. The first item is "Mark as Read"
because let's be honest, you're going to be using that the most. Then
the next "group" of actions are what users can "do" with the room. This
is like "Notification settings", "Favorite" and etc. Then there's room
settings, and leave.

Secondly, the "Favorite" action now uses the same icon we use elsewhere.

Third, "Notification State" is a weird name for this action and renamed
to simply "Notifications".

Finally, the "Mark as Read" action is now disabled when there's nothing
else to read.
2024-11-16 14:25:12 +00:00
Joshua Goins
f802dbe686 Port from deprecated AboutKDE component to AboutKDEPage 2024-11-16 14:24:10 +00:00
Joshua Goins
2379e3d83b Don't scroll up when clicking on the same room over and over
If you try to click on your current room in the list, it scrolls up the
messages a bit. This is because in RoomManager::visitRoom it's being
called with an empty eventId and we will happily emit a goToEvent. This
is despite there being nothing to go to.

Fixes #677.
2024-11-16 14:18:32 +00:00
Joshua Goins
9e90ac0412 Add margins to the room drawer header to match Kirigami
Otherwise it sticks to the left of the drawer and looks kinda ugly.
2024-11-16 14:16:50 +00:00
Joshua Goins
c27948ca3c Change the leave button in the drawer to "Leave this space" if needed 2024-11-16 14:14:32 +00:00
Joshua Goins
c3b9d664df "Room Information" title should be capitalized 2024-11-16 14:13:32 +00:00
Joshua Goins
31ef0a5223 Make it so the filename is filled out by default when saving files
This was never ported from the Qt labs platform FileDialog, because
currentFile doesn't exist anymore. It's now called selectedFile.
2024-11-16 14:12:19 +00:00
Joshua Goins
14c58acea1 Improve the appearance of the welcome page user list
Before it only listed the user id, and nothing else. If you had multiple
accounts, it's a little difficult to tell them apart. Now the user
selection appears like how they are displayed elsewhere in NeoChat, with
the display name and avatar.

| Before | After |
| ------ | ------ |
|   ![Screenshot_20241115_221425](/uploads/3986e4c7bbb7dcdca67ee30bb529767e/Screenshot_20241115_221425.png){width=786 height=822}     |        ![Screenshot_20241115_221149](/uploads/57eb1a7e57ba5ae8c41dd922cbf39c62/Screenshot_20241115_221149.png){width=786 height=822} |
2024-11-16 14:11:55 +00:00
l10n daemon script
5dae20603e GIT_SILENT Sync po/docbooks with svn 2024-11-16 01:34:25 +00:00
Joshua Goins
3f6fa94289 Port from Kirigami Add-ons Banner to Kirigami InlineMessage 2024-11-15 17:29:56 +00:00
l10n daemon script
117615a8b0 GIT_SILENT Sync po/docbooks with svn 2024-11-15 01:32:04 +00:00
l10n daemon script
4a52773c7d GIT_SILENT Sync po/docbooks with svn 2024-11-14 01:31:26 +00:00
l10n daemon script
edfee495c6 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"
2024-11-14 01:24:49 +00:00
l10n daemon script
7d112df7c6 GIT_SILENT made messages (after extraction) 2024-11-14 00:40:15 +00:00
l10n daemon script
9acaaade45 GIT_SILENT Sync po/docbooks with svn 2024-11-13 01:30:31 +00:00
l10n daemon script
aaca28dbf6 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"
2024-11-13 01:24:06 +00:00
Paul Brown
d4ef5f9d4d Update org.kde.neochat.appdata.xml 2024-11-12 19:02:36 +00:00
James Graham
2095dea801 Add #if for patch to fix pendingEventAdded event ref 2024-11-12 16:25:23 +00:00
James Graham
a36f7ef10d Fix test 2024-11-12 16:25:23 +00:00
James Graham
9874962ee3 Make sure that the content model is loaded properly when a new event is set. This fixes seeing an unknown event message for all new events. Instead a loading symbol is briefly seen before switching to the actual content. 2024-11-12 16:25:23 +00:00
l10n daemon script
4b08022075 GIT_SILENT Sync po/docbooks with svn 2024-11-12 01:33:25 +00:00
l10n daemon script
dc3db3aec4 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"
2024-11-12 01:26:03 +00:00
l10n daemon script
0568c2a93d GIT_SILENT Sync po/docbooks with svn 2024-11-11 01:35:46 +00:00
l10n daemon script
7ab0a6fc9e 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"
2024-11-11 01:24:43 +00:00
Joshua Goins
d6b780762e PollHandler: Make sure it's not constructible from QML 2024-11-10 15:16:26 +00:00
Joshua Goins
5ef66b5cf6 PollHandler: Ensure that m_pollStartEvent is always initialized to null
Otherwise it may be undefined, and we DO create default-constructed
PollHandler. For example, one is used as a fallback poll object
in NeoChatRoom::poll.

This is blind fix for a pretty nasty poll-related crash we saw a few
months ago.

BUG: 493649
2024-11-10 15:16:26 +00:00
l10n daemon script
19e8cd5e48 GIT_SILENT Sync po/docbooks with svn 2024-11-10 01:35:11 +00:00
l10n daemon script
df5117892f 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"
2024-11-10 01:24:25 +00:00
l10n daemon script
aaa4216f55 GIT_SILENT made messages (after extraction) 2024-11-10 00:40:38 +00:00
Joshua Goins
85ee5084b6 Add m.room.create state events to sync_response
In case we need to access the creation state in an appium test in the
future.
2024-11-09 23:21:37 +00:00
Joshua Goins
bb9ce117de Hide rooms that have a defined room type
I have a room with a custom type that's only for holding data, and
doesn't need to be shown in the room list. Currently the spec is a bit
vague about what clients should do, but hiding them is probably fine
for now.
2024-11-09 23:21:37 +00:00
Carl Schwan
00c5aa26bb RoomGeneralPage: Add missing separator
And some other minor fixes
2024-11-09 23:11:19 +00:00
Joshua Goins
bae4de227c Make closing link previews instant, as it should be
We were missing a endResetModel() call, now with it added the removal
happens instantly.
2024-11-09 23:10:54 +00:00
Joshua Goins
253f891c5a Stop being able to crash NeoChat by pressing a button repeatedly
If you spam click the "Close link preview" button, it's possible to
crash NeoChat. This is because the index check is wrong for the array
size.

It's possible to even do this due to a bug causing the removal to be
reflected visually too slowly, that's fixed in the next commit.
2024-11-09 23:10:54 +00:00
Joshua Goins
6966159062 Improve clicking link previews
First of all, clicking on them actually works - because we were missing
an import for RoomManager. Secondly, we use a dedicated TapHandler
since onLinkActivated sucks. We want to be able to click anywhere on the
preview to go to the website/room anyway.
2024-11-09 23:10:40 +00:00
Joshua Goins
07d3b80c3e Don't set isThread on the message and file delegate context menus
It doesn't have a property called isThread, and I don't know where it
went - if it ever existed?
2024-11-09 23:10:31 +00:00
Joshua Goins
a41d0f3214 Make fullscreen images focused when they're opened
Otherwise keyboard shortcuts don't work until you tap the image, which
makes no sense.

BUG: 484322
2024-11-09 23:10:21 +00:00
Joshua Goins
1ee15de78b Fix viewing any kind of data in developer tools
Fix pageStack being undefined, so we're able to view event data again.
2024-11-09 23:10:08 +00:00
Carl Schwan
b044358970 Update checkbox of PollComponent
Use FormCheckDelegate instead of a CheckBox inside a RowLayout. This
increase the click area particularly on mobile.
2024-11-09 23:09:51 +00:00
Oliver Beard
d2e11bb3bb timeline: Round separators for replies and link previews 2024-11-09 23:09:33 +00:00
Joshua Goins
a55bac899c README: Change snap store badge to the one from apps.kde.org
It seems CORS is blocking access to the badge, but we have rehosted on
apps.kde.org.
2024-11-09 21:32:45 +00:00
Joshua Goins
c2380fb8df Update network proxy page with the improved version from Tokodon
This functions the same, but looks a bit nicer.
2024-11-09 17:11:11 +00:00
Joshua Goins
f31c644b13 Update desktop file and app description to match AppStream data
This was updated to "Chat on Matrix" but in other places it was never
switched from "Matrix client" and the like. Now it should be more
consistent.
2024-11-09 17:11:00 +00:00
Joshua Goins
26cd621d0e Clarify that sorting rooms by activity isn't the only thing it does
Recently, it also sorts rooms based on unread notification count and
importance. This adds a clarification to the setting so users (like me)
aren't confused why it isn't sorting only by activity.
2024-11-09 16:46:54 +00:00
l10n daemon script
4c58512c54 GIT_SILENT Sync po/docbooks with svn 2024-11-09 01:30:21 +00:00
Albert Astals Cid
04c1b47660 GIT_SILENT Upgrade release service version to 25.03.70. 2024-11-08 19:38:32 +01:00
l10n daemon script
7b249e9fa6 GIT_SILENT Sync po/docbooks with svn 2024-11-08 01:30:13 +00:00
l10n daemon script
46593ef68f GIT_SILENT Sync po/docbooks with svn 2024-11-07 01:31:11 +00:00
Eren Karakas
b70f73c7d6 Make send message and insert newline shortcuts configurable
Currently both Enter and Ctrl+Enter send the message in ChatBar on
desktop. This might be unexpected behavior to users coming from other
chat applications (eg. WhatsApp, Telegram, Element) as those send
with Enter only by default. They allow changing send to Ctrl+Enter
in settings and other option is used to insert a newline.

BUG: 476758
2024-11-06 21:09:58 +00:00
l10n daemon script
ece5e34fa2 GIT_SILENT Sync po/docbooks with svn 2024-11-06 01:31:11 +00:00
Joshua Goins
74b400288d ManualRoomDialog: Use Window.window attached property
This fixes a runtime error since applicationWindow is undefined.
2024-11-05 16:45:37 +00:00
Joshua Goins
83f19b0631 ExploreRoomsPage: Fix "Enter a room address" button not working
This was due to an undefined applicationWindow singleton, but we don't
actually need it and can use Overlay.overlay directly.
2024-11-05 16:45:37 +00:00
l10n daemon script
c905d2d6fb GIT_SILENT Sync po/docbooks with svn 2024-11-05 01:30:46 +00:00
l10n daemon script
d0c1eb2f04 GIT_SILENT Sync po/docbooks with svn 2024-11-04 01:28:25 +00:00
Tobias Fella
d7d9d29c1d Remove no longer needed ifdefs 2024-11-03 18:42:56 +01:00
Tobias Fella
2a3f019ec6 Port away from Quotient::Omittable 2024-11-03 18:26:46 +01:00
Tobias Fella
d384d50b0d Remove olm target check
Olm isn't option in libQuotient anymore, there's no point in checking
2024-11-03 18:21:19 +01:00
Tobias Fella
be8cb12bba Require libQuotient 0.9 2024-11-03 17:48:40 +01:00
l10n daemon script
a1aa2918be GIT_SILENT Sync po/docbooks with svn 2024-11-03 01:27:04 +00:00
l10n daemon script
d7536bccb3 GIT_SILENT Sync po/docbooks with svn 2024-11-02 01:29:54 +00:00
l10n daemon script
6b677355e1 GIT_SILENT made messages (after extraction) 2024-11-02 00:39:52 +00:00
James Graham
85d625f6ac Delete ActionsHandler
Move the fucntionality of ActionsHandler into ChatbarCache and ActionsModel.

At this stage there wasn't much left that is was doing and the functionality could easily move.
2024-11-01 17:00:08 +00:00
l10n daemon script
f5d6f87afe GIT_SILENT Sync po/docbooks with svn 2024-11-01 01:29:19 +00:00
Joshua Goins
31d755f407 ManualRoomDialog: Change title of the dialog to reduce duplication
"Room ID or Alias" is duplicated twice here, once in the dialog title
and a second time as the label for the text field. Let's change it to a
more suitable name "Manually Enter a Room".
2024-10-30 22:25:53 -04:00
Joshua Goins
ebfc20d4b4 ExploreRoomsPage: Add better text to the "Enter a room address" button
This adds a short explanatory label to the "Enter a room address" button
in this list, in case the user does not know where to find their room
address.

Also changes the name of the button to "Enter a Room Manually" to refer
that you can enter aliases here as well.
2024-10-30 22:24:20 -04:00
l10n daemon script
b83d42103f GIT_SILENT Sync po/docbooks with svn 2024-10-31 01:30:42 +00:00
Heiko Becker
c9856347fe GIT_SILENT Update Appstream for new release
(cherry picked from commit 4b7011fe14)
2024-10-31 01:14:26 +01:00
l10n daemon script
7224c92caf 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"
2024-10-30 01:23:46 +00:00
l10n daemon script
a84f98c96f GIT_SILENT made messages (after extraction) 2024-10-30 00:39:47 +00:00
l10n daemon script
a40bccc29d GIT_SILENT Sync po/docbooks with svn 2024-10-28 01:33:27 +00:00
l10n daemon script
171897161c GIT_SILENT made messages (after extraction) 2024-10-28 00:40:09 +00:00
l10n daemon script
013ad49e2b GIT_SILENT Sync po/docbooks with svn 2024-10-27 01:34:19 +00:00
l10n daemon script
b357586164 GIT_SILENT made messages (after extraction) 2024-10-27 00:40:08 +00:00
James Graham
5642f3416a Pester Network Error Messages
Stop sending a pester message using showPassiveNotification every time there is a network error. There is already a proper handling with `isOnline` in `NeochatConnection`. This just causes issues overlapping content.

BUG: 488572
2024-10-26 12:28:28 +00:00
l10n daemon script
9bbb1710df GIT_SILENT made messages (after extraction) 2024-10-26 00:40:19 +00:00
l10n daemon script
e2b7679252 GIT_SILENT Sync po/docbooks with svn 2024-10-25 01:30:33 +00:00
l10n daemon script
5c353cd4b5 GIT_SILENT made messages (after extraction) 2024-10-25 00:40:01 +00:00
l10n daemon script
cba6dc994f GIT_SILENT Sync po/docbooks with svn 2024-10-24 01:29:20 +00:00
l10n daemon script
b0c4b7fc2a GIT_SILENT made messages (after extraction) 2024-10-24 00:39:47 +00:00
James Graham
ed7aff1f24 Updates for further event content changes
Adapt to https://github.com/quotient-im/libQuotient/pull/812
2024-10-23 16:38:51 +00:00
l10n daemon script
33f4be0d88 GIT_SILENT made messages (after extraction) 2024-10-23 00:40:25 +00:00
l10n daemon script
1178cafef0 GIT_SILENT made messages (after extraction) 2024-10-22 00:39:47 +00:00
l10n daemon script
1be97e65b4 GIT_SILENT Sync po/docbooks with svn 2024-10-21 01:30:29 +00:00
Carl Schwan
634cefc694 Simplify appstream summary
- matrix -> Matrix
- Remove "with your friends" as NeoChat is not only about chatting with
  your friend

CCBUG: 480167
2024-10-20 23:33:48 +00:00
l10n daemon script
36469c6097 GIT_SILENT Sync po/docbooks with svn 2024-10-20 01:30:26 +00:00
l10n daemon script
0cd9c6a434 GIT_SILENT Sync po/docbooks with svn 2024-10-19 01:31:03 +00:00
l10n daemon script
e285a94273 GIT_SILENT Sync po/docbooks with svn 2024-10-17 01:30:25 +00:00
James Graham
a2ec6d97b1 Update for the latest event content changes to libquotient 2024-10-16 17:32:55 +00:00
l10n daemon script
b42fd3fc51 GIT_SILENT Sync po/docbooks with svn 2024-10-16 01:32:19 +00:00
Tobias Fella
12971bb08b Minor cleanup 2024-10-15 22:01:32 +02:00
James Graham
79deda4f2d Make sure that content is kept alive so fileInfo can be used throughout the function 2024-10-15 16:30:57 +00:00
James Graham
365af2cd6c Mobile Pages
Currently the page experienc on mobile is suboptimal as back gestures do not work and the startup behaviour is not ideal.

This reworks it so that pages are now pushed as a layer on mobile and at startup only a saved space is restored. It is also setup so that on mobile you'll never see a blank room page (like when you select friends or home).
2024-10-15 16:13:02 +00:00
l10n daemon script
cc50e76c0d GIT_SILENT Sync po/docbooks with svn 2024-10-15 01:28:56 +00:00
James Graham
16df22af68 Update for the API changes to RoomMessageEvent in libquotient 0.9 2024-10-14 21:59:56 +01:00
l10n daemon script
53a957fa15 GIT_SILENT Sync po/docbooks with svn 2024-10-14 01:27:49 +00:00
James Graham
69571489fa Mobile DelegateContextMenu
Make sure that the mobile DelegateContextMenu is getting the author data correctly
2024-10-13 14:47:10 +00:00
James Graham
3a66b4d67e Fix mobile ExploreComponent
Make sure that the search filter is removed when another button is pressed.
Make sure that the popup closes when one of the other menus is open.
Make the separator is at the top on NavigationTabBar
2024-10-13 12:41:56 +00:00
l10n daemon script
ba8d2b1281 GIT_SILENT Sync po/docbooks with svn 2024-10-12 01:29:09 +00:00
Tobias Fella
11185c127d Escape display name in verification event 2024-10-11 22:46:38 +02:00
Tobias Fella
0ab3bfd4f3 Fix crash when opening invitation
There seem to be problems with the model not updating correctly when the room is set. This fix is a bit dirty, but seems to work well enough

BUG: 493197
2024-10-11 22:15:38 +02:00
Tobias Fella
b5fdb3d0db Fix transitioning from invitation page to normal room page 2024-10-11 21:11:17 +02:00
Tobias Fella
9ef342e448 Fix emoji detection
The previous code considered "123" to be an emoji
2024-10-11 21:00:51 +02:00
Tobias Fella
0bda65b5ac Make sure that we don't immediately open all new rooms
Some confusing interaction with the code removed here causes us to open all rooms that come in.
We generally don't want that. I also don't understand why we're connecting for new rooms here - the room is already available in this case.
2024-10-11 20:44:15 +02:00
James Graham
005b7a760c Request Notification Permission
If notification permission has not been granted and permission has not previously been asked for request notification permission.
2024-10-11 16:28:59 +00:00
Tobias Fella
ab3c40a709 Don't double-escape subject 2024-10-11 16:12:00 +02:00
Tobias Fella
a809b7f11e Fix avatar size for some uncommon cases 2024-10-11 15:07:46 +02:00
l10n daemon script
702c7d49a8 GIT_SILENT Sync po/docbooks with svn 2024-10-11 01:34:35 +00:00
Tobias Fella
a2afaf40cd Remove some leftover debug logging 2024-10-10 22:06:19 +02:00
Joshua Goins
eb900a5c2c Fix access of notifications manager in UnifiedPush codepath 2024-10-08 07:56:11 -04:00
l10n daemon script
14eadb3b92 GIT_SILENT Sync po/docbooks with svn 2024-10-08 01:29:52 +00:00
l10n daemon script
a5d84bb266 GIT_SILENT made messages (after extraction) 2024-10-08 00:39:44 +00:00
Oliver Beard
5a03ce4e95 qml/RoomDrawer: Add & fix separators
A separator has been added to the drawer view's left side, and the position of the NavigationTabBar has been set so it correctly draws the separator at the top and not the bottom.
2024-10-07 18:43:40 +00:00
l10n daemon script
607db82db0 GIT_SILENT Sync po/docbooks with svn 2024-10-07 01:28:24 +00:00
Heiko Becker
56b302d4c8 GIT_SILENT Update Appstream for new release
(cherry picked from commit 765292e0e9)
2024-10-06 22:02:31 +02:00
James Graham
1237e9d4bd NotificationManager rework
Rework notifications manager to no longer be a singleton, but a component of controller.

The dependency on it for neochat room and connection is also removed.
2024-10-06 17:14:18 +00:00
Carl Schwan
71468e453c RoomGeneralPage: Move avatar setting outside of FormCard 2024-10-06 09:38:37 +00:00
Nicolas Fella
d0a915e81c Add missing QML import name 2024-10-06 09:38:19 +00:00
l10n daemon script
e7751f40fa GIT_SILENT Sync po/docbooks with svn 2024-10-06 01:28:31 +00:00
l10n daemon script
498240c6ec 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"
2024-10-06 01:22:10 +00:00
l10n daemon script
d448cf4c7e GIT_SILENT made messages (after extraction) 2024-10-06 00:39:44 +00:00
James Graham
d7d9f254ff Remove the dependence on RoomManager from NeochatConnection.
At the same time use the roomCreated signal from connection for auto resolving as it's specifically designed for user created rooms.
2024-10-05 18:39:21 +01:00
James Graham
aedba5c650 Strip single <p> tags inside TextHandler rather than actions handler 2024-10-05 17:05:36 +00:00
James Graham
a4e9794b13 MessageComponent
It feels weird to have anything that needs MessageComponent have to depend on all of MessageContentModel and pull in it's dependencies. This moves MessageComponent into its own header.
2024-10-05 15:51:19 +00:00
James Graham
4bd4bd6f22 Rework ActionsHandler
Rework ActionsHandler as static helper functions.

The functions are now invoked from ChatBarCache so there is no need to pass an actions handler object around qml simplifying the code.
2024-10-05 13:44:53 +00:00
l10n daemon script
ac9bfbff78 GIT_SILENT Sync po/docbooks with svn 2024-10-05 01:29:07 +00:00
James Graham
e26392ce94 requestDirectChat
Use Quotient::connection::requestDirectChat directly as it can already handle all the conditions.
2024-10-04 16:52:05 +00:00
l10n daemon script
a314e56425 GIT_SILENT Sync po/docbooks with svn 2024-10-04 01:30:17 +00:00
James Graham
4af27f7609 Remove unneeded includes 2024-10-03 23:22:48 +01:00
James Graham
773017c881 errorOccured
Have controller link to neochatconnection for errorOccured rather than call directly to remove dependency on controller.

For all the same reasons as network/neochat!1926
2024-10-03 20:32:30 +00:00
James Graham
153cbeae8a ShowMessage
Move showMessage to RoomManager and merge warning in. A new Message type enum is created aligned with the Kirgami.MessageType used by Kirigami.Banner to avoid needing to translate from 2 enums. 

showMessage is also sent as a signal from NeoChatRoom (and via the room from ActionsModel), this removes the need for them to have a dependency on Controller (and RoomManager). While not necessarily the cause of Windows crashes the spaghetti dependencies of RoomManager and Controller throughout the code base has made debugging that harder so this aims to simplify that as well.
2024-10-03 18:42:29 +00:00
l10n daemon script
777ea9fbe0 GIT_SILENT Sync po/docbooks with svn 2024-10-03 01:28:58 +00:00
James Graham
d22cc7f40a Use Knotificationpermission 2024-10-02 21:07:58 +00:00
Carl Schwan
c28ca9087c RemoveChildDialog: Don't wrap delegates in FormCard 2024-10-02 17:12:36 +00:00
l10n daemon script
857412d9cc GIT_SILENT Sync po/docbooks with svn 2024-10-02 01:29:32 +00:00
James Graham
4b132460f6 Remove unused include utils.h in neochatroom 2024-10-01 19:31:52 +01:00
James Graham
644f5c0ce1 Permission Manager
Add permission manager from Itinerary so that Android permissions can be checked.

Note at the moment the request permission functions are not hooked up so on Android the permission will need to be manually set on. I'll hook this up later but I wanted to confirm my suspicion on notifications being the current cause of crashes.
2024-10-01 17:29:39 +00:00
l10n daemon script
45439e17c9 GIT_SILENT Sync po/docbooks with svn 2024-10-01 01:29:28 +00:00
Carl Schwan
485e0f0510 Make hover actions position logic more robust 2024-09-30 20:30:36 +00:00
Carl Schwan
2969d20a92 Put hover actions in the RowLayouts
Simplify the code compared to use an ActionToolBar and this should makes
it more reliable.
2024-09-30 20:30:36 +00:00
Soumyadeep Ghosh
7cf97a5c9b snap: update to latest tag 2024-09-28 12:59:32 +05:30
l10n daemon script
f2894ed774 GIT_SILENT Sync po/docbooks with svn 2024-09-27 01:31:05 +00:00
Tobias Fella
3e1044b8fd Adapt to libQuotient changes 2024-09-26 19:55:41 +02:00
l10n daemon script
0c6480c5e0 GIT_SILENT Sync po/docbooks with svn 2024-09-26 01:29:11 +00:00
Luc Schrijvers
232c45979a Disable KRunner, X11, and DBus on Haiku 2024-09-25 13:31:00 +00:00
James Graham
848031c315 Add the notifcation permision for modern android versions 2024-09-25 05:22:26 +00:00
l10n daemon script
bf2389c31c GIT_SILENT Sync po/docbooks with svn 2024-09-25 01:30:30 +00:00
James Graham
d87bba7993 Revert "snap: update for latest tag"
This reverts commit 6e81701d4b
2024-09-24 21:57:18 +00:00
Carl Schwan
9b837a8656 SelectParentDialog: Fix selecting room 2024-09-24 21:08:34 +00:00
Carl Schwan
90061caec3 Fix undefined StyledText error
StyledText is an enum for Text not TextEdit
2024-09-24 19:30:27 +00:00
Soumyadeep Ghosh
6e81701d4b snap: update for latest tag 2024-09-24 17:59:10 +05:30
l10n daemon script
2164c4c3f8 GIT_SILENT Sync po/docbooks with svn 2024-09-24 01:36:38 +00:00
Nate Graham
a0665187c6 Use common key for saving window state data
We typically use "MainWindow" here, not "Main".
2024-09-23 14:37:32 -06:00
l10n daemon script
77e933f620 GIT_SILENT Sync po/docbooks with svn 2024-09-23 01:27:36 +00:00
l10n daemon script
deed9cf6d7 GIT_SILENT Sync po/docbooks with svn 2024-09-22 01:31:14 +00:00
Carl Schwan
fad4e506bc Fix display name in verification message
Previously this would be undefined and i18n doesn't handle this very
well
2024-09-21 13:26:03 +00:00
Carl Schwan
9d6b940b78 Port away from Qt.Plaform.labs
This bring a QtWidget dependency on Android
2024-09-21 12:12:14 +00:00
Carl Schwan
cfb663d399 RoomGeneralPage: Split general info in two card
So that the save button isn't floating in the middle of the card
2024-09-21 12:11:50 +00:00
Carl Schwan
4620c176b6 AppearanceSettingsPage: Hide separator for transparancy setting on Android 2024-09-21 12:11:39 +00:00
Carl Schwan
8c927a59d9 NetworkProxyPage: Add separator between form delegates 2024-09-21 12:11:27 +00:00
Carl Schwan
2fe9bc9846 Fix arrow icons on Android 2024-09-21 12:11:14 +00:00
Carl Schwan
b9615eadb3 Use FormTextAreaDelegate 2024-09-21 12:11:04 +00:00
Volker Krause
e4e5d14d6e Use released KQuickImageEditor 2024-09-21 09:46:33 +00:00
Carl Schwan
75eddd2a6f amdroid: Add missing icon for preferences 2024-09-21 11:45:38 +02:00
l10n daemon script
634407a22d GIT_SILENT Sync po/docbooks with svn 2024-09-21 01:31:02 +00:00
Carl Schwan
42dd2e5413 Ensure floating buttons have correct size on mobile 2024-09-20 19:52:58 +00:00
Carl Schwan
2a6e3c0add Remove global menu support from Android
This pull out QtWidgets which we don't need
2024-09-20 12:25:19 +00:00
Volker Krause
abb81e0d8e Update craft.ini
KF 6.6 for KColorScheme is sufficient meanwhile, but we need a newer
KQuickImageEditor to properly load with Qt6.
2024-09-20 10:49:33 +00:00
l10n daemon script
e9a4b43331 GIT_SILENT Sync po/docbooks with svn 2024-09-20 01:35:20 +00:00
Tobias Fella
08b2c39a61 Make ChooseRoomDialog a SearchPage
Makes it look a bit nicer and more standardised
2024-09-19 19:51:32 +02:00
James Graham
f89cec9c55 Make sure that m_components cannot be accessed out of bounds in closeLinkPreview
https://crash-reports.kde.org/organizations/kde/issues/67352/?project=18&query=is%3Aunresolved+issue.priority%3A%5Bhigh%2C+medium%5D&referrer=issue-stream&statsPeriod=14d&stream_index=5
2024-09-19 17:18:25 +00:00
James Graham
4c49ca2a51 NeochatRoomMember ID fallback
Make sure that when the returned RoomMember in NeochatRoomMember is empty that displayname and similar functions return the member Matrix ID

BUG: 491025
2024-09-19 15:43:02 +00:00
Tobias Fella
52ab6f484b Fix crash in MessageContentModel 2024-09-19 16:45:05 +02:00
Tobias Fella
2a5359e73b Fix NeoChatRoom::directChatRemoteMember
- Don't access member that's not there
- Use NeoChatRoomMember instead of RoomMember

BUG: 492733
2024-09-19 16:04:27 +02:00
l10n daemon script
977ea73237 GIT_SILENT Sync po/docbooks with svn 2024-09-19 01:29:31 +00:00
Tobias Fella
a769b904dc Fix compilation 2024-09-18 14:36:12 +02:00
l10n daemon script
5def5124ef GIT_SILENT Sync po/docbooks with svn 2024-09-18 01:32:34 +00:00
l10n daemon script
40c3cc7f9e GIT_SILENT Sync po/docbooks with svn 2024-09-17 01:31:27 +00:00
l10n daemon script
17c0906044 GIT_SILENT Sync po/docbooks with svn 2024-09-16 01:42:20 +00:00
James Graham
38cfc915f1 Use autoTransform on images
closes network/neochat#674
2024-09-15 10:21:54 +00:00
James Graham
1821d9fc04 Move reply pane to use MessageContentModel
Move reply pane to use MessageContentModel. This means the reply pane component is no longer required.

This commit also limits the size of code and image componets in a reply to keep them from getting too huge.
2024-09-15 08:39:22 +00:00
James Graham
ec6a8dd028 Only save eventId in MessageContentModel
Turns out trying to manage pointers in the model is a bad idea so only save eventId in MessageContentModel, events pointers will now only be obtained temporarily then discarded to avoid both creating additional copies of the event in the model and potential sources of crashes.

This also creates a basic unit test that we can add to going forward.
2024-09-15 08:28:46 +00:00
l10n daemon script
e0c3b7f808 GIT_SILENT Sync po/docbooks with svn 2024-09-15 01:32:10 +00:00
l10n daemon script
155dc4919e 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"
2024-09-15 01:23:53 +00:00
l10n daemon script
a38c53b2be GIT_SILENT Sync po/docbooks with svn 2024-09-14 01:34:50 +00:00
James Graham
67dfc7b32e Fix Eventhandler strings for translation
Change the generic representations of events in event handler to always have a full string to aid translation.

The aggregated list is then converted to be a simple list of single event generic descriptions to avoid string puzzles.

Fixes network/neochat#638

BUG: 466201, BUG: 491024
2024-09-13 17:11:50 +00:00
Tobias Fella
f156551d4f Fix compilation against changes in libQuotient 2024-09-13 15:43:10 +02:00
Tobias Fella
5c04eb85af Fix filtering users in the member list 2024-09-13 14:36:27 +02:00
l10n daemon script
1efa27177a GIT_SILENT Sync po/docbooks with svn 2024-09-13 01:30:57 +00:00
Bart Ribbers
17da652152 Don't open a room by default on mobile
Since the room window is fullscreen on mobile and you can't see the room list,
the first thing you'll be doing is backing out so you can choose the actual room you want to see
2024-09-12 15:17:38 +00:00
Tobias Fella
4ff866ea29 use kcolorscheme master 2024-09-12 13:58:46 +02:00
Tobias Fella
3c9c7abe35 Try fixing android 2024-09-12 13:58:43 +02:00
l10n daemon script
157017126a GIT_SILENT Sync po/docbooks with svn 2024-09-12 01:26:57 +00:00
l10n daemon script
928e4ae5ed 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"
2024-09-12 01:20:59 +00:00
l10n daemon script
2bc790f4cb GIT_SILENT made messages (after extraction) 2024-09-12 00:39:36 +00:00
l10n daemon script
a3148b264c GIT_SILENT Sync po/docbooks with svn 2024-09-11 01:27:19 +00:00
Carl Schwan
16b27700f5 Unify redaction handling
The code in messageeventmodel was calling to EventHandler::getBody who
already handles reaction handlings
2024-09-10 12:08:00 +00:00
Carl Schwan
9ee10b6968 Escape html from redaction message 2024-09-10 12:08:00 +00:00
l10n daemon script
4d0db0b5c2 GIT_SILENT Sync po/docbooks with svn 2024-09-10 01:26:40 +00:00
l10n daemon script
6e49aaf17b GIT_SILENT Sync po/docbooks with svn 2024-09-09 01:28:45 +00:00
Joshua Goins
1092d75f2e Remove vestigial references to window geometry
These weren't removed in d165cd955d
because I forgot.
2024-09-08 07:32:00 +00:00
Joshua Goins
8059c3797d Remove unused lambda capture variables
This removes two compile time warnings.
2024-09-08 07:24:24 +00:00
Joshua Goins
1302d62ad9 SpaceDrawer: Remove seemingly non-existent signal call 2024-09-08 07:23:58 +00:00
Joshua Goins
8eaae4034d Shorten the error message passive notification
The default timeout is a bit long, "short" is 3 seconds shorter than the
default. For  long-term network errors, we have a banner telling you so
anyway. This should hopefully reduce the notification spam when you have
temporary network dropouts.
2024-09-08 07:23:30 +00:00
l10n daemon script
354e3414a1 GIT_SILENT Sync po/docbooks with svn 2024-09-08 01:28:05 +00:00
James Graham
fc24beae6d Use im-kick-user consistently for logout.
I went with im-kick-user as it's fully red at all sizes.

BUG: 491355
2024-09-07 12:41:26 +00:00
Claire Elford
923cc67b55 Add test for missing character before a Matrix ID 2024-09-07 10:19:12 +00:00
Claire Elford
30e24069bc Fix missing character before a Matrix ID
When you send messages like "a @blankeclair:catgirl.cloud b" or
"]#rainversewiki:catgirl.cloud", they would be rendered like
"a@blankeclair:catgirl.cloud b" and "#rainversewiki:catgirl.cloud"
respectively. This commit fixes that by not matching the character before the
MXID in the regex.
2024-09-07 10:19:12 +00:00
Claire Elford
f7533a454c Fix increasing font size of certain emojis
Before this commit, NeoChat has two methods of detecting whether or not a piece
of text was an emoji. One is through a regex, and the other is by using the ICU
library. The two methods are used in different parts of the code.

This commit removes the regex detector and instead uses ICU for all the places
where NeoChat needs to figure out whether or not a string is an emoji. This
fixes increasing the font size for messages that only consist of emoji when
certain emoji are used that the regex did not handle (such as the transgender
symbol and transgender flag emojis).
2024-09-07 09:49:27 +00:00
Claire Elford
ab4e1a86dc Fix parsing self-closing tags with no space (such as <br/>)
If there was no space between the tag name and the slash of a self-closing tag,
the code assumes that the tag name is "br/". This commit adds the slash as a
character to close a tag on, so that "<br/>" is treated as a self-closing "br".

BUG: 487377
2024-09-07 12:50:18 +10:00
l10n daemon script
d28c2ed113 GIT_SILENT Sync po/docbooks with svn 2024-09-07 01:27:17 +00:00
Heiko Becker
3a467328f5 GIT_SILENT Update Appstream for new release
(cherry picked from commit b6dac3bbdf)
2024-09-07 00:48:52 +02:00
Tobias Fella
979d83cb01 Remove calls from tests 2024-09-06 08:21:06 +00:00
Joshua Goins
d165cd955d Use the new KConfig WindowStateSaver
This removes some NeoChat-specific code we have for saving/restoring the
window.
2024-09-06 08:21:06 +00:00
l10n daemon script
6eb770343e GIT_SILENT Sync po/docbooks with svn 2024-09-06 01:34:07 +00:00
James Graham
54be52b855 Fix default permissions settings
Make sure that if default permissions or basic permissons are not present in the power level event that they are set properly when changed rather than in the event section.

Also define some of the commonly used strings

BUG: 491371
2024-09-05 13:48:42 +00:00
Tobias Fella
d201333409 Don't consider events that change membership to be renames
Otherwise, the "Show rename events" flag affects the visibility of events where we don't expect it
2024-09-05 13:27:52 +02:00
Tobias Fella
3db8b4cd17 Ask for a reason when kicking a user 2024-09-05 13:22:23 +02:00
l10n daemon script
0e246a00bc GIT_SILENT Sync po/docbooks with svn 2024-09-05 01:26:38 +00:00
l10n daemon script
e638fa8929 GIT_SILENT Sync po/docbooks with svn 2024-09-04 01:26:36 +00:00
l10n daemon script
cdc982ad91 GIT_SILENT Sync po/docbooks with svn 2024-09-03 01:26:14 +00:00
Joshua Goins
bff69e21ad Add icons for the menus in the message context menu
In Qt6 we can now assign icons to menus, so let's add what's missing.
2024-09-02 17:28:03 +00:00
Joshua Goins
e117cc0cfb Move the "Show User" action to DelegateContextMenu, add it for media
This was missing from the media context menu, and should be added. Since
it's shared between messages and files, it's now a common action.
2024-09-02 17:28:03 +00:00
Joshua Goins
3af1a88e05 Don't show the "Web Search" action in the media context menu
All this does is try to web search for the filename of the image, which
is useless. Let's hide it entirely in this case.
2024-09-02 17:28:03 +00:00
Joshua Goins
aa116a35f5 Web Shortcuts: kcmshell5 does not exist anymore on Plasma 6
This is hardcoded, but it's probably a safe assumption to think most
people running modern NeoChat are using Plasma 6 anyway.
2024-09-02 17:15:37 +00:00
Joshua Goins
ac232d7f55 Change the "configure room" button icon to something more fitting
This button doesn't actually configure anything, you can do plenty of
actions like "mark as read" and such. Since the button isn't solely for
configuration, we should use an overflow menu icon instead.
2024-09-02 17:02:36 +00:00
Joshua Goins
928911e33c Settings: Overhaul the Security page
This improves the organization of this page, which is starting to become
a bit of a mess. The "Hide images and video events" option is moved
here, and the page is rebranded accordingly for "Security & Safety".
Unnecessary headings are removed, and the ignored users button is moved
to the top of the page.

Explanations for the import/keys buttons are added. The key display
is removed as it's not useful for the user (because they don't know what
to do with it) nor developers (because you can't copy it.) We can add
it back somewhere else.

This has the added benefit of making the whole page fit in the default
settings window size too.
2024-09-02 16:13:12 +00:00
Joshua Goins
e3c30f5bb3 Settings: Hide "minimize to system tray" when system tray is disabled
We do this for the state events setting, so let's do it here too.
2024-09-02 16:01:51 +00:00
Tobias Fella
4b51855528 Add name for Avatar in RecommendedSpaceDialog 2024-09-02 17:26:14 +02:00
Tobias Fella
79c27db0a9 Improve InviteUserPage 2024-09-02 16:33:45 +02:00
Tobias Fella
78e42ab352 Make it possible to invite users that were previously in the room by command 2024-09-02 15:54:40 +02:00
Tobias Fella
93909c45ee Refactor dialogs for reporting, banning, and removing messages 2024-09-02 15:18:32 +02:00
Joshua Goins
eda0bf4b23 LocationChooser: Add a "Locate" button to locate yourself
The map centers on London by default, but for the other people living
outside it may find it hard to figure out where they are. This adds a
button that calls into QtPositioning to center the map over where you
are.
2024-09-02 13:09:21 +00:00
Joshua Goins
4f4b10e0b6 LocationChooser: Add a "Re-center" action to find the pinned location
It's somewhat easy to scroll or pan away from the pin you placed, so
let's add an action to re-center the map if you want to find it again.
2024-09-02 13:09:21 +00:00
Joshua Goins
2b374a8bec Add an icon when you only have invites but no direct messages
There's an edge case with the friends icon, where it will display a
blank circle if you only have pending invites but no actual direct
messages. Now an icon is added to make it clear there is pending invites
and it's not a visual bug.
2024-09-02 12:40:54 +00:00
Tobias Fella
7821de4a8d Don't end kick messages without a reason with a ":"
BUG: 492512
2024-09-02 14:39:17 +02:00
Tobias Fella
86b88c851f Close AccountSwitchDialog when logging in a new connection
Otherwise, the dialog is still opened after login has finished
2024-09-02 14:38:33 +02:00
Tobias Fella
1157882f1b Don't register ThreadChatBarModel as a QML_ELEMENT
It's not needed and doesn't work
2024-09-02 13:36:32 +02:00
Tobias Fella
594d1373c9 Cleanup 2024-09-02 13:30:15 +02:00
Tobias Fella
1e6948bbc7 Fix crash during logout 2024-09-02 12:49:38 +02:00
Tobias Fella
41845b97d5 Fix crash on logout 2024-09-02 12:45:11 +02:00
Tobias Fella
bb56f74622 Fix initials for avatar in UserInfo 2024-09-02 10:34:39 +02:00
l10n daemon script
a98f6ac331 GIT_SILENT Sync po/docbooks with svn 2024-09-02 01:26:52 +00:00
Joshua Goins
15d6287995 LocationHelper: Move clamp from zoomToFit to QML
The OSM plugin has a different zoom tolerance than what we're hardcoding
here. This fixes the map looking funky from being too zoomed while
trying to fit multiple location points at once.
2024-09-01 18:48:20 +00:00
Joshua Goins
0242ab72e8 Allow opening locations in your preferred map
Once someone shares a location with you, typically you want to open it
in your preferred mapping application or website. For example, being
sent a location to a restaurant and needing to route it via Google Maps.

Now in NeoChat you can click on the "Open Externally" button on a
location message. On KDE Plasma, the default application can be set
under System Settings. On Android, this URI is handled by Google Maps
and possibly others.
2024-09-01 18:23:49 +00:00
Joshua Goins
4b7cbf37d5 AuthorDelegate: Don't make empty space clickable 2024-09-01 15:55:51 +00:00
Joshua Goins
0ccfe7d991 AuthorDelegate: Add pointing hand cursor to indicate you can tap
It's not immediately obvious that you can press on this static text to
bring up the user's details. This isn't a problem with the avatar - for
example - because it has a pointing hand cursor. Let's do the same here.
2024-09-01 15:55:51 +00:00
Joshua Goins
ab5585cd06 Make the SectionDelegate look just a little bit better
This has insets on it - probably from qqc2-desktop-style - and makes it
look extremely bad when scrolling through messages as the background
size doesn't match. Now the insets are set to zero, except for topInset.
This is done to work around a visual bug where you can see a one-pixel
line right above the SectionDelegate when scrolling.
2024-09-01 11:42:17 -04:00
Joshua Goins
b25a0a7a4e Settings: Hide certain pages when we have no connection
This hides the "Notifications", "Security", "Accounts" and "Devices"
page from the settings if we have no connection. This can now happen
since the user is able to enter the full settings without being logged
in from the welcome page.
2024-09-01 14:21:14 +00:00
Joshua Goins
f574c12adc WelcomePage: Redesign to center the contents, other misc improvements
We now remove the header from the page, and replace it with a separator
(it still lives with an InlineMessage for error handling.) The contents
of this page are now centered, and the maximum width of the buttons are
reduced.

Along with that are two smaller misc improvements. One is that the
duplicate separator underneath "Register" is now gone. Another is that
the full settings page may now be opened from here, allowing users to
access more than proxy settings.
2024-09-01 14:21:14 +00:00
James Graham
05a2f03c18 Make use of the proposed new KColorScheme API
Make use of new API proposed in frameworks/kcolorscheme!12
2024-09-01 14:17:41 +00:00
l10n daemon script
cbc2e65856 GIT_SILENT Sync po/docbooks with svn 2024-09-01 01:26:36 +00:00
l10n daemon script
1df80a7d2a GIT_SILENT Sync po/docbooks with svn 2024-08-31 01:27:27 +00:00
l10n daemon script
050a014df7 GIT_SILENT Sync po/docbooks with svn 2024-08-30 01:35:35 +00:00
l10n daemon script
4775ae09b0 GIT_SILENT Sync po/docbooks with svn 2024-08-29 01:27:27 +00:00
l10n daemon script
8d4c3bb4fc GIT_SILENT Sync po/docbooks with svn 2024-08-28 01:38:01 +00:00
l10n daemon script
439260ff03 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"
2024-08-28 01:25:46 +00:00
James Graham
5b04ad6805 Minor fixes for MessageContentModel and ThreadModel 2024-08-27 15:58:27 +01:00
James Graham
6d77ed1e0e Create an LRU cache for linkpreviewers
Create an LRU cache for linkpreviewers to stop the storage growing continuously as new links are made.
2024-08-27 08:15:03 +00:00
l10n daemon script
cc7f783b50 GIT_SILENT Sync po/docbooks with svn 2024-08-27 01:29:27 +00:00
James Graham
1d7ed1983b Use the ChatBar Component for new thread messages
![image](/uploads/a24dea919e7f165d602991659f517c22/image.png){width=148 height=210}

Note: there is still an issue where after starting a new thread the threaded messages only appear after a restart as the root event needs re-downloading from the server to get the thread info added. My plan is to tackle this next.
2024-08-26 19:13:19 +00:00
Tobias Fella
c0151353c5 Add an option to hide images and videos by default
Implements #658
2024-08-26 17:42:29 +02:00
l10n daemon script
72994248fa GIT_SILENT Sync po/docbooks with svn 2024-08-26 01:35:00 +00:00
l10n daemon script
af0d3d5ee1 GIT_SILENT Sync po/docbooks with svn 2024-08-25 01:26:59 +00:00
James Graham
07dd1d2e91 Make sure that the reply message is hidden if a user is ignored.
This will hide the content when a user ignores and re show it if unignored in the same session.

Note: If the client is restarted the rely will be blanked as the server refuses to send the message. However if unignoring a restart is currently required to get the full timeline back. This can't be trivially fixed as it takes a bit of time for the server to deal with the unblock and allow the message to be downloaded. With no signals available to jump off we'd just have to poll the endpoint which considering this is not going to happen often seems like a bad idea for minimal gain.

Closes network/neochat#657
2024-08-24 16:37:51 +00:00
Tobias Fella
185a88a16e Fix opening ignored users page 2024-08-24 17:32:32 +02:00
Tobias Fella
e3059e636a Fix ignoring invitations 2024-08-24 17:32:15 +02:00
Tobias Fella
aeb566746a Use plaintext for reply author component 2024-08-24 17:30:46 +02:00
Tobias Fella
8da567d9fa Don't run QtKeychain job in a nested event loop
Doing that causes deadlocks and there's no need for it here
2024-08-24 11:21:20 +02:00
Tobias Fella
d99f69cc24 Adapt to libQuotient API change 2024-08-24 11:03:28 +02:00
l10n daemon script
8240962400 GIT_SILENT Sync po/docbooks with svn 2024-08-24 01:27:32 +00:00
Tobias Fella
bd1d4289c0 Ensure that room list does not show rooms without title during startup
There's a brief moment during startup where the model knows about the rooms, but their state
is not loaded, which makes them show up in the room list with ugly fallback titles.
To prevent this, we delay closing the welcome page until the basestate is loaded.
2024-08-23 17:08:46 +02:00
James Graham
5d2139471a MessageEditComponent Updates
Rename MessageEditComponent to ChatBarComponent in preparation for also using it with threads and cleanup.
2024-08-23 14:56:01 +00:00
Tobias Fella
d49a64ac1e Remove unused MatrixImageProvider sources 2024-08-23 16:36:07 +02:00
James Graham
cd867ea581 Rework event handler to be just a series of static helper functions
- Clear out unused functions
- All functions are now static

This is because we pretty much always used it in the form:
```
EventHandler eventHandler(room, event);
eventHandler.function();
```
This simplifies it all to a single call.
2024-08-23 14:30:03 +00:00
James Graham
32fd62c484 Add option to reset all config values to their default
Closes network/neochat#504
2024-08-23 09:20:09 +00:00
l10n daemon script
f2c561bd15 GIT_SILENT Sync po/docbooks with svn 2024-08-23 01:27:31 +00:00
James Graham
80734944d3 Use sections for power levels in the user list
Closes network/neochat#654
2024-08-22 19:34:31 +00:00
James Graham
b3afa9f595 Add delegates to show room upgrades into the timeline model.
The delegates are at the beginning for upgraded rooms and end for predecessors.

Closes: network/neochat#620 and network/neochat#619
2024-08-22 17:21:36 +00:00
l10n daemon script
656558850c GIT_SILENT Sync po/docbooks with svn 2024-08-22 01:30:41 +00:00
Tobias Fella
e41cca9be0 Make room leaving more robust 2024-08-21 20:31:33 +02:00
Laurent Montel
46916b34d4 Remove duplicate headers from headers/cpp 2024-08-21 12:28:57 +00:00
l10n daemon script
4f9ca3e74d GIT_SILENT Sync po/docbooks with svn 2024-08-20 01:36:45 +00:00
l10n daemon script
2bcdd0f52b GIT_SILENT Sync po/docbooks with svn 2024-08-19 01:30:37 +00:00
James Graham
56d790dda9 Thread View
So at the moment this remains behind the feature flag as this only adds a threadmodel and a basic visualisation. There is much more to come to get it ready for full release.
2024-08-18 15:19:03 +00:00
James Graham
149013d2ff Fix pending events not showing when the server event turns up 2024-08-18 15:26:31 +02:00
Tobias Fella
776807580a Re-add pending event indicator
This seems to have been accidentally removed

BUG: 491277
2024-08-18 15:26:30 +02:00
Tobias Fella
75e9eee3a9 Fix pending events all showing the same text
The content models were stored in the hasmap under the same key, since they all don't have a valid event id yet.
Store them under their transaction id instead.

BUG: 491277
2024-08-18 15:26:28 +02:00
l10n daemon script
183615fa7b GIT_SILENT Sync po/docbooks with svn 2024-08-16 01:25:55 +00:00
Heiko Becker
7405fbaa16 GIT_SILENT Update Appstream for new release
(cherry picked from commit 86d85c6ce7)
2024-08-16 00:35:07 +02:00
Andreas Sturmlechner
22743b6d8b Include missing ECMQmlModule
Amends bc67033c00 and e0c3a1c143

No idea why this isn't caught by CI, but it fails for me otherwise.

Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2024-08-15 19:47:13 +00:00
l10n daemon script
b5864f02cb GIT_SILENT Sync po/docbooks with svn 2024-08-15 01:28:27 +00:00
Julius Künzel
373431fb1a Add job to publish to Microsoft Store
This still requires a manual action in the MS Partner Center, but makes the submission easier.
Note that on purpose the job will only be available on release branches, not on master
2024-08-10 10:38:18 +02:00
Julius Künzel
29167fbdf2 Enable appx build for Windows 2024-08-10 10:38:16 +02:00
Tobias Fella
699ac3a8e6 Remove undefined function declarations 2024-08-10 10:35:38 +02:00
l10n daemon script
d7b572faaf 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"
2024-08-10 01:26:10 +00:00
l10n daemon script
a09b4e7f2c GIT_SILENT made messages (after extraction) 2024-08-10 00:41:18 +00:00
l10n daemon script
a794420d3e 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"
2024-08-09 01:21:45 +00:00
l10n daemon script
8b606266c5 GIT_SILENT made messages (after extraction) 2024-08-09 00:39:31 +00:00
l10n daemon script
11e4329d5e GIT_SILENT Sync po/docbooks with svn 2024-08-07 01:28:10 +00:00
James Graham
68dfc6ca81 Show notification counts on the room Avatars when collapsed.
Show notification counts on the room Avatars when collapsed.

BUG: 468520
2024-08-06 20:51:16 +00:00
l10n daemon script
e0c8945431 GIT_SILENT Sync po/docbooks with svn 2024-08-06 01:27:29 +00:00
l10n daemon script
fe60ee00fb GIT_SILENT Sync po/docbooks with svn 2024-08-04 01:27:54 +00:00
l10n daemon script
5990e00577 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"
2024-08-04 01:20:55 +00:00
l10n daemon script
358a699290 GIT_SILENT Sync po/docbooks with svn 2024-08-03 01:28:22 +00:00
l10n daemon script
da6e4378d9 GIT_SILENT Sync po/docbooks with svn 2024-08-02 01:30:14 +00:00
l10n daemon script
5edb0629b3 GIT_SILENT Sync po/docbooks with svn 2024-08-01 01:31:30 +00:00
Tobias Fella
5170854a2c Add UI for importing and exporting megolm keys 2024-07-31 23:05:54 +02:00
James Graham
37d6033df4 Manage MessageContentModels properly so we don't leak memory.
- Manage MessageContentModels properly so we don't leak memory creating new ones every time the role is refreshed.
- Parent and reply MessageContentModels to their message to make sure they get cleaned up when the parent is deleted.
- Make sure ReactionModels are cleaned up on room change to stop that list just growing.
2024-07-31 17:57:11 +00:00
l10n daemon script
ea2f891533 GIT_SILENT Sync po/docbooks with svn 2024-07-30 01:30:47 +00:00
Tobias Fella
42cec7d5ba Show time without timezone in tooltip
Constructing the timezone string is relatively heavy and is done for each delegate, even when the tooltip is never shown
2024-07-29 17:46:27 +02:00
Tobias Fella
2df2e39d43 Show time without seconds in timeline 2024-07-29 17:46:15 +02:00
l10n daemon script
b34525a4d8 GIT_SILENT Sync po/docbooks with svn 2024-07-29 01:29:11 +00:00
l10n daemon script
0a7bd64b0d GIT_SILENT Sync po/docbooks with svn 2024-07-28 01:30:28 +00:00
James Graham
11fd4f88ec Create NeochatRoomMember as a shim for RoomMember
The intention is that NeochatRoomMember can be created passed to QML and then be fully managed by it. It effectively just grabs the current RoomMember, calls the correct function then discards it so that we don't end up trying to access an already deleted state event.
2024-07-27 08:46:56 +00:00
l10n daemon script
4d9452b862 GIT_SILENT Sync po/docbooks with svn 2024-07-27 01:30:45 +00:00
Joshua Goins
060e30e612 Suggest what to do on the empty welcome screen
A simple change, adds a sentence to the "Welcome to NeoChat" message
when it first starts up and you have nothing selected.
2024-07-26 19:32:10 +00:00
Joshua Goins
ade7270add ThreePIdCard: Use Title Case and add an icon for the "Add" button 2024-07-26 19:10:30 +00:00
Joshua Goins
338428b0c0 AccountEditor: Improve strings a little
Changes things to Title Case as necessary, and shortens the QR code
option to be more succinct.
2024-07-26 19:10:30 +00:00
Joshua Goins
54574e4450 AccountEditor: Add placeholder text for "Label"
This makes it much clearer what possible purpose this field could do.
2024-07-26 19:10:30 +00:00
Joshua Goins
ae564451b8 AccountEditor: Add icons to all of these form card buttons
This helps make them stand out apart from the text they are surrounded
by.
2024-07-26 19:10:30 +00:00
Joshua Goins
9a4504ce61 Make the "Unignore this user" button work on the Ignored Users page
This function now takes a QString. This should be fine to use since it's
in the 0.8.2 release which we require.
2024-07-26 19:01:19 +00:00
Joshua Goins
80785a2ff3 Use a Kirigami.PlaceholderMessage in the ignored list
This makes it look cleaner when there's nothing there, and looks
 standard beside other KDE applications. Also removes a duplicate
"Ignored Users" form header that didn't add anything.
2024-07-26 19:01:19 +00:00
Joshua Goins
cb8ed02e82 Fix the emoji page not doing anything
This is yet more stuff broken due to referencing a implicit pageStack
that no longer exists.
2024-07-26 18:55:23 +00:00
Joshua Goins
a64c1b8eab Add a "Show QR code" to the account menu
Making this a couple of clicks away under the account menu should make
it easier to show the QR code without digging through the settings.
2024-07-26 18:44:05 +00:00
Tobias Fella
d43aa169c3 Use let kconfig register the config class 2024-07-26 19:25:55 +02:00
Tobias Fella
8ae7141851 Use cmake function instead of kcfgc 2024-07-26 19:25:00 +02:00
Joshua Goins
d3908db2c9 Fix Carl's avatar in the about data
This is supposed to be a QUrl to catch the correct overload.
2024-07-26 07:41:21 +00:00
Yuri Chornoivan
55de838a55 Fix minor typo 2024-07-26 08:36:41 +03:00
l10n daemon script
4cdfc38b87 GIT_SILENT Sync po/docbooks with svn 2024-07-26 01:26:00 +00:00
Joshua Goins
51197d7c1a Don't flag invite notifications as persistent
These really don't need to be persistent, as they even stick around
when NeoChat is closed. This also spams the user's notification system
usually, if they get lots of invitations at once which don't go away
automatically.
2024-07-25 20:11:50 +00:00
Joshua Goins
2a2a2e0c05 Hint that the user can auto-reject invitations on the invite page
This should help make the new setting more discoverable.
2024-07-25 19:58:58 +00:00
Joshua Goins
07fee30cc0 Allow blocking invites from people you don't share a room with
Matrix currently has a significant moderation loophole, thanks to
invites. Right now, anyone can invite anyone to a room - and clients
like NeoChat will gladly display these rooms to them and even give you
a notification.

However, this creates a pretty easy attack since room names and avatars
are arbitrary and this is a known vector of harassment in the Matrix
community. There's currently no tools to block this server-side, so
let's try to improve the situation where we can.

This adds a new setting to the Security page, wherein it allows you to
block invites from people you don't share a room with. This prevents the
notification from appearing and NeoChat will attempt to leave the room
immediately.

Since this depends on MSC 2666 - a currently unstable feature - the
server may not support it and NeoChat will disable the setting in this
case.
2024-07-25 19:58:58 +00:00
Joshua Goins
83c6ce0ace Don't display notifications for invite rooms
Sometimes a ghost notification will appear, this is sometimes a stray
m.room.invite notification we didn't handle or some other event. Let's
outright reject all notifications from Invite-type rooms to prevent this
from happening altogether.
2024-07-25 15:35:12 -04:00
Joshua Goins
fb7303efa0 Revert "Allow blocking invites from people you don't share a room with"
This reverts commit ef5585d312. This was
supposed to be in an MR.
2024-07-25 15:04:53 -04:00
Joshua Goins
ef5585d312 Allow blocking invites from people you don't share a room with
Matrix currently has a significant moderation loophole, thanks to
invites. Right now, anyone can invite anyone to a room - and clients
like NeoChat will gladly display these rooms to them and even give you
a notification.

However, this creates a pretty easy attack since room names and avatars
are arbitrary and this is a known vector of harassment in the Matrix
community. There's currently no tools to block this server-side, so
let's try to improve the situation where we can.

This adds a new setting to the Security page, wherein it allows you to
block invites from people you don't share a room with. This prevents the
notification from appearing and NeoChat will attempt to leave the room
immediately.

Since this depends on MSC 2666 - a currently unstable feature - the
server may not support it and NeoChat will disable the setting in this
case.
2024-07-25 15:03:22 -04:00
James Graham
11475259a1 Make sure that apostrophes are unescaped when visualising messages
Title

BUG: 488325
2024-07-23 17:27:00 +00:00
l10n daemon script
9df4cd6f13 GIT_SILENT Sync po/docbooks with svn 2024-07-23 01:27:45 +00:00
Albert Astals Cid
c9583964c8 GIT_SILENT Upgrade release service version to 24.11.70. 2024-07-21 12:54:43 +02:00
l10n daemon script
5022ed1a63 GIT_SILENT Sync po/docbooks with svn 2024-07-21 01:27:43 +00:00
James Graham
799ffa18c4 Fix the javascript when using .? 2024-07-20 22:35:41 +00:00
Tobias Fella
6323e27040 Remove customemojimodel_p.h
It's been unused for a long time
2024-07-20 22:49:22 +02:00
Tobias Fella
03acb26109 Fix some clazy warnings 2024-07-20 22:38:45 +02:00
Tobias Fella
34fc1f6a6b Fix text alignment in ReadMarkerDelegate 2024-07-20 21:54:55 +02:00
James Graham
042032ec46 Update user sort
Update the user model so it also sorts by power level and update how we initialize the model to improve performance.

The following is also changed:
- Store a single `UserListModel` in `RoomManager` and use it for everything, this means we don't create extra models (incluiding the long initialisation for each in big rooms)
- By using the single model once it has loaded the users of the new room opening and closing the draw now happens instantly (previously the model would have to be loaded every time the drawer was opened).
- To stop the initial loading and room change of Neochat slowing down (as the `UserListModel` would be loaded before the `TimelineView` is shown) the initialisation of the model is delayed until the `TimelineView` has loaded. This prioritises showing some messages in the timeline over populating the model so in large rooms the user list will initially be blank, but this keeps the initial load snappier.
2024-07-20 18:12:30 +00:00
James Graham
73de99f661 Create a list model for readmarkers
Create a list model for read markers. The primary reason is to stop `RoomMembers` being accessed after their state event is deleted. With this the read marker doesn't pass and `RoomMember` objects to qml.
2024-07-20 18:05:15 +00:00
l10n daemon script
cc068f9ebb GIT_SILENT Sync po/docbooks with svn 2024-07-19 01:26:38 +00:00
l10n daemon script
2bfc2b1944 GIT_SILENT Sync po/docbooks with svn 2024-07-18 01:23:41 +00:00
l10n daemon script
029936112f GIT_SILENT made messages (after extraction) 2024-07-18 00:39:13 +00:00
Andreas Gattringer
d5e400e8a4 stop video on destruction to avoid segfault 2024-07-17 11:08:53 +00:00
Andreas Gattringer
9dbb9c3f3b fix filedownload on maximized view and context menu
- move the logic for remembering local filenames from
  messagecontentmodel to neochatroom
- use it also when maximized and in context menu opening, to stop
  re-downloading
- make onFileTransferCompleted signal connection one-shot (stops memory
  leak)
2024-07-17 08:29:29 +00:00
l10n daemon script
a101e6b0a7 GIT_SILENT Sync po/docbooks with svn 2024-07-17 01:24:34 +00:00
Andreas Gattringer
c525ea55ce fix typo in PollComponent that causes polls to not be checked correctly 2024-07-16 21:41:25 +02:00
Andreas Gattringer
8ca45f298f show thumbail when video is stopped 2024-07-16 12:27:35 +02:00
Andreas Gattringer
cb4c6cb677 Audio filename, caption and seeking
- display caption independent of filename (consistancy to File)
- fix audio seeking not working
- change two comments and a variable name
2024-07-16 12:27:35 +02:00
Andreas Gattringer
d574a97a35 take the correct filename for synthax highlighting in code block 2024-07-16 12:27:35 +02:00
Andreas Gattringer
20f9a86ad9 stop assigning undefined to bool in TimelineView and MessageDelegate 2024-07-16 12:27:35 +02:00
Andreas Gattringer
a4a411cf1f correctly display filename in MimeComponents
(previously it would include the caption text and save with it as
filename)
Don't append filename to the caption.

Relevant spec: https://spec.matrix.org/latest/client-server-api/#mfile
2024-07-16 12:27:35 +02:00
Andreas Gattringer
029bda5734 fix code preview components
- fix them vanishing after "opening" a file (they get downloaded to
  /tmp/ without extension, which caused them to lose preview)
- make all text/plain mimetype files preview
- don't show them in Replies (in consistency with media components)
2024-07-16 12:27:35 +02:00
Andreas Gattringer
0fd578e6aa Refactor MimeComponent
- Fix size and duration not showing in MimeComponents in replies
- Display Itinerary in replies as MimeComponent (in consistency with media
  components)
2024-07-16 12:27:35 +02:00
l10n daemon script
95e1bee5e6 GIT_SILENT Sync po/docbooks with svn 2024-07-16 01:23:40 +00:00
Tobias Fella
c8dc10f311 Don't escape display name for leave events in subtitle 2024-07-15 22:04:32 +02:00
l10n daemon script
a00f4e393b GIT_SILENT Sync po/docbooks with svn 2024-07-15 01:26:14 +00:00
l10n daemon script
5a7dea8857 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"
2024-07-15 01:18:59 +00:00
Andreas Gattringer
27e8970fff fix segfault at loadError from message with attached file 2024-07-14 19:46:04 +00:00
Andreas Gattringer
7848274ba1 fix resetting of message content components
and don't reset whole Moessage content when author changes
2024-07-14 18:25:02 +02:00
l10n daemon script
145bf0298b GIT_SILENT Sync po/docbooks with svn 2024-07-14 01:23:33 +00:00
James Graham
0392a33b54 Content Model Author
Make sure that the Author in the content model is only ever updated int he following circumstacnes:
- The member updates their displayname or avatar
- A previously hidden author line is shown

This is done by only doing a full reset on initialisation or when a previously hidden author is shown. The rest of the time we only reset the other content in the message, i.e. everything below the author line.
2024-07-13 16:01:42 +00:00
Andreas Gattringer
b211f46e3e Do heavy things less often on room change event 2024-07-13 15:22:52 +00:00
James Graham
709711c3ca Fix the InlineMessages for the room upgrades in RoomSettings
Fix the InlineMessages for the room upgrades in RoomSettings, they should show even if we aren't currently in the room. Resolve resource can join the predecessor/sucessor if required.

Closes network/neochat#618
2024-07-13 15:14:50 +00:00
l10n daemon script
841607406f GIT_SILENT Sync po/docbooks with svn 2024-07-13 01:25:56 +00:00
Andreas Gattringer
70b726b04d reset currentRoom before opening last room of connection 2024-07-12 07:41:25 +00:00
l10n daemon script
2799248106 GIT_SILENT Sync po/docbooks with svn 2024-07-12 01:24:11 +00:00
Carl Schwan
2321299084 Pass ApplicationWindow to AccountMenu 2024-07-11 20:55:04 +00:00
Andreas Gattringer
aa00773a3b update only position of HoverActions when contentY of TimelineView
changes
2024-07-11 14:06:58 +02:00
l10n daemon script
733de1d0e1 GIT_SILENT Sync po/docbooks with svn 2024-07-11 01:24:16 +00:00
Tobias Fella
c16e4ad412 Fix opening pages from account menu 2024-07-10 21:39:15 +02:00
l10n daemon script
d1cf7a07f1 GIT_SILENT Sync po/docbooks with svn 2024-07-10 01:25:42 +00:00
James Graham
8751f6fea7 Never refresh the author role except when a member updated signal is generated. This should remove a potential source of crashes where a RoomMember object tries to access an already deleted room member state event 2024-07-09 20:21:13 +01:00
Andreas Gattringer
ea1b577ec7 fix room settings not opening from RoomDrawerPage, by launching it according to RoomDrawer 2024-07-09 10:49:18 +02:00
l10n daemon script
a41afa70eb GIT_SILENT Sync po/docbooks with svn 2024-07-08 01:23:33 +00:00
Tobias Fella
e11c97cdc0 Move NeoChatRoom::showMessage to Controller
It doesn't fit into NeoChatRoom conceptually
2024-07-07 13:44:55 +02:00
l10n daemon script
d76e44512e GIT_SILENT Sync po/docbooks with svn 2024-07-07 01:24:08 +00:00
l10n daemon script
fd1931377d 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"
2024-07-07 01:17:56 +00:00
James Graham
68ce6c4ed7 It seems like it's possible for contentModel.Author to be accessed before the contentModel is set so make sure this fails nicely 2024-07-06 20:05:16 +00:00
Carl Schwan
a2ee330307 Use new SearchDialog from Kirigami 2024-07-06 18:16:43 +00:00
Tobias Fella
c3a17f951e Only enable taphandler for mouse clicks
BUG: 486545
2024-07-06 20:11:21 +02:00
Nicolas Fella
443c0f34d7 Rework Add Server dialog
Use plain items instead of FormCard delegate items, this isn't a FormCard

Set placeholder text for textfield

Remove redundant subtitle

Use inlinemessage for errors

Before:

![image.png](/uploads/375d3c668271f6bce699cc9462f6deb9/image.png){width=405 height=247}

After:

![image.png](/uploads/b8d79a4fdb0914d1f82ecec0760ad80f/image.png){width=404 height=234}
2024-07-06 18:10:34 +00:00
Tobias Fella
8725600368 Port away from deprecated QDateTime constructor 2024-07-06 20:00:04 +02:00
Tobias Fella
3b83b7f190 Set Qt policy 4 2024-07-06 20:00:04 +02:00
Tobias Fella
e0c3a1c143 Port to ecm_add_qml_module 2024-07-06 19:50:20 +02:00
Tobias Fella
bec1ad7bee Fix crashes when logging out of connection 2024-07-06 19:44:00 +02:00
Tobias Fella
9e6c00f78c Fix logout from account menu 2024-07-06 19:28:54 +02:00
Tobias Fella
abbbd9d705 Use RoomMember object for code component 2024-07-06 16:27:08 +02:00
Tobias Fella
4a5c012f05 Fix avatar in chatbar 2024-07-06 16:22:03 +02:00
Tobias Fella
319862b3d4 Fix avatars in full window image view 2024-07-06 16:20:16 +02:00
Tobias Fella
1fcf58024d Fix avatars in CodeMaximizeComponent 2024-07-06 16:18:49 +02:00
Tobias Fella
9db162d0fc Fix avatar in state delegates 2024-07-06 16:16:44 +02:00
Tobias Fella
13b15390c3 Fix crashes due to event being deleted 2024-07-06 15:54:29 +02:00
l10n daemon script
799b62e9d2 GIT_SILENT Sync po/docbooks with svn 2024-07-06 01:25:14 +00:00
Tobias Fella
d91ed535ad Don't access event after it was deleted
- NeoChat stores pointer to event
- Event is replaced
- libQuotient deletes the event and notifies us that the event changes
- We're accessing the event to check its id
- Boom

Make this not boom by accessing the ID that we're additionally storing anyway.
2024-07-05 16:54:38 +02:00
l10n daemon script
c48b9874bf GIT_SILENT Sync po/docbooks with svn 2024-07-04 01:22:48 +00:00
l10n daemon script
6a788f6c32 GIT_SILENT Sync po/docbooks with svn 2024-07-03 01:26:09 +00:00
Tobias Fella
53519a604e Don't store member objects in userlistmodel 2024-07-02 18:17:27 +02:00
l10n daemon script
2818b87f02 GIT_SILENT Sync po/docbooks with svn 2024-07-02 01:24:50 +00:00
l10n daemon script
50e10133b9 GIT_SILENT Sync po/docbooks with svn 2024-07-01 01:23:04 +00:00
James Graham
068161719e Update author documentation now we've moved to RoomMember 2024-06-30 17:59:40 +01:00
James Graham
c8d5d095e0 REmove unneeded includes 2024-06-30 17:59:40 +01:00
James Graham
52d07320ef Make the author line in the bubble and reply be part of the content model 2024-06-30 17:59:40 +01:00
Tobias Fella
158b9ea2ca Fix opening QR code 2024-06-30 18:06:57 +02:00
James Graham
6a100bfbab When switching rooms first set the room to nullptr to clear any objects in MessageEventModel and UserListModel 2024-06-30 14:24:21 +00:00
Tobias Fella
b3bd6ee176 Fix showing avatar image in HiddenDelegate 2024-06-30 15:53:02 +02:00
Tobias Fella
04696b27eb Use type for member property 2024-06-30 15:53:02 +02:00
Tobias Fella
332937dbc1 Register RoomMember as foreign type 2024-06-30 15:52:45 +02:00
l10n daemon script
92d932ce5c GIT_SILENT Sync po/docbooks with svn 2024-06-30 01:25:04 +00:00
Carl Schwan
af3c4f536a Fix dev tools 2024-06-29 18:03:43 +02:00
Carl Schwan
b98ca5af52 Use SpellcheckingConfigurationModule 2024-06-29 18:03:43 +02:00
Carl Schwan
a15a11f44b Rework config dialog
Use new KirigamiAddons ConfigurationsView as a replacement for
CategorizedSettings. Fix some issues on desktop when running Qt 6.8
and some less recent issues on mobile.

Visually this looks the same.
2024-06-29 18:03:40 +02:00
Tobias Fella
1460132772 Fix typing indicator 2024-06-29 17:30:29 +02:00
Tobias Fella
76d68bb3e4 Fix tests 2024-06-29 17:30:29 +02:00
Tobias Fella
08df0ebbea user details dialog fixes 2024-06-29 17:30:29 +02:00
Tobias Fella
e918db2cc1 Port away from function that we can't use yet 2024-06-29 17:30:29 +02:00
James Graham
2e0dc8db94 Make sure that the member objects get updated for the MessageEventModel and MessageContentModel when the user updates their avatar or name 2024-06-29 17:30:29 +02:00
James Graham
b78a9f2a9c Find users by Id in UserListModel as there may be users with the same display name which will end up with the wrong member replaced 2024-06-29 17:30:28 +02:00
James Graham
832e6b9de0 Make sure that the member object gets switched when a member's avatar or name is updated because the old state event will now be deleted and we need to ref the new one. 2024-06-29 17:30:28 +02:00
James Graham
feec7ca408 Don't store RoomMembers in ReactionModel
Don't store RoomMember objects in the reaction model just grab them everytime incase the state event is replaced.
2024-06-29 17:30:28 +02:00
James Graham
408f0a12e2 Use new libquotient functionality to encrypt direct chats by default
Needs https://github.com/quotient-im/libQuotient/pull/730
2024-06-29 17:30:28 +02:00
James Graham
35ab4e6e09 Use updated membersTyping functions from libquotient 2024-06-29 17:30:28 +02:00
James Graham
430bafafe7 Make use of new RoomMember item from libquotient
Depends on https://github.com/quotient-im/libQuotient/pull/695

Currently basic just to show a working implementation using RoomMember. Currently only the room event and search models are moved over. Will change everything else over once the dependent pr is complete.
2024-06-29 17:30:28 +02:00
James Graham
58d727350d Remove uses of Quotient:Omittable
Note this technically won't build for now because of the lack of RoomMember support but I'll push that at the quotient-next branch next. 

This is needed as well to get a branch that builds on dev.
2024-06-29 17:30:04 +02:00
l10n daemon script
c6a4057659 GIT_SILENT Sync po/docbooks with svn 2024-06-29 01:24:06 +00:00
Heiko Becker
60ac806690 GIT_SILENT Update Appstream for new release
(cherry picked from commit c00debf1c3)
2024-06-28 23:18:10 +02:00
James Graham
e9263fc596 Use the new libquotient version of the serveracl event
Use with https://github.com/quotient-im/libQuotient/pull/729
2024-06-28 15:02:56 +02:00
Tobias Fella
3dd28a0382 Unconditionally use SSSS 2024-06-28 14:33:23 +02:00
l10n daemon script
066cb8184e GIT_SILENT Sync po/docbooks with svn 2024-06-28 01:33:31 +00:00
l10n daemon script
373e22b999 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"
2024-06-28 01:20:45 +00:00
Tobias Fella
fd8725f649 Add basic cross-signing support
(cherry picked from commit e1795076c8c41a34b91c31df35d1e344d0b14887)
2024-06-27 18:51:38 +02:00
Tobias Fella
3d433762b1 Fix ifs for ssss 2024-06-27 18:23:29 +02:00
Tobias Fella
ea4cb5bf62 Disable FreeBSD CI 2024-06-27 18:08:47 +02:00
Tobias Fella
a6ca3b8203 Require libQuotient 0.8.2 2024-06-27 18:08:28 +02:00
l10n daemon script
bc4ceb6d52 GIT_SILENT Sync po/docbooks with svn 2024-06-27 01:26:42 +00:00
l10n daemon script
24480229cd GIT_SILENT Sync po/docbooks with svn 2024-06-26 01:25:49 +00:00
l10n daemon script
8b10573197 GIT_SILENT Sync po/docbooks with svn 2024-06-25 01:23:09 +00:00
l10n daemon script
cbc81e8285 GIT_SILENT Sync po/docbooks with svn 2024-06-24 01:22:01 +00:00
Tobias Fella
0b9a978061 Update global menu 2024-06-23 21:09:33 +02:00
l10n daemon script
e974e5d13b GIT_SILENT Sync po/docbooks with svn 2024-06-23 01:25:25 +00:00
l10n daemon script
5a42e86bf6 GIT_SILENT Sync po/docbooks with svn 2024-06-22 01:29:39 +00:00
l10n daemon script
889946e186 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"
2024-06-22 01:19:19 +00:00
l10n daemon script
5c47e8044e GIT_SILENT made messages (after extraction) 2024-06-22 00:39:08 +00:00
l10n daemon script
1a974ac305 GIT_SILENT Sync po/docbooks with svn 2024-06-21 01:23:47 +00:00
Tobias Fella
db1bf61805 GlobalMenu: remove shortcut for QuickSwitcher
The shortcut needs to work when there is no Global Menu, so it's also in QuickSwitcher.qml.
It can't be in both places, since that breaks it. So we remove it here.

BUG: 488212
2024-06-20 18:14:36 +02:00
Tobias Fella
5456b4a7ff Fix global menu 2024-06-20 18:03:42 +02:00
l10n daemon script
a08ffaae77 GIT_SILENT Sync po/docbooks with svn 2024-06-20 01:27:52 +00:00
l10n daemon script
18445f55f0 GIT_SILENT Sync po/docbooks with svn 2024-06-19 01:24:36 +00:00
Albert Astals Cid
2daf3b5c4b CI: Disable requiring Windows tests passing
Has been broken for 4 consecutive weeks
2024-06-18 22:42:00 +02:00
l10n daemon script
923b844212 GIT_SILENT Sync po/docbooks with svn 2024-06-18 01:29:17 +00:00
James Lyne
0aec9f8472 Fix search results on room search page
Add missing IsEditable role to search model
2024-06-17 20:15:36 +00:00
l10n daemon script
23b0c8a143 GIT_SILENT Sync po/docbooks with svn 2024-06-17 01:25:34 +00:00
l10n daemon script
3c2c2e2bd8 GIT_SILENT Sync po/docbooks with svn 2024-06-16 01:24:54 +00:00
l10n daemon script
c1465a7368 GIT_SILENT Sync po/docbooks with svn 2024-06-14 01:22:57 +00:00
l10n daemon script
be1dadab74 GIT_SILENT Sync po/docbooks with svn 2024-06-13 01:23:33 +00:00
Derry Tutt
1cba39eae9 Update strings to be more clear for the average user 2024-06-12 19:37:39 +00:00
Tobias Fella
a0c8bdf021 Make notifications more useful
- Refactor and cleanup code
- Don't paginate through notifications - it spams the server with requests and realistically only contains anything relevant on startup after a long time, in which case you're going to lose some notification anyway
- Only show newest notification for the respective room, closing the old notification if one exists
- Only show text of this notification

BUG: 475228
2024-06-12 21:28:09 +02:00
l10n daemon script
6fdb22a5b5 GIT_SILENT Sync po/docbooks with svn 2024-06-12 01:26:41 +00:00
l10n daemon script
19c370a273 GIT_SILENT made messages (after extraction) 2024-06-12 00:38:48 +00:00
Tobias Fella
861336ea97 Remove unnecessary check 2024-06-11 21:48:56 +02:00
l10n daemon script
24219bcb03 GIT_SILENT Sync po/docbooks with svn 2024-06-11 01:29:06 +00:00
Tobias Fella
77ed762e2c Use plaintext for room aliases 2024-06-10 22:20:37 +02:00
l10n daemon script
d45b6cb03d GIT_SILENT Sync po/docbooks with svn 2024-06-10 01:35:39 +00:00
Heiko Becker
9a921b2e0d GIT_SILENT Update Appstream for new release
(cherry picked from commit c5c47d7b67)
2024-06-10 00:48:21 +02:00
l10n daemon script
c17e213e11 GIT_SILENT Sync po/docbooks with svn 2024-06-09 01:23:44 +00:00
Joshua Goins
6275d7afaa Switch from QQC2.ApplicationWindow.overlay to QQC2.Overlay.overlay
Closes #648
2024-06-08 11:47:30 -04:00
Joshua Goins
364eda6400 Fix keyboard navigation on search pages
Some of our search pages (such as the room and user search) has a list
header item. Due to how this works, it's not actually a part of the
list view keyboard navigation and a whole separate item. So in the tab
order, it comes *after* the list view which makes no sense. And it's
part of the list view, so users must expect it to be selectable with the
up and down arrows like other items.

This simple change makes it so it behaves as expected. The first actual
list item is selected by default, but it's possible to navigate to the
list header item via the up arrow key and then return to the list view
using the down arrow. The list header item is also removed from the tab
order and the whole page is much nicer to use now.
2024-06-08 15:44:38 +00:00
Joshua Goins
ccf34cfe20 The "Search Room" action should be called "Search Rooms" 2024-06-08 15:42:10 +00:00
Joshua Goins
7daae6a2d9 Fix the tooltips for the two drawer buttons at the top
One of them didn't even have a tooltip, which is a simple oversight
since it already has accessible text. The tooltips now use the attached
property instead of creating a new QQC2.ToolTip too.
2024-06-08 15:42:10 +00:00
Joshua Goins
277a4ad124 Fix keyboard navigation in space drawer
Some of the items were able to activated via the keyboard, but many were
not like the notifications and "create a space" buttons. This is because
the signals were hooked up to onClicked but the accessible and keyboard
nav were hooked up to onSelected. All of the buttons trigger their
actions with onSelected now.
2024-06-08 15:32:37 +00:00
Joshua Goins
b11d46e34a Add keyboard navigation for server selection in room search dialog
This was previously not keyboard navigable at all, making it
impossible to switch servers in this dialog solely with a keyboard. This
patch makes it possible to do some basic selection but not deletion yet,
but it's a good start.
2024-06-08 15:32:26 +00:00
Joshua Goins
e8ad0a055d Remove room member highlight on click
Previously it was possible to keep clicking and highlighting each member
which doesn't make any sense. We could make this exclusive by having it
highlight only when index == currentIndex, but honestly it doesn't need
to be highlighted at all. Clicking on a room member opens their user
card, there's no persistent state the user needs to keep track of here.
2024-06-08 11:25:49 -04:00
Joshua Goins
8a8c745d77 Use Qt.alpha in ThemeRadioButton
This was newly added in Qt6 and simplifies a Qt.rgba call we used here.
2024-06-08 14:35:09 +00:00
Joshua Goins
a523fe7674 Add focus border for the theme radio button, used on the Appearance page
Otherwise it's impossible to tell which option you're on, if you're
solely using a keyboard.
2024-06-08 14:35:09 +00:00
Joshua Goins
dc9a150929 Fix QR code not showing when tapping the button under account settings 2024-06-08 14:34:58 +00:00
Joshua Goins
be66ffef0f Fix map copyright link activation
The argument was missing, so it wasn't possible to actually click and
visit the copyright notices linked on maps.
2024-06-08 14:34:48 +00:00
Joshua Goins
f278cc0c86 Don't show the map if there's no locations available
It's hard to the read the text when there's a beige map behind it, and
unnecessary anyway.
2024-06-08 14:34:48 +00:00
Nicolas Fella
7f72808a9a Fixup AttachDialog
Use standard spacing values

Use implicit button size
2024-06-08 14:34:40 +00:00
Joshua Goins
1d5297c0f0 Rename the header for room actions "Actions" instead of "Options"
These aren't really configurable options in the usual sense, but rather
actions you can take in the room.
2024-06-08 14:34:21 +00:00
Joshua Goins
e40528ba45 Use a more natural sounding action name for favoriting the room
"Make room favorite" doesn't sound very natural in English, but
"Favorite this room" is and fits in with the rest of the actions here.
2024-06-08 14:34:21 +00:00
Tobias Fella
29972b5867 Port away from commitSingleShot 2024-06-08 15:43:16 +02:00
James Graham
91109ca845 Get 3PID binds on startup
Get binds on startup and update the staus properly after bind/unbinding 3PIDs
2024-06-08 08:42:34 +00:00
l10n daemon script
8e5ccb5461 GIT_SILENT Sync po/docbooks with svn 2024-06-08 01:27:06 +00:00
l10n daemon script
8a75967953 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"
2024-06-08 01:19:18 +00:00
l10n daemon script
3f4965b182 GIT_SILENT made messages (after extraction) 2024-06-08 00:39:04 +00:00
l10n daemon script
2ac266029c GIT_SILENT Sync po/docbooks with svn 2024-06-07 01:22:56 +00:00
l10n daemon script
3261231d07 GIT_SILENT made messages (after extraction) 2024-06-07 00:38:46 +00:00
l10n daemon script
95ce6385b0 GIT_SILENT Sync po/docbooks with svn 2024-06-05 01:36:19 +00:00
Volker Krause
64c5894602 Fix notifyrc file location 2024-06-04 15:01:54 +00:00
l10n daemon script
8c4839c300 GIT_SILENT Sync po/docbooks with svn 2024-06-04 01:22:51 +00:00
l10n daemon script
80c2bc1a52 GIT_SILENT Sync po/docbooks with svn 2024-06-03 01:23:01 +00:00
l10n daemon script
bcd417e039 GIT_SILENT Sync po/docbooks with svn 2024-06-02 01:24:06 +00:00
l10n daemon script
ac216f697f GIT_SILENT Sync po/docbooks with svn 2024-06-01 01:24:07 +00:00
l10n daemon script
9893fae27c 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"
2024-06-01 01:18:01 +00:00
l10n daemon script
cb183efa66 GIT_SILENT made messages (after extraction) 2024-06-01 00:39:03 +00:00
Carl Schwan
87d707bc21 CreateRoomDialog: Add missing formcard separators 2024-05-31 10:38:47 +00:00
James Graham
227ebd610a Support binding 3PIDs
Closes network/neochat#565
2024-05-31 09:25:42 +00:00
James Graham
ab4af48e52 Integrate NeoChatMaximizeComponent with MediaManger
Needs libraries/kirigami-addons!227

Closes network/neochat#641
2024-05-31 09:01:13 +00:00
l10n daemon script
78a8227219 GIT_SILENT Sync po/docbooks with svn 2024-05-31 01:31:41 +00:00
l10n daemon script
ec73a53101 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"
2024-05-31 01:19:16 +00:00
l10n daemon script
3fb1d086b7 GIT_SILENT Sync po/docbooks with svn 2024-05-30 01:24:46 +00:00
l10n daemon script
5d4a12c127 GIT_SILENT Sync po/docbooks with svn 2024-05-29 01:36:10 +00:00
Carl Schwan
d2a79214b5 Fix crash when sending message
Ensure the message has an eventId which only happens after the message
is sent.
2024-05-28 15:21:04 +02:00
Laurent Montel
db57111188 Use 6.7 image 2024-05-28 06:49:38 +02:00
l10n daemon script
b22276bcd5 GIT_SILENT Sync po/docbooks with svn 2024-05-28 01:22:05 +00:00
Tobias Fella
6e2d85f2d2 Add some Q_UNUSEDs 2024-05-27 20:06:28 +02:00
James Graham
efb72652ce Use MessageContentModel for replies
This allows code and other components to be displayed nicely.
2024-05-27 14:54:42 +00:00
l10n daemon script
3615c3e8e5 GIT_SILENT Sync po/docbooks with svn 2024-05-27 01:25:06 +00:00
Nicolas Fella
8186ee0e3f Add missing dependencies to QML modules 2024-05-26 16:15:57 +02:00
Nicolas Fella
74aa14c011 Make org.kde.neochat import all submodules
This way we don't need to explicitly import the various submodules and instead only import org.kde.neochat
2024-05-26 16:15:43 +02:00
l10n daemon script
aa68369f95 GIT_SILENT Sync po/docbooks with svn 2024-05-26 01:22:22 +00:00
Laurent Montel
7837364b86 Add missing include moc 2024-05-25 18:38:20 +00:00
James Graham
31f0e39617 Convert TimelineDelegate to cpp
There are 2 main reason for doing this:
1. Because I can, I wanted to see if I could do it
2. It gets rid of the janky qml re parenting stuff so should be faster.
2024-05-25 17:06:13 +00:00
James Graham
a48151920d Permission model
Add a model for managing permissions (power levels) in rooms. This gets rid of a whole bunch of boiler plate in NeoChat and as a bonus makes it easy to add a feature to allow setting the permission level for any event.
2024-05-25 16:25:39 +00:00
Tobias Fella
8199653ddd Use simpler and less restrictive server url regex
The old regex was too restrictive. The new one will allow invalid urls, but we don't gain anything from preventing that

BUG: 486888
2024-05-25 15:00:12 +02:00
Tobias Fella
9d650519c7 Don't suggest joining the last opened room when neochat is started after the cache was cleared 2024-05-25 14:45:45 +02:00
l10n daemon script
a545eaca97 GIT_SILENT Sync po/docbooks with svn 2024-05-25 01:25:23 +00:00
James Graham
9da9de2d2d Make sure to ask before leaving a room
Make sure to ask before leaving a room

BUG: 486546
2024-05-24 08:12:26 +00:00
James Graham
c9ddd9d513 Improve the behaviour of jump to last unread message.
Improve the behaviour of jump to last unread message. The view will now jump as high as possible if the last unread message isn't loaded. This at least triggers more items to load which will eventually get the user to the last unread message.
2024-05-24 07:55:05 +00:00
Carl Schwan
415cfd57e7 Use standard Dialog background for emoji dialog 2024-05-22 10:00:51 +00:00
l10n daemon script
b1ede93cdb 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"
2024-05-22 01:19:14 +00:00
l10n daemon script
32df465c8a GIT_SILENT made messages (after extraction) 2024-05-22 00:39:06 +00:00
l10n daemon script
2f1c5df11d GIT_SILENT Sync po/docbooks with svn 2024-05-21 01:29:24 +00:00
l10n daemon script
f85f6a7fdc GIT_SILENT Sync po/docbooks with svn 2024-05-20 01:24:20 +00:00
l10n daemon script
93cc80a4b3 GIT_SILENT Sync po/docbooks with svn 2024-05-19 01:24:46 +00:00
Heiko Becker
0f3e401d42 GIT_SILENT Update Appstream for new release
(cherry picked from commit c66c035dbb)
2024-05-17 00:50:28 +02:00
l10n daemon script
a9f2c33053 GIT_SILENT Sync po/docbooks with svn 2024-05-16 01:36:22 +00:00
l10n daemon script
c5ef8c3fec 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"
2024-05-16 01:17:33 +00:00
l10n daemon script
ad696bd60b GIT_SILENT Sync po/docbooks with svn 2024-05-15 01:23:49 +00:00
Tobias Fella
9da7462f3e Fix compatibility with newer quotient job api 2024-05-14 16:50:38 +02:00
l10n daemon script
0ecdf354c6 GIT_SILENT Sync po/docbooks with svn 2024-05-14 01:25:59 +00:00
l10n daemon script
67f10632fb GIT_SILENT Sync po/docbooks with svn 2024-05-13 01:27:15 +00:00
l10n daemon script
044b910357 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"
2024-05-13 01:17:24 +00:00
James Graham
3de943e50f Set Identity server
Part of network/neochat#565
2024-05-12 18:22:27 +00:00
James Graham
1a2272249d Support adding 3 PIDs
Implements part of network/neochat#565

Note the phone number stuff is untested as neither kde.org or matrix.org have them switched on.
2024-05-12 17:02:09 +00:00
James Graham
0dfeec6cae Make sure that the link preview sizes correctly so that there is no bubble overflow 2024-05-12 13:13:11 +00:00
l10n daemon script
8d193021ab GIT_SILENT Sync po/docbooks with svn 2024-05-12 01:30:54 +00:00
Carl Schwan
f45226a680 Port all confirm dialogs to Kirigami.PromptDialog 2024-05-10 12:54:13 +00:00
l10n daemon script
40d55805ff GIT_SILENT Sync po/docbooks with svn 2024-05-10 01:24:43 +00:00
l10n daemon script
7d6d0b012b GIT_SILENT Sync po/docbooks with svn 2024-05-09 01:29:36 +00:00
l10n daemon script
d7d2c6bf01 GIT_SILENT made messages (after extraction) 2024-05-09 00:39:06 +00:00
Akseli Lahtinen
ce1c6096d3 Check for tagToken length
Sometimes this part of the code causes segfault for me.

I don't know what the root cause is though, it's *very* random. I guess the tagToken should always be long enough, but sometimes it's just one character.
2024-05-08 19:41:39 +00:00
l10n daemon script
8c4ea6180a GIT_SILENT Sync po/docbooks with svn 2024-05-08 01:30:36 +00:00
l10n daemon script
95649ba758 GIT_SILENT Sync po/docbooks with svn 2024-05-07 01:25:20 +00:00
l10n daemon script
2b61f2a9e8 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"
2024-05-07 01:16:57 +00:00
Tobias Fella
47242aa66c Remove remaining uses of MatrixImageProvider and remove it 2024-05-06 18:18:48 +02:00
Tobias Fella
31a8abe615 Don't use applicationWindow() for loading key verification sessions 2024-05-06 15:54:50 +02:00
Tobias Fella
4dae3c34b4 Move HoverLinkIndicator to separate file 2024-05-06 15:54:49 +02:00
Tobias Fella
65aa8919b9 Port ConfirmUrlDialog away from applicationWindow() 2024-05-06 15:54:49 +02:00
Tobias Fella
acf8eac0d4 Cleanup JoinRoomDialog 2024-05-06 15:54:49 +02:00
Tobias Fella
e8aa29d3c2 Move direct chat confirmation dialog to separate file 2024-05-06 15:54:49 +02:00
Tobias Fella
a36194afe2 Remove unused component 2024-05-06 15:54:49 +02:00
Tobias Fella
8554898e45 Move consent sheet to separate file 2024-05-06 15:54:46 +02:00
l10n daemon script
55b29f645b GIT_SILENT Sync po/docbooks with svn 2024-05-06 01:25:56 +00:00
l10n daemon script
52cc91ba3e GIT_SILENT made messages (after extraction) 2024-05-06 00:38:19 +00:00
Tobias Fella
c9e3a9f8fe Add back include 2024-05-05 21:56:00 +02:00
Tobias Fella
c3de788956 Cleanup includes 2024-05-05 21:49:43 +02:00
Tobias Fella
53e3c36f7e Explicitely include QFont 2024-05-05 21:33:47 +02:00
Tobias Fella
20908107a0 Remove unused includes 2024-05-05 21:21:43 +02:00
l10n daemon script
cbe1a0d6f8 GIT_SILENT Sync po/docbooks with svn 2024-05-05 01:26:11 +00:00
Tobias Fella
7d8c79ec57 Remove unused include 2024-05-04 11:27:30 +02:00
Tobias Fella
9bb89c728f Move KUnifiedPush include to source file 2024-05-04 11:22:00 +02:00
Tobias Fella
be2e28cc53 Cleanup API 2024-05-04 11:17:45 +02:00
Tobias Fella
57be57971d Remove unused include 2024-05-04 11:17:08 +02:00
Tobias Fella
64eed47f04 Port some things away from Omittable 2024-05-04 10:56:12 +02:00
l10n daemon script
7659105fda GIT_SILENT Sync po/docbooks with svn 2024-05-04 01:33:07 +00:00
Carl Schwan
18c9376992 Fix micro spacing inconsistency in SpaceHierarchyDelegate
Exposing index allows RoundedItemDelegate to use a consistent padding
for the first and last item in the listview.
2024-05-04 00:36:34 +02:00
Tobias Fella
3a4aca7fbd Fix module 2024-05-03 22:40:32 +02:00
Tobias Fella
85ff8cdd4a Fix spacing of HiddenDelegate 2024-05-03 14:57:18 +02:00
l10n daemon script
b1048cb84a GIT_SILENT Sync po/docbooks with svn 2024-05-03 01:24:47 +00:00
l10n daemon script
ff9cf9abf6 GIT_SILENT Sync po/docbooks with svn 2024-05-02 01:23:33 +00:00
l10n daemon script
157c84663d GIT_SILENT Sync po/docbooks with svn 2024-05-01 01:23:12 +00:00
Tobias Fella
908e4fb5a4 Add test for room creation 2024-04-30 23:37:33 +02:00
Tobias Fella
fdca7d58e5 Push ImageEditorPage using pushDialogLayer
BUG: 486315
2024-04-30 23:36:17 +02:00
Tobias Fella
2eb26d512b Fix opening room on mobile 2024-04-30 22:05:22 +02:00
Tobias Fella
b7fdf7d60f Port away from qstr 2024-04-30 21:39:13 +02:00
Tobias Fella
2a1e66468d Fix if X11 on apple 2024-04-30 21:28:38 +02:00
Tobias Fella
cd7faf751e Show captions 2024-04-30 20:28:53 +02:00
Bart De Vries
d79d806cda Add "No Proxy" option to proxy settings 2024-04-30 20:19:22 +02:00
Bart De Vries
cae389c04c Make neochat obey the system-wide proxy settings 2024-04-30 20:19:22 +02:00
Tobias Fella
4e6850a60c Adapt to behavior change in libQuotient 2024-04-30 17:37:22 +02:00
l10n daemon script
a71f8cf358 GIT_SILENT Sync po/docbooks with svn 2024-04-30 01:24:36 +00:00
Carl Schwan
fe6ff9b2b6 Cleanup quick switcher
- Make it modal
- Fix spacing
- Use bottom separator instead of frame for search field
2024-04-29 16:54:25 +00:00
l10n daemon script
5a0f3e474d GIT_SILENT Sync po/docbooks with svn 2024-04-29 01:25:09 +00:00
Nate Graham
cd9c4239b1 Revert "Preserve mx-reply in the edited message if it exists"
This reverts commit fa57db8e83, because it
caused the last message you replied to to be shown as a reply in every
subsequent message you send, but they're only visible to people using
clients that aren't NeoChat, so you don't immediately realize that
you're causing pandemonium in your chatrooms.
2024-04-28 08:05:52 -06:00
James Graham
0cf1d87b12 Fix the master show link preview setting and add back the per room setting
Title

BUG: 486126
2024-04-28 10:51:44 +00:00
l10n daemon script
99d91c1e07 GIT_SILENT Sync po/docbooks with svn 2024-04-28 01:21:04 +00:00
l10n daemon script
457f80bc9b GIT_SILENT Sync po/docbooks with svn 2024-04-27 01:20:24 +00:00
Tobias Fella
968e3fc23b Cleanup Main.qml 2024-04-26 22:59:06 +02:00
Tobias Fella
b1e338579d Port UserConsentSheet to Kirigami.Dialog 2024-04-26 22:51:48 +02:00
Tobias Fella
7e4361bb5e Fix AddServerSheet 2024-04-26 19:11:03 +02:00
l10n daemon script
dd35fa3d91 GIT_SILENT Sync po/docbooks with svn 2024-04-26 01:20:19 +00:00
Tobias Fella
affb911d97 Create QML module for chatbar 2024-04-25 23:02:59 +02:00
Tobias Fella
785f9cd1b6 Fix some warnings 2024-04-25 22:57:24 +02:00
Tobias Fella
419056f098 Fix QML warnings 2024-04-25 22:56:28 +02:00
Tobias Fella
396db18de2 Fix unqualified access 2024-04-25 22:53:16 +02:00
Tobias Fella
f124721cd9 Fix qmlls warning 2024-04-25 22:52:38 +02:00
Tobias Fella
52e49b49fb Remove unnecessary parent 2024-04-25 22:51:59 +02:00
Tobias Fella
10e03777e9 Fix qmlls warning 2024-04-25 22:48:55 +02:00
Tobias Fella
27538b72e9 Remove unused imports 2024-04-25 22:48:20 +02:00
Tobias Fella
19ace37a75 Fix HoverLinkIndicator warnings 2024-04-25 22:47:55 +02:00
Tobias Fella
90c5377ef0 Port direct chat confirmation dialog away from OverlaySheet 2024-04-25 22:47:17 +02:00
Tobias Fella
f0e3d3e474 Fix some dialog parenting 2024-04-25 22:46:03 +02:00
Tobias Fella
26064ebf8c Remove stray log statement 2024-04-25 22:34:40 +02:00
Tobias Fella
1d92b74131 Use escaped room name in ConfirmLeaveDialog 2024-04-25 22:30:53 +02:00
l10n daemon script
5f83c137d2 GIT_SILENT Sync po/docbooks with svn 2024-04-25 01:21:47 +00:00
Joshua Goins
fa57db8e83 Preserve mx-reply in the edited message if it exists 2024-04-24 19:03:28 +00:00
Joshua Goins
9d3c2e19f5 Strip replies when calling EventHandler::getMarkdownBody()
This matches previous behavior before
e2eb6ab33c and including the markdown
blockquote breaks edits. This also adds a test to ensure it does indeed
get stripped.
2024-04-24 19:03:28 +00:00
l10n daemon script
46a9600d55 GIT_SILENT Sync po/docbooks with svn 2024-04-24 01:29:43 +00:00
James Graham
de40701cf6 Multiple Link Previews
- Show a preview for each link in the text below the block in which it appears.
- Allow link previews to be dismissed
2024-04-23 19:45:33 +00:00
Tobias Fella
307536c6b6 Use escaped title in devtools 2024-04-23 13:27:15 +02:00
Tobias Fella
203be8bd35 Work around QML opening dialog in wrong window 2024-04-23 13:24:00 +02:00
Tobias Fella
1e644587b3 Replace Quotient::Connection with NeoChatConnection where possible 2024-04-23 12:35:15 +02:00
James Graham
66a60f09e3 Refactor the MessageComponentModel component update 2024-04-23 11:39:18 +02:00
Tobias Fella
69b6f16ec1 Remove search bar; Use QuickSwitcher instead 2024-04-23 10:19:43 +02:00
l10n daemon script
10b4274a3d GIT_SILENT Sync po/docbooks with svn 2024-04-23 01:29:16 +00:00
James Graham
28c9d94457 Fix Roomlist Shortcuts
Fix the ctrl + pgup/pgdwn shortcuts for the room list so that they work with tree model

BUG: 485949
2024-04-22 22:01:46 +00:00
Tobias Fella
d74fd1a560 Force author display name in HiddenDelegate to PlainText 2024-04-22 23:14:31 +02:00
James Graham
624b1b06c5 Make the SpaceDrawer navigable with the keyboard. 2024-04-22 18:18:15 +02:00
Carl Schwan
95376c2ccc Apply 1 suggestion(s) to 1 file(s)
Co-authored-by: Carl Schwan <carl@carlschwan.eu>
2024-04-22 14:58:44 +00:00
James Graham
1eb622165b Use AvatarButton in UserInfo instead of a custom button. This has the advantage of showing keyboard focus properly 2024-04-22 16:50:56 +02:00
Nate Graham
9d6ba324fb Use more appropriate icons and tooltips for the room info drawer handles
Right now they use the standard text but left and right arrow icons,
which is a bit odd, and I think fails to convey what will happen when
clicked especially whern the drawer is closed.

Instead, let's use descriptive tooltip text for both, and a descriptive
icon for the the "this will open the drawer" handle button. For the one
to close the drawer, the default icon seems better, so let's stop
overriding it.
2024-04-22 08:10:39 +00:00
l10n daemon script
d462852ab9 GIT_SILENT Sync po/docbooks with svn 2024-04-22 01:28:36 +00:00
l10n daemon script
eeec81898b 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"
2024-04-22 01:15:14 +00:00
James Graham
ab0c8b8170 Use new cornerRadius Kirigami unit across the app 2024-04-21 19:19:57 +02:00
James Graham
91d295e0bb Make sure that tab can be used to navigate away from the chatbar 2024-04-21 17:18:12 +00:00
James Graham
125974dd7a Add Carl's focus title hack as a devtool option 2024-04-21 17:02:26 +00:00
Tobias Fella
92895a7d00 Improve CodeComponent background 2024-04-21 18:21:54 +02:00
Nate Graham
d9308440e6 Make the "add new" menu button a hamburger menu
I know hamburger menus sometimes aren't amazing, but the current icon is
misleading. It's a plus button which generally means "create new".
However the menu is full of actions not related to creating new things,
including:

- Explore Rooms
- Find your Friends
- Scan a QR Code

These actions may technically result in a new room appearing in the
sidebar, but that's not a user's definition of creating a new thing;
these are *joining* a thing, and the fact that a new entry appears in
the sidebar is an implementation detail.

As a result the existing icon is inaccurate, and also holds back the
menu from adding additional items in the future that are even less
related to creating new rooms. An example would be the quick room
switcher, which is not exposed visibly in the UI anywhere, and could not
logically live in the current menu without changing its icon and text.
2024-04-21 15:07:29 +00:00
James Graham
5340142c06 Change actionChanged to notificationActionChanged
Change actionChanged to notificationActionChanged to avoid any clashes with ItemDelegate action property signals
2024-04-21 14:37:02 +00:00
James Graham
012d30ee9f Elide the Hidden delegate text 2024-04-21 13:33:00 +02:00
James Graham
031d69d996 Only override the DelegateType when showing hidden messages 2024-04-21 13:25:49 +02:00
James Graham
8b63c18f65 Implement devtoool to show hidden timeline messages 2024-04-21 10:50:59 +00:00
James Graham
dc2f11eb2b Fancy Effects 2021-2024 gone but never forgotten
Remove fancy effects as it's busted and causing CPU spikes.
2024-04-21 10:50:45 +00:00
James Graham
13e64a9487 Use 0.8.x for libQuotient flatpak 2024-04-21 12:41:21 +02:00
Albert Astals Cid
6a3c88ae5b GIT_SILENT Upgrade release service version to 24.07.70. 2024-04-21 12:28:32 +02:00
774 changed files with 389910 additions and 186802 deletions

2
.contextProperties.ini Normal file
View File

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

View File

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

View File

@@ -2,7 +2,7 @@
"id": "org.kde.neochat",
"branch": "master",
"runtime": "org.kde.Platform",
"runtime-version": "6.6",
"runtime-version": "6.10",
"sdk": "org.kde.Sdk",
"command": "neochat",
"tags": [
@@ -20,18 +20,39 @@
"--talk-name=org.kde.kwalletd5",
"--talk-name=org.kde.StatusNotifierWatcher",
"--talk-name=org.freedesktop.secrets",
"--talk-name=org.kde.kuiserver",
"--own-name=org.kde.StatusNotifierItem-2-2"
],
"cleanup": [
"/include",
"/lib/*.a",
"/lib/cmake",
"/lib/pkgconfig",
"/share/ndk-modules"
],
"modules": [
{
"name": "kirigamiaddons",
"config-opts": [ "-DBUILD_TESTING=OFF" ],
"name": "opencv",
"config-opts": [
"-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", "commit": "34d311219e8b7209746a98b3a29b91ded05ff936" } ]
"sources": [
{
"type": "git",
"url": "https://github.com/opencv/opencv"
}
],
"builddir": true
},
{
"name": "kquickimageeditor",
"config-opts": [ "-DBUILD_WITH_QT6=ON" ],
"config-opts": [
"-DBUILD_WITH_QT6=ON",
"-DBUILD_TESTING=OFF"
],
"buildsystem": "cmake-ninja",
"sources": [
{
@@ -43,17 +64,20 @@
{
"name": "olm",
"buildsystem": "cmake-ninja",
"config-opts": [ "-DOLM_TESTS=OFF" ],
"config-opts": [
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
"-DOLM_TESTS=OFF"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.matrix.org/matrix-org/olm.git",
"tag": "3.2.10",
"tag": "3.2.16",
"x-checker-data": {
"type": "git",
"tag-pattern": "^([\\d.]+)$"
},
"commit": "9908862979147a71dc6abaecd521be526ae77be1"
"commit": "7e0c8277032e40308987257b711b38af8d77cc69"
}
]
},
@@ -65,13 +89,13 @@
"-Dvapi=false",
"-Dgtk_doc=false",
"-Dintrospection=false",
"-Dgcrypt=false"
"-Dcrypto=disabled"
],
"sources": [
{
"type": "archive",
"url": "https://download.gnome.org/sources/libsecret/0.20/libsecret-0.20.5.tar.xz",
"sha256": "3fb3ce340fcd7db54d87c893e69bfc2b1f6e4d4b279065ffe66dac9f0fd12b4d",
"url": "https://download.gnome.org/sources/libsecret/0.21/libsecret-0.21.7.tar.xz",
"sha256": "6b452e4750590a2b5617adc40026f28d2f4903de15f1250e1d1c40bfd68ed55e",
"x-checker-data": {
"type": "gnome",
"name": "libsecret",
@@ -86,13 +110,13 @@
"sources": [
{
"type": "archive",
"url": "https://github.com/frankosterfeld/qtkeychain/archive/0.14.2.tar.gz",
"sha256": "cf2e972b783ba66334a79a30f6b3a1ea794a1dc574d6c3bebae5ffd2f0399571",
"url": "https://github.com/frankosterfeld/qtkeychain/archive/refs/tags/0.15.0.tar.gz",
"sha256": "f4254dc8f0933b06d90672d683eab08ef770acd8336e44dfa030ce041dc2ca22",
"x-checker-data": {
"type": "anitya",
"project-id": 4138,
"stable-only": true,
"url-template": "https://github.com/frankosterfeld/qtkeychain/archive/v$version.tar.gz"
"url-template": "https://github.com/frankosterfeld/qtkeychain/archive/refs/tags/$version.tar.gz"
}
}
],
@@ -100,7 +124,8 @@
"-DBUILD_WITH_QT6=ON",
"-DCMAKE_INSTALL_LIBDIR=/app/lib",
"-DLIB_INSTALL_DIR=/app/lib",
"-DBUILD_TRANSLATIONS=NO"
"-DBUILD_TRANSLATIONS=NO",
"-DBUILD_TESTING=OFF"
]
},
{
@@ -123,34 +148,37 @@
{
"name": "cmark",
"buildsystem": "cmake-ninja",
"config-opts": [ "-DCMARK_TESTS=OFF" ],
"config-opts": [
"-DCMARK_TESTS=OFF",
"-DCMAKE_BUILD_TYPE=Release",
"-DCMAKE_INSTALL_PREFIX=/app"
],
"sources": [
{
"type": "git",
"url": "https://github.com/commonmark/cmark.git"
}
],
"config-opts": [
"-DCMARK_TESTS=OFF",
"-DCMAKE_BUILD_TYPE=Release",
"-DCMAKE_INSTALL_PREFIX=/app"
],
"builddir": true
},
{
"name": "qcoro",
"name": "kunifiedpush",
"buildsystem": "cmake-ninja",
"config-opts": [ "-DQCORO_BUILD_EXAMPLES=OFF", "-DBUILD_TESTING=OFF" ],
"builddir": true,
"config-opts": [
"-DENABLE_TESTING=OFF",
"-DKUNIFIEDPUSH_CLIENT_ONLY=ON"
],
"sources": [
{
"type": "archive",
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.7.0.tar.gz",
"sha256": "23ef0217926e67c8d2eb861cf91617da2f7d8d5a9ae6c62321b21448b1669210",
"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": 236236,
"project-id": 8763,
"stable-only": true,
"url-template": "https://github.com/danvratil/qcoro/archive/refs/tags/v$version.tar.gz"
"url-template": "https://download.kde.org/stable/release-service/$version/src/kunifiedpush-$version.tar.xz"
}
}
]
@@ -158,14 +186,15 @@
{
"name": "neochat",
"buildsystem": "cmake-ninja",
"config-opts": [
"-DBUILD_TESTING=OFF",
"-DNEOCHAT_FLATPAK=ON"
],
"sources": [
{
"type": "dir",
"path": "."
}
],
"config-opts": [
"-DNEOCHAT_FLATPAK=ON"
]
}
]

View File

@@ -5,11 +5,17 @@ include:
- project: sysadmin/ci-utilities
file:
- /gitlab-templates/reuse-lint.yml
- /gitlab-templates/json-validation.yml
- /gitlab-templates/xml-lint.yml
- /gitlab-templates/yaml-lint.yml
- /gitlab-templates/android-qt6.yml
- /gitlab-templates/linux-qt6.yml
- /gitlab-templates/linux-qt6-next.yml
- /gitlab-templates/windows-qt6.yml
- /gitlab-templates/freebsd-qt6.yml
# - /gitlab-templates/freebsd-qt6.yml
- /gitlab-templates/flatpak.yml
- /gitlab-templates/snap-snapcraft-lxd.yml
- /gitlab-templates/craft-android-qt6-apks.yml
- /gitlab-templates/craft-appimage-qt6.yml
- /gitlab-templates/craft-windows-x86-64-qt6.yml
- /gitlab-templates/craft-windows-appx-qt6.yml

View File

@@ -2,42 +2,44 @@
# SPDX-License-Identifier: BSD-2-Clause
Dependencies:
- 'on': ['Linux', 'Android', 'FreeBSD', 'Windows']
'require':
'frameworks/extra-cmake-modules': '@latest-kf6'
'frameworks/kcoreaddons': '@latest-kf6'
'frameworks/kirigami': '@latest-kf6'
'frameworks/ki18n': '@latest-kf6'
'frameworks/kconfig': '@latest-kf6'
'frameworks/syntax-highlighting': '@latest-kf6'
'frameworks/kitemmodels': '@latest-kf6'
'frameworks/kquickcharts': '@latest-kf6'
'frameworks/knotifications': '@latest-kf6'
'frameworks/kcolorscheme': '@latest-kf6'
'libraries/kquickimageeditor': '@latest-kf6'
'frameworks/sonnet': '@latest-kf6'
'frameworks/prison': '@latest-kf6'
'libraries/kirigami-addons': '@latest-kf6'
'third-party/libquotient': '@latest'
'third-party/qtkeychain': '@latest'
'third-party/cmark': '@latest'
'third-party/qcoro': '@latest'
- 'on': ['Windows', 'Linux', 'FreeBSD']
'require':
'frameworks/qqc2-desktop-style': '@latest-kf6'
'frameworks/kio': '@latest-kf6'
'frameworks/kwindowsystem': '@latest-kf6'
'frameworks/kstatusnotifieritem': '@latest-kf6'
'frameworks/kcrash': '@latest-kf6'
- 'on': ['Linux', 'FreeBSD']
'require':
'frameworks/kdbusaddons': '@latest-kf6'
'frameworks/purpose': '@latest-kf6'
- 'on': ['Linux', 'Android', 'FreeBSD', 'Windows']
'require':
'frameworks/extra-cmake-modules': '@latest-kf6'
'frameworks/kcoreaddons': '@latest-kf6'
'frameworks/kirigami': '@latest-kf6'
'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'
'libraries/kquickimageeditor': '@latest-kf6'
'frameworks/sonnet': '@latest-kf6'
'frameworks/prison': '@latest-kf6'
'libraries/kirigami-addons': '@latest-kf6'
'third-party/libquotient': '@latest'
'third-party/qtkeychain': '@latest'
'third-party/cmark': '@latest'
'third-party/qcoro': '@latest'
- 'on': ['Windows', 'Linux', 'FreeBSD']
'require':
'frameworks/qqc2-desktop-style': '@latest-kf6'
'frameworks/kio': '@latest-kf6'
'frameworks/kwindowsystem': '@latest-kf6'
- 'on': ['Linux', 'FreeBSD']
'require':
'frameworks/kdbusaddons': '@latest-kf6'
'frameworks/purpose': '@latest-kf6'
'libraries/kunifiedpush': '@latest-kf6'
- 'on': ['Linux']
'require':
'sdk/selenium-webdriver-at-spi': '@latest-kf6'
- 'on': ['Linux']
'require':
'sdk/selenium-webdriver-at-spi': '@latest-kf6'
Options:
per-test-timeout: 90
require-passing-tests-on: [ '@all' ]
require-passing-tests-on: ['Linux', 'Android', 'FreeBSD', 'Windows']
run-qmllint: True
enable-lsan: True

View File

@@ -1,55 +0,0 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: NeoChat
Upstream-Contact: Carl Schwan <carlschwan@kde.org>
Files: 128-logo.png icons/* logo.png org.kde.neochat.svg org.kde.neochat.tray.svg android/res/drawable/neochat.png
Copyright: 2020 Carson Black <uhhadd@gmail.com>
License: LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
Files: android/res/drawable/splash.xml
Copyright: 2020 Tobias Fella <tobias.fella@kde.org>
License: BSD-2-Clause
Files: .gitignore
Copyright: None
License: CC0-1.0
Files: .gitlab/issue_templates/bug.md
Copyright: 2021 Carl Schwan <carlschwan@kde.org>
License: CC0-1.0
Files: src/res.qrc src/res_android.qrc src/res_desktop.qrc
Copyright: None
License: CC0-1.0
Files: cmake/Flatpak/99-noto-mono-color-emoji.conf
Copyright: 2021 Carl Schwan <carlschwan@kde.org>
License: BSD-2-Clause
Files: src/neochatconfig.kcfg
Copyright: 2020-2021 Carl Schwan <carlschwan@kde.org>, Tobias Fella <tobias.fella@kde.org>
License: BSD-2-Clause
Files: src/neochat.notifyrc
Copyright: 2020 Tobias Fella <tobias.fella@kde.org>
License: BSD-2-Clause
Files: src/qml/confetti.png src/qml/glowdot.png
Copyright: 2021 Alexey Andreyev <aa13q@ya.ru>
License: CC0-1.0
Files: .flatpak-manifest.json
Copyright: 2020-2022 Tobias Fella <tobias.fella@kde.org>
License: BSD-2-Clause
Files: autotests/data/*
Copyright: none
License: CC0-1.0
Files: appiumtests/data/*
Copyright: 2023 Tobias Fella <tobias.fella@kde.org>
License: CC0-1.0
Files: src/purpose/purposeplugin.json
Copyright: 2023 Tobias Fella <tobias.fella@kde.org>
License: BSD-2-Clause

View File

@@ -7,15 +7,15 @@
cmake_minimum_required(VERSION 3.16)
# KDE Applications version, managed by release script.
set(RELEASE_SERVICE_VERSION_MAJOR "24")
set(RELEASE_SERVICE_VERSION_MINOR "04")
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.0")
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)
@@ -38,30 +38,34 @@ include(KDEGitCommitHooks)
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
)
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg WebView)
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg TextToSpeech WebView)
set_package_properties(Qt6 PROPERTIES
TYPE REQUIRED
PURPOSE "Basic application components"
)
qt_policy(SET QTP0001 NEW)
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 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"
@@ -70,7 +74,7 @@ set_package_properties(KF6Kirigami PROPERTIES
TYPE REQUIRED
PURPOSE "Kirigami application UI framework"
)
find_package(KF6KirigamiAddons 0.7.2 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)
@@ -84,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)
find_package(KF6SyntaxHighlighting ${KF_MIN_VERSION} REQUIRED)
set_package_properties(KF6QQC2DesktopStyle PROPERTIES
TYPE RUNTIME
@@ -98,11 +102,11 @@ else()
)
endif()
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED)
endif()
find_package(QuotientQt6 0.7)
find_package(QuotientQt6 0.9.5)
set_package_properties(QuotientQt6 PROPERTIES
TYPE REQUIRED
DESCRIPTION "Qt wrapper around Matrix API"
@@ -110,11 +114,6 @@ set_package_properties(QuotientQt6 PROPERTIES
PURPOSE "Talk with matrix server"
)
if (NOT TARGET Olm::Olm)
message(FATAL_ERROR "NeoChat requires Quotient with the E2EE feature enabled")
endif()
find_package(cmark)
set_package_properties(cmark PROPERTIES
TYPE REQUIRED
@@ -147,16 +146,24 @@ set_package_properties(KF6DocTools PROPERTIES DESCRIPTION
TYPE OPTIONAL
)
find_package(KUnifiedPush QUIET)
set_package_properties(KUnifiedPush PROPERTIES
TYPE OPTIONAL
PURPOSE "Push notification support"
URL "https://invent.kde.org/libraries/kunifiedpush"
)
option(WITH_UNIFIEDPUSH "Build with KUnifiedPush support" ON)
if (ANDROID OR APPLE OR WIN32 OR HAIKU)
set(WITH_UNIFIEDPUSH OFF)
endif()
if (WITH_UNIFIEDPUSH)
find_package(KUnifiedPush)
set_package_properties(KUnifiedPush PROPERTIES
TYPE REQUIRED
PURPOSE "Push notification support"
URL "https://invent.kde.org/libraries/kunifiedpush"
)
endif()
if(ANDROID)
find_package(Sqlite3)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/android/version.gradle.in ${CMAKE_BINARY_DIR}/version.gradle)
set(BUILD_TESTING FALSE)
endif()
ki18n_install(po)
@@ -171,9 +178,12 @@ add_definitions(-DQT_NO_FOREACH)
add_subdirectory(src)
if (BUILD_TESTING)
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test)
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test HttpServer QuickTest)
add_subdirectory(autotests)
add_subdirectory(appiumtests)
# add_subdirectory(appiumtests)
if (NOT ANDROID)
add_subdirectory(memorytests)
endif()
endif()
if(KF6DocTools_FOUND)
@@ -181,7 +191,7 @@ if(KF6DocTools_FOUND)
add_subdirectory(doc)
endif()
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
if (NOT ANDROID)
file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES src/*.cpp src/*.h)

View File

@@ -11,7 +11,7 @@ A Qt/QML based Matrix client.
<a href='https://matrix.org'><img src='https://matrix.org/docs/legacy/made-for-matrix.png' alt='Made for Matrix' height=64 target=_blank /></a>
<a href='https://flathub.org/apps/details/org.kde.neochat'><img width='190px' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-i-en.png'/></a>
<a href='https://snapcraft.io/neochat'><img width='190px' alt='Download on the Snap Store' src='https://snapcraft.io/static/images/badges/en/snap-store-black.svg'/></a>
<a href='https://snapcraft.io/neochat'><img width='190px' alt='Download on the Snap Store' src='https://apps.kde.org/store_badges/snapstore/en.svg'/></a>
## Introduction
@@ -25,21 +25,16 @@ Qt-based SDK for the [Matrix Protocol](https://spec.matrix.org/).
## Features
NeoChat aims to be a fully featured application for the Matrix specification. As such most parts of the current specification are supported, with the notable exceptions
of VoIP, threads, and some aspects of End-to-End Encryption. There are a few other smaller omissions due to the fact that the Matrix spec is constantly
NeoChat aims to be a fully featured application for the Matrix specification. As such, most parts of the current specification are supported, with the notable exceptions
of VoIP, threads, and some aspects of End-to-End Encryption. There are a few other smaller omissions due to the Matrix spec constantly
evolving, but the aim remains to provide eventual support for the entire spec.
Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:
- Polls - MSC3381
- Sticker Packs - MSC2545
- Location Events - MSC3488
## Get it
Details where to find stable releases for NeoChat can be found on its [homepage](https://apps.kde.org/neochat).
Nightly builds for linux and windows can be downloaded from [cdn.kde.org](https://cdn.kde.org/ci-builds/network/neochat/).
Nightly builds for android are available from [KDE's nightly F-Droid repository](https://community.kde.org/Android/F-Droid).
Nightly builds for Linux and Windows can be downloaded from [cdn.kde.org](https://cdn.kde.org/ci-builds/network/neochat/).
Nightly builds for Android are available from [KDE's nightly F-Droid repository](https://community.kde.org/Android/F-Droid).
Nightly Flatpaks are available from [KDE's nightly Flatpak repository](https://userbase.kde.org/Tutorials/Flatpak).
## Building NeoChat
@@ -48,12 +43,12 @@ The best way to build KDE apps during development is to use `kdesrc-build`. The
the KDE community website's get involved section under [development](https://community.kde.org/Get_Involved/development). This
is primarily aimed at Linux development.
For Windows and Android [Craft](https://invent.kde.org/packaging/craft) is the primary choice. There are guides for setting up
For Windows and Android, [Craft](https://invent.kde.org/packaging/craft) is the primary choice. There are guides for setting up
development environments for [Windows](https://community.kde.org/Get_Involved/development/Windows) and [Android](https://develop.kde.org/docs/packaging/android/building_applications/).
## Running
Just start the executable in your preferred way - either from the build directory or from the installed location.
Start the executable in your preferred way either from the build directory or from the installed location.
## Tests
@@ -66,12 +61,12 @@ be complete.
![coverage](https://invent.kde.org/network/neochat/badges/master/pipeline.svg)
Currently the number of tests is limited, but growing. If anyone wants to help improve this, those
Currently, the number of tests is limited but growing. If anyone wants to help improve this, those
contributions would be especially welcome.
## Contributing
As is the case throughout the KDE ecosystem contributions are welcome from all. The code base is managed in the
As is the case throughout the KDE ecosystem, contributions are welcome from all. The code base is managed in the
[NeoChat repository](https://invent.kde.org/network/neochat) of the KDE Gitlab instance.
- [Code of Conduct](https://kde.org/code-of-conduct)
@@ -86,7 +81,7 @@ The best place to reach the maintainers is on the KDE Matrix instance in the Neo
## Acknowledgement
NeoChat utilizes [libQuotient](https://github.com/quotient-im/libQuotient/) as its Matrix SDK.
NeoChat uses [libQuotient](https://github.com/quotient-im/libQuotient/) as its Matrix SDK.
NeoChat is a fork of [Spectral](https://gitlab.com/spectral-im/spectral/).

96
REUSE.toml Normal file
View File

@@ -0,0 +1,96 @@
# SPDX-FileCopyrightText: none
# SPDX-License-Identifier: CC0-1.0
version = 1
SPDX-PackageName = "NeoChat"
SPDX-PackageSupplier = "Carl Schwan <carlschwan@kde.org>"
[[annotations]]
path = ["128-logo.png", "icons/**", "logo.png", "org.kde.neochat.svg", "org.kde.neochat.tray.svg", "android/res/drawable/neochat.png", "android/neochat-playstore.png"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2020 Carson Black <uhhadd@gmail.com>"
SPDX-License-Identifier = "LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL"
[[annotations]]
path = "android/res/drawable/splash.xml"
precedence = "aggregate"
SPDX-FileCopyrightText = "2020 Tobias Fella <tobias.fella@kde.org>"
SPDX-License-Identifier = "BSD-2-Clause"
[[annotations]]
path = ".gitignore"
precedence = "aggregate"
SPDX-FileCopyrightText = "None"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = ".gitlab/issue_templates/bug.md"
precedence = "aggregate"
SPDX-FileCopyrightText = "2021 Carl Schwan <carlschwan@kde.org>"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = ["src/res.qrc", "src/res_android.qrc", "src/res_desktop.qrc"]
precedence = "aggregate"
SPDX-FileCopyrightText = "None"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = "cmake/Flatpak/99-noto-mono-color-emoji.conf"
precedence = "aggregate"
SPDX-FileCopyrightText = "2021 Carl Schwan <carlschwan@kde.org>"
SPDX-License-Identifier = "BSD-2-Clause"
[[annotations]]
path = "src/app/neochatconfig.kcfg"
precedence = "aggregate"
SPDX-FileCopyrightText = "2020-2021 Carl Schwan <carlschwan@kde.org>, Tobias Fella <tobias.fella@kde.org>"
SPDX-License-Identifier = "BSD-2-Clause"
[[annotations]]
path = "src/app/neochat.notifyrc"
precedence = "aggregate"
SPDX-FileCopyrightText = "2020 Tobias Fella <tobias.fella@kde.org>"
SPDX-License-Identifier = "BSD-2-Clause"
[[annotations]]
path = ["src/qml/confetti.png", "src/qml/glowdot.png"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2021 Alexey Andreyev <aa13q@ya.ru>"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = ".flatpak-manifest.json"
precedence = "aggregate"
SPDX-FileCopyrightText = "2020-2022 Tobias Fella <tobias.fella@kde.org>"
SPDX-License-Identifier = "BSD-2-Clause"
[[annotations]]
path = "autotests/data/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "none"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = "appiumtests/data/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2023 Tobias Fella <tobias.fella@kde.org>"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = "src/purpose/purposeplugin.json"
precedence = "aggregate"
SPDX-FileCopyrightText = "2023 Tobias Fella <tobias.fella@kde.org>"
SPDX-License-Identifier = "BSD-2-Clause"
[[annotations]]
path = "memorytests/memtest-sync.json"
precedence = "aggregate"
SPDX-FileCopyrightText = "2024 James Graham <james.h.graham@protonmail.com>"
SPDX-License-Identifier = "BSD-2-Clause"
[[annotations]]
path = ".contextProperties.ini"
precedence = "aggregate"
SPDX-FileCopyrightText = "2025 Tobias Fella <tobias.fella@kde.org>"
SPDX-License-Identifier = "BSD-2-Clause"

View File

@@ -55,5 +55,6 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <!-- for Android >= 33 -->
</manifest>

View File

@@ -12,7 +12,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.1'
classpath 'com.android.tools.build:gradle:8.6.0'
}
}
@@ -23,8 +23,7 @@ repositories {
apply plugin: 'com.android.application'
apply from: '../version.gradle'
def timestamp = (int)(new Date().getTime()/1000)
apply from: '../ecm-version.gradle'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
@@ -79,9 +78,9 @@ android {
targetSdkVersion qtTargetSdkVersion
applicationId "org.kde.neochat"
namespace "org.kde.neochat"
versionCode timestamp
versionName projectVersionFull
manifestPlaceholders = [versionName: projectVersionFull, versionCode: timestamp]
versionCode ecmVersionCode
versionName ecmVersionName
manifestPlaceholders = [versionName: ecmVersionName, versionCode: ecmVersionCode]
}
packagingOptions {

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -1,6 +0,0 @@
// SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
// SPDX-License-Identifier: BSD-3-Clause
ext {
projectVersionFull = "@NEOCHAT_VERSION@"
}

56
appiumtests/createroomtest.py Executable file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
import os
import subprocess
import sys
import unittest
import time
from appium import webdriver
from appium.options.common.base import AppiumOptions
from appium.webdriver.common.appiumby import AppiumBy
class CreateRoomTest(unittest.TestCase):
mockServerProcess: subprocess.Popen
@classmethod
def setUpClass(cls):
cls.mockServerProcess = subprocess.Popen([sys.executable, os.path.join(os.path.dirname(__file__), "login-server.py")])
options = AppiumOptions()
options.set_capability("app", "neochat --ignore-ssl-errors --test")
cls.driver = webdriver.Remote(command_executor='http://127.0.0.1:4723', options=options)
def setUp(self):
pass
def tearDown(self):
if not self._outcome.result.wasSuccessful():
self.driver.get_screenshot_as_file("failed_test_shot_{}.png".format(self.id()))
@classmethod
def tearDownClass(self):
self.mockServerProcess.terminate()
self.driver.quit()
def test_create_room(self):
self.driver.find_element(by=AppiumBy.NAME, value="@user:localhost:1234").click()
self.driver.find_element(by=AppiumBy.NAME, value="Show Menu").click()
self.driver.find_element(by=AppiumBy.NAME, value="Create a Room").click()
self.driver.find_element(by=AppiumBy.NAME, value="Name:").send_keys("Super awesome room name")#
time.sleep(0.1) # without this, the second half of the text is sent to the topic field?!
self.driver.find_element(by=AppiumBy.NAME, value="Topic:").send_keys("There are not enough raccoons here")
time.sleep(0.1)
self.driver.find_element(by=AppiumBy.NAME, value="Create Room").click()
time.sleep(0.1)
self.driver.find_element(by=AppiumBy.NAME, value="Super awesome room name").click()
self.driver.find_element(by=AppiumBy.NAME, value="Show Room Information").click()
self.driver.find_element(by=AppiumBy.NAME, value="There are not enough raccoons here")
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,78 @@
{
"next_batch": "batch1234",
"rooms": {
"join": {
"!newroom123321:localhost:1234": {
"state": {
"events": [
{
"type": "m.room.member",
"state_key": "@user:localhost:1234",
"sender": "@user:localhost:1234",
"origin_server_ts": 1432735824653,
"event_id": "$event_id_1234_0:localhost:1234",
"room_id": "!newroom123321:localhost:1234",
"content": {
"avatar_url": "",
"displayname": "A Display Name",
"membership": "join",
"reason": "Nothing"
},
"unsigned": {
"age": 1234
}
},
{
"type": "m.room.name",
"state_key": "",
"sender": "@user:localhost:1234",
"origin_server_ts": 1432735824653,
"event_id": "$event_id_1234_1:localhost:1234",
"room_id": "!newroom123321:localhost:1234",
"content": {
"name": "Super awesome room name"
},
"unsigned": {
"age": 1234
}
},
{
"type": "m.room.topic",
"state_key": "",
"sender": "@user:localhost:1234",
"origin_server_ts": 1432735824653,
"event_id": "$event_id_1234_2:localhost:1234",
"room_id": "!newroom123321:localhost:1234",
"content": {
"topic": "There are not enough raccoons here"
},
"unsigned": {
"age": 1234
}
}
]
},
"timeline": {
"events": [
{
"type": "m.room.message",
"sender": "@user:localhost:1234",
"origin_server_ts": 1432735824653,
"event_id": "$event_id_1234_1:localhost:1234",
"room_id": "!newroom123321:localhost:1234",
"content": {
"body": "This is a message",
"format": "org.matrix.custom.html",
"formatted_body": "<a href=\"https://matrix.to/#/@user:localhost:1234\">User</a>:",
"msgtype": "m.text"
},
"unsigned": {
"age": 1234
}
}
]
}
}
}
}
}

View File

@@ -5,6 +5,26 @@
"!room_id_1234:localhost:1234": {
"state": {
"events": [
{
"content": {
"m.federate": true,
"predecessor": {
"event_id": "$something:example.org",
"room_id": "!oldroom:example.org"
},
"room_version": "11"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"state_key": "",
"type": "m.room.create",
"unsigned": {
"age": 1234,
"membership": "join"
}
},
{
"type": "m.room.member",
"state_key": "@user:localhost:1234",
@@ -26,6 +46,26 @@
},
"timeline": {
"events": [
{
"content": {
"m.federate": true,
"predecessor": {
"event_id": "$something:example.org",
"room_id": "!oldroom:example.org"
},
"room_version": "11"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"state_key": "",
"type": "m.room.create",
"unsigned": {
"age": 1234,
"membership": "join"
}
},
{
"type": "m.room.message",
"sender": "@user:localhost:1234",

View File

@@ -6,6 +6,8 @@ from flask import Flask, request, abort
import os
app = Flask(__name__)
next_sync_payload = ""
@app.route("/_matrix/client/v3/login", methods=["GET"])
def login_get():
@@ -42,8 +44,13 @@ def load_json(name):
@app.route("/_matrix/client/r0/sync")
def sync():
result = load_json("sync_response_no_rooms") if ("login" in request.headers.get("Authorization")) else load_json("sync_response_rooms")
global next_sync_payload
result = dict()
if len(next_sync_payload) > 0:
result = load_json(next_sync_payload)
next_sync_payload = ""
else:
result = load_json("sync_response_no_rooms") if ("login" in request.headers.get("Authorization")) else load_json("sync_response_rooms")
return result
@app.route("/.well-known/matrix/client")
@@ -65,6 +72,27 @@ def upload_keys():
reply = dict()
return reply
@app.route("/_matrix/client/v3/createRoom", methods=["POST"])
def create_room():
global next_sync_payload
data = request.get_json()
if data["name"] != "Super awesome room name" or data["topic"] != "There are not enough raccoons here":
return dict(), 400
response = dict()
response["room_id"] = "!newroom123321:localhost:1234"
next_sync_payload = "sync_response_new_room"
return response
@app.route("/_matrix/client/v3/publicRooms", methods=["POST"])
def public_rooms():
if request.get_json()["filter"]["generic_search_term"] == "forbidden":
data = dict()
data["errcode"] = "M_FORBIDDEN"
data["error"] = "You are not allowed to search for this. Go to https://wikipedia.org for more information"
return data, 403
return dict()
if __name__ == "__main__":
app.run(ssl_context='adhoc', port=1234)

View File

@@ -3,11 +3,15 @@
enable_testing()
add_library(neochat_server STATIC server.cpp)
target_link_libraries(neochat_server PUBLIC Qt::HttpServer QuotientQt6)
add_definitions(-DDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" )
ecm_add_test(
neochatroomtest.cpp
LINK_LIBRARIES neochat Qt::Test
LINK_LIBRARIES neochat Qt::Test Qt::HttpServer neochat_server
TEST_NAME neochatroomtest
)
@@ -37,26 +41,14 @@ ecm_add_test(
ecm_add_test(
chatbarcachetest.cpp
LINK_LIBRARIES neochat Qt::Test
LINK_LIBRARIES neochat Qt::Test Qt::HttpServer neochat_server
TEST_NAME chatbarcachetest
)
ecm_add_test(
chatdocumenthandlertest.cpp
timelinemessagemodeltest.cpp
LINK_LIBRARIES neochat Qt::Test
TEST_NAME chatdocumenthandlertest
)
ecm_add_test(
messageeventmodeltest.cpp
LINK_LIBRARIES neochat Qt::Test
TEST_NAME messageeventmodeltest
)
ecm_add_test(
actionshandlertest.cpp
LINK_LIBRARIES neochat Qt::Test
TEST_NAME actionshandlertest
TEST_NAME timelinemessagemodeltest
)
ecm_add_test(
@@ -82,3 +74,75 @@ ecm_add_test(
LINK_LIBRARIES neochat Qt::Test
TEST_NAME linkpreviewertest
)
ecm_add_test(
messagecontentmodeltest.cpp
LINK_LIBRARIES neochat Qt::Test
TEST_NAME messagecontentmodeltest
)
ecm_add_test(
actionstest.cpp
LINK_LIBRARIES neochat Qt::Test neochat_server
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
TEST_NAME roommanagertest
)
ecm_add_test(
modeltest.cpp
LINK_LIBRARIES neochat Qt::Test neochat_server Devtools
TEST_NAME modeltest
)
ecm_add_test(
blockcachetest.cpp
LINK_LIBRARIES neochat Qt::Test
TEST_NAME blockcachetest
)
macro(add_qml_tests)
if (WIN32)
set(_extra_args -platform offscreen)
endif()
foreach(test ${ARGV})
add_test(NAME ${test}
COMMAND qmltest
${_extra_args}
-input ${test}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endforeach()
endmacro()
add_executable(qmltest qmltest.cpp
chatkeyhelpertesthelper.h
chatmarkdownhelpertestwrapper.h
chattextitemhelpertesthelper.h
)
qt_add_qml_module(qmltest URI NeoChatTestUtils)
target_link_libraries(qmltest
PRIVATE
Qt6::Qml
Qt6::QuickTest
LibNeoChat
LibNeoChatplugin
)
add_qml_tests(
chattextitemhelpertest.qml
chatmarkdownhelpertest.qml
chatkeyhelpertest.qml
)

View File

@@ -1,43 +0,0 @@
// 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 "actionshandler.h"
#include "chatbarcache.h"
#include "testutils.h"
class ActionsHandlerTest : public QObject
{
Q_OBJECT
private:
Quotient::Connection *connection = Quotient::Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
ActionsHandler *actionsHandler = new ActionsHandler(this);
private Q_SLOTS:
void nullObject();
};
void ActionsHandlerTest::nullObject()
{
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
actionsHandler->handleMessageEvent(nullptr);
auto chatBarCache = new ChatBarCache(this);
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
actionsHandler->handleMessageEvent(chatBarCache);
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"));
actionsHandler->setRoom(room);
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
actionsHandler->handleMessageEvent(nullptr);
// The final one should throw no warning so we make sure.
QTest::failOnWarning("ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
actionsHandler->handleMessageEvent(chatBarCache);
}
QTEST_GUILESS_MAIN(ActionsHandlerTest)
#include "actionshandlertest.moc"

153
autotests/actionstest.cpp Normal file
View File

@@ -0,0 +1,153 @@
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QObject>
#include <QTest>
#include <QSignalSpy>
#include <QVariantList>
#include "accountmanager.h"
#include "blockcache.h"
#include "chatbarcache.h"
#include "enums/messagecomponenttype.h"
#include "models/actionsmodel.h"
#include "server.h"
#include "testutils.h"
using namespace Quotient;
//TODO: rainbow, rainbowme, plain, spoiler, me, join, knock, j, part, leave, nick, roomnick, myroomnick, ignore, unignore, react, ban, unban, kick
class ActionsTest : public QObject
{
Q_OBJECT
private:
Connection *connection = nullptr;
NeoChatRoom *room = nullptr;
void expectMessage(const QString &actionName, const QString &args, MessageType::Type type, const QString &message);
Server server;
private Q_SLOTS:
void initTestCase();
void testActions();
void testActions_data();
void testInvite();
};
void ActionsTest::initTestCase()
{
Connection::setRoomType<NeoChatRoom>();
server.start();
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
auto accountManager = new AccountManager(true);
QSignalSpy spy(accountManager, &AccountManager::connectionAdded);
connection = accountManager->accounts()->front();
auto roomId = server.createRoom(u"@user:localhost:1234"_s);
server.inviteUser(roomId, u"@invited:example.com"_s);
server.banUser(roomId, u"@banned:example.com"_s);
server.joinUser(roomId, u"@example:example.com"_s);
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());
room = dynamic_cast<NeoChatRoom *>(connection->room(roomId));
QVERIFY(room);
}
void ActionsTest::testActions_data()
{
QTest::addColumn<QString>("command");
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)
<< 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)
<< std::make_optional(Quotient::RoomMessageEvent::MsgType::Text);
QTest::newRow("unflip") << u"/unflip Hello"_s << std::make_optional(u"┬──┬ ( ゜-゜ノ) Hello"_s)
<< std::make_optional(Quotient::RoomMessageEvent::MsgType::Text);
QTest::newRow("rainbow") << u"/rainbow Hello"_s << std::optional<QString>() << std::optional<Quotient::RoomMessageEvent::MsgType>();
QTest::newRow("rainbowme") << u"/rainbowme Hello"_s << std::optional<QString>() << std::optional<Quotient::RoomMessageEvent::MsgType>();
QTest::newRow("plain") << u"/plain <b>Hello</b>"_s << std::optional<QString>() << std::optional<Quotient::RoomMessageEvent::MsgType>();
QTest::newRow("spoiler") << u"/spoiler Hello"_s << std::optional<QString>() << std::optional<Quotient::RoomMessageEvent::MsgType>();
QTest::newRow("me") << u"/me Hello"_s << std::make_optional(u"Hello"_s) << std::make_optional(Quotient::RoomMessageEvent::MsgType::Emote);
QTest::newRow("notice") << u"/notice Hello"_s << std::make_optional(u"Hello"_s) << std::make_optional(Quotient::RoomMessageEvent::MsgType::Notice);
QTest::newRow("message") << u"Hello"_s << std::make_optional(u"Hello"_s) << std::make_optional(Quotient::RoomMessageEvent::MsgType::Text);
QTest::newRow("invite") << u"/invite @foo:example.com"_s << std::optional<QString>() << std::optional<Quotient::RoomMessageEvent::MsgType>();
//TODO: join, knock, j, part, leave, nick, roomnick, myroomnick, ignore, unignore, react, ban, unban, kick
}
void ActionsTest::testActions()
{
QFETCH(QString, command);
QFETCH(std::optional<QString>, resultText);
QFETCH(std::optional<Quotient::RoomMessageEvent::MsgType>, type);
auto cache = new ChatBarCache(room);
cache->cache() += Block::CacheItem{.type = MessageComponentType::Text, .content = QTextDocumentFragment::fromMarkdown(command)};
auto result = ActionsModel::handleAction(room, cache);
QCOMPARE(resultText, std::get<std::optional<QString>>(result));
QCOMPARE(type, std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result));
}
static ActionsModel::Action findAction(const QString &name)
{
for (const auto &action : ActionsModel::allActions()) {
if (action.prefix == name) {
return action;
}
}
return {};
}
void ActionsTest::expectMessage(const QString &actionName, const QString &args, MessageType::Type type, const QString &message)
{
auto action = findAction(actionName);
QSignalSpy spy(room, &NeoChatRoom::showMessage);
auto result = action.handle(args, room, nullptr);
auto expected = QVariantList {type, message};
auto signal = spy.takeFirst();
QCOMPARE(signal, expected);
}
void ActionsTest::testInvite()
{
expectMessage(u"invite"_s, u"foo"_s, MessageType::Error, u"'foo' does not look like a matrix id."_s);
expectMessage(u"invite"_s, u"@invited:example.com"_s, MessageType::Information, u"@invited:example.com is already invited to this room."_s);
QCOMPARE(room->memberState(u"@invited:example.com"_s), Membership::Invite);
expectMessage(u"invite"_s, u"@banned:example.com"_s, MessageType::Information, u"@banned:example.com is banned from this room."_s);
QCOMPARE(room->memberState(u"@banned:example.com"_s), Membership::Ban);
expectMessage(u"invite"_s, connection->userId(), MessageType::Positive, u"You are already in this room."_s);
QCOMPARE(room->memberState(connection->userId()), Membership::Join);
expectMessage(u"invite"_s, u"@example:example.com"_s, MessageType::Information, u"@example:example.com is already in this room."_s);
QCOMPARE(room->memberState(u"@example:example.com"_s), Membership::Join);
QCOMPARE(room->memberState(u"@user:example.com"_s), Membership::Leave);
expectMessage(u"invite"_s, u"@user:example.com"_s, MessageType::Positive, u"@user:example.com was invited into this room."_s);
QSignalSpy spy(room, &NeoChatRoom::changed);
QVERIFY(spy.wait());
auto tries = 0;
while (room->memberState(u"@user:example.com"_s) != Membership::Invite) {
QVERIFY(spy.wait());
tries += 1;
if (tries > 3) {
QVERIFY(false);
}
}
QCOMPARE(room->memberState(u"@user:example.com"_s), Membership::Invite);
}
QTEST_MAIN(ActionsTest)
#include "actionstest.moc"

View File

@@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: 2026 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 <QObject>
#include <QTest>
#include "blockcache.h"
#include "enums/messagecomponenttype.h"
using namespace Block;
class BlockCacheTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void toStringTest_data();
void toStringTest();
};
void BlockCacheTest::toStringTest_data()
{
QTest::addColumn<QString>("inputString");
QTest::addColumn<MessageComponentType::Type>("itemType");
QTest::addColumn<QString>("outputstring");
QTest::newRow("plainText") << u"test string"_s << MessageComponentType::Text << u"test string"_s;
QTest::newRow("list") << u"- list 1\n- list 2\n- list 3\n"_s << MessageComponentType::Text << u"- list 1\n- list 2\n- list 3"_s;
QTest::newRow("code") << u"for (some code) {\n\n do something\n\n}"_s << MessageComponentType::Code
<< u"```\nfor (some code) {\n do something\n}\n```"_s;
QTest::newRow("quote") << u"\"this is a quote\""_s << MessageComponentType::Quote << u"> this is a quote"_s;
QTest::newRow("heading") << u"# heading\n\nnext line"_s << MessageComponentType::Text << u"# heading\n\nnext line"_s;
}
void BlockCacheTest::toStringTest()
{
QFETCH(QString, inputString);
QFETCH(MessageComponentType::Type, itemType);
QFETCH(QString, outputstring);
Cache cache;
cache += CacheItem{
.type = itemType,
.content = QTextDocumentFragment::fromMarkdown(inputString),
};
QCOMPARE(cache.toString(), outputstring);
}
QTEST_MAIN(BlockCacheTest)
#include "blockcachetest.moc"

View File

@@ -6,12 +6,19 @@
#include <QObject>
#include <QTest>
#include <QSignalSpy>
#include <Quotient/roommember.h>
#include <Quotient/syncdata.h>
#include <qtestcase.h>
#include <KLocalizedString>
#include "accountmanager.h"
#include "blockcache.h"
#include "chatbarcache.h"
#include "neochatroom.h"
#include "server.h"
#include "testutils.h"
using namespace Quotient;
@@ -22,119 +29,148 @@ class ChatBarCacheTest : public QObject
private:
Connection *connection = nullptr;
TestUtils::TestRoom *room = nullptr;
NeoChatRoom *room = nullptr;
Server server;
QString eventId;
private Q_SLOTS:
void initTestCase();
void empty();
void noRoom();
void badParent();
void reply();
void replyMissingUser();
void edit();
void attachment();
};
void ChatBarCacheTest::initTestCase()
{
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-min-sync.json"));
Connection::setRoomType<NeoChatRoom>();
server.start();
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
auto accountManager = new AccountManager(true, this);
QSignalSpy spy(accountManager, &AccountManager::connectionAdded);
connection = dynamic_cast<NeoChatConnection *>(accountManager->accounts()->front());
const auto roomId = server.createRoom(u"@user:localhost:1234"_s);
eventId = server.sendEvent(roomId,
u"m.room.message"_s,
QJsonObject{
{u"body"_s, u"foo"_s},
{u"msgtype"_s, u"m.text"_s},
});
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());
room = dynamic_cast<NeoChatRoom *>(connection->room(roomId));
QVERIFY(room);
server.joinUser(room->id(), u"@foo:server.com"_s);
QVERIFY(syncSpy.wait());
QVERIFY(syncSpy.wait());
}
void ChatBarCacheTest::empty()
{
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
QCOMPARE(chatBarCache->text(), QString());
QCOMPARE(chatBarCache->cache().toString(), QString());
QCOMPARE(chatBarCache->isReplying(), false);
QCOMPARE(chatBarCache->replyId(), QString());
QCOMPARE(chatBarCache->isEditing(), false);
QCOMPARE(chatBarCache->editId(), QString());
QCOMPARE(chatBarCache->relationUser(), room->getUser(nullptr));
QCOMPARE(chatBarCache->relationAuthor(), room->member(QString()));
QCOMPARE(chatBarCache->relationMessage(), QString());
QCOMPARE(chatBarCache->attachmentPath(), QString());
}
void ChatBarCacheTest::noRoom()
{
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache());
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
// These should return empty even though a reply ID has been set because the
// ChatBarCache has no parent.
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.");
QCOMPARE(chatBarCache->relationUser(), QVariantMap());
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.");
QCOMPARE(chatBarCache->relationMessage(), QString());
}
void ChatBarCacheTest::badParent()
{
QScopedPointer<QObject> badParent(new QObject());
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(badParent.get()));
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
// These should return empty even though a reply ID has been set because the
// ChatBarCache has no parent.
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.");
QCOMPARE(chatBarCache->relationUser(), QVariantMap());
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.");
QCOMPARE(chatBarCache->relationMessage(), QString());
}
void ChatBarCacheTest::reply()
{
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
chatBarCache->setText(QLatin1String("some text"));
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
chatBarCache->cache() += Block::CacheItem{.type = MessageComponentType::Text, .content = QTextDocumentFragment::fromMarkdown(u"some text"_s)};
chatBarCache->setAttachmentPath(u"some/path"_s);
chatBarCache->setReplyId(eventId);
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
QCOMPARE(chatBarCache->cache().toString(), u"some text"_s);
QCOMPARE(chatBarCache->isReplying(), true);
QCOMPARE(chatBarCache->replyId(), QLatin1String("$153456789:example.org"));
QCOMPARE(chatBarCache->replyId(), eventId);
QCOMPARE(chatBarCache->isEditing(), false);
QCOMPARE(chatBarCache->editId(), QString());
QCOMPARE(chatBarCache->relationUser(), room->getUser(room->user(QLatin1String("@example:example.org"))));
QCOMPARE(chatBarCache->relationMessage(), QLatin1String("This is an example\ntext message"));
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@foo:server.com"_s));
QCOMPARE(chatBarCache->relationMessage(), u"foo"_s);
QCOMPARE(chatBarCache->attachmentPath(), QString());
QCOMPARE(chatBarCache->relationAuthorIsPresent(), true);
}
void ChatBarCacheTest::replyMissingUser()
{
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
chatBarCache->cache() += Block::CacheItem{.type = MessageComponentType::Text, .content = QTextDocumentFragment::fromMarkdown(u"some text"_s)};
chatBarCache->setAttachmentPath(u"some/path"_s);
chatBarCache->setReplyId(eventId);
QCOMPARE(chatBarCache->cache().toString(), u"some text"_s);
QCOMPARE(chatBarCache->isReplying(), true);
QCOMPARE(chatBarCache->replyId(), eventId);
QCOMPARE(chatBarCache->isEditing(), false);
QCOMPARE(chatBarCache->editId(), QString());
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@foo:server.com"_s));
QCOMPARE(chatBarCache->relationMessage(), u"foo"_s);
QCOMPARE(chatBarCache->attachmentPath(), QString());
QCOMPARE(chatBarCache->relationAuthorIsPresent(), true);
QSignalSpy relationAuthorIsPresentSpy(chatBarCache.get(), &ChatBarCache::relationAuthorIsPresentChanged);
// sync again, which will simulate the reply user leaving the room
QSignalSpy syncSpy(connection, &Connection::syncDone);
server.sendStateEvent(room->id(), u"m.room.member"_s, u"@foo:server.com"_s, {{u"membership"_s, u"leave"_s}});
QVERIFY(syncSpy.wait());
QVERIFY(syncSpy.wait());
QTRY_COMPARE(relationAuthorIsPresentSpy.count(), 1);
QCOMPARE(chatBarCache->relationAuthorIsPresent(), false);
}
void ChatBarCacheTest::edit()
{
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
chatBarCache->setText(QLatin1String("some text"));
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
chatBarCache->setEditId(QLatin1String("$153456789:example.org"));
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
chatBarCache->cache() += Block::CacheItem{.type = MessageComponentType::Text, .content = QTextDocumentFragment::fromMarkdown(u"some text"_s)};
chatBarCache->setAttachmentPath(u"some/path"_s);
connect(chatBarCache.get(), &ChatBarCache::relationIdChanged, this, [this](const QString &oldEventId, const QString &newEventId) {
QCOMPARE(oldEventId, QString());
QCOMPARE(newEventId, eventId);
});
chatBarCache->setEditId(eventId);
QCOMPARE(chatBarCache->cache().toString(), u"some text"_s);
QCOMPARE(chatBarCache->isReplying(), false);
QCOMPARE(chatBarCache->replyId(), QString());
QCOMPARE(chatBarCache->isEditing(), true);
QCOMPARE(chatBarCache->editId(), QLatin1String("$153456789:example.org"));
QCOMPARE(chatBarCache->relationUser(), room->getUser(room->user(QLatin1String("@example:example.org"))));
QCOMPARE(chatBarCache->relationMessage(), QLatin1String("This is an example\ntext message"));
QCOMPARE(chatBarCache->editId(), eventId);
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@foo:server.com"_s));
QCOMPARE(chatBarCache->relationMessage(), u"foo"_s);
QCOMPARE(chatBarCache->attachmentPath(), QString());
}
void ChatBarCacheTest::attachment()
{
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
chatBarCache->setText(QLatin1String("some text"));
chatBarCache->setEditId(QLatin1String("$153456789:example.org"));
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
chatBarCache->cache() += Block::CacheItem{.type = MessageComponentType::Text, .content = QTextDocumentFragment::fromMarkdown(u"some text"_s)};
chatBarCache->setEditId(eventId);
chatBarCache->setAttachmentPath(u"some/path"_s);
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
QCOMPARE(chatBarCache->cache().toString(), u"some text"_s);
QCOMPARE(chatBarCache->isReplying(), false);
QCOMPARE(chatBarCache->replyId(), QString());
QCOMPARE(chatBarCache->isEditing(), false);
QCOMPARE(chatBarCache->editId(), QString());
QCOMPARE(chatBarCache->relationUser(), room->getUser(nullptr));
QCOMPARE(chatBarCache->relationAuthor(), room->member(QString()));
QCOMPARE(chatBarCache->relationMessage(), QString());
QCOMPARE(chatBarCache->attachmentPath(), QLatin1String("some/path"));
QCOMPARE(chatBarCache->attachmentPath(), u"some/path"_s);
}
QTEST_MAIN(ChatBarCacheTest)

View File

@@ -1,36 +0,0 @@
// 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 <QObject>
#include <QTest>
#include "chatdocumenthandler.h"
#include "neochatconfig.h"
class ChatDocumentHandlerTest : public QObject
{
Q_OBJECT
private:
ChatDocumentHandler emptyHandler;
private Q_SLOTS:
void initTestCase();
void nullComplete();
};
void ChatDocumentHandlerTest::initTestCase()
{
// HACK: this is to stop KStatusNotifierItem SEGFAULTING on cleanup.
NeoChatConfig::self()->setSystemTray(false);
}
void ChatDocumentHandlerTest::nullComplete()
{
QTest::ignoreMessage(QtWarningMsg, "complete called with m_document set to nullptr.");
emptyHandler.complete(0);
}
QTEST_MAIN(ChatDocumentHandlerTest)
#include "chatdocumenthandlertest.moc"

View File

@@ -0,0 +1,88 @@
// SPDX-FileCopyrightText: 2026 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtTest
import org.kde.neochat.libneochat
import NeoChatTestUtils
TestCase {
name: "ChatKeyHelperTest"
TextEdit {
id: textEdit
Keys.onPressed: (event) => {
event.accepted = testHelper.keyHelper.handleKey(event.key, event.modifiers);
}
}
ChatTextItemHelper {
id: textItemHelper
textItem: textEdit
}
ChatKeyHelperTestHelper {
id: testHelper
textItem: textItemHelper
}
SignalSpy {
id: spyUp
target: testHelper.keyHelper
signalName: "unhandledUp"
}
SignalSpy {
id: spyDown
target: testHelper.keyHelper
signalName: "unhandledDown"
}
SignalSpy {
id: spyDelete
target: testHelper.keyHelper
signalName: "unhandledDelete"
}
SignalSpy {
id: spyBackSpace
target: testHelper.keyHelper
signalName: "unhandledBackspace"
}
function init(): void {
textEdit.clear();
spyUp.clear();
spyDown.clear();
spyDelete.clear();
spyBackSpace.clear();
textEdit.forceActiveFocus();
}
function cleanupTestCase(): void {
testHelper.textItem = null;
textItemHelper.textItem = null;
}
function test_upDown(): void {
textEdit.insert(0, "line 1\nline 2\nline 3")
textEdit.cursorPosition = 0;
keyClick(Qt.Key_Up);
compare(spyUp.count, 1);
compare(spyDown.count, 0);
keyClick(Qt.Key_Down);
compare(spyUp.count, 1);
compare(spyDown.count, 0);
keyClick(Qt.Key_Down);
compare(spyUp.count, 1);
compare(spyDown.count, 0);
keyClick(Qt.Key_Down);
compare(spyUp.count, 1);
compare(spyDown.count, 1);
}
}

View File

@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QObject>
#include <QQuickItem>
#include <QQuickTextDocument>
#include <QTextCursor>
#include <QTextDocumentFragment>
#include "chatkeyhelper.h"
#include "chattextitemhelper.h"
class ChatKeyHelperTestHelper : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(ChatTextItemHelper *textItem READ textItem WRITE setTextItem NOTIFY textItemChanged)
Q_PROPERTY(ChatKeyHelper *keyHelper READ keyHelper CONSTANT)
public:
explicit ChatKeyHelperTestHelper(QObject *parent = nullptr)
: QObject(parent)
, m_keyHelper(new ChatKeyHelper(this))
{
}
ChatTextItemHelper *textItem() const
{
return m_keyHelper->textItem();
}
void setTextItem(ChatTextItemHelper *textItem)
{
if (textItem == m_keyHelper->textItem()) {
return;
}
m_keyHelper->setTextItem(textItem);
Q_EMIT textItemChanged();
}
ChatKeyHelper *keyHelper() const
{
return m_keyHelper;
}
Q_SIGNALS:
void textItemChanged();
private:
QPointer<ChatKeyHelper> m_keyHelper;
};

View File

@@ -0,0 +1,173 @@
// SPDX-FileCopyrightText: 2026 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtTest
import org.kde.neochat.libneochat
import NeoChatTestUtils
TestCase {
name: "ChatMarkdownHelperTest"
TextEdit {
id: textEdit
textFormat: TextEdit.RichText
}
TextEdit {
id: textEdit2
}
ChatMarkdownHelperTestWrapper {
id: chatMarkdownHelper
textItem: textEdit
}
SignalSpy {
id: spyItem
target: chatMarkdownHelper
signalName: "textItemChanged"
}
SignalSpy {
id: spyUnhandledFormat
target: chatMarkdownHelper
signalName: "unhandledBlockFormat"
}
function initTestCase(): void {
textEdit.forceActiveFocus();
}
function cleanup(): void {
chatMarkdownHelper.clear();
compare(chatMarkdownHelper.checkText(""), true);
compare(chatMarkdownHelper.checkFormats([]), true);
compare(textEdit.cursorPosition, 0);
}
function test_item(): void {
spyItem.clear();
compare(chatMarkdownHelper.textItem, textEdit);
chatMarkdownHelper.textItem = textEdit2;
compare(chatMarkdownHelper.textItem, textEdit2);
chatMarkdownHelper.textItem = textEdit;
compare(chatMarkdownHelper.textItem, textEdit);
}
function test_textFormat_data() {
return [
{tag: "bold", input: "**b** ", outText: ["*", "**", "b", "b*", "b**", "b "], outFormats: [[], [], [RichFormat.Bold], [RichFormat.Bold], [RichFormat.Bold], []], unhandled: 0},
{tag: "italic", input: "*i* ", outText: ["*", "i", "i*", "i "], outFormats: [[], [RichFormat.Italic], [RichFormat.Italic], []], unhandled: 0},
{tag: "heading 1", input: "# h", outText: ["#", "# ", "h"], outFormats: [[], [], [RichFormat.Bold, RichFormat.Heading1]], unhandled: 0},
{tag: "heading 2", input: "## h", outText: ["#", "##", "## ", "h"], outFormats: [[], [], [], [RichFormat.Bold, RichFormat.Heading2]], unhandled: 0},
{tag: "heading 3", input: "### h", outText: ["#", "##", "###", "### ", "h"], outFormats: [[], [], [], [], [RichFormat.Bold, RichFormat.Heading3]], unhandled: 0},
{tag: "heading 4", input: "#### h", outText: ["#", "##", "###", "####", "#### ", "h"], outFormats: [[], [], [], [], [], [RichFormat.Bold, RichFormat.Heading4]], unhandled: 0},
{tag: "heading 5", input: "##### h", outText: ["#", "##", "###", "####", "#####", "##### ", "h"], outFormats: [[], [], [], [], [], [], [RichFormat.Bold, RichFormat.Heading5]], unhandled: 0},
{tag: "heading 6", input: "###### h", outText: ["#", "##", "###", "####", "#####", "######", "###### ", "h"], outFormats: [[], [], [], [], [], [] ,[], [RichFormat.Bold, RichFormat.Heading6]], unhandled: 0},
{tag: "quote", input: "> q", outText: [">", "> ", "q"], outFormats: [[], [], []], unhandled: 1},
{tag: "quote - no space", input: ">q", outText: [">", "q"], outFormats: [[], [], []], unhandled: 1},
{tag: "unorderedlist 1", input: "* l", outText: ["*", "* ", "l"], outFormats: [[], [], [RichFormat.UnorderedList]], unhandled: 0},
{tag: "unorderedlist 2", input: "- l", outText: ["-", "- ", "l"], outFormats: [[], [], [RichFormat.UnorderedList]], unhandled: 0},
{tag: "orderedlist 1", input: "1. l", outText: ["1", "1.", "1. ", "l"], outFormats: [[], [], [], [RichFormat.OrderedList]], unhandled: 0},
{tag: "orderedlist 2", input: "1) l", outText: ["1", "1)", "1) ", "l"], outFormats: [[], [], [], [RichFormat.OrderedList]], unhandled: 0},
{tag: "inline code", input: "`c` ", outText: ["`", "c", "c`", "c "], outFormats: [[], [RichFormat.InlineCode], [RichFormat.InlineCode], []], unhandled: 0},
{tag: "code", input: "``` ", outText: ["`", "``", "```", " "], outFormats: [[], [], [], []], unhandled: 1},
{tag: "strikethrough", input: "~~s~~ ", outText: ["~", "~~", "s", "s~", "s~~", "s "], outFormats: [[], [], [RichFormat.Strikethrough], [RichFormat.Strikethrough], [RichFormat.Strikethrough], []], unhandled: 0},
{tag: "underline", input: "_u_ ", outText: ["_", "u", "u_", "u "], outFormats: [[], [RichFormat.Underline], [RichFormat.Underline], []], unhandled: 0},
{tag: "multiple closable", input: "***_~~t~~_*** ", outText: ["*", "**", "*", "_", "~", "~~", "t", "t~", "t~~", "t_", "t*", "t**", "t*", "t "], outFormats: [[], [], [RichFormat.Bold], [RichFormat.Bold, RichFormat.Italic], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline, RichFormat.Strikethrough], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline, RichFormat.Strikethrough], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline, RichFormat.Strikethrough], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline], [RichFormat.Bold, RichFormat.Italic], [RichFormat.Bold, RichFormat.Italic], [RichFormat.Italic], []], unhandled: 0},
{tag: "nonclosable closable", input: "* **b** ", outText: ["*", "* ", "*", "**", "b", "b*", "b**", "b "], outFormats: [[], [], [RichFormat.UnorderedList], [RichFormat.UnorderedList], [RichFormat.Bold, RichFormat.UnorderedList], [RichFormat.Bold, RichFormat.UnorderedList], [RichFormat.Bold, RichFormat.UnorderedList], [RichFormat.UnorderedList]], unhandled: 0},
{tag: "not at line start", input: " 1) ", outText: [" ", " 1", " 1)", " 1) "], outFormats: [[], [], [], []], unhandled: 0},
]
}
function test_textFormat(data): void {
spyUnhandledFormat.clear();
compare(spyUnhandledFormat.count, 0);
for (let i = 0; i < data.input.length; i++) {
keyClick(data.input[i]);
compare(chatMarkdownHelper.checkText(data.outText[i]), true);
compare(chatMarkdownHelper.checkFormats(data.outFormats[i]), true);
}
compare(spyUnhandledFormat.count, data.unhandled);
}
function test_backspace(): void {
keyClick("*");
compare(chatMarkdownHelper.checkText("*"), true);
compare(chatMarkdownHelper.checkFormats([]), true);
keyClick("*");
compare(chatMarkdownHelper.checkText("**"), true);
compare(chatMarkdownHelper.checkFormats([]), true);
keyClick("b");
compare(chatMarkdownHelper.checkText("b"), true);
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
keyClick("o");
compare(chatMarkdownHelper.checkText("bo"), true);
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
keyClick("l");
compare(chatMarkdownHelper.checkText("bol"), true);
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
keyClick("d");
compare(chatMarkdownHelper.checkText("bold"), true);
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
keyClick(Qt.Key_Backspace);
compare(chatMarkdownHelper.checkText("bol"), true);
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
keyClick(Qt.Key_Backspace);
compare(chatMarkdownHelper.checkText("bo"), true);
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
keyClick("*");
compare(chatMarkdownHelper.checkText("bo*"), true);
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
keyClick("*");
compare(chatMarkdownHelper.checkText("bo**"), true);
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
keyClick(" ");
compare(chatMarkdownHelper.checkText("bo "), true);
compare(chatMarkdownHelper.checkFormats([]), true);
}
function test_cursorMove(): void {
keyClick("t");
keyClick("e");
keyClick("s");
keyClick("t");
compare(chatMarkdownHelper.checkText("test"), true);
compare(chatMarkdownHelper.checkFormats([]), true);
keyClick("*");
keyClick("*");
keyClick("b");
compare(chatMarkdownHelper.checkText("testb"), true);
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
textEdit.cursorPosition = 2;
keyClick("*");
keyClick("*");
keyClick("b");
compare(chatMarkdownHelper.checkText("tebstb"), true);
compare(chatMarkdownHelper.checkFormats([]), true);
}
function test_insertText(): void {
textEdit.insert(0, "test");
compare(chatMarkdownHelper.checkText("test"), true);
compare(chatMarkdownHelper.checkFormats([]), true);
textEdit.insert(4, "**b");
compare(chatMarkdownHelper.checkText("test**b"), true);
compare(chatMarkdownHelper.checkFormats([]), true);
textEdit.clear();
textEdit.insert(0, "test");
compare(chatMarkdownHelper.checkText("test"), true);
compare(chatMarkdownHelper.checkFormats([]), true);
textEdit.insert(2, "**b");
compare(chatMarkdownHelper.checkText("te**bst"), true);
compare(chatMarkdownHelper.checkFormats([]), true);
}
}

View File

@@ -0,0 +1,82 @@
// SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QObject>
#include <QQuickItem>
#include <QTextCursor>
#include "chatmarkdownhelper.h"
#include "chattextitemhelper.h"
#include "enums/richformat.h"
class ChatMarkdownHelperTestWrapper : public QObject
{
Q_OBJECT
QML_ELEMENT
/**
* @brief The QML text Item the ChatMerkdownHelper is handling.
*/
Q_PROPERTY(QQuickItem *textItem READ textItem WRITE setTextItem NOTIFY textItemChanged)
public:
explicit ChatMarkdownHelperTestWrapper(QObject *parent = nullptr)
: QObject(parent)
, m_chatMarkdownHelper(new ChatMarkdownHelper(this))
, m_textItem(new ChatTextItemHelper(this))
{
m_chatMarkdownHelper->setTextItem(m_textItem);
connect(m_chatMarkdownHelper, &ChatMarkdownHelper::textItemChanged, this, &ChatMarkdownHelperTestWrapper::textItemChanged);
connect(m_chatMarkdownHelper, &ChatMarkdownHelper::unhandledBlockFormat, this, &ChatMarkdownHelperTestWrapper::unhandledBlockFormat);
}
QQuickItem *textItem() const
{
return m_textItem->textItem();
}
void setTextItem(QQuickItem *textItem)
{
m_textItem->setTextItem(textItem);
}
Q_INVOKABLE bool checkText(const QString &text)
{
const auto doc = m_textItem->document();
if (!doc) {
return false;
}
return text == doc->toPlainText();
}
Q_INVOKABLE bool checkFormats(QList<RichFormat::Format> formats)
{
const auto cursor = m_textItem->textCursor();
if (cursor.isNull()) {
return false;
}
return RichFormat::formatsAtCursor(cursor) == formats;
}
Q_INVOKABLE void clear()
{
auto cursor = m_textItem->textCursor();
if (cursor.isNull()) {
return;
}
cursor.select(QTextCursor::Document);
cursor.removeSelectedText();
cursor.setBlockCharFormat(RichFormat::charFormatForFormat(RichFormat::Paragraph));
cursor.setBlockFormat(RichFormat::blockFormatForFormat(RichFormat::Paragraph));
}
Q_SIGNALS:
void textItemChanged();
void unhandledBlockFormat(RichFormat::Format format);
private:
QPointer<ChatMarkdownHelper> m_chatMarkdownHelper;
QPointer<ChatTextItemHelper> m_textItem;
};

View File

@@ -0,0 +1,301 @@
// SPDX-FileCopyrightText: 2026 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtTest
import org.kde.neochat.libneochat
import NeoChatTestUtils
TestCase {
name: "ChatTextItemHelperTest"
TextEdit {
id: textEdit
}
TextEdit {
id: textEdit2
}
ChatTextItemHelper {
id: textItemHelper
textItem: textEdit
}
ChatTextItemHelperTestHelper {
id: testHelper
textItem: textItemHelper
}
SignalSpy {
id: spyItem
target: textItemHelper
signalName: "textItemChanged"
}
SignalSpy {
id: spyContentsChanged
target: textItemHelper
signalName: "contentsChanged"
}
SignalSpy {
id: spyContentsChange
target: textItemHelper
signalName: "contentsChange"
}
SignalSpy {
id: spyCursor
target: textItemHelper
signalName: "cursorPositionChanged"
}
function init(): void {
testHelper.setFixedChars("", "");
textEdit.clear();
textEdit2.clear();
spyItem.clear();
spyContentsChange.clear();
spyContentsChanged.clear();
spyCursor.clear();
}
function cleanupTestCase(): void {
testHelper.textItem = null;
textItemHelper.textItem = null;
}
function test_item(): void {
compare(textItemHelper.textItem, textEdit);
compare(spyItem.count, 0);
textItemHelper.textItem = textEdit2;
compare(textItemHelper.textItem, textEdit2);
compare(spyItem.count, 1);
textItemHelper.textItem = textEdit;
compare(textItemHelper.textItem, textEdit);
compare(spyItem.count, 2);
}
function test_fixedChars(): void {
textEdit.forceActiveFocus();
testHelper.setFixedChars("1", "2");
compare(textEdit.text, "12");
compare(textEdit.cursorPosition, 1);
compare(spyCursor.count, 0);
keyClick("b");
compare(textEdit.text, "1b2");
compare(textEdit.cursorPosition, 2);
compare(spyCursor.count, 1);
keyClick(Qt.Key_Left);
compare(textEdit.text, "1b2");
compare(textEdit.cursorPosition, 1);
compare(spyCursor.count, 2);
keyClick(Qt.Key_Left);
compare(textEdit.text, "1b2");
compare(textEdit.cursorPosition, 1);
compare(spyCursor.count, 3);
keyClick(Qt.Key_Right);
compare(textEdit.text, "1b2");
compare(textEdit.cursorPosition, 2);
compare(spyCursor.count, 4);
keyClick(Qt.Key_Right);
compare(textEdit.text, "1b2");
compare(textEdit.cursorPosition, 2);
compare(spyCursor.count, 5);
}
function test_document(): void {
// We can't get to the QTextDocument from QML so we have to use a helper function.
compare(testHelper.compareDocuments(textEdit.textDocument), true);
textEdit.insert(0, "test text");
compare(testHelper.lineCount(), 1);
textEdit.insert(textEdit.text.length, "\ntest text");
compare(testHelper.lineCount(), 2);
textEdit.clear()
compare(textEdit.text.length, 0);
}
function test_takeFirstBlock(): void {
textEdit.insert(0, "test text");
compare(testHelper.firstBlockText(), "test text");
compare(textEdit.text.length, 0);
textEdit.insert(0, "test text\nmore test text");
compare(testHelper.firstBlockText(), "test text");
compare(textEdit.text, "more test text");
compare(testHelper.firstBlockText(), "more test text");
compare(textEdit.text, "");
compare(textEdit.text.length, 0);
}
function test_fillFragments(): void {
textEdit.insert(0, "before fragment\nmid fragment\nafter fragment");
compare(testHelper.checkFragments("before fragment\nmid fragment", "after fragment", ""), true);
textEdit.clear();
textEdit.insert(0, "before fragment\nmid fragment\nafter fragment");
textEdit.cursorPosition = 16;
compare(testHelper.checkFragments("before fragment", "mid fragment", "after fragment"), true);
textEdit.clear();
textEdit.insert(0, "before fragment\nmid fragment\nafter fragment");
textEdit.cursorPosition = 29;
compare(testHelper.checkFragments("before fragment\nmid fragment", "after fragment", ""), true);
textEdit.clear();
}
function test_insertFragment(): void {
testHelper.insertFragment("test text");
compare(textEdit.text, "test text");
compare(textEdit.cursorPosition, 9);
testHelper.insertFragment("beginning ", 1);
compare(textEdit.text, "beginning test text");
compare(textEdit.cursorPosition, 10);
testHelper.insertFragment(" end", 2);
compare(textEdit.text, "beginning test text end");
compare(textEdit.cursorPosition, 23);
textEdit.clear();
testHelper.insertFragment("test text", 0, true);
compare(textEdit.text, "test text");
compare(textEdit.cursorPosition, 0);
}
function test_cursor(): void {
// We can't get to the QTextCursor from QML so we have to use a helper function.
compare(testHelper.compareCursor(textEdit.cursorPosition, textEdit.selectionStart, textEdit.selectionEnd), true);
compare(textEdit.cursorPosition, testHelper.cursorPosition());
// Check we get the appropriate content and cursor change signals when inserting text.
textEdit.insert(0, "test text")
compare(spyContentsChange.count, 1);
compare(spyContentsChange.signalArguments[0][0], 0);
compare(spyContentsChange.signalArguments[0][1], 0);
compare(spyContentsChange.signalArguments[0][2], 9);
compare(spyContentsChanged.count, 1);
compare(spyCursor.count, 1);
compare(spyCursor.signalArguments[0][0], true);
compare(testHelper.compareCursor(textEdit.cursorPosition, textEdit.selectionStart, textEdit.selectionEnd), true);
compare(textEdit.cursorPosition, testHelper.cursorPosition());
// Check we get only get a cursor change signal when moving the cursor.
textEdit.cursorPosition = 4;
compare(spyContentsChanged.count, 1);
compare(spyCursor.count, 2);
compare(spyCursor.signalArguments[1][0], false);
textEdit.selectAll();
compare(spyContentsChanged.count, 1);
compare(spyCursor.count, 2);
compare(testHelper.compareCursor(textEdit.cursorPosition, textEdit.selectionStart, textEdit.selectionEnd), true);
compare(textEdit.cursorPosition, testHelper.cursorPosition());
// Check we get the appropriate content and cursor change signals when removing text.
textEdit.clear();
compare(spyContentsChange.count, 2);
compare(spyContentsChange.signalArguments[1][0], 0);
compare(spyContentsChange.signalArguments[1][1], 9);
compare(spyContentsChange.signalArguments[1][2], 0);
compare(spyContentsChanged.count, 2);
compare(spyCursor.count, 3);
compare(spyCursor.signalArguments[2][0], true);
}
function test_setCursor(): void {
textEdit.insert(0, "test text");
compare(textEdit.cursorPosition, 9);
compare(spyCursor.count, 1);
testHelper.setCursorPosition(5);
compare(textEdit.cursorPosition, 5);
compare(spyCursor.count, 2);
testHelper.setCursorPosition(1);
compare(textEdit.cursorPosition, 1);
compare(spyCursor.count, 3);
textEdit.cursorVisible = false;
compare(textEdit.cursorVisible, false);
testHelper.setCursorVisible(true);
compare(textEdit.cursorVisible, true);
testHelper.setCursorVisible(false);
compare(textEdit.cursorVisible, false);
}
function test_setCursorFromTextItem(): void {
textEdit.insert(0, "line 1\nline 2");
textEdit2.insert(0, "line 1\nline 2");
testHelper.setCursorFromTextItem(textEdit2, false, 0);
compare(textEdit.cursorPosition, 7);
testHelper.setCursorFromTextItem(textEdit2, true, 7);
compare(textEdit.cursorPosition, 0);
testHelper.setCursorFromTextItem(textEdit2, false, 1);
compare(textEdit.cursorPosition, 8);
testHelper.setCursorFromTextItem(textEdit2, true, 8);
compare(textEdit.cursorPosition, 1);
testHelper.setFixedChars("1", "2");
testHelper.setCursorFromTextItem(textEdit2, false, 0);
compare(textEdit.cursorPosition, 8);
testHelper.setCursorFromTextItem(textEdit2, true, 7);
compare(textEdit.cursorPosition, 1);
}
function test_mergeFormat(): void {
textEdit.insert(0, "lots of text");
testHelper.setCursorPosition(0);
testHelper.mergeFormatOnCursor(RichFormat.Bold);
compare(testHelper.checkFormatsAtCursor([RichFormat.Bold]), true);
testHelper.mergeFormatOnCursor(RichFormat.Italic);
compare(testHelper.checkFormatsAtCursor([RichFormat.Bold, RichFormat.Italic]), true);
testHelper.setCursorPosition(6);
compare(testHelper.checkFormatsAtCursor([]), true);
testHelper.mergeFormatOnCursor(RichFormat.Underline);
compare(testHelper.checkFormatsAtCursor([RichFormat.Underline]), true);
testHelper.setCursorPosition(9);
compare(testHelper.checkFormatsAtCursor([]), true);
testHelper.mergeFormatOnCursor(RichFormat.Strikethrough);
compare(testHelper.checkFormatsAtCursor([RichFormat.Strikethrough]), true);
textEdit.clear();
textEdit.insert(0, "heading");
testHelper.mergeFormatOnCursor(RichFormat.Heading1);
compare(testHelper.checkFormatsAtCursor([RichFormat.Bold, RichFormat.Heading1]), true);
testHelper.mergeFormatOnCursor(RichFormat.Heading2);
compare(testHelper.checkFormatsAtCursor([RichFormat.Bold, RichFormat.Heading2]), true);
testHelper.mergeFormatOnCursor(RichFormat.Paragraph);
compare(testHelper.checkFormatsAtCursor([]), true);
textEdit.clear();
textEdit.insert(0, "text");
testHelper.mergeFormatOnCursor(RichFormat.UnorderedList);
compare(testHelper.checkFormatsAtCursor([RichFormat.UnorderedList]), true);
compare(testHelper.markdownText(), "- text");
testHelper.mergeFormatOnCursor(RichFormat.OrderedList);
compare(testHelper.checkFormatsAtCursor([RichFormat.OrderedList]), true);
compare(testHelper.markdownText(), "1. text");
textEdit.clear();
}
function test_list(): void {
compare(testHelper.canIndentListMoreAtCursor(), true);
testHelper.indentListMoreAtCursor();
compare(testHelper.canIndentListMoreAtCursor(), true);
testHelper.indentListMoreAtCursor();
compare(testHelper.canIndentListMoreAtCursor(), true);
testHelper.indentListMoreAtCursor();
compare(testHelper.canIndentListMoreAtCursor(), false);
compare(testHelper.canIndentListLessAtCursor(), true);
testHelper.indentListLessAtCursor();
compare(testHelper.canIndentListLessAtCursor(), true);
testHelper.indentListLessAtCursor();
compare(testHelper.canIndentListLessAtCursor(), true);
testHelper.indentListLessAtCursor();
compare(testHelper.canIndentListLessAtCursor(), false);
}
function test_forceActiveFocus(): void {
textEdit2.forceActiveFocus();
compare(textEdit.activeFocus, false);
testHelper.forceActiveFocus();
compare(textEdit.activeFocus, true);
}
}

View File

@@ -0,0 +1,218 @@
// SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QObject>
#include <QQuickItem>
#include <QQuickTextDocument>
#include <QTextCursor>
#include <QTextDocumentFragment>
#include "chattextitemhelper.h"
class ChatTextItemHelperTestHelper : public QObject
{
Q_OBJECT
QML_ELEMENT
/**
* @brief The QML text Item the TextItemHelper is handling.
*/
Q_PROPERTY(ChatTextItemHelper *textItem READ textItem WRITE setTextItem NOTIFY textItemChanged)
public:
explicit ChatTextItemHelperTestHelper(QObject *parent = nullptr)
: QObject(parent)
{
}
ChatTextItemHelper *textItem() const
{
return m_textItem;
}
void setTextItem(ChatTextItemHelper *textItem)
{
if (textItem == m_textItem) {
return;
}
m_textItem = textItem;
Q_EMIT textItemChanged();
}
Q_INVOKABLE void setFixedChars(const QString &startChars, const QString &endChars)
{
if (!m_textItem) {
return;
}
m_textItem->setFixedChars(startChars, endChars);
}
Q_INVOKABLE bool compareDocuments(QQuickTextDocument *document)
{
if (!m_textItem) {
return false;
}
return document->textDocument() == m_textItem->document();
}
Q_INVOKABLE int lineCount()
{
if (!m_textItem) {
return -1;
}
return m_textItem->lineCount();
}
Q_INVOKABLE QString firstBlockText()
{
if (!m_textItem) {
return {};
}
return m_textItem->takeFirstBlock().toPlainText();
}
Q_INVOKABLE bool checkFragments(const QString &before, const QString &mid, const QString &after)
{
if (!m_textItem) {
return false;
}
bool hasBefore = false;
QTextDocumentFragment midFragment;
std::optional<QTextDocumentFragment> afterFragment = std::nullopt;
m_textItem->fillFragments(hasBefore, midFragment, afterFragment);
return hasBefore && m_textItem->document()->toPlainText() == before && midFragment.toPlainText() == mid && after.isEmpty()
? !afterFragment
: afterFragment->toPlainText() == after;
}
Q_INVOKABLE void insertFragment(const QString &text, ChatTextItemHelper::InsertPosition position = ChatTextItemHelper::Cursor, bool keepPosition = false)
{
if (!m_textItem) {
return;
}
const auto fragment = QTextDocumentFragment::fromPlainText(text);
m_textItem->insertFragment(fragment, position, keepPosition);
}
Q_INVOKABLE bool compareCursor(int cursorPosition, int selectionStart, int selectionEnd)
{
if (!m_textItem) {
return false;
}
const auto cursor = m_textItem->textCursor();
if (cursor.isNull()) {
return false;
}
const auto posSame = cursor.position() == cursorPosition;
const auto startSame = cursor.selectionStart() == selectionStart;
const auto endSame = cursor.selectionEnd() == selectionEnd;
return posSame && startSame && endSame;
}
Q_INVOKABLE int cursorPosition() const
{
if (!m_textItem) {
return -1;
}
return *m_textItem->cursorPosition();
}
Q_INVOKABLE void setCursorPosition(int pos)
{
if (!m_textItem) {
return;
}
m_textItem->setCursorPosition(pos);
}
Q_INVOKABLE void setCursorVisible(bool visible)
{
if (!m_textItem) {
return;
}
m_textItem->setCursorVisible(visible);
}
Q_INVOKABLE void setCursorFromTextItem(QQuickItem *item, bool infront, int cursorPos)
{
if (!m_textItem) {
return;
}
const auto textItem = new ChatTextItemHelper(this);
textItem->setTextItem(item);
textItem->setCursorPosition(cursorPos);
m_textItem->setCursorFromTextItem(textItem, infront);
textItem->deleteLater();
}
Q_INVOKABLE void mergeFormatOnCursor(RichFormat::Format format)
{
if (!m_textItem) {
return;
}
m_textItem->mergeFormatOnCursor(format);
}
Q_INVOKABLE bool checkFormatsAtCursor(QList<RichFormat::Format> formats)
{
const auto cursor = m_textItem->textCursor();
if (cursor.isNull()) {
return false;
}
return RichFormat::formatsAtCursor(cursor) == formats;
}
Q_INVOKABLE bool canIndentListMoreAtCursor() const
{
if (!m_textItem) {
return false;
}
return m_textItem->canIndentListMoreAtCursor();
}
Q_INVOKABLE bool canIndentListLessAtCursor() const
{
if (!m_textItem) {
return false;
}
return m_textItem->canIndentListLessAtCursor();
}
Q_INVOKABLE void indentListMoreAtCursor()
{
if (!m_textItem) {
return;
}
m_textItem->indentListMoreAtCursor();
}
Q_INVOKABLE void indentListLessAtCursor()
{
if (!m_textItem) {
return;
}
m_textItem->indentListLessAtCursor();
}
Q_INVOKABLE void forceActiveFocus() const
{
if (!m_textItem) {
return;
}
m_textItem->forceActiveFocus();
}
Q_INVOKABLE QString markdownText() const
{
if (!m_textItem) {
return {};
}
return m_textItem->markdownText();
}
Q_SIGNALS:
void textItemChanged();
private:
QPointer<ChatTextItemHelper> m_textItem;
};

View File

@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDNTCCAh2gAwIBAgIUXbyWfTfcvVLrVB1qx36pW/7IkwMwDQYJKoZIhvcNAQEL
BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE
CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAgFw0yNDEyMjQxNTAxMDNaGA8yNTcyMDcy
NDE1MDEwM1owQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEc
MBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAKlxZ540TQ1uUDAR7ZJ9ue0PzcD2dPmblIIddyekvZS59V7X
drhamclXpHE2EelR87Sexst0BaHH/jmrHwxCtwbeXHZ8ueJHkGHJ5DLZCCiwfG+Q
gml7wlSXxXz37vie2tdlZh2yJSM8yvLAYceHb2zOskaGvul7ZITIS0JrPc3o6VZk
+MYGkYtA2JfUsv3jH4oQbxOf7RXqhWNAXbB+3hlwRBwMIdyoBNK6YS9QSrTeS9jj
UqgO5QmaQZOVvpaPf1Y/rHHLd2Qa6+a/cCJ1sr2biagb75AihpQFsK/oy6D1PP70
zTe7hPWn/efEpmtCV7CQ8ti4cRu0Kjy0T8grtCsCAwEAAaMhMB8wHQYDVR0OBBYE
FIFlylzwADNLfgTDNkhFeFelaEDxMA0GCSqGSIb3DQEBCwUAA4IBAQBQ2rw4GLIU
v+GY7Qru9LttkrQPd2bZXKxDMd/jT+wjmMVtqS4MAsCuDYwaYLjU1aWyqy0mN+lY
A17kD0VjBNBy45sYqkZveY0ks8mCScBemtrIDmjz2tiueecBIEASwEPBOZgv5/MV
cz864FiChF+2r8Zl8bhycGy9DEpRjzYKvIQWSDHQ3zpuh3iBnjfoieLHWX2kKCpk
ouS3V6485rHNCWsZT5IcCwfBFQkOuWRJpIazpz4AfwZh1TK9+bgiKA5EyZjSNrKw
xGQSpMSTRQMB0/FOCL/AixhN9unVFUViqUcdtSfoHE1VyBHv9kDT/cYms/Xl4B0t
/ZSQJ/D/Km1+
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCpcWeeNE0NblAw
Ee2SfbntD83A9nT5m5SCHXcnpL2UufVe13a4WpnJV6RxNhHpUfO0nsbLdAWhx/45
qx8MQrcG3lx2fLniR5BhyeQy2QgosHxvkIJpe8JUl8V89+74ntrXZWYdsiUjPMry
wGHHh29szrJGhr7pe2SEyEtCaz3N6OlWZPjGBpGLQNiX1LL94x+KEG8Tn+0V6oVj
QF2wft4ZcEQcDCHcqATSumEvUEq03kvY41KoDuUJmkGTlb6Wj39WP6xxy3dkGuvm
v3AidbK9m4moG++QIoaUBbCv6Mug9Tz+9M03u4T1p/3nxKZrQlewkPLYuHEbtCo8
tE/IK7QrAgMBAAECggEAH9qmeKrra2F4KLlOGNKS//qPGz4Z+ozhi95/NpA1Zb7Z
3pUSCBFcROo5i2D3WA4kiymoRLpQjrv60puVcCggoWVvK4VCKsR6Y6/hOx/q9T9M
fWrE4ZC3FVEc+uPfZJT0nja9TkrdyXSV0LITD8Ap1eI7yJ9vR5R/bqj64QcpLMrU
QeoQIy1oTMR+qdjj33duyRwBZU3Yf8FRB2iW6OILZ8hzFo1jngec7dph9a1RK4e0
mEPdc9ywsKlDM7P0Y7zdmjar5XtQn87GiwNhz23f1fzCC2axLtOW0Xm4e4Qumehb
WrIi6Vfq8IWMglU7QrBJ7iR0Ls+XoKA5GxomV2IJZQKBgQDoIkOl5YGPQ3iGR+WK
e5/2Ml4G/uURzYiOlzSsyfoPXyO4EI2BJd5HkH+EvfgRx4xKkxUZRJdzR7llYPl8
BFYcFitvhO8SbD0mNAB5YW7f+3v1pgEN2umzoKd389Zx5WqTZ7YB1VG5RN/Q1JJL
2JM0Xgamq2vNtx3roRPxDBeW7QKBgQC63R/bmACJbgIzfaVBX4Zie3NQG0/Hf+gF
LnBwUmQDZOR7MY+kSiIUVMn3NuZRiCSCFBVwApruyK8r535JCibTVm5PWjvhFddY
LgaPOCKGlm9TLScjoH1pErYgG3uJ4nXeRfXhg4mco6EkrC7RzQywrd0VDoqpuc1Y
EKfEsYk8dwKBgE+mSh3nNOBKX1V73+f3aTiZqaeu2DyWkG+UtE9BclrJ40Cp9VPG
AZH+o7KRWEgJdzqzYv7riSfWCWgesRv7hOxYMwktzLY+i3DLUQpVAy05ZhwwnJX7
ckrfKfc/pGoqNLplUI8qecMfPciy14vMwR2r0Y5orTHFzi9mcqg35PQ1AoGAW2LX
OLq+0HdHhk0Va8I+450CSRQCUUvhed87SANTPEG0Z/dWC3/h6NWKrGdh/k+5oxAV
Z+EuSkdFPBCLt0bKtCKZ8h7sF+lplotz08kdQXsC2MfFU2wiySdIgK1QHp/tCxZl
6LM+sqdnoJrAjwRcB3AQJkMlV1ox7ba/hbdZqYMCgYBS6+JUXSSASpm5ZHd32a8m
xwryEZ7H6Hek6lvMHdxmwoKat5dCavxw64nrtyeeGZpg1W3zLLyamF9x/8kMyr6y
KKvtBfJ5sCvAbt80o9Pbs6R3yDB3AKiD3s3PQK7lol1nhE/8IbsF2r8JEQVcYd/k
oBzkl7MrMyLhhaCqSxwqQQ==
-----END PRIVATE KEY-----

View File

@@ -25,7 +25,7 @@
"content": {
"user_ids": [
"@alice:matrix.org",
"@bob:example.com"
"@bob:kde.org"
]
},
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
@@ -35,7 +35,7 @@
"content": {
"$153456789:example.org": {
"m.read": {
"@alice:matrix.org": {
"@alice:example.org": {
"ts": 1436451550453
}
}
@@ -47,7 +47,7 @@
"content": {
"$1532735824654:example.org": {
"m.read": {
"@bob:example.com": {
"@bob:kde.org": {
"ts": 1436451550453
}
}
@@ -67,6 +67,18 @@
},
"type": "m.receipt"
},
{
"content": {
"$1532735824654:example.org": {
"m.read": {
"@tim2:example.com": {
"ts": 1436451550454
}
}
}
},
"type": "m.receipt"
},
{
"content": {
"$1532735824654:example.org": {
@@ -136,6 +148,22 @@
"age": 1234
}
},
{
"content": {
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Bob",
"membership": "join"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "bob:kde.org",
"state_key": "@bob:kde.org",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
},
{
"content": {
"displayname": "Look\nat\nme\nI\nput\nnewlines\nin\nmy\ndisplay name",
@@ -156,7 +184,7 @@
"summary": {
"m.heroes": [
"@alice:example.com",
"@bob:example.com"
"@bob:kde.org"
],
"m.invited_member_count": 0,
"m.joined_member_count": 2

View File

@@ -1,14 +0,0 @@
{
"content": {
"body": "https://matrix.to/#/@alice:example.org",
"msgtype": "m.text"
},
"event_id": "$validlink1:example.org",
"origin_server_ts": 1432735824654,
"room_id": "!test:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1234
}
}

View File

@@ -1,14 +0,0 @@
{
"content": {
"body": "mxc://example.org/SEsfnsuifSDFSSEF",
"msgtype": "m.text"
},
"event_id": "$validlink1:example.org",
"origin_server_ts": 1432735824654,
"room_id": "!test:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1234
}
}

View File

@@ -1,14 +0,0 @@
{
"content": {
"body": "testhttps://kde.org",
"msgtype": "m.text"
},
"event_id": "$validlink1:example.org",
"origin_server_ts": 1432735824654,
"room_id": "!test:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1234
}
}

View File

@@ -37,16 +37,59 @@
"events": [
{
"content": {
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Alice Margatroid",
"membership": "join",
"reason": "Looking for support"
"displayname": "Example",
"membership": "join"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"state_key": "@alice:example.org",
"state_key": "@example:example.org",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
},
{
"content": {
"displayname": "Bob",
"membership": "join"
},
"event_id": "$143273blorb3PhrSn:example.org",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@bob:kde.org",
"state_key": "@bob:kde.org",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
},
{
"content": {
"displayname": "Invited",
"membership": "invite"
},
"event_id": "$asdfpj443PhrSn:example.org",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"state_key": "@invited:example.com",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
},
{
"content": {
"displayname": "Banned",
"membership": "ban"
},
"event_id": "$asdfpj443PhrSnasfd:example.org",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@banned:example.com",
"state_key": "@banned:example.com",
"type": "m.room.member",
"unsigned": {
"age": 1234
@@ -59,7 +102,7 @@
"@alice:example.com",
"@bob:example.com"
],
"m.invited_member_count": 0,
"m.invited_member_count": 1,
"m.joined_member_count": 2
},
"timeline": {

View File

@@ -8,14 +8,14 @@
"answers": [
{
"id": "option1",
"org.matrix.msc1767.text": "option1"
"org.matrix.msc1767.text": "option1text"
},
{
"id": "option2",
"org.matrix.msc1767.text": "option2"
"org.matrix.msc1767.text": "option2text"
}
],
"kind": "org.matrix.msc3381.poll.disclosed",
"kind": "org.matrix.msc3381.poll.undisclosed",
"max_selections": 1,
"question": {
"body": "test",

View File

@@ -130,7 +130,23 @@
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"state_key": "@alice:example.org",
"state_key": "@alice:matrix.org",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
},
{
"content": {
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Bob",
"membership": "join"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "bob:example.org",
"state_key": "@bob:example.org",
"type": "m.room.member",
"unsigned": {
"age": 1234

View File

@@ -51,6 +51,21 @@
"unsigned": {
"age": 1234
}
},
{
"content": {
"displayname": "Example",
"membership": "join"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1432735824653,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"state_key": "@example:example.org",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
}
]
},

View File

@@ -1,14 +0,0 @@
{
"content": {
"body": "https://kde.org",
"msgtype": "m.text"
},
"event_id": "$validlink1:example.org",
"origin_server_ts": 1432735824654,
"room_id": "!test:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1234
}
}

View File

@@ -1,14 +0,0 @@
{
"content": {
"body": "www.example.org",
"msgtype": "m.text"
},
"event_id": "$validlink1:example.org",
"origin_server_ts": 1432735824654,
"room_id": "!test:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1234
}
}

View File

@@ -1,16 +0,0 @@
{
"content": {
"body": "[Rich Link](https://kde.org)",
"format": "org.matrix.custom.html",
"formatted_body": "<a href=\"https://kde.org\">Rich Link</a>",
"msgtype": "m.text"
},
"event_id": "$validlink1:example.org",
"origin_server_ts": 1432735824654,
"room_id": "!test:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1234
}
}

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QObject>
#include <QQuickItem>
#include <QTest>
#include "delegatesizehelper.h"
@@ -30,7 +31,7 @@ void DelegateSizeHelperTest::risingPercentage_data()
QTest::addColumn<int>("currentPercentageWidth");
QTest::addColumn<qreal>("currentWidth");
QTest::newRow("zero") << qreal(0) << int(0) << qreal(0);
QTest::newRow("zero") << qreal(0) << int(100) << qreal(0);
QTest::newRow("one hundred") << qreal(100) << int(0) << qreal(0);
QTest::newRow("one fifty") << qreal(150) << int(50) << qreal(75);
QTest::newRow("two hundred") << qreal(200) << int(100) << qreal(200);
@@ -43,16 +44,18 @@ void DelegateSizeHelperTest::risingPercentage()
QFETCH(int, currentPercentageWidth);
QFETCH(qreal, currentWidth);
auto item = QQuickItem();
item.setWidth(parentWidth);
DelegateSizeHelper delegateSizeHelper;
delegateSizeHelper.setParentItem(&item);
delegateSizeHelper.setStartBreakpoint(100);
delegateSizeHelper.setEndBreakpoint(200);
delegateSizeHelper.setStartPercentWidth(0);
delegateSizeHelper.setEndPercentWidth(100);
delegateSizeHelper.setParentWidth(parentWidth);
QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth);
QCOMPARE(delegateSizeHelper.availablePercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.availableWidth(), currentWidth);
}
void DelegateSizeHelperTest::fallingPercentage_data()
@@ -74,16 +77,18 @@ void DelegateSizeHelperTest::fallingPercentage()
QFETCH(int, currentPercentageWidth);
QFETCH(qreal, currentWidth);
auto item = QQuickItem();
item.setWidth(parentWidth);
DelegateSizeHelper delegateSizeHelper;
delegateSizeHelper.setParentItem(&item);
delegateSizeHelper.setStartBreakpoint(100);
delegateSizeHelper.setEndBreakpoint(200);
delegateSizeHelper.setStartPercentWidth(100);
delegateSizeHelper.setEndPercentWidth(0);
delegateSizeHelper.setParentWidth(parentWidth);
QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth);
QCOMPARE(delegateSizeHelper.availablePercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.availableWidth(), currentWidth);
}
void DelegateSizeHelperTest::equalPercentage_data()
@@ -105,16 +110,18 @@ void DelegateSizeHelperTest::equalPercentage()
QFETCH(int, currentPercentageWidth);
QFETCH(qreal, currentWidth);
auto item = QQuickItem();
item.setWidth(parentWidth);
DelegateSizeHelper delegateSizeHelper;
delegateSizeHelper.setParentItem(&item);
delegateSizeHelper.setStartBreakpoint(100);
delegateSizeHelper.setEndBreakpoint(200);
delegateSizeHelper.setStartPercentWidth(50);
delegateSizeHelper.setEndPercentWidth(50);
delegateSizeHelper.setParentWidth(parentWidth);
QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth);
QCOMPARE(delegateSizeHelper.availablePercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.availableWidth(), currentWidth);
}
void DelegateSizeHelperTest::equalBreakpoint_data()
@@ -124,9 +131,9 @@ void DelegateSizeHelperTest::equalBreakpoint_data()
QTest::addColumn<int>("currentPercentageWidth");
QTest::addColumn<qreal>("currentWidth");
QTest::newRow("start higher") << int(100) << int(0) << int(-1) << qreal(0);
QTest::newRow("start higher") << int(100) << int(0) << int(100) << qreal(1000);
QTest::newRow("equal") << int(50) << int(50) << int(50) << qreal(500);
QTest::newRow("end higher") << int(0) << int(100) << int(-1) << qreal(0);
QTest::newRow("end higher") << int(0) << int(100) << int(100) << qreal(1000);
}
/**
@@ -140,16 +147,18 @@ void DelegateSizeHelperTest::equalBreakpoint()
QFETCH(int, currentPercentageWidth);
QFETCH(qreal, currentWidth);
auto item = QQuickItem();
item.setWidth(1000);
DelegateSizeHelper delegateSizeHelper;
delegateSizeHelper.setParentItem(&item);
delegateSizeHelper.setStartBreakpoint(100);
delegateSizeHelper.setEndBreakpoint(100);
delegateSizeHelper.setStartPercentWidth(startPercentageWidth);
delegateSizeHelper.setEndPercentWidth(endPercentageWidth);
delegateSizeHelper.setParentWidth(1000);
QCOMPARE(delegateSizeHelper.currentPercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.currentWidth(), currentWidth);
QCOMPARE(delegateSizeHelper.availablePercentageWidth(), currentPercentageWidth);
QCOMPARE(delegateSizeHelper.availableWidth(), currentWidth);
}
QTEST_GUILESS_MAIN(DelegateSizeHelperTest)

View File

@@ -11,6 +11,8 @@
#include <Quotient/connection.h>
#include <Quotient/quotient_common.h>
#include <Quotient/syncdata.h>
#include <qcbormap.h>
#include <qtestcase.h>
#include "linkpreviewer.h"
#include "models/reactionmodel.h"
@@ -29,23 +31,15 @@ private:
Connection *connection = nullptr;
TestUtils::TestRoom *room = nullptr;
EventHandler emptyHandler = EventHandler(nullptr, nullptr);
private Q_SLOTS:
void initTestCase();
void eventId();
void nullEventId();
void author();
void nullAuthor();
void authorDisplayName();
void nullAuthorDisplayName();
void singleLineSidplayName();
void nullSingleLineDisplayName();
void time();
void nullTime();
void timeString();
void nullTimeString();
void highlighted();
void nullHighlighted();
void hidden();
@@ -56,213 +50,128 @@ private Q_SLOTS:
void genericBody();
void nullGenericBody();
void markdownBody();
void markdownBodyReply();
void subtitle();
void nullSubtitle();
void mediaInfo();
void nullMediaInfo();
void hasReply();
void nullHasReply();
void replyId();
void nullReplyId();
void replyAuthor();
void nullReplyAuthor();
void replyBody();
void nullReplyBody();
void replyMediaInfo();
void nullReplyMediaInfo();
void thread();
void nullThread();
void location();
void nullLocation();
void readMarkers();
void nullReadMarkers();
};
void EventHandlerTest::initTestCase()
{
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-eventhandler-sync.json"));
}
void EventHandlerTest::eventId()
{
EventHandler eventHandler(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandler.getId(), QStringLiteral("$153456789:example.org"));
}
void EventHandlerTest::nullEventId()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getId called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getId(), QString());
}
void EventHandlerTest::author()
{
auto event = room->messageEvents().at(0).get();
auto author = room->user(event->senderId());
EventHandler eventHandler(room, event);
auto eventHandlerAuthor = eventHandler.getAuthor();
QCOMPARE(eventHandlerAuthor["isLocalUser"_ls], author->id() == room->localUser()->id());
QCOMPARE(eventHandlerAuthor["id"_ls], author->id());
QCOMPARE(eventHandlerAuthor["displayName"_ls], author->displayname(room));
QCOMPARE(eventHandlerAuthor["avatarSource"_ls], room->avatarForMember(author));
QCOMPARE(eventHandlerAuthor["avatarMediaId"_ls], author->avatarMediaId(room));
QCOMPARE(eventHandlerAuthor["color"_ls], Utils::getUserColor(author->hueF()));
QCOMPARE(eventHandlerAuthor["object"_ls], QVariant::fromValue(author));
}
void EventHandlerTest::nullAuthor()
{
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getAuthor(), QVariantMap());
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_event set to nullptr. Returning empty user.");
QCOMPARE(noEventHandler.getAuthor(), room->getUser(nullptr));
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-eventhandler-sync.json"_s);
}
void EventHandlerTest::authorDisplayName()
{
EventHandler eventHandler(room, room->messageEvents().at(1).get());
QCOMPARE(eventHandler.getAuthorDisplayName(), QStringLiteral("before"));
QCOMPARE(EventHandler::authorDisplayName(room, room->messageEvents().at(1).get()), u"before"_s);
}
void EventHandlerTest::nullAuthorDisplayName()
{
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getAuthorDisplayName(), QString());
QTest::ignoreMessage(QtWarningMsg, "authorDisplayName called with room set to nullptr.");
QCOMPARE(EventHandler::authorDisplayName(nullptr, nullptr), QString());
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getAuthorDisplayName(), QString());
QTest::ignoreMessage(QtWarningMsg, "authorDisplayName called with event set to nullptr.");
QCOMPARE(EventHandler::authorDisplayName(room, nullptr), QString());
}
void EventHandlerTest::singleLineSidplayName()
{
EventHandler eventHandler(room, room->messageEvents().at(11).get());
QCOMPARE(eventHandler.singleLineAuthorDisplayname(), QStringLiteral("Look at me I put newlines in my display name"));
QCOMPARE(EventHandler::singleLineAuthorDisplayname(room, room->messageEvents().at(11).get()), "Look at me I put newlines in my display name"_L1);
}
void EventHandlerTest::nullSingleLineDisplayName()
{
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_room set to nullptr.");
QCOMPARE(emptyHandler.singleLineAuthorDisplayname(), QString());
QTest::ignoreMessage(QtWarningMsg, "singleLineAuthorDisplayname called with room set to nullptr.");
QCOMPARE(EventHandler::singleLineAuthorDisplayname(nullptr, nullptr), QString());
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_event set to nullptr.");
QCOMPARE(noEventHandler.singleLineAuthorDisplayname(), QString());
QTest::ignoreMessage(QtWarningMsg, "singleLineAuthorDisplayname called with event set to nullptr.");
QCOMPARE(EventHandler::singleLineAuthorDisplayname(room, nullptr), QString());
}
void EventHandlerTest::time()
{
EventHandler eventHandler(room, room->messageEvents().at(0).get());
const auto event = room->messageEvents().at(0).get();
QCOMPARE(eventHandler.getTime(), QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC));
QCOMPARE(eventHandler.getTime(true, QDateTime::fromMSecsSinceEpoch(1234, Qt::UTC)), QDateTime::fromMSecsSinceEpoch(1234, Qt::UTC));
QCOMPARE(EventHandler::dateTime(room, event), QDateTime::fromMSecsSinceEpoch(1432735824654, QTimeZone(QTimeZone::UTC)));
const auto txID = room->postJson("m.room.message"_L1, event->fullJson());
QCOMPARE(room->pendingEvents().size(), 1);
const auto pendingIt = room->findPendingEvent(txID);
QCOMPARE(EventHandler::dateTime(room, pendingIt->event(), true), pendingIt->lastUpdated());
room->discardMessage(txID);
QCOMPARE(room->pendingEvents().size(), 0);
}
void EventHandlerTest::nullTime()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getTime called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getTime(), QDateTime());
QTest::ignoreMessage(QtWarningMsg, "time called with room set to nullptr.");
QCOMPARE(EventHandler::dateTime(nullptr, nullptr), QDateTime());
EventHandler eventHandler(room, room->messageEvents().at(0).get());
QTest::ignoreMessage(QtWarningMsg, "a value must be provided for lastUpdated for a pending event.");
QCOMPARE(eventHandler.getTime(true), QDateTime());
}
void EventHandlerTest::timeString()
{
EventHandler eventHandler(room, room->messageEvents().at(0).get());
KFormat format;
QCOMPARE(eventHandler.getTimeString(false),
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toLocalTime().time(), QLocale::ShortFormat));
QCOMPARE(eventHandler.getTimeString(true),
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toLocalTime().date(), QLocale::ShortFormat));
QCOMPARE(eventHandler.getTimeString(false, QLocale::ShortFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().time(), QLocale::ShortFormat));
QCOMPARE(eventHandler.getTimeString(true, QLocale::ShortFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::ShortFormat));
QCOMPARE(eventHandler.getTimeString(false, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().time(), QLocale::LongFormat));
QCOMPARE(eventHandler.getTimeString(true, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::LongFormat));
}
void EventHandlerTest::nullTimeString()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getTimeString called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getTimeString(false), QString());
EventHandler eventHandler(room, room->messageEvents().at(0).get());
QTest::ignoreMessage(QtWarningMsg, "a value must be provided for lastUpdated for a pending event.");
QCOMPARE(eventHandler.getTimeString(false, QLocale::ShortFormat, true), QString());
QTest::ignoreMessage(QtWarningMsg, "time called with event set to nullptr.");
QCOMPARE(EventHandler::dateTime(room, nullptr), QDateTime());
}
void EventHandlerTest::highlighted()
{
EventHandler eventHandlerHighlight(room, room->messageEvents().at(2).get());
QCOMPARE(eventHandlerHighlight.isHighlighted(), true);
EventHandler eventHandlerNoHighlight(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandlerNoHighlight.isHighlighted(), false);
QCOMPARE(EventHandler::isHighlighted(room, room->messageEvents().at(2).get()), true);
QCOMPARE(EventHandler::isHighlighted(room, room->messageEvents().at(0).get()), false);
}
void EventHandlerTest::nullHighlighted()
{
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with m_room set to nullptr.");
QCOMPARE(emptyHandler.isHighlighted(), false);
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with room set to nullptr.");
QCOMPARE(EventHandler::isHighlighted(nullptr, nullptr), false);
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with m_event set to nullptr.");
QCOMPARE(noEventHandler.isHighlighted(), false);
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with event set to nullptr.");
QCOMPARE(EventHandler::isHighlighted(room, nullptr), false);
}
void EventHandlerTest::hidden()
{
EventHandler eventHandlerHidden(room, room->messageEvents().at(3).get());
QCOMPARE(eventHandlerHidden.isHidden(), true);
EventHandler eventHandlerNoHidden(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandlerNoHidden.isHidden(), false);
QCOMPARE(EventHandler::isHidden(room, room->messageEvents().at(3).get()), true);
QCOMPARE(EventHandler::isHidden(room, room->messageEvents().at(0).get()), false);
}
void EventHandlerTest::nullHidden()
{
QTest::ignoreMessage(QtWarningMsg, "isHidden called with m_room set to nullptr.");
QCOMPARE(emptyHandler.isHidden(), false);
QTest::ignoreMessage(QtWarningMsg, "isHidden called with room set to nullptr.");
QCOMPARE(EventHandler::isHidden(nullptr, nullptr), false);
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "isHidden called with m_event set to nullptr.");
QCOMPARE(noEventHandler.isHidden(), false);
QTest::ignoreMessage(QtWarningMsg, "isHidden called with event set to nullptr.");
QCOMPARE(EventHandler::isHidden(room, nullptr), false);
}
void EventHandlerTest::body()
{
EventHandler eventHandler(room, room->messageEvents().at(0).get());
const auto event = room->messageEvents().at(0).get();
QCOMPARE(eventHandler.getRichBody(), QStringLiteral("<b>This is an example<br>text message</b>"));
QCOMPARE(eventHandler.getRichBody(true), QStringLiteral("<b>This is an example text message</b>"));
QCOMPARE(eventHandler.getPlainBody(), QStringLiteral("This is an example\ntext message"));
QCOMPARE(eventHandler.getPlainBody(true), QStringLiteral("This is an example text message"));
QCOMPARE(EventHandler::richBody(room, event), u"<b>This is an example<br>text message</b>"_s);
QCOMPARE(EventHandler::richBody(room, event, true), u"<b>This is an example text message</b>"_s);
QCOMPARE(EventHandler::plainBody(room, event), u"This is an example\ntext message"_s);
QCOMPARE(EventHandler::plainBody(room, event, true), u"This is an example text message"_s);
}
void EventHandlerTest::nullBody()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "richBody called with room set to nullptr.");
QCOMPARE(EventHandler::richBody(nullptr, nullptr), QString());
QTest::ignoreMessage(QtWarningMsg, "getRichBody called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getRichBody(), QString());
QTest::ignoreMessage(QtWarningMsg, "richBody called with event set to nullptr.");
QCOMPARE(EventHandler::richBody(room, nullptr), QString());
QTest::ignoreMessage(QtWarningMsg, "getPlainBody called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getPlainBody(), QString());
QTest::ignoreMessage(QtWarningMsg, "plainBody called with room set to nullptr.");
QCOMPARE(EventHandler::plainBody(nullptr, nullptr), QString());
QTest::ignoreMessage(QtWarningMsg, "plainBody called with event set to nullptr.");
QCOMPARE(EventHandler::plainBody(room, nullptr), QString());
}
void EventHandlerTest::genericBody_data()
@@ -270,11 +179,11 @@ void EventHandlerTest::genericBody_data()
QTest::addColumn<int>("eventNum");
QTest::addColumn<QString>("output");
QTest::newRow("message") << 0 << QStringLiteral("sent a message");
QTest::newRow("member") << 1 << QStringLiteral("changed their display name and updated their avatar");
QTest::newRow("message 2") << 2 << QStringLiteral("sent a message");
QTest::newRow("reaction") << 3 << QStringLiteral("Unknown event");
QTest::newRow("video") << 4 << QStringLiteral("sent a message");
QTest::newRow("message") << 0 << u"<a href=\"https://matrix.to/#/@example:example.org\">after</a> sent a message"_s;
QTest::newRow("member") << 1 << u"<a href=\"https://matrix.to/#/@example:example.org\">after</a> changed their display name and updated their avatar"_s;
QTest::newRow("message 2") << 2 << u"<a href=\"https://matrix.to/#/@example:example.org\">after</a> sent a message"_s;
QTest::newRow("reaction") << 3 << u"Unknown event"_s;
QTest::newRow("video") << 4 << u"<a href=\"https://matrix.to/#/@example:example.org\">after</a> sent a message"_s;
}
void EventHandlerTest::genericBody()
@@ -282,291 +191,116 @@ void EventHandlerTest::genericBody()
QFETCH(int, eventNum);
QFETCH(QString, output);
EventHandler eventHandler(room, room->messageEvents().at(eventNum).get());
QCOMPARE(eventHandler.getGenericBody(), output);
QCOMPARE(EventHandler::genericBody(room, room->messageEvents().at(eventNum).get()), output);
}
void EventHandlerTest::nullGenericBody()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getGenericBody called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getGenericBody(), QString());
QTest::ignoreMessage(QtWarningMsg, "genericBody called with room set to nullptr.");
QCOMPARE(EventHandler::genericBody(nullptr, nullptr), QString());
QTest::ignoreMessage(QtWarningMsg, "genericBody called with event set to nullptr.");
QCOMPARE(EventHandler::genericBody(room, nullptr), QString());
}
void EventHandlerTest::markdownBody()
{
EventHandler eventHandler(room, room->messageEvents().at(0).get());
QCOMPARE(EventHandler::markdownBody(room->messageEvents().at(0).get()), u"This is an example\ntext message"_s);
}
QCOMPARE(eventHandler.getMarkdownBody(), QStringLiteral("This is an example\ntext message"));
void EventHandlerTest::markdownBodyReply()
{
QCOMPARE(EventHandler::markdownBody(room->messageEvents().at(5).get()), u"reply"_s);
}
void EventHandlerTest::subtitle()
{
EventHandler eventHandler(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandler.subtitleText(), QStringLiteral("after: This is an example text message"));
EventHandler eventHandler2(room, room->messageEvents().at(2).get());
QCOMPARE(eventHandler2.subtitleText(), QStringLiteral("after: This is a highlight @bob:kde.org and this is a link https://kde.org"));
QCOMPARE(EventHandler::subtitleText(room, room->messageEvents().at(0).get()), u"after: This is an example text message"_s);
QCOMPARE(EventHandler::subtitleText(room, room->messageEvents().at(2).get()),
u"after: This is a highlight @bob:kde.org and this is a link https://kde.org"_s);
}
void EventHandlerTest::nullSubtitle()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "subtitleText called with m_event set to nullptr.");
QCOMPARE(noEventHandler.subtitleText(), QString());
QTest::ignoreMessage(QtWarningMsg, "subtitleText called with room set to nullptr.");
QCOMPARE(EventHandler::subtitleText(nullptr, nullptr), QString());
QTest::ignoreMessage(QtWarningMsg, "subtitleText called with event set to nullptr.");
QCOMPARE(EventHandler::subtitleText(room, nullptr), QString());
}
void EventHandlerTest::mediaInfo()
{
auto event = room->messageEvents().at(4).get();
EventHandler eventHandler(room, event);
auto mediaInfo = EventHandler::mediaInfo(room, event);
auto thumbnailInfo = mediaInfo["tempInfo"_L1].toMap();
auto mediaInfo = eventHandler.getMediaInfo();
auto thumbnailInfo = mediaInfo["tempInfo"_ls].toMap();
QCOMPARE(mediaInfo["source"_ls], room->makeMediaUrl(event->id(), QUrl("mxc://kde.org/1234567"_ls)));
QCOMPARE(mediaInfo["mimeType"_ls], QStringLiteral("video/mp4"));
QCOMPARE(mediaInfo["mimeIcon"_ls], QStringLiteral("video-mp4"));
QCOMPARE(mediaInfo["size"_ls], 62650636);
QCOMPARE(mediaInfo["duration"_ls], 10);
QCOMPARE(mediaInfo["width"_ls], 1920);
QCOMPARE(mediaInfo["height"_ls], 1080);
QCOMPARE(thumbnailInfo["source"_ls], room->makeMediaUrl(event->id(), QUrl("mxc://kde.org/2234567"_ls)));
QCOMPARE(thumbnailInfo["mimeType"_ls], QStringLiteral("image/jpeg"));
QCOMPARE(thumbnailInfo["mimeIcon"_ls], QStringLiteral("image-jpeg"));
QCOMPARE(thumbnailInfo["size"_ls], 382249);
QCOMPARE(thumbnailInfo["width"_ls], 800);
QCOMPARE(thumbnailInfo["height"_ls], 450);
QCOMPARE(mediaInfo["source"_L1], room->makeMediaUrl(event->id(), QUrl("mxc://kde.org/1234567"_L1)));
QCOMPARE(mediaInfo["mimeType"_L1], u"video/mp4"_s);
QCOMPARE(mediaInfo["mimeIcon"_L1], u"video-mp4"_s);
QCOMPARE(mediaInfo["size"_L1], 62650636);
QCOMPARE(mediaInfo["duration"_L1], 10);
QCOMPARE(mediaInfo["width"_L1], 1920);
QCOMPARE(mediaInfo["height"_L1], 1080);
QCOMPARE(thumbnailInfo["source"_L1], room->makeMediaUrl(event->id(), QUrl("mxc://kde.org/2234567"_L1)));
QCOMPARE(thumbnailInfo["mimeType"_L1], u"image/jpeg"_s);
QCOMPARE(thumbnailInfo["mimeIcon"_L1], u"image-jpeg"_s);
QCOMPARE(thumbnailInfo["size"_L1], 382249);
QCOMPARE(thumbnailInfo["width"_L1], 800);
QCOMPARE(thumbnailInfo["height"_L1], 450);
}
void EventHandlerTest::nullMediaInfo()
{
QTest::ignoreMessage(QtWarningMsg, "getMediaInfo called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getMediaInfo(), QVariantMap());
QTest::ignoreMessage(QtWarningMsg, "mediaInfo called with room set to nullptr.");
QCOMPARE(EventHandler::mediaInfo(nullptr, nullptr), QVariantMap());
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getMediaInfo called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getMediaInfo(), QVariantMap());
}
void EventHandlerTest::hasReply()
{
EventHandler eventHandlerReply(room, room->messageEvents().at(5).get());
QCOMPARE(eventHandlerReply.hasReply(), true);
EventHandler eventHandlerNoReply(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandlerNoReply.hasReply(), false);
}
void EventHandlerTest::nullHasReply()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "hasReply called with m_event set to nullptr.");
QCOMPARE(noEventHandler.hasReply(), false);
}
void EventHandlerTest::replyId()
{
EventHandler eventHandlerReply(room, room->messageEvents().at(5).get());
QCOMPARE(eventHandlerReply.getReplyId(), QStringLiteral("$153456789:example.org"));
EventHandler eventHandlerNoReply(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandlerNoReply.getReplyId(), QStringLiteral(""));
}
void EventHandlerTest::nullReplyId()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getReplyId called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getReplyId(), QString());
QTest::ignoreMessage(QtWarningMsg, "mediaInfo called with event set to nullptr.");
QCOMPARE(EventHandler::mediaInfo(room, nullptr), QVariantMap());
}
void EventHandlerTest::replyAuthor()
{
auto replyEvent = room->messageEvents().at(0).get();
auto replyAuthor = room->user(replyEvent->senderId());
EventHandler eventHandler(room, room->messageEvents().at(5).get());
auto replyAuthor = room->member(replyEvent->senderId());
auto eventHandlerReplyAuthor = EventHandler::replyAuthor(room, room->messageEvents().at(5).get());
auto eventHandlerReplyAuthor = eventHandler.getReplyAuthor();
QCOMPARE(eventHandlerReplyAuthor.isLocalMember(), replyAuthor.id() == room->localMember().id());
QCOMPARE(eventHandlerReplyAuthor.id(), replyAuthor.id());
QCOMPARE(eventHandlerReplyAuthor.displayName(), replyAuthor.displayName());
QCOMPARE(eventHandlerReplyAuthor.avatarUrl(), replyAuthor.avatarUrl());
QCOMPARE(eventHandlerReplyAuthor.avatarMediaId(), replyAuthor.avatarMediaId());
QCOMPARE(eventHandlerReplyAuthor.color(), replyAuthor.color());
QCOMPARE(eventHandlerReplyAuthor["isLocalUser"_ls], replyAuthor->id() == room->localUser()->id());
QCOMPARE(eventHandlerReplyAuthor["id"_ls], replyAuthor->id());
QCOMPARE(eventHandlerReplyAuthor["displayName"_ls], replyAuthor->displayname(room));
QCOMPARE(eventHandlerReplyAuthor["avatarSource"_ls], room->avatarForMember(replyAuthor));
QCOMPARE(eventHandlerReplyAuthor["avatarMediaId"_ls], replyAuthor->avatarMediaId(room));
QCOMPARE(eventHandlerReplyAuthor["color"_ls], Utils::getUserColor(replyAuthor->hueF()));
QCOMPARE(eventHandlerReplyAuthor["object"_ls], QVariant::fromValue(replyAuthor));
EventHandler eventHandlerNoAuthor(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandlerNoAuthor.getReplyAuthor(), room->getUser(nullptr));
QCOMPARE(EventHandler::replyAuthor(room, room->messageEvents().at(0).get()), RoomMember());
}
void EventHandlerTest::nullReplyAuthor()
{
QTest::ignoreMessage(QtWarningMsg, "getReplyAuthor called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getReplyAuthor(), QVariantMap());
QTest::ignoreMessage(QtWarningMsg, "replyAuthor called with room set to nullptr.");
QCOMPARE(EventHandler::replyAuthor(nullptr, nullptr), RoomMember());
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getReplyAuthor called with m_event set to nullptr. Returning empty user.");
QCOMPARE(noEventHandler.getReplyAuthor(), room->getUser(nullptr));
}
void EventHandlerTest::replyBody()
{
EventHandler eventHandler(room, room->messageEvents().at(5).get());
QCOMPARE(eventHandler.getReplyRichBody(), QStringLiteral("<b>This is an example<br>text message</b>"));
QCOMPARE(eventHandler.getReplyRichBody(true), QStringLiteral("<b>This is an example text message</b>"));
QCOMPARE(eventHandler.getReplyPlainBody(), QStringLiteral("This is an example\ntext message"));
QCOMPARE(eventHandler.getReplyPlainBody(true), QStringLiteral("This is an example text message"));
}
void EventHandlerTest::nullReplyBody()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getReplyRichBody called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getReplyRichBody(), QString());
QTest::ignoreMessage(QtWarningMsg, "getReplyPlainBody called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getReplyPlainBody(), QString());
}
void EventHandlerTest::replyMediaInfo()
{
auto event = room->messageEvents().at(6).get();
auto replyEvent = room->messageEvents().at(4).get();
EventHandler eventHandler(room, event);
auto mediaInfo = eventHandler.getReplyMediaInfo();
auto thumbnailInfo = mediaInfo["tempInfo"_ls].toMap();
QCOMPARE(mediaInfo["source"_ls], room->makeMediaUrl(replyEvent->id(), QUrl("mxc://kde.org/1234567"_ls)));
QCOMPARE(mediaInfo["mimeType"_ls], QStringLiteral("video/mp4"));
QCOMPARE(mediaInfo["mimeIcon"_ls], QStringLiteral("video-mp4"));
QCOMPARE(mediaInfo["size"_ls], 62650636);
QCOMPARE(mediaInfo["duration"_ls], 10);
QCOMPARE(mediaInfo["width"_ls], 1920);
QCOMPARE(mediaInfo["height"_ls], 1080);
QCOMPARE(thumbnailInfo["source"_ls], room->makeMediaUrl(replyEvent->id(), QUrl("mxc://kde.org/2234567"_ls)));
QCOMPARE(thumbnailInfo["mimeType"_ls], QStringLiteral("image/jpeg"));
QCOMPARE(thumbnailInfo["mimeIcon"_ls], QStringLiteral("image-jpeg"));
QCOMPARE(thumbnailInfo["size"_ls], 382249);
QCOMPARE(thumbnailInfo["width"_ls], 800);
QCOMPARE(thumbnailInfo["height"_ls], 450);
}
void EventHandlerTest::nullReplyMediaInfo()
{
QTest::ignoreMessage(QtWarningMsg, "getReplyMediaInfo called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getReplyMediaInfo(), QVariantMap());
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getReplyMediaInfo called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getReplyMediaInfo(), QVariantMap());
}
void EventHandlerTest::thread()
{
EventHandler eventHandlerNoThread(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandlerNoThread.isThreaded(), false);
QCOMPARE(eventHandlerNoThread.threadRoot(), QString());
EventHandler eventHandlerThreadRoot(room, room->messageEvents().at(9).get());
QCOMPARE(eventHandlerThreadRoot.isThreaded(), true);
QCOMPARE(eventHandlerThreadRoot.threadRoot(), QStringLiteral("$threadroot:example.org"));
QCOMPARE(eventHandlerThreadRoot.getReplyId(), QStringLiteral("$threadroot:example.org"));
EventHandler eventHandlerThreadReply(room, room->messageEvents().at(10).get());
QCOMPARE(eventHandlerThreadReply.isThreaded(), true);
QCOMPARE(eventHandlerThreadReply.threadRoot(), QStringLiteral("$threadroot:example.org"));
QCOMPARE(eventHandlerThreadReply.getReplyId(), QStringLiteral("$threadmessage1:example.org"));
}
void EventHandlerTest::nullThread()
{
QTest::ignoreMessage(QtWarningMsg, "isThreaded called with m_event set to nullptr.");
QCOMPARE(emptyHandler.isThreaded(), false);
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "threadRoot called with m_event set to nullptr.");
QCOMPARE(noEventHandler.threadRoot(), QString());
QTest::ignoreMessage(QtWarningMsg, "replyAuthor called with event set to nullptr. Returning empty user.");
QCOMPARE(EventHandler::replyAuthor(room, nullptr), RoomMember());
}
void EventHandlerTest::location()
{
EventHandler eventHandler(room, room->messageEvents().at(7).get());
QCOMPARE(eventHandler.getLatitude(), QStringLiteral("51.7035").toFloat());
QCOMPARE(eventHandler.getLongitude(), QStringLiteral("-1.14394").toFloat());
QCOMPARE(eventHandler.getLocationAssetType(), QStringLiteral("m.pin"));
QCOMPARE(EventHandler::latitude(room->messageEvents().at(7).get()), u"51.7035"_s.toFloat());
QCOMPARE(EventHandler::longitude(room->messageEvents().at(7).get()), u"-1.14394"_s.toFloat());
QCOMPARE(EventHandler::locationAssetType(room->messageEvents().at(7).get()), u"m.pin"_s);
}
void EventHandlerTest::nullLocation()
{
QTest::ignoreMessage(QtWarningMsg, "getLatitude called with m_event set to nullptr.");
QCOMPARE(emptyHandler.getLatitude(), -100.0);
QTest::ignoreMessage(QtWarningMsg, "latitude called with event set to nullptr.");
QCOMPARE(EventHandler::latitude(nullptr), -100.0);
QTest::ignoreMessage(QtWarningMsg, "getLongitude called with m_event set to nullptr.");
QCOMPARE(emptyHandler.getLongitude(), -200.0);
QTest::ignoreMessage(QtWarningMsg, "longitude called with event set to nullptr.");
QCOMPARE(EventHandler::longitude(nullptr), -200.0);
QTest::ignoreMessage(QtWarningMsg, "getLocationAssetType called with m_event set to nullptr.");
QCOMPARE(emptyHandler.getLocationAssetType(), QString());
}
void EventHandlerTest::readMarkers()
{
EventHandler eventHandler(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandler.hasReadMarkers(), true);
auto readMarkers = eventHandler.getReadMarkers();
QCOMPARE(readMarkers.size(), 1);
QCOMPARE(readMarkers[0].toMap()["id"_ls], QStringLiteral("@alice:matrix.org"));
QCOMPARE(eventHandler.getNumberExcessReadMarkers(), QString());
QCOMPARE(eventHandler.getReadMarkersString(), QStringLiteral("1 user: @alice:matrix.org"));
EventHandler eventHandler2(room, room->messageEvents().at(2).get());
QCOMPARE(eventHandler2.hasReadMarkers(), true);
readMarkers = eventHandler2.getReadMarkers();
QCOMPARE(readMarkers.size(), 5);
QCOMPARE(eventHandler2.getNumberExcessReadMarkers(), QStringLiteral("+ 1"));
// There are no guarantees on the order of the users it will be different every time so don't match the whole string.
QCOMPARE(eventHandler2.getReadMarkersString().startsWith(QStringLiteral("6 users:")), true);
}
void EventHandlerTest::nullReadMarkers()
{
QTest::ignoreMessage(QtWarningMsg, "hasReadMarkers called with m_room set to nullptr.");
QCOMPARE(emptyHandler.hasReadMarkers(), false);
QTest::ignoreMessage(QtWarningMsg, "getReadMarkers called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getReadMarkers(), QVariantList());
QTest::ignoreMessage(QtWarningMsg, "getNumberExcessReadMarkers called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getNumberExcessReadMarkers(), QString());
QTest::ignoreMessage(QtWarningMsg, "getReadMarkersString called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getReadMarkersString(), QString());
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "hasReadMarkers called with m_event set to nullptr.");
QCOMPARE(noEventHandler.hasReadMarkers(), false);
QTest::ignoreMessage(QtWarningMsg, "getReadMarkers called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getReadMarkers(), QVariantList());
QTest::ignoreMessage(QtWarningMsg, "getNumberExcessReadMarkers called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getNumberExcessReadMarkers(), QString());
QTest::ignoreMessage(QtWarningMsg, "getReadMarkersString called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getReadMarkersString(), QString());
QTest::ignoreMessage(QtWarningMsg, "locationAssetType called with event set to nullptr.");
QCOMPARE(EventHandler::locationAssetType(nullptr), QString());
}
QTEST_MAIN(EventHandlerTest)

View File

@@ -6,12 +6,11 @@
#include "linkpreviewer.h"
#include "utils.h"
#include <Quotient/events/roommessageevent.h>
#include <Quotient/quotient_common.h>
#include <Quotient/syncdata.h>
#include "utils.h"
#include "testutils.h"
using namespace Quotient;
@@ -20,67 +19,72 @@ class LinkPreviewerTest : public QObject
{
Q_OBJECT
private:
Connection *connection = nullptr;
TestUtils::TestRoom *room = nullptr;
private Q_SLOTS:
void initTestCase();
void linkPreviewsMatch_data();
void linkPreviewsMatch();
void multipleLinkPreviewsMatch_data();
void multipleLinkPreviewsMatch();
void linkPreviewsReject_data();
void linkPreviewsReject();
};
void LinkPreviewerTest::initTestCase()
{
connection = Connection::makeMockConnection(QStringLiteral("@bob:example.org"));
room = new TestUtils::TestRoom(connection, QStringLiteral("!test:example.org"));
}
void LinkPreviewerTest::linkPreviewsMatch_data()
{
QTest::addColumn<QString>("eventSource");
QTest::addColumn<QString>("inputString");
QTest::addColumn<QUrl>("testOutputLink");
QTest::newRow("plainHttps") << QStringLiteral("test-validplainlink-event.json") << QUrl("https://kde.org"_ls);
QTest::newRow("richHttps") << QStringLiteral("test-validrichlink-event.json") << QUrl("https://kde.org"_ls);
QTest::newRow("plainWww") << QStringLiteral("test-validplainwwwlink-event.json") << QUrl("www.example.org"_ls);
QTest::newRow("multipleHttps") << QStringLiteral("test-multiplelink-event.json") << QUrl("www.example.org"_ls);
QTest::newRow("plainHttps") << u"https://kde.org"_s << QUrl(u"https://kde.org"_s);
QTest::newRow("richHttps") << u"<a href=\"https://kde.org\">Rich Link</a>"_s << QUrl(u"https://kde.org"_s);
QTest::newRow("richHttpsLinkDescription") << u"<a href=\"https://kde.org\">https://kde.org</a>"_s << QUrl(u"https://kde.org"_s);
}
void LinkPreviewerTest::linkPreviewsMatch()
{
QFETCH(QString, eventSource);
QFETCH(QString, inputString);
QFETCH(QUrl, testOutputLink);
auto event = TestUtils::loadEventFromFile<RoomMessageEvent>(eventSource);
auto linkPreviewer = LinkPreviewer(LinkPreviewer::linkPreview(event.get()), connection);
auto link = LinkPreviewer::linkPreviews(inputString)[0];
QCOMPARE(linkPreviewer.empty(), false);
QCOMPARE(linkPreviewer.url(), testOutputLink);
QCOMPARE(link, testOutputLink);
}
void LinkPreviewerTest::multipleLinkPreviewsMatch_data()
{
QTest::addColumn<QString>("inputString");
QTest::addColumn<QList<QUrl>>("testOutputLinks");
QTest::newRow("multipleHttps") << u"www.example.org https://kde.org"_s << QList{QUrl(u"www.example.org"_s), QUrl(u"https://kde.org"_s)};
QTest::newRow("multipleHttps1Invalid") << u"www.example.org mxc://example.org/SEsfnsuifSDFSSEF"_s << QList{QUrl(u"www.example.org"_s)};
}
void LinkPreviewerTest::multipleLinkPreviewsMatch()
{
QFETCH(QString, inputString);
QFETCH(QList<QUrl>, testOutputLinks);
auto links = LinkPreviewer::linkPreviews(inputString);
QCOMPARE(links, testOutputLinks);
}
void LinkPreviewerTest::linkPreviewsReject_data()
{
QTest::addColumn<QString>("eventSource");
QTest::addColumn<QString>("inputString");
QTest::newRow("mxc") << QStringLiteral("test-invalidmxclink-event.json");
QTest::newRow("matrixTo") << QStringLiteral("test-invalidmatrixtolink-event.json");
QTest::newRow("noSpace") << QStringLiteral("test-invalidnospacelink-event.json");
QTest::newRow("mxc") << u"mxc://example.org/SEsfnsuifSDFSSEF"_s;
QTest::newRow("matrixTo") << u"https://matrix.to/#/@alice:example.org"_s;
QTest::newRow("noSpace") << u"testhttps://kde.org"_s;
}
void LinkPreviewerTest::linkPreviewsReject()
{
QFETCH(QString, eventSource);
QFETCH(QString, inputString);
auto event = TestUtils::loadEventFromFile<RoomMessageEvent>(eventSource);
auto linkPreviewer = LinkPreviewer(LinkPreviewer::linkPreview(event.get()), connection);
auto links = LinkPreviewer::linkPreviews(inputString);
QCOMPARE(linkPreviewer.empty(), true);
QCOMPARE(linkPreviewer.url(), QUrl());
QCOMPARE(links.empty(), true);
}
QTEST_MAIN(LinkPreviewerTest)

View File

@@ -25,6 +25,7 @@ private Q_SLOTS:
void MediaSizeHelperTest::uninitialized()
{
MediaSizeHelper mediasizehelper;
mediasizehelper.setMaxSize(540, 540);
QCOMPARE(mediasizehelper.currentSize(), QSize(540, qRound(qreal(NeoChatConfig::self()->mediaMaxWidth()) / qreal(16.0) * qreal(9.0))));
}
@@ -60,6 +61,7 @@ void MediaSizeHelperTest::limits()
QFETCH(QSize, currentSize);
MediaSizeHelper mediasizehelper;
mediasizehelper.setMaxSize(540, 540);
mediasizehelper.setMediaWidth(mediaWidth);
mediasizehelper.setMediaHeight(mediaHeight);
mediasizehelper.setContentMaxWidth(contentMaxWidth);

View File

@@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QObject>
#include <QSignalSpy>
#include <QTest>
#include <Quotient/connection.h>
#include <Quotient/quotient_common.h>
#include <Quotient/roommember.h>
#include <Quotient/syncdata.h>
#include "models/eventmessagecontentmodel.h"
#include "neochatconnection.h"
#include "testutils.h"
using namespace Quotient;
using namespace Qt::Literals::StringLiterals;
class MessageContentModelTest : public QObject
{
Q_OBJECT
private:
Connection *connection = nullptr;
private Q_SLOTS:
void initTestCase();
void missingEvent();
};
void MessageContentModelTest::initTestCase()
{
connection = new NeoChatConnection;
}
void MessageContentModelTest::missingEvent()
{
auto room = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s);
auto model1 = EventMessageContentModel(room, u"$153456789:example.org"_s);
QCOMPARE(model1.rowCount(), 1);
QCOMPARE(model1.data(model1.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
QCOMPARE(model1.data(model1.index(0), MessageContentModel::DisplayRole), u"Loading…"_s);
auto model2 = 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);
room->syncNewEvents(u"test-min-sync.json"_s);
QCOMPARE(model1.rowCount(), 2);
QCOMPARE(model1.data(model1.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Author);
QCOMPARE(model1.data(model1.index(1), MessageContentModel::ComponentTypeRole), MessageComponentType::Text);
QCOMPARE(model1.data(model1.index(1), MessageContentModel::DisplayRole), u"<b>This is an example<br>text message</b>"_s);
}
QTEST_MAIN(MessageContentModelTest)
#include "messagecontentmodeltest.moc"

621
autotests/modeltest.cpp Normal file
View File

@@ -0,0 +1,621 @@
// 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 <QAbstractItemModelTester>
#include <QObject>
#include <QSignalSpy>
#include <QTest>
#include <QVariantList>
#include <Quotient/connection.h>
#include "accountmanager.h"
#include "contentprovider.h"
#include "enums/powerlevel.h"
#include "enums/roomsortparameter.h"
#include "models/accountemoticonmodel.h"
#include "models/actionsmodel.h"
#include "models/commonroomsmodel.h"
#include "models/completionmodel.h"
#include "models/completionproxymodel.h"
#include "models/customemojimodel.h"
#include "models/devicesmodel.h"
#include "models/devicesproxymodel.h"
#include "models/emojimodel.h"
#include "models/emoticonfiltermodel.h"
#include "models/eventmessagecontentmodel.h"
#include "models/imagepacksmodel.h"
#include "models/linemodel.h"
#include "models/livelocationsmodel.h"
#include "models/locationsmodel.h"
#include "models/messagecontentfiltermodel.h"
#include "models/messagecontentmodel.h"
#include "models/notificationsmodel.h"
#include "models/permissionsmodel.h"
#include "models/pinnedmessagemodel.h"
#include "models/pollanswermodel.h"
#include "models/publicroomlistmodel.h"
#include "models/pushrulemodel.h"
#include "models/readmarkermodel.h"
#include "models/roomsortparametermodel.h"
#include "models/searchmodel.h"
#include "models/serverlistmodel.h"
#include "models/spacechildrenmodel.h"
#include "models/spacechildsortfiltermodel.h"
#include "models/statefiltermodel.h"
#include "models/statekeysmodel.h"
#include "models/statemodel.h"
#include "models/stickermodel.h"
#include "models/threadmodel.h"
#include "models/threepidmodel.h"
#include "models/userdirectorylistmodel.h"
#include "models/userfiltermodel.h"
#include "models/webshortcutmodel.h"
#include "neochatroom.h"
#include "pollhandler.h"
#include "roommanager.h"
#include "server.h"
using namespace Quotient;
// TODO: Add data to all models as relevant.
// Performs basic tests on all models in NeoChat
// When adding a new test, create the model first, then the tester, then initialize the model (e.g., setConnection and setRoom).
// That way, the models are also tested for whether they can handle having no connection etc.
class ModelTest : public QObject
{
Q_OBJECT
private:
NeoChatConnection *connection = nullptr;
NeoChatRoom *room = nullptr;
QString eventId;
Server server;
private Q_SLOTS:
void initTestCase();
void testRoomTreeModel();
void testMessageContentModel();
void testEventMessageContentModel();
void testThreadModel();
void testThreadFetchModel();
void testThreadChatBarModel();
void testReactionModel();
void testPollAnswerModel();
void testLineModel();
void testSpaceChildrenModel();
void testItineraryModel();
void testPublicRoomListModel();
void testMessageFilterModel();
void testThreePIdModel();
void testMediaMessageFilterModel();
void testWebshortcutModel();
void testTimelineMessageModel();
void testReadMarkerModel();
void testSearchModel();
void testStateModel();
void testTimelineModel();
void testStateKeysModel();
void testPinnedMessageModel();
void testUserListModel();
void testStickerModel();
void testPowerLevelModel();
void testImagePacksModel();
void testCompletionModel();
void testRoomListModel();
void testCommonRoomsModel();
void testNotificationsModel();
void testLocationsModel();
void testServerListModel();
void testEmojiModel();
void testCustomEmojiModel();
void testPushRuleModel();
void testActionsModel();
void testDevicesModel();
void testUserDirectoryListModel();
void testAccountEmoticonModel();
void testPermissionsModel();
void testLiveLocationsModel();
void testRoomSortParameterModel();
void testSortFilterRoomTreeModel();
void testSortFilterSpaceListModel();
void testSortFilterRoomListModel();
void testSpaceChildSortFilterModel();
void testStateFilterModel();
void testMessageContentFilterModel();
void testUserFilterModel();
void testEmoticonFilterModel();
void testDevicesProxyModel();
void testCompletionProxyModel();
};
void ModelTest::initTestCase()
{
Connection::setRoomType<NeoChatRoom>();
server.start();
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
auto accountManager = new AccountManager(true, this);
QSignalSpy spy(accountManager, &AccountManager::connectionAdded);
connection = dynamic_cast<NeoChatConnection *>(accountManager->accounts()->front());
const auto roomId = server.createRoom(u"@user:localhost:1234"_s);
eventId = server.sendEvent(roomId,
u"m.room.message"_s,
QJsonObject{
{u"body"_s, u"foo"_s},
{u"msgtype"_s, u"m.text"_s},
});
server.sendEvent(roomId,
u"m.room.message"_s,
QJsonObject{
{u"body"_s, u"asdf"_s},
{u"m.relates_to"_s,
QJsonObject{
{u"event_id"_s, u"$GEucSt3TfVl6DVpKEyeOlRsXzjLv2ZCVgSQuQclFg1o"_s},
{u"is_falling_back"_s, true},
{u"m.in_reply_to"_s, QJsonObject{{u"event_id"_s, u"$GEucSt3TfVl6DVpKEyeOlRsXzjLv2ZCVgSQuQclFg1o"_s}}},
{u"rel_type"_s, u"m.thread"_s},
}},
{u"msgtype"_s, u"m.text"_s},
});
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());
room = dynamic_cast<NeoChatRoom *>(connection->room(roomId));
QVERIFY(room);
}
void ModelTest::testRoomTreeModel()
{
auto roomTreeModel = new RoomTreeModel(this);
auto tester = new QAbstractItemModelTester(roomTreeModel, roomTreeModel);
tester->setUseFetchMore(true);
roomTreeModel->setConnection(connection);
}
void ModelTest::testMessageContentModel()
{
auto contentModel = std::make_unique<MessageContentModel>(room, eventId);
auto tester = new QAbstractItemModelTester(contentModel.get(), contentModel.get());
tester->setUseFetchMore(true);
}
void ModelTest::testEventMessageContentModel()
{
auto model = std::make_unique<EventMessageContentModel>(room, eventId);
auto tester = new QAbstractItemModelTester(model.get(), model.get());
tester->setUseFetchMore(true);
}
void ModelTest::testThreadModel()
{
auto model = std::make_unique<ThreadModel>(eventId, room);
auto tester = new QAbstractItemModelTester(model.get(), model.get());
tester->setUseFetchMore(true);
}
void ModelTest::testThreadFetchModel()
{
auto threadModel = std::make_unique<ThreadModel>(eventId, room);
auto model = new ThreadFetchModel(threadModel.get());
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
}
void ModelTest::testThreadChatBarModel()
{
auto threadModel = std::make_unique<ThreadModel>(eventId, room);
auto model = new ThreadChatBarModel(threadModel.get(), room);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
}
void ModelTest::testReactionModel()
{
auto messageContentModel = std::make_unique<MessageContentModel>(room);
auto model = new ReactionModel(messageContentModel.get(), eventId, room);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
}
void ModelTest::testPollAnswerModel()
{
auto handler = std::make_unique<PollHandler>(room, eventId);
auto model = new PollAnswerModel(handler.get());
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
}
void ModelTest::testLineModel()
{
auto model = new LineModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
auto document = new QTextDocument(this);
model->setDocument(document);
document->setPlainText(u"foo\nbar\n\nbaz"_s);
}
void ModelTest::testSpaceChildrenModel()
{
auto model = new SpaceChildrenModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setSpace(room);
}
void ModelTest::testItineraryModel()
{
auto model = new ItineraryModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
}
void ModelTest::testPublicRoomListModel()
{
auto model = new PublicRoomListModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setConnection(connection);
}
void ModelTest::testMessageFilterModel()
{
auto timelineModel = new TimelineModel(this);
auto model = new MessageFilterModel(this, timelineModel);
auto tester = new QAbstractItemModelTester(model, model);
timelineModel->setRoom(room);
tester->setUseFetchMore(true);
}
void ModelTest::testThreePIdModel()
{
auto model = new ThreePIdModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setConnection(connection);
}
void ModelTest::testMediaMessageFilterModel()
{
auto timelineModel = new TimelineModel(this);
auto messageFilterModel = new MessageFilterModel(this, timelineModel);
auto model = new MediaMessageFilterModel(this, messageFilterModel);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
timelineModel->setRoom(room);
}
void ModelTest::testWebshortcutModel()
{
auto model = new WebShortcutModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setSelectedText(u"Foo"_s);
}
void ModelTest::testTimelineMessageModel()
{
auto model = new TimelineMessageModel();
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setRoom(room);
}
void ModelTest::testReadMarkerModel()
{
auto model = std::make_unique<ReadMarkerModel>(eventId, room);
auto tester = new QAbstractItemModelTester(model.get(), model.get());
tester->setUseFetchMore(true);
}
void ModelTest::testSearchModel()
{
auto model = new SearchModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setSearchText(u"foo"_s);
model->setRoom(room);
}
void ModelTest::testStateModel()
{
auto model = new StateModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setRoom(room);
}
void ModelTest::testTimelineModel()
{
auto model = new TimelineModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setRoom(room);
}
void ModelTest::testStateKeysModel()
{
auto model = new StateKeysModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setEventType(u"m.room.member"_s);
model->setRoom(room);
}
void ModelTest::testPinnedMessageModel()
{
auto model = new PinnedMessageModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setRoom(room);
}
void ModelTest::testUserListModel()
{
auto model = new UserListModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setRoom(room);
}
void ModelTest::testStickerModel()
{
auto model = new StickerModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setPackIndex(0);
model->setRoom(room);
auto imagePacksModel = new ImagePacksModel(this);
model->setModel(imagePacksModel);
imagePacksModel->setRoom(room);
imagePacksModel->setShowEmoticons(true);
imagePacksModel->setShowStickers(true);
}
void ModelTest::testPowerLevelModel()
{
auto model = new PowerLevelModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
}
void ModelTest::testImagePacksModel()
{
auto model = new ImagePacksModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setRoom(room);
model->setShowEmoticons(true);
model->setShowStickers(true);
}
void ModelTest::testCompletionModel()
{
auto model = new CompletionModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setAutoCompletionType(CompletionModel::Room);
auto roomListModel = new RoomListModel(this);
roomListModel->setConnection(connection);
model->setRoomListModel(roomListModel);
}
void ModelTest::testRoomListModel()
{
auto model = new RoomListModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setConnection(connection);
}
void ModelTest::testCommonRoomsModel()
{
auto model = new CommonRoomsModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setConnection(connection);
model->setUserId(u"@user:example.com"_s);
}
void ModelTest::testNotificationsModel()
{
auto model = new NotificationsModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setConnection(connection);
}
void ModelTest::testLocationsModel()
{
auto model = new LocationsModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setRoom(room);
}
void ModelTest::testServerListModel()
{
auto model = new ServerListModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setConnection(connection);
}
void ModelTest::testEmojiModel()
{
auto tester = new QAbstractItemModelTester(&EmojiModel::instance(), &EmojiModel::instance());
tester->setUseFetchMore(true);
}
void ModelTest::testCustomEmojiModel()
{
auto tester = new QAbstractItemModelTester(&CustomEmojiModel::instance(), &CustomEmojiModel::instance());
tester->setUseFetchMore(true);
CustomEmojiModel::instance().setConnection(connection);
}
void ModelTest::testPushRuleModel()
{
auto model = new PushRuleModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setConnection(connection);
}
void ModelTest::testActionsModel()
{
auto tester = new QAbstractItemModelTester(&ActionsModel::instance(), &ActionsModel::instance());
tester->setUseFetchMore(true);
}
void ModelTest::testDevicesModel()
{
auto model = new DevicesModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setConnection(connection);
}
void ModelTest::testUserDirectoryListModel()
{
auto model = new UserDirectoryListModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setConnection(connection);
model->setSearchText(u"foo"_s);
}
void ModelTest::testAccountEmoticonModel()
{
auto model = new AccountEmoticonModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setConnection(connection);
}
void ModelTest::testPermissionsModel()
{
auto model = new PermissionsModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setRoom(room);
}
void ModelTest::testLiveLocationsModel()
{
auto model = new LiveLocationsModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setRoom(room);
}
void ModelTest::testRoomSortParameterModel()
{
auto model = new RoomSortParameterModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
}
void ModelTest::testSortFilterRoomTreeModel()
{
auto sourceModel = new RoomTreeModel(this);
auto model = new SortFilterRoomTreeModel(sourceModel, sourceModel);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
sourceModel->setConnection(connection);
}
void ModelTest::testSortFilterSpaceListModel()
{
auto sourceModel = new RoomListModel(this);
auto model = new SortFilterSpaceListModel(sourceModel, sourceModel);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
sourceModel->setConnection(connection);
}
void ModelTest::testSortFilterRoomListModel()
{
auto sourceModel = new RoomListModel(this);
auto model = new SortFilterRoomListModel(sourceModel, sourceModel);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
sourceModel->setConnection(connection);
}
void ModelTest::testSpaceChildSortFilterModel()
{
auto model = new SpaceChildSortFilterModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
auto spaceChildrenModel = new SpaceChildrenModel(this);
model->setSourceModel(spaceChildrenModel);
spaceChildrenModel->setSpace(nullptr);
}
void ModelTest::testStateFilterModel()
{
auto model = new StateFilterModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
auto stateModel = new StateModel(this);
model->setSourceModel(stateModel);
stateModel->setRoom(room);
}
void ModelTest::testMessageContentFilterModel()
{
auto model = new MessageContentFilterModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setSourceModel(ContentProvider::self().contentModelForEvent(room, eventId));
}
void ModelTest::testUserFilterModel()
{
auto model = new UserFilterModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
auto userListModel = new UserListModel(this);
model->setSourceModel(userListModel);
userListModel->setRoom(room);
}
void ModelTest::testEmoticonFilterModel()
{
auto model = new EmoticonFilterModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
auto accountEmoticonModel = new AccountEmoticonModel(this);
model->setSourceModel(accountEmoticonModel);
model->setShowEmojis(true);
model->setShowStickers(true);
accountEmoticonModel->setConnection(connection);
}
void ModelTest::testDevicesProxyModel()
{
auto model = new DevicesProxyModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
auto devicesModel = new DevicesModel(this);
model->setSourceModel(devicesModel);
devicesModel->setConnection(dynamic_cast<NeoChatConnection *>(connection));
}
void ModelTest::testCompletionProxyModel()
{
auto model = new CompletionProxyModel(this);
auto tester = new QAbstractItemModelTester(model, model);
tester->setUseFetchMore(true);
model->setSourceModel(&EmojiModel::instance());
}
QTEST_MAIN(ModelTest)
#include "modeltest.moc"

View File

@@ -9,6 +9,10 @@
#include <Quotient/quotient_common.h>
#include <Quotient/syncdata.h>
#include <KLocalizedString>
#include "accountmanager.h"
#include "server.h"
#include "testutils.h"
using namespace Quotient;
@@ -18,7 +22,8 @@ class NeoChatRoomTest : public QObject {
private:
Connection *connection = nullptr;
TestUtils::TestRoom *room = nullptr;
NeoChatRoom *room = nullptr;
Server server;
private Q_SLOTS:
void initTestCase();
@@ -27,8 +32,27 @@ private Q_SLOTS:
void NeoChatRoomTest::initTestCase()
{
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), "test-min-sync.json"_ls);
Connection::setRoomType<NeoChatRoom>();
server.start();
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
auto accountManager = new AccountManager(true, this);
QSignalSpy spy(accountManager, &AccountManager::connectionAdded);
connection = dynamic_cast<NeoChatConnection *>(accountManager->accounts()->front());
const auto roomId = server.createRoom(u"@user:localhost:1234"_s);
server.sendEvent(roomId,
u"m.room.message"_s,
QJsonObject{
{u"body"_s, u"foo"_s},
{u"msgtype"_s, u"m.text"_s},
});
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());
room = dynamic_cast<NeoChatRoom *>(connection->room(roomId));
QVERIFY(room);
}
void NeoChatRoomTest::eventTest()

View File

@@ -31,8 +31,8 @@ private Q_SLOTS:
void PollHandlerTest::initTestCase()
{
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), "test-pollhandlerstart-sync.json"_ls);
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-pollhandlerstart-sync.json"_s);
}
// Basically don't crash.
@@ -41,29 +41,32 @@ void PollHandlerTest::nullObject()
auto pollHandler = PollHandler();
QCOMPARE(pollHandler.hasEnded(), false);
QCOMPARE(pollHandler.answerCount(), 0);
QCOMPARE(pollHandler.numAnswers(), 0);
QCOMPARE(pollHandler.question(), QString());
QCOMPARE(pollHandler.options(), QJsonArray());
QCOMPARE(pollHandler.answers(), QJsonObject());
QCOMPARE(pollHandler.counts(), QJsonObject());
QCOMPARE(pollHandler.kind(), QString());
QCOMPARE(pollHandler.kind(), PollKind::Disclosed);
}
void PollHandlerTest::poll()
{
auto startEvent = eventCast<const PollStartEvent>(room->messageEvents().at(0).get());
auto pollHandler = PollHandler(room, startEvent);
auto pollHandler = PollHandler(room, startEvent->id());
auto options = QJsonArray{QJsonObject{{"id"_ls, "option1"_ls}, {"org.matrix.msc1767.text"_ls, "option1"_ls}},
QJsonObject{{"id"_ls, "option2"_ls}, {"org.matrix.msc1767.text"_ls, "option2"_ls}}};
QList<Quotient::EventContent::Answer> options = {EventContent::Answer{"option1"_L1, "option1"_L1}, EventContent::Answer{"option2"_L1, "option2"_L1}};
const auto answer0 = pollHandler.answerAtRow(0);
const auto answer1 = pollHandler.answerAtRow(1);
QCOMPARE(pollHandler.hasEnded(), false);
QCOMPARE(pollHandler.answerCount(), 0);
QCOMPARE(pollHandler.question(), QStringLiteral("test"));
QCOMPARE(pollHandler.options(), options);
QCOMPARE(pollHandler.answers(), QJsonObject());
QCOMPARE(pollHandler.counts(), QJsonObject());
QCOMPARE(pollHandler.kind(), QStringLiteral("org.matrix.msc3381.poll.disclosed"));
QCOMPARE(pollHandler.numAnswers(), 2);
QCOMPARE(pollHandler.question(), u"test"_s);
QCOMPARE(answer0.id, "option1"_L1);
QCOMPARE(answer1.id, "option2"_L1);
QCOMPARE(answer0.text, "option1text"_L1);
QCOMPARE(answer1.text, "option2text"_L1);
QCOMPARE(pollHandler.answerCountAtId(answer0.id), 0);
QCOMPARE(pollHandler.answerCountAtId(answer1.id), 0);
QCOMPARE(pollHandler.checkMemberSelectedId(connection->userId(), answer0.id), false);
QCOMPARE(pollHandler.checkMemberSelectedId(connection->userId(), answer1.id), false);
QCOMPARE(pollHandler.kind(), PollKind::Undisclosed);
}
QTEST_GUILESS_MAIN(PollHandlerTest)

9
autotests/qmltest.cpp Normal file
View File

@@ -0,0 +1,9 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include <quicktest.h>
QUICK_TEST_MAIN(NeoChat)

View File

@@ -9,6 +9,7 @@
#include <Quotient/events/roommessageevent.h>
#include "models/eventmessagecontentmodel.h"
#include "testutils.h"
using namespace Quotient;
@@ -20,61 +21,50 @@ class ReactionModelTest : public QObject
private:
Connection *connection = nullptr;
TestUtils::TestRoom *room = nullptr;
EventMessageContentModel *parentModel;
private Q_SLOTS:
void initTestCase();
void nullModel();
void basicReaction();
void newReaction();
};
void ReactionModelTest::initTestCase()
{
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-reactionmodel-sync.json"));
}
void ReactionModelTest::nullModel()
{
auto model = ReactionModel(nullptr, nullptr);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.data(model.index(0), ReactionModel::TextContentRole), QVariant());
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-reactionmodel-sync.json"_s);
parentModel = new EventMessageContentModel(room, "123456"_L1);
}
void ReactionModelTest::basicReaction()
{
auto event = eventCast<const RoomMessageEvent>(room->messageEvents().at(0).get());
auto model = ReactionModel(event, room);
auto model = ReactionModel(parentModel, event->id(), room);
QCOMPARE(model.rowCount(), 1);
QCOMPARE(model.data(model.index(0), ReactionModel::TextContentRole), QStringLiteral("<span style=\"font-family: 'emoji';\">👍</span>"));
QCOMPARE(model.data(model.index(0), ReactionModel::ReactionRole), QStringLiteral("👍"));
QCOMPARE(model.data(model.index(0), ReactionModel::ToolTipRole),
QStringLiteral("@alice:matrix.org reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
auto authorList = QVariantList{room->getUser(room->user(QStringLiteral("@alice:matrix.org")))};
QCOMPARE(model.data(model.index(0), ReactionModel::AuthorsRole), authorList);
QCOMPARE(model.data(model.index(0), ReactionModel::HasLocalUser), false);
QCOMPARE(model.data(model.index(0), ReactionModel::TextContentRole), u"<span style=\"font-family: 'emoji';\">👍</span>"_s);
QCOMPARE(model.data(model.index(0), ReactionModel::ReactionRole), u"👍"_s);
QCOMPARE(model.data(model.index(0), ReactionModel::ToolTipRole), u"Alice Margatroid reacted with <span style=\"font-family: 'emoji';\">👍</span>"_s);
QCOMPARE(model.data(model.index(0), ReactionModel::HasLocalMember), false);
}
void ReactionModelTest::newReaction()
{
auto event = eventCast<const RoomMessageEvent>(room->messageEvents().at(0).get());
auto model = new ReactionModel(event, room);
auto model = new ReactionModel(parentModel, event->id(), room);
QCOMPARE(model->rowCount(), 1);
QCOMPARE(model->data(model->index(0), ReactionModel::ToolTipRole),
QStringLiteral("@alice:matrix.org reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
QCOMPARE(model->data(model->index(0), ReactionModel::ToolTipRole), u"Alice Margatroid reacted with <span style=\"font-family: 'emoji';\">👍</span>"_s);
QSignalSpy spy(model, SIGNAL(modelReset()));
room->syncNewEvents(QLatin1String("test-reactionmodel-extra-sync.json"));
room->syncNewEvents(u"test-reactionmodel-extra-sync.json"_s);
QCOMPARE(model->rowCount(), 2);
QCOMPARE(spy.count(), 2); // Once for each of the 2 new reactions.
QCOMPARE(model->data(model->index(1), ReactionModel::ReactionRole), QStringLiteral("😆"));
QCOMPARE(model->data(model->index(1), ReactionModel::ReactionRole), u"😆"_s);
QCOMPARE(model->data(model->index(0), ReactionModel::ToolTipRole),
QStringLiteral("@alice:matrix.org and @bob:example.org reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
u"Alice Margatroid and Bob reacted with <span style=\"font-family: 'emoji';\">👍</span>"_s);
delete model;
}

View File

@@ -0,0 +1,141 @@
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QObject>
#include <QSignalSpy>
#include <QTest>
#include <QVariantList>
#include "accountmanager.h"
#include "models/actionsmodel.h"
#include "roommanager.h"
#include "server.h"
#include "testutils.h"
using namespace Quotient;
class RoomManagerTest : public QObject
{
Q_OBJECT
private:
NeoChatConnection *connection = nullptr;
NeoChatRoom *room = nullptr;
Server server;
private Q_SLOTS:
void initTestCase();
void testMaximizeMedia();
void testResolveMatrixLinks();
};
void RoomManagerTest::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);
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());
room = dynamic_cast<NeoChatRoom *>(connection->room(roomId));
QVERIFY(room);
RoomManager::instance().setConnection(connection);
QSignalSpy roomSpy(&RoomManager::instance(), &RoomManager::currentRoomChanged);
RoomManager::instance().resolveResource(room->id());
QVERIFY(roomSpy.size() > 0);
}
void RoomManagerTest::testMaximizeMedia()
{
QSignalSpy spy(&RoomManager::instance(), &RoomManager::showMaximizedMedia);
QSignalSpy syncSpy(connection, &Connection::syncDone);
QTest::ignoreMessage(QtMsgType::QtWarningMsg, "Tried to open media for empty event id");
RoomManager::instance().maximizeMedia(QString());
QVERIFY(!spy.wait(10));
QTest::ignoreMessage(QtMsgType::QtWarningMsg, "Tried to open media for unknown event id \"Doesn't exist\"");
RoomManager::instance().maximizeMedia(u"Doesn't exist"_s);
QVERIFY(!spy.wait(10));
const auto eventWithoutMedia = server.sendEvent(room->id(),
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());
QTest::ignoreMessage(QtMsgType::QtWarningMsg, u"Tried to open media for unknown event id \"%1\""_s.arg(eventWithoutMedia).toLatin1().data());
RoomManager::instance().maximizeMedia(eventWithoutMedia);
QVERIFY(!spy.wait(10));
// NOTE: This is supposed to test that maximizing pending media works correctly. This probably doesn't work in the UI yet, but at least the backend supports
// it. If the server ever learns how to process events, this becomes pointless and we need to find a way of preventing *these* events from arriving
auto pendingEventWithoutMedia = room->postText(u"Hello"_s);
QTest::ignoreMessage(QtMsgType::QtWarningMsg, u"Tried to open media for unknown event id \"%1\""_s.arg(pendingEventWithoutMedia).toLatin1().data());
RoomManager::instance().maximizeMedia(pendingEventWithoutMedia);
QVERIFY(!spy.wait(10));
const auto eventWithMedia = server.sendEvent(room->id(),
u"m.room.message"_s,
QJsonObject({
{u"body"_s, u"Foo"_s},
{u"filename"_s, u"foo.jpg"_s},
{u"info"_s,
QJsonObject{
{u"h"_s, 1000},
{u"w"_s, 2000},
{u"size"_s, 10000},
{u"mimetype"_s, u"image/png"_s},
}},
{u"msgtype"_s, u"m.image"_s},
{u"url"_s, u"mxc://foo.bar/asdf"_s},
}));
QVERIFY(syncSpy.wait());
QVERIFY(syncSpy.wait());
QVERIFY(syncSpy.wait());
RoomManager::instance().maximizeMedia(eventWithMedia);
QVERIFY(spy.size() == 1);
QVERIFY(spy[0][0] == 0);
auto pendingEventWithMedia = room->postJson(u"m.room.message"_s,
QJsonObject({
{u"body"_s, u"Foo"_s},
{u"filename"_s, u"foo.jpg"_s},
{u"info"_s,
QJsonObject{
{u"h"_s, 1000},
{u"w"_s, 2000},
{u"size"_s, 10000},
{u"mimetype"_s, u"image/png"_s},
}},
{u"msgtype"_s, u"m.image"_s},
{u"url"_s, u"mxc://foo.bar/asdf"_s},
}));
RoomManager::instance().maximizeMedia(pendingEventWithMedia);
QVERIFY(spy.size() == 2);
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"

402
autotests/server.cpp Normal file
View File

@@ -0,0 +1,402 @@
// SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "server.h"
#include <QFile>
#include <QHttpServerResponder>
#include <QJsonArray>
#include <QJsonDocument>
#include <QNetworkReply>
#include <QSslCertificate>
#include <QSslKey>
#include <QUuid>
#include <Quotient/networkaccessmanager.h>
using namespace Qt::Literals::StringLiterals;
QString generateEventId()
{
return u"$"_s + QString::fromLatin1(QCryptographicHash::hash(QUuid::createUuid().toString().toLatin1(), QCryptographicHash::Sha1).toBase64());
}
QString generateRoomId()
{
return u"!%1:localhost:1234"_s
.arg(QString::fromLatin1(QCryptographicHash::hash(QUuid::createUuid().toString().toLatin1(), QCryptographicHash::Sha1).toBase64()))
.replace(u'/', QChar());
}
Server::Server()
{
}
void Server::start()
{
QObject::connect(Quotient::NetworkAccessManager::instance(),
&QNetworkAccessManager::sslErrors,
Quotient::NetworkAccessManager::instance(),
[](QNetworkReply *reply) {
reply->ignoreSslErrors();
});
m_server.route(u"/.well-known/matrix/client"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
responder.write(QJsonDocument(QJsonObject{
{u"m.homeserver"_s, QJsonObject{{u"base_url"_s, u"https://localhost:1234"_s}}},
}),
QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/versions"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
responder.write(QJsonDocument(QJsonObject{
{u"versions"_s,
QJsonArray{
u"v1.0"_s,
u"v1.1"_s,
u"v1.2"_s,
u"v1.3"_s,
u"v1.4"_s,
u"v1.5"_s,
u"v1.6"_s,
u"v1.7"_s,
u"v1.8"_s,
u"v1.9"_s,
u"v1.10"_s,
u"v1.11"_s,
u"v1.12"_s,
u"v1.13"_s,
}},
}),
QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/v3/capabilities"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
responder.write(
QJsonDocument(QJsonObject{{u"capabilities"_s,
QJsonObject{
{u"m.room_versions"_s, QJsonObject{{u"m.available"_s, QJsonObject{{u"1"_s, u"stable"_s}}}, {u"default"_s, u"1"_s}}},
}}}),
QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/v3/account/whoami"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
responder.write(QJsonDocument(QJsonObject{
{u"device_id"_s, u"device_id_1234"_s},
{u"user_id"_s, u"@user:localhost:1234"_s},
}),
QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/v3/login"_s, QHttpServerRequest::Method::Post, [](QHttpServerResponder &responder) {
// TODO
// if data["identifier"]["user"] != "user" or data["password"] != "1234":
// abort(403)
responder.write(QJsonDocument(QJsonObject{
{u"access_token"_s, u"token_login"_s},
{u"device_id"_s, u"device_1234"_s},
{u"user_id"_s, u"@user:localhost:1234"_s},
}),
QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/v3/login"_s, QHttpServerRequest::Method::Get, [](QHttpServerResponder &responder) {
responder.write(QJsonDocument(QJsonObject{
{u"flows"_s, QJsonArray{QJsonObject{{u"type"_s, u"m.login.password"_s}}}},
}),
QHttpServerResponder::StatusCode::Ok);
});
m_server.route(u"/_matrix/client/v3/rooms/<arg>/invite"_s,
QHttpServerRequest::Method::Post,
[this](const QString &roomId, QHttpServerResponder &responder, const QHttpServerRequest &request) {
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, &Server::sync);
QSslConfiguration config;
QFile key(QStringLiteral(DATA_DIR) + u"/localhost.key"_s);
void(key.open(QFile::ReadOnly));
config.setPrivateKey(QSslKey(&key, QSsl::Rsa));
config.setLocalCertificate(QSslCertificate::fromPath(QStringLiteral(DATA_DIR) + u"/localhost.crt"_s).constFirst());
m_sslServer.setSslConfiguration(config);
if (!m_sslServer.listen(QHostAddress::LocalHost, 1234) || !m_server.bind(&m_sslServer)) {
qFatal() << "Server failed to listen on a port.";
return;
} else {
qInfo() << "Server listening";
}
}
QString Server::createRoom(const QString &matrixId)
{
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)
{
Changes changes;
changes.invitations += Changes::InviteUser{
.userId = matrixId,
.roomId = roomId,
};
m_state += changes;
}
void Server::banUser(const QString &roomId, const QString &matrixId)
{
Changes changes;
changes.bans += Changes::BanUser{
.userId = matrixId,
.roomId = roomId,
};
m_state += changes;
}
void Server::joinUser(const QString &roomId, const QString &matrixId)
{
Changes changes;
changes.joins += Changes::JoinUser{
.userId = matrixId,
.roomId = roomId,
};
m_state += changes;
}
QString Server::createServerNoticesRoom(const QString &matrixId)
{
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();
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;
}
QString Server::sendStateEvent(const QString &roomId, const QString &eventType, const QString &stateKey, const QJsonObject &content)
{
Changes changes;
const auto eventId = generateEventId();
const auto json = 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},
{u"state_key"_s, stateKey}};
changes.events += Changes::Event{
.fullJson = json,
};
changes.stateEvents += Changes::Event{.fullJson = json};
m_state += changes;
return eventId;
}
void Server::sync(const QHttpServerRequest &request, QHttpServerResponder &responder)
{
QJsonObject joinRooms;
auto token = request.query().queryItemValue(u"since"_s).toInt();
const auto changes = m_state.mid(token);
for (const auto &change : changes) {
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 : changes) {
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 : changes) {
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 : changes) {
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 : changes) {
for (const auto &state : change.stateEvents) {
const auto &roomId = state.fullJson[u"room_id"_s].toString();
// TODO: The join could be for a room we haven't joined yet. Shouldn't be necessary for now, though.
auto stateEvents = joinRooms[roomId][u"state"_s][u"events"_s].toArray();
stateEvents.append(state.fullJson);
auto room = joinRooms[roomId].toObject();
room[u"state"_s] = QJsonObject{{u"events"_s, stateEvents}};
joinRooms[roomId] = room;
}
}
for (const auto &change : changes) {
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;
}
responder.write(QJsonDocument(syncData), QHttpServerResponder::StatusCode::Ok);
}

80
autotests/server.h Normal file
View File

@@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
// 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;
QList<Event> stateEvents;
};
struct RoomData {
QStringList members;
QString id;
QStringList tags;
};
class Server : public QObject
{
Q_OBJECT
public:
Server();
void start();
/**
* Create a room and place the user with id matrixId in it.
* Returns the room's id
*/
QString createRoom(const QString &matrixId);
void inviteUser(const QString &roomId, const QString &matrixId);
void banUser(const QString &roomId, const QString &matrixId);
void joinUser(const QString &roomId, const QString &matrixId);
/**
* Create a server notices room.
*/
QString createServerNoticesRoom(const QString &matrixId);
QString sendEvent(const QString &roomId, const QString &eventType, const QJsonObject &content);
QString sendStateEvent(const QString &roomId, const QString &eventType, const QString &stateKey, const QJsonObject &content);
private:
QHttpServer m_server;
QSslServer m_sslServer;
void sync(const QHttpServerRequest &request, QHttpServerResponder &responder);
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>
@@ -31,8 +32,8 @@ public:
{
if (!syncFileName.isEmpty()) {
QFile testSyncFile;
testSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + syncFileName);
testSyncFile.open(QIODevice::ReadOnly);
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + syncFileName);
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));
@@ -45,8 +46,8 @@ inline Quotient::event_ptr_tt<EventT> loadEventFromFile(const QString &eventFile
{
if (!eventFileName.isEmpty()) {
QFile testEventFile;
testEventFile.setFileName(QLatin1String(DATA_DIR) + u'/' + eventFileName);
testEventFile.open(QIODevice::ReadOnly);
testEventFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + eventFileName);
Q_UNUSED(testEventFile.open(QIODevice::ReadOnly));
auto testSyncJson = QJsonDocument::fromJson(testEventFile.readAll()).object();
return Quotient::loadEvent<EventT>(testSyncJson);
}

View File

@@ -8,13 +8,12 @@
#include <Quotient/quotient_common.h>
#include <Quotient/syncdata.h>
#include <qnamespace.h>
#include <Kirigami/Platform/PlatformTheme>
#include "enums/messagecomponenttype.h"
#include "models/customemojimodel.h"
#include "models/messagecontentmodel.h"
#include "neochatconnection.h"
#include "utils.h"
#include "testutils.h"
@@ -35,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();
@@ -45,7 +48,10 @@ private Q_SLOTS:
void sendCustomEmoji();
void sendCustomEmojiCode_data();
void sendCustomEmojiCode();
void sendCustomTags_data();
void sendCustomTags();
void receiveSpacelessSelfClosingTag();
void receiveStripReply();
void receivePlainTextIn();
@@ -59,53 +65,63 @@ private Q_SLOTS:
void receiveRichStrikethrough();
void receiveRichtextIn();
void receiveRichMxcUrl();
void receiveRichPlainUrl_data();
void receiveRichPlainUrl();
void receiveRichEdited_data();
void receiveRichEdited();
void receiveLineSeparator();
void receiveRichCodeUrl();
void receiveRichColor();
void componentOutput_data();
void componentOutput();
void updateSpoiler_data();
void updateSpoiler();
};
void TextHandlerTest::initTestCase()
{
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
connection->setAccountData("im.ponies.user_emotes"_ls,
QJsonObject{{"images"_ls,
QJsonObject{{"test"_ls,
QJsonObject{{"body"_ls, "Test custom emoji"_ls},
{"url"_ls, "mxc://example.org/test"_ls},
{"usage"_ls, QJsonArray{"emoticon"_ls}}}}}}});
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
connection->setAccountData(u"im.ponies.user_emotes"_s,
QJsonObject{{"images"_L1,
QJsonObject{{"test"_L1,
QJsonObject{{"body"_L1, "Test custom emoji"_L1},
{"url"_L1, "mxc://example.org/test"_L1},
{"usage"_L1, QJsonArray{"emoticon"_L1}}}}}}});
CustomEmojiModel::instance().setConnection(static_cast<NeoChatConnection *>(connection));
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-texthandler-sync.json"));
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-texthandler-sync.json"_s);
}
void TextHandlerTest::allowedAttributes()
{
const QString testInputString1 = QStringLiteral("<p><span data-mx-spoiler><font color=#FFFFFF>Test</font><span></p>");
const QString testOutputString1 = QStringLiteral("<p><span data-mx-spoiler><font color=#FFFFFF>Test</font><span></p>");
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 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 = QStringLiteral("<p><a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a></p>");
const QString testOutputString2 = QStringLiteral("<p><a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a></p>");
const QString testInputString2 = 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()
{
const QString testInputString = QStringLiteral("<p>Allowed</p> <span>Allowed</span> <body>Disallowed</body>");
const QString testOutputString = QStringLiteral("<p>Allowed</p> <span>Allowed</span> Disallowed");
const QString testInputString = u"<p>Allowed</p> <span>Allowed</span> <body>Disallowed</body>"_s;
const QString testOutputString = u"<p>Allowed</p> <span>Allowed</span> Disallowed"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -116,8 +132,8 @@ void TextHandlerTest::stripDisallowedTags()
void TextHandlerTest::stripDisallowedAttributes()
{
const QString testInputString = QStringLiteral("<p style=\"font-size:50px;\" color=#FFFFFF>Test</p>");
const QString testOutputString = QStringLiteral("<p>Test</p>");
const QString testInputString = u"<p style=\"font-size:50px;\" color=#FFFFFF>Test</p>"_s;
const QString testOutputString = u"Test"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -132,8 +148,8 @@ void TextHandlerTest::stripDisallowedAttributes()
*/
void TextHandlerTest::emptyCodeTags()
{
const QString testInputString = QStringLiteral("<pre><code></code></pre>");
const QString testOutputString = QStringLiteral("<pre><code></code></pre>");
const QString testInputString = u"<pre><code></code></pre>"_s;
const QString testOutputString = u"<pre><code></code></pre>"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -142,10 +158,60 @@ 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 = QStringLiteral("This data should just be put in a paragraph.");
const QString testOutputString = QStringLiteral("<p>This data should just be put in a paragraph.</p>");
const QString testInputString = u"This data should just be left alone."_s;
const QString testOutputString = u"This data should just be left alone."_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -155,11 +221,10 @@ void TextHandlerTest::sendSimpleStringCase()
void TextHandlerTest::sendSingleParaMarkup()
{
const QString testInputString = QStringLiteral(
"Text para with **bold**, *italic*, [link](https://kde.org), ![image](mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e), `inline code`.");
const QString testOutputString = QStringLiteral(
"<p>Text para with <strong>bold</strong>, <em>italic</em>, <a href=\"https://kde.org\">link</a>, <img "
"src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e\" alt=\"image\">, <code>inline code</code>.</p>");
const QString testInputString =
u"Text para with **bold**, *italic*, [link](https://kde.org), ![image](mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e), `inline code`."_s;
const QString testOutputString =
u"Text para with <strong>bold</strong>, <em>italic</em>, <a href=\"https://kde.org\">link</a>, <img src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e\" alt=\"image\">, <code>inline code</code>."_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -170,11 +235,9 @@ void TextHandlerTest::sendSingleParaMarkup()
void TextHandlerTest::sendMultipleSectionMarkup()
{
const QString testInputString =
QStringLiteral("Text para\n> blockquote\n* List 1\n* List 2\n1. one\n2. two\n# Heading 1\n## Heading 2\nhorizontal rule\n\n---\n```\ncodeblock\n```");
const QString testOutputString = QStringLiteral(
"<p>Text para</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>\n<ul>\n<li>List 1</li>\n<li>List "
"2</li>\n</ul>\n<ol>\n<li>one</li>\n<li>two</li>\n</ol>\n<h1>Heading 1</h1>\n<h2>Heading 2</h2>\n<p>horizontal "
"rule</p>\n<hr>\n<pre><code>codeblock\n</code></pre>");
u"Text para\n> blockquote\n* List 1\n* List 2\n1. one\n2. two\n# Heading 1\n## Heading 2\nhorizontal rule\n\n---\n```\ncodeblock\n```"_s;
const QString testOutputString =
u"<p>Text para</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>\n<ul>\n<li>List 1</li>\n<li>List 2</li>\n</ul>\n<ol>\n<li>one</li>\n<li>two</li>\n</ol>\n<h1>Heading 1</h1>\n<h2>Heading 2</h2>\n<p>horizontal rule</p>\n<hr>\n<pre><code>codeblock\n</code></pre>"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -184,8 +247,8 @@ void TextHandlerTest::sendMultipleSectionMarkup()
void TextHandlerTest::sendBadLinks()
{
const QString testInputString = QStringLiteral("[link](kde.org), ![image](https://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e)");
const QString testOutputString = QStringLiteral("<p><a>link</a>, <img alt=\"image\"></p>");
const QString testInputString = u"[link](kde.org), ![image](https://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e)"_s;
const QString testOutputString = u"<a>link</a>, <img alt=\"image\">"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -198,9 +261,9 @@ void TextHandlerTest::sendBadLinks()
*/
void TextHandlerTest::sendEscapeCode()
{
const QString testInputString = QStringLiteral("```\n<p>Test <span style=\"font-size:50px;\">some</span> code</p>\n```");
const QString testInputString = u"```\n<p>Test <span style=\"font-size:50px;\">some</span> code</p>\n```"_s;
const QString testOutputString =
QStringLiteral("<pre><code>&lt;p&gt;Test &lt;span style=&quot;font-size:50px;&quot;&gt;some&lt;/span&gt; code&lt;/p&gt;\n</code></pre>");
u"<pre><code>&lt;p&gt;Test &lt;span style=&quot;font-size:50px;&quot;&gt;some&lt;/span&gt; code&lt;/p&gt;\n</code></pre>"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -210,8 +273,8 @@ void TextHandlerTest::sendEscapeCode()
void TextHandlerTest::sendCodeClass()
{
const QString testInputString = QStringLiteral("```html\nsome code\n```\n<pre><code class=\"code-underline\">some more code</code></pre>");
const QString testOutputString = QStringLiteral("<pre><code class=\"language-html\">some code\n</code></pre>\n<pre><code>some more code</code></pre>");
const QString testInputString = u"```html\nsome code\n```\n<pre><code class=\"code-underline\">some more code</code></pre>"_s;
const QString testOutputString = u"<pre><code class=\"language-html\">some code\n</code></pre>\n<pre><code>some more code</code></pre>"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -221,9 +284,9 @@ void TextHandlerTest::sendCodeClass()
void TextHandlerTest::sendCustomEmoji()
{
const QString testInputString = QStringLiteral(":test:");
const QString testOutputString = QStringLiteral(
"<p><img data-mx-emoticon=\"\" src=\"mxc://example.org/test\" alt=\":test:\" title=\":test:\" height=\"32\" vertical-align=\"middle\" /></p>");
const QString testInputString = u":test:"_s;
const QString testOutputString =
u"<img data-mx-emoticon=\"\" src=\"mxc://example.org/test\" alt=\":test:\" title=\":test:\" height=\"32\" vertical-align=\"middle\" />"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -236,8 +299,8 @@ void TextHandlerTest::sendCustomEmojiCode_data()
QTest::addColumn<QString>("testInputString");
QTest::addColumn<QString>("testOutputString");
QTest::newRow("inline") << QStringLiteral("`:test:`") << QStringLiteral("<p><code>:test:</code></p>");
QTest::newRow("block") << QStringLiteral("```\n:test:\n```") << QStringLiteral("<pre><code>:test:\n</code></pre>");
QTest::newRow("inline") << u"`:test:`"_s << u"<code>:test:</code>"_s;
QTest::newRow("block") << u"```\n:test:\n```"_s << u"<pre><code>:test:\n</code></pre>"_s;
}
// Custom emojis in code blocks should be left alone.
@@ -252,12 +315,58 @@ void TextHandlerTest::sendCustomEmojiCode()
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
}
void TextHandlerTest::sendCustomTags_data()
{
QTest::addColumn<QString>("testInputString");
QTest::addColumn<QString>("testOutputString");
// spoiler
QTest::newRow("incomplete spoiler") << u"||test"_s << u"||test"_s;
QTest::newRow("complete spoiler") << u"||test||"_s << u"<span data-mx-spoiler>test</span>"_s;
QTest::newRow("multiple spoiler") << u"||apple||banana||pear||"_s << u"<span data-mx-spoiler>apple</span>banana<span data-mx-spoiler>pear</span>"_s;
QTest::newRow("inside code block spoiler") << u"```||apple||```"_s << u"<code>||apple||</code>"_s;
QTest::newRow("outside code block spoiler") << u"||apple|| ```||banana||``` ||pear||"_s
<< u"<span data-mx-spoiler>apple</span> <code>||banana||</code> <span data-mx-spoiler>pear</span>"_s;
QTest::newRow("complex spoiler") << u"Between `formFactor == Horizontal||Vertical` and `location == top||left||bottom||right`"_s
<< u"Between <code>formFactor == Horizontal||Vertical</code> and <code>location == top||left||bottom||right</code>"_s;
// strikethrough
QTest::newRow("incomplete strikethrough") << u"~~test"_s << u"~~test"_s;
QTest::newRow("complete strikethrough") << u"~~test~~"_s << u"<del>test</del>"_s;
QTest::newRow("inside code block strikethrough") << u"```~~apple~~```"_s << u"<code>~~apple~~</code>"_s;
QTest::newRow("outside code block strikethrough") << u"~~apple~~ ```~~banana~~``` ~~pear~~"_s
<< u"<del>apple</del> <code>~~banana~~</code> <del>pear</del>"_s;
}
void TextHandlerTest::sendCustomTags()
{
QFETCH(QString, testInputString);
QFETCH(QString, testOutputString);
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
}
void TextHandlerTest::receiveSpacelessSelfClosingTag()
{
const QString testInputString = u"Test...<br/>...ing"_s;
const QString testRichOutputString = u"Test...<br/>...ing"_s;
const QString testPlainOutputString = u"Test...\n...ing"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleRecieveRichText(), testRichOutputString);
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText), testPlainOutputString);
}
void TextHandlerTest::receiveStripReply()
{
const QString testInputString = QStringLiteral(
"<mx-reply><blockquote><a href=\"https://matrix.to/#/!somewhere:example.org/$event:example.org\">In reply to</a><a "
"href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a><br />Message replied to.</blockquote></mx-reply>Reply message.");
const QString testOutputString = QStringLiteral("Reply message.");
const QString testInputString =
u"<mx-reply><blockquote><a href=\"https://matrix.to/#/!somewhere:example.org/$event:example.org\">In reply to</a><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a><br />Message replied to.</blockquote></mx-reply>Reply message."_s;
const QString testOutputString = u"Reply message."_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -271,9 +380,10 @@ void TextHandlerTest::receiveRichInPlainOut_data()
QTest::addColumn<QString>("testInputString");
QTest::addColumn<QString>("testOutputString");
QTest::newRow("ampersand") << QStringLiteral("a &amp; b") << QStringLiteral("a & b");
QTest::newRow("quote") << QStringLiteral("&quot;a and b&quot;") << QStringLiteral("\"a and b\"");
QTest::newRow("new line") << QStringLiteral("new<br>line") << QStringLiteral("new\nline");
QTest::newRow("ampersand") << u"a &amp; b"_s << u"a & b"_s;
QTest::newRow("quote") << u"&quot;a and b&quot;"_s << u"\"a and b\""_s;
QTest::newRow("new line") << u"new<br>line"_s << u"new\nline"_s;
QTest::newRow("unescape") << u"can&#x27;t"_s << u"can't"_s;
}
void TextHandlerTest::receiveRichInPlainOut()
@@ -289,13 +399,14 @@ void TextHandlerTest::receiveRichInPlainOut()
void TextHandlerTest::receivePlainTextIn()
{
const QString testInputString = QStringLiteral("<plain text in tag bracket>\nTest link https://kde.org.");
const QString testOutputStringRich = QStringLiteral("&lt;plain text in tag bracket&gt;<br>Test link <a href=\"https://kde.org\">https://kde.org</a>.");
QString testOutputStringPlain = QStringLiteral("<plain text in tag bracket>\nTest link https://kde.org.");
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\" 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.
const QString testInputString2 = QStringLiteral("last line is \"Time to switch to a new topic.\"");
const QString testOutputString2 = QStringLiteral("last line is \"Time to switch to a new topic.\"");
const QString testInputString2 = u"last line is \"Time to switch to a new topic.\""_s;
const QString testOutputString2 = u"last line is \"Time to switch to a new topic.\""_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -310,12 +421,12 @@ void TextHandlerTest::receivePlainTextIn()
void TextHandlerTest::receiveStripNewlines()
{
const QString testInputStringPlain = QStringLiteral("Test\nmany\nnew\nlines.");
const QString testInputStringRich = QStringLiteral("Test<br>many<br />new<br>lines.");
const QString testOutputString = QStringLiteral("Test many new lines.");
const QString testInputStringPlain = u"Test\nmany\nnew\nlines."_s;
const QString testInputStringRich = u"Test<br>many<br />new<br>lines."_s;
const QString testOutputString = u"Test many new lines."_s;
const QString testInputStringPlain2 = QStringLiteral("* List\n* Items");
const QString testOutputString2 = QStringLiteral("List Items");
const QString testInputStringPlain2 = u"* List\n* Items"_s;
const QString testOutputString2 = u"List Items"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputStringPlain);
@@ -337,8 +448,8 @@ void TextHandlerTest::receiveStripNewlines()
*/
void TextHandlerTest::receivePlainStripHtml()
{
const QString testInputString = QStringLiteral("<p>Test</p> <pre><code>Some code <strong>with tags</strong></code></pre>");
const QString testOutputString = QStringLiteral("Test Some code <strong>with tags</strong>");
const QString testInputString = u"<p>Test</p> <pre><code>Some code <strong>with tags</strong></code></pre>"_s;
const QString testOutputString = u"Test Some code <strong>with tags</strong>"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -348,8 +459,8 @@ void TextHandlerTest::receivePlainStripHtml()
void TextHandlerTest::receivePlainStripMarkup()
{
const QString testInputString = QStringLiteral("**bold** `<p>inline code</p>` *italic*");
const QString testOutputString = QStringLiteral("bold <p>inline code</p> italic");
const QString testInputString = u"**bold** `<p>inline code</p>` *italic*"_s;
const QString testOutputString = u"bold <p>inline code</p> italic"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -359,8 +470,8 @@ void TextHandlerTest::receivePlainStripMarkup()
void TextHandlerTest::receiveRichUserPill()
{
const QString testInputString = QStringLiteral("<p><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></p>");
const QString testOutputString = QStringLiteral("<p><b><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></b></p>");
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\" style=\"text-decoration: none;\">@alice:example.org</a></b>"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -370,8 +481,8 @@ void TextHandlerTest::receiveRichUserPill()
void TextHandlerTest::receiveRichStrikethrough()
{
const QString testInputString = QStringLiteral("<p><del>Test</del></p>");
const QString testOutputString = QStringLiteral("<p><s>Test</s></p>");
const QString testInputString = u"<p><del>Test</del></p>"_s;
const QString testOutputString = u"<s>Test</s>"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -381,8 +492,8 @@ void TextHandlerTest::receiveRichStrikethrough()
void TextHandlerTest::receiveRichtextIn()
{
const QString testInputString = QStringLiteral("<p>Test</p> <pre><code>Some code <strong>with tags</strong></code></pre>");
const QString testOutputString = QStringLiteral("<p>Test</p> <pre><code>Some code &lt;strong&gt;with tags&lt;/strong&gt;</code></pre>");
const QString testInputString = u"<p>Test</p> <pre><code>Some code <strong>with tags</strong></code></pre>"_s;
const QString testOutputString = u"<p>Test</p> <pre><code>Some code &lt;strong&gt;with tags&lt;/strong&gt;</code></pre>"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -392,15 +503,10 @@ void TextHandlerTest::receiveRichtextIn()
void TextHandlerTest::receiveRichMxcUrl()
{
const QString testInputString = QStringLiteral(
"<img src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e\" alt=\"image\"><img src=\"mxc://kde.org/34c3464b3a1bd7f55af2d559e07d2c773c430e73\" "
"alt=\"image\">");
const QString testOutputString = QStringLiteral(
"<img "
"src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e?user_id=@bob:kde.org&room_id=%23myroom:kde.org&event_id=$143273582443PhrSn:example.org\" "
"alt=\"image\"><img "
"src=\"mxc://kde.org/34c3464b3a1bd7f55af2d559e07d2c773c430e73?user_id=@bob:kde.org&room_id=%23myroom:kde.org&event_id=$143273582443PhrSn:example.org\" "
"alt=\"image\">");
const QString testInputString =
u"<img src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e\" alt=\"image\"><img src=\"mxc://kde.org/34c3464b3a1bd7f55af2d559e07d2c773c430e73\" alt=\"image\">"_s;
const QString testOutputString =
u"<img src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e?user_id=@bob:kde.org&room_id=%23myroom:kde.org&event_id=$143273582443PhrSn:example.org\" alt=\"image\"><img src=\"mxc://kde.org/34c3464b3a1bd7f55af2d559e07d2c773c430e73?user_id=@bob:kde.org&room_id=%23myroom:kde.org&event_id=$143273582443PhrSn:example.org\" alt=\"image\">"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -408,6 +514,34 @@ void TextHandlerTest::receiveRichMxcUrl()
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, room, room->messageEvents().at(0).get()), testOutputString);
}
void TextHandlerTest::receiveRichPlainUrl_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<QString>("output");
// This is an actual link that caused trouble which is why it's so long. Keeping
// so we can confirm consistent behaviour for complex urls.
QTest::addRow("link 1")
<< u"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im <a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">Link already rich</a>"_s
<< u"<a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\" 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/\" 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" 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\" 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;
}
/**
* For when your rich input string has a plain text url left in.
*
@@ -416,51 +550,13 @@ void TextHandlerTest::receiveRichMxcUrl()
*/
void TextHandlerTest::receiveRichPlainUrl()
{
// This is an actual link that caused trouble which is why it's so long. Keeping
// so we can confirm consistent behaviour for complex urls.
const QString testInputStringLink1 = QStringLiteral(
"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>");
const QString testOutputStringLink1 = QStringLiteral(
"<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>");
// Another real case. The linkification wasn't handling it when a single link
// contains what looks like and email. It was been broken into 3 but needs to
// be just single link.
const QString testInputStringLink2 = QStringLiteral("https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/");
const QString testOutputStringLink2 = QStringLiteral(
"<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>");
QString testInputStringEmail = QStringLiteral(R"(email@example.com <a href="mailto:email@example.com">Link already rich</a>)");
QString testOutputStringEmail =
QStringLiteral(R"(<a href="mailto:email@example.com">email@example.com</a> <a href="mailto:email@example.com">Link already rich</a>)");
QString testInputStringMxId = QStringLiteral("@user:kde.org <a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a>");
QString testOutputStringMxId = QStringLiteral(
"<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>");
QFETCH(QString, input);
QFETCH(QString, output);
TextHandler testTextHandler;
testTextHandler.setData(testInputStringLink1);
testTextHandler.setData(input);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringLink1);
testTextHandler.setData(testInputStringLink2);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringLink2);
testTextHandler.setData(testInputStringEmail);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringEmail);
testTextHandler.setData(testInputStringMxId);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringMxId);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), output);
}
void TextHandlerTest::receiveRichEdited_data()
@@ -468,9 +564,12 @@ void TextHandlerTest::receiveRichEdited_data()
QTest::addColumn<QString>("testInputString");
QTest::addColumn<QString>("testOutputString");
QTest::newRow("basic") << QStringLiteral("Edited") << QStringLiteral("Edited <span style=\"color:#000000\">(edited)</span>");
QTest::newRow("multiple paragraphs") << QStringLiteral("<p>Edited</p>\n<p>Edited</p>")
<< QStringLiteral("<p>Edited</p>\n<p>Edited <span style=\"color:#000000\">(edited)</span></p>");
auto theme = static_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
QTest::newRow("basic") << u"Edited"_s << u"Edited <span style=\"color:%1\">(edited)</span>"_s.arg(theme ? theme->disabledTextColor().name() : u"#000000"_s);
QTest::newRow("multiple paragraphs") << u"<p>Edited</p>\n<p>Edited</p>"_s
<< u"<p>Edited</p>\n<p>Edited <span style=\"color:%1\">(edited)</span></p>"_s.arg(
theme ? theme->disabledTextColor().name() : u"#000000"_s);
}
void TextHandlerTest::receiveRichEdited()
@@ -487,61 +586,70 @@ void TextHandlerTest::receiveRichEdited()
void TextHandlerTest::receiveLineSeparator()
{
auto text = QStringLiteral("foo\u2028bar");
auto text = u"foo\u2028bar"_s;
TextHandler textHandler;
textHandler.setData(text);
QCOMPARE(textHandler.handleRecievePlainText(Qt::PlainText, true), QStringLiteral("foo bar"));
QCOMPARE(textHandler.handleRecievePlainText(Qt::PlainText, true), u"foo bar"_s);
}
void TextHandlerTest::receiveRichCodeUrl()
{
auto input = QStringLiteral("<code>https://kde.org</code>");
auto input = u"<code>https://kde.org</code>"_s;
TextHandler testTextHandler;
testTextHandler.setData(input);
QCOMPARE(testTextHandler.handleRecieveRichText(), input);
}
void TextHandlerTest::receiveRichColor()
{
const QString testInputString =
u"<span data-mx-color=\"#ff00be\">¯</span><span data-mx-color=\"#ff3b1d\">\\</span><span data-mx-color=\"#ffa600\">_</span><span data-mx-color=\"#64d200\">(</span><span data-mx-color=\"#00e261\">ツ</span><span data-mx-color=\"#00e7ff\">)</span><span data-mx-color=\"#00e1ff\">_</span><span data-mx-color=\"#00bdff\">/</span><span data-mx-color=\"#ff60ff\">¯</span>"_s;
const QString testOutputString =
u"<span style=\"color: #ff00be;\">¯</span><span style=\"color: #ff3b1d;\">\\</span><span style=\"color: #ffa600;\">_</span><span style=\"color: #64d200;\">(</span><span style=\"color: #00e261;\">ツ</span><span style=\"color: #00e7ff;\">)</span><span style=\"color: #00e1ff;\">_</span><span style=\"color: #00bdff;\">/</span><span style=\"color: #ff60ff;\">¯</span>"_s;
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
}
void TextHandlerTest::componentOutput_data()
{
QTest::addColumn<QString>("testInputString");
QTest::addColumn<QList<MessageComponent>>("testOutputComponents");
QTest::newRow("multiple paragraphs") << QStringLiteral("<p>Text</p>\n<p>Text</p>")
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}},
MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}}};
QTest::newRow("code") << QStringLiteral("<p>Text</p>\n<pre><code class=\"language-html\">Some code\n</code></pre>")
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}},
MessageComponent{MessageComponentType::Code,
QStringLiteral("Some code"),
QVariantMap{{QStringLiteral("class"), QStringLiteral("html")}}}};
QTest::newRow("quote") << QStringLiteral("<p>Text</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>")
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}},
MessageComponent{MessageComponentType::Quote, QStringLiteral("\"blockquote\""), {}}};
QTest::newRow("no tag first paragraph") << QStringLiteral("Text\n<p>Text</p>")
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}},
MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}}};
QTest::newRow("no tag last paragraph") << QStringLiteral("<p>Text</p>\nText")
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}},
MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}}};
QTest::newRow("inline code") << QStringLiteral("<p><code>https://kde.org</code></p>\n<p>Text</p>")
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, QStringLiteral("<code>https://kde.org</code>"), {}},
MessageComponent{MessageComponentType::Text, QStringLiteral("Text"), {}}};
QTest::newRow("inline code single block") << QStringLiteral("<code>https://kde.org</code>")
QTest::newRow("multiple paragraphs") << u"<p>Text</p>\n<p>Text</p>"_s
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, u"Text"_s, {}},
MessageComponent{MessageComponentType::Text, u"Text"_s, {}}};
QTest::newRow("code") << u"<p>Text</p>\n<pre><code class=\"language-html\">Some code\n</code></pre>"_s
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, u"Text"_s, {}},
MessageComponent{MessageComponentType::Code, u"Some code"_s, QVariantMap{{u"class"_s, u"html"_s}}}};
QTest::newRow("quote") << u"<p>Text</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>"_s
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, u"Text"_s, {}},
MessageComponent{MessageComponentType::Quote, u"“blockquote”"_s, {}}};
QTest::newRow("multiple paragraph quote") << u"<blockquote>\n<p>blockquote</p>\n<p>next paragraph</p>\n</blockquote>"_s
<< QList<MessageComponent>{
MessageComponent{MessageComponentType::Text, QStringLiteral("<code>https://kde.org</code>"), {}}};
MessageComponent{MessageComponentType::Quote, u"<p>“blockquote</p>\n<p>next paragraph”</p>"_s, {}}};
QTest::newRow("no tag first paragraph") << u"Text\n<p>Text</p>"_s
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, u"Text"_s, {}},
MessageComponent{MessageComponentType::Text, u"Text"_s, {}}};
QTest::newRow("no tag last paragraph") << u"<p>Text</p>\nText"_s
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, u"Text"_s, {}},
MessageComponent{MessageComponentType::Text, u"Text"_s, {}}};
QTest::newRow("inline code") << u"<p><code>https://kde.org</code></p>\n<p>Text</p>"_s
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, u"<code>https://kde.org</code>"_s, {}},
MessageComponent{MessageComponentType::Text, u"Text"_s, {}}};
QTest::newRow("inline code single block") << u"<code>https://kde.org</code>"_s
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, u"<code>https://kde.org</code>"_s, {}}};
QTest::newRow("long start tag")
<< QStringLiteral(
"Ah, you mean something like<br/><pre data-md=\"```\"><code class=\"language-qml\"># main.qml\nimport CustomQml\n...\nControls.TextField { id: "
"someField }\nCustomQml {\n someTextProperty: someField.text\n}\n</code></pre>Sure you can, it's still local to the same file where you "
"defined the id")
<< u"Ah, you mean something like<br/><pre data-md=\"```\"><code class=\"language-qml\"># main.qml\nimport CustomQml\n...\nControls.TextField { id: someField }\nCustomQml {\n someTextProperty: someField.text\n}\n</code></pre>Sure you can, it's still local to the same file where you defined the id"_s
<< QList<MessageComponent>{
MessageComponent{MessageComponentType::Text, QStringLiteral("Ah, you mean something like"), {}},
MessageComponent{MessageComponentType::Text, u"Ah, you mean something like<br/>"_s, {}},
MessageComponent{
MessageComponentType::Code,
QStringLiteral(
"# main.qml\nimport CustomQml\n...\nControls.TextField { id: someField }\nCustomQml {\n someTextProperty: someField.text\n}"),
QVariantMap{{QStringLiteral("class"), QStringLiteral("qml")}}},
MessageComponent{MessageComponentType::Text, QStringLiteral("Sure you can, it's still local to the same file where you defined the id"), {}}};
u"# main.qml\nimport CustomQml\n...\nControls.TextField { id: someField }\nCustomQml {\n someTextProperty: someField.text\n}"_s,
QVariantMap{{u"class"_s, u"qml"_s}}},
MessageComponent{MessageComponentType::Text, u"Sure you can, it's still local to the same file where you defined the id"_s, {}}};
}
void TextHandlerTest::componentOutput()
@@ -553,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

@@ -10,20 +10,20 @@
#include <Quotient/syncdata.h>
#include "enums/delegatetype.h"
#include "models/messageeventmodel.h"
#include "models/timelinemessagemodel.h"
#include "neochatroom.h"
#include "testutils.h"
using namespace Quotient;
class MessageEventModelTest : public QObject
class TimelineMessageModelTest : public QObject
{
Q_OBJECT
private:
Connection *connection = nullptr;
MessageEventModel *model = nullptr;
TimelineMessageModel *model = nullptr;
private Q_SLOTS:
void initTestCase();
@@ -40,70 +40,70 @@ private Q_SLOTS:
void cleanup();
};
void MessageEventModelTest::initTestCase()
void TimelineMessageModelTest::initTestCase()
{
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
}
void MessageEventModelTest::init()
void TimelineMessageModelTest::init()
{
QCOMPARE(model, nullptr);
model = new MessageEventModel;
model = new TimelineMessageModel;
}
// Make sure that basic empty rooms can be switched without crashing.
void MessageEventModelTest::switchEmptyRoom()
void TimelineMessageModelTest::switchEmptyRoom()
{
auto firstRoom = new TestUtils::TestRoom(connection, QStringLiteral("#firstRoom:kde.org"));
auto secondRoom = new TestUtils::TestRoom(connection, QStringLiteral("#secondRoom:kde.org"));
auto firstRoom = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s);
auto secondRoom = new TestUtils::TestRoom(connection, u"#secondRoom:kde.org"_s);
QSignalSpy spy(model, SIGNAL(roomChanged()));
QSignalSpy spy(model, SIGNAL(roomChanged(NeoChatRoom *, NeoChatRoom *)));
QCOMPARE(model->room(), nullptr);
model->setRoom(firstRoom);
QCOMPARE(spy.count(), 1);
QCOMPARE(model->room()->id(), QStringLiteral("#firstRoom:kde.org"));
QCOMPARE(model->room()->id(), u"#firstRoom:kde.org"_s);
model->setRoom(secondRoom);
QCOMPARE(spy.count(), 2);
QCOMPARE(model->room()->id(), QStringLiteral("#secondRoom:kde.org"));
QCOMPARE(model->room()->id(), u"#secondRoom:kde.org"_s);
model->setRoom(nullptr);
QCOMPARE(spy.count(), 3);
QCOMPARE(model->room(), nullptr);
}
// Make sure that rooms with some events can be switched without crashing
void MessageEventModelTest::switchSyncedRoom()
void TimelineMessageModelTest::switchSyncedRoom()
{
auto firstRoom = new TestUtils::TestRoom(connection, QStringLiteral("#firstRoom:kde.org"), QLatin1String("test-messageventmodel-sync.json"));
auto secondRoom = new TestUtils::TestRoom(connection, QStringLiteral("#secondRoom:kde.org"), QLatin1String("test-messageventmodel-sync.json"));
auto firstRoom = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s, u"test-messageventmodel-sync.json"_s);
auto secondRoom = new TestUtils::TestRoom(connection, u"#secondRoom:kde.org"_s, u"test-messageventmodel-sync.json"_s);
QSignalSpy spy(model, SIGNAL(roomChanged()));
QSignalSpy spy(model, SIGNAL(roomChanged(NeoChatRoom *, NeoChatRoom *)));
QCOMPARE(model->room(), nullptr);
model->setRoom(firstRoom);
QCOMPARE(spy.count(), 1);
QCOMPARE(model->room()->id(), QStringLiteral("#firstRoom:kde.org"));
QCOMPARE(model->room()->id(), u"#firstRoom:kde.org"_s);
model->setRoom(secondRoom);
QCOMPARE(spy.count(), 2);
QCOMPARE(model->room()->id(), QStringLiteral("#secondRoom:kde.org"));
QCOMPARE(model->room()->id(), u"#secondRoom:kde.org"_s);
model->setRoom(nullptr);
QCOMPARE(spy.count(), 3);
QCOMPARE(model->room(), nullptr);
}
void MessageEventModelTest::simpleTimeline()
void TimelineMessageModelTest::simpleTimeline()
{
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-messageventmodel-sync.json"));
auto room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-messageventmodel-sync.json"_s);
model->setRoom(room);
QCOMPARE(model->rowCount(), 2);
QCOMPARE(model->data(model->index(0), MessageEventModel::DelegateTypeRole), DelegateType::State);
QCOMPARE(model->data(model->index(0)), QStringLiteral("changed their display name to Example Changed"));
QCOMPARE(model->data(model->index(0), TimelineMessageModel::DelegateTypeRole), DelegateType::State);
QCOMPARE(model->data(model->index(0)), u"changed their display name to Example Changed"_s);
QCOMPARE(model->data(model->index(1)), QStringLiteral("<b>This is an example<br>text message</b>"));
QCOMPARE(model->data(model->index(1), MessageEventModel::DelegateTypeRole), DelegateType::Message);
QCOMPARE(model->data(model->index(1), MessageEventModel::EventIdRole), QStringLiteral("$153456789:example.org"));
QCOMPARE(model->data(model->index(1)), u"<b>This is an example<br>text message</b>"_s);
QCOMPARE(model->data(model->index(1), TimelineMessageModel::DelegateTypeRole), DelegateType::Message);
QCOMPARE(model->data(model->index(1), TimelineMessageModel::EventIdRole), u"$153456789:example.org"_s);
QTest::ignoreMessage(QtWarningMsg, "Index QModelIndex(-1,-1,0x0,QObject(0x0)) is not valid (expected valid)");
QCOMPARE(model->data(model->index(-1)), QVariant());
@@ -111,33 +111,37 @@ void MessageEventModelTest::simpleTimeline()
QCOMPARE(model->data(model->index(model->rowCount())), QVariant());
}
// Sync some events into the MessageEventModel's current room and don't crash.
void MessageEventModelTest::syncNewEvents()
// Sync some events into the TimelineMessageModel's current room and don't crash.
void TimelineMessageModelTest::syncNewEvents()
{
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"));
auto room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s);
QSignalSpy spy(room, SIGNAL(aboutToAddNewMessages(Quotient::RoomEventsRange)));
model->setRoom(room);
QCOMPARE(model->rowCount(), 0);
room->syncNewEvents(QLatin1String("test-messageventmodel-sync.json"));
room->syncNewEvents(u"test-messageventmodel-sync.json"_s);
QCOMPARE(model->rowCount(), 2);
QCOMPARE(spy.count(), 1);
}
// Check the adding of pending events to the room doesn't cause any issues in the model.
void MessageEventModelTest::pendingEvent()
void TimelineMessageModelTest::pendingEvent()
{
QSignalSpy spyInsert(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
QSignalSpy spyRemove(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)));
QSignalSpy spyChanged(model, SIGNAL(dataChanged(const QModelIndex, const QModelIndex, const QList<int> &)));
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"));
auto room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s);
model->setRoom(room);
QCOMPARE(model->rowCount(), 0);
auto txnId = room->postPlainText("New plain message"_ls);
#if Quotient_VERSION_MINOR > 9
auto txnId = room->postText("New plain message"_L1);
#else
auto txnId = room->postPlainText("New plain message"_L1);
#endif
QCOMPARE(model->rowCount(), 1);
QCOMPARE(spyInsert.count(), 1);
@@ -145,26 +149,30 @@ void MessageEventModelTest::pendingEvent()
QCOMPARE(model->rowCount(), 0);
QCOMPARE(spyRemove.count(), 1);
txnId = room->postPlainText("New plain message"_ls);
#if Quotient_VERSION_MINOR > 9
txnId = room->postText("New plain message"_L1);
#else
txnId = room->postPlainText("New plain message"_L1);
#endif
QCOMPARE(model->rowCount(), 1);
QCOMPARE(spyInsert.count(), 2);
// We need to manually set the transaction ID of the new message as it will be
// different every time.
QFile testSyncFile;
testSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + QLatin1String("test-pending-sync.json"));
testSyncFile.open(QIODevice::ReadOnly);
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + u"test-pending-sync.json"_s);
QVERIFY(testSyncFile.open(QIODevice::ReadOnly));
auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll());
auto root = testSyncJson.object();
auto timeline = root["timeline"_ls].toObject();
auto events = timeline["events"_ls].toArray();
auto timeline = root["timeline"_L1].toObject();
auto events = timeline["events"_L1].toArray();
auto firstEvent = events[0].toObject();
firstEvent.insert(QLatin1String("unsigned"), QJsonObject{{QLatin1String("transaction_id"), txnId}});
firstEvent.insert("unsigned"_L1, QJsonObject{{"transaction_id"_L1, txnId}});
events[0] = firstEvent;
timeline.insert("events"_ls, events);
root.insert("timeline"_ls, timeline);
timeline.insert("events"_L1, events);
root.insert("timeline"_L1, timeline);
testSyncJson.setObject(root);
SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, testSyncJson.object());
SyncRoomData roomData(u"@bob:kde.org"_s, JoinState::Join, testSyncJson.object());
room->update(std::move(roomData));
QCOMPARE(model->rowCount(), 1);
@@ -174,7 +182,7 @@ void MessageEventModelTest::pendingEvent()
auto isPendingChanged = false;
for (auto signal : spyChanged) {
auto roles = signal.at(2).toList();
if (roles.contains(MessageEventModel::IsPendingRole)) {
if (roles.contains(TimelineMessageModel::IsPendingRole)) {
isPendingChanged = true;
}
}
@@ -182,33 +190,33 @@ void MessageEventModelTest::pendingEvent()
}
// Make sure that the signals are disconnecting correctly when a room is switched.
void MessageEventModelTest::disconnect()
void TimelineMessageModelTest::disconnect()
{
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"));
auto room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s);
model->setRoom(room);
QSignalSpy spy(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)));
model->setRoom(nullptr);
room->syncNewEvents(QLatin1String("test-messageventmodel-sync.json"));
room->syncNewEvents(u"test-messageventmodel-sync.json"_s);
QCOMPARE(spy.count(), 0);
}
void MessageEventModelTest::idToRow()
void TimelineMessageModelTest::idToRow()
{
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-min-sync.json"));
auto room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-min-sync.json"_s);
model->setRoom(room);
QCOMPARE(model->eventIdToRow(QStringLiteral("$153456789:example.org")), 0);
QCOMPARE(model->indexForEventId(u"$153456789:example.org"_s).row(), 0);
}
void MessageEventModelTest::cleanup()
void TimelineMessageModelTest::cleanup()
{
delete model;
model = nullptr;
QCOMPARE(model, nullptr);
}
QTEST_MAIN(MessageEventModelTest)
#include "messageeventmodeltest.moc"
QTEST_MAIN(TimelineMessageModelTest)
#include "timelinemessagemodeltest.moc"

View File

@@ -17,7 +17,6 @@ class WindowControllerTest : public QObject
private Q_SLOTS:
void nullWindow();
void geometry();
void showAndRaise();
void toggle();
@@ -30,32 +29,10 @@ void WindowControllerTest::nullWindow()
auto &instance = WindowController::instance();
QCOMPARE(instance.window(), nullptr);
instance.restoreGeometry();
instance.saveGeometry();
instance.showAndRaiseWindow({});
instance.toggleWindow();
}
void WindowControllerTest::geometry()
{
auto &instance = WindowController::instance();
QWindow window;
window.setGeometry(0, 0, 200, 200);
instance.setWindow(&window);
QCOMPARE(instance.window(), &window);
instance.saveGeometry();
const auto stateConfig = KSharedConfig::openStateConfig();
KConfigGroup windowGroup = stateConfig->group(QStringLiteral("Window"));
QCOMPARE(KWindowConfig::hasSavedWindowSize(windowGroup), true);
window.setGeometry(0, 0, 400, 400);
QCOMPARE(window.geometry(), QRect(0, 0, 400, 400));
instance.restoreGeometry();
QCOMPARE(window.geometry(), QRect(0, 0, 200, 200));
}
void WindowControllerTest::showAndRaise()
{
auto &instance = WindowController::instance();
@@ -96,6 +73,16 @@ void WindowControllerTest::toggle()
instance.toggleWindow();
QCOMPARE(window.windowState(), Qt::WindowNoState);
QCOMPARE(window.isVisible(), false);
// make sure we restore maximized state when toggling
instance.toggleWindow();
window.setVisibility(QWindow::Maximized);
QCOMPARE(window.windowState(), Qt::WindowMaximized);
instance.toggleWindow();
QCOMPARE(window.isVisible(), false);
instance.toggleWindow();
QCOMPARE(window.windowState(), Qt::WindowMaximized);
QCOMPARE(window.isVisible(), true);
}
QTEST_MAIN(WindowControllerTest)

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

@@ -0,0 +1,36 @@
# SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
# SPDX-License-Identifier: BSD-2-Clause
add_definitions(-DDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}" )
qt_add_executable(timeline_memtest
main.cpp
)
target_link_libraries(timeline_memtest PRIVATE neochatplugin Timelineplugin)
target_link_libraries(timeline_memtest PUBLIC
Qt::Core
Qt::Quick
Qt::Qml
Qt::Gui
Qt::QuickControls2
Qt::Widgets
KF6::I18nQml
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
QML_FILES
Main.qml
SOURCES
memtesttimelinemodel.cpp
memtesttimelinemodel.h
DEPENDENCIES
QtCore
QtQuick
IMPORTS
org.kde.neochat.timeline
)

34
memorytests/Main.qml Normal file
View File

@@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtQuick.Controls as QQC2
import org.kde.kirigami as Kirigami
import org.kde.neochat
QQC2.ApplicationWindow {
id: root
title: "Timeline Memory Test"
minimumWidth: Kirigami.Units.gridUnit * 30
minimumHeight: Kirigami.Units.gridUnit * 30
visible: true
QQC2.ScrollView {
width: root.width
height: root.height
contentItem: ListView {
cacheBuffer: 1000000
model: messageFilterModel
delegate: EventDelegate {
room: memTestTimelineModel.room
}
}
}
}

34
memorytests/main.cpp Normal file
View File

@@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <KLocalizedQmlContext>
#include <KLocalizedString>
#include "memtesttimelinemodel.h"
#include "models/messagefiltermodel.h"
using namespace Qt::StringLiterals;
int main(int argc, char **argv)
{
QApplication app(argc, argv);
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
QQmlApplicationEngine engine;
KLocalization::setupLocalizedContext(&engine);
MemTestTimelineModel *memTestTimelineModel = new MemTestTimelineModel;
MessageFilterModel *messageFilterModel = new MessageFilterModel(nullptr, memTestTimelineModel);
engine.rootContext()->setContextProperty(u"memTestTimelineModel"_s, memTestTimelineModel);
engine.rootContext()->setContextProperty(u"messageFilterModel"_s, messageFilterModel);
engine.loadFromModule("org.kde.neochat.timeline_memtest", "Main");
return app.exec();
}

View File

@@ -0,0 +1,379 @@
{
"ephemeral": {
"events": [
{
"content": {
"$1000000000000:example.org": {
"m.read": {
"@alice:example.org": {
"ts": 1000000000000
}
}
}
},
"type": "m.receipt"
},
{
"content": {
"$1000000000000:example.org": {
"m.read": {
"@bob:example.org": {
"ts": 1000000000000
}
}
}
},
"type": "m.receipt"
},
{
"content": {
"$1000000000003:example.org": {
"m.read": {
"@tim:example.org": {
"ts": 1000000000000
}
}
}
},
"type": "m.receipt"
},
{
"content": {
"$1000000000003:example.org": {
"m.read": {
"@example:example.org": {
"ts": 1000000000000
}
}
}
},
"type": "m.receipt"
},
{
"content": {
"$1000000000003:example.org": {
"m.read": {
"@jeff:example.org": {
"ts": 1000000000000
}
}
}
},
"type": "m.receipt"
},
{
"content": {
"$1000000000003:example.org": {
"m.read": {
"@tina:example.org": {
"ts": 1000000000000
}
}
}
},
"type": "m.receipt"
},
{
"content": {
"$1000000000003:example.org": {
"m.read": {
"@sally:example.org": {
"ts": 1000000000000
}
}
}
},
"type": "m.receipt"
},
{
"content": {
"$1000000000003:example.org": {
"m.read": {
"@fred:example.org": {
"ts": 1000000000000
}
}
}
},
"type": "m.receipt"
}
]
},
"state": {
"events": [
{
"content": {
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Example",
"membership": "join"
},
"event_id": "$143273582555PhrSn:example.org",
"origin_server_ts": 1000000000000,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "example:example.org",
"state_key": "@example:example.org",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
},
{
"content": {
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Alice",
"membership": "join",
"reason": "Looking for support"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1000000000000,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "alice:example.org",
"state_key": "@alice:example.org",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
},
{
"content": {
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Bob",
"membership": "join"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1000000000000,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "bob:example.org",
"state_key": "@bob:example.org",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
},
{
"content": {
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Tim",
"membership": "join"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1000000000000,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "tim:example.org",
"state_key": "@tim:example.org",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
},
{
"content": {
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Jeff",
"membership": "join"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1000000000000,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "jeff:example.org",
"state_key": "@jeff:example.org",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
},
{
"content": {
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Tina",
"membership": "join"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1000000000000,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "tina:example.org",
"state_key": "@tina:example.org",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
},
{
"content": {
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Sally",
"membership": "join"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1000000000000,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "sally:example.org",
"state_key": "@sally:example.org",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
},
{
"content": {
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
"displayname": "Fred",
"membership": "join"
},
"event_id": "$143273582443PhrSn:example.org",
"origin_server_ts": 1000000000000,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "fred:example.org",
"state_key": "@fred:example.org",
"type": "m.room.member",
"unsigned": {
"age": 1234
}
}
]
},
"timeline": {
"events": [
{
"content": {
"body": "This is an example text message",
"format": "org.matrix.custom.html",
"formatted_body": "This is an example<br>text message",
"msgtype": "m.text"
},
"event_id": 0,
"origin_server_ts": 1000000000000,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1232
}
},
{
"content": {
"body": "This is a highlight @bob:example.org",
"msgtype": "m.text"
},
"event_id": 1,
"origin_server_ts": 1000000000001,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1233
}
},
{
"content": {
"m.relates_to": {
"event_id": 1,
"key": "👍",
"rel_type": "m.annotation"
}
},
"origin_server_ts": 1000000000002,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@alice:example.org",
"type": "m.reaction",
"unsigned": {
"age": 390159120
},
"event_id": 2,
"age": 390159120
},
{
"content": {
"body": "reply",
"format": "org.matrix.custom.html",
"formatted_body": "reply",
"m.relates_to": {
"m.in_reply_to": {
"event_id": 0
}
},
"msgtype": "m.text"
},
"origin_server_ts": 1000000000003,
"sender": "@alice:example.org",
"type": "m.room.message",
"unsigned": {
"age": 98
},
"event_id": 3,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org"
},
{
"age": 96845207,
"content": {
"body": "Lat: 51.7035, Lon: -1.14394",
"geo_uri": "geo:51.7035,-1.14394",
"msgtype": "m.location",
"org.matrix.msc1767.text": "Lat: 51.7035, Lon: -1.14394",
"org.matrix.msc3488.asset": {
"type": "m.pin"
},
"org.matrix.msc3488.location": {
"uri": "geo:51.7035,-1.14394"
}
},
"event_id": 4,
"origin_server_ts": 1000000000004,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 96845207
}
},
{
"content": {
"body": "```cpp\nint main(int argc, char **argv)\n{\n QApplication app(argc, argv);\n\n KLocalizedString::setApplicationDomain(QByteArrayLiteral(\"neochat\"));\n\n QQmlApplicationEngine engine;\n engine.loadFromModule(\"org.kde.neochat.timeline-memtest\", \"Main\");\n\n return app.exec();\n}\n```",
"format": "org.matrix.custom.html",
"formatted_body": "<pre><code class=\"language-cpp\">int main(int argc, char **argv)\n{\n QApplication app(argc, argv);\n\n KLocalizedString::setApplicationDomain(QByteArrayLiteral(&quot;neochat&quot;));\n\n QQmlApplicationEngine engine;\n engine.loadFromModule(&quot;org.kde.neochat.timeline-memtest&quot;, &quot;Main&quot;);\n\n return app.exec();\n}\n</code></pre>",
"msgtype": "m.text"
},
"event_id": 5,
"origin_server_ts": 1000000000005,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@bob:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1233
}
},
{
"content": {
"body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed fringilla risus, eget lacinia risus. Suspendisse at magna id justo sagittis suscipit. Maecenas eros quam, pulvinar a consequat sed, varius vitae risus. Cras congue est eget felis porttitor lobortis. Nam cursus, nulla ut finibus suscipit, tellus eros tincidunt ante, a volutpat velit lectus sit amet turpis. Morbi leo justo, fringilla sed rutrum a, suscipit a quam. Proin rhoncus neque eget ligula ullamcorper pellentesque. Mauris volutpat malesuada nunc. Nullam finibus enim eu nibh placerat imperdiet. Nullam in mi in diam luctus scelerisque dignissim non erat. ",
"msgtype": "m.text"
},
"event_id": 6,
"origin_server_ts": 1000000000006,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1232
}
},
{
"content": {
"body": "> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed fringilla risus, eget lacinia risus. Suspendisse at magna id justo sagittis suscipit. Maecenas eros quam, pulvinar a consequat sed, varius vitae risus. Cras congue est eget felis porttitor lobortis. Nam cursus, nulla ut finibus suscipit, tellus eros tincidunt ante, a volutpat velit lectus sit amet turpis. Morbi leo justo, fringilla sed rutrum a, suscipit a quam. Proin rhoncus neque eget ligula ullamcorper pellentesque. Mauris volutpat malesuada nunc. Nullam finibus enim eu nibh placerat imperdiet. Nullam in mi in diam luctus scelerisque dignissim non erat. ",
"format": "org.matrix.custom.html",
"formatted_body": "<blockquote>\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sed fringilla risus, eget lacinia risus. Suspendisse at magna id justo sagittis suscipit. Maecenas eros quam, pulvinar a consequat sed, varius vitae risus. Cras congue est eget felis porttitor lobortis. Nam cursus, nulla ut finibus suscipit, tellus eros tincidunt ante, a volutpat velit lectus sit amet turpis. Morbi leo justo, fringilla sed rutrum a, suscipit a quam. Proin rhoncus neque eget ligula ullamcorper pellentesque. Mauris volutpat malesuada nunc. Nullam finibus enim eu nibh placerat imperdiet. Nullam in mi in diam luctus scelerisque dignissim non erat.</p>\n</blockquote>",
"msgtype": "m.text"
},
"event_id": 7,
"origin_server_ts": 1000000000007,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1232
}
}
],
"limited": true,
"prev_batch": "t34-23535_0_0"
}
}

View File

@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "memtesttimelinemodel.h"
#include <Quotient/events/eventcontent.h>
#include <Quotient/events/roommessageevent.h>
using namespace Quotient;
MemTestTimelineModel::MemTestTimelineModel(QObject *parent)
: MessageModel(parent)
{
beginResetModel();
m_connection = Connection::makeMockConnection(u"@bob:example.org"_s);
m_room = new MemTestRoom(m_connection, u"#memtestroom:example.org"_s, u"memtest-sync.json"_s);
for (const auto &eventIt : m_room->messageEvents()) {
Q_EMIT newEventAdded(eventIt.event());
}
endResetModel();
}
std::optional<std::reference_wrapper<const RoomEvent>> MemTestTimelineModel::getEventForIndex(QModelIndex index) const
{
return *m_room->messageEvents().at(index.row()).event();
}
int MemTestTimelineModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_room->messageEvents().size();
}
#include "moc_memtesttimelinemodel.cpp"

View File

@@ -0,0 +1,121 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QAbstractListModel>
#include <QQmlEngine>
#include <Quotient/events/roomevent.h>
#include <Quotient/syncdata.h>
#include "models/messagemodel.h"
namespace Quotient
{
class Connection;
}
class NeoChatRoom;
class MemTestRoom : public NeoChatRoom
{
public:
MemTestRoom(Quotient::Connection *connection, const QString &roomName, const QString &syncFileName = {})
: NeoChatRoom(connection, roomName, Quotient::JoinState::Join)
{
syncNewEvents(syncFileName);
}
void update(Quotient::SyncRoomData &&data, bool fromCache = false)
{
Room::updateData(std::move(data), fromCache);
}
void syncNewEvents(const QString &syncFileName)
{
if (!syncFileName.isEmpty()) {
QFile testSyncFile;
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + syncFileName);
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);
testSyncJson["timeline"_L1] = timelineJson;
Quotient::SyncRoomData roomData(id(), Quotient::JoinState::Join, testSyncJson);
update(std::move(roomData));
}
}
QJsonArray multiplyEvents(QJsonArray events, int factor)
{
QJsonArray newArray;
int eventNum = 0;
int ts = 0;
for (int i = 0; i < factor; ++i) {
for (const auto &event : events) {
auto eventObject = event.toObject();
auto contentJson = eventObject["content"_L1].toObject();
if (contentJson.contains("m.relates_to"_L1)) {
auto relatesToJson = contentJson["m.relates_to"_L1].toObject();
if (relatesToJson.contains("m.in_reply_to"_L1)) {
auto replyJson = relatesToJson["m.in_reply_to"_L1].toObject();
const auto currentId = eventObject["event_id"_L1].toInt();
const auto currentReplyId = replyJson["event_id"_L1].toInt();
replyJson["event_id"_L1] = "$%1:example.org"_L1.arg(QString::number(eventNum - (currentId - currentReplyId)));
relatesToJson["m.in_reply_to"_L1] = replyJson;
} else if (relatesToJson.contains("event_id"_L1)) {
const auto currentId = eventObject["event_id"_L1].toInt();
const auto currentRelationId = relatesToJson["event_id"_L1].toInt();
relatesToJson["event_id"_L1] = "$%1:example.org"_L1.arg(QString::number(eventNum - (currentId - currentRelationId)));
}
contentJson["m.relates_to"_L1] = relatesToJson;
eventObject["content"_L1] = contentJson;
}
eventObject["event_id"_L1] = "$%1:example.org"_L1.arg(QString::number(eventNum));
eventObject["origin_server_ts"_L1] = ts;
auto unsignedJson = eventObject["unsigned"_L1].toObject();
unsignedJson["age"_L1] = ts;
eventObject["unsigned"_L1] = unsignedJson;
newArray.append(eventObject);
++eventNum;
++ts;
}
}
return newArray;
}
};
/**
* @class MemTestTimelineModel
*
* This is a special version of the MessageModel design to load an unchanging set
* of events from a json file so that timeline memory optimisations can be measured.
*/
class MemTestTimelineModel : public MessageModel
{
Q_OBJECT
QML_ELEMENT
public:
explicit MemTestTimelineModel(QObject *parent = nullptr);
/**
* @brief Number of rows in the model.
*
* @sa QAbstractItemModel::rowCount
*/
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
private:
QPointer<Quotient::Connection> m_connection;
std::vector<Quotient::RoomEventPtr> m_events;
std::optional<std::reference_wrapper<const Quotient::RoomEvent>> getEventForIndex(QModelIndex index) const override;
};

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,8 @@ Name[eu]=NeoChat
Name[fi]=NeoChat
Name[fr]=NeoChat
Name[gl]=NeoChat
Name[he]=NeoChat
Name[hi]=नियोचैट
Name[hu]=NeoChat
Name[ia]=Neochat
Name[id]=NeoChat
@@ -35,13 +37,13 @@ Name[pt]=NeoChat
Name[pt_BR]=NeoChat
Name[ro]=NeoChat
Name[ru]=NeoChat
Name[sa]=नवचैट्
Name[sk]=NeoChat
Name[sl]=NeoChat
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
@@ -59,6 +61,8 @@ GenericName[eu]=Matrix bezeroa
GenericName[fi]=Matrix-asiakas
GenericName[fr]=Client « Matrix »
GenericName[gl]=Cliente de Matrix
GenericName[he]=לקוח Matrix
GenericName[hi]=मैट्रिक्स क्लाइंट
GenericName[hu]=Matrix kliens
GenericName[ia]=Cliente de Matrice
GenericName[id]=Klien Matrix
@@ -76,55 +80,49 @@ GenericName[pt]=Cliente de Matrix
GenericName[pt_BR]=Cliente Matrix
GenericName[ro]=Client Matrix
GenericName[ru]=Клиент Matrix
GenericName[sa]=मैट्रिक्स क्लाइंट
GenericName[sk]=Matrix Client
GenericName[sl]=Odjemalec Matrix
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=Client for the Matrix protocol
Comment[ar]=عميل لميفاق ماتركس
Comment[az]=Matrix protokolu üçün müştəri
Comment[ca]=Client per al protocol Matrix
Comment[ca@valencia]=Client per al protocol Matrix
Comment[de]=Programm für das Matrix-Protokoll
Comment[el]=Πελάτης για το πρωτόκολλο Matrix
Comment[en_GB]=Client for the Matrix protocol
Comment[eo]=Kliento por la Matrix-protokolo
Comment[es]=Cliente para el protocolo Matrix
Comment[eu]=Matrix protokolorako bezeroa
Comment[fi]=Asiakas Matrix-yhteyskäytännölle
Comment[fr]=Client pour le protocole « Matrix »
Comment[gl]=Cliente para o protocolo Matrix.
Comment[hu]=Kliens a Matrix protokollhoz
Comment[ia]=Cliente per le protocollo de Matrix
Comment[id]=Klien untuk protokol Matrix
Comment[ie]=Un cliente del protocol Matrix
Comment[it]=Client per il protocollo Matrix
Comment[ka]=კლიენტი Matrix-ის პროტოკოლისთვის
Comment[ko]=Matrix 프로토콜용 클라이언트
Comment[lt]=Matrix protokolo kliento programa
Comment[lv]=Klients „Matrix“ protokolam
Comment[nl]=Client voor het Matrix-protocol
Comment[nn]=Klient for Matrix-protokollen
Comment[pa]=ਮੈਟਰਿਕਸ ਪਰੋਟੋਕਾਲ ਲਈ ਕਲਾਈਂਟ ਹੈ
Comment[pl]=Program obsługi protokołu Matriksa
Comment[pt]=Cliente para o protocolo Matrix
Comment[pt_BR]=Cliente para o protocolo Matrix
Comment[ro]=Client pentru protocolul Matrix
Comment[ru]=Клиент для протокола Matrix
Comment[sk]=Klient protokolu Matrix
Comment[sl]=Odjemalec za protokol Matrix
Comment[sv]=Klient för protokollet Matrix
Comment[ta]=Matrix நெறிமுறைக்கான வாங்கி
Comment[tr]=Matrix protokolü için istemci
Comment[uk]=Клієнт протоколу Matrix
Comment[x-test]=xxClient for the Matrix protocolxx
Comment[zh_CN]=为 Matrix 协议打造的客户端
Comment[zh_TW]=Matrix 通訊協定的用戶端
Comment=Chat on Matrix
Comment[ar]=دردش على ماتركس
Comment[ca]=Xat a Matrix
Comment[ca@valencia]=Xat a Matrix
Comment[de]=Über Matrix unterhalten
Comment[en_GB]=Chat on Matrix
Comment[eo]=Babilo en Matrix
Comment[es]=Chat en Matrix
Comment[eu]=Berriketa Matrix-en
Comment[fi]=Keskustele Matrixissä
Comment[fr]=Clavarder sur Matrix
Comment[gl]=Charle en Matrix
Comment[he]=התכתבות דרך Matrix
Comment[hi]=मैट्रिक्स पर चैट करें
Comment[hu]=Csevegés Matrixon
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
Comment[sv]=Chatta på Matrix
Comment[ta]=மேட்ரிக்ஸில் உரையாட உதவும்
Comment[tr]=Matrix üzerinde sohbet edin
Comment[uk]=Спілкування у Matrix
Comment[zh_CN]= Matrix 上聊天
Comment[zh_TW]= Matrix 上聊天
MimeType=x-scheme-handler/matrix;
Exec=neochat %u
Terminal=false

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

@@ -77,7 +77,7 @@ SPDX-License-Identifier: CC-BY-SA-4.0
></term>
<listitem>
<para
>L'URI de Matrix per a un usuari o una sala. P. ex. matrix:u/usuari:exemple.org o matrix:r/root:exemple.org. Això farà que el NeoChat intenti obrir la sala o conversa indicada. </para>
>L'URI de Matrix per a un usuari o una sala. P. ex. matrix:u/usuari:example.org o matrix:r/root:example.org. Això farà que el NeoChat intenti obrir la sala o conversa indicada. </para>
</listitem>
</varlistentry>
</variablelist>

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

7580
po/ga/neochat.po Normal file

File diff suppressed because it is too large Load Diff

8865
po/gl/neochat.po Normal file

File diff suppressed because it is too large Load Diff

8110
po/he/neochat.po Normal file

File diff suppressed because it is too large Load Diff

8781
po/hi/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

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