Compare commits

..

179 Commits

Author SHA1 Message Date
l10n daemon script
cedbbd6c56 GIT_SILENT Sync po/docbooks with svn 2025-03-07 03:19:05 +00:00
Joshua Goins
3215224528 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.



(cherry picked from commit d103de96aa)

Co-authored-by: Balló György <ballogyor@gmail.com>
2025-03-06 16:13:40 +00:00
Joshua Goins
2f6d7308c5 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


(cherry picked from commit f1253e4ede)

Co-authored-by: Joshua Goins <josh@redstrate.com>
2025-03-06 15:54:35 +00:00
l10n daemon script
6450f0dbe8 GIT_SILENT Sync po/docbooks with svn 2025-03-05 04:02:18 +00:00
l10n daemon script
3d7a964889 GIT_SILENT Sync po/docbooks with svn 2025-03-02 03:20:29 +00:00
l10n daemon script
2ab2570d94 GIT_SILENT Sync po/docbooks with svn 2025-03-01 03:27:11 +00:00
l10n daemon script
bf81e743f4 GIT_SILENT Sync po/docbooks with svn 2025-02-28 03:12:06 +00:00
Heiko Becker
a85af258fe GIT_SILENT Update Appstream for new release 2025-02-27 21:59:11 +01:00
Heiko Becker
063e7393ac GIT_SILENT Upgrade release service version to 24.12.3. 2025-02-27 20:52:47 +01:00
l10n daemon script
dafd02790f GIT_SILENT Sync po/docbooks with svn 2025-02-27 03:09:50 +00:00
Joshua Goins
29dd6b12ec 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.


(cherry picked from commit db36f187dc)

Co-authored-by: Joshua Goins <josh@redstrate.com>
2025-02-26 23:59:20 +00:00
l10n daemon script
9c7a49bf62 GIT_SILENT Sync po/docbooks with svn 2025-02-25 03:13:37 +00:00
l10n daemon script
e3cb592cd1 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-25 03:09:09 +00:00
Scarlett Moore
2dc54441ac snapcraft: Move to core24
(cherry picked from commit 20488ee400)
2025-02-21 15:45:16 -05:00
l10n daemon script
9259e7bb52 GIT_SILENT Sync po/docbooks with svn 2025-02-15 03:43:21 +00:00
l10n daemon script
9212cf3f45 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-14 03:21:19 +00:00
l10n daemon script
49a99058b7 GIT_SILENT Sync po/docbooks with svn 2025-02-13 03:24:32 +00:00
l10n daemon script
b7c9ec9bd2 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 03:19:25 +00:00
l10n daemon script
683d1f3487 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 03:04:26 +00:00
l10n daemon script
e7563870bf 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-08 03:05:46 +00:00
l10n daemon script
59a865e1d3 GIT_SILENT Sync po/docbooks with svn 2025-02-05 03:13:25 +00:00
Joshua Goins
75b6444c1e StateKeys: Fix opening a specific state key member
(cherry picked from commit 8d33fe6221)
2025-02-04 15:21:35 -05:00
l10n daemon script
180e927f82 GIT_SILENT Sync po/docbooks with svn 2025-02-04 03:12:08 +00:00
Joshua Goins
fcb32b1974 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.

(cherry picked from commit 268975bc3b)
2025-02-03 20:13:04 -05:00
Joshua Goins
bf9d1e5261 Only build 0.9.2 tag in Flatpak manifest 2025-02-03 19:46:28 -05:00
l10n daemon script
d6eb91c476 GIT_SILENT Sync po/docbooks with svn 2025-02-02 03:16:21 +00:00
Heiko Becker
96b03082e3 GIT_SILENT Update Appstream for new release 2025-01-31 01:43:53 +01:00
Heiko Becker
962063e58a GIT_SILENT Upgrade release service version to 24.12.2. 2025-01-31 00:36:56 +01:00
l10n daemon script
06a09700a0 GIT_SILENT Sync po/docbooks with svn 2025-01-30 03:11:16 +00:00
Gary Wang
12657abd03 CI: add frameworks/kiconthemes to .kde-ci.yaml
(cherry picked from commit 38a391b7fa)
2025-01-29 21:19:35 +00:00
Gary Wang
3752c3b872 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/

(cherry picked from commit 82434fe87c)
2025-01-29 21:19:35 +00:00
l10n daemon script
acf3f7030d GIT_SILENT Sync po/docbooks with svn 2025-01-27 03:21:03 +00:00
l10n daemon script
63ed48f283 GIT_SILENT Sync po/docbooks with svn 2025-01-20 03:22:19 +00:00
Joshua Goins
25f2693710 Make sure space drawer icons are available for android
(cherry picked from commit a37c9d6cea)

Co-authored-by: James Graham <james.h.graham@protonmail.com>
2025-01-18 22:29:42 +00:00
Carl Schwan
2efcc1041b add missing icons on Android
(cherry picked from commit 191cd7cbba)

Co-authored-by: Mark Penner <mrp@markpenner.space>
2025-01-18 20:19:55 +00:00
Joshua Goins
1f5823cec0 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


(cherry picked from commit 590fba7deb)

Co-authored-by: Joshua Goins <josh@redstrate.com>
2025-01-18 15:08:50 +00:00
l10n daemon script
efd18fa2d6 GIT_SILENT Sync po/docbooks with svn 2025-01-18 03:15:57 +00:00
l10n daemon script
98dc2cf41a 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-18 03:11:47 +00:00
l10n daemon script
97f27a1ae0 GIT_SILENT Sync po/docbooks with svn 2025-01-17 03:10:22 +00:00
Joshua Goins
46b9566242 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.


(cherry picked from commit d542033125)

Co-authored-by: Joshua Goins <josh@redstrate.com>
2025-01-16 20:49:07 +00:00
l10n daemon script
70ab0374ec GIT_SILENT Sync po/docbooks with svn 2025-01-16 03:10:33 +00:00
l10n daemon script
2b2e991bb8 GIT_SILENT Sync po/docbooks with svn 2025-01-14 03:14:34 +00:00
l10n daemon script
fbf4dfbe35 GIT_SILENT Sync po/docbooks with svn 2025-01-13 03:18:02 +00:00
Tobias Fella
1344e46201 Disable TextHandlerTest on CI
It's broken for no apparent reason while working fine locally

(cherry picked from commit d2e5c1d33b)
2025-01-11 17:27:29 +01:00
l10n daemon script
728d133b7c GIT_SILENT Sync po/docbooks with svn 2025-01-11 03:08:02 +00:00
Carl Schwan
a41be9e19b Improve handling of DonwloadAction
Set the progress the download action only when the currentProgressInfo
changed, otherwise we sometimes end up in a data race.

(cherry picked from commit 2a9c75e24f)
2025-01-10 11:05:51 +01:00
Carl Schwan
aa5ece8bfb Expose ProgressInfoRole also for other type of attachments 2025-01-10 11:05:25 +01:00
Carl Schwan
21f5ee74ba Fix right clicking on NeoChatMaximizedComponent
The api of RoomManager.viewEventMenu changed and now require also
passing the author.


(cherry picked from commit 7231662f94)

Co-authored-by: Carl Schwan <carl.schwan@gnupg.com>
2025-01-10 10:03:03 +00:00
Carl Schwan
2e6cf03c15 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.


(cherry picked from commit f14dfc5de8)

Co-authored-by: Carl Schwan <carl.schwan@gnupg.com>
2025-01-10 10:02:29 +00:00
l10n daemon script
9332910bcb GIT_SILENT Sync po/docbooks with svn 2025-01-06 03:17:39 +00:00
l10n daemon script
157c098af3 GIT_SILENT Sync po/docbooks with svn 2025-01-05 03:13:35 +00:00
l10n daemon script
9fe134e7f0 GIT_SILENT Sync po/docbooks with svn 2025-01-03 03:12:37 +00:00
l10n daemon script
46aaab3fb0 GIT_SILENT made messages (after extraction) 2025-01-03 02:32:10 +00:00
Heiko Becker
a5b37a78a0 GIT_SILENT Update Appstream for new release 2025-01-02 15:06:29 +01:00
Heiko Becker
59699abb94 GIT_SILENT Upgrade release service version to 24.12.1. 2025-01-02 13:56:27 +01:00
l10n daemon script
3cc0d89ee5 GIT_SILENT Sync po/docbooks with svn 2025-01-02 03:12:16 +00:00
l10n daemon script
96e83fc71b 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-02 03:06:21 +00:00
Carl Schwan
d89019d752 Use symbolic icon for purpose plugin
(cherry picked from commit 28c4c0b48c)

Co-authored-by: Carl Schwan <carl@carlschwan.eu>
2024-12-31 15:27:13 +00:00
l10n daemon script
51565dfdd2 GIT_SILENT Sync po/docbooks with svn 2024-12-30 03:20:50 +00:00
l10n daemon script
e1d09171d5 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 03:21:35 +00:00
l10n daemon script
f86572f880 GIT_SILENT Sync po/docbooks with svn 2024-12-27 03:13:18 +00:00
Joshua Goins
d7451834f3 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.


(cherry picked from commit a456b10420)

Co-authored-by: Joshua Goins <josh@redstrate.com>
2024-12-25 15:29:34 +00:00
l10n daemon script
a4767cea7d GIT_SILENT Sync po/docbooks with svn 2024-12-25 03:41:51 +00:00
Joshua Goins
4c43869fd4 Port away from methods removed in libquotient. This fixes BUG: 497458
(cherry picked from commit b7dee707a3)

Co-authored-by: Jan Rathmann <jan.rathmann@gmx.de>
2024-12-25 02:01:12 +00:00
l10n daemon script
e603664521 GIT_SILENT Sync po/docbooks with svn 2024-12-24 03:09:17 +00:00
Nicolas Fella
369242ab31 [UserInfo] Fix shortcut
Set the keysequence on the action directly

This makes it actually work

(cherry picked from commit 7bef8c99ec)
2024-12-23 12:27:26 +01:00
l10n daemon script
013773d465 GIT_SILENT Sync po/docbooks with svn 2024-12-22 03:13:29 +00:00
l10n daemon script
20b17a58d3 GIT_SILENT Sync po/docbooks with svn 2024-12-20 03:18:28 +00:00
l10n daemon script
1c4bb79347 GIT_SILENT made messages (after extraction) 2024-12-19 02:30:43 +00:00
l10n daemon script
6d2b49f3eb GIT_SILENT Sync po/docbooks with svn 2024-12-16 03:58:38 +00:00
Joshua Goins
e3d5867da6 snap: use cmake snap and use . as source
- override the `PATH`
- use gcc from toolchains PPA


(cherry picked from commit e2b0a105a7)

Co-authored-by: Soumyadeep Ghosh <soumyadeepghosh2004@zohomail.in>
2024-12-15 13:43:11 +00:00
Joshua Goins
a046e3ed27 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.

(cherry picked from commit 843da2664f)
2024-12-15 07:55:46 -05:00
Joshua Goins
5b935c1d33 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
(cherry picked from commit 23eaa6a4c7)
2024-12-15 07:55:46 -05:00
l10n daemon script
fe6bc5a36e GIT_SILENT Sync po/docbooks with svn 2024-12-14 04:02:23 +00:00
Volker Krause
c085be4f6e Update Android Gradle plugin version to 8.6.0
Necessary for building against Android SDK 35.

(cherry picked from commit 9b0d01619c)
2024-12-13 17:15:11 +01:00
l10n daemon script
1f73a9dc90 GIT_SILENT Sync po/docbooks with svn 2024-12-13 03:10:02 +00:00
Thiago Sueto
63206ef1dd 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)


(cherry picked from commit 29820e2ab2)

3cea53f5 Don't set emoji size to font size

Co-authored-by: Thiago Sueto <herzenschein@gmail.com>
2024-12-12 18:14:36 +00:00
Tobias Fella
0d286db0c2 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 17:16:10 +01:00
l10n daemon script
7d3f478a74 GIT_SILENT Sync po/docbooks with svn 2024-12-09 03:22:50 +00:00
l10n daemon script
6df2ebd1eb 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 03:17:34 +00:00
l10n daemon script
252fb6eb21 GIT_SILENT Sync po/docbooks with svn 2024-12-07 03:11:18 +00:00
l10n daemon script
5873092356 GIT_SILENT made messages (after extraction) 2024-12-07 02:32:10 +00:00
l10n daemon script
30822003d1 GIT_SILENT Sync po/docbooks with svn 2024-12-06 03:08:46 +00:00
l10n daemon script
52ae237eb7 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 03:05:39 +00:00
Joshua Goins
ee02abfe37 snap: Update libquotient 2024-12-04 20:04:56 +00:00
James Graham
f0de235f37 Fix removeConnection
Check m_accountsLoading and m_connectionsLoading separately for removal as when loadAccessTokenFromKeyChain() fails m_connectionsLoading won't have an entry for it


(cherry picked from commit 57e7004e05)

9aadd773 Check m_accountsLoading and m_connectionsLoading separately for removal as...

Co-authored-by: James Graham <james.h.graham@protonmail.com>
2024-12-04 17:01:38 +00:00
l10n daemon script
9e9fe6d275 GIT_SILENT Sync po/docbooks with svn 2024-12-04 03:10:16 +00:00
l10n daemon script
f4ca5f0f34 GIT_SILENT Sync po/docbooks with svn 2024-12-03 03:18:08 +00:00
l10n daemon script
5f240fa05c GIT_SILENT made messages (after extraction) 2024-12-03 02:33:48 +00:00
Heiko Becker
1e29eca59a GIT_SILENT Update Appstream for new release 2024-12-03 01:09:52 +01:00
Heiko Becker
1f71ec3bf8 GIT_SILENT Upgrade release service version to 24.12.0. 2024-12-03 00:09:15 +01:00
Carl Schwan
64c5ad88f6 Remove system information from device display name
BUG: 496901


(cherry picked from commit 9d887ba3e7)

Co-authored-by: Tobias Fella <fella@posteo.de>
2024-12-02 16:07:56 +00:00
l10n daemon script
fb5a3c1c5c GIT_SILENT Sync po/docbooks with svn 2024-12-02 04:15:51 +00:00
l10n daemon script
4a5a83f94a GIT_SILENT Sync po/docbooks with svn 2024-11-29 03:16:44 +00:00
l10n daemon script
133edc249f 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 03:11:59 +00:00
Albert Astals Cid
da30e66127 GIT_SILENT Upgrade release service version to 24.11.90. 2024-11-28 09:19:03 +01:00
l10n daemon script
4516e1e0f4 GIT_SILENT Sync po/docbooks with svn 2024-11-28 03:07:16 +00:00
l10n daemon script
bd80f65163 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-28 03:04:07 +00:00
l10n daemon script
f828ecf282 GIT_SILENT Sync po/docbooks with svn 2024-11-27 03:10:17 +00:00
l10n daemon script
a0483167c5 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 03:07:27 +00:00
l10n daemon script
87288f508c GIT_SILENT made messages (after extraction) 2024-11-27 02:32:20 +00:00
l10n daemon script
dc184ed2fd GIT_SILENT Sync po/docbooks with svn 2024-11-26 03:08:20 +00:00
l10n daemon script
49e1bf9ab1 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 03:05:24 +00:00
l10n daemon script
74acf3f9dc GIT_SILENT made messages (after extraction) 2024-11-24 02:34:26 +00:00
l10n daemon script
38205d2791 GIT_SILENT Sync po/docbooks with svn 2024-11-23 03:05:59 +00:00
l10n daemon script
81da926d4f 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-23 03:02:07 +00:00
Carl Schwan
1018fe5d3f Remove layout attached properties
They don't do anything

(cherry picked from commit fbb2afdb49)
2024-11-22 09:05:04 -05:00
Thiago Sueto
58b32dd50f 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)|

(cherry picked from commit 240cf6a0ed)
2024-11-22 09:05:04 -05:00
Tobias Fella
82184b895a Escape display name in WelcomePage
(cherry picked from commit dcd9ee93de)
2024-11-22 09:05:04 -05:00
Joshua Goins
da0f6f78a4 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"

(cherry picked from commit 2a8cd74ab1)
2024-11-22 09:05:03 -05:00
Joshua Goins
cfd06d064c 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.

(cherry picked from commit 63bc7055c2)
2024-11-22 09:05:03 -05:00
Joshua Goins
a90e9ae92a 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.

(cherry picked from commit 1cca9733d6)
2024-11-22 09:05:03 -05:00
Joshua Goins
8ab0002057 TextHandler: Use the fancy Unicode quotation characters
As per our HIG, we should use these in quotations instead of the normal
quote characters.

(cherry picked from commit 1104da5e2c)
2024-11-22 09:05:03 -05:00
Joshua Goins
e1840be234 Limit the width of a user's QR code
This fixes the lopsided layout in the user details dialog.

(cherry picked from commit 3a9718c09d)
2024-11-22 09:05:02 -05:00
Joshua Goins
d6ecaaa344 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.

(cherry picked from commit 55362c5573)
2024-11-22 09:05:02 -05:00
Joshua Goins
0c08c2ab89 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.

(cherry picked from commit 0bba2299b3)
2024-11-22 09:05:02 -05:00
Joshua Goins
39ff11e059 Add icons to the recommended space actions, fix spacing of the items
(cherry picked from commit 45685af9e9)
2024-11-22 09:05:01 -05:00
Joshua Goins
93254431c5 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.

(cherry picked from commit 6c416a9338)
2024-11-22 09:05:01 -05:00
Joshua Goins
fc14a8eac8 Ensure it's not possible for the recommended space avatar to assert
(cherry picked from commit 1b0027e1d2)
2024-11-22 09:05:01 -05:00
Joshua Goins
50759bb3ca Fix avatars not loading in the room completion model
(cherry picked from commit 2409adf516)
2024-11-22 09:05:01 -05:00
Joshua Goins
23134d8e72 Make sure RoomInformation's source is type url
(cherry picked from commit 554801dfe4)
2024-11-22 09:05:01 -05:00
Joshua Goins
7cd095f76a Remove now unused NeoChatRoomMember::avatarMediaId()
(cherry picked from commit 20c23917e9)
2024-11-22 09:05:01 -05:00
Joshua Goins
d5c3054da4 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.

(cherry picked from commit ef953b7574)
2024-11-22 09:05:00 -05:00
Joshua Goins
ae12c838bd 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

(cherry picked from commit 6b79795229)
2024-11-22 09:05:00 -05:00
Joshua Goins
51727dd345 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.

(cherry picked from commit a39194b2ad)

Co-authored-by: Joshua Goins <josh@redstrate.com>
2024-11-22 13:45:04 +00:00
l10n daemon script
5611b000fb GIT_SILENT Sync po/docbooks with svn 2024-11-20 03:11:39 +00:00
l10n daemon script
461896e228 GIT_SILENT Sync po/docbooks with svn 2024-11-19 03:09:14 +00:00
Carl Schwan
e388536a03 Make sure the loading text for a new login wraps
Title

BUG: 493869


(cherry picked from commit aff0402f71)

58b69cf1 Make sure the loading text for a new login wraps

Co-authored-by: James Graham <james.h.graham@protonmail.com>
2024-11-18 08:42:36 +00:00
Carl Schwan
61f22edd86 Make sure that for multiple sed edits we grab the eventID of the original message not the replacement
(cherry picked from commit 24e1a0a596)

Co-authored-by: James Graham <james.h.graham@protonmail.com>
2024-11-18 08:35:24 +00:00
l10n daemon script
9e368691d6 GIT_SILENT Sync po/docbooks with svn 2024-11-18 03:18:50 +00:00
l10n daemon script
dad2b3ec8f 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 03:14:09 +00:00
Joshua Goins
8821c37ff8 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.

(cherry picked from commit 437c981d30)
2024-11-16 13:54:46 -05:00
Thiago Sueto
c105170eca 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.

(cherry picked from commit 396cc8e8ef)
2024-11-16 13:41:58 -05:00
Joshua Goins
b07c04eddc Change the room alias text color to disabled
It's less important than the title, and this should reduce it's visual
prominence.

(cherry picked from commit 0334cae4c8)
2024-11-16 13:30:20 -05:00
Joshua Goins
7aa0f68b10 DelegateContextMenu: Add support for separators in the mobile menu too
(cherry picked from commit a3f5962809)
2024-11-16 13:30:20 -05:00
Joshua Goins
cbdae4c312 DelegateContextMenu: Add support for separator actions
(cherry picked from commit d34f89fc4b)
2024-11-16 13:30:20 -05:00
Joshua Goins
9210940556 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.

(cherry picked from commit a909ed498f)
2024-11-16 13:30:19 -05:00
Joshua Goins
d8489527b4 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.

(cherry picked from commit 16f4e17e8f)
2024-11-16 13:30:19 -05:00
Joshua Goins
64c9cd97de Settings: Use symbolic version of the NeoChat icon
To match the rest of the icons in this sidebar, we can reuse our tray
icon.

(cherry picked from commit 0e9592a96c)
2024-11-16 13:30:19 -05:00
Joshua Goins
119a9890b1 Add placeholder icon when there's no emojis or stickers
(cherry picked from commit 704ee6a53a)
2024-11-16 13:30:19 -05:00
Joshua Goins
69be6b5939 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.

(cherry picked from commit 5b9afbce9a)
2024-11-16 13:30:19 -05:00
Joshua Goins
112152f2df Port from deprecated AboutKDE component to AboutKDEPage
(cherry picked from commit f802dbe686)
2024-11-16 13:30:18 -05:00
Joshua Goins
2ef634a6cb 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.

(cherry picked from commit 2379e3d83b)
2024-11-16 13:30:18 -05:00
Joshua Goins
fd31b4fb74 Add margins to the room drawer header to match Kirigami
Otherwise it sticks to the left of the drawer and looks kinda ugly.

(cherry picked from commit 9e90ac0412)
2024-11-16 13:30:18 -05:00
Joshua Goins
3b12520fa2 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.

(cherry picked from commit 31ef0a5223)
2024-11-16 13:30:18 -05:00
Joshua Goins
24718a5f72 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} |

(cherry picked from commit 14c58acea1)
2024-11-16 13:30:18 -05:00
Joshua Goins
db62bacc7e Port from Kirigami Add-ons Banner to Kirigami InlineMessage
(cherry picked from commit 3f6fa94289)
2024-11-16 13:30:18 -05:00
l10n daemon script
6500669b67 GIT_SILENT Sync po/docbooks with svn 2024-11-15 03:10:56 +00:00
l10n daemon script
557d151ed4 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-15 03:06:59 +00:00
l10n daemon script
95ffd485b4 GIT_SILENT made messages (after extraction) 2024-11-15 02:31:39 +00:00
l10n daemon script
ebd38fb435 GIT_SILENT Sync po/docbooks with svn 2024-11-14 03:10:10 +00:00
l10n daemon script
a5b999e682 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 03:06:40 +00:00
l10n daemon script
41d34fc0e4 GIT_SILENT Sync po/docbooks with svn 2024-11-13 03:08:49 +00:00
l10n daemon script
b51194f90f 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 03:05:22 +00:00
l10n daemon script
80ac9e1ba7 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 03:11:52 +00:00
l10n daemon script
e3874c824a GIT_SILENT Sync po/docbooks with svn 2024-11-11 03:23:01 +00:00
l10n daemon script
6599c6b609 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 03:15:47 +00:00
l10n daemon script
13d522221c GIT_SILENT made messages (after extraction) 2024-11-11 02:40:16 +00:00
Joshua Goins
dd8f926f32 PollHandler: Make sure it's not constructible from QML
(cherry picked from commit d6b780762e)
2024-11-10 10:29:26 -05:00
Joshua Goins
258312e798 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
(cherry picked from commit 5ef66b5cf6)
2024-11-10 10:29:17 -05:00
Joshua Goins
43d40c7e75 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.

(cherry picked from commit 85ee5084b6)
2024-11-10 07:40:41 -05:00
Joshua Goins
cbcc9a6514 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.

(cherry picked from commit bb9ce117de)
2024-11-10 07:40:41 -05:00
l10n daemon script
625048610b GIT_SILENT Sync po/docbooks with svn 2024-11-10 03:30:48 +00:00
l10n daemon script
fa47b67e3d 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 03:16:42 +00:00
Carl Schwan
9347a66acf RoomGeneralPage: Add missing separator
And some other minor fixes

(cherry picked from commit 00c5aa26bb)
2024-11-09 18:12:25 -05:00
Joshua Goins
317df56ffa Make closing link previews instant, as it should be
We were missing a endResetModel() call, now with it added the removal
happens instantly.

(cherry picked from commit bae4de227c)
2024-11-09 18:12:25 -05:00
Joshua Goins
fed9197716 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.

(cherry picked from commit 253f891c5a)
2024-11-09 18:12:25 -05:00
Joshua Goins
1e892599e9 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.

(cherry picked from commit 6966159062)
2024-11-09 18:12:25 -05:00
Joshua Goins
b7229ca0cf 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?

(cherry picked from commit 07d3b80c3e)
2024-11-09 18:12:25 -05:00
Joshua Goins
953b711823 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
(cherry picked from commit a41d0f3214)
2024-11-09 18:12:25 -05:00
Joshua Goins
01d903efd3 Fix viewing any kind of data in developer tools
Fix pageStack being undefined, so we're able to view event data again.

(cherry picked from commit 1ee15de78b)
2024-11-09 18:12:25 -05:00
Carl Schwan
241dd81932 Update checkbox of PollComponent
Use FormCheckDelegate instead of a CheckBox inside a RowLayout. This
increase the click area particularly on mobile.

(cherry picked from commit b044358970)
2024-11-09 18:12:24 -05:00
Oliver Beard
f6dfe0cbcf timeline: Round separators for replies and link previews
(cherry picked from commit d2e11bb3bb)
2024-11-09 18:12:24 -05:00
Joshua Goins
f10b97139c 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.

(cherry picked from commit a55bac899c)
2024-11-09 18:12:24 -05:00
Joshua Goins
385c5b3405 Update network proxy page with the improved version from Tokodon
This functions the same, but looks a bit nicer.

(cherry picked from commit c2380fb8df)
2024-11-09 16:28:47 -05:00
Joshua Goins
7bc6f906f8 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.

(cherry picked from commit f31c644b13)
2024-11-09 16:28:47 -05:00
Joshua Goins
b8b1434a95 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.

(cherry picked from commit 26cd621d0e)
2024-11-09 16:28:47 -05:00
l10n daemon script
85c7a4bcb3 GIT_SILENT Sync po/docbooks with svn 2024-11-09 03:10:22 +00:00
Albert Astals Cid
84b698a7e8 GIT_SILENT Upgrade release service version to 24.11.80. 2024-11-08 19:06:28 +01:00
282 changed files with 40330 additions and 83106 deletions

View File

@@ -2,7 +2,5 @@
; SPDX-License-Identifier: CC0-1.0
[BlueprintSettings]
kde/frameworks/extra-cmake-modules.version=master
kde/unreleased/kirigami-addons.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.8",
"runtime-version": "6.7",
"sdk": "org.kde.Sdk",
"command": "neochat",
"tags": [
@@ -25,23 +25,13 @@
"modules": [
{
"name": "kirigamiaddons",
"config-opts": [
"-DBUILD_TESTING=OFF"
],
"config-opts": [ "-DBUILD_TESTING=OFF" ],
"buildsystem": "cmake-ninja",
"sources": [
{
"type": "git",
"url": "https://invent.kde.org/libraries/kirigami-addons.git"
}
]
"sources": [ { "type": "git", "url": "https://invent.kde.org/libraries/kirigami-addons.git", "commit": "34d311219e8b7209746a98b3a29b91ded05ff936" } ]
},
{
"name": "kquickimageeditor",
"config-opts": [
"-DBUILD_WITH_QT6=ON",
"-DBUILD_TESTING=OFF"
],
"config-opts": [ "-DBUILD_WITH_QT6=ON" ],
"buildsystem": "cmake-ninja",
"sources": [
{
@@ -53,19 +43,17 @@
{
"name": "olm",
"buildsystem": "cmake-ninja",
"config-opts": [
"-DOLM_TESTS=OFF"
],
"config-opts": [ "-DOLM_TESTS=OFF" ],
"sources": [
{
"type": "git",
"url": "https://gitlab.matrix.org/matrix-org/olm.git",
"tag": "3.2.16",
"tag": "3.2.10",
"x-checker-data": {
"type": "git",
"tag-pattern": "^([\\d.]+)$"
},
"commit": "7e0c8277032e40308987257b711b38af8d77cc69"
"commit": "9908862979147a71dc6abaecd521be526ae77be1"
}
]
},
@@ -77,13 +65,13 @@
"-Dvapi=false",
"-Dgtk_doc=false",
"-Dintrospection=false",
"-Dcrypto=disabled"
"-Dgcrypt=false"
],
"sources": [
{
"type": "archive",
"url": "https://download.gnome.org/sources/libsecret/0.21/libsecret-0.21.6.tar.xz",
"sha256": "747b8c175be108c880d3adfb9c3537ea66e520e4ad2dccf5dce58003aeeca090",
"url": "https://download.gnome.org/sources/libsecret/0.20/libsecret-0.20.5.tar.xz",
"sha256": "3fb3ce340fcd7db54d87c893e69bfc2b1f6e4d4b279065ffe66dac9f0fd12b4d",
"x-checker-data": {
"type": "gnome",
"name": "libsecret",
@@ -98,13 +86,13 @@
"sources": [
{
"type": "archive",
"url": "https://github.com/frankosterfeld/qtkeychain/archive/refs/tags/0.15.0.tar.gz",
"sha256": "f4254dc8f0933b06d90672d683eab08ef770acd8336e44dfa030ce041dc2ca22",
"url": "https://github.com/frankosterfeld/qtkeychain/archive/0.14.2.tar.gz",
"sha256": "cf2e972b783ba66334a79a30f6b3a1ea794a1dc574d6c3bebae5ffd2f0399571",
"x-checker-data": {
"type": "anitya",
"project-id": 4138,
"stable-only": true,
"url-template": "https://github.com/frankosterfeld/qtkeychain/archive/refs/tags/$version.tar.gz"
"url-template": "https://github.com/frankosterfeld/qtkeychain/archive/v$version.tar.gz"
}
}
],
@@ -112,8 +100,7 @@
"-DBUILD_WITH_QT6=ON",
"-DCMAKE_INSTALL_LIBDIR=/app/lib",
"-DLIB_INSTALL_DIR=/app/lib",
"-DBUILD_TRANSLATIONS=NO",
"-DBUILD_TESTING=OFF"
"-DBUILD_TRANSLATIONS=NO"
]
},
{
@@ -123,7 +110,7 @@
{
"type": "git",
"url": "https://github.com/quotient-im/libQuotient.git",
"branch": "dev",
"tag": "0.9.2",
"disable-submodules": true
}
],
@@ -136,31 +123,29 @@
{
"name": "cmark",
"buildsystem": "cmake-ninja",
"config-opts": [
"-DCMARK_TESTS=OFF",
"-DCMAKE_BUILD_TYPE=Release",
"-DCMAKE_INSTALL_PREFIX=/app"
],
"config-opts": [ "-DCMARK_TESTS=OFF" ],
"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",
"buildsystem": "cmake-ninja",
"config-opts": [
"-DQCORO_BUILD_EXAMPLES=OFF",
"-DBUILD_TESTING=OFF"
],
"config-opts": [ "-DQCORO_BUILD_EXAMPLES=OFF", "-DBUILD_TESTING=OFF" ],
"sources": [
{
"type": "archive",
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.11.0.tar.gz",
"sha256": "9942c5b4c533192f6c5954dc6d10178b3829075e6a621b67df73f0a4b74d8297",
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.7.0.tar.gz",
"sha256": "23ef0217926e67c8d2eb861cf91617da2f7d8d5a9ae6c62321b21448b1669210",
"x-checker-data": {
"type": "anitya",
"project-id": 236236,
@@ -173,15 +158,14 @@
{
"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,14 +5,10 @@ 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

View File

@@ -2,43 +2,43 @@
# 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'
'frameworks/kiconthemes': '@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/kitemmodels': '@latest-kf6'
'frameworks/kquickcharts': '@latest-kf6'
'frameworks/knotifications': '@latest-kf6'
'frameworks/kcolorscheme': '@latest-kf6'
'frameworks/kiconthemes': '@latest-kf6'
'libraries/kquickimageeditor': '@latest-kf6'
'frameworks/sonnet': '@latest-kf6'
'frameworks/prison': '@latest-kf6'
'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']
'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: ['Linux', 'Android', 'FreeBSD', 'Windows']
require-passing-tests-on: [ 'Linux', 'Android', 'FreeBSD' ]

55
.reuse/dep5 Normal file
View File

@@ -0,0 +1,55 @@
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,9 +7,9 @@
cmake_minimum_required(VERSION 3.16)
# KDE Applications version, managed by release script.
set(RELEASE_SERVICE_VERSION_MAJOR "25")
set(RELEASE_SERVICE_VERSION_MINOR "04")
set(RELEASE_SERVICE_VERSION_MICRO "1")
set(RELEASE_SERVICE_VERSION_MAJOR "24")
set(RELEASE_SERVICE_VERSION_MINOR "12")
set(RELEASE_SERVICE_VERSION_MICRO "3")
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
@@ -174,9 +174,6 @@ if (BUILD_TESTING)
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test)
add_subdirectory(autotests)
add_subdirectory(appiumtests)
if (NOT ANDROID)
add_subdirectory(memorytests)
endif()
endif()
if(KF6DocTools_FOUND)

View File

@@ -1,90 +0,0 @@
# 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/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/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"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -83,15 +83,6 @@ def create_room():
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__":

View File

@@ -11,13 +11,11 @@ ecm_add_test(
TEST_NAME neochatroomtest
)
if (NOT $ENV{KDE_CI})
ecm_add_test(
texthandlertest.cpp
LINK_LIBRARIES neochat Qt::Test
TEST_NAME texthandlertest
)
endif()
# ecm_add_test(
# texthandlertest.cpp
# LINK_LIBRARIES neochat Qt::Test
# TEST_NAME texthandlertest
# )
ecm_add_test(
delegatesizehelpertest.cpp
@@ -50,9 +48,9 @@ ecm_add_test(
)
ecm_add_test(
timelinemessagemodeltest.cpp
messageeventmodeltest.cpp
LINK_LIBRARIES neochat Qt::Test
TEST_NAME timelinemessagemodeltest
TEST_NAME messageeventmodeltest
)
ecm_add_test(
@@ -84,9 +82,3 @@ ecm_add_test(
LINK_LIBRARIES neochat Qt::Test
TEST_NAME messagecontentmodeltest
)
ecm_add_test(
actionstest.cpp
LINK_LIBRARIES neochat Qt::Test
TEST_NAME actionstest
)

View File

@@ -1,120 +0,0 @@
// 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 "chatbarcache.h"
#include "models/actionsmodel.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;
TestUtils::TestRoom *room = nullptr;
void expectMessage(const QString &actionName, const QString &args, MessageType::Type type, const QString &message);
private Q_SLOTS:
void initTestCase();
void testActions();
void testActions_data();
void testInvite();
};
void ActionsTest::initTestCase()
{
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-min-sync.json"));
}
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();
cache->setText(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, &TestUtils::TestRoom::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.org"_s, MessageType::Information, u"@example:example.org is already in this room."_s);
QCOMPARE(room->memberState(u"@example:example.org"_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);
//TODO mock server, wait for invite state to change
//TODO QCOMPARE(room->memberState(u"@user:example.com"_s), Membership::Invite);
}
QTEST_MAIN(ActionsTest)
#include "actionstest.moc"

View File

@@ -38,8 +38,8 @@ private Q_SLOTS:
void ChatBarCacheTest::initTestCase()
{
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, "test-min-sync.json"_L1);
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-min-sync.json"));
}
void ChatBarCacheTest::empty()
@@ -59,7 +59,7 @@ void ChatBarCacheTest::empty()
void ChatBarCacheTest::noRoom()
{
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache());
chatBarCache->setReplyId(u"$153456789:example.org"_s);
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
// These should return empty even though a reply ID has been set because the
// ChatBarCache has no parent.
@@ -75,7 +75,7 @@ void ChatBarCacheTest::badParent()
{
QScopedPointer<QObject> badParent(new QObject());
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(badParent.get()));
chatBarCache->setReplyId(u"$153456789:example.org"_s);
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
// These should return empty even though a reply ID has been set because the
// ChatBarCache has no parent.
@@ -90,17 +90,17 @@ void ChatBarCacheTest::badParent()
void ChatBarCacheTest::reply()
{
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
chatBarCache->setText(u"some text"_s);
chatBarCache->setAttachmentPath(u"some/path"_s);
chatBarCache->setReplyId(u"$153456789:example.org"_s);
chatBarCache->setText(QLatin1String("some text"));
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
chatBarCache->setReplyId(QLatin1String("$153456789:example.org"));
QCOMPARE(chatBarCache->text(), u"some text"_s);
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
QCOMPARE(chatBarCache->isReplying(), true);
QCOMPARE(chatBarCache->replyId(), u"$153456789:example.org"_s);
QCOMPARE(chatBarCache->replyId(), QLatin1String("$153456789:example.org"));
QCOMPARE(chatBarCache->isEditing(), false);
QCOMPARE(chatBarCache->editId(), QString());
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@example:example.org"_s));
QCOMPARE(chatBarCache->relationMessage(), u"This is an example\ntext message"_s);
QCOMPARE(chatBarCache->relationAuthor(), room->member(QLatin1String("@example:example.org")));
QCOMPARE(chatBarCache->relationMessage(), QLatin1String("This is an example\ntext message"));
QCOMPARE(chatBarCache->attachmentPath(), QString());
}
@@ -108,39 +108,39 @@ void ChatBarCacheTest::edit()
{
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
chatBarCache->setText(u"some text"_s);
chatBarCache->setAttachmentPath(u"some/path"_s);
chatBarCache->setText(QLatin1String("some text"));
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
connect(chatBarCache.get(), &ChatBarCache::relationIdChanged, this, [](const QString &oldEventId, const QString &newEventId) {
QCOMPARE(oldEventId, QString());
QCOMPARE(newEventId, QString(u"$153456789:example.org"_s));
QCOMPARE(newEventId, QString(QLatin1String("$153456789:example.org")));
});
chatBarCache->setEditId(u"$153456789:example.org"_s);
chatBarCache->setEditId(QLatin1String("$153456789:example.org"));
QCOMPARE(chatBarCache->text(), u"some text"_s);
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
QCOMPARE(chatBarCache->isReplying(), false);
QCOMPARE(chatBarCache->replyId(), QString());
QCOMPARE(chatBarCache->isEditing(), true);
QCOMPARE(chatBarCache->editId(), u"$153456789:example.org"_s);
QCOMPARE(chatBarCache->relationAuthor(), room->member(u"@example:example.org"_s));
QCOMPARE(chatBarCache->relationMessage(), u"This is an example\ntext message"_s);
QCOMPARE(chatBarCache->editId(), QLatin1String("$153456789:example.org"));
QCOMPARE(chatBarCache->relationAuthor(), room->member(QLatin1String("@example:example.org")));
QCOMPARE(chatBarCache->relationMessage(), QLatin1String("This is an example\ntext message"));
QCOMPARE(chatBarCache->attachmentPath(), QString());
}
void ChatBarCacheTest::attachment()
{
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
chatBarCache->setText(u"some text"_s);
chatBarCache->setEditId(u"$153456789:example.org"_s);
chatBarCache->setAttachmentPath(u"some/path"_s);
chatBarCache->setText(QLatin1String("some text"));
chatBarCache->setEditId(QLatin1String("$153456789:example.org"));
chatBarCache->setAttachmentPath(QLatin1String("some/path"));
QCOMPARE(chatBarCache->text(), u"some text"_s);
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
QCOMPARE(chatBarCache->isReplying(), false);
QCOMPARE(chatBarCache->replyId(), QString());
QCOMPARE(chatBarCache->isEditing(), false);
QCOMPARE(chatBarCache->editId(), QString());
QCOMPARE(chatBarCache->relationAuthor(), room->member(QString()));
QCOMPARE(chatBarCache->relationMessage(), QString());
QCOMPARE(chatBarCache->attachmentPath(), u"some/path"_s);
QCOMPARE(chatBarCache->attachmentPath(), QLatin1String("some/path"));
}
QTEST_MAIN(ChatBarCacheTest)

View File

@@ -49,51 +49,6 @@
"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
}
}
]
},
@@ -102,7 +57,7 @@
"@alice:example.com",
"@bob:example.com"
],
"m.invited_member_count": 1,
"m.invited_member_count": 0,
"m.joined_member_count": 2
},
"timeline": {

View File

@@ -11,8 +11,6 @@
#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"
@@ -34,6 +32,8 @@ private:
private Q_SLOTS:
void initTestCase();
void eventId();
void nullEventId();
void authorDisplayName();
void nullAuthorDisplayName();
void singleLineSidplayName();
@@ -56,21 +56,38 @@ private Q_SLOTS:
void nullSubtitle();
void mediaInfo();
void nullMediaInfo();
void hasReply();
void nullHasReply();
void replyId();
void nullReplyId();
void replyAuthor();
void nullReplyAuthor();
void thread();
void nullThread();
void location();
void nullLocation();
};
void EventHandlerTest::initTestCase()
{
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-eventhandler-sync.json"_s);
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-eventhandler-sync.json"));
}
void EventHandlerTest::eventId()
{
QCOMPARE(EventHandler::id(room->messageEvents().at(0).get()), QStringLiteral("$153456789:example.org"));
}
void EventHandlerTest::nullEventId()
{
QTest::ignoreMessage(QtWarningMsg, "id called with event set to nullptr.");
QCOMPARE(EventHandler::id(nullptr), QString());
}
void EventHandlerTest::authorDisplayName()
{
QCOMPARE(EventHandler::authorDisplayName(room, room->messageEvents().at(1).get()), u"before"_s);
QCOMPARE(EventHandler::authorDisplayName(room, room->messageEvents().at(1).get()), QStringLiteral("before"));
}
void EventHandlerTest::nullAuthorDisplayName()
@@ -84,7 +101,8 @@ void EventHandlerTest::nullAuthorDisplayName()
void EventHandlerTest::singleLineSidplayName()
{
QCOMPARE(EventHandler::singleLineAuthorDisplayname(room, room->messageEvents().at(11).get()), "Look at me I put newlines in my display name"_L1);
QCOMPARE(EventHandler::singleLineAuthorDisplayname(room, room->messageEvents().at(11).get()),
QStringLiteral("Look at me I put newlines in my display name"));
}
void EventHandlerTest::nullSingleLineDisplayName()
@@ -100,24 +118,17 @@ void EventHandlerTest::time()
{
const auto event = room->messageEvents().at(0).get();
QCOMPARE(EventHandler::time(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::time(room, pendingIt->event(), true), pendingIt->lastUpdated());
room->discardMessage(txID);
QCOMPARE(room->pendingEvents().size(), 0);
QCOMPARE(EventHandler::time(event), QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC));
QCOMPARE(EventHandler::time(event, true, QDateTime::fromMSecsSinceEpoch(1234, Qt::UTC)), QDateTime::fromMSecsSinceEpoch(1234, Qt::UTC));
}
void EventHandlerTest::nullTime()
{
QTest::ignoreMessage(QtWarningMsg, "time called with room set to nullptr.");
QCOMPARE(EventHandler::time(nullptr, nullptr), QDateTime());
QTest::ignoreMessage(QtWarningMsg, "time called with event set to nullptr.");
QCOMPARE(EventHandler::time(room, nullptr), QDateTime());
QCOMPARE(EventHandler::time(nullptr), QDateTime());
QTest::ignoreMessage(QtWarningMsg, "a value must be provided for lastUpdated for a pending event.");
QCOMPARE(EventHandler::time(room->messageEvents().at(0).get(), true), QDateTime());
}
void EventHandlerTest::timeString()
@@ -126,27 +137,20 @@ void EventHandlerTest::timeString()
KFormat format;
QCOMPARE(EventHandler::timeString(room, event, false),
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1432735824654, QTimeZone(QTimeZone::UTC)).toLocalTime().time(), QLocale::ShortFormat));
QCOMPARE(EventHandler::timeString(room, event, true),
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1432735824654, QTimeZone(QTimeZone::UTC)).toLocalTime().date(), QLocale::ShortFormat));
QCOMPARE(EventHandler::timeString(room, event, u"hh:mm"_s), QDateTime::fromMSecsSinceEpoch(1432735824654, QTimeZone(QTimeZone::UTC)).toString(u"hh:mm"_s));
const auto txID = room->postJson("m.room.message"_L1, event->fullJson());
QCOMPARE(room->pendingEvents().size(), 1);
const auto pendingIt = room->findPendingEvent(txID);
QCOMPARE(EventHandler::timeString(room, pendingIt->event(), false, QLocale::ShortFormat, true),
QLocale().toString(pendingIt->lastUpdated().toLocalTime().time(), QLocale::ShortFormat));
QCOMPARE(EventHandler::timeString(room, pendingIt->event(), true, QLocale::ShortFormat, true),
format.formatRelativeDate(pendingIt->lastUpdated().toLocalTime().date(), QLocale::ShortFormat));
QCOMPARE(EventHandler::timeString(room, pendingIt->event(), false, QLocale::LongFormat, true),
QLocale().toString(pendingIt->lastUpdated().toLocalTime().time(), QLocale::LongFormat));
QCOMPARE(EventHandler::timeString(room, pendingIt->event(), true, QLocale::LongFormat, true),
format.formatRelativeDate(pendingIt->lastUpdated().toLocalTime().date(), QLocale::LongFormat));
room->discardMessage(txID);
QCOMPARE(room->pendingEvents().size(), 0);
QCOMPARE(EventHandler::timeString(event, false),
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toLocalTime().time(), QLocale::ShortFormat));
QCOMPARE(EventHandler::timeString(event, true),
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toLocalTime().date(), QLocale::ShortFormat));
QCOMPARE(EventHandler::timeString(event, false, QLocale::ShortFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().time(), QLocale::ShortFormat));
QCOMPARE(EventHandler::timeString(event, true, QLocale::ShortFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::ShortFormat));
QCOMPARE(EventHandler::timeString(event, false, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().time(), QLocale::LongFormat));
QCOMPARE(EventHandler::timeString(event, true, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::LongFormat));
QCOMPARE(EventHandler::timeString(event, QStringLiteral("hh:mm")),
QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toString(QStringLiteral("hh:mm")));
}
void EventHandlerTest::highlighted()
@@ -183,10 +187,10 @@ void EventHandlerTest::body()
{
const auto event = room->messageEvents().at(0).get();
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);
QCOMPARE(EventHandler::richBody(room, event), QStringLiteral("<b>This is an example<br>text message</b>"));
QCOMPARE(EventHandler::richBody(room, event, true), QStringLiteral("<b>This is an example text message</b>"));
QCOMPARE(EventHandler::plainBody(room, event), QStringLiteral("This is an example\ntext message"));
QCOMPARE(EventHandler::plainBody(room, event, true), QStringLiteral("This is an example text message"));
}
void EventHandlerTest::nullBody()
@@ -209,11 +213,13 @@ void EventHandlerTest::genericBody_data()
QTest::addColumn<int>("eventNum");
QTest::addColumn<QString>("output");
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;
QTest::newRow("message") << 0 << QStringLiteral("<a href=\"https://matrix.to/#/@example:example.org\">after</a> sent a message");
QTest::newRow("member") << 1
<< QStringLiteral(
"<a href=\"https://matrix.to/#/@example:example.org\">after</a> changed their display name and updated their avatar");
QTest::newRow("message 2") << 2 << QStringLiteral("<a href=\"https://matrix.to/#/@example:example.org\">after</a> sent a message");
QTest::newRow("reaction") << 3 << QStringLiteral("Unknown event");
QTest::newRow("video") << 4 << QStringLiteral("<a href=\"https://matrix.to/#/@example:example.org\">after</a> sent a message");
}
void EventHandlerTest::genericBody()
@@ -235,19 +241,19 @@ void EventHandlerTest::nullGenericBody()
void EventHandlerTest::markdownBody()
{
QCOMPARE(EventHandler::markdownBody(room->messageEvents().at(0).get()), u"This is an example\ntext message"_s);
QCOMPARE(EventHandler::markdownBody(room->messageEvents().at(0).get()), QStringLiteral("This is an example\ntext message"));
}
void EventHandlerTest::markdownBodyReply()
{
QCOMPARE(EventHandler::markdownBody(room->messageEvents().at(5).get()), u"reply"_s);
QCOMPARE(EventHandler::markdownBody(room->messageEvents().at(5).get()), QStringLiteral("reply"));
}
void EventHandlerTest::subtitle()
{
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(0).get()), QStringLiteral("after: This is an example text message"));
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);
QStringLiteral("after: This is a highlight @bob:kde.org and this is a link https://kde.org"));
}
void EventHandlerTest::nullSubtitle()
@@ -263,21 +269,21 @@ void EventHandlerTest::mediaInfo()
{
auto event = room->messageEvents().at(4).get();
auto mediaInfo = EventHandler::mediaInfo(room, event);
auto thumbnailInfo = mediaInfo["tempInfo"_L1].toMap();
auto thumbnailInfo = mediaInfo["tempInfo"_ls].toMap();
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);
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);
}
void EventHandlerTest::nullMediaInfo()
@@ -289,6 +295,30 @@ void EventHandlerTest::nullMediaInfo()
QCOMPARE(EventHandler::mediaInfo(room, nullptr), QVariantMap());
}
void EventHandlerTest::hasReply()
{
QCOMPARE(EventHandler::hasReply(room->messageEvents().at(5).get()), true);
QCOMPARE(EventHandler::hasReply(room->messageEvents().at(0).get()), false);
}
void EventHandlerTest::nullHasReply()
{
QTest::ignoreMessage(QtWarningMsg, "hasReply called with event set to nullptr.");
QCOMPARE(EventHandler::hasReply(nullptr), false);
}
void EventHandlerTest::replyId()
{
QCOMPARE(EventHandler::replyId(room->messageEvents().at(5).get()), QStringLiteral("$153456789:example.org"));
QCOMPARE(EventHandler::replyId(room->messageEvents().at(0).get()), QStringLiteral(""));
}
void EventHandlerTest::nullReplyId()
{
QTest::ignoreMessage(QtWarningMsg, "replyId called with event set to nullptr.");
QCOMPARE(EventHandler::replyId(nullptr), QString());
}
void EventHandlerTest::replyAuthor()
{
auto replyEvent = room->messageEvents().at(0).get();
@@ -314,11 +344,34 @@ void EventHandlerTest::nullReplyAuthor()
QCOMPARE(EventHandler::replyAuthor(room, nullptr), RoomMember());
}
void EventHandlerTest::thread()
{
QCOMPARE(EventHandler::isThreaded(room->messageEvents().at(0).get()), false);
QCOMPARE(EventHandler::threadRoot(room->messageEvents().at(0).get()), QString());
QCOMPARE(EventHandler::isThreaded(room->messageEvents().at(9).get()), true);
QCOMPARE(EventHandler::threadRoot(room->messageEvents().at(9).get()), QStringLiteral("$threadroot:example.org"));
QCOMPARE(EventHandler::replyId(room->messageEvents().at(9).get()), QStringLiteral("$threadroot:example.org"));
QCOMPARE(EventHandler::isThreaded(room->messageEvents().at(10).get()), true);
QCOMPARE(EventHandler::threadRoot(room->messageEvents().at(10).get()), QStringLiteral("$threadroot:example.org"));
QCOMPARE(EventHandler::replyId(room->messageEvents().at(10).get()), QStringLiteral("$threadmessage1:example.org"));
}
void EventHandlerTest::nullThread()
{
QTest::ignoreMessage(QtWarningMsg, "isThreaded called with event set to nullptr.");
QCOMPARE(EventHandler::isThreaded(nullptr), false);
QTest::ignoreMessage(QtWarningMsg, "threadRoot called with event set to nullptr.");
QCOMPARE(EventHandler::threadRoot(nullptr), QString());
}
void EventHandlerTest::location()
{
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);
QCOMPARE(EventHandler::latitude(room->messageEvents().at(7).get()), QStringLiteral("51.7035").toFloat());
QCOMPARE(EventHandler::longitude(room->messageEvents().at(7).get()), QStringLiteral("-1.14394").toFloat());
QCOMPARE(EventHandler::locationAssetType(room->messageEvents().at(7).get()), QStringLiteral("m.pin"));
}
void EventHandlerTest::nullLocation()

View File

@@ -38,8 +38,8 @@ private Q_SLOTS:
void LinkPreviewerTest::initTestCase()
{
connection = Connection::makeMockConnection(u"@bob:example.org"_s);
room = new TestUtils::TestRoom(connection, u"!test:example.org"_s);
connection = Connection::makeMockConnection(QStringLiteral("@bob:example.org"));
room = new TestUtils::TestRoom(connection, QStringLiteral("!test:example.org"));
}
void LinkPreviewerTest::linkPreviewsMatch_data()
@@ -47,9 +47,9 @@ void LinkPreviewerTest::linkPreviewsMatch_data()
QTest::addColumn<QString>("inputString");
QTest::addColumn<QUrl>("testOutputLink");
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);
QTest::newRow("plainHttps") << QStringLiteral("https://kde.org") << QUrl("https://kde.org"_ls);
QTest::newRow("richHttps") << QStringLiteral("<a href=\"https://kde.org\">Rich Link</a>") << QUrl("https://kde.org"_ls);
QTest::newRow("richHttpsLinkDescription") << QStringLiteral("<a href=\"https://kde.org\">https://kde.org</a>") << QUrl("https://kde.org"_ls);
}
void LinkPreviewerTest::linkPreviewsMatch()
@@ -67,8 +67,8 @@ 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)};
QTest::newRow("multipleHttps") << QStringLiteral("www.example.org https://kde.org") << QList{QUrl("www.example.org"_ls), QUrl("https://kde.org"_ls)};
QTest::newRow("multipleHttps1Invalid") << QStringLiteral("www.example.org mxc://example.org/SEsfnsuifSDFSSEF") << QList{QUrl("www.example.org"_ls)};
}
void LinkPreviewerTest::multipleLinkPreviewsMatch()
@@ -85,9 +85,9 @@ void LinkPreviewerTest::linkPreviewsReject_data()
{
QTest::addColumn<QString>("inputString");
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;
QTest::newRow("mxc") << QStringLiteral("mxc://example.org/SEsfnsuifSDFSSEF");
QTest::newRow("matrixTo") << QStringLiteral("https://matrix.to/#/@alice:example.org");
QTest::newRow("noSpace") << QStringLiteral("testhttps://kde.org");
}
void LinkPreviewerTest::linkPreviewsReject()

View File

@@ -32,25 +32,25 @@ private Q_SLOTS:
void MessageContentModelTest::initTestCase()
{
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
}
void MessageContentModelTest::missingEvent()
{
auto room = new TestUtils::TestRoom(connection, u"#firstRoom:kde.org"_s);
auto model1 = MessageContentModel(room, u"$153456789:example.org"_s);
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#firstRoom:kde.org"));
auto model1 = MessageContentModel(room, "$153456789:example.org"_L1);
QCOMPARE(model1.rowCount(), 1);
QCOMPARE(model1.data(model1.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
QCOMPARE(model1.data(model1.index(0), MessageContentModel::DisplayRole), u"Loading"_s);
QCOMPARE(model1.data(model1.index(0), MessageContentModel::DisplayRole), "Loading"_L1);
auto model2 = MessageContentModel(room, u"$153456789:example.org"_s, true);
auto model2 = MessageContentModel(room, "$153456789:example.org"_L1, true);
QCOMPARE(model2.rowCount(), 1);
QCOMPARE(model2.data(model2.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Loading);
QCOMPARE(model2.data(model2.index(0), MessageContentModel::DisplayRole), u"Loading reply"_s);
QCOMPARE(model2.data(model2.index(0), MessageContentModel::DisplayRole), "Loading reply"_L1);
room->syncNewEvents(u"test-min-sync.json"_s);
room->syncNewEvents(QLatin1String("test-min-sync.json"));
QCOMPARE(model1.rowCount(), 2);
QCOMPARE(model1.data(model1.index(0), MessageContentModel::ComponentTypeRole), MessageComponentType::Author);
QCOMPARE(model1.data(model1.index(1), MessageContentModel::ComponentTypeRole), MessageComponentType::Text);

View File

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

View File

@@ -27,8 +27,8 @@ private Q_SLOTS:
void NeoChatRoomTest::initTestCase()
{
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-min-sync.json"_s);
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), "test-min-sync.json"_ls);
}
void NeoChatRoomTest::eventTest()

View File

@@ -31,8 +31,8 @@ private Q_SLOTS:
void PollHandlerTest::initTestCase()
{
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-pollhandlerstart-sync.json"_s);
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), "test-pollhandlerstart-sync.json"_ls);
}
// Basically don't crash.
@@ -54,16 +54,16 @@ void PollHandlerTest::poll()
auto startEvent = eventCast<const PollStartEvent>(room->messageEvents().at(0).get());
auto pollHandler = PollHandler(room, startEvent);
auto options = QJsonArray{QJsonObject{{"id"_L1, "option1"_L1}, {"org.matrix.msc1767.text"_L1, "option1"_L1}},
QJsonObject{{"id"_L1, "option2"_L1}, {"org.matrix.msc1767.text"_L1, "option2"_L1}}};
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}}};
QCOMPARE(pollHandler.hasEnded(), false);
QCOMPARE(pollHandler.answerCount(), 0);
QCOMPARE(pollHandler.question(), u"test"_s);
QCOMPARE(pollHandler.question(), QStringLiteral("test"));
QCOMPARE(pollHandler.options(), options);
QCOMPARE(pollHandler.answers(), QJsonObject());
QCOMPARE(pollHandler.counts(), QJsonObject());
QCOMPARE(pollHandler.kind(), u"org.matrix.msc3381.poll.disclosed"_s);
QCOMPARE(pollHandler.kind(), QStringLiteral("org.matrix.msc3381.poll.disclosed"));
}
QTEST_GUILESS_MAIN(PollHandlerTest)

View File

@@ -20,50 +20,59 @@ class ReactionModelTest : public QObject
private:
Connection *connection = nullptr;
TestUtils::TestRoom *room = nullptr;
MessageContentModel *parentModel;
private Q_SLOTS:
void initTestCase();
void nullModel();
void basicReaction();
void newReaction();
};
void ReactionModelTest::initTestCase()
{
connection = Connection::makeMockConnection(u"@bob:kde.org"_s);
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-reactionmodel-sync.json"_s);
parentModel = new MessageContentModel(room, "123456"_L1);
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());
}
void ReactionModelTest::basicReaction()
{
auto event = eventCast<const RoomMessageEvent>(room->messageEvents().at(0).get());
auto model = ReactionModel(parentModel, event->id(), room);
auto model = ReactionModel(event, room);
QCOMPARE(model.rowCount(), 1);
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::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 Margatroid reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
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(parentModel, event->id(), room);
auto model = new ReactionModel(event, room);
QCOMPARE(model->rowCount(), 1);
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::ToolTipRole),
QStringLiteral("Alice Margatroid reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
QSignalSpy spy(model, SIGNAL(modelReset()));
room->syncNewEvents(u"test-reactionmodel-extra-sync.json"_s);
room->syncNewEvents(QLatin1String("test-reactionmodel-extra-sync.json"));
QCOMPARE(model->rowCount(), 2);
QCOMPARE(spy.count(), 2); // Once for each of the 2 new reactions.
QCOMPARE(model->data(model->index(1), ReactionModel::ReactionRole), u"😆"_s);
QCOMPARE(model->data(model->index(1), ReactionModel::ReactionRole), QStringLiteral("😆"));
QCOMPARE(model->data(model->index(0), ReactionModel::ToolTipRole),
u"Alice Margatroid and Bob reacted with <span style=\"font-family: 'emoji';\">👍</span>"_s);
QStringLiteral("Alice Margatroid and Bob reacted with <span style=\"font-family: 'emoji';\">👍</span>"));
delete model;
}

View File

@@ -31,7 +31,7 @@ public:
{
if (!syncFileName.isEmpty()) {
QFile testSyncFile;
testSyncFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + syncFileName);
testSyncFile.setFileName(QLatin1String(DATA_DIR) + u'/' + syncFileName);
testSyncFile.open(QIODevice::ReadOnly);
const auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll());
Quotient::SyncRoomData roomData(id(), Quotient::JoinState::Join, testSyncJson.object());
@@ -45,7 +45,7 @@ inline Quotient::event_ptr_tt<EventT> loadEventFromFile(const QString &eventFile
{
if (!eventFileName.isEmpty()) {
QFile testEventFile;
testEventFile.setFileName(QStringLiteral(DATA_DIR) + u'/' + eventFileName);
testEventFile.setFileName(QLatin1String(DATA_DIR) + u'/' + eventFileName);
testEventFile.open(QIODevice::ReadOnly);
auto testSyncJson = QJsonDocument::fromJson(testEventFile.readAll()).object();
return Quotient::loadEvent<EventT>(testSyncJson);

View File

@@ -71,25 +71,25 @@ private Q_SLOTS:
void TextHandlerTest::initTestCase()
{
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}}}}}}});
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}}}}}}});
CustomEmojiModel::instance().setConnection(static_cast<NeoChatConnection *>(connection));
room = new TestUtils::TestRoom(connection, u"#myroom:kde.org"_s, u"test-texthandler-sync.json"_s);
room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"), QLatin1String("test-texthandler-sync.json"));
}
void TextHandlerTest::allowedAttributes()
{
const QString testInputString1 = u"<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>"_s;
const QString testOutputString1 = u"<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>"_s;
const QString testInputString1 = QStringLiteral("<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>");
const QString testOutputString1 = QStringLiteral("<span data-mx-spoiler><font color=#FFFFFF>Test</font><span>");
// Handle urls where the href has either single (') or double (") quotes.
const QString testInputString2 = u"<a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a>"_s;
const QString testOutputString2 = u"<a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a>"_s;
const QString testInputString2 = QStringLiteral("<a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a>");
const QString testOutputString2 = QStringLiteral("<a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString1);
@@ -104,8 +104,8 @@ void TextHandlerTest::allowedAttributes()
void TextHandlerTest::stripDisallowedTags()
{
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;
const QString testInputString = QStringLiteral("<p>Allowed</p> <span>Allowed</span> <body>Disallowed</body>");
const QString testOutputString = QStringLiteral("<p>Allowed</p> <span>Allowed</span> Disallowed");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -116,8 +116,8 @@ void TextHandlerTest::stripDisallowedTags()
void TextHandlerTest::stripDisallowedAttributes()
{
const QString testInputString = u"<p style=\"font-size:50px;\" color=#FFFFFF>Test</p>"_s;
const QString testOutputString = u"Test"_s;
const QString testInputString = QStringLiteral("<p style=\"font-size:50px;\" color=#FFFFFF>Test</p>");
const QString testOutputString = QStringLiteral("Test");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -132,8 +132,8 @@ void TextHandlerTest::stripDisallowedAttributes()
*/
void TextHandlerTest::emptyCodeTags()
{
const QString testInputString = u"<pre><code></code></pre>"_s;
const QString testOutputString = u"<pre><code></code></pre>"_s;
const QString testInputString = QStringLiteral("<pre><code></code></pre>");
const QString testOutputString = QStringLiteral("<pre><code></code></pre>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -144,8 +144,8 @@ void TextHandlerTest::emptyCodeTags()
void TextHandlerTest::sendSimpleStringCase()
{
const QString testInputString = u"This data should just be left alone."_s;
const QString testOutputString = u"This data should just be left alone."_s;
const QString testInputString = QStringLiteral("This data should just be left alone.");
const QString testOutputString = QStringLiteral("This data should just be left alone.");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -155,10 +155,11 @@ void TextHandlerTest::sendSimpleStringCase()
void TextHandlerTest::sendSingleParaMarkup()
{
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;
const QString testInputString = QStringLiteral(
"Text para with **bold**, *italic*, [link](https://kde.org), ![image](mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e), `inline code`.");
const QString testOutputString = QStringLiteral(
"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>.");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -169,9 +170,11 @@ void TextHandlerTest::sendSingleParaMarkup()
void TextHandlerTest::sendMultipleSectionMarkup()
{
const QString testInputString =
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;
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>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -181,8 +184,8 @@ void TextHandlerTest::sendMultipleSectionMarkup()
void TextHandlerTest::sendBadLinks()
{
const QString testInputString = u"[link](kde.org), ![image](https://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e)"_s;
const QString testOutputString = u"<a>link</a>, <img alt=\"image\">"_s;
const QString testInputString = QStringLiteral("[link](kde.org), ![image](https://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e)");
const QString testOutputString = QStringLiteral("<a>link</a>, <img alt=\"image\">");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -195,9 +198,9 @@ void TextHandlerTest::sendBadLinks()
*/
void TextHandlerTest::sendEscapeCode()
{
const QString testInputString = u"```\n<p>Test <span style=\"font-size:50px;\">some</span> code</p>\n```"_s;
const QString testInputString = QStringLiteral("```\n<p>Test <span style=\"font-size:50px;\">some</span> code</p>\n```");
const QString testOutputString =
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;
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>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -207,8 +210,8 @@ void TextHandlerTest::sendEscapeCode()
void TextHandlerTest::sendCodeClass()
{
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;
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>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -218,9 +221,9 @@ void TextHandlerTest::sendCodeClass()
void TextHandlerTest::sendCustomEmoji()
{
const QString testInputString = u":test:"_s;
const QString testInputString = QStringLiteral(":test:");
const QString testOutputString =
u"<img data-mx-emoticon=\"\" src=\"mxc://example.org/test\" alt=\":test:\" title=\":test:\" height=\"32\" vertical-align=\"middle\" />"_s;
QStringLiteral("<img data-mx-emoticon=\"\" src=\"mxc://example.org/test\" alt=\":test:\" title=\":test:\" height=\"32\" vertical-align=\"middle\" />");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -233,8 +236,8 @@ void TextHandlerTest::sendCustomEmojiCode_data()
QTest::addColumn<QString>("testInputString");
QTest::addColumn<QString>("testOutputString");
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;
QTest::newRow("inline") << QStringLiteral("`:test:`") << QStringLiteral("<code>:test:</code>");
QTest::newRow("block") << QStringLiteral("```\n:test:\n```") << QStringLiteral("<pre><code>:test:\n</code></pre>");
}
// Custom emojis in code blocks should be left alone.
@@ -251,9 +254,9 @@ void TextHandlerTest::sendCustomEmojiCode()
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;
const QString testInputString = QStringLiteral("Test...<br/>...ing");
const QString testRichOutputString = QStringLiteral("Test...<br/>...ing");
const QString testPlainOutputString = QStringLiteral("Test...\n...ing");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -264,9 +267,10 @@ void TextHandlerTest::receiveSpacelessSelfClosingTag()
void TextHandlerTest::receiveStripReply()
{
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;
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.");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -280,10 +284,10 @@ void TextHandlerTest::receiveRichInPlainOut_data()
QTest::addColumn<QString>("testInputString");
QTest::addColumn<QString>("testOutputString");
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;
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("unescape") << QStringLiteral("can&#x27;t") << QStringLiteral("can't");
}
void TextHandlerTest::receiveRichInPlainOut()
@@ -299,13 +303,13 @@ void TextHandlerTest::receiveRichInPlainOut()
void TextHandlerTest::receivePlainTextIn()
{
const QString testInputString = u"<plain text in tag bracket>\nTest link https://kde.org."_s;
const QString testOutputStringRich = u"&lt;plain text in tag bracket&gt;<br>Test link <a href=\"https://kde.org\">https://kde.org</a>."_s;
QString testOutputStringPlain = u"<plain text in tag bracket>\nTest link https://kde.org."_s;
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.");
// Make sure quotes are maintained in a plain string.
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;
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.\"");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -320,12 +324,12 @@ void TextHandlerTest::receivePlainTextIn()
void TextHandlerTest::receiveStripNewlines()
{
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 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 testInputStringPlain2 = u"* List\n* Items"_s;
const QString testOutputString2 = u"List Items"_s;
const QString testInputStringPlain2 = QStringLiteral("* List\n* Items");
const QString testOutputString2 = QStringLiteral("List Items");
TextHandler testTextHandler;
testTextHandler.setData(testInputStringPlain);
@@ -347,8 +351,8 @@ void TextHandlerTest::receiveStripNewlines()
*/
void TextHandlerTest::receivePlainStripHtml()
{
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;
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>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -358,8 +362,8 @@ void TextHandlerTest::receivePlainStripHtml()
void TextHandlerTest::receivePlainStripMarkup()
{
const QString testInputString = u"**bold** `<p>inline code</p>` *italic*"_s;
const QString testOutputString = u"bold <p>inline code</p> italic"_s;
const QString testInputString = QStringLiteral("**bold** `<p>inline code</p>` *italic*");
const QString testOutputString = QStringLiteral("bold <p>inline code</p> italic");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -369,8 +373,8 @@ void TextHandlerTest::receivePlainStripMarkup()
void TextHandlerTest::receiveRichUserPill()
{
const QString testInputString = u"<p><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></p>"_s;
const QString testOutputString = u"<b><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></b>"_s;
const QString testInputString = QStringLiteral("<p><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></p>");
const QString testOutputString = QStringLiteral("<b><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></b>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -380,8 +384,8 @@ void TextHandlerTest::receiveRichUserPill()
void TextHandlerTest::receiveRichStrikethrough()
{
const QString testInputString = u"<p><del>Test</del></p>"_s;
const QString testOutputString = u"<s>Test</s>"_s;
const QString testInputString = QStringLiteral("<p><del>Test</del></p>");
const QString testOutputString = QStringLiteral("<s>Test</s>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -391,8 +395,8 @@ void TextHandlerTest::receiveRichStrikethrough()
void TextHandlerTest::receiveRichtextIn()
{
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;
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>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -402,10 +406,15 @@ void TextHandlerTest::receiveRichtextIn()
void TextHandlerTest::receiveRichMxcUrl()
{
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;
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\">");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -423,27 +432,38 @@ 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 =
u"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im <a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">Link already rich</a>"_s;
const QString testOutputStringLink1 =
u"<a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im</a> <a href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">Link already rich</a>"_s;
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 = u"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/"_s;
const QString testOutputStringLink2 =
u"<a href=\"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/\">https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/</a>"_s;
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 = uR"(email@example.com <a href="mailto:email@example.com">Link already rich</a>)"_s;
QString testOutputStringEmail = uR"(<a href="mailto:email@example.com">email@example.com</a> <a href="mailto:email@example.com">Link already rich</a>)"_s;
QString 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 = u"@user:kde.org <a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a>"_s;
QString testOutputStringMxId =
u"<b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a></b>"_s;
QString 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>");
QString testInputStringMxIdWithPrefix = u"a @user:kde.org b"_s;
QString testOutputStringMxIdWithPrefix = u"a <b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> b"_s;
QString testInputStringMxIdWithPrefix = QStringLiteral("a @user:kde.org b");
QString testOutputStringMxIdWithPrefix = QStringLiteral("a <b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> b");
TextHandler testTextHandler;
testTextHandler.setData(testInputStringLink1);
@@ -468,9 +488,9 @@ void TextHandlerTest::receiveRichEdited_data()
QTest::addColumn<QString>("testInputString");
QTest::addColumn<QString>("testOutputString");
QTest::newRow("basic") << u"Edited"_s << u"Edited <span style=\"color:#000000\">(edited)</span>"_s;
QTest::newRow("multiple paragraphs") << u"<p>Edited</p>\n<p>Edited</p>"_s
<< u"<p>Edited</p>\n<p>Edited <span style=\"color:#000000\">(edited)</span></p>"_s;
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>");
}
void TextHandlerTest::receiveRichEdited()
@@ -487,15 +507,15 @@ void TextHandlerTest::receiveRichEdited()
void TextHandlerTest::receiveLineSeparator()
{
auto text = u"foo\u2028bar"_s;
auto text = QStringLiteral("foo\u2028bar");
TextHandler textHandler;
textHandler.setData(text);
QCOMPARE(textHandler.handleRecievePlainText(Qt::PlainText, true), u"foo bar"_s);
QCOMPARE(textHandler.handleRecievePlainText(Qt::PlainText, true), QStringLiteral("foo bar"));
}
void TextHandlerTest::receiveRichCodeUrl()
{
auto input = u"<code>https://kde.org</code>"_s;
auto input = QStringLiteral("<code>https://kde.org</code>");
TextHandler testTextHandler;
testTextHandler.setData(input);
QCOMPARE(testTextHandler.handleRecieveRichText(), input);
@@ -503,10 +523,14 @@ void TextHandlerTest::receiveRichCodeUrl()
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;
const QString testInputString = QStringLiteral(
"<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>");
const QString testOutputString = QStringLiteral(
"<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>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -521,38 +545,42 @@ void TextHandlerTest::componentOutput_data()
QTest::addColumn<QString>("testInputString");
QTest::addColumn<QList<MessageComponent>>("testOutputComponents");
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
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>")
<< QList<MessageComponent>{
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, {}}};
MessageComponent{MessageComponentType::Text, QStringLiteral("<code>https://kde.org</code>"), {}}};
QTest::newRow("long start tag")
<< 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
<< 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")
<< QList<MessageComponent>{
MessageComponent{MessageComponentType::Text, u"Ah, you mean something like<br/>"_s, {}},
MessageComponent{MessageComponentType::Text, QStringLiteral("Ah, you mean something like<br/>"), {}},
MessageComponent{
MessageComponentType::Code,
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, {}}};
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"), {}}};
}
void TextHandlerTest::componentOutput()

View File

@@ -1,34 +0,0 @@
# 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
KF6::Kirigami
QuotientQt6
neochat
)
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
)

View File

@@ -1,33 +0,0 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
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 {
model: messageFilterModel
delegate: EventDelegate {
room: memTestTimelineModel.room
}
}
}
}

View File

@@ -1,30 +0,0 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#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;
engine.loadFromModule("org.kde.neochat.timeline-memtest", "Main");
MemTestTimelineModel *memTestTimelineModel = new MemTestTimelineModel;
MessageFilterModel *messageFilterModel = new MessageFilterModel(nullptr, memTestTimelineModel);
engine.rootContext()->setContextProperty(u"memTestTimelineModel"_s, memTestTimelineModel);
engine.rootContext()->setContextProperty(u"messageFilterModel"_s, messageFilterModel);
return app.exec();
}

View File

@@ -1,655 +0,0 @@
{
"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": "$1000000000000:example.org",
"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": "$1000000000001:example.org",
"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": "$1000000000001:example.org",
"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": "$1000000000002:example.org",
"age": 390159120
},
{
"content": {
"body": "reply",
"format": "org.matrix.custom.html",
"formatted_body": "reply",
"m.relates_to": {
"m.in_reply_to": {
"event_id": "$1000000000000:example.org"
}
},
"msgtype": "m.text"
},
"origin_server_ts": 1000000000003,
"sender": "@alice:example.org",
"type": "m.room.message",
"unsigned": {
"age": 98
},
"event_id": "$1000000000003:example.org",
"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": "$1000000000004:example.org",
"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": "$1000000000005:example.org",
"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": "$1000000000006:example.org",
"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": "$1000000000007:example.org",
"origin_server_ts": 1000000000007,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1232
}
},
{
"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": "$1000000000008:example.org",
"origin_server_ts": 1000000000008,
"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": "$1000000000009:example.org",
"origin_server_ts": 1000000000009,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1233
}
},
{
"content": {
"m.relates_to": {
"event_id": "$1000000000009:example.org",
"key": "👍",
"rel_type": "m.annotation"
}
},
"origin_server_ts": 1000000000010,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@alice:example.org",
"type": "m.reaction",
"unsigned": {
"age": 390159120
},
"event_id": "$1000000000010:example.org",
"age": 390159120
},
{
"content": {
"body": "reply",
"format": "org.matrix.custom.html",
"formatted_body": "reply",
"m.relates_to": {
"m.in_reply_to": {
"event_id": "$1000000000008:example.org"
}
},
"msgtype": "m.text"
},
"origin_server_ts": 1000000000011,
"sender": "@alice:example.org",
"type": "m.room.message",
"unsigned": {
"age": 98
},
"event_id": "$1000000000011:example.org",
"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": "$1000000000012:example.org",
"origin_server_ts": 1000000000012,
"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": "$1000000000013:example.org",
"origin_server_ts": 1000000000013,
"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": "$1000000000014:example.org",
"origin_server_ts": 1000000000014,
"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": "$1000000000015:example.org",
"origin_server_ts": 1000000000015,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1232
}
},
{
"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": "$1000000000016:example.org",
"origin_server_ts": 1000000000016,
"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": "$1000000000017:example.org",
"origin_server_ts": 1000000000017,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@example:example.org",
"type": "m.room.message",
"unsigned": {
"age": 1233
}
},
{
"content": {
"m.relates_to": {
"event_id": "$1000000000017:example.org",
"key": "👍",
"rel_type": "m.annotation"
}
},
"origin_server_ts": 1000000000018,
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"sender": "@alice:example.org",
"type": "m.reaction",
"unsigned": {
"age": 390159120
},
"event_id": "$1000000000018:example.org",
"age": 390159120
},
{
"content": {
"body": "reply",
"format": "org.matrix.custom.html",
"formatted_body": "reply",
"m.relates_to": {
"m.in_reply_to": {
"event_id": "$1000000000016:example.org"
}
},
"msgtype": "m.text"
},
"origin_server_ts": 1000000000019,
"sender": "@alice:example.org",
"type": "m.room.message",
"unsigned": {
"age": 98
},
"event_id": "$1000000000019:example.org",
"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": "$1000000000020:example.org",
"origin_server_ts": 1000000000020,
"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": "$1000000000021:example.org",
"origin_server_ts": 1000000000021,
"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": "$1000000000022:example.org",
"origin_server_ts": 1000000000022,
"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": "$1000000000023:example.org",
"origin_server_ts": 1000000000023,
"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

@@ -1,36 +0,0 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "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

@@ -1,75 +0,0 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QAbstractListModel>
#include <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);
testSyncFile.open(QIODevice::ReadOnly);
auto testSyncJson = QJsonDocument::fromJson(testSyncFile.readAll()).object();
Quotient::SyncRoomData roomData(id(), Quotient::JoinState::Join, testSyncJson);
update(std::move(roomData));
}
}
};
/**
* @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;
};

View File

@@ -26,7 +26,6 @@
<name xml:lang="fr">NeoChat</name>
<name xml:lang="gl">NeoChat</name>
<name xml:lang="he">NeoChat</name>
<name xml:lang="hi">नियोचैट</name>
<name xml:lang="hu">NeoChat</name>
<name xml:lang="ia">Neochat</name>
<name xml:lang="id">NeoChat</name>
@@ -42,7 +41,6 @@
<name xml:lang="pt">NeoChat</name>
<name xml:lang="pt-BR">NeoChat</name>
<name xml:lang="ru">NeoChat</name>
<name xml:lang="sa">नवचैट्</name>
<name xml:lang="sk">NeoChat</name>
<name xml:lang="sl">NeoChat</name>
<name xml:lang="sv">NeoChat</name>
@@ -65,18 +63,13 @@
<summary xml:lang="fr">Discuter sur Matrix</summary>
<summary xml:lang="gl">Charlar en Matrix</summary>
<summary xml:lang="he">התכתבות דרך Matrix</summary>
<summary xml:lang="hi">मैट्रिक्स पर चैट करें</summary>
<summary xml:lang="hu">Csevegés Matrixon</summary>
<summary xml:lang="ia">Conversation en ditecto sur Matrix</summary>
<summary xml:lang="it">Chat su Matrix</summary>
<summary xml:lang="ka">ისაუბრეთ Matrix-ზე</summary>
<summary xml:lang="ko">Matrix에서 대화하기</summary>
<summary xml:lang="lv">Tērzējiet „Matrix“ tīklā</summary>
<summary xml:lang="nl">Chat op Matrix</summary>
<summary xml:lang="nn">Prat med via Matrix</summary>
<summary xml:lang="pl">Rozmawiaj na Matriksie</summary>
<summary xml:lang="ru">Общение в Matrix</summary>
<summary xml:lang="sa">Matrix इत्यत्र गपशपं कुर्वन्तु</summary>
<summary xml:lang="sl">Klepet na Matrixu</summary>
<summary xml:lang="sv">Chatta på Matrix</summary>
<summary xml:lang="ta">மேட்ரிக்ஸுக்கான உரையாடல் செயலி</summary>
@@ -99,18 +92,15 @@
<p xml:lang="fr">NeoChat est une application de discussions vous permettant de profiter pleinement du réseau Matrix. Elle vous offre un moyen sécurisé d'envoyer des messages de texte, des vidéos et des fichiers audio à votre famille, vos collègues et vos ami(e)s.</p>
<p xml:lang="gl">NeoChat é unha aplicación de conversa que lle permite usar todas as funcionalidades da rede Matrix. Fornece unha forma segura de enviar mensaxes de texto e ficheiros de vídeo e son a familiares, amizades ou no traballo.</p>
<p xml:lang="he">NeoChat הוא יישום התכתבות שמאפשר לך לנצל את רשת Matrix במלואה. הוא מספק דרך מאובטחת לשליחת הודעות כתובות, סרטונים וקובצי שמע למשפחה, לעמיתים לעבודה ולחברים.</p>
<p xml:lang="hi">नियोचैट एक चैट ऐप है जो आपको मैट्रिक्स नेटवर्क का पूरा लाभ उठाने देता है। यह आपको अपने परिवार, सहकर्मियों और दोस्तों को टेक्स्ट संदेश, वीडियो और ऑडियो फ़ाइलें भेजने का एक सुरक्षित तरीका प्रदान करता है।</p>
<p xml:lang="hu">A NeoChat egy olyan csevegőalkalmazás, amellyel teljes mértékben kihasználhatja a Matrix hálózatot. Biztonságos módot biztosít szöveges üzenetek, videók és hangfájlok küldéséhez családtagjainak, kollégáinak és barátainak.</p>
<p xml:lang="ia">NeoChat es un app de conversation que te permitte prender avantage plen del rete Matrix. Il te forni un modo secur de inviar messages de texto, videos e files audio a tui familia, collegas e amicos.</p>
<p xml:lang="it">NeoChat è un'applicazione di chat che ti consente di sfruttare appieno la rete Matrix. Ti fornisce un modo sicuro per inviare messaggi di testo, video e file audio a familiari, colleghi e amici.</p>
<p xml:lang="ka">NeoChat ჩატის აპია, რომელიც საშუალება გაძლევთ, Matrix-ის ქსელის საშუალებები ბოლომდე გამოიყენოთ. ის გაძლევთ უსაფრთხო გზას, გააგზავნოთ ტექსტური შეტყობინებები, ვიდეოებ და აუდიოფაილები თქვენს ოჯახთან, კოლეგებთან და მეგობრებთან.</p>
<p xml:lang="ko">NeoChat은 Matrix 네트워크를 사용하는 채팅 앱입니다. 텍스트 메시지, 동영상, 오디오 파일을 가족, 친구, 동료와 안전하게 공유할 수 있습니다.</p>
<p xml:lang="lv">„NeoChat“ ir tērzēšanas programma, kas ļauj pilnvērtīgi izmantot „Matrix“ tīklu. Tā sniedz drošu veidu teksta ziņu, video un audio sūtīšanai ģimenes locekļiem, kolēģiem un draugiem.</p>
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
<p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p>
<p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p>
<p xml:lang="ru">NeoChat — приложение для общения, предоставляющее все преимущества сети Matrix. С его помощью можно безопасно отправлять текстовые сообщения, видеозаписи и звуковые файлы родственникам, коллегам и друзьям.</p>
<p xml:lang="sa">NeoChat इति एकं गपशप-अनुप्रयोगं यत् भवान् Matrix-जालस्य पूर्णं लाभं ग्रहीतुं शक्नोति । एतत् भवन्तं भवतः परिवाराय, सहकारिभ्यः, मित्रेभ्यः च पाठसन्देशान्, भिडियो, श्रव्यसञ्चिकाः च प्रेषयितुं सुरक्षितं मार्गं प्रदाति ।</p>
<p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p>
<p xml:lang="sv">NeoChat är ett chattprogram som låter dig dra full nytta av Matrix-nätverket. Det ger dig ett säkert sätt att skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner.</p>
<p xml:lang="tr">NeoChat, Matrix ağının tüm özelliklerini kullanan bir sohbet uygulamasıdır. Ailenize, arkadaşlarınıza ve iş arkadaşlarınıza metin iletileri, ses ve video dosyaları göndermenin kolay bir yolunu sunar.</p>
@@ -131,7 +121,6 @@
<p xml:lang="fr">L'objectif de NeoChat est d'être une application complète pour le protocole Matrix. En tant que tel, tout dans la spécification stable actuelle avec les exceptions notables de VoIP, les processus et certains aspects du chiffrement de bout en bout sont pris en charge. Il y a quelques autres petites omissions en raison du fait que la spécification du protocole Matrix est en constante évolution. Cependant, l'objectif reste de fournir un soutien éventuel pour l'ensemble de la spécification.</p>
<p xml:lang="gl">NeoChat pretende ser unha aplicación completa para a especificación de Matrix. Coas excepcións de VoIP, conversas fiadas e algúns aspectos da cifraxe de extremo a extremo, a versión estábel segue as especificacións. Existen algunhas outras pequenas omisións debido ao feito de que Matrix está en continua evolución pero a intención é fornecer compatibilidade coa especificación completa.</p>
<p xml:lang="he">NeoChat מתיימר להיות יישום עתיר יכולות לפי מפרט Matrix. כיוון שזה ייעודו, כל מה שבמפרט היציב עם חריגות משמעותיות כגון VoIP, שרשורים ועוד מגוון היבטים של הצפנה מקצה לקצה נתמכים גם הם. יש מספר השמטות קטן עקב העובדה שהמפרט של Matrix ממשיך להתפתח אך המטרה היא להמשיך לספק תמיכה בסופו של דבר לכל המפרט.</p>
<p xml:lang="hi">नियोचैट का लक्ष्य मैट्रिक्स विनिर्देश के लिए एक पूर्ण विशेषताओं वाला अनुप्रयोग बनना है। इस प्रकार वर्तमान स्थिर विनिर्देश में वीओआईपी, थ्रेड्स और एंड-टू-एंड एन्क्रिप्शन के कुछ पहलुओं के उल्लेखनीय अपवादों के साथ सब कुछ समर्थित है। मैट्रिक्स विनिर्देश लगातार विकसित हो रहा है, इस तथ्य के कारण कुछ अन्य छोटी चूकें हैं, लेकिन उद्देश्य पूरे विनिर्देश के लिए अंतिम समर्थन प्रदान करना है।</p>
<p xml:lang="hu">A NeoChat célja, hogy a Matrix specifikációnak megfelelő teljes funkcionalitású alkalmazás legyen. Mint ilyen, a jelenlegi stabil specifikáció támogatott a VoIP, a szálak és a végpontok közötti titkosítás egyes elemeinek kivételével. Van még néhány kisebb hiányosság annak köszönhetően, hogy a Matrix specifikáció folyamatosan fejlődik, de végső cél a teljes specifikáció megvalósítása.</p>
<p xml:lang="ia">NeoChat aspira a esser un application plenmente eminente per le specification de Matrix. Tal como omne cosas in le specification currentemente stabile con le exceptiones notabile de VOIP, threads e alcun aspectos del cryptation End-to-End es supportate. Il ha ltere pauc omissiones, debite al facto que le specification de Matrix es in evolution constante ma le aspiration remane a fornir supporto eventual per le integre specification.</p>
<p xml:lang="it">NeoChat mira ad essere un'applicazione completa per le specifiche Matrix. Pertanto, sono supportati tutti gli elementi dell'attuale specifica stabile con le notevoli eccezioni di VoIP, conversazioni e alcuni aspetti della cifratura end-to-end. Ci sono alcune altre piccole omissioni dovute al fatto che le specifiche Matrix sono in continua evoluzione, ma l'obiettivo rimane quello di fornire un eventuale supporto per l'intera specifica.</p>
@@ -143,7 +132,6 @@
<p xml:lang="pl">NeoChat w zamyśle ma być pełnowartościową aplikacją wg wytycznych Matriksa. Z tego powodu, wszystko, co jest obecnie w stabilnych wytycznych z pominięciem VoIP, wątków i niektórych części szyfrowania Użytkownik-do-Użytkownika są obecnie obsługiwane. Pominięto też kilka mniejszych rzeczy ze względu na ciągły rozwój wytycznych Matriksa, lecz celem nadal jest zapewnienie obsługi wszystkich wytycznych.</p>
<p xml:lang="pt">O NeoChat pretende ser uma aplicação completa para a especificação do Matrix. Como tal, tudo o que existe na especificação estável actual, com as notáveis excepções do VoIP, tópicos e alguns aspectos da Encriptação Ponto-a-Ponto, são suportados. Existem mais algumas omissões, devido ao facto que a norma do Matrix está em constante evolução, mas o objectivo continua a ser oferecer o suporte eventual para a norma por inteiro.</p>
<p xml:lang="ru">Целью создания NeoChat является полноценная реализация программы для спецификации Matrix. Как следствие, реализовано всё в текущей стабильной спецификации (за исключением голосовой интернет-связи, потоков и некоторых аспектов сквозного шифрования). Есть также несколько других незначительных пробелов, обусловленных постоянными изменениями спецификации Matrix. Тем не менее, стоит задача в итоге предоставить полную поддержку спецификации.</p>
<p xml:lang="sa">NeoChat इत्यस्य उद्देश्यं Matrix विनिर्देशस्य कृते पूर्णतया विशेषतायुक्तः अनुप्रयोगः भवितुम् अस्ति । यथा तथा वर्तमानस्थिरविनिर्देशे सर्वं VoIP इत्यस्य उल्लेखनीयअपवादैः सह, थ्रेड्स तथा च End-to-End Encryption इत्यस्य केचन पक्षाः समर्थिताः सन्ति । अन्ये कतिचन लघु लोपाः सन्ति यतोहि Matrix spec निरन्तरं विकसितः अस्ति परन्तु उद्देश्यं सम्पूर्ण spec कृते अन्ततः समर्थनं प्रदातुं अवशिष्टम् अस्ति</p>
<p xml:lang="sl">Neochat cilja, da bi bila popolna aplikacija po specifikaciji Matrixa. Kot takšna vsebuje vse v trenutni stabilni specifikaciji z pomembnimi izjemami pri VoIP, nitih in nekaterih vidikov šifriranja od konca do konca. Obstaja nekaj drugih manjših opustitev zaradi dejstva, da se specifikacija Matrix nenehno razvija, vendar cilj ostaja zagotoviti morebitno podporo celotni specifikaciji.</p>
<p xml:lang="sv">NeoChat har som mål att vara ett fullständigt program enligt Matrix-specifikationen. Som sådant stöds allt i den nuvarande stabila specifikationen, med de nämnvärda undantagen VoIP, trådar och några aspekter av kryptering hela vägen. Det finns några ytterligare utelämnanden på grund av att Matrix-specifikationen hela tiden utvecklas, men målet förblir att till slut erbjuda stöd för hela specifikationen.</p>
<p xml:lang="tr">NeoChat, Matrix belirtimi için tam özellikli bir uygulama olmayı hedefler. Bu nedenle; VoIP, ileti zincirleri ve Uçtan Uca Şifrelemenin bazı yönleri gibi dikkate değer istisnalar dışında var olan kararlı belirtimdeki her şey desteklenir. Matrix belirtiminin sürekli gelişmesi nedeniyle birkaç küçük eksiklik daha var; ancak amaç tüm belirtim için nihai destek sağlamak olmayı sürdürüyor.</p>
@@ -164,7 +152,6 @@
<p xml:lang="fr">En raison de la nature du développement des spécifications du protocole Matrix, NeoChat prend également en charge de nombreuses fonctionnalités instables. Actuellement, ce sont :</p>
<p xml:lang="gl">Debido á natureza do desenvolvemento da especificación de Matrix, NeoChat tamén inclúe varias funcionalidades non estábeis:</p>
<p xml:lang="he">מטבע הדברים, הפיתוח של NeoChat תומך במגוון יכולות מפוקפקות כתלות בהתפתחות המפרט הטכני של Matrix. היכולות האלה הן:</p>
<p xml:lang="hi">मैट्रिक्स विनिर्देश विकास की प्रकृति के कारण नियोचैट कई अस्थिर सुविधाओं का भी समर्थन करता है। वर्तमान में ये हैं:</p>
<p xml:lang="hu">A Matrix specifikáció fejlesztésének jellegéből adódóan a NeoChat számos instabil funkciót is támogat. Jelenleg a következőket:</p>
<p xml:lang="ia">Debite al natura del disveloppamento de specification de Matrix NeoChat tamben supporta numerose characteristicas instabile. Currentemente istes es:</p>
<p xml:lang="it">A causa della natura dello sviluppo delle specifiche Matrix, NeoChat supporta anche numerose funzionalità instabili. Attualmente queste sono:</p>
@@ -176,7 +163,6 @@
<p xml:lang="pl">Ze względu na sposób rozwoju Matriksa, NeoChat obsługuje także kilka niestabilnych możliwości. Obecnie są to:</p>
<p xml:lang="pt">Devido à natureza do desenvolvimento da especificação do Matrix, o NeoChat também suporta diversas funcionalidades instáveis. De momento são:</p>
<p xml:lang="ru">В силу природы разработки спецификации Matrix в NeoChat тоже предусмотрена поддержка многочисленных нестабильных возможностей. В текущей версии это следующие возможности:</p>
<p xml:lang="sa">Matrix विनिर्देशविकासस्य प्रकृतेः कारणात् NeoChat अपि अनेकानाम् अस्थिरविशेषतानां समर्थनं करोति । सम्प्रति एते सन्ति :</p>
<p xml:lang="sl">Zaradi narave razvoja specifikacije Matrixa NeoChat podpira tudi številne nestabilne zmožnosti. Trenutno so to:</p>
<p xml:lang="sv">På grund av sättet Matrix-specifikationens utvecklas, stöder NeoChat också ett stor antal instabila funktioner. För närvarande är de:</p>
<p xml:lang="ta">மேட்ரிக்ஸு நெறிமுறை வரையறுக்கப்படும் வித‍த்தின் காரணமாக, பல நிலையற்ற அம்சங்களையும் நியோச்சாட் ஆதரிக்கிறது. தற்போது ஆதரிக்கப்படுபவை:</p>
@@ -198,7 +184,6 @@
<li xml:lang="fr">Sondages - MSC3381</li>
<li xml:lang="gl">Enquisas — MSC3381</li>
<li xml:lang="he">סקרים - MSC3381</li>
<li xml:lang="hi">पोल - MSC3381</li>
<li xml:lang="hu">Szavazások - MSC3381</li>
<li xml:lang="ia">Inquestas - MSC3381</li>
<li xml:lang="it">Sondaggi - MSC3381</li>
@@ -210,7 +195,6 @@
<li xml:lang="pl">Ankiety - MSC3381</li>
<li xml:lang="pt">Inquéritos - MSC3381</li>
<li xml:lang="ru">Голосования — MSC3381</li>
<li xml:lang="sa">मतदान - MSC3381</li>
<li xml:lang="sl">Polls - MSC3381</li>
<li xml:lang="sv">Polls - MSC3381</li>
<li xml:lang="ta">வாக்கெடுப்புகள் - MSC3381</li>
@@ -231,7 +215,6 @@
<li xml:lang="fr">Paquets d'auto-collants - MSC2545</li>
<li xml:lang="gl">Paquetes de adhesivos — MSC2545</li>
<li xml:lang="he">חבילות מדבקות - MSC2545</li>
<li xml:lang="hi">स्टिकर पैक - MSC2545</li>
<li xml:lang="hu">Matricacsomagok - MSC2545</li>
<li xml:lang="ia">Etiquetta gummate (sticker) -MSC2545</li>
<li xml:lang="it">Pacchetti di adesivi - MSC2545</li>
@@ -243,7 +226,6 @@
<li xml:lang="pl">Paczki naklejek - MSC2545</li>
<li xml:lang="pt">Pacotes de Autocolantes - MSC2545</li>
<li xml:lang="ru">Наборы стикеров — MSC2545</li>
<li xml:lang="sa">स्टिकर पैक - MSC2545</li>
<li xml:lang="sl">Sticker Packs - MSC2545</li>
<li xml:lang="sv">Sticker Packs - MSC2545</li>
<li xml:lang="ta">ஒட்டி தொகுப்புகள் - MSC2545</li>
@@ -264,7 +246,6 @@
<li xml:lang="fr">Événements de lieu - MSC3488</li>
<li xml:lang="gl">Localización de eventos — MSC3488</li>
<li xml:lang="he">אירועי מקום - MSC3488</li>
<li xml:lang="hi">स्थान घटनाएँ - MSC3488</li>
<li xml:lang="hu">Események helyadatai - MSC3488</li>
<li xml:lang="ia">Eventos de Location - MSC3488</li>
<li xml:lang="it">Località eventi - MSC3488</li>
@@ -276,7 +257,6 @@
<li xml:lang="pl">Wydarzenia w miejscach - MSC3488</li>
<li xml:lang="pt">Eventos com Localizações - MSC3488</li>
<li xml:lang="ru">События местоположения — MSC3488</li>
<li xml:lang="sa">स्थान घटनाएँ - MSC3488</li>
<li xml:lang="sl">Location Events - MSC3488</li>
<li xml:lang="sv">Location Events - MSC3488</li>
<li xml:lang="ta">இட நிகழ்வுகள் - MSC3488</li>
@@ -313,7 +293,6 @@
<value key="KDE::windows_store::StoreLogoSquare">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/storelogo-1080x1080.png</value>
<value key="KDE::windows_store::Icon">https://invent.kde.org/network/neochat/-/raw/master/icons/300-apps-neochat.png</value>
<value key="KDE::windows_store::PromotionalArt16x9">https://invent.kde.org/network/neochat/-/raw/master/icons/windows/promoimage-1920x1080.png</value>
<value key="KDE::supporters">Tanguy Fardet;[dabe](https://freeradical.zone/@dabe);[lengau](https://mastodon.world/@lengau);Joshua Strobl;Stuart Turton</value>
</custom>
<launchable type="desktop-id">org.kde.neochat.desktop</launchable>
<screenshots>
@@ -333,7 +312,6 @@
<caption xml:lang="fr">Vue principale avec la liste des salons ainsi que des informations sur les salons et forums de discussions</caption>
<caption xml:lang="gl">Vista principal coa lista de salas, a charla, e información da sala.</caption>
<caption xml:lang="he">תצוגה ראשית עם רשימת חדרים, צ׳אט ופרטי חדר</caption>
<caption xml:lang="hi">कमरे की सूची, चैट और कमरे की जानकारी के साथ मुख्य दृश्य</caption>
<caption xml:lang="hu">A fő nézet a szobalistával, csevegéssel és szobainformációkkal</caption>
<caption xml:lang="ia">Vista principal con lista de sala, chat e information de sala</caption>
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
@@ -345,7 +323,6 @@
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
<caption xml:lang="sa">कक्षसूची, गपशपः, कक्षसूचना च सह मुख्यदृश्यम्</caption>
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
<caption xml:lang="sv">Huvudvy med rumslista, chatt, och rumsinformation</caption>
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
@@ -370,18 +347,15 @@
<caption xml:lang="fr">Découvrez de nouvelles communautés avec les espaces sous Matrix</caption>
<caption xml:lang="gl">Descubra novas comunidades dos espazos de Matrix.</caption>
<caption xml:lang="he">אפשר להיחשף לקהילות חדשות דרך Matrix Spaces</caption>
<caption xml:lang="hi">मैट्रिक्स स्पेस के साथ नए समुदायों की खोज करें</caption>
<caption xml:lang="hu">Fedezzen fel új közösségeket a Matrix Terek segítségével</caption>
<caption xml:lang="ia">Discoperi nove communitate con Matrix Spaces (Spatios de Matrix)</caption>
<caption xml:lang="it">Scopri nuove comunità con Matrix Spaces</caption>
<caption xml:lang="ka">აღმოაჩინეთ ახალი საზოგადოებები Matrix Spaces-თან ერთად</caption>
<caption xml:lang="ko">Matrix 스페이스에서 새로운 커뮤니티 탐험</caption>
<caption xml:lang="lv">Atklājiet jaunas kopienas ar „Matrix“ telpām</caption>
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
<caption xml:lang="nn">Oppdag nye fellesskap med Matrix Spaces</caption>
<caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption>
<caption xml:lang="ru">Поиск новых сообществ с помощью Matrix Spaces</caption>
<caption xml:lang="sa">Matrix Spaces इत्यनेन सह नूतनानां समुदायानाम् अन्वेषणं कुर्वन्तु</caption>
<caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption>
<caption xml:lang="sv">Upptäck nya gemenskaper med Matrix Spaces</caption>
<caption xml:lang="ta">மேட்ரிக்ஸு இடங்களின் மூலம் புதிய சமூகங்களைக் கண்டுபிடிக்கலாம்</caption>
@@ -413,7 +387,6 @@
<caption xml:lang="fr">Vue principale avec la liste des salons ainsi que des informations sur les salons et forums de discussions</caption>
<caption xml:lang="gl">Vista principal coa lista de salas, a charla, e información da sala.</caption>
<caption xml:lang="he">תצוגה ראשית עם רשימת חדרים, צ׳אט ופרטי חדר</caption>
<caption xml:lang="hi">कमरे की सूची, चैट और कमरे की जानकारी के साथ मुख्य दृश्य</caption>
<caption xml:lang="hu">A fő nézet a szobalistával, csevegéssel és szobainformációkkal</caption>
<caption xml:lang="ia">Vista principal con lista de sala, chat e information de sala</caption>
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
@@ -425,7 +398,6 @@
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
<caption xml:lang="pt">A área principal com a lista de salas e com informações sobre a conversa e a sala</caption>
<caption xml:lang="ru">Главное окно со списком комнат, чатом и информацией о комнате</caption>
<caption xml:lang="sa">कक्षसूची, गपशपः, कक्षसूचना च सह मुख्यदृश्यम्</caption>
<caption xml:lang="sl">Glavni pogled s seznamom sob, klepetom in informacijami o sobah</caption>
<caption xml:lang="sv">Huvudvy med rumslista, chatt, och rumsinformation</caption>
<caption xml:lang="ta">அரங்குப்பட்டியல், உரையாடல், மற்றும் அரங்குவிவரங்களைக் கொண்டுள்ள பிரதான காட்சி</caption>
@@ -451,7 +423,6 @@
<caption xml:lang="fr">Écran de connexion</caption>
<caption xml:lang="gl">Pantalla de identificación.</caption>
<caption xml:lang="he">מסך כניסה</caption>
<caption xml:lang="hi">लॉगिन स्क्रीन</caption>
<caption xml:lang="hu">Bejelentkező képernyő</caption>
<caption xml:lang="ia">Schermo de accesso</caption>
<caption xml:lang="it">Schermata di accesso</caption>
@@ -463,7 +434,6 @@
<caption xml:lang="pl">Ekran logowania</caption>
<caption xml:lang="pt">Ecrã de autenticação</caption>
<caption xml:lang="ru">Окно входа</caption>
<caption xml:lang="sa">लॉगिन् स्क्रीन</caption>
<caption xml:lang="sl">Prijavni zaslon</caption>
<caption xml:lang="sv">Inloggningsfönster</caption>
<caption xml:lang="ta">நுழைவுத் திரை</caption>
@@ -477,8 +447,6 @@
<content_attribute id="social-chat">intense</content_attribute>
</content_rating>
<releases>
<release version="25.04.1" date="2025-05-08"/>
<release version="25.04.0" date="2025-04-17"/>
<release version="24.12.3" date="2025-03-06"/>
<release version="24.12.2" date="2025-02-06"/>
<release version="24.12.1" date="2025-01-09"/>
@@ -657,9 +625,6 @@
<url>https://carlschwan.eu/2020/12/23/announcing-neochat-1.0-the-kde-matrix-client/</url>
</release>
</releases>
<requires>
<display_length compare="ge">360</display_length>
</requires>
<branding>
<color type="primary" scheme_preference="light">#a6e4f3</color>
<color type="primary" scheme_preference="dark">#235670</color>

View File

@@ -19,7 +19,6 @@ Name[fi]=NeoChat
Name[fr]=NeoChat
Name[gl]=NeoChat
Name[he]=NeoChat
Name[hi]=नियोचैट
Name[hu]=NeoChat
Name[ia]=Neochat
Name[id]=NeoChat
@@ -37,7 +36,6 @@ Name[pt]=NeoChat
Name[pt_BR]=NeoChat
Name[ro]=NeoChat
Name[ru]=NeoChat
Name[sa]=नवचैट्
Name[sk]=NeoChat
Name[sl]=NeoChat
Name[sv]=NeoChat
@@ -63,7 +61,6 @@ 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
@@ -81,7 +78,6 @@ 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
@@ -104,24 +100,18 @@ 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[lv]=Tērzējiet „Matrix“ tīklā
Comment[ka]=საუბარი Matrix-ზე
Comment[nl]=Chat op Matrix
Comment[pl]=Rozmawiaj na Matriksie
Comment[pt_BR]=Bate papo na 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[x-test]=xxChat on Matrixxx
Comment[zh_CN]=在 Matrix 上聊天
Comment[zh_TW]=在 Matrix 上聊天
MimeType=x-scheme-handler/matrix;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2024-2025 Scarlett Moore <sgmoore@kde.org>
# SPDX-FileCopyrightText: 2024 Scarlett Moore <sgmoore@kde.org>
#
# SPDX-License-Identifier: CC0-1.0
---
@@ -24,15 +24,12 @@ apps:
- network-manager-observe
- password-manager-service
- accounts-service
environment:
QT_PLUGIN_PATH: "$SNAP/usr/lib/$CRAFT_ARCH_TRIPLET/plugins/snap/kf6-core24/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/plugins"
QML_IMPORT_PATH: "$SNAP/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/qml:/snap/kf6-core24/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/qml"
compression: lzo
package-repositories:
- type: apt
ppa: ubuntu-toolchain-r/test
- type: apt
ppa: ubuntu-toolchain-r/test
slots:
session-dbus-interface:
@@ -44,12 +41,11 @@ parts:
olm:
source: https://gitlab.matrix.org/matrix-org/olm.git
source-depth: 1
source-tag: '3.2.16'
source-tag: '3.2.12'
plugin: cmake
cmake-parameters:
- -DCMAKE_BUILD_TYPE=Release
- -DCMAKE_INSTALL_PREFIX=/usr
- -DCMAKE_POLICY_VERSION_MINIMUM=3.5
prime:
- -usr/include
- -usr/lib/*/pkgconfig
@@ -85,7 +81,7 @@ parts:
plugin: cmake
build-environment:
- PATH: /snap/bin:${PATH}
- PKG_CONFIG_PATH: "$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET/pkgconfig:$PKG_CONFIG_PATH"
- PKG_CONFIG_PATH: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET/pkgconfig:$PKG_CONFIG_PATH
cmake-parameters:
- -DCMAKE_INSTALL_PREFIX=/usr
- -DCMAKE_BUILD_TYPE=Release
@@ -140,24 +136,11 @@ parts:
- -usr/lib/*/pkgconfig
- -usr/lib/*/cmake
kunifiedpush:
source: https://invent.kde.org/libraries/kunifiedpush.git
plugin: cmake
build-environment:
- PATH: /snap/bin:${PATH}
- PYTHONPATH: ${CRAFT_STAGE}/lib/python3.12/site-packages:${CRAFT_STAGE}/usr/lib/python3/dist-packages
- LD_LIBRARY_PATH: "/snap/mesa-2404/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:$CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/kde-qt6-core24-sdk/current/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libproxy:$LD_LIBRARY_PATH"
cmake-parameters:
- -DCMAKE_INSTALL_PREFIX=/usr
- -DCMAKE_BUILD_TYPE=Release
- -DBUILD_TESTING=OFF
neochat:
after:
- qtkeychain
- libquotient
- kquickimageeditor
- kunifiedpush
parse-info:
- usr/share/metainfo/org.kde.neochat.appdata.xml
source: .

View File

@@ -18,8 +18,8 @@ add_library(neochat STATIC
models/customemojimodel.h
clipboard.cpp
clipboard.h
models/timelinemessagemodel.cpp
models/timelinemessagemodel.h
models/messageeventmodel.cpp
models/messageeventmodel.h
models/messagefiltermodel.cpp
models/messagefiltermodel.h
models/roomlistmodel.cpp
@@ -107,6 +107,8 @@ add_library(neochat STATIC
models/imagepacksmodel.h
events/imagepackevent.cpp
events/imagepackevent.h
events/joinrulesevent.cpp
events/joinrulesevent.h
models/reactionmodel.cpp
models/reactionmodel.h
delegatesizehelper.cpp
@@ -124,6 +126,12 @@ add_library(neochat STATIC
registration.cpp
neochatconnection.cpp
neochatconnection.h
jobs/neochatdeactivateaccountjob.cpp
jobs/neochatdeactivateaccountjob.h
jobs/neochatdeletedevicejob.cpp
jobs/neochatdeletedevicejob.h
jobs/neochatchangepasswordjob.cpp
jobs/neochatchangepasswordjob.h
jobs/neochatgetcommonroomsjob.cpp
jobs/neochatgetcommonroomsjob.h
mediasizehelper.cpp
@@ -168,6 +176,8 @@ add_library(neochat STATIC
models/threepidmodel.h
threepidaddhelper.cpp
threepidaddhelper.h
jobs/neochatadd3pidjob.cpp
jobs/neochatadd3pidjob.h
identityserverhelper.cpp
identityserverhelper.h
enums/powerlevel.cpp
@@ -184,18 +194,6 @@ add_library(neochat STATIC
models/threadmodel.h
enums/messagetype.h
messagecomponent.h
enums/roomsortparameter.cpp
enums/roomsortparameter.h
models/roomsortparametermodel.cpp
models/roomsortparametermodel.h
models/messagemodel.cpp
models/messagemodel.h
models/messagecontentfiltermodel.cpp
models/messagecontentfiltermodel.h
models/pinnedmessagemodel.cpp
models/pinnedmessagemodel.h
models/commonroomsmodel.cpp
models/commonroomsmodel.h
)
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
@@ -215,7 +213,7 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/AccountMenu.qml
qml/ExploreComponent.qml
qml/ExploreComponentMobile.qml
qml/RoomContextMenu.qml
qml/ContextMenu.qml
qml/CollapsedRoomDelegate.qml
qml/RoomDelegate.qml
qml/RoomListPage.qml
@@ -251,7 +249,6 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/MessageSourceSheet.qml
qml/ConfirmEncryptionDialog.qml
qml/RoomSearchPage.qml
qml/RoomPinnedMessagesPage.qml
qml/LocationChooser.qml
qml/TimelineView.qml
qml/InvitationView.qml
@@ -330,10 +327,10 @@ if(WIN32)
endif()
ecm_qt_declare_logging_category(neochat
HEADER "messagemodel_logging.h"
IDENTIFIER "Message"
CATEGORY_NAME "org.kde.neochat.messagemodel"
DESCRIPTION "Neochat: messagemodel"
HEADER "messageeventmodel_logging.h"
IDENTIFIER "MessageEvent"
CATEGORY_NAME "org.kde.neochat.messageeventmodel"
DESCRIPTION "Neochat: messageeventmodel"
DEFAULT_SEVERITY Info
EXPORT NEOCHAT
)
@@ -540,9 +537,6 @@ if(ANDROID)
"list-remove-symbolic"
"edit-delete"
"user-home-symbolic"
"pin-symbolic"
"kt-restore-defaults-symbolic"
"user-symbolic"
)
ecm_add_android_apk(neochat-app ANDROID_DIR ${CMAKE_SOURCE_DIR}/android)
else()

View File

@@ -237,6 +237,20 @@ QQC2.Control {
onFormattingSelected: _private.formatText(format, selectionStart, selectionEnd)
}
Keys.onDeletePressed: {
if (selectedText.length > 0) {
remove(selectionStart, selectionEnd);
} else {
remove(cursorPosition, cursorPosition + 1);
}
if (textField.text == selectedText || textField.text.length <= 1) {
root.currentRoom.sendTypingNotification(false);
repeatTimer.stop();
}
if (quickFormatBar.visible) {
quickFormatBar.close();
}
}
Keys.onEnterPressed: event => {
const controlIsPressed = event.modifiers & Qt.ControlModifier;
if (completionMenu.visible) {
@@ -275,7 +289,7 @@ QQC2.Control {
completionMenu.decrementIndex();
} else if (event.key === Qt.Key_Down && completionMenu.visible) {
completionMenu.incrementIndex();
} else if (event.key === Qt.Key_Backspace || event.key === Qt.Key_Delete) {
} else if (event.key === Qt.Key_Backspace) {
if (textField.text == selectedText || textField.text.length <= 1) {
root.currentRoom.sendTypingNotification(false);
repeatTimer.stop();

View File

@@ -11,8 +11,6 @@
#include "neochatroom.h"
#include "texthandler.h"
using namespace Qt::StringLiterals;
ChatBarCache::ChatBarCache(QObject *parent)
: QObject(parent)
{
@@ -57,7 +55,7 @@ QString ChatBarCache::formatMentions() const
}
formattedText = formattedText.replace(mention.cursor.anchor(),
mention.cursor.position() - mention.cursor.anchor(),
u"[%1](https://matrix.to/#/%2)"_s.arg(mention.text.toHtmlEscaped(), mention.id));
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text.toHtmlEscaped(), mention.id));
}
return formattedText;
@@ -256,7 +254,7 @@ void ChatBarCache::updateMentions(QQuickTextDocument *document, ChatDocumentHand
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
if (const auto &roomMessageEvent = &*event->viewAs<Quotient::RoomMessageEvent>()) {
// Replaces the mentions that are baked into the HTML but plaintext in the original markdown
const QRegularExpression re(uR"lit(<a\shref="https:\/\/matrix.to\/#\/([\S]*)"\s?>([\S]*)<\/a>)lit"_s);
const QRegularExpression re(QStringLiteral(R"lit(<a\shref="https:\/\/matrix.to\/#\/([\S]*)"\s?>([\S]*)<\/a>)lit"));
m_mentions.clear();
@@ -309,7 +307,7 @@ void ChatBarCache::postMessage()
}
const auto result = ActionsModel::handleAction(room, this);
if (!result.second.has_value()) {
if (!result.first.has_value()) {
return;
}
@@ -321,24 +319,8 @@ void ChatBarCache::postMessage()
return;
}
bool isReply = !replyId().isEmpty();
const auto replyIt = room->findInTimeline(replyId());
if (replyIt == room->historyEdge()) {
isReply = false;
}
auto content = std::make_unique<Quotient::EventContent::TextContent>(sendText, u"text/html"_s);
std::optional<Quotient::EventRelation> relatesTo = std::nullopt;
if (!threadId().isEmpty()) {
relatesTo = Quotient::EventRelation::replyInThread(threadId(), !isReply, isReply ? replyId() : threadId());
} else if (!editId().isEmpty()) {
relatesTo = Quotient::EventRelation::replace(editId());
} else if (isReply) {
relatesTo = Quotient::EventRelation::replyTo(replyId());
}
room->post<Quotient::RoomMessageEvent>(text(), *std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result), std::move(content), relatesTo);
auto type = std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result);
room->postMessage(text(), sendText, type ? *type : Quotient::RoomMessageEvent::MsgType::Text, replyId(), editId(), threadId());
clearCache();
}

View File

@@ -16,8 +16,6 @@
#include "chatdocumenthandler_logging.h"
using namespace Qt::StringLiterals;
class SyntaxHighlighter : public QSyntaxHighlighter
{
public:
@@ -222,18 +220,15 @@ void ChatDocumentHandler::complete(int index)
return;
}
// Ensure we only search for the beginning of the current completion identifier
const auto fromIndex = qMax(completionStartIndex(), 0);
if (m_completionModel->autoCompletionType() == CompletionModel::User) {
auto name = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::DisplayNameRole).toString();
auto id = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
auto text = getText();
auto at = text.indexOf(QLatin1Char('@'), fromIndex);
auto at = text.lastIndexOf(QLatin1Char('@'), cursorPosition() - 1);
QTextCursor cursor(document()->textDocument());
cursor.setPosition(at);
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
cursor.insertText(name + u" "_s);
cursor.insertText(name + QStringLiteral(" "));
cursor.setPosition(at);
cursor.setPosition(cursor.position() + name.size(), QTextCursor::KeepAnchor);
cursor.setKeepPositionOnInsert(true);
@@ -242,19 +237,19 @@ void ChatDocumentHandler::complete(int index)
} else if (m_completionModel->autoCompletionType() == CompletionModel::Command) {
auto command = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedTextRole).toString();
auto text = getText();
auto at = text.indexOf(QLatin1Char('/'), fromIndex);
auto at = text.lastIndexOf(QLatin1Char('/'));
QTextCursor cursor(document()->textDocument());
cursor.setPosition(at);
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
cursor.insertText(u"/%1 "_s.arg(command));
cursor.insertText(QStringLiteral("/%1 ").arg(command));
} else if (m_completionModel->autoCompletionType() == CompletionModel::Room) {
auto alias = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
auto text = getText();
auto at = text.indexOf(QLatin1Char('#'), fromIndex);
auto at = text.lastIndexOf(QLatin1Char('#'), cursorPosition() - 1);
QTextCursor cursor(document()->textDocument());
cursor.setPosition(at);
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
cursor.insertText(alias + u" "_s);
cursor.insertText(alias + QStringLiteral(" "));
cursor.setPosition(at);
cursor.setPosition(cursor.position() + alias.size(), QTextCursor::KeepAnchor);
cursor.setKeepPositionOnInsert(true);
@@ -263,7 +258,7 @@ void ChatDocumentHandler::complete(int index)
} else if (m_completionModel->autoCompletionType() == CompletionModel::Emoji) {
auto shortcode = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedTextRole).toString();
auto text = getText();
auto at = text.indexOf(QLatin1Char(':'), fromIndex);
auto at = text.lastIndexOf(QLatin1Char(':'));
QTextCursor cursor(document()->textDocument());
cursor.setPosition(at);
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);

View File

@@ -14,8 +14,6 @@
#include <QStandardPaths>
#include <QUrl>
using namespace Qt::StringLiterals;
Clipboard::Clipboard(QObject *parent)
: QObject(parent)
, m_clipboard(QGuiApplication::clipboard())
@@ -35,14 +33,14 @@ QImage Clipboard::image() const
QString Clipboard::saveImage(QString localPath) const
{
QString imageDir(u"%1/screenshots"_s.arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)));
QString imageDir(QStringLiteral("%1/screenshots").arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)));
if (!QDir().exists(imageDir)) {
QDir().mkdir(imageDir);
}
if (localPath.isEmpty()) {
localPath = u"file://%1/%2.png"_s.arg(imageDir, QDateTime::currentDateTime().toString(u"yyyy-MM-dd-hh-mm-ss"_s));
localPath = QStringLiteral("file://%1/%2.png").arg(imageDir, QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd-hh-mm-ss")));
}
QUrl url(localPath);
if (!url.isLocalFile()) {
@@ -63,7 +61,7 @@ QString Clipboard::saveImage(QString localPath) const
void Clipboard::saveText(QString message)
{
static QRegularExpression re(u"<[^>]*>"_s);
static QRegularExpression re(QStringLiteral("<[^>]*>"));
auto *mimeData = new QMimeData; // ownership is transferred to clipboard
mimeData->setHtml(message);
mimeData->setText(message.replace(re, QString()));

View File

@@ -8,6 +8,7 @@
ColorSchemer::ColorSchemer(QObject *parent)
: QObject(parent)
, c(new KColorSchemeManager(this))
{
}
@@ -17,17 +18,17 @@ ColorSchemer::~ColorSchemer()
QAbstractItemModel *ColorSchemer::model() const
{
return KColorSchemeManager::instance()->model();
return c->model();
}
void ColorSchemer::apply(int idx)
{
KColorSchemeManager::instance()->activateScheme(KColorSchemeManager::instance()->model()->index(idx, 0));
c->activateScheme(c->model()->index(idx, 0));
}
int ColorSchemer::indexForCurrentScheme()
{
return KColorSchemeManager::instance()->indexForSchemeId(KColorSchemeManager::instance()->activeSchemeId()).row();
return c->indexForSchemeId(c->activeSchemeId()).row();
}
#include "moc_colorschemer.cpp"

View File

@@ -49,4 +49,7 @@ public:
* @sa KColorScheme
*/
Q_INVOKABLE int indexForCurrentScheme();
private:
KColorSchemeManager *c;
};

View File

@@ -63,7 +63,7 @@ Controller::Controller(QObject *parent)
});
} else {
auto c = new NeoChatConnection(this);
c->assumeIdentity(u"@user:localhost:1234"_s, u"device_1234"_s, u"token_1234"_s);
c->assumeIdentity(QStringLiteral("@user:localhost:1234"), QStringLiteral("device_1234"), QStringLiteral("token_1234"));
connect(c, &Connection::connected, this, [c, this]() {
m_accountRegistry.add(c);
c->syncLoop();
@@ -117,7 +117,7 @@ Controller::Controller(QObject *parent)
});
#ifdef HAVE_KUNIFIEDPUSH
auto connector = new KUnifiedPush::Connector(u"org.kde.neochat"_s);
auto connector = new KUnifiedPush::Connector(QStringLiteral("org.kde.neochat"));
connect(connector, &KUnifiedPush::Connector::endpointChanged, this, [this](const QString &endpoint) {
m_endpoint = endpoint;
for (auto &quotientConnection : m_accountRegistry) {
@@ -186,7 +186,7 @@ void Controller::dropConnection(NeoChatConnection *c)
void Controller::invokeLogin()
{
const auto accounts = SettingsGroup("Accounts"_L1).childGroups();
const auto accounts = SettingsGroup("Accounts"_ls).childGroups();
for (const auto &accountId : accounts) {
AccountSettings account{accountId};
m_accountsLoading += accountId;
@@ -287,7 +287,7 @@ bool Controller::supportSystemTray() const
return false;
#else
auto de = QString::fromLatin1(qgetenv("XDG_CURRENT_DESKTOP"));
return de != u"GNOME"_s && de != u"Pantheon"_s;
return de != QStringLiteral("GNOME") && de != QStringLiteral("Pantheon");
#endif
}
@@ -340,7 +340,7 @@ void Controller::setActiveConnection(NeoChatConnection *connection)
void Controller::listenForNotifications()
{
#ifdef HAVE_KUNIFIEDPUSH
auto connector = new KUnifiedPush::Connector(u"org.kde.neochat"_s);
auto connector = new KUnifiedPush::Connector(QStringLiteral("org.kde.neochat"));
auto timer = new QTimer();
connect(timer, &QTimer::timeout, qGuiApp, &QGuiApplication::quit);
@@ -369,19 +369,19 @@ void Controller::updateBadgeNotificationCount(NeoChatConnection *connection, int
#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
#ifndef Q_OS_ANDROID
// copied from Telegram desktop
const auto launcherUrl = "application://org.kde.neochat.desktop"_L1;
const auto launcherUrl = "application://org.kde.neochat.desktop"_ls;
// Gnome requires that count is a 64bit integer
const qint64 counterSlice = std::min(count, 9999);
QVariantMap dbusUnityProperties;
if (counterSlice > 0) {
dbusUnityProperties["count"_L1] = counterSlice;
dbusUnityProperties["count-visible"_L1] = true;
dbusUnityProperties["count"_ls] = counterSlice;
dbusUnityProperties["count-visible"_ls] = true;
} else {
dbusUnityProperties["count-visible"_L1] = false;
dbusUnityProperties["count-visible"_ls] = false;
}
auto signal = QDBusMessage::createSignal("/com/canonical/unity/launcherentry/neochat"_L1, "com.canonical.Unity.LauncherEntry"_L1, "Update"_L1);
auto signal = QDBusMessage::createSignal("/com/canonical/unity/launcherentry/neochat"_ls, "com.canonical.Unity.LauncherEntry"_ls, "Update"_ls);
signal.setArguments({launcherUrl, dbusUnityProperties});
@@ -430,7 +430,7 @@ void Controller::removeConnection(const QString &userId)
}
if (m_connectionsLoading.contains(userId) && m_connectionsLoading[userId]) {
auto connection = m_connectionsLoading[userId];
SettingsGroup("Accounts"_L1).remove(userId);
SettingsGroup("Accounts"_ls).remove(userId);
}
}

View File

@@ -11,6 +11,7 @@
#include <Quotient/accountregistry.h>
class TrayIcon;
class QQuickTextDocument;
namespace QKeychain
{

View File

@@ -33,28 +33,4 @@ ColumnLayout {
}
}
}
FormCard.FormCard {
FormCard.FormSwitchDelegate {
id: showAccessTokenCheckbox
text: i18nc("@info", "Show Access Token")
description: i18n("This should not be shared with anyone, even other users. This token gives full access to your account.")
}
FormCard.FormTextDelegate {
text: i18nc("@info", "Access Token")
description: root.connection.accessToken
visible: showAccessTokenCheckbox.checked
contentItem.children: QQC2.Button {
text: i18nc("@action:button", "Copy access token to clipboard")
icon.name: "edit-copy"
display: QQC2.AbstractButton.IconOnly
onClicked: Clipboard.saveText(root.connection.accessToken)
QQC2.ToolTip.text: text
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
QQC2.ToolTip.visible: hovered
}
}
}
}

View File

@@ -21,22 +21,19 @@ ColumnLayout {
title: i18nc("@title", "Choose Room")
}
FormCard.FormCard {
FormCard.FormButtonDelegate {
text: root.room?.displayNameForHtml ?? i18nc("@info", "No room selected")
description: i18nc("@info", "Click to choose a room");
onClicked: {
let dialog = root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), {
connection: root.connection,
}, {
title: i18nc("@title:dialog", "Choose Room"),
width: Kirigami.Units.gridUnit * 24
});
dialog.chosen.connect(id => root.room = root.connection.room(id))
}
FormCard.FormComboBoxDelegate {
id: roomComboBox
text: i18n("Room")
textRole: "escapedDisplayName"
valueRole: "roomId"
displayText: RoomManager.roomListModel.data(RoomManager.roomListModel.index(currentIndex, 0), RoomListModel.EscapedDisplayNameRole)
model: RoomManager.roomListModel
currentIndex: 0
displayMode: FormCard.FormComboBoxDelegate.Page
Component.onCompleted: currentIndex = RoomManager.roomListModel.rowForRoom(root.room)
onCurrentValueChanged: root.room = RoomManager.roomListModel.roomByAliasOrId(roomComboBox.currentValue)
}
FormCard.FormTextDelegate {
visible: root.room
text: i18n("Room Id: %1", root.room.id)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,8 +4,6 @@
#include "emojitones.h"
#include "models/emojimodel.h"
using namespace Qt::StringLiterals;
QMultiHash<QString, QVariant> EmojiTones::_tones = {
#include "emojitones_data.h"
};

File diff suppressed because it is too large Load Diff

View File

@@ -13,8 +13,6 @@
#include "events/pollevent.h"
using namespace Qt::StringLiterals;
/**
* @class DelegateType
*
@@ -60,7 +58,7 @@ public:
return Message;
}
if (event.isStateEvent()) {
if (event.matrixType() == u"org.matrix.msc3672.beacon_info"_s) {
if (event.matrixType() == QStringLiteral("org.matrix.msc3672.beacon_info")) {
return Message;
}
return State;

View File

@@ -13,8 +13,6 @@
#include "events/pollevent.h"
using namespace Qt::StringLiterals;
/**
* @class MessageComponentType
*
@@ -50,17 +48,11 @@ public:
LiveLocation, /**< The initial event of a shared live location (i.e., the place where this is supposed to be shown in the timeline). */
Encrypted, /**< An encrypted message that cannot be decrypted. */
Reply, /**< A component to show a replied-to message. */
Reaction, /**< A component to show the reactions to this message. */
LinkPreview, /**< A preview of a URL in the message. */
LinkPreviewLoad, /**< A loading dialog for a link preview. */
ChatBar, /**< A text edit for editing a message. */
ThreadRoot, /**< The root message of the thread. */
ThreadBody, /**< The other messages in the thread. */
ReplyButton, /**< A button to reply in the current thread. */
FetchButton, /**< A button to fetch more messages in the current thread. */
Verification, /**< A user verification session start message. */
Loading, /**< The component is loading. */
Separator, /**< A horizontal separator. */
Other, /**< Anything that cannot be classified as another type. */
};
Q_ENUM(Type);
@@ -100,7 +92,7 @@ public:
return MessageComponentType::Image;
}
if (event.isStateEvent()) {
if (event.matrixType() == u"org.matrix.msc3672.beacon_info"_s) {
if (event.matrixType() == QStringLiteral("org.matrix.msc3672.beacon_info")) {
return MessageComponentType::LiveLocation;
}
return MessageComponentType::Other;
@@ -128,10 +120,10 @@ public:
*/
static Type typeForTag(const QString &tag)
{
if (tag == u"pre"_s || tag == u"pre"_s) {
if (tag == QLatin1String("pre") || tag == QLatin1String("pre")) {
return Code;
}
if (tag == u"blockquote"_s) {
if (tag == QLatin1String("blockquote")) {
return Quote;
}
return Text;

View File

@@ -10,8 +10,6 @@
#include <KLocalizedString>
using namespace Qt::StringLiterals;
class NeoChatRoomType : public QObject
{
Q_OBJECT
@@ -77,19 +75,19 @@ public:
{
switch (category) {
case NeoChatRoomType::Invited:
return u"user-invisible"_s;
return QStringLiteral("user-invisible");
case NeoChatRoomType::Favorite:
return u"favorite"_s;
return QStringLiteral("favorite");
case NeoChatRoomType::Direct:
return u"dialog-messages"_s;
return QStringLiteral("dialog-messages");
case NeoChatRoomType::Normal:
return u"group"_s;
return QStringLiteral("group");
case NeoChatRoomType::Deprioritized:
return u"object-order-lower"_s;
return QStringLiteral("object-order-lower");
case NeoChatRoomType::Space:
return u"group"_s;
return QStringLiteral("group");
default:
return u"tools-report-bug"_s;
return QStringLiteral("tools-report-bug");
}
}
};

View File

@@ -6,8 +6,6 @@
#include <QObject>
#include <QQmlEngine>
using namespace Qt::StringLiterals;
/**
* @class PushRuleKind
*
@@ -44,15 +42,15 @@ public:
{
switch (kind) {
case Kind::Override:
return u"override"_s;
return QLatin1String("override");
case Kind::Content:
return u"content"_s;
return QLatin1String("content");
case Kind::Room:
return u"room"_s;
return QLatin1String("room");
case Kind::Sender:
return u"sender"_s;
return QLatin1String("sender");
case Kind::Underride:
return u"underride"_s;
return QLatin1String("underride");
default:
return {};
}
@@ -172,15 +170,15 @@ public:
{
switch (section) {
case Section::Master:
return u"Master"_s;
return QLatin1String("Master");
case Section::Room:
return u"Room Notifications"_s;
return QLatin1String("Room Notifications");
case Section::Mentions:
return u"@Mentions"_s;
return QLatin1String("@Mentions");
case Section::Keywords:
return u"Keywords"_s;
return QLatin1String("Keywords");
case Section::Invites:
return u"Invites"_s;
return QLatin1String("Invites");
default:
return {};
}

View File

@@ -1,162 +0,0 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "roomsortparameter.h"
#include <algorithm>
#include "neochatconfig.h"
#include "neochatroom.h"
namespace
{
template<typename T>
int typeCompare(T left, T right)
{
return left == right ? 0 : left > right ? 1 : -1;
}
template<>
int typeCompare<QString>(QString left, QString right)
{
return left.localeAwareCompare(right);
}
static const QList<RoomSortParameter::Parameter> allSortPriorities = {
RoomSortParameter::AlphabeticalAscending,
RoomSortParameter::AlphabeticalDescending,
RoomSortParameter::HasUnread,
RoomSortParameter::MostUnread,
RoomSortParameter::HasHighlight,
RoomSortParameter::MostHighlights,
RoomSortParameter::LastActive,
};
static const QList<RoomSortParameter::Parameter> alphabeticalSortPriorities = {
RoomSortParameter::AlphabeticalAscending,
};
static const QList<RoomSortParameter::Parameter> activitySortPriorities = {
RoomSortParameter::HasHighlight,
RoomSortParameter::MostHighlights,
RoomSortParameter::HasUnread,
RoomSortParameter::MostUnread,
RoomSortParameter::LastActive,
};
static const QList<RoomSortParameter::Parameter> lastMessageSortPriorities = {
RoomSortParameter::LastActive,
};
}
QList<RoomSortParameter::Parameter> RoomSortParameter::allParameterList()
{
return allSortPriorities;
}
QList<RoomSortParameter::Parameter> RoomSortParameter::currentParameterList()
{
QList<RoomSortParameter::Parameter> configParamList;
switch (static_cast<NeoChatConfig::EnumSortOrder::type>(NeoChatConfig::sortOrder())) {
case NeoChatConfig::EnumSortOrder::Activity:
configParamList = activitySortPriorities;
break;
case NeoChatConfig::EnumSortOrder::Alphabetical:
configParamList = alphabeticalSortPriorities;
break;
case NeoChatConfig::EnumSortOrder::LastMessage:
configParamList = lastMessageSortPriorities;
break;
case NeoChatConfig::EnumSortOrder::Custom: {
const auto intList = NeoChatConfig::customSortOrder();
std::transform(intList.constBegin(), intList.constEnd(), std::back_inserter(configParamList), [](int param) {
return static_cast<Parameter>(param);
});
break;
}
default:
break;
}
if (configParamList.isEmpty()) {
return activitySortPriorities;
}
return configParamList;
}
void RoomSortParameter::saveNewParameterList(const QList<Parameter> &newList)
{
QList<int> intList;
std::transform(newList.constBegin(), newList.constEnd(), std::back_inserter(intList), [](Parameter param) {
return static_cast<int>(param);
});
NeoChatConfig::setCustomSortOrder(intList);
NeoChatConfig::setSortOrder(NeoChatConfig::EnumSortOrder::Custom);
NeoChatConfig::self()->save();
}
int RoomSortParameter::compareParameter(Parameter parameter, NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
{
switch (parameter) {
case AlphabeticalAscending:
return compareParameter<AlphabeticalAscending>(leftRoom, rightRoom);
case AlphabeticalDescending:
return compareParameter<AlphabeticalDescending>(leftRoom, rightRoom);
case HasUnread:
return compareParameter<HasUnread>(leftRoom, rightRoom);
case MostUnread:
return compareParameter<MostUnread>(leftRoom, rightRoom);
case HasHighlight:
return compareParameter<HasHighlight>(leftRoom, rightRoom);
case MostHighlights:
return compareParameter<MostHighlights>(leftRoom, rightRoom);
case LastActive:
return compareParameter<LastActive>(leftRoom, rightRoom);
default:
return 0;
}
}
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalAscending>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
{
return -typeCompare(leftRoom->displayName(), rightRoom->displayName());
}
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalDescending>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
{
return typeCompare(leftRoom->displayName(), rightRoom->displayName());
}
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::HasUnread>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
{
return typeCompare(leftRoom->contextAwareNotificationCount() > 0, rightRoom->contextAwareNotificationCount() > 0);
}
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::MostUnread>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
{
return typeCompare(leftRoom->contextAwareNotificationCount(), rightRoom->contextAwareNotificationCount());
}
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::HasHighlight>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
{
const auto leftHighlight = leftRoom->highlightCount() > 0 && leftRoom->contextAwareNotificationCount() > 0;
const auto rightHighlight = rightRoom->highlightCount() > 0 && rightRoom->contextAwareNotificationCount() > 0;
return typeCompare(leftHighlight, rightHighlight);
}
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::MostHighlights>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
{
return typeCompare(int(leftRoom->highlightCount()), int(rightRoom->highlightCount()));
}
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::LastActive>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
{
return typeCompare(leftRoom->lastActiveTime(), rightRoom->lastActiveTime());
}

View File

@@ -1,142 +0,0 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QObject>
#include <QQmlEngine>
#include <KLocalizedString>
class NeoChatRoom;
/**
* @class RoomSortParameter
*
* A class with the Parameter enum for room sort parameters.
*/
class RoomSortParameter : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
public:
/**
* @brief Defines the available sort parameters.
*
* @note All values are specifically numbered as they should never change even
* if new options are later added. This is because they are stored in
* the config as ints and changing would break someones config on upgrade.
*/
enum Parameter {
AlphabeticalAscending = 0,
AlphabeticalDescending = 1,
HasUnread = 2,
MostUnread = 3,
HasHighlight = 4,
MostHighlights = 5,
LastActive = 6,
};
Q_ENUM(Parameter)
/**
* @brief Translate the Parameter enum value to a human readable name string.
*
* @sa Parameter
*/
Q_INVOKABLE static QString parameterName(Parameter parameter)
{
switch (parameter) {
case Parameter::AlphabeticalAscending:
return i18nc("As in sorting alphabetically with A first and Z last", "Alphabetical Ascending");
case Parameter::AlphabeticalDescending:
return i18nc("As in sorting alphabetically with Z first and A last", "Alphabetical Descending");
case Parameter::HasUnread:
return i18nc("As in sorting rooms with unread message above those without", "Has Unread Messages");
case Parameter::MostUnread:
return i18nc("As in sorting rooms with the most unread messages higher", "Most Unread Messages");
case Parameter::HasHighlight:
return i18nc("As in sorting rooms with highlighted message above those without", "Has Highlighted Messages");
case Parameter::MostHighlights:
return i18nc("As in sorting rooms with the most highlighted messages higher", "Most Highlighted Messages");
case Parameter::LastActive:
return i18nc("As in sorting the chat room with the newest meassage first", "Last Active");
default:
return {};
}
};
/**
* @brief Translate the Parameter enum value to a human readable description string.
*
* @sa Parameter
*/
Q_INVOKABLE static QString parameterDescription(Parameter parameter)
{
switch (parameter) {
case Parameter::AlphabeticalAscending:
return i18nc("@info", "Room names closer to A alphabetically are higher");
case Parameter::AlphabeticalDescending:
return i18nc("@info", "Room names closer to Z alphabetically are higher");
case Parameter::HasUnread:
return i18nc("@info", "Rooms with unread messages are higher");
case Parameter::MostUnread:
return i18nc("@info", "Rooms with the most unread message are higher");
case Parameter::HasHighlight:
return i18nc("@info", "Rooms with highlighted messages are higher");
case Parameter::MostHighlights:
return i18nc("@info", "Rooms with the most highlighted messages are higher");
case Parameter::LastActive:
return i18nc("@info", "Rooms with the newer messages are higher");
default:
return {};
}
};
/**
* @brief List of all available Parameter sort orders.
*/
static QList<Parameter> allParameterList();
/**
* @brief The current Parameter sort order list.
*/
static QList<Parameter> currentParameterList();
/**
* @brief Save the give Parameter sort order list as the custom sort order.
*/
static void saveNewParameterList(const QList<Parameter> &newList);
/**
* @brief Compare the given parameter of the two given rooms.
*
* @return 0 if they are equal, 1 if the left is greater and -1 if the right is greater.
*
* @sa Parameter
*/
static int compareParameter(Parameter parameter, NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
private:
template<Parameter parameter>
static int compareParameter(NeoChatRoom *, NeoChatRoom *)
{
return false;
}
};
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalAscending>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalDescending>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::HasUnread>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::MostUnread>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::HasHighlight>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::MostHighlights>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::LastActive>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);

View File

@@ -49,6 +49,16 @@ Q_DECLARE_FLAGS(MemberChanges, MemberChange)
Q_DECLARE_OPERATORS_FOR_FLAGS(MemberChanges)
};
QString EventHandler::id(const Quotient::RoomEvent *event)
{
if (event == nullptr) {
qCWarning(EventHandling) << "id called with event set to nullptr.";
return {};
}
return !event->id().isEmpty() ? event->id() : event->transactionId();
}
QString EventHandler::authorDisplayName(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool isPending)
{
if (room == nullptr) {
@@ -60,9 +70,9 @@ QString EventHandler::authorDisplayName(const NeoChatRoom *room, const Quotient:
return {};
}
if (is<RoomMemberEvent>(*event) && event->unsignedJson()["prev_content"_L1].toObject().contains("displayname"_L1)
if (is<RoomMemberEvent>(*event) && !event->unsignedJson()[QStringLiteral("prev_content")][QStringLiteral("displayname")].isNull()
&& event->stateKey() == event->senderId()) {
auto previousDisplayName = event->unsignedJson()["prev_content"_L1]["displayname"_L1].toString().toHtmlEscaped();
auto previousDisplayName = event->unsignedJson()[QStringLiteral("prev_content")][QStringLiteral("displayname")].toString().toHtmlEscaped();
if (previousDisplayName.isEmpty()) {
previousDisplayName = event->senderId();
}
@@ -86,39 +96,32 @@ QString EventHandler::singleLineAuthorDisplayname(const NeoChatRoom *room, const
const auto author = isPending ? room->localMember() : room->member(event->senderId());
auto displayName = author.displayName();
displayName.replace(u"<br>\n"_s, u" "_s);
displayName.replace(u"<br>"_s, u" "_s);
displayName.replace(u"<br />\n"_s, u" "_s);
displayName.replace(u"<br />"_s, u" "_s);
displayName.replace(u'\n', u" "_s);
displayName.replace(u'\u2028', u" "_s);
displayName.replace(QStringLiteral("<br>\n"), QStringLiteral(" "));
displayName.replace(QStringLiteral("<br>"), QStringLiteral(" "));
displayName.replace(QStringLiteral("<br />\n"), QStringLiteral(" "));
displayName.replace(QStringLiteral("<br />"), QStringLiteral(" "));
displayName.replace(u'\n', QStringLiteral(" "));
displayName.replace(u'\u2028', QStringLiteral(" "));
return displayName;
}
QDateTime EventHandler::time(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool isPending)
QDateTime EventHandler::time(const Quotient::RoomEvent *event, bool isPending, QDateTime lastUpdated)
{
if (room == nullptr) {
qCWarning(EventHandling) << "time called with room set to nullptr.";
return {};
}
if (event == nullptr) {
qCWarning(EventHandling) << "time called with event set to nullptr.";
return {};
}
if (isPending) {
const auto pendingIt = room->findPendingEvent(event->transactionId());
if (pendingIt != room->pendingEvents().end()) {
return pendingIt->lastUpdated();
}
if (isPending && lastUpdated == QDateTime()) {
qCWarning(EventHandling) << "a value must be provided for lastUpdated for a pending event.";
return {};
}
return event->originTimestamp();
return isPending ? lastUpdated : event->originTimestamp();
}
QString EventHandler::timeString(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool relative, QLocale::FormatType format, bool isPending)
QString EventHandler::timeString(const Quotient::RoomEvent *event, bool relative, QLocale::FormatType format, bool isPending, QDateTime lastUpdated)
{
auto ts = time(room, event, isPending);
auto ts = time(event, isPending, lastUpdated);
if (ts.isValid()) {
if (relative) {
KFormat formatter;
@@ -130,9 +133,9 @@ QString EventHandler::timeString(const NeoChatRoom *room, const Quotient::RoomEv
return {};
}
QString EventHandler::timeString(const NeoChatRoom *room, const Quotient::RoomEvent *event, const QString &format, bool isPending)
QString EventHandler::timeString(const Quotient::RoomEvent *event, const QString &format, bool isPending, const QDateTime &lastUpdated)
{
return time(room, event, isPending).toLocalTime().toString(format);
return time(event, isPending, lastUpdated).toLocalTime().toString(format);
}
bool EventHandler::isHighlighted(const NeoChatRoom *room, const Quotient::RoomEvent *event)
@@ -202,7 +205,7 @@ bool EventHandler::isHidden(const NeoChatRoom *room, const Quotient::RoomEvent *
}
// hide ending live location beacons
if (event->isStateEvent() && event->matrixType() == "org.matrix.msc3672.beacon_info"_L1 && !event->contentJson()["live"_L1].toBool()) {
if (event->isStateEvent() && event->matrixType() == "org.matrix.msc3672.beacon_info"_ls && !event->contentJson()["live"_ls].toBool()) {
return true;
}
@@ -211,7 +214,7 @@ bool EventHandler::isHidden(const NeoChatRoom *room, const Quotient::RoomEvent *
Qt::TextFormat EventHandler::messageBodyInputFormat(const Quotient::RoomMessageEvent &event)
{
if (event.mimeType().name() == "text/plain"_L1) {
if (event.mimeType().name() == "text/plain"_ls) {
return Qt::PlainText;
} else {
return Qt::RichText;
@@ -288,7 +291,7 @@ QString EventHandler::markdownBody(const Quotient::RoomEvent *event)
QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent *event, Qt::TextFormat format, bool stripNewlines)
{
if (event->isRedacted() && !event->isStateEvent()) {
if (event->isRedacted()) {
auto reason = event->redactedBecause()->reason();
return (reason.isEmpty()) ? i18n("<i>[This message was deleted]</i>") : i18n("<i>[This message was deleted: %1]</i>", reason.toHtmlEscaped());
}
@@ -316,8 +319,8 @@ QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent
}
if (prettyPrint) {
subjectName =
u"<a href=\"https://matrix.to/#/%1\" style=\"color: %2\">%3</a>"_s.arg(e.userId(), room->member(e.userId()).color().name(), subjectName);
subjectName = QStringLiteral("<a href=\"https://matrix.to/#/%1\" style=\"color: %2\">%3</a>")
.arg(e.userId(), room->member(e.userId()).color().name(), subjectName);
}
// The below code assumes senderName output in AuthorRole
@@ -383,7 +386,7 @@ QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent
if (e.senderId() == e.userId()) {
return i18n("left the room");
}
if (const auto &reason = e.contentJson()["reason"_L1].toString().toHtmlEscaped(); !reason.isEmpty()) {
if (const auto &reason = e.contentJson()["reason"_ls].toString().toHtmlEscaped(); !reason.isEmpty()) {
return i18n("has put %1 out of the room: %2", subjectName, reason);
}
return i18n("has put %1 out of the room", subjectName);
@@ -398,7 +401,7 @@ QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent
return i18n("self-banned from the room");
}
case Membership::Knock: {
QString reason(e.contentJson()["reason"_L1].toString().toHtmlEscaped());
QString reason(e.contentJson()["reason"_ls].toString().toHtmlEscaped());
return reason.isEmpty() ? i18n("requested an invite") : i18n("requested an invite with reason: %1", reason);
}
default:;
@@ -426,26 +429,26 @@ QString EventHandler::getBody(const NeoChatRoom *room, const Quotient::RoomEvent
},
[prettyPrint](const RoomCreateEvent &e) {
return e.isUpgrade()
? i18n("upgraded the room to version %1", e.version().isEmpty() ? "1"_L1 : (prettyPrint ? e.version().toHtmlEscaped() : e.version()))
: i18n("created the room, version %1", e.version().isEmpty() ? "1"_L1 : (prettyPrint ? e.version().toHtmlEscaped() : e.version()));
? i18n("upgraded the room to version %1", e.version().isEmpty() ? "1"_ls : (prettyPrint ? e.version().toHtmlEscaped() : e.version()))
: i18n("created the room, version %1", e.version().isEmpty() ? "1"_ls : (prettyPrint ? e.version().toHtmlEscaped() : e.version()));
},
[](const RoomPowerLevelsEvent &) {
return i18nc("'power level' means permission level", "changed the power levels for this room");
},
[](const LocationBeaconEvent &e) {
return e.contentJson()["description"_L1].toString();
return e.contentJson()["description"_ls].toString();
},
[](const RoomServerAclEvent &) {
return i18n("changed the server access control lists for this room");
},
[](const WidgetEvent &e) {
if (e.fullJson()["unsigned"_L1]["prev_content"_L1].toObject().isEmpty()) {
return i18nc("[User] added <name> widget", "added %1 widget", e.contentJson()["name"_L1].toString());
if (e.fullJson()["unsigned"_ls]["prev_content"_ls].toObject().isEmpty()) {
return i18nc("[User] added <name> widget", "added %1 widget", e.contentJson()["name"_ls].toString());
}
if (e.contentJson().isEmpty()) {
return i18nc("[User] removed <name> widget", "removed %1 widget", e.fullJson()["unsigned"_L1]["prev_content"_L1]["name"_L1].toString());
return i18nc("[User] removed <name> widget", "removed %1 widget", e.fullJson()["unsigned"_ls]["prev_content"_ls]["name"_ls].toString());
}
return i18nc("[User] configured <name> widget", "configured %1 widget", e.contentJson()["name"_L1].toString());
return i18nc("[User] configured <name> widget", "configured %1 widget", e.contentJson()["name"_ls].toString());
},
[prettyPrint](const StateEvent &e) {
return e.stateKey().isEmpty() ? i18n("updated %1 state", e.matrixType())
@@ -466,7 +469,7 @@ QString EventHandler::getMessageBody(const NeoChatRoom *room, const RoomMessageE
if (fileCaption.isEmpty()) {
fileCaption = event.plainBody();
} else if (fileCaption != event.plainBody()) {
fileCaption = event.plainBody() + " | "_L1 + fileCaption;
fileCaption = event.plainBody() + " | "_ls + fileCaption;
}
textHandler.setData(fileCaption);
return !fileCaption.isEmpty() ? textHandler.handleRecievePlainText(Qt::PlainText, stripNewlines) : i18n("a file");
@@ -482,7 +485,7 @@ QString EventHandler::getMessageBody(const NeoChatRoom *room, const RoomMessageE
textHandler.setData(body);
Qt::TextFormat inputFormat;
if (event.mimeType().name() == "text/plain"_L1) {
if (event.mimeType().name() == "text/plain"_ls) {
inputFormat = Qt::PlainText;
} else {
inputFormat = Qt::RichText;
@@ -505,12 +508,12 @@ QString EventHandler::genericBody(const NeoChatRoom *room, const Quotient::RoomE
qCWarning(EventHandling) << "genericBody called with event set to nullptr.";
return {};
}
if (event->isRedacted() && !event->isStateEvent()) {
if (event->isRedacted()) {
return i18n("<i>[This message was deleted]</i>");
}
const auto sender = room->member(event->senderId());
const auto senderString = u"<a href=\"https://matrix.to/#/%1\">%2</a>"_s.arg(sender.id(), sender.htmlSafeDisplayName());
const auto senderString = QStringLiteral("<a href=\"https://matrix.to/#/%1\">%2</a>").arg(sender.id(), sender.htmlSafeDisplayName());
return switchOnType(
*event,
@@ -639,7 +642,7 @@ QString EventHandler::genericBody(const NeoChatRoom *room, const Quotient::RoomE
return i18n("%1 changed the server access control lists for this room", senderString);
},
[senderString](const WidgetEvent &e) {
if (e.fullJson()["unsigned"_L1]["prev_content"_L1].toObject().isEmpty()) {
if (e.fullJson()["unsigned"_ls]["prev_content"_ls].toObject().isEmpty()) {
return i18n("%1 added a widget", senderString);
}
if (e.contentJson().isEmpty()) {
@@ -666,7 +669,7 @@ QString EventHandler::subtitleText(const NeoChatRoom *room, const Quotient::Room
qCWarning(EventHandling) << "subtitleText called with event set to nullptr.";
return {};
}
return singleLineAuthorDisplayname(room, event) + (event->isStateEvent() ? u" "_s : u": "_s) + plainBody(room, event, true);
return singleLineAuthorDisplayname(room, event) + (event->isStateEvent() ? QLatin1String(" ") : QLatin1String(": ")) + plainBody(room, event, true);
}
QVariantMap EventHandler::mediaInfo(const NeoChatRoom *room, const Quotient::RoomEvent *event)
@@ -697,7 +700,7 @@ QVariantMap EventHandler::getMediaInfoForEvent(const NeoChatRoom *room, const Qu
QVariantMap mediaInfo = getMediaInfoFromFileInfo(room, content.get(), eventId, false, false);
// if filename isn't specifically given, it is in body
// https://spec.matrix.org/latest/client-server-api/#mfile
mediaInfo["filename"_L1] = content->commonInfo().originalName.isEmpty() ? roomMessageEvent->plainBody() : content->commonInfo().originalName;
mediaInfo["filename"_ls] = content->commonInfo().originalName.isEmpty() ? roomMessageEvent->plainBody() : content->commonInfo().originalName;
return mediaInfo;
} else if (event->is<StickerEvent>()) {
@@ -719,80 +722,80 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const NeoChatRoom *room,
QVariantMap mediaInfo;
// Get the mxc URL for the media.
if (!fileContent->url().isValid() || fileContent->url().scheme() != u"mxc"_s || eventId.isEmpty()) {
mediaInfo["source"_L1] = QUrl();
if (!fileContent->url().isValid() || fileContent->url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
mediaInfo["source"_ls] = QUrl();
} else {
QUrl source = room->makeMediaUrl(eventId, fileContent->url());
if (source.isValid()) {
mediaInfo["source"_L1] = source;
mediaInfo["source"_ls] = source;
} else {
mediaInfo["source"_L1] = QUrl();
mediaInfo["source"_ls] = QUrl();
}
}
auto mimeType = fileContent->type();
// Add the MIME type for the media if available.
mediaInfo["mimeType"_L1] = mimeType.name();
mediaInfo["mimeType"_ls] = mimeType.name();
// Add the MIME type icon if available.
mediaInfo["mimeIcon"_L1] = mimeType.iconName();
mediaInfo["mimeIcon"_ls] = mimeType.iconName();
// Add media size if available.
mediaInfo["size"_L1] = fileContent->commonInfo().payloadSize;
mediaInfo["size"_ls] = fileContent->commonInfo().payloadSize;
mediaInfo["isSticker"_L1] = isSticker;
mediaInfo["isSticker"_ls] = isSticker;
// Add parameter depending on media type.
if (mimeType.name().contains(u"image"_s)) {
if (mimeType.name().contains(QStringLiteral("image"))) {
if (auto castInfo = static_cast<const EventContent::ImageContent *>(fileContent)) {
mediaInfo["width"_L1] = castInfo->imageSize.width();
mediaInfo["height"_L1] = castInfo->imageSize.height();
mediaInfo["width"_ls] = castInfo->imageSize.width();
mediaInfo["height"_ls] = castInfo->imageSize.height();
// TODO: Images in certain formats (e.g. WebP) will be erroneously marked as animated, even if they are static.
mediaInfo["animated"_L1] = QMovie::supportedFormats().contains(mimeType.preferredSuffix().toUtf8());
mediaInfo["animated"_ls] = QMovie::supportedFormats().contains(mimeType.preferredSuffix().toUtf8());
QVariantMap tempInfo;
auto thumbnailInfo = getMediaInfoFromTumbnail(room, castInfo->thumbnail, eventId);
if (thumbnailInfo["source"_L1].toUrl().scheme() == "mxc"_L1) {
if (thumbnailInfo["source"_ls].toUrl().scheme() == "mxc"_ls) {
tempInfo = thumbnailInfo;
} else {
QString blurhash = castInfo->originalInfoJson["xyz.amorgan.blurhash"_L1].toString();
QString blurhash = castInfo->originalInfoJson["xyz.amorgan.blurhash"_ls].toString();
if (blurhash.isEmpty()) {
tempInfo["source"_L1] = QUrl();
tempInfo["source"_ls] = QUrl();
} else {
tempInfo["source"_L1] = QUrl("image://blurhash/"_L1 + blurhash);
tempInfo["source"_ls] = QUrl("image://blurhash/"_ls + blurhash);
}
}
mediaInfo["tempInfo"_L1] = tempInfo;
mediaInfo["tempInfo"_ls] = tempInfo;
}
}
if (mimeType.name().contains(u"video"_s)) {
if (mimeType.name().contains(QStringLiteral("video"))) {
if (auto castInfo = static_cast<const EventContent::VideoContent *>(fileContent)) {
mediaInfo["width"_L1] = castInfo->imageSize.width();
mediaInfo["height"_L1] = castInfo->imageSize.height();
mediaInfo["duration"_L1] = castInfo->duration;
mediaInfo["width"_ls] = castInfo->imageSize.width();
mediaInfo["height"_ls] = castInfo->imageSize.height();
mediaInfo["duration"_ls] = castInfo->duration;
if (!isThumbnail) {
QVariantMap tempInfo;
auto thumbnailInfo = getMediaInfoFromTumbnail(room, castInfo->thumbnail, eventId);
if (thumbnailInfo["source"_L1].toUrl().scheme() == "mxc"_L1) {
if (thumbnailInfo["source"_ls].toUrl().scheme() == "mxc"_ls) {
tempInfo = thumbnailInfo;
} else {
QString blurhash = castInfo->originalInfoJson["xyz.amorgan.blurhash"_L1].toString();
QString blurhash = castInfo->originalInfoJson["xyz.amorgan.blurhash"_ls].toString();
if (blurhash.isEmpty()) {
tempInfo["source"_L1] = QUrl();
tempInfo["source"_ls] = QUrl();
} else {
tempInfo["source"_L1] = QUrl("image://blurhash/"_L1 + blurhash);
tempInfo["source"_ls] = QUrl("image://blurhash/"_ls + blurhash);
}
}
mediaInfo["tempInfo"_L1] = tempInfo;
mediaInfo["tempInfo"_ls] = tempInfo;
}
}
}
if (mimeType.name().contains(u"audio"_s)) {
if (mimeType.name().contains(QStringLiteral("audio"))) {
if (auto castInfo = static_cast<const EventContent::AudioContent *>(fileContent)) {
mediaInfo["duration"_L1] = castInfo->duration;
mediaInfo["duration"_ls] = castInfo->duration;
}
}
@@ -803,34 +806,59 @@ QVariantMap EventHandler::getMediaInfoFromTumbnail(const NeoChatRoom *room, cons
{
QVariantMap thumbnailInfo;
if (!thumbnail.url().isValid() || thumbnail.url().scheme() != u"mxc"_s || eventId.isEmpty()) {
thumbnailInfo["source"_L1] = QUrl();
if (!thumbnail.url().isValid() || thumbnail.url().scheme() != QStringLiteral("mxc") || eventId.isEmpty()) {
thumbnailInfo["source"_ls] = QUrl();
} else {
QUrl source = room->makeMediaUrl(eventId, thumbnail.url());
if (source.isValid()) {
thumbnailInfo["source"_L1] = source;
thumbnailInfo["source"_ls] = source;
} else {
thumbnailInfo["source"_L1] = QUrl();
thumbnailInfo["source"_ls] = QUrl();
}
}
auto mimeType = thumbnail.mimeType;
// Add the MIME type for the media if available.
thumbnailInfo["mimeType"_L1] = mimeType.name();
thumbnailInfo["mimeType"_ls] = mimeType.name();
// Add the MIME type icon if available.
thumbnailInfo["mimeIcon"_L1] = mimeType.iconName();
thumbnailInfo["mimeIcon"_ls] = mimeType.iconName();
// Add media size if available.
thumbnailInfo["size"_L1] = thumbnail.payloadSize;
thumbnailInfo["size"_ls] = thumbnail.payloadSize;
thumbnailInfo["width"_L1] = thumbnail.imageSize.width();
thumbnailInfo["height"_L1] = thumbnail.imageSize.height();
thumbnailInfo["width"_ls] = thumbnail.imageSize.width();
thumbnailInfo["height"_ls] = thumbnail.imageSize.height();
return thumbnailInfo;
}
bool EventHandler::hasReply(const Quotient::RoomEvent *event, bool showFallbacks)
{
if (event == nullptr) {
qCWarning(EventHandling) << "hasReply called with event set to nullptr.";
return false;
}
const auto relations = event->contentPart<QJsonObject>("m.relates_to"_ls);
if (!relations.isEmpty()) {
const bool hasReplyRelation = relations.contains("m.in_reply_to"_ls);
bool isFallingBack = relations["is_falling_back"_ls].toBool();
return hasReplyRelation && (showFallbacks ? true : !isFallingBack);
}
return false;
}
QString EventHandler::replyId(const Quotient::RoomEvent *event)
{
if (event == nullptr) {
qCWarning(EventHandling) << "replyId called with event set to nullptr.";
return {};
}
return event->contentJson()["m.relates_to"_ls].toObject()["m.in_reply_to"_ls].toObject()["event_id"_ls].toString();
}
Quotient::RoomMember EventHandler::replyAuthor(const NeoChatRoom *room, const Quotient::RoomEvent *event)
{
if (room == nullptr) {
@@ -849,6 +877,38 @@ Quotient::RoomMember EventHandler::replyAuthor(const NeoChatRoom *room, const Qu
}
}
bool EventHandler::isThreaded(const Quotient::RoomEvent *event)
{
if (event == nullptr) {
qCWarning(EventHandling) << "isThreaded called with event set to nullptr.";
return false;
}
return (event->contentPart<QJsonObject>("m.relates_to"_ls).contains("rel_type"_ls)
&& event->contentPart<QJsonObject>("m.relates_to"_ls)["rel_type"_ls].toString() == "m.thread"_ls)
|| (!event->unsignedPart<QJsonObject>("m.relations"_ls).isEmpty() && event->unsignedPart<QJsonObject>("m.relations"_ls).contains("m.thread"_ls));
}
QString EventHandler::threadRoot(const Quotient::RoomEvent *event)
{
if (event == nullptr) {
qCWarning(EventHandling) << "threadRoot called with event set to nullptr.";
return {};
}
// Get the thread root ID from m.relates_to if it exists.
if (event->contentPart<QJsonObject>("m.relates_to"_ls).contains("rel_type"_ls)
&& event->contentPart<QJsonObject>("m.relates_to"_ls)["rel_type"_ls].toString() == "m.thread"_ls) {
return event->contentPart<QJsonObject>("m.relates_to"_ls)["event_id"_ls].toString();
}
// For thread root events they have an m.relations in the unsigned part with a m.thread object.
// If so return the event ID as it is the root.
if (!event->unsignedPart<QJsonObject>("m.relations"_ls).isEmpty() && event->unsignedPart<QJsonObject>("m.relations"_ls).contains("m.thread"_ls)) {
return id(event);
}
return {};
}
float EventHandler::latitude(const Quotient::RoomEvent *event)
{
if (event == nullptr) {
@@ -856,7 +916,7 @@ float EventHandler::latitude(const Quotient::RoomEvent *event)
return -100.0;
}
const auto geoUri = event->contentJson()["geo_uri"_L1].toString();
const auto geoUri = event->contentJson()["geo_uri"_ls].toString();
if (geoUri.isEmpty()) {
return -100.0; // latitude runs from -90deg to +90deg so -100 is out of range.
}
@@ -871,7 +931,7 @@ float EventHandler::longitude(const Quotient::RoomEvent *event)
return -200.0;
}
const auto geoUri = event->contentJson()["geo_uri"_L1].toString();
const auto geoUri = event->contentJson()["geo_uri"_ls].toString();
if (geoUri.isEmpty()) {
return -200.0; // longitude runs from -180deg to +180deg so -200 is out of range.
}
@@ -886,7 +946,7 @@ QString EventHandler::locationAssetType(const Quotient::RoomEvent *event)
return {};
}
const auto assetType = event->contentJson()["org.matrix.msc3488.asset"_L1].toObject()["type"_L1].toString();
const auto assetType = event->contentJson()["org.matrix.msc3488.asset"_ls].toObject()["type"_ls].toString();
if (assetType.isEmpty()) {
return {};
}

View File

@@ -37,6 +37,14 @@ class NeoChatRoom;
class EventHandler
{
public:
/**
* @brief Return the ID of the event.
*
* Returns the transaction ID if the Matrix ID is empty, which may be the case
* for a pending event.
*/
static QString id(const Quotient::RoomEvent *event);
/**
* @brief Get the display name of the event author.
*
@@ -64,7 +72,7 @@ public:
/**
* @brief Return a QDateTime object for the event timestamp.
*/
static QDateTime time(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool isPending = false);
static QDateTime time(const Quotient::RoomEvent *event, bool isPending = false, QDateTime lastUpdated = {});
/**
* @brief Return a QString for the event timestamp.
@@ -80,11 +88,11 @@ public:
* @param lastUpdated the time the event was last updated locally as this cannot be
* obtained from the event.
*/
static QString timeString(const NeoChatRoom *room,
const Quotient::RoomEvent *event,
static QString timeString(const Quotient::RoomEvent *event,
bool relative,
QLocale::FormatType format = QLocale::ShortFormat,
bool isPending = false);
bool isPending = false,
QDateTime lastUpdated = {});
/**
* @brief Return a QString for the event timestamp.
@@ -98,7 +106,7 @@ public:
* @param lastUpdated the time the event was last updated locally as this cannot be
* obtained from the event.
*/
static QString timeString(const NeoChatRoom *room, const Quotient::RoomEvent *event, const QString &format, bool isPending = false);
static QString timeString(const Quotient::RoomEvent *event, const QString &format, bool isPending = false, const QDateTime &lastUpdated = {});
/**
* @brief Whether the event should be highlighted in the timeline.
@@ -212,6 +220,20 @@ public:
*/
static QVariantMap mediaInfo(const NeoChatRoom *room, const Quotient::RoomEvent *event);
/**
* @brief Whether the event is a reply to another in the timeline.
*
* @param showFallbacks whether message that have is_falling_back set true should
* show the fallback reply. Leave true for non-threaded
* timelines.
*/
static bool hasReply(const Quotient::RoomEvent *event, bool showFallbacks = true);
/**
* @brief Return the Matrix ID of the event replied to.
*/
static QString replyId(const Quotient::RoomEvent *event);
/**
* @brief Get the author of the event replied to in context of the room.
*
@@ -227,6 +249,20 @@ public:
*/
static Quotient::RoomMember replyAuthor(const NeoChatRoom *room, const Quotient::RoomEvent *event);
/**
* @brief Whether the message is part of a thread.
*
* i.e. There is a rel_type of m.thread.
*/
static bool isThreaded(const Quotient::RoomEvent *event);
/**
* @brief Return the Matrix ID of the thread's root message.
*
* Empty if this not part of a thread.
*/
static QString threadRoot(const Quotient::RoomEvent *event);
/**
* @brief Return the latitude for the event.
*

View File

@@ -8,31 +8,31 @@ using namespace Quotient;
ImagePackEventContent::ImagePackEventContent(const QJsonObject &json)
{
if (json.contains("pack"_L1)) {
if (json.contains(QStringLiteral("pack"))) {
pack = ImagePackEventContent::Pack{
fromJson<std::optional<QString>>(json["pack"_L1].toObject()["display_name"_L1]),
fromJson<std::optional<QUrl>>(json["pack"_L1].toObject()["avatar_url"_L1]),
fromJson<std::optional<QStringList>>(json["pack"_L1].toObject()["usage"_L1]),
fromJson<std::optional<QString>>(json["pack"_L1].toObject()["attribution"_L1]),
fromJson<std::optional<QString>>(json["pack"_ls].toObject()["display_name"_ls]),
fromJson<std::optional<QUrl>>(json["pack"_ls].toObject()["avatar_url"_ls]),
fromJson<std::optional<QStringList>>(json["pack"_ls].toObject()["usage"_ls]),
fromJson<std::optional<QString>>(json["pack"_ls].toObject()["attribution"_ls]),
};
} else {
pack = std::nullopt;
}
const auto &keys = json["images"_L1].toObject().keys();
const auto &keys = json["images"_ls].toObject().keys();
for (const auto &k : keys) {
std::optional<EventContent::ImageInfo> info;
if (json["images"_L1][k].toObject().contains("info"_L1)) {
info = EventContent::ImageInfo(QUrl(json["images"_L1][k]["url"_L1].toString()), json["images"_L1][k]["info"_L1].toObject(), k);
if (json["images"_ls][k].toObject().contains(QStringLiteral("info"))) {
info = EventContent::ImageInfo(QUrl(json["images"_ls][k]["url"_ls].toString()), json["images"_ls][k]["info"_ls].toObject(), k);
} else {
info = std::nullopt;
}
images += ImagePackImage{
k,
fromJson<QUrl>(json["images"_L1][k]["url"_L1].toString()),
fromJson<std::optional<QString>>(json["images"_L1][k]["body"_L1]),
fromJson<QUrl>(json["images"_ls][k]["url"_ls].toString()),
fromJson<std::optional<QString>>(json["images"_ls][k]["body"_ls]),
info,
fromJson<std::optional<QStringList>>(json["images"_L1][k]["usage"_L1]),
fromJson<std::optional<QStringList>>(json["images"_ls][k]["usage"_ls]),
};
}
}
@@ -42,42 +42,42 @@ void ImagePackEventContent::fillJson(QJsonObject *o) const
if (pack) {
QJsonObject packJson;
if (pack->displayName) {
packJson["display_name"_L1] = *pack->displayName;
packJson["display_name"_ls] = *pack->displayName;
}
if (pack->usage) {
QJsonArray usageJson;
for (const auto &usage : *pack->usage) {
usageJson += usage;
}
packJson["usage"_L1] = usageJson;
packJson["usage"_ls] = usageJson;
}
if (pack->avatarUrl) {
packJson["avatar_url"_L1] = pack->avatarUrl->toString();
packJson["avatar_url"_ls] = pack->avatarUrl->toString();
}
if (pack->attribution) {
packJson["attribution"_L1] = *pack->attribution;
packJson["attribution"_ls] = *pack->attribution;
}
(*o)["pack"_L1] = packJson;
(*o)["pack"_ls] = packJson;
}
QJsonObject imagesJson;
for (const auto &image : images) {
QJsonObject imageJson;
imageJson["url"_L1] = image.url.toString();
imageJson["url"_ls] = image.url.toString();
if (image.body) {
imageJson["body"_L1] = *image.body;
imageJson["body"_ls] = *image.body;
}
if (image.usage) {
QJsonArray usageJson;
for (const auto &usage : *image.usage) {
usageJson += usage;
}
imageJson["usage"_L1] = usageJson;
imageJson["usage"_ls] = usageJson;
}
if (image.info.has_value()) {
imageJson["info"_L1] = Quotient::EventContent::toInfoJson(*image.info);
imageJson["info"_ls] = Quotient::EventContent::toInfoJson(*image.info);
}
imagesJson[image.shortcode] = imageJson;
}
(*o)["images"_L1] = imagesJson;
(*o)["images"_ls] = imagesJson;
}

View File

@@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: 2019 Kitsune Ral <Kitsune-Ral@users.sf.net>
// SPDX-License-Identifier: LGPL-2.1-or-later
#include "joinrulesevent.h"
using namespace Quotient;
QString JoinRulesEvent::joinRule() const
{
return fromJson<QString>(contentJson()["join_rule"_ls]);
}
QJsonArray JoinRulesEvent::allow() const
{
return contentJson()["allow"_ls].toArray();
}

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