Compare commits

...

387 Commits

Author SHA1 Message Date
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
l10n daemon script
7b249e9fa6 GIT_SILENT Sync po/docbooks with svn 2024-11-08 01:30:13 +00:00
l10n daemon script
46593ef68f GIT_SILENT Sync po/docbooks with svn 2024-11-07 01:31:11 +00:00
Eren Karakas
b70f73c7d6 Make send message and insert newline shortcuts configurable
Currently both Enter and Ctrl+Enter send the message in ChatBar on
desktop. This might be unexpected behavior to users coming from other
chat applications (eg. WhatsApp, Telegram, Element) as those send
with Enter only by default. They allow changing send to Ctrl+Enter
in settings and other option is used to insert a newline.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

This also creates a basic unit test that we can add to going forward.
2024-09-15 08:28:46 +00:00
l10n daemon script
e0c3b7f808 GIT_SILENT Sync po/docbooks with svn 2024-09-15 01:32:10 +00:00
l10n daemon script
155dc4919e SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-09-15 01:23:53 +00:00
l10n daemon script
a38c53b2be GIT_SILENT Sync po/docbooks with svn 2024-09-14 01:34:50 +00:00
James Graham
67dfc7b32e Fix Eventhandler strings for translation
Change the generic representations of events in event handler to always have a full string to aid translation.

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

Fixes network/neochat#638

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

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

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

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

Also define some of the commonly used strings

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

BUG: 488325
2024-07-23 17:27:00 +00:00
l10n daemon script
9df4cd6f13 GIT_SILENT Sync po/docbooks with svn 2024-07-23 01:27:45 +00:00
Albert Astals Cid
c9583964c8 GIT_SILENT Upgrade release service version to 24.11.70. 2024-07-21 12:54:43 +02:00
260 changed files with 65208 additions and 42223 deletions

View File

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

View File

@@ -110,7 +110,7 @@
{
"type": "git",
"url": "https://github.com/quotient-im/libQuotient.git",
"branch": "0.8.x",
"branch": "dev",
"disable-submodules": true
}
],

View File

@@ -10,6 +10,8 @@ include:
- /gitlab-templates/windows-qt6.yml
# - /gitlab-templates/freebsd-qt6.yml
- /gitlab-templates/flatpak.yml
- /gitlab-templates/snap-snapcraft-lxd.yml
- /gitlab-templates/craft-android-qt6-apks.yml
- /gitlab-templates/craft-appimage-qt6.yml
- /gitlab-templates/craft-windows-x86-64-qt6.yml
- /gitlab-templates/craft-windows-appx-qt6.yml

View File

@@ -8,13 +8,13 @@ cmake_minimum_required(VERSION 3.16)
# KDE Applications version, managed by release script.
set(RELEASE_SERVICE_VERSION_MAJOR "24")
set(RELEASE_SERVICE_VERSION_MINOR "07")
set(RELEASE_SERVICE_VERSION_MICRO "70")
set(RELEASE_SERVICE_VERSION_MINOR "12")
set(RELEASE_SERVICE_VERSION_MICRO "0")
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
set(KF_MIN_VERSION "6.0")
set(KF_MIN_VERSION "6.6")
set(QT_MIN_VERSION "6.5")
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
@@ -38,6 +38,9 @@ include(KDEGitCommitHooks)
include(ECMCheckOutboundLicense)
include(ECMQtDeclareLoggingCategory)
include(ECMAddAndroidApk)
include(ECMQmlModule)
include(GenerateExportHeader)
include(ECMGenerateHeaders)
if (NOT ANDROID)
include(KDEClangFormat)
endif()
@@ -59,7 +62,6 @@ set_package_properties(Qt6 PROPERTIES
PURPOSE "Basic application components"
)
qt_policy(SET QTP0001 NEW)
if (QT_KNOWN_POLICY_QTP0004)
qt_policy(SET QTP0004 NEW)
endif ()
@@ -101,11 +103,11 @@ else()
)
endif()
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED)
endif()
find_package(QuotientQt6 0.8.2)
find_package(QuotientQt6 0.9)
set_package_properties(QuotientQt6 PROPERTIES
TYPE REQUIRED
DESCRIPTION "Qt wrapper around Matrix API"
@@ -113,11 +115,6 @@ set_package_properties(QuotientQt6 PROPERTIES
PURPOSE "Talk with matrix server"
)
if (NOT TARGET Olm::Olm)
message(FATAL_ERROR "NeoChat requires Quotient with the E2EE feature enabled")
endif()
find_package(cmark)
set_package_properties(cmark PROPERTIES
TYPE REQUIRED

View File

@@ -11,7 +11,7 @@ A Qt/QML based Matrix client.
<a href='https://matrix.org'><img src='https://matrix.org/docs/legacy/made-for-matrix.png' alt='Made for Matrix' height=64 target=_blank /></a>
<a href='https://flathub.org/apps/details/org.kde.neochat'><img width='190px' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-i-en.png'/></a>
<a href='https://snapcraft.io/neochat'><img width='190px' alt='Download on the Snap Store' src='https://snapcraft.io/static/images/badges/en/snap-store-black.svg'/></a>
<a href='https://snapcraft.io/neochat'><img width='190px' alt='Download on the Snap Store' src='https://apps.kde.org/store_badges/snapstore/en.svg'/></a>
## Introduction

View File

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

View File

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

View File

@@ -53,12 +53,6 @@ ecm_add_test(
TEST_NAME messageeventmodeltest
)
ecm_add_test(
actionshandlertest.cpp
LINK_LIBRARIES neochat Qt::Test
TEST_NAME actionshandlertest
)
ecm_add_test(
windowcontrollertest.cpp
LINK_LIBRARIES neochat Qt::Test
@@ -82,3 +76,9 @@ ecm_add_test(
LINK_LIBRARIES neochat Qt::Test
TEST_NAME linkpreviewertest
)
ecm_add_test(
messagecontentmodeltest.cpp
LINK_LIBRARIES neochat Qt::Test
TEST_NAME messagecontentmodeltest
)

View File

@@ -1,43 +0,0 @@
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QTest>
#include "actionshandler.h"
#include "chatbarcache.h"
#include "testutils.h"
class ActionsHandlerTest : public QObject
{
Q_OBJECT
private:
Quotient::Connection *connection = Quotient::Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
ActionsHandler *actionsHandler = new ActionsHandler(this);
private Q_SLOTS:
void nullObject();
};
void ActionsHandlerTest::nullObject()
{
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
actionsHandler->handleMessageEvent(nullptr);
auto chatBarCache = new ChatBarCache(this);
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
actionsHandler->handleMessageEvent(chatBarCache);
auto room = new TestUtils::TestRoom(connection, QStringLiteral("#myroom:kde.org"));
actionsHandler->setRoom(room);
QTest::ignoreMessage(QtWarningMsg, "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
actionsHandler->handleMessageEvent(nullptr);
// The final one should throw no warning so we make sure.
QTest::failOnWarning("ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.");
actionsHandler->handleMessageEvent(chatBarCache);
}
QTEST_GUILESS_MAIN(ActionsHandlerTest)
#include "actionshandlertest.moc"

View File

@@ -51,7 +51,7 @@ void ChatBarCacheTest::empty()
QCOMPARE(chatBarCache->replyId(), QString());
QCOMPARE(chatBarCache->isEditing(), false);
QCOMPARE(chatBarCache->editId(), QString());
QCOMPARE(chatBarCache->relationUser(), room->member(QString()));
QCOMPARE(chatBarCache->relationAuthor(), room->member(QString()));
QCOMPARE(chatBarCache->relationMessage(), QString());
QCOMPARE(chatBarCache->attachmentPath(), QString());
}
@@ -65,7 +65,7 @@ void ChatBarCacheTest::noRoom()
// ChatBarCache has no parent.
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.");
QCOMPARE(chatBarCache->relationUser(), Quotient::RoomMember());
QCOMPARE(chatBarCache->relationAuthor(), Quotient::RoomMember());
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.");
QCOMPARE(chatBarCache->relationMessage(), QString());
@@ -81,7 +81,7 @@ void ChatBarCacheTest::badParent()
// ChatBarCache has no parent.
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.");
QCOMPARE(chatBarCache->relationUser(), Quotient::RoomMember());
QCOMPARE(chatBarCache->relationAuthor(), Quotient::RoomMember());
QTest::ignoreMessage(QtWarningMsg, "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.");
QCOMPARE(chatBarCache->relationMessage(), QString());
@@ -99,7 +99,7 @@ void ChatBarCacheTest::reply()
QCOMPARE(chatBarCache->replyId(), QLatin1String("$153456789:example.org"));
QCOMPARE(chatBarCache->isEditing(), false);
QCOMPARE(chatBarCache->editId(), QString());
QCOMPARE(chatBarCache->relationUser(), room->member(QLatin1String("@example: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());
}
@@ -107,8 +107,13 @@ void ChatBarCacheTest::reply()
void ChatBarCacheTest::edit()
{
QScopedPointer<ChatBarCache> chatBarCache(new ChatBarCache(room));
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(QLatin1String("$153456789:example.org")));
});
chatBarCache->setEditId(QLatin1String("$153456789:example.org"));
QCOMPARE(chatBarCache->text(), QLatin1String("some text"));
@@ -116,7 +121,7 @@ void ChatBarCacheTest::edit()
QCOMPARE(chatBarCache->replyId(), QString());
QCOMPARE(chatBarCache->isEditing(), true);
QCOMPARE(chatBarCache->editId(), QLatin1String("$153456789:example.org"));
QCOMPARE(chatBarCache->relationUser(), room->member(QLatin1String("@example: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());
}
@@ -133,7 +138,7 @@ void ChatBarCacheTest::attachment()
QCOMPARE(chatBarCache->replyId(), QString());
QCOMPARE(chatBarCache->isEditing(), false);
QCOMPARE(chatBarCache->editId(), QString());
QCOMPARE(chatBarCache->relationUser(), room->member(QString()));
QCOMPARE(chatBarCache->relationAuthor(), room->member(QString()));
QCOMPARE(chatBarCache->relationMessage(), QString());
QCOMPARE(chatBarCache->attachmentPath(), QLatin1String("some/path"));
}

View File

@@ -29,15 +29,11 @@ private:
Connection *connection = nullptr;
TestUtils::TestRoom *room = nullptr;
EventHandler emptyHandler = EventHandler(nullptr, nullptr);
private Q_SLOTS:
void initTestCase();
void eventId();
void nullEventId();
void author();
void nullAuthor();
void authorDisplayName();
void nullAuthorDisplayName();
void singleLineSidplayName();
@@ -45,7 +41,6 @@ private Q_SLOTS:
void time();
void nullTime();
void timeString();
void nullTimeString();
void highlighted();
void nullHighlighted();
void hidden();
@@ -67,10 +62,6 @@ private Q_SLOTS:
void nullReplyId();
void replyAuthor();
void nullReplyAuthor();
void replyBody();
void nullReplyBody();
void replyMediaInfo();
void nullReplyMediaInfo();
void thread();
void nullThread();
void location();
@@ -85,182 +76,136 @@ void EventHandlerTest::initTestCase()
void EventHandlerTest::eventId()
{
EventHandler eventHandler(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandler.getId(), QStringLiteral("$153456789:example.org"));
QCOMPARE(EventHandler::id(room->messageEvents().at(0).get()), QStringLiteral("$153456789:example.org"));
}
void EventHandlerTest::nullEventId()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getId called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getId(), QString());
}
void EventHandlerTest::author()
{
auto event = room->messageEvents().at(0).get();
auto author = room->member(event->senderId());
EventHandler eventHandler(room, event);
auto eventHandlerAuthor = eventHandler.getAuthor();
QCOMPARE(eventHandlerAuthor.isLocalMember(), author.id() == room->localMember().id());
QCOMPARE(eventHandlerAuthor.id(), author.id());
QCOMPARE(eventHandlerAuthor.displayName(), author.displayName());
QCOMPARE(eventHandlerAuthor.avatarUrl(), author.avatarUrl());
QCOMPARE(eventHandlerAuthor.avatarMediaId(), author.avatarMediaId());
QCOMPARE(eventHandlerAuthor.color(), author.color());
}
void EventHandlerTest::nullAuthor()
{
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getAuthor(), RoomMember());
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getAuthor called with m_event set to nullptr. Returning empty user.");
QCOMPARE(noEventHandler.getAuthor(), RoomMember());
QTest::ignoreMessage(QtWarningMsg, "id called with event set to nullptr.");
QCOMPARE(EventHandler::id(nullptr), QString());
}
void EventHandlerTest::authorDisplayName()
{
EventHandler eventHandler(room, room->messageEvents().at(1).get());
QCOMPARE(eventHandler.getAuthorDisplayName(), QStringLiteral("before"));
QCOMPARE(EventHandler::authorDisplayName(room, room->messageEvents().at(1).get()), QStringLiteral("before"));
}
void EventHandlerTest::nullAuthorDisplayName()
{
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getAuthorDisplayName(), QString());
QTest::ignoreMessage(QtWarningMsg, "authorDisplayName called with room set to nullptr.");
QCOMPARE(EventHandler::authorDisplayName(nullptr, nullptr), QString());
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getAuthorDisplayName(), QString());
QTest::ignoreMessage(QtWarningMsg, "authorDisplayName called with event set to nullptr.");
QCOMPARE(EventHandler::authorDisplayName(room, nullptr), QString());
}
void EventHandlerTest::singleLineSidplayName()
{
EventHandler eventHandler(room, room->messageEvents().at(11).get());
QCOMPARE(eventHandler.singleLineAuthorDisplayname(), QStringLiteral("Look at me I put newlines in my display name"));
QCOMPARE(EventHandler::singleLineAuthorDisplayname(room, room->messageEvents().at(11).get()),
QStringLiteral("Look at me I put newlines in my display name"));
}
void EventHandlerTest::nullSingleLineDisplayName()
{
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_room set to nullptr.");
QCOMPARE(emptyHandler.singleLineAuthorDisplayname(), QString());
QTest::ignoreMessage(QtWarningMsg, "singleLineAuthorDisplayname called with room set to nullptr.");
QCOMPARE(EventHandler::singleLineAuthorDisplayname(nullptr, nullptr), QString());
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getAuthorDisplayName called with m_event set to nullptr.");
QCOMPARE(noEventHandler.singleLineAuthorDisplayname(), QString());
QTest::ignoreMessage(QtWarningMsg, "singleLineAuthorDisplayname called with event set to nullptr.");
QCOMPARE(EventHandler::singleLineAuthorDisplayname(room, nullptr), QString());
}
void EventHandlerTest::time()
{
EventHandler eventHandler(room, room->messageEvents().at(0).get());
const auto event = room->messageEvents().at(0).get();
QCOMPARE(eventHandler.getTime(), QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC));
QCOMPARE(eventHandler.getTime(true, QDateTime::fromMSecsSinceEpoch(1234, Qt::UTC)), QDateTime::fromMSecsSinceEpoch(1234, Qt::UTC));
QCOMPARE(EventHandler::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()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getTime called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getTime(), QDateTime());
QTest::ignoreMessage(QtWarningMsg, "time called with event set to nullptr.");
QCOMPARE(EventHandler::time(nullptr), QDateTime());
EventHandler eventHandler(room, room->messageEvents().at(0).get());
QTest::ignoreMessage(QtWarningMsg, "a value must be provided for lastUpdated for a pending event.");
QCOMPARE(eventHandler.getTime(true), QDateTime());
QCOMPARE(EventHandler::time(room->messageEvents().at(0).get(), true), QDateTime());
}
void EventHandlerTest::timeString()
{
EventHandler eventHandler(room, room->messageEvents().at(0).get());
const auto event = room->messageEvents().at(0).get();
KFormat format;
QCOMPARE(eventHandler.getTimeString(false),
QCOMPARE(EventHandler::timeString(event, false),
QLocale().toString(QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toLocalTime().time(), QLocale::ShortFormat));
QCOMPARE(eventHandler.getTimeString(true),
QCOMPARE(EventHandler::timeString(event, true),
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toLocalTime().date(), QLocale::ShortFormat));
QCOMPARE(eventHandler.getTimeString(false, QLocale::ShortFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
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.getTimeString(true, QLocale::ShortFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
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.getTimeString(false, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
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.getTimeString(true, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
QCOMPARE(EventHandler::timeString(event, true, QLocale::LongFormat, true, QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC)),
format.formatRelativeDate(QDateTime::fromMSecsSinceEpoch(1690699214545, Qt::UTC).toLocalTime().date(), QLocale::LongFormat));
}
void EventHandlerTest::nullTimeString()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getTimeString called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getTimeString(false), QString());
EventHandler eventHandler(room, room->messageEvents().at(0).get());
QTest::ignoreMessage(QtWarningMsg, "a value must be provided for lastUpdated for a pending event.");
QCOMPARE(eventHandler.getTimeString(false, QLocale::ShortFormat, true), QString());
QCOMPARE(EventHandler::timeString(event, QStringLiteral("hh:mm")),
QDateTime::fromMSecsSinceEpoch(1432735824654, Qt::UTC).toString(QStringLiteral("hh:mm")));
}
void EventHandlerTest::highlighted()
{
EventHandler eventHandlerHighlight(room, room->messageEvents().at(2).get());
QCOMPARE(eventHandlerHighlight.isHighlighted(), true);
EventHandler eventHandlerNoHighlight(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandlerNoHighlight.isHighlighted(), false);
QCOMPARE(EventHandler::isHighlighted(room, room->messageEvents().at(2).get()), true);
QCOMPARE(EventHandler::isHighlighted(room, room->messageEvents().at(0).get()), false);
}
void EventHandlerTest::nullHighlighted()
{
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with m_room set to nullptr.");
QCOMPARE(emptyHandler.isHighlighted(), false);
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with room set to nullptr.");
QCOMPARE(EventHandler::isHighlighted(nullptr, nullptr), false);
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with m_event set to nullptr.");
QCOMPARE(noEventHandler.isHighlighted(), false);
QTest::ignoreMessage(QtWarningMsg, "isHighlighted called with event set to nullptr.");
QCOMPARE(EventHandler::isHighlighted(room, nullptr), false);
}
void EventHandlerTest::hidden()
{
EventHandler eventHandlerHidden(room, room->messageEvents().at(3).get());
QCOMPARE(eventHandlerHidden.isHidden(), true);
EventHandler eventHandlerNoHidden(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandlerNoHidden.isHidden(), false);
QCOMPARE(EventHandler::isHidden(room, room->messageEvents().at(3).get()), true);
QCOMPARE(EventHandler::isHidden(room, room->messageEvents().at(0).get()), false);
}
void EventHandlerTest::nullHidden()
{
QTest::ignoreMessage(QtWarningMsg, "isHidden called with m_room set to nullptr.");
QCOMPARE(emptyHandler.isHidden(), false);
QTest::ignoreMessage(QtWarningMsg, "isHidden called with room set to nullptr.");
QCOMPARE(EventHandler::isHidden(nullptr, nullptr), false);
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "isHidden called with m_event set to nullptr.");
QCOMPARE(noEventHandler.isHidden(), false);
QTest::ignoreMessage(QtWarningMsg, "isHidden called with event set to nullptr.");
QCOMPARE(EventHandler::isHidden(room, nullptr), false);
}
void EventHandlerTest::body()
{
EventHandler eventHandler(room, room->messageEvents().at(0).get());
const auto event = room->messageEvents().at(0).get();
QCOMPARE(eventHandler.getRichBody(), QStringLiteral("<b>This is an example<br>text message</b>"));
QCOMPARE(eventHandler.getRichBody(true), QStringLiteral("<b>This is an example text message</b>"));
QCOMPARE(eventHandler.getPlainBody(), QStringLiteral("This is an example\ntext message"));
QCOMPARE(eventHandler.getPlainBody(true), QStringLiteral("This is an example text message"));
QCOMPARE(EventHandler::richBody(room, event), 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()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "richBody called with room set to nullptr.");
QCOMPARE(EventHandler::richBody(nullptr, nullptr), QString());
QTest::ignoreMessage(QtWarningMsg, "getRichBody called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getRichBody(), QString());
QTest::ignoreMessage(QtWarningMsg, "richBody called with event set to nullptr.");
QCOMPARE(EventHandler::richBody(room, nullptr), QString());
QTest::ignoreMessage(QtWarningMsg, "getPlainBody called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getPlainBody(), QString());
QTest::ignoreMessage(QtWarningMsg, "plainBody called with room set to nullptr.");
QCOMPARE(EventHandler::plainBody(nullptr, nullptr), QString());
QTest::ignoreMessage(QtWarningMsg, "plainBody called with event set to nullptr.");
QCOMPARE(EventHandler::plainBody(room, nullptr), QString());
}
void EventHandlerTest::genericBody_data()
@@ -268,11 +213,13 @@ void EventHandlerTest::genericBody_data()
QTest::addColumn<int>("eventNum");
QTest::addColumn<QString>("output");
QTest::newRow("message") << 0 << QStringLiteral("sent a message");
QTest::newRow("member") << 1 << QStringLiteral("changed their display name and updated their avatar");
QTest::newRow("message 2") << 2 << QStringLiteral("sent a message");
QTest::newRow("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("sent a message");
QTest::newRow("video") << 4 << QStringLiteral("<a href=\"https://matrix.to/#/@example:example.org\">after</a> sent a message");
}
void EventHandlerTest::genericBody()
@@ -280,54 +227,48 @@ void EventHandlerTest::genericBody()
QFETCH(int, eventNum);
QFETCH(QString, output);
EventHandler eventHandler(room, room->messageEvents().at(eventNum).get());
QCOMPARE(eventHandler.getGenericBody(), output);
QCOMPARE(EventHandler::genericBody(room, room->messageEvents().at(eventNum).get()), output);
}
void EventHandlerTest::nullGenericBody()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getGenericBody called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getGenericBody(), QString());
QTest::ignoreMessage(QtWarningMsg, "genericBody called with room set to nullptr.");
QCOMPARE(EventHandler::genericBody(nullptr, nullptr), QString());
QTest::ignoreMessage(QtWarningMsg, "genericBody called with event set to nullptr.");
QCOMPARE(EventHandler::genericBody(room, nullptr), QString());
}
void EventHandlerTest::markdownBody()
{
EventHandler eventHandler(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandler.getMarkdownBody(), QStringLiteral("This is an example\ntext message"));
QCOMPARE(EventHandler::markdownBody(room->messageEvents().at(0).get()), QStringLiteral("This is an example\ntext message"));
}
void EventHandlerTest::markdownBodyReply()
{
EventHandler eventHandler(room, room->messageEvents().at(5).get());
QCOMPARE(eventHandler.getMarkdownBody(), QStringLiteral("reply"));
QCOMPARE(EventHandler::markdownBody(room->messageEvents().at(5).get()), QStringLiteral("reply"));
}
void EventHandlerTest::subtitle()
{
EventHandler eventHandler(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandler.subtitleText(), QStringLiteral("after: This is an example text message"));
EventHandler eventHandler2(room, room->messageEvents().at(2).get());
QCOMPARE(eventHandler2.subtitleText(), QStringLiteral("after: This is a highlight @bob:kde.org and this is a link https://kde.org"));
QCOMPARE(EventHandler::subtitleText(room, room->messageEvents().at(0).get()), QStringLiteral("after: This is an example text message"));
QCOMPARE(EventHandler::subtitleText(room, room->messageEvents().at(2).get()),
QStringLiteral("after: This is a highlight @bob:kde.org and this is a link https://kde.org"));
}
void EventHandlerTest::nullSubtitle()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "subtitleText called with m_event set to nullptr.");
QCOMPARE(noEventHandler.subtitleText(), QString());
QTest::ignoreMessage(QtWarningMsg, "subtitleText called with room set to nullptr.");
QCOMPARE(EventHandler::subtitleText(nullptr, nullptr), QString());
QTest::ignoreMessage(QtWarningMsg, "subtitleText called with event set to nullptr.");
QCOMPARE(EventHandler::subtitleText(room, nullptr), QString());
}
void EventHandlerTest::mediaInfo()
{
auto event = room->messageEvents().at(4).get();
EventHandler eventHandler(room, event);
auto mediaInfo = eventHandler.getMediaInfo();
auto mediaInfo = EventHandler::mediaInfo(room, event);
auto thumbnailInfo = mediaInfo["tempInfo"_ls].toMap();
QCOMPARE(mediaInfo["source"_ls], room->makeMediaUrl(event->id(), QUrl("mxc://kde.org/1234567"_ls)));
@@ -347,53 +288,42 @@ void EventHandlerTest::mediaInfo()
void EventHandlerTest::nullMediaInfo()
{
QTest::ignoreMessage(QtWarningMsg, "getMediaInfo called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getMediaInfo(), QVariantMap());
QTest::ignoreMessage(QtWarningMsg, "mediaInfo called with room set to nullptr.");
QCOMPARE(EventHandler::mediaInfo(nullptr, nullptr), QVariantMap());
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getMediaInfo called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getMediaInfo(), QVariantMap());
QTest::ignoreMessage(QtWarningMsg, "mediaInfo called with event set to nullptr.");
QCOMPARE(EventHandler::mediaInfo(room, nullptr), QVariantMap());
}
void EventHandlerTest::hasReply()
{
EventHandler eventHandlerReply(room, room->messageEvents().at(5).get());
QCOMPARE(eventHandlerReply.hasReply(), true);
EventHandler eventHandlerNoReply(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandlerNoReply.hasReply(), false);
QCOMPARE(EventHandler::hasReply(room->messageEvents().at(5).get()), true);
QCOMPARE(EventHandler::hasReply(room->messageEvents().at(0).get()), false);
}
void EventHandlerTest::nullHasReply()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "hasReply called with m_event set to nullptr.");
QCOMPARE(noEventHandler.hasReply(), false);
QTest::ignoreMessage(QtWarningMsg, "hasReply called with event set to nullptr.");
QCOMPARE(EventHandler::hasReply(nullptr), false);
}
void EventHandlerTest::replyId()
{
EventHandler eventHandlerReply(room, room->messageEvents().at(5).get());
QCOMPARE(eventHandlerReply.getReplyId(), QStringLiteral("$153456789:example.org"));
EventHandler eventHandlerNoReply(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandlerNoReply.getReplyId(), QStringLiteral(""));
QCOMPARE(EventHandler::replyId(room->messageEvents().at(5).get()), QStringLiteral("$153456789:example.org"));
QCOMPARE(EventHandler::replyId(room->messageEvents().at(0).get()), QStringLiteral(""));
}
void EventHandlerTest::nullReplyId()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getReplyId called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getReplyId(), QString());
QTest::ignoreMessage(QtWarningMsg, "replyId called with event set to nullptr.");
QCOMPARE(EventHandler::replyId(nullptr), QString());
}
void EventHandlerTest::replyAuthor()
{
auto replyEvent = room->messageEvents().at(0).get();
auto replyAuthor = room->member(replyEvent->senderId());
EventHandler eventHandler(room, room->messageEvents().at(5).get());
auto eventHandlerReplyAuthor = eventHandler.getReplyAuthor();
auto eventHandlerReplyAuthor = EventHandler::replyAuthor(room, room->messageEvents().at(5).get());
QCOMPARE(eventHandlerReplyAuthor.isLocalMember(), replyAuthor.id() == room->localMember().id());
QCOMPARE(eventHandlerReplyAuthor.id(), replyAuthor.id());
@@ -402,121 +332,58 @@ void EventHandlerTest::replyAuthor()
QCOMPARE(eventHandlerReplyAuthor.avatarMediaId(), replyAuthor.avatarMediaId());
QCOMPARE(eventHandlerReplyAuthor.color(), replyAuthor.color());
EventHandler eventHandlerNoAuthor(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandlerNoAuthor.getReplyAuthor(), RoomMember());
QCOMPARE(EventHandler::replyAuthor(room, room->messageEvents().at(0).get()), RoomMember());
}
void EventHandlerTest::nullReplyAuthor()
{
QTest::ignoreMessage(QtWarningMsg, "getReplyAuthor called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getReplyAuthor(), RoomMember());
QTest::ignoreMessage(QtWarningMsg, "replyAuthor called with room set to nullptr.");
QCOMPARE(EventHandler::replyAuthor(nullptr, nullptr), RoomMember());
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getReplyAuthor called with m_event set to nullptr. Returning empty user.");
QCOMPARE(noEventHandler.getReplyAuthor(), RoomMember());
}
void EventHandlerTest::replyBody()
{
EventHandler eventHandler(room, room->messageEvents().at(5).get());
QCOMPARE(eventHandler.getReplyRichBody(), QStringLiteral("<b>This is an example<br>text message</b>"));
QCOMPARE(eventHandler.getReplyRichBody(true), QStringLiteral("<b>This is an example text message</b>"));
QCOMPARE(eventHandler.getReplyPlainBody(), QStringLiteral("This is an example\ntext message"));
QCOMPARE(eventHandler.getReplyPlainBody(true), QStringLiteral("This is an example text message"));
}
void EventHandlerTest::nullReplyBody()
{
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getReplyRichBody called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getReplyRichBody(), QString());
QTest::ignoreMessage(QtWarningMsg, "getReplyPlainBody called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getReplyPlainBody(), QString());
}
void EventHandlerTest::replyMediaInfo()
{
auto event = room->messageEvents().at(6).get();
auto replyEvent = room->messageEvents().at(4).get();
EventHandler eventHandler(room, event);
auto mediaInfo = eventHandler.getReplyMediaInfo();
auto thumbnailInfo = mediaInfo["tempInfo"_ls].toMap();
QCOMPARE(mediaInfo["source"_ls], room->makeMediaUrl(replyEvent->id(), QUrl("mxc://kde.org/1234567"_ls)));
QCOMPARE(mediaInfo["mimeType"_ls], QStringLiteral("video/mp4"));
QCOMPARE(mediaInfo["mimeIcon"_ls], QStringLiteral("video-mp4"));
QCOMPARE(mediaInfo["size"_ls], 62650636);
QCOMPARE(mediaInfo["duration"_ls], 10);
QCOMPARE(mediaInfo["width"_ls], 1920);
QCOMPARE(mediaInfo["height"_ls], 1080);
QCOMPARE(thumbnailInfo["source"_ls], room->makeMediaUrl(replyEvent->id(), QUrl("mxc://kde.org/2234567"_ls)));
QCOMPARE(thumbnailInfo["mimeType"_ls], QStringLiteral("image/jpeg"));
QCOMPARE(thumbnailInfo["mimeIcon"_ls], QStringLiteral("image-jpeg"));
QCOMPARE(thumbnailInfo["size"_ls], 382249);
QCOMPARE(thumbnailInfo["width"_ls], 800);
QCOMPARE(thumbnailInfo["height"_ls], 450);
}
void EventHandlerTest::nullReplyMediaInfo()
{
QTest::ignoreMessage(QtWarningMsg, "getReplyMediaInfo called with m_room set to nullptr.");
QCOMPARE(emptyHandler.getReplyMediaInfo(), QVariantMap());
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "getReplyMediaInfo called with m_event set to nullptr.");
QCOMPARE(noEventHandler.getReplyMediaInfo(), QVariantMap());
QTest::ignoreMessage(QtWarningMsg, "replyAuthor called with event set to nullptr. Returning empty user.");
QCOMPARE(EventHandler::replyAuthor(room, nullptr), RoomMember());
}
void EventHandlerTest::thread()
{
EventHandler eventHandlerNoThread(room, room->messageEvents().at(0).get());
QCOMPARE(eventHandlerNoThread.isThreaded(), false);
QCOMPARE(eventHandlerNoThread.threadRoot(), QString());
QCOMPARE(EventHandler::isThreaded(room->messageEvents().at(0).get()), false);
QCOMPARE(EventHandler::threadRoot(room->messageEvents().at(0).get()), QString());
EventHandler eventHandlerThreadRoot(room, room->messageEvents().at(9).get());
QCOMPARE(eventHandlerThreadRoot.isThreaded(), true);
QCOMPARE(eventHandlerThreadRoot.threadRoot(), QStringLiteral("$threadroot:example.org"));
QCOMPARE(eventHandlerThreadRoot.getReplyId(), QStringLiteral("$threadroot:example.org"));
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"));
EventHandler eventHandlerThreadReply(room, room->messageEvents().at(10).get());
QCOMPARE(eventHandlerThreadReply.isThreaded(), true);
QCOMPARE(eventHandlerThreadReply.threadRoot(), QStringLiteral("$threadroot:example.org"));
QCOMPARE(eventHandlerThreadReply.getReplyId(), QStringLiteral("$threadmessage1:example.org"));
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 m_event set to nullptr.");
QCOMPARE(emptyHandler.isThreaded(), false);
QTest::ignoreMessage(QtWarningMsg, "isThreaded called with event set to nullptr.");
QCOMPARE(EventHandler::isThreaded(nullptr), false);
EventHandler noEventHandler(room, nullptr);
QTest::ignoreMessage(QtWarningMsg, "threadRoot called with m_event set to nullptr.");
QCOMPARE(noEventHandler.threadRoot(), QString());
QTest::ignoreMessage(QtWarningMsg, "threadRoot called with event set to nullptr.");
QCOMPARE(EventHandler::threadRoot(nullptr), QString());
}
void EventHandlerTest::location()
{
EventHandler eventHandler(room, room->messageEvents().at(7).get());
QCOMPARE(eventHandler.getLatitude(), QStringLiteral("51.7035").toFloat());
QCOMPARE(eventHandler.getLongitude(), QStringLiteral("-1.14394").toFloat());
QCOMPARE(eventHandler.getLocationAssetType(), QStringLiteral("m.pin"));
QCOMPARE(EventHandler::latitude(room->messageEvents().at(7).get()), 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()
{
QTest::ignoreMessage(QtWarningMsg, "getLatitude called with m_event set to nullptr.");
QCOMPARE(emptyHandler.getLatitude(), -100.0);
QTest::ignoreMessage(QtWarningMsg, "latitude called with event set to nullptr.");
QCOMPARE(EventHandler::latitude(nullptr), -100.0);
QTest::ignoreMessage(QtWarningMsg, "getLongitude called with m_event set to nullptr.");
QCOMPARE(emptyHandler.getLongitude(), -200.0);
QTest::ignoreMessage(QtWarningMsg, "longitude called with event set to nullptr.");
QCOMPARE(EventHandler::longitude(nullptr), -200.0);
QTest::ignoreMessage(QtWarningMsg, "getLocationAssetType called with m_event set to nullptr.");
QCOMPARE(emptyHandler.getLocationAssetType(), QString());
QTest::ignoreMessage(QtWarningMsg, "locationAssetType called with event set to nullptr.");
QCOMPARE(EventHandler::locationAssetType(nullptr), QString());
}
QTEST_MAIN(EventHandlerTest)

View File

@@ -0,0 +1,61 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <QObject>
#include <QSignalSpy>
#include <QTest>
#include <Quotient/connection.h>
#include <Quotient/quotient_common.h>
#include <Quotient/roommember.h>
#include <Quotient/syncdata.h>
#include "models/messagecontentmodel.h"
#include "testutils.h"
using namespace Quotient;
using namespace Qt::Literals::StringLiterals;
class MessageContentModelTest : public QObject
{
Q_OBJECT
private:
Connection *connection = nullptr;
private Q_SLOTS:
void initTestCase();
void missingEvent();
};
void MessageContentModelTest::initTestCase()
{
connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
}
void MessageContentModelTest::missingEvent()
{
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), "Loading"_L1);
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), "Loading reply"_L1);
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);
QCOMPARE(model1.data(model1.index(1), MessageContentModel::DisplayRole), u"<b>This is an example<br>text message</b>"_s);
}
QTEST_MAIN(MessageContentModelTest)
#include "messagecontentmodeltest.moc"

View File

@@ -12,9 +12,7 @@
#include "enums/messagecomponenttype.h"
#include "models/customemojimodel.h"
#include "models/messagecontentmodel.h"
#include "neochatconnection.h"
#include "utils.h"
#include "testutils.h"
@@ -46,6 +44,7 @@ private Q_SLOTS:
void sendCustomEmojiCode_data();
void sendCustomEmojiCode();
void receiveSpacelessSelfClosingTag();
void receiveStripReply();
void receivePlainTextIn();
@@ -85,11 +84,11 @@ void TextHandlerTest::initTestCase()
void TextHandlerTest::allowedAttributes()
{
const QString testInputString1 = QStringLiteral("<p><span data-mx-spoiler><font color=#FFFFFF>Test</font><span></p>");
const QString testOutputString1 = QStringLiteral("<p><span data-mx-spoiler><font color=#FFFFFF>Test</font><span></p>");
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 = QStringLiteral("<p><a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a></p>");
const QString testOutputString2 = QStringLiteral("<p><a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a></p>");
const QString testInputString2 = 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);
@@ -117,7 +116,7 @@ void TextHandlerTest::stripDisallowedTags()
void TextHandlerTest::stripDisallowedAttributes()
{
const QString testInputString = QStringLiteral("<p style=\"font-size:50px;\" color=#FFFFFF>Test</p>");
const QString testOutputString = QStringLiteral("<p>Test</p>");
const QString testOutputString = QStringLiteral("Test");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -144,8 +143,8 @@ void TextHandlerTest::emptyCodeTags()
void TextHandlerTest::sendSimpleStringCase()
{
const QString testInputString = QStringLiteral("This data should just be put in a paragraph.");
const QString testOutputString = QStringLiteral("<p>This data should just be put in a paragraph.</p>");
const QString testInputString = QStringLiteral("This data should just be left alone.");
const QString testOutputString = QStringLiteral("This data should just be left alone.");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -158,8 +157,8 @@ void TextHandlerTest::sendSingleParaMarkup()
const QString testInputString = QStringLiteral(
"Text para with **bold**, *italic*, [link](https://kde.org), ![image](mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e), `inline code`.");
const QString testOutputString = QStringLiteral(
"<p>Text para with <strong>bold</strong>, <em>italic</em>, <a href=\"https://kde.org\">link</a>, <img "
"src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e\" alt=\"image\">, <code>inline code</code>.</p>");
"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);
@@ -185,7 +184,7 @@ void TextHandlerTest::sendMultipleSectionMarkup()
void TextHandlerTest::sendBadLinks()
{
const QString testInputString = QStringLiteral("[link](kde.org), ![image](https://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e)");
const QString testOutputString = QStringLiteral("<p><a>link</a>, <img alt=\"image\"></p>");
const QString testOutputString = QStringLiteral("<a>link</a>, <img alt=\"image\">");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -222,8 +221,8 @@ void TextHandlerTest::sendCodeClass()
void TextHandlerTest::sendCustomEmoji()
{
const QString testInputString = QStringLiteral(":test:");
const QString testOutputString = QStringLiteral(
"<p><img data-mx-emoticon=\"\" src=\"mxc://example.org/test\" alt=\":test:\" title=\":test:\" height=\"32\" vertical-align=\"middle\" /></p>");
const QString testOutputString =
QStringLiteral("<img data-mx-emoticon=\"\" src=\"mxc://example.org/test\" alt=\":test:\" title=\":test:\" height=\"32\" vertical-align=\"middle\" />");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -236,7 +235,7 @@ void TextHandlerTest::sendCustomEmojiCode_data()
QTest::addColumn<QString>("testInputString");
QTest::addColumn<QString>("testOutputString");
QTest::newRow("inline") << QStringLiteral("`:test:`") << QStringLiteral("<p><code>:test:</code></p>");
QTest::newRow("inline") << QStringLiteral("`:test:`") << QStringLiteral("<code>:test:</code>");
QTest::newRow("block") << QStringLiteral("```\n:test:\n```") << QStringLiteral("<pre><code>:test:\n</code></pre>");
}
@@ -252,6 +251,19 @@ void TextHandlerTest::sendCustomEmojiCode()
QCOMPARE(testTextHandler.handleSendText(), testOutputString);
}
void TextHandlerTest::receiveSpacelessSelfClosingTag()
{
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);
QCOMPARE(testTextHandler.handleRecieveRichText(), testRichOutputString);
QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText), testPlainOutputString);
}
void TextHandlerTest::receiveStripReply()
{
const QString testInputString = QStringLiteral(
@@ -274,6 +286,7 @@ void TextHandlerTest::receiveRichInPlainOut_data()
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()
@@ -360,7 +373,7 @@ void TextHandlerTest::receivePlainStripMarkup()
void TextHandlerTest::receiveRichUserPill()
{
const QString testInputString = QStringLiteral("<p><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></p>");
const QString testOutputString = QStringLiteral("<p><b><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></b></p>");
const QString testOutputString = QStringLiteral("<b><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></b>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -371,7 +384,7 @@ void TextHandlerTest::receiveRichUserPill()
void TextHandlerTest::receiveRichStrikethrough()
{
const QString testInputString = QStringLiteral("<p><del>Test</del></p>");
const QString testOutputString = QStringLiteral("<p><s>Test</s></p>");
const QString testOutputString = QStringLiteral("<s>Test</s>");
TextHandler testTextHandler;
testTextHandler.setData(testInputString);
@@ -448,6 +461,9 @@ void TextHandlerTest::receiveRichPlainUrl()
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 = 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);
@@ -461,6 +477,9 @@ void TextHandlerTest::receiveRichPlainUrl()
testTextHandler.setData(testInputStringMxId);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringMxId);
testTextHandler.setData(testInputStringMxIdWithPrefix);
QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringMxIdWithPrefix);
}
void TextHandlerTest::receiveRichEdited_data()
@@ -516,7 +535,7 @@ void TextHandlerTest::componentOutput_data()
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\""), {}}};
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"), {}}};
@@ -535,7 +554,7 @@ void TextHandlerTest::componentOutput_data()
"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, QStringLiteral("Ah, you mean something like"), {}},
MessageComponent{MessageComponentType::Text, QStringLiteral("Ah, you mean something like<br/>"), {}},
MessageComponent{
MessageComponentType::Code,
QStringLiteral(

View File

@@ -17,7 +17,6 @@ class WindowControllerTest : public QObject
private Q_SLOTS:
void nullWindow();
void geometry();
void showAndRaise();
void toggle();
@@ -30,32 +29,10 @@ void WindowControllerTest::nullWindow()
auto &instance = WindowController::instance();
QCOMPARE(instance.window(), nullptr);
instance.restoreGeometry();
instance.saveGeometry();
instance.showAndRaiseWindow({});
instance.toggleWindow();
}
void WindowControllerTest::geometry()
{
auto &instance = WindowController::instance();
QWindow window;
window.setGeometry(0, 0, 200, 200);
instance.setWindow(&window);
QCOMPARE(instance.window(), &window);
instance.saveGeometry();
const auto stateConfig = KSharedConfig::openStateConfig();
KConfigGroup windowGroup = stateConfig->group(QStringLiteral("Window"));
QCOMPARE(KWindowConfig::hasSavedWindowSize(windowGroup), true);
window.setGeometry(0, 0, 400, 400);
QCOMPARE(window.geometry(), QRect(0, 0, 400, 400));
instance.restoreGeometry();
QCOMPARE(window.geometry(), QRect(0, 0, 200, 200));
}
void WindowControllerTest::showAndRaise()
{
auto &instance = WindowController::instance();

View File

@@ -25,6 +25,7 @@
<name xml:lang="fi">NeoChat</name>
<name xml:lang="fr">NeoChat</name>
<name xml:lang="gl">NeoChat</name>
<name xml:lang="he">NeoChat</name>
<name xml:lang="hu">NeoChat</name>
<name xml:lang="ia">Neochat</name>
<name xml:lang="id">NeoChat</name>
@@ -49,46 +50,46 @@
<name xml:lang="x-test">xxNeoChatxx</name>
<name xml:lang="zh-CN">NeoChat</name>
<name xml:lang="zh-TW">NeoChat</name>
<summary>Chat with your friends on matrix</summary>
<summary xml:lang="ar">دردش مع أصدقائك على ماتركس</summary>
<summary xml:lang="ca">Xategeu amb els vostres amics a Matrix</summary>
<summary xml:lang="ca-valencia">Xategeu amb els vostres amics a Matrix</summary>
<summary xml:lang="cs">Mluvte se svými přáteli na Matrixu</summary>
<summary xml:lang="en-GB">Chat with your friends on matrix</summary>
<summary xml:lang="eo">Babilu kun viaj amikoj sur matrix</summary>
<summary xml:lang="es">Charle con sus amigos en matrix</summary>
<summary xml:lang="eu">Berriketan jardun zure lagunekin «Matrix»en</summary>
<summary xml:lang="fi">Keskustelu ystäviesi kanssa Matrixissa</summary>
<summary xml:lang="fr">Discuter avec vos ami(e)s sur le réseau Matrix</summary>
<summary xml:lang="gl">Charle coas súas amizades en Matrix.</summary>
<summary xml:lang="hu">Csevegjen barátaival a matrixon</summary>
<summary xml:lang="ia">Starta Conversation con tu amicos sur matrix</summary>
<summary xml:lang="it">Conversa con i tuoi contatti su matrix</summary>
<summary xml:lang="ka">ესაუბრეთ მეგობრებს Matrix-ზე</summary>
<summary xml:lang="ko">Matrix를 사용하여 친구들과 대화하기</summary>
<summary xml:lang="lv">Tērzējiet ar saviem draugiem „Matrix“ tīklā</summary>
<summary xml:lang="nl">Met uw vrienden chatten op matrix</summary>
<summary xml:lang="nn">Prat med vennar på Matrix</summary>
<summary xml:lang="pl">Rozmawiaj ze swoimi znajomymi w Matriksie</summary>
<summary xml:lang="sl">Klepet z vašimi prijatelji na matrixu</summary>
<summary xml:lang="sv">Chatta med dina vänner på Matrix</summary>
<summary xml:lang="ta">மேட்ரிக்ஸு மூலம் உங்கள் நண்பர்களிடம் பேசலாம்</summary>
<summary xml:lang="tr">Matrixte arkadaşlarınızla sohbet edin</summary>
<summary xml:lang="uk">Спілкуйтеся з вашими друзями у matrix</summary>
<summary xml:lang="x-test">xxChat with your friends on matrixxx</summary>
<summary xml:lang="zh-CN">在 Matrix 上与朋友聊天</summary>
<summary xml:lang="zh-TW">在 Matrix 上與您的朋友聊天</summary>
<summary>Chat on Matrix</summary>
<summary xml:lang="ar">دردش على ماتركس</summary>
<summary xml:lang="ca">Xat a Matrix</summary>
<summary xml:lang="ca-valencia">Xat a Matrix</summary>
<summary xml:lang="de">Über Matrix unterhalten</summary>
<summary xml:lang="en-GB">Chat on Matrix</summary>
<summary xml:lang="es">Charle en Matrix</summary>
<summary xml:lang="eu">Berriketa Matrix-en</summary>
<summary xml:lang="fi">Keskustelu Matrixissä</summary>
<summary xml:lang="fr">Discuter sur Matrix</summary>
<summary xml:lang="gl">Charlar en Matrix</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="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="sl">Klepet na Matrixu</summary>
<summary xml:lang="sv">Chatta på Matrix</summary>
<summary xml:lang="ta">மேட்ரிக்ஸுக்கான உரையாடல் செயலி</summary>
<summary xml:lang="tr">Matrix Üzerinde Sohbet</summary>
<summary xml:lang="uk">Спілкування у Matrix</summary>
<summary xml:lang="x-test">xxChat on Matrixxx</summary>
<summary xml:lang="zh-TW">Matrix 上聊天</summary>
<description>
<p>NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
<p xml:lang="ar">نيوتشات هو تطبيق دردشة يتيح لك الاستفادة الكاملة من شبكة Matrix. فهو يوفر لك طريقة آمنة لإرسال الرسائل النصية ومقاطع الفيديو والملفات الصوتية إلى عائلتك وزملائك وأصدقائك.</p>
<p xml:lang="ca">El NeoChat és una aplicació de xat que us permet aprofitar plenament la xarxa Matrix. Proporciona una manera segura d'enviar missatges de text, vídeos i arxius d'àudio a la vostra família, companys i amics.</p>
<p xml:lang="ca-valencia">NeoChat és una aplicació de xat que us permet aprofitar plenament la xarxa Matrix. Proporciona una manera segura d'enviar missatges de text, vídeos i arxius d'àudio a la vostra família, companys i amics.</p>
<p xml:lang="de">NeoChat ist eine Anwendung für Unterhaltungen mit allen Vorteilen des Matrix-Netzwerkes. Sie bietet eine sichere Möglichkeit zum Versenden von Nachrichten, Videos und Audiodateien and die Familienmitglieder.</p>
<p xml:lang="el">Το NeoChat είναι μια εφαρμογή συνομιλίας που σας επιτρέπει να εκμεταλλευτείτε πλήρως το δίκτυο Matrix. Σας παρέχει έναν ασφαλή τρόπο να στέλνετε μηνύματα κειμένου, βίντεο και αρχεία ήχου στην οικογένεια, τους συναδέλφους και τους φίλους σας.</p>
<p xml:lang="en-GB">NeoChat is a chat app that lets you take full advantage of the Matrix network. It provides you with a secure way to send text messages, videos and audio files to your family, colleagues and friends.</p>
<p xml:lang="eo">NeoChat estas babilej-apo, kiu ebligas al vi plene profiti de la Matrix-reto. Ĝi provizas al vi sekuran manieron sendi tekstmesaĝojn, filmetojn kaj sondosierojn al via familio, kolegoj kaj amikoj.</p>
<p xml:lang="es">NeoChat es una aplicación de chat que le permite aprovechar al máximo la red Matrix. Le proporciona un modo seguro de enviar mensajes de texto, vídeos y archivos de sonido a su familia, colegas y amigos.</p>
<p xml:lang="eu">NeoChat, Matrix sarearen abantaila guztiei probetsua ateratzeko aukera ematen dizun berriketa aplikaizo bat da. Zure familiari, kideei eta lagunei testu mezuak, bideoak eta audio fitxategiak era seguruan bidaltzeko aukera ematen dizu.</p>
<p xml:lang="fi">NeoChat on keskustelusovellus, jolla Matrix-verkosta saa täyden hyödyn. Se tarjoaa salatun kanavan lähettää perheelle, työkavereille ja ystäville tekstiviestejä sekä video- ja äänitiedostoja.</p>
<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="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>
@@ -97,6 +98,7 @@
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
<p xml:lang="nn">NeoChat er ein prateapp som lèt deg bruka all funksjonalitet i Matrix-nettverket. Du kan utveksla tekst, lyd og videoar med vennar, familie og kollegaar på ein trygg måte.</p>
<p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p>
<p xml:lang="ru">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>
@@ -107,6 +109,8 @@
<p xml:lang="ar">يهدف نيوتشات إلى أن يكون تطبيقًا كامل الميزات لمواصفات ماتركس. على هذا النحو يتم دعم كل شيء في المواصفات المستقرة الحالية مع الاستثناءات الملحوظة لـ VoIP والخيوط وبعض جوانب التشفير من طرف إلى طرف. هناك عدد قليل من الإغفالات الصغيرة الأخرى بسبب حقيقة أن مواصفات ماتركس تتطور باستمرار ، ولكن يبقى الهدف توفير الدعم النهائي للمواصفات بأكملها.</p>
<p xml:lang="ca">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptatge d'extrem a extrem. Hi ha algunes altres omissions més petites a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu segueix sent proporcionar suport eventual per a tota l'especificació.</p>
<p xml:lang="ca-valencia">NeoChat pretén ser una aplicació amb totes les característiques per a l'especificació de Matrix. Com a tal, s'ha implementat tota l'especificació actual estable amb les notables excepcions de la VoIP, fils i alguns aspectes de l'encriptació d'extrem a extrem. Hi ha algunes altres omissions més xicotetes a causa del fet que l'especificació de Matrix està evolucionant constantment, però l'objectiu seguix sent proporcionar suport eventual per a tota l'especificació.</p>
<p xml:lang="de">NeoChat versucht eine vollumfängliche Anwendung für die Spezifikation von Matrix zu sein. Damit wird alles der aktuellen stabilen Spezifikation mit den erwähnenswerten Ausnahmen von VoIP, Diskussionsfäden und ein paar Teilen der Ende-zu-Ende-Verschlüsselung unterstützt. Zudem sind andere kleinere Auslassungen vorhanden, da sich die Matrixspezifikation ständig weiterentwickelt. Nichtsdestotrotz soll letztendlich die gesamte Spezifikation unterstützt werden.</p>
<p xml:lang="el">Το NeoChat στοχεύει να είναι μια πλήρως εξοπλισμένη εφαρμογή για τις προδιαγραφές Matrix. Ως εκ τούτου, υποστηρίζονται όλα τα στοιχεία της τρέχουσας σταθερής προδιαγραφής με τις αξιοσημείωτες εξαιρέσεις του VoIP, των νημάτων και ορισμένων πτυχών της κρυπτογράφησης στα άκρα. Υπάρχουν μερικές άλλες μικρότερες παραλείψεις που οφείλονται στο γεγονός ότι η προδιαγραφή Matrix εξελίσσεται συνεχώς, αλλά ο στόχος παραμένει να παρέχεται τελικά υποστήριξη για ολόκληρη την προδιαγραφή.</p>
<p xml:lang="en-GB">NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly evolving but the aim remains to provide eventual support for the entire spec.</p>
<p xml:lang="eo">NeoChat celas esti plene kapabla aplikaĵo por la Matrix-specifo. Kiel tia, ĉio en la nuna stabila specifo kun la rimarkindaj esceptoj de VoIP, fadenoj kaj kelkaj aspektoj de Fin-al-Fina Ĉifrado estas subtenataj. Estas kelkaj aliaj pli malgrandaj preterlasoj pro la fakto, ke la Matrix-speco konstante evoluas, sed la celo restas provizi finfine subtenon por la tuta specifaĵo.</p>
<p xml:lang="es">NeoChat pretende ser una aplicación con todas las funciones para la especificación de Matrix. Como tal, admite todo en la especificación estable actual, con las notables excepciones de VoIP, subprocesos y algunas funciones de cifrado de extremo a extremo. Existen algunas omisiones menos importantes debido al hecho de que la especificación de Matrix está en constante evolución, pero el objetivo sigue siendo brindar compatibilidad final con toda la especificación.</p>
@@ -114,6 +118,7 @@
<p xml:lang="fi">NeoChat pyrkii olemaan Matrix-määritelmän täysominaisuuksinen sovellus, joten se tukee kaikkea nykyisessä vakaassa määritelmässä muutamaa huomattavaa poikkeusta lukuun ottamatta (VoIP, säikeet ja jotkin piirteet päästä päähän -salauksessa). Joitakin pienempiäkin puutteita on Matrix-määritelmän jatkuvan kehityksen vuoksi, mutta lopputavoitteena on tarjota määritelmän täysi tuki.</p>
<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 é implementar a especificación completa.</p>
<p xml:lang="he">NeoChat מתיימר להיות יישום עתיר יכולות לפי מפרט Matrix. כיוון שזה ייעודו, כל מה שבמפרט היציב עם חריגות משמעותיות כגון VoIP, שרשורים ועוד מגוון היבטים של הצפנה מקצה לקצה נתמכים גם הם. יש מספר השמטות קטן עקב העובדה שהמפרט של Matrix ממשיך להתפתח אך המטרה היא להמשיך לספק תמיכה בסופו של דבר לכל המפרט.</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>
@@ -135,6 +140,8 @@
<p xml:lang="ar">نظرًا لطبيعة تطوير مواصفات ماتركس، يدعم نيوتشات أيضًا العديد من الميزات غير المستقرة وهي:</p>
<p xml:lang="ca">A causa de la naturalesa del desenvolupament de l'especificació de Matrix, el NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
<p xml:lang="ca-valencia">A causa de la naturalea del desenvolupament de l'especificació de Matrix, NeoChat també implementa nombroses característiques inestables. Actualment són:</p>
<p xml:lang="de">Durch die Weiterentwicklung der Matrix-Spezifikation unterstützt auch NeoChat einige als noch instabil gekennzeichnete Funktionen. Derzeit sind das:</p>
<p xml:lang="el">Λόγω της φύσης της ανάπτυξης των προδιαγραφών Matrix, το NeoChat υποστηρίζει επίσης πολλά ασταθή χαρακτηριστικά. Επί του παρόντος, αυτά είναι:</p>
<p xml:lang="en-GB">Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:</p>
<p xml:lang="eo">Pro la naturo de la Matrix-specifevoluo NeoChat ankaŭ subtenas multajn malstabilajn funkciojn. Nuntempe ĉi tiuj estas:</p>
<p xml:lang="es">Debido a la naturaleza del desarrollo de la especificación de Matrix, NeoChat también permite numerosas funciones no estables, como:</p>
@@ -142,6 +149,7 @@
<p xml:lang="fi">Matrix-määritelmän kehittyessä NeoChat tukee myös monia epävakaita ominaisuuksia. Tällä hetkellä näitä ovat:</p>
<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="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>
@@ -165,6 +173,7 @@
<li xml:lang="ar">التصويت - MSC3381</li>
<li xml:lang="ca">Enquestes - MSC3381</li>
<li xml:lang="ca-valencia">Enquestes - MSC3381</li>
<li xml:lang="el">Δημοσκοπήσεις - MSC3381</li>
<li xml:lang="en-GB">Polls - MSC3381</li>
<li xml:lang="eo">Enketoj - MSC3381</li>
<li xml:lang="es">Encuestas - MSC3381</li>
@@ -172,6 +181,7 @@
<li xml:lang="fi">Kyselyt MSC3381</li>
<li xml:lang="fr">Sondages - MSC3381</li>
<li xml:lang="gl">Enquisas — MSC3381</li>
<li xml:lang="he">סקרים - MSC3381</li>
<li xml:lang="hu">Szavazások - MSC3381</li>
<li xml:lang="ia">Inquestas - MSC3381</li>
<li xml:lang="it">Sondaggi - MSC3381</li>
@@ -182,6 +192,7 @@
<li xml:lang="nn">Avstemmingar  MSC3381</li>
<li xml:lang="pl">Ankiety - MSC3381</li>
<li xml:lang="pt">Inquéritos - MSC3381</li>
<li xml:lang="ru">Голосования — MSC3381</li>
<li xml:lang="sl">Polls - MSC3381</li>
<li xml:lang="sv">Polls - MSC3381</li>
<li xml:lang="ta">வாக்கெடுப்புகள் - MSC3381</li>
@@ -193,6 +204,7 @@
<li xml:lang="ar">حزم الملصقات - MSC2545</li>
<li xml:lang="ca">Paquets d'adhesius - MSC2545</li>
<li xml:lang="ca-valencia">Paquets d'adhesius - MSC2545</li>
<li xml:lang="el">Πακέτα αυτοκόλλητων - MSC2545</li>
<li xml:lang="en-GB">Sticker Packs - MSC2545</li>
<li xml:lang="eo">Glumark-Pakoj - MSC2545</li>
<li xml:lang="es">Paquetes de pegatinas - MSC2545</li>
@@ -200,6 +212,7 @@
<li xml:lang="fi">Tarrapakkaukset MSC2545</li>
<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="hu">Matricacsomagok - MSC2545</li>
<li xml:lang="ia">Etiquetta gummate (sticker) -MSC2545</li>
<li xml:lang="it">Pacchetti di adesivi - MSC2545</li>
@@ -222,6 +235,7 @@
<li xml:lang="ar">موقع الأحداث - MSC3488</li>
<li xml:lang="ca">Esdeveniments d'ubicació - MSC3488</li>
<li xml:lang="ca-valencia">Esdeveniments d'ubicació - MSC3488</li>
<li xml:lang="el">Τοποθεσία γεγονότα - MSC3488</li>
<li xml:lang="en-GB">Location Events - MSC3488</li>
<li xml:lang="eo">Lokaj Eventoj - MSC3488</li>
<li xml:lang="es">Eventos de ubicación - MSC3488</li>
@@ -229,6 +243,7 @@
<li xml:lang="fi">Sijaintitapahtumat MSC3488</li>
<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="hu">Események helyadatai - MSC3488</li>
<li xml:lang="ia">Eventos de Location - MSC3488</li>
<li xml:lang="it">Località eventi - MSC3488</li>
@@ -285,6 +300,8 @@
<caption xml:lang="ar">العرض الرئيسة مع قائمة الغرف والدردشات و معلومات الغرفة</caption>
<caption xml:lang="ca">Vista principal amb la llista de sales, xats i informació de les sales</caption>
<caption xml:lang="ca-valencia">Vista principal amb la llista de sales, xats i informació de les sales</caption>
<caption xml:lang="de">Hauptansicht mit Raumliste, Unterhaltung und Raum-Informationen</caption>
<caption xml:lang="el">Κύρια προβολή με λίστα δωματίων, συνομιλία και πληροφορίες δωματίων</caption>
<caption xml:lang="en-GB">Main view with room list, chat, and room information</caption>
<caption xml:lang="eo">Ĉefa vido kun ĉambra listo, babilejo kaj ĉambra informo</caption>
<caption xml:lang="es">Vista principal con la lista de salas, chat e información de la sala</caption>
@@ -292,6 +309,7 @@
<caption xml:lang="fi">Päänäkymä, jossa huoneluettelo, keskustelu ja huoneen tiedot</caption>
<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="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>
@@ -317,12 +335,16 @@
<caption xml:lang="ar">اكتشف مجتمعات جديدة مع فضاءات ماتركس</caption>
<caption xml:lang="ca">Descobriu comunitats noves amb els espais de Matrix</caption>
<caption xml:lang="ca-valencia">Descobriu comunitats noves amb els espais de Matrix</caption>
<caption xml:lang="de">Neue Gemeinschaften mit den Umgebungen von Matrix erkunden</caption>
<caption xml:lang="el">Ανακαλύψτε νέες κοινότητες με το Matrix Spaces</caption>
<caption xml:lang="en-GB">Discover new communities with Matrix Spaces</caption>
<caption xml:lang="eo">Malkovru novajn komunumojn per Matrix Spaces</caption>
<caption xml:lang="es">Descubra nuevas comunidades con los espacios de Matrix</caption>
<caption xml:lang="eu">Ezagutu komunitate berriak Matrixeko Tokiak erabiliz</caption>
<caption xml:lang="fi">Löydä uusia yhteisöjä Matrix Spacesillä</caption>
<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="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>
@@ -331,8 +353,10 @@
<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="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>
<caption xml:lang="tr">Matrix Alanlar ile yeni topluluklar keşfedin</caption>
<caption xml:lang="uk">Пошук нових спільнот за допомогою Matrix Spaces</caption>
<caption xml:lang="x-test">xxDiscover new communities with Matrix Spacesxx</caption>
@@ -351,6 +375,8 @@
<caption xml:lang="ar">العرض الرئيسة مع قائمة الغرف والدردشات و معلومات الغرفة</caption>
<caption xml:lang="ca">Vista principal amb la llista de sales, xats i informació de les sales</caption>
<caption xml:lang="ca-valencia">Vista principal amb la llista de sales, xats i informació de les sales</caption>
<caption xml:lang="de">Hauptansicht mit Raumliste, Unterhaltung und Raum-Informationen</caption>
<caption xml:lang="el">Κύρια προβολή με λίστα δωματίων, συνομιλία και πληροφορίες δωματίων</caption>
<caption xml:lang="en-GB">Main view with room list, chat, and room information</caption>
<caption xml:lang="eo">Ĉefa vido kun ĉambra listo, babilejo kaj ĉambra informo</caption>
<caption xml:lang="es">Vista principal con la lista de salas, chat e información de la sala</caption>
@@ -358,6 +384,7 @@
<caption xml:lang="fi">Päänäkymä, jossa huoneluettelo, keskustelu ja huoneen tiedot</caption>
<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="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>
@@ -384,6 +411,8 @@
<caption xml:lang="ca">Pantalla d'inici de sessió</caption>
<caption xml:lang="ca-valencia">Pantalla d'inici de sessió</caption>
<caption xml:lang="cs">Přihlašovací obrazovka</caption>
<caption xml:lang="de">Anmeldebildschirm</caption>
<caption xml:lang="el">Οθόνη εισόδου</caption>
<caption xml:lang="en-GB">Login screen</caption>
<caption xml:lang="eo">Ensaluta ekrano</caption>
<caption xml:lang="es">Pantalla de inicio de sesión</caption>
@@ -391,6 +420,7 @@
<caption xml:lang="fi">Kirjautumisnäkymä</caption>
<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="hu">Bejelentkező képernyő</caption>
<caption xml:lang="ia">Schermo de accesso</caption>
<caption xml:lang="it">Schermata di accesso</caption>
@@ -415,6 +445,11 @@
<content_attribute id="social-chat">intense</content_attribute>
</content_rating>
<releases>
<release version="24.12.0" date="2024-12-12"/>
<release version="24.08.3" date="2024-11-07"/>
<release version="24.08.2" date="2024-10-10"/>
<release version="24.08.1" date="2024-09-12"/>
<release version="24.08.0" date="2024-08-22"/>
<release version="24.05.2" date="2024-07-04"/>
<release version="24.05.1" date="2024-06-13"/>
<release version="24.05.0" date="2024-05-23"/>

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

5879
po/gl/neochat.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,122 @@
<?xml version="1.0" ?>
<!DOCTYPE refentry PUBLIC "-//KDE//DTD DocBook XML V4.5-Based Variant V1.1//EN" "dtd/kdedbx45.dtd" [
<!ENTITY % Slovenian "INCLUDE">
]>
<!--
SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
SPDX-License-Identifier: CC-BY-SA-4.0
-->
<refentry lang="&language;">
<refentryinfo>
<title
>Uporabniški priročnik za NeoChat</title>
<author
><firstname
>Carl</firstname
><surname
>Schwan</surname
> <contrib
>Stran z navodili za NeoChat.</contrib
> <email
>carl@carlschwan.eu</email
></author>
<date
>01.11.2022</date>
<releaseinfo
>22,09</releaseinfo>
<productname
>NeoChat</productname>
</refentryinfo>
<refmeta>
<refentrytitle>
<command
>neochat</command>
</refentrytitle>
<manvolnum
>1</manvolnum>
</refmeta>
<refnamediv>
<refname
>neochat</refname>
<refpurpose
>Odjemalec za interakcijo s protokolom za matrično sporočanje</refpurpose>
</refnamediv>
<!-- body begins here -->
<refsynopsisdiv id='synopsis'>
<cmdsynopsis
><command
>neochat</command
> <arg choice="opt"
><replaceable
>URI</replaceable
></arg
> </cmdsynopsis>
</refsynopsisdiv>
<refsect1 id="description">
<title
>Opis</title>
<para
><command
>neochat</command
> je aplikacija za klepet za matrični protokol, ki deluje na namizju in mobilni napravi. </para>
</refsect1>
<refsect1 id="options"
><title
>Možnosti</title>
<variablelist>
<varlistentry>
<term
><option
>URI</option
></term>
<listitem>
<para
>Uri matrike za uporabnika ali sobo. npr. matrix:u/user:example.org in matrix:r/root:example.org. Tako bo NeoChat poskušal odpreti dano sobo ali pogovor. </para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1 id="bug">
<title
>Poročanje o napakah</title>
<para
>Napake in zahteve po funkcijah lahko prijavite na <ulink url="https://bugs.kde.org/enter_bug.cgi?product=NeoChat&amp;component=General"
>https://bugs.kde.org/enter_bug.cgi? product=NeoChat&amp;component=General</ulink
></para>
</refsect1>
<refsect1>
<title
>Poglej tudi</title>
<simplelist>
<member
>Seznam pogostih vprašanj o Matrix <ulink url="https://matrix.org/faq/"
>https://matrix.org/faq/</ulink
> </member>
<member
>kf5options(7)</member>
<member
>qt5options(7)</member>
</simplelist>
</refsect1>
<refsect1 id="copyright"
><title
>Avtorske pravice</title>
<para
>Avtorske pravice &copy; 2020-2022 Tobias Fella </para>
<para
>Avtorske pravice &copy; 2020-2022 Carl Schwan </para>
<para
>Licenca: GNU General Public različica 3 ali novejša &lt;<ulink url="https://www.gnu.org/licenses/gpl-3.0.html"
>https://www.gnu.org/licenses/gpl-3.0 .html</ulink
>&gt;</para>
</refsect1>
</refentry>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +0,0 @@
# SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
#
# SPDX-License-Identifier: GPL-3.0-or-later
kdoctools_create_manpage(man-neochat.1.docbook 1 INSTALL_DESTINATION ${MAN_INSTALL_DIR})

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

158
snapcraft.yaml Normal file
View File

@@ -0,0 +1,158 @@
# SPDX-FileCopyrightText: 2024 Scarlett Moore <sgmoore@kde.org>
#
# SPDX-License-Identifier: CC0-1.0
---
name: neochat
base: core22
adopt-info: neochat
grade: stable
confinement: strict
apps:
neochat:
extensions:
- kde-neon-6
command: usr/bin/neochat
common-id: org.kde.neochat
desktop: usr/share/applications/org.kde.neochat.desktop
plugs:
- home
- removable-media
- audio-playback
- unity7
- network
- network-bind
- network-manager-observe
- password-manager-service
- accounts-service
compression: lzo
slots:
session-dbus-interface:
interface: dbus
name: org.kde.neochat
bus: session
parts:
olm:
source: https://gitlab.matrix.org/matrix-org/olm.git
source-depth: 1
source-tag: '3.2.12'
plugin: cmake
cmake-parameters:
- -DCMAKE_BUILD_TYPE=Release
- -DCMAKE_INSTALL_PREFIX=/usr
prime:
- -usr/include
- -usr/lib/*/pkgconfig
- -usr/lib/*/cmake
libsecret:
source: https://gitlab.gnome.org/GNOME/libsecret.git
source-tag: '0.21.4'
source-depth: 1
plugin: meson
meson-parameters:
- --prefix=/usr
- -Doptimization=3
- -Ddebug=true
- -Dmanpage=false
- -Dvapi=false
- -Dintrospection=false
- -Dcrypto=disabled
- -Dgtk_doc=false
build-packages:
- meson
- libglib2.0-dev
- libgcrypt20-dev
prime:
- -usr/include
- -usr/lib/*/pkgconfig
qtkeychain:
after: [libsecret]
source: https://github.com/frankosterfeld/qtkeychain.git
source-tag: 0.14.3
source-depth: 1
plugin: cmake
build-environment:
- PKG_CONFIG_PATH: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET/pkgconfig:$PKG_CONFIG_PATH
cmake-parameters:
- -DCMAKE_INSTALL_PREFIX=/usr
- -DCMAKE_BUILD_TYPE=Release
- -DBUILD_TRANSLATIONS=NO
- -DBUILD_WITH_QT6=ON
prime:
- -usr/include
- -usr/lib/*/pkgconfig
- -usr/lib/*/cmake
libquotient:
after:
- olm
- qtkeychain
source: https://github.com/quotient-im/libQuotient.git
source-tag: 0.9.1
source-depth: 1
plugin: cmake
build-packages:
- libssl-dev
cmake-parameters:
- -DCMAKE_INSTALL_PREFIX=/usr
- -DCMAKE_BUILD_TYPE=Release
- -DBUILD_TESTING=OFF
- -DQuotient_ENABLE_E2EE=ON
- -DBUILD_WITH_QT6=ON
prime:
- -usr/include
- -usr/lib/*/pkgconfig
- -usr/lib/*/cmake
kquickimageeditor:
source: https://invent.kde.org/libraries/kquickimageeditor.git
source-tag: 'v0.3.0'
source-depth: 1
plugin: cmake
cmake-parameters:
- -DCMAKE_INSTALL_PREFIX=/usr
- -DCMAKE_BUILD_TYPE=Release
- -DBUILD_WITH_QT6=ON
- -DBUILD_TESTING=OFF
prime:
- -usr/include
- -usr/lib/*/pkgconfig
- -usr/lib/*/cmake
neochat:
after:
- qtkeychain
- libquotient
- kquickimageeditor
parse-info:
- usr/share/metainfo/org.kde.neochat.appdata.xml
source: https://invent.kde.org/network/neochat.git
source-tag: 'v24.08.1'
plugin: cmake
build-packages:
- cmark
- libcmark-dev
- libsqlite3-dev
- libvulkan-dev
- libxkbcommon-dev
- libicu-dev
- libpulse0
cmake-parameters:
- -DCMAKE_INSTALL_PREFIX=/usr
- -DCMAKE_BUILD_TYPE=Release
- -DBUILD_TESTING=OFF
prime:
- -usr/share/man
deps:
after: [neochat]
plugin: nil
stage-packages:
- libcmark0.30.2
prime:
- usr/lib/*/libcmark.so*

View File

@@ -10,8 +10,6 @@ endif()
add_library(neochat STATIC
controller.cpp
controller.h
actionshandler.cpp
actionshandler.h
models/emojimodel.cpp
models/emojimodel.h
emojitones.cpp
@@ -134,6 +132,8 @@ add_library(neochat STATIC
jobs/neochatdeletedevicejob.h
jobs/neochatchangepasswordjob.cpp
jobs/neochatchangepasswordjob.h
jobs/neochatgetcommonroomsjob.cpp
jobs/neochatgetcommonroomsjob.h
mediasizehelper.cpp
mediasizehelper.h
eventhandler.cpp
@@ -188,12 +188,24 @@ add_library(neochat STATIC
threepidbindhelper.h
models/readmarkermodel.cpp
models/readmarkermodel.h
neochatroommember.cpp
neochatroommember.h
models/threadmodel.cpp
models/threadmodel.h
enums/messagetype.h
messagecomponent.h
)
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
QT_QML_SINGLETON_TYPE TRUE
)
if(ANDROID OR WIN32)
set_source_files_properties(qml/ShareActionStub.qml PROPERTIES
QT_QML_SOURCE_TYPENAME ShareAction
)
endif()
ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat
QML_FILES
@@ -232,15 +244,10 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/EmojiSas.qml
qml/ConfirmDeactivateAccountDialog.qml
qml/VerificationCanceled.qml
qml/GlobalMenu.qml
qml/EditMenu.qml
qml/MessageDelegateContextMenu.qml
qml/FileDelegateContextMenu.qml
qml/MessageSourceSheet.qml
qml/ReportSheet.qml
qml/ConfirmEncryptionDialog.qml
qml/RemoveSheet.qml
qml/BanSheet.qml
qml/RoomSearchPage.qml
qml/LocationChooser.qml
qml/TimelineView.qml
@@ -284,6 +291,8 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/ConsentDialog.qml
qml/AskDirectChatConfirmation.qml
qml/HoverLinkIndicator.qml
qml/AvatarNotification.qml
qml/ReasonDialog.qml
DEPENDENCIES
QtCore
QtQuick
@@ -301,16 +310,16 @@ add_subdirectory(devtools)
add_subdirectory(login)
add_subdirectory(chatbar)
if(UNIX)
qt_target_qml_sources(neochat QML_FILES qml/ShareAction.qml)
else()
set_source_files_properties(qml/ShareActionStub.qml PROPERTIES
QT_RESOURCE_ALIAS qml/ShareAction.qml
if(NOT ANDROID AND NOT WIN32)
qt_target_qml_sources(neochat QML_FILES
qml/ShareAction.qml
qml/GlobalMenu.qml
qml/EditMenu.qml
)
else()
qt_target_qml_sources(neochat QML_FILES qml/ShareActionStub.qml)
endif()
configure_file(config-neochat.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-neochat.h)
if(WIN32)
@@ -380,7 +389,7 @@ if(NOT ANDROID)
target_compile_definitions(neochat PUBLIC -DHAVE_ICU)
endif()
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
target_compile_definitions(neochat PUBLIC -DHAVE_RUNNER)
target_compile_definitions(neochat PUBLIC -DHAVE_X11=1)
target_sources(neochat PRIVATE runner.cpp)
@@ -421,7 +430,7 @@ if (TARGET KF6::Crash)
target_link_libraries(neochat PUBLIC KF6::Crash)
endif()
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
kconfig_target_kcfg_file(neochat FILE neochatconfig.kcfg CLASS_NAME NeoChatConfig MUTATORS GENERATE_PROPERTIES DEFAULT_VALUE_GETTERS PARENT_IN_CONSTRUCTOR SINGLETON GENERATE_MOC QML_REGISTRATION)
if(NEOCHAT_FLATPAK)
target_compile_definitions(neochat PUBLIC NEOCHAT_FLATPAK)
@@ -436,8 +445,11 @@ if(ANDROID)
target_sources(neochat-app PRIVATE notifyrc.qrc)
target_link_libraries(neochat PUBLIC Qt::Svg OpenSSL::SSL)
kirigami_package_breeze_icons(ICONS
"arrow-down"
"arrow-up"
"arrow-down-symbolic"
"arrow-up-symbolic"
"arrow-up-double-symbolic"
"arrow-left-symbolic"
"arrow-right-symbolic"
"checkmark"
"help-about"
"im-user"
@@ -446,6 +458,7 @@ if(ANDROID)
"mail-attachment"
"dialog-cancel"
"preferences-desktop-emoticons"
"preferences-security"
"document-open"
"document-save"
"document-send"

View File

@@ -1,170 +0,0 @@
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "actionshandler.h"
#include <Quotient/csapi/joining.h>
#include <Quotient/events/roommemberevent.h>
#include <cmark.h>
#include <KLocalizedString>
#include <QStringBuilder>
#include "models/actionsmodel.h"
#include "neochatconfig.h"
#include "texthandler.h"
using namespace Quotient;
ActionsHandler::ActionsHandler(QObject *parent)
: QObject(parent)
{
}
NeoChatRoom *ActionsHandler::room() const
{
return m_room;
}
void ActionsHandler::setRoom(NeoChatRoom *room)
{
if (m_room == room) {
return;
}
m_room = room;
Q_EMIT roomChanged();
}
void ActionsHandler::handleMessageEvent(ChatBarCache *chatBarCache)
{
if (!m_room || !chatBarCache) {
qWarning() << "ActionsHandler::handleMessageEvent - called with m_room and/or chatBarCache set to nullptr.";
return;
}
checkEffects(chatBarCache->text());
if (!chatBarCache->attachmentPath().isEmpty()) {
QUrl url(chatBarCache->attachmentPath());
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
m_room->uploadFile(QUrl(path), chatBarCache->text().isEmpty() ? path.mid(path.lastIndexOf(u'/') + 1) : chatBarCache->text());
chatBarCache->setAttachmentPath({});
chatBarCache->setText({});
return;
}
QString handledText = chatBarCache->text();
handledText = handleMentions(handledText, chatBarCache->mentions());
handleMessage(m_room->mainCache()->text(), handledText, chatBarCache);
}
QString ActionsHandler::handleMentions(QString handledText, QList<Mention> *mentions)
{
std::sort(mentions->begin(), mentions->end(), [](const auto &a, const auto &b) -> bool {
return a.cursor.anchor() > b.cursor.anchor();
});
for (const auto &mention : *mentions) {
if (mention.text.isEmpty() || mention.id.isEmpty()) {
continue;
}
handledText = handledText.replace(mention.cursor.anchor(),
mention.cursor.position() - mention.cursor.anchor(),
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text.toHtmlEscaped(), mention.id));
}
mentions->clear();
return handledText;
}
void ActionsHandler::handleMessage(const QString &text, QString handledText, ChatBarCache *chatBarCache)
{
Q_ASSERT(m_room);
if (NeoChatConfig::allowQuickEdit()) {
QRegularExpression sed(QStringLiteral("^s/([^/]*)/([^/]*)(/g)?$"));
auto match = sed.match(text);
if (match.hasMatch()) {
const QString regex = match.captured(1);
const QString replacement = match.captured(2).toHtmlEscaped();
const QString flags = match.captured(3);
for (auto it = m_room->messageEvents().crbegin(); it != m_room->messageEvents().crend(); it++) {
if (const auto event = eventCast<const RoomMessageEvent>(&**it)) {
if (event->senderId() == m_room->localMember().id() && event->hasTextContent()) {
QString originalString;
if (event->content()) {
originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content())->body;
} else {
originalString = event->plainBody();
}
if (flags == "/g"_ls) {
m_room->postHtmlMessage(handledText, originalString.replace(regex, replacement), event->msgtype(), {}, event->id());
} else {
m_room->postHtmlMessage(handledText,
originalString.replace(originalString.indexOf(regex), regex.size(), replacement),
event->msgtype(),
{},
event->id());
}
return;
}
}
}
}
}
auto messageType = RoomMessageEvent::MsgType::Text;
if (handledText.startsWith(QLatin1Char('/'))) {
for (const auto &action : ActionsModel::instance().allActions()) {
if (handledText.indexOf(action.prefix) == 1
&& (handledText.indexOf(" "_ls) == action.prefix.length() + 1 || handledText.length() == action.prefix.length() + 1)) {
handledText = action.handle(handledText.mid(action.prefix.length() + 1).trimmed(), m_room, chatBarCache);
if (action.messageType.has_value()) {
messageType = *action.messageType;
}
if (action.messageAction) {
break;
} else {
return;
}
}
}
}
TextHandler textHandler;
textHandler.setData(handledText);
handledText = textHandler.handleSendText();
if (handledText.count("<p>"_ls) == 1 && handledText.count("</p>"_ls) == 1) {
handledText.remove("<p>"_ls);
handledText.remove("</p>"_ls);
}
if (handledText.length() == 0) {
return;
}
m_room->postMessage(text, handledText, messageType, chatBarCache->replyId(), chatBarCache->editId(), chatBarCache->threadId());
}
void ActionsHandler::checkEffects(const QString &text)
{
std::optional<QString> effect = std::nullopt;
if (text.contains(QStringLiteral("\u2744"))) {
effect = QLatin1String("snowflake");
} else if (text.contains(QStringLiteral("\u1F386"))) {
effect = QLatin1String("fireworks");
} else if (text.contains(QStringLiteral("\u2F387"))) {
effect = QLatin1String("fireworks");
} else if (text.contains(QStringLiteral("\u1F389"))) {
effect = QLatin1String("confetti");
} else if (text.contains(QStringLiteral("\u1F38A"))) {
effect = QLatin1String("confetti");
}
if (effect.has_value()) {
Q_EMIT showEffect(*effect);
}
}
#include "moc_actionshandler.cpp"

View File

@@ -1,66 +0,0 @@
// SPDX-FileCopyrightText: 2020 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QObject>
#include <QQmlEngine>
#include <Quotient/events/roommessageevent.h>
#include "chatbarcache.h"
#include "neochatroom.h"
class NeoChatRoom;
/**
* @class ActionsHandler
*
* This class handles chat messages ready for posting to a room.
*
* Everything that needs to be done to prepare the message for posting in a room
* including:
* - File handling
* - User mentions
* - Quick edits
* - Chat actions
* - Custom emojis
*
* @note A chat action is a message starting with /, resulting in something other
* than a normal message being sent (e.g. /me, /join).
*
* @sa ActionsModel, NeoChatRoom
*/
class ActionsHandler : public QObject
{
Q_OBJECT
QML_ELEMENT
/**
* @brief The room that messages will be sent to.
*/
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
public:
explicit ActionsHandler(QObject *parent = nullptr);
[[nodiscard]] NeoChatRoom *room() const;
void setRoom(NeoChatRoom *room);
Q_SIGNALS:
void roomChanged();
void showEffect(const QString &effect);
public Q_SLOTS:
/**
* @brief Pre-process text and send message event.
*/
void handleMessageEvent(ChatBarCache *chatBarCache);
private:
QPointer<NeoChatRoom> m_room;
void checkEffects(const QString &text);
QString handleMentions(QString handledText, QList<Mention> *mentions);
void handleMessage(const QString &text, QString handledText, ChatBarCache *chatBarCache);
};

View File

@@ -11,7 +11,6 @@ ecm_add_qml_module(chatbar GENERATE_PLUGIN_SOURCE
CompletionMenu.qml
EmojiDelegate.qml
EmojiGrid.qml
ReplyPane.qml
PieProgressBar.qml
EmojiPicker.qml
EmojiDialog.qml

View File

@@ -53,14 +53,6 @@ QQC2.Control {
}
}
/**
* @brief The ActionsHandler object to use.
*
* This is expected to have the correct room set otherwise messages will be sent
* to the wrong room.
*/
required property ActionsHandler actionsHandler
/**
* @brief The list of actions in the ChatBar.
*
@@ -175,6 +167,7 @@ QQC2.Control {
Layout.fillWidth: true
Layout.margins: Kirigami.Units.largeSpacing
Layout.preferredHeight: active ? item.implicitHeight : 0
active: visible
visible: root.currentRoom.mainCache.replyId.length > 0 || root.currentRoom.mainCache.attachmentPath.length > 0
@@ -183,13 +176,14 @@ QQC2.Control {
RowLayout {
QQC2.ScrollView {
id: chatBarScrollView
Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.fillWidth: true
Layout.maximumHeight: Kirigami.Units.gridUnit * 8
Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.minimumHeight: Kirigami.Units.gridUnit * 2
Layout.minimumHeight: Kirigami.Units.gridUnit * 3
// HACK: This is to stop the ScrollBar flickering on and off as the height is increased
QQC2.ScrollBar.vertical.policy: chatBarHeightAnimation.running && implicitHeight <= height ? QQC2.ScrollBar.AlwaysOff : QQC2.ScrollBar.AsNeeded
@@ -219,7 +213,7 @@ QQC2.Control {
}
onTextChanged: {
if (!repeatTimer.running && Config.typingNotifications) {
if (!repeatTimer.running && NeoChatConfig.typingNotifications) {
var textExists = text.length > 0;
root.currentRoom.sendTypingNotification(textExists);
textExists ? repeatTimer.start() : repeatTimer.stop();
@@ -258,20 +252,22 @@ QQC2.Control {
}
}
Keys.onEnterPressed: event => {
const controlIsPressed = event.modifiers & Qt.ControlModifier;
if (completionMenu.visible) {
completionMenu.complete();
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile) {
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile || NeoChatConfig.sendMessageWith === 1 && !controlIsPressed || NeoChatConfig.sendMessageWith === 0 && controlIsPressed) {
textField.insert(cursorPosition, "\n");
} else {
} else if (NeoChatConfig.sendMessageWith === 0 && !controlIsPressed || NeoChatConfig.sendMessageWith === 1 && controlIsPressed) {
_private.postMessage();
}
}
Keys.onReturnPressed: event => {
const controlIsPressed = event.modifiers & Qt.ControlModifier;
if (completionMenu.visible) {
completionMenu.complete();
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile) {
} else if (event.modifiers & Qt.ShiftModifier || Kirigami.Settings.isMobile || NeoChatConfig.sendMessageWith === 1 && !controlIsPressed || NeoChatConfig.sendMessageWith === 0 && controlIsPressed) {
textField.insert(cursorPosition, "\n");
} else {
} else if (NeoChatConfig.sendMessageWith === 0 && !controlIsPressed || NeoChatConfig.sendMessageWith === 1 && controlIsPressed) {
_private.postMessage();
}
}
@@ -325,12 +321,11 @@ QQC2.Control {
id: actionsRow
spacing: 0
Layout.alignment: Qt.AlignBottom
Layout.bottomMargin: Kirigami.Units.smallSpacing * 1.5
Layout.bottomMargin: Kirigami.Units.smallSpacing * 4
Repeater {
model: root.actions
delegate: QQC2.ToolButton {
Layout.alignment: Qt.AlignVCenter
icon.name: modelData.isBusy ? "" : (modelData.icon.name.length > 0 ? modelData.icon.name : modelData.icon.source)
onClicked: modelData.trigger()
@@ -347,29 +342,45 @@ QQC2.Control {
}
}
}
DelegateSizeHelper {
id: chatBarSizeHelper
startBreakpoint: Kirigami.Units.gridUnit * 46
endBreakpoint: Kirigami.Units.gridUnit * 66
startPercentWidth: 100
endPercentWidth: Config.compactLayout ? 100 : 85
maxWidth: Config.compactLayout ? -1 : Kirigami.Units.gridUnit * 60
endPercentWidth: NeoChatConfig.compactLayout ? 100 : 85
maxWidth: NeoChatConfig.compactLayout ? -1 : Kirigami.Units.gridUnit * 60
parentWidth: root.width
}
Component {
id: replyPane
ReplyPane {
userName: _private.chatBarCache.relationUser.displayName
userColor: _private.chatBarCache.relationUser.color
userAvatar: _private.chatBarCache.relationUser.avatarUrl
text: _private.chatBarCache.relationMessage
Item {
implicitWidth: replyComponent.implicitWidth
implicitHeight: replyComponent.implicitHeight
ReplyComponent {
id: replyComponent
replyEventId: _private.chatBarCache.replyId
replyAuthor: _private.chatBarCache.relationAuthor
replyContentModel: _private.chatBarCache.relationEventContentModel
maxContentWidth: paneLoader.item.width
}
QQC2.Button {
id: cancelButton
onCancel: {
_private.chatBarCache.replyId = "";
_private.chatBarCache.attachmentPath = "";
anchors.top: parent.top
anchors.right: parent.right
display: QQC2.AbstractButton.IconOnly
text: i18nc("@action:button", "Cancel reply")
icon.name: "dialog-close"
onClicked: {
_private.chatBarCache.replyId = "";
_private.chatBarCache.attachmentPath = "";
}
QQC2.ToolTip.text: text
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
}
}
}
@@ -391,11 +402,10 @@ QQC2.Control {
onChatBarCacheChanged: documentHandler.chatBarCache = chatBarCache
function postMessage() {
root.actionsHandler.handleMessageEvent(_private.chatBarCache);
_private.chatBarCache.postMessage();
repeatTimer.stop();
root.currentRoom.markAllMessagesAsRead();
textField.clear();
_private.chatBarCache.replyId = "";
messageSent();
}

View File

@@ -33,7 +33,7 @@ QQC2.ItemDelegate {
Kirigami.Icon {
width: Kirigami.Units.gridUnit * 0.5
height: Kirigami.Units.gridUnit * 0.5
source: "arrow-down"
source: "arrow-down-symbolic"
anchors.bottom: parent.bottom
anchors.right: parent.right
visible: root.showTones
@@ -43,6 +43,9 @@ QQC2.ItemDelegate {
anchors.fill: parent
visible: root.emoji.startsWith("mxc") || root.isImage
source: visible ? root.emoji : ""
fillMode: Image.PreserveAspectFit
sourceSize.width: width
sourceSize.height: height
}
}

View File

@@ -84,6 +84,7 @@ QQC2.ScrollView {
Kirigami.PlaceholderMessage {
anchors.centerIn: parent
icon.name: root.stickers ? "stickers" : "preferences-desktop-emoticons"
text: root.stickers ? i18n("No stickers") : i18n("No emojis")
visible: emojis.count === 0
}

View File

@@ -66,6 +66,7 @@ ColumnLayout {
Layout.fillWidth: true
Layout.preferredHeight: root.categoryIconSize + QQC2.ScrollBar.horizontal.height
QQC2.ScrollBar.horizontal.height: QQC2.ScrollBar.horizontal.visible ? QQC2.ScrollBar.horizontal.implicitHeight : 0
visible: categories.count !== 0
ListView {
id: categories
@@ -201,8 +202,13 @@ ColumnLayout {
width: root.categoryIconSize
height: width
checked: stickerModel.packIndex === model.index
padding: Kirigami.Units.largeSpacing
contentItem: Image {
source: model.avatarUrl
fillMode: Image.PreserveAspectFit
sourceSize.width: width
sourceSize.height: height
}
QQC2.ToolTip.text: model.name
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay

View File

@@ -1,98 +0,0 @@
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.de>
// SPDX-FileCopyrightText: 2020 Noah Davis <noahadvs@gmail.com>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as QQC2
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.neochat
RowLayout {
id: root
property string userName
property color userColor
property url userAvatar: ""
property var text
signal cancel
Rectangle {
id: verticalBorder
Layout.fillHeight: true
implicitWidth: Kirigami.Units.smallSpacing
color: userColor
}
ColumnLayout {
RowLayout {
KirigamiComponents.Avatar {
id: replyAvatar
implicitWidth: Kirigami.Units.iconSizes.small
implicitHeight: Kirigami.Units.iconSizes.small
source: userAvatar
name: userName
color: userColor
}
QQC2.Label {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft
color: userColor
text: userName
elide: Text.ElideRight
}
}
QQC2.TextArea {
id: textArea
Layout.fillWidth: true
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
text: "<style> a{color:" + Kirigami.Theme.linkColor + ";}.user-pill{}</style>" + replyTextMetrics.elidedText
selectByMouse: true
selectByKeyboard: true
readOnly: true
wrapMode: TextEdit.Wrap
textFormat: TextEdit.RichText
background: Item {}
HoverHandler {
cursorShape: textArea.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor
}
TextMetrics {
id: replyTextMetrics
text: root.text
font: textArea.font
elide: Qt.ElideRight
elideWidth: textArea.width * 2 - Kirigami.Units.smallSpacing * 2
}
}
}
QQC2.ToolButton {
id: cancelButton
Layout.alignment: Qt.AlignVCenter
display: QQC2.AbstractButton.IconOnly
text: i18nc("@action:button", "Cancel reply")
icon.name: "dialog-close"
onClicked: {
root.cancel();
}
QQC2.ToolTip.text: text
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
}
}

View File

@@ -7,7 +7,9 @@
#include "chatdocumenthandler.h"
#include "eventhandler.h"
#include "models/actionsmodel.h"
#include "neochatroom.h"
#include "texthandler.h"
ChatBarCache::ChatBarCache(QObject *parent)
: QObject(parent)
@@ -28,6 +30,37 @@ void ChatBarCache::setText(const QString &text)
Q_EMIT textChanged();
}
QString ChatBarCache::sendText() const
{
if (!attachmentPath().isEmpty()) {
QUrl url(attachmentPath());
auto path = url.isLocalFile() ? url.toLocalFile() : url.toString();
return text().isEmpty() ? path.mid(path.lastIndexOf(u'/') + 1) : text();
}
return formatMentions();
}
QString ChatBarCache::formatMentions() const
{
auto mentions = m_mentions;
std::sort(mentions.begin(), mentions.end(), [](const auto &a, const auto &b) {
return a.cursor.anchor() > b.cursor.anchor();
});
auto formattedText = text();
for (const auto &mention : mentions) {
if (mention.text.isEmpty() || mention.id.isEmpty()) {
continue;
}
formattedText = formattedText.replace(mention.cursor.anchor(),
mention.cursor.position() - mention.cursor.anchor(),
QStringLiteral("[%1](https://matrix.to/#/%2)").arg(mention.text.toHtmlEscaped(), mention.id));
}
return formattedText;
}
bool ChatBarCache::isReplying() const
{
return m_relationType == Reply && !m_relationId.isEmpty();
@@ -53,6 +86,7 @@ void ChatBarCache::setReplyId(const QString &replyId)
m_relationType = Reply;
}
m_attachmentPath = QString();
delete m_relationContentModel;
Q_EMIT relationIdChanged(oldEventId, m_relationId);
Q_EMIT attachmentPathChanged();
}
@@ -82,11 +116,12 @@ void ChatBarCache::setEditId(const QString &editId)
m_relationType = Edit;
}
m_attachmentPath = QString();
delete m_relationContentModel;
Q_EMIT relationIdChanged(oldEventId, m_relationId);
Q_EMIT attachmentPathChanged();
}
Quotient::RoomMember ChatBarCache::relationUser() const
Quotient::RoomMember ChatBarCache::relationAuthor() const
{
if (parent() == nullptr) {
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
@@ -119,12 +154,33 @@ QString ChatBarCache::relationMessage() const
}
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
EventHandler eventhandler(room, &**event);
return eventhandler.getMarkdownBody();
return EventHandler::markdownBody(&**event);
}
return {};
}
MessageContentModel *ChatBarCache::relationEventContentModel()
{
if (parent() == nullptr) {
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
return nullptr;
}
if (m_relationId.isEmpty()) {
return nullptr;
}
if (m_relationContentModel != nullptr) {
return m_relationContentModel;
}
auto room = dynamic_cast<NeoChatRoom *>(parent());
if (room == nullptr) {
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
return nullptr;
}
m_relationContentModel = new MessageContentModel(room, m_relationId, true);
return m_relationContentModel;
}
bool ChatBarCache::isThreaded() const
{
return !m_threadId.isEmpty();
@@ -140,8 +196,8 @@ void ChatBarCache::setThreadId(const QString &threadId)
if (m_threadId == threadId) {
return;
}
m_threadId = threadId;
Q_EMIT threadIdChanged();
const auto oldThreadId = std::exchange(m_threadId, threadId);
Q_EMIT threadIdChanged(oldThreadId, m_threadId);
}
QString ChatBarCache::attachmentPath() const
@@ -157,10 +213,22 @@ void ChatBarCache::setAttachmentPath(const QString &attachmentPath)
m_attachmentPath = attachmentPath;
m_relationType = None;
const auto oldEventId = std::exchange(m_relationId, QString());
delete m_relationContentModel;
Q_EMIT attachmentPathChanged();
Q_EMIT relationIdChanged(oldEventId, m_relationId);
}
void ChatBarCache::clearRelations()
{
const auto oldEventId = std::exchange(m_relationId, QString());
const auto oldThreadId = std::exchange(m_threadId, QString());
m_attachmentPath = QString();
delete m_relationContentModel;
Q_EMIT relationIdChanged(oldEventId, m_relationId);
Q_EMIT threadIdChanged(oldThreadId, m_threadId);
Q_EMIT attachmentPathChanged();
}
QList<Mention> *ChatBarCache::mentions()
{
return &m_mentions;
@@ -224,4 +292,43 @@ void ChatBarCache::setSavedText(const QString &savedText)
m_savedText = savedText;
}
void ChatBarCache::postMessage()
{
auto room = dynamic_cast<NeoChatRoom *>(parent());
if (room == nullptr) {
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
return;
}
if (!attachmentPath().isEmpty()) {
room->uploadFile(QUrl(attachmentPath()), sendText());
clearCache();
return;
}
const auto result = ActionsModel::handleAction(room, this);
if (!result.first.has_value()) {
return;
}
TextHandler textHandler;
textHandler.setData(*std::get<std::optional<QString>>(result));
const auto sendText = textHandler.handleSendText();
if (sendText.length() == 0) {
return;
}
room->postMessage(text(), sendText, *std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result), replyId(), editId(), threadId());
clearCache();
}
void ChatBarCache::clearCache()
{
setText({});
m_mentions.clear();
m_savedText = QString();
clearRelations();
}
#include "moc_chatbarcache.cpp"

View File

@@ -8,6 +8,8 @@
#include <QQuickTextDocument>
#include <QTextCursor>
#include "models/messagecontentmodel.h"
class ChatDocumentHandler;
namespace Quotient
@@ -33,7 +35,7 @@ struct Mention {
* A class to cache data from a chat bar.
*
* A chat bar can be anything that allows users to compose or edit message, it doesn't
* necessarily have to use the ChatBar component, e.g. MessageEditComponent.
* necessarily have to use the ChatBar component, e.g. ChatBarComponent.
*
* This object is intended to allow the current contents of a chat bar to be cached
* between different rooms, i.e. there is an expectation that each NeoChatRoom could
@@ -43,7 +45,7 @@ struct Mention {
* as it's parent. This is necessary for certain functions which need to get
* relevant room information.
*
* @sa ChatBar, MessageEditComponent, NeoChatRoom
* @sa ChatBar, ChatBarComponent, NeoChatRoom
*/
class ChatBarCache : public QObject
{
@@ -100,7 +102,7 @@ class ChatBarCache : public QObject
*
* @sa Quotient::RoomMember
*/
Q_PROPERTY(Quotient::RoomMember relationUser READ relationUser NOTIFY relationIdChanged)
Q_PROPERTY(Quotient::RoomMember relationAuthor READ relationAuthor NOTIFY relationIdChanged)
/**
* @brief The content of the related message.
@@ -109,6 +111,13 @@ class ChatBarCache : public QObject
*/
Q_PROPERTY(QString relationMessage READ relationMessage NOTIFY relationIdChanged)
/**
* @brief The MessageContentModel for the related message.
*
* Will be nullptr if no related message.
*/
Q_PROPERTY(MessageContentModel *relationEventContentModel READ relationEventContentModel NOTIFY relationIdChanged)
/**
* @brief Whether the chat bar is replying in a thread.
*/
@@ -144,6 +153,7 @@ public:
explicit ChatBarCache(QObject *parent = nullptr);
QString text() const;
QString sendText() const;
void setText(const QString &text);
bool isReplying() const;
@@ -154,9 +164,10 @@ public:
QString editId() const;
void setEditId(const QString &editId);
Quotient::RoomMember relationUser() const;
Quotient::RoomMember relationAuthor() const;
QString relationMessage() const;
MessageContentModel *relationEventContentModel();
bool isThreaded() const;
QString threadId() const;
@@ -165,6 +176,13 @@ public:
QString attachmentPath() const;
void setAttachmentPath(const QString &attachmentPath);
/**
* @brief Clear all relations in the cache.
*
* This includes relation ID, thread root ID and attachment path.
*/
Q_INVOKABLE void clearRelations();
/**
* @brief Retrieve the mentions for the current chat bar text.
*/
@@ -185,18 +203,29 @@ public:
*/
void setSavedText(const QString &savedText);
/**
* @brief Post the contents of the cache as a message in the room.
*/
Q_INVOKABLE void postMessage();
Q_SIGNALS:
void textChanged();
void relationIdChanged(const QString &oldEventId, const QString &newEventId);
void threadIdChanged();
void threadIdChanged(const QString &oldThreadId, const QString &newThreadId);
void attachmentPathChanged();
private:
QString m_text = QString();
QString formatMentions() const;
QString m_relationId = QString();
RelationType m_relationType = RelationType::None;
QString m_threadId = QString();
QString m_attachmentPath = QString();
QList<Mention> m_mentions;
QString m_savedText;
QPointer<MessageContentModel> m_relationContentModel;
void clearCache();
};

View File

@@ -26,23 +26,9 @@ void ColorSchemer::apply(int idx)
c->activateScheme(c->model()->index(idx, 0));
}
void ColorSchemer::apply(const QString &name)
int ColorSchemer::indexForCurrentScheme()
{
c->activateScheme(c->indexForScheme(name));
}
int ColorSchemer::indexForScheme(const QString &name) const
{
auto index = c->indexForScheme(name).row();
if (index == -1) {
index = 0;
}
return index;
}
QString ColorSchemer::nameForIndex(int index) const
{
return c->model()->data(c->model()->index(index, 0), Qt::DisplayRole).toString();
return c->indexForSchemeId(c->activeSchemeId()).row();
}
#include "moc_colorschemer.cpp"

View File

@@ -44,21 +44,11 @@ public:
Q_INVOKABLE void apply(int idx);
/**
* @brief Activates the KColorScheme with the given name.
* @brief Get the row for the current color scheme.
*
* @sa KColorScheme
*/
Q_INVOKABLE void apply(const QString &name);
/**
* @brief Returns the index for the scheme with the given name.
*/
Q_INVOKABLE int indexForScheme(const QString &name) const;
/**
* @brief Returns the name for the scheme with the given index.
*/
Q_INVOKABLE QString nameForIndex(int index) const;
Q_INVOKABLE int indexForCurrentScheme();
private:
KColorSchemeManager *c;

View File

@@ -13,7 +13,6 @@
#include <signal.h>
#include <Quotient/accountregistry.h>
#include <Quotient/csapi/notifications.h>
#include <Quotient/qt_connection_util.h>
#include <Quotient/settings.h>
@@ -64,7 +63,7 @@ Controller::Controller(QObject *parent)
});
} else {
auto c = new NeoChatConnection(this);
c->assumeIdentity(QStringLiteral("@user:localhost:1234"), QStringLiteral("token_1234"));
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();
@@ -166,6 +165,10 @@ void Controller::addConnection(NeoChatConnection *c)
dropConnection(c);
});
connect(c, &NeoChatConnection::badgeNotificationCountChanged, this, &Controller::updateBadgeNotificationCount);
connect(c, &NeoChatConnection::syncDone, this, [this, c]() {
m_notificationsManager.handleNotifications(c);
});
connect(c, &NeoChatConnection::showInviteNotification, &m_notificationsManager, &NotificationsManager::postInviteNotification);
c->sync();
@@ -176,6 +179,8 @@ void Controller::dropConnection(NeoChatConnection *c)
{
Q_ASSERT_X(c, __FUNCTION__, "Attempt to drop a null connection");
c->disconnect(this);
c->disconnect(&m_notificationsManager);
m_accountRegistry.drop(c);
Q_EMIT connectionDropped(c);
}
@@ -202,15 +207,26 @@ void Controller::invokeLogin()
m_connectionsLoading[accountId] = connection;
connect(connection, &NeoChatConnection::connected, this, [this, connection, accountId] {
connection->loadState();
addConnection(connection);
m_accountsLoading.removeAll(connection->userId());
m_connectionsLoading.remove(accountId);
Q_EMIT accountsLoadingChanged();
if (connection->allRooms().size() == 0 || connection->allRooms()[0]->currentState().get<RoomCreateEvent>()) {
addConnection(connection);
m_accountsLoading.removeAll(connection->userId());
m_connectionsLoading.remove(accountId);
Q_EMIT accountsLoadingChanged();
} else {
connect(
connection->allRooms()[0],
&Room::baseStateLoaded,
this,
[this, connection, accountId]() {
addConnection(connection);
m_accountsLoading.removeAll(connection->userId());
m_connectionsLoading.remove(accountId);
Q_EMIT accountsLoadingChanged();
},
Qt::SingleShotConnection);
}
});
connect(connection, &NeoChatConnection::networkError, this, [this](const QString &error, const QString &, int, int) {
Q_EMIT errorOccured(i18n("Network Error: %1", error), {});
});
connection->assumeIdentity(account.userId(), accessToken);
connection->assumeIdentity(account.userId(), account.deviceId(), accessToken);
});
}
}
@@ -230,17 +246,17 @@ QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const QStrin
switch (job->error()) {
case QKeychain::EntryNotFound:
Q_EMIT errorOccured(i18n("Access token wasn't found"), i18n("Maybe it was deleted?"));
Q_EMIT errorOccured(i18n("Access token wasn't found: Maybe it was deleted?"));
break;
case QKeychain::AccessDeniedByUser:
case QKeychain::AccessDenied:
Q_EMIT errorOccured(i18n("Access to keychain was denied."), i18n("Please allow NeoChat to read the access token"));
Q_EMIT errorOccured(i18n("Access to keychain was denied: Please allow NeoChat to read the access token"));
break;
case QKeychain::NoBackendAvailable:
Q_EMIT errorOccured(i18n("No keychain available."), i18n("Please install a keychain, e.g. KWallet or GNOME keyring on Linux"));
Q_EMIT errorOccured(i18n("No keychain available: Please install a keychain, e.g. KWallet or GNOME keyring on Linux"));
break;
case QKeychain::OtherError:
Q_EMIT errorOccured(i18n("Unable to read access token"), job->errorString());
Q_EMIT errorOccured(i18n("Unable to read access token: %1", job->errorString()));
break;
default:
break;
@@ -251,23 +267,19 @@ QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const QStrin
return job;
}
bool Controller::saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken)
void Controller::saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken)
{
qDebug() << "Save the access token to the keychain for " << userId;
QKeychain::WritePasswordJob job(qAppName());
job.setAutoDelete(false);
job.setKey(userId);
job.setBinaryData(accessToken);
QEventLoop loop;
QKeychain::WritePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.start();
loop.exec();
if (job.error()) {
qWarning() << "Could not save access token to the keychain: " << qPrintable(job.errorString());
return false;
}
return true;
auto job = new QKeychain::WritePasswordJob(qAppName());
job->setAutoDelete(true);
job->setKey(userId);
job->setBinaryData(accessToken);
connect(job, &QKeychain::WritePasswordJob::finished, this, [job]() {
if (job->error()) {
qWarning() << "Could not save access token to the keychain: " << qPrintable(job->errorString());
}
});
job->start();
}
bool Controller::supportSystemTray() const
@@ -309,11 +321,18 @@ void Controller::setActiveConnection(NeoChatConnection *connection)
return;
}
if (m_connection != nullptr) {
m_connection->disconnect(this);
m_connection->disconnect(&m_notificationsManager);
}
m_connection = connection;
if (m_connection != nullptr) {
m_connection->refreshBadgeNotificationCount();
updateBadgeNotificationCount(m_connection, m_connection->badgeNotificationCount());
connect(m_connection, &NeoChatConnection::errorOccured, this, &Controller::errorOccured);
}
Q_EMIT activeConnectionChanged(m_connection);
@@ -328,7 +347,7 @@ void Controller::listenForNotifications()
connect(timer, &QTimer::timeout, qGuiApp, &QGuiApplication::quit);
connect(connector, &KUnifiedPush::Connector::messageReceived, [timer](const QByteArray &data) {
NotificationsManager::instance().postPushNotification(data);
instance().m_notificationsManager.postPushNotification(data);
timer->stop();
});
@@ -340,6 +359,11 @@ void Controller::listenForNotifications()
#endif
}
void Controller::clearInvitationNotification(const QString &roomId)
{
m_notificationsManager.clearInvitationNotification(roomId);
}
void Controller::updateBadgeNotificationCount(NeoChatConnection *connection, int count)
{
if (connection == m_connection) {
@@ -399,21 +423,38 @@ void Controller::setTestMode(bool test)
void Controller::removeConnection(const QString &userId)
{
if (m_connectionsLoading.contains(userId) && m_connectionsLoading[userId]) {
auto connection = m_connectionsLoading[userId];
// When loadAccessTokenFromKeyChain() fails m_connectionsLoading won't have an
// entry for it so we need to check both separately.
if (m_accountsLoading.contains(userId)) {
m_accountsLoading.removeAll(userId);
Q_EMIT accountsLoadingChanged();
}
if (m_connectionsLoading.contains(userId) && m_connectionsLoading[userId]) {
auto connection = m_connectionsLoading[userId];
SettingsGroup("Accounts"_ls).remove(userId);
}
}
bool Controller::csSupported() const
{
#if Quotient_VERSION_MINOR > 9
return true;
#else
return false;
#endif
}
void Controller::revertToDefaultConfig()
{
const auto config = NeoChatConfig::self();
config->setDefaults();
config->save();
}
bool Controller::isImageShown(const QString &eventId)
{
return m_shownImages.contains(eventId);
}
void Controller::markImageShown(const QString &eventId)
{
m_shownImages.append(eventId);
}
#include "moc_controller.cpp"

View File

@@ -7,6 +7,7 @@
#include <QQmlEngine>
#include "neochatconnection.h"
#include "notificationsmanager.h"
#include <Quotient/accountregistry.h>
class TrayIcon;
@@ -53,16 +54,6 @@ class Controller : public QObject
Q_PROPERTY(bool csSupported READ csSupported CONSTANT)
public:
/**
* @brief Define the types on inline messages that can be shown.
*/
enum MessageType {
Positive, /**< Positive message, typically green. */
Info, /**< Info message, typically highlight color. */
Error, /**< Error message, typically red. */
};
Q_ENUM(MessageType)
static Controller &instance();
static Controller *create(QQmlEngine *engine, QJSEngine *)
{
@@ -86,7 +77,7 @@ public:
/**
* @brief Save an access token to the keychain for the given account.
*/
bool saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken);
void saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken);
[[nodiscard]] bool supportSystemTray() const;
@@ -98,6 +89,13 @@ public:
*/
static void listenForNotifications();
/**
* @brief Clear an existing invite notification for the given room.
*
* Nothing happens if the given room doesn't have an invite notification.
*/
Q_INVOKABLE void clearInvitationNotification(const QString &roomId);
Q_INVOKABLE QString loadFileContent(const QString &path) const;
Quotient::AccountRegistry &accounts();
@@ -108,6 +106,17 @@ public:
bool csSupported() const;
/**
* @brief Revert all configuration values to their default.
*
* The parameters along with their defaults are specified in the config file
* neochatconfig.kcfg.
*/
Q_INVOKABLE void revertToDefaultConfig();
Q_INVOKABLE bool isImageShown(const QString &eventId);
Q_INVOKABLE void markImageShown(const QString &eventId);
private:
explicit Controller(QObject *parent = nullptr);
@@ -116,13 +125,13 @@ private:
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const QString &account);
void loadSettings();
void saveSettings() const;
Quotient::AccountRegistry m_accountRegistry;
QStringList m_accountsLoading;
QMap<QString, QPointer<NeoChatConnection>> m_connectionsLoading;
QString m_endpoint;
QStringList m_shownImages;
NotificationsManager m_notificationsManager;
private Q_SLOTS:
void invokeLogin();
@@ -130,10 +139,12 @@ private Q_SLOTS:
void updateBadgeNotificationCount(NeoChatConnection *connection, int count);
Q_SIGNALS:
void errorOccured(const QString &error, const QString &detail);
/**
* @brief Request a error message be shown to the user.
*/
void errorOccured(const QString &error);
void connectionAdded(NeoChatConnection *connection);
void connectionDropped(NeoChatConnection *connection);
void activeConnectionChanged(NeoChatConnection *connection);
void accountsLoadingChanged();
void showMessage(MessageType messageType, const QString &message);
};

View File

@@ -4,6 +4,7 @@
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import QtQuick.Window
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
@@ -23,7 +24,7 @@ ColumnLayout {
model: root.connection.accountDataEventTypes
delegate: FormCard.FormButtonDelegate {
text: modelData
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
onClicked: root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
sourceText: root.connection.accountDataJsonString(modelData)
}, {
title: i18nc("@title:window", "Event Source"),

View File

@@ -17,25 +17,25 @@ FormCard.FormCardPage {
FormCard.FormCheckDelegate {
text: i18nc("@option:check", "Show hidden events in the timeline")
checked: Config.showAllEvents
checked: NeoChatConfig.showAllEvents
onToggled: Config.showAllEvents = checked
onToggled: NeoChatConfig.showAllEvents = checked
}
FormCard.FormCheckDelegate {
id: roomAccountDataVisibleCheck
text: i18nc("@option:check Enable the matrix 'threads' feature", "Always allow device verification")
description: i18n("Allow the user to start a verification session with devices that were already verified")
checked: Config.alwaysVerifyDevice
checked: NeoChatConfig.alwaysVerifyDevice
onToggled: Config.alwaysVerifyDevice = checked
onToggled: NeoChatConfig.alwaysVerifyDevice = checked
}
FormCard.FormCheckDelegate {
text: i18nc("@option:check", "Show focus in window header")
checked: Config.windowTitleFocus
checked: NeoChatConfig.windowTitleFocus
onToggled: {
Config.windowTitleFocus = checked;
Config.save();
NeoChatConfig.windowTitleFocus = checked;
NeoChatConfig.save();
}
}
}

View File

@@ -18,23 +18,23 @@ FormCard.FormCardPage {
FormCard.FormCheckDelegate {
id: roomAccountDataVisibleCheck
text: i18nc("@option:check Enable the matrix 'threads' feature", "Threads")
checked: Config.threads
checked: NeoChatConfig.threads
onToggled: Config.threads = checked
onToggled: NeoChatConfig.threads = checked
}
FormCard.FormCheckDelegate {
text: i18nc("@option:check Enable the matrix 'secret backup' feature", "Secret Backup")
checked: Config.secretBackup
checked: NeoChatConfig.secretBackup
onToggled: Config.secretBackup = checked
onToggled: NeoChatConfig.secretBackup = checked
}
FormCard.FormCheckDelegate {
text: i18nc("@option:check Enable the matrix feature to add a phone number as a third party ID", "Add phone numbers as 3PIDs")
checked: Config.phone3PId
checked: NeoChatConfig.phone3PId
onToggled: {
Config.phone3PId = checked
Config.save();
NeoChatConfig.phone3PId = checked
NeoChatConfig.save();
}
}
}

View File

@@ -3,6 +3,7 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Window
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
@@ -47,7 +48,7 @@ ColumnLayout {
model: root.room.accountDataEventTypes
delegate: FormCard.FormButtonDelegate {
text: modelData
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
onClicked: root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
sourceText: root.room.roomAcountDataJson(text)
}, {
title: i18n("Event Source"),
@@ -77,7 +78,7 @@ ColumnLayout {
if (model.eventCount === 1) {
openEventSource(model.type, model.stateKey);
} else {
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'StateKeys'), {
root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'StateKeys'), {
room: root.room,
eventType: model.type
}, {
@@ -89,7 +90,7 @@ ColumnLayout {
}
}
function openEventSource(type: string, stateKey: string): void {
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
onClicked: root.Window.window.pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
model: stateModel,
allowEdit: true,
room: root.room,

View File

@@ -38,6 +38,8 @@ public:
ReadMarker, /**< The local user read marker. */
Loading, /**< A delegate to tell the user more messages are being loaded. */
TimelineEnd, /**< A delegate to inform that all messages are loaded. */
Predecessor, /**< A delegate to show a room predecessor. */
Successor, /**< A delegate to show a room successor. */
Other, /**< Anything that cannot be classified as another type. */
};
Q_ENUM(Type);

View File

@@ -50,7 +50,7 @@ public:
Reply, /**< A component to show a replied-to message. */
LinkPreview, /**< A preview of a URL in the message. */
LinkPreviewLoad, /**< A loading dialog for a link preview. */
Edit, /**< A text edit for editing a message. */
ChatBar, /**< A text edit for editing a message. */
Verification, /**< A user verification session start message. */
Loading, /**< The component is loading. */
Other, /**< Anything that cannot be classified as another type. */

31
src/enums/messagetype.h Normal file
View File

@@ -0,0 +1,31 @@
// 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>
/**
* @class MessageType
*
* This class is designed to define the MessageType enumeration.
*/
class MessageType : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
public:
/**
* @brief The types of messages that can be shown.
*/
enum Type {
Information = 0, /**< Info message, typically highlight color. */
Positive, /**< Positive message, typically green. */
Warning, /**< Warning message, typically amber. */
Error, /**< Error message, typically red. */
};
Q_ENUM(Type);
};

File diff suppressed because it is too large Load Diff

View File

@@ -3,24 +3,22 @@
#pragma once
#include <QObject>
#include <KFormat>
#include <Quotient/eventitem.h>
#include <Quotient/events/roomevent.h>
#include <Quotient/events/roommessageevent.h>
#include "enums/messagecomponenttype.h"
#include <QDateTime>
#include <QString>
#include <Quotient/events/eventcontent.h>
namespace Quotient
{
namespace EventContent
{
class FileInfo;
}
class RoomEvent;
class RoomMember;
class RoomMessageEvent;
}
class LinkPreviewer;
class NeoChatRoom;
class ReactionModel;
/**
* @class EventHandler
@@ -38,40 +36,19 @@ class ReactionModel;
*/
class EventHandler
{
Q_GADGET
public:
EventHandler(const NeoChatRoom *room, const Quotient::RoomEvent *event);
/**
* @brief Return the Matrix ID of the event.
* @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.
*/
QString getId() const;
/**
* @brief The MessageComponentType to use to visualise the main event content.
*/
MessageComponentType::Type messageComponentType() const;
/**
* @brief Get the author of the event in context of the room.
*
* An empty Quotient::RoomMember will be returned if the EventHandler hasn't had
* the room or event initialised.
*
* @param isPending if the event is pending, i.e. has not been confirmed by
* the server.
*
* @return a Quotient::RoomMember object for the user.
*
* @sa Quotient::RoomMember
*/
Quotient::RoomMember getAuthor(bool isPending = false) const;
static QString id(const Quotient::RoomEvent *event);
/**
* @brief Get the display name of the event author.
*
* This method is separate from getAuthor() and special in that it will return
* This method is special in that it will return
* the old display name of the author if the current event is one that caused it
* to change. This allows for scenarios where the UI wishes to notify that a
* user's display name has changed and what it changed from.
@@ -79,7 +56,7 @@ public:
* @param isPending whether the event is pending as this cannot be derived from
* just the event object.
*/
QString getAuthorDisplayName(bool isPending = false) const;
static QString authorDisplayName(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool isPending = false);
/**
* @brief Get the display name of the event author but with any newlines removed.
@@ -90,12 +67,12 @@ public:
* @param isPending whether the event is pending as this cannot be derived from
* just the event object.
*/
QString singleLineAuthorDisplayname(bool isPending = false) const;
static QString singleLineAuthorDisplayname(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool isPending = false);
/**
* @brief Return a QDateTime object for the event timestamp.
*/
QDateTime getTime(bool isPending = false, QDateTime lastUpdated = {}) const;
static QDateTime time(const Quotient::RoomEvent *event, bool isPending = false, QDateTime lastUpdated = {});
/**
* @brief Return a QString for the event timestamp.
@@ -111,14 +88,32 @@ public:
* @param lastUpdated the time the event was last updated locally as this cannot be
* obtained from the event.
*/
QString getTimeString(bool relative, QLocale::FormatType format = QLocale::ShortFormat, bool isPending = false, QDateTime lastUpdated = {}) const;
static QString timeString(const Quotient::RoomEvent *event,
bool relative,
QLocale::FormatType format = QLocale::ShortFormat,
bool isPending = false,
QDateTime lastUpdated = {});
/**
* @brief Return a QString for the event timestamp.
*
* This is intended to return a string that is read for display in the UI without
* any further manipulation required.
*
* @param format the format to use as a string.
* @param isPending whether the event is pending as this cannot be derived from
* just the event object.
* @param lastUpdated the time the event was last updated locally as this cannot be
* obtained from the event.
*/
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.
*
* @note Messages in direct chats are never highlighted.
*/
bool isHighlighted();
static bool isHighlighted(const NeoChatRoom *room, const Quotient::RoomEvent *event);
/**
* @brief Whether the event should be hidden in the timeline.
@@ -127,7 +122,7 @@ public:
* user has hidden all state events or if the sender has been ignored by the local
* user.
*/
bool isHidden();
static bool isHidden(const NeoChatRoom *room, const Quotient::RoomEvent *event);
/**
* @brief The input format of the body in the message.
@@ -159,7 +154,7 @@ public:
*
* @param stripNewlines whether the output should have new lines in it.
*/
QString getRichBody(bool stripNewlines = false) const;
static QString richBody(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool stripNewlines = false);
/**
* @brief Output a string for the message content ready for display in a plain text field.
@@ -175,14 +170,14 @@ public:
*
* @param stripNewlines whether the output should have new lines in it.
*/
QString getPlainBody(bool stripNewlines = false) const;
static QString plainBody(const NeoChatRoom *room, const Quotient::RoomEvent *event, bool stripNewlines = false);
/**
* @brief Output the original body for the message content, useful for editing the original message.
*
* The event type must be a room message event.
*/
QString getMarkdownBody() const;
static QString markdownBody(const Quotient::RoomEvent *event);
/**
* @brief Output a generic string for the message content ready for display.
@@ -195,9 +190,9 @@ public:
* E.g. For a message the text will be:
* "sent a message"
*
* @sa getRichBody(), getPlainBody()
* @sa richBody(), plainBody()
*/
QString getGenericBody() const;
static QString genericBody(const NeoChatRoom *room, const Quotient::RoomEvent *event);
/**
* @brief Output a string for the event to be used as a RoomList subtitle.
@@ -205,7 +200,7 @@ public:
* The output includes the username followed by the plain message, all with no
* line breaks.
*/
QString subtitleText() const;
static QString subtitleText(const NeoChatRoom *room, const Quotient::RoomEvent *event);
/**
* @brief Return the media info for the event.
@@ -223,22 +218,21 @@ public:
* - tempInfo - mediaInfo (with the same properties as this except no tempInfo) for a temporary image while the file downloads.
* - isSticker - Whether the image is a sticker or not
*/
QVariantMap getMediaInfo() const;
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.
*/
bool hasReply() const;
static bool hasReply(const Quotient::RoomEvent *event, bool showFallbacks = true);
/**
* @brief Return the Matrix ID of the event replied to.
*/
QString getReplyId() const;
/**
* @brief The MessageComponentType to use to visualise the reply content.
*/
MessageComponentType::Type replyMessageComponentType() const;
static QString replyId(const Quotient::RoomEvent *event);
/**
* @brief Get the author of the event replied to in context of the room.
@@ -253,73 +247,21 @@ public:
*
* @sa Quotient::RoomMember
*/
Quotient::RoomMember getReplyAuthor() const;
/**
* @brief Output a string for the message content of the event replied to ready
* for display in a rich text field.
*
* The output string is dependant upon the event type and the desired output format.
*
* For most messages this is the body content of the message. For media messages
* this will be the caption and for state events it will be a string specific
* to that event with some dynamic details about the event added.
*
* E.g. For a room topic state event the text will be:
* "set the topic to: <new topic text>"
*
* @param stripNewlines whether the output should have new lines in it.
*/
QString getReplyRichBody(bool stripNewlines = false) const;
/**
* @brief Output a string for the message content of the event replied to ready
* for display in a plain text field.
*
* The output string is dependant upon the event type and the desired output format.
*
* For most messages this is the body content of the message. For media messages
* this will be the caption and for state events it will be a string specific
* to that event with some dynamic details about the event added.
*
* E.g. For a room topic state event the text will be:
* "set the topic to: <new topic text>"
*
* @param stripNewlines whether the output should have new lines in it.
*/
QString getReplyPlainBody(bool stripNewlines = false) const;
/**
* @brief Return the media info for the event replied to.
*
* An empty QVariantMap will be returned for any event that doesn't have any
* media info.
*
* @return This should consist of the following:
* - source - The mxc URL for the media.
* - mimeType - The MIME type of the media (should be image/xxx for this delegate).
* - mimeIcon - The MIME icon name (should be image-xxx).
* - size - The file size in bytes.
* - width - The width in pixels of the audio media.
* - height - The height in pixels of the audio media.
* - tempInfo - mediaInfo (with the same properties as this except no tempInfo) for a temporary image while the file downloads.
* - isSticker - Whether the image is a sticker or not
*/
QVariantMap getReplyMediaInfo() const;
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.
*/
bool isThreaded() const;
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.
*/
QString threadRoot() const;
static QString threadRoot(const Quotient::RoomEvent *event);
/**
* @brief Return the latitude for the event.
@@ -327,7 +269,7 @@ public:
* Returns -100.0 if the event doesn't have a location (latitudes are in the
* range -90deg to +90deg so -100 is out of range).
*/
float getLatitude() const;
static float latitude(const Quotient::RoomEvent *event);
/**
* @brief Return the longitude for the event.
@@ -335,23 +277,22 @@ public:
* Returns -200.0 if the event doesn't have a location (latitudes are in the
* range -180deg to +180deg so -200 is out of range).
*/
float getLongitude() const;
static float longitude(const Quotient::RoomEvent *event);
/**
* @brief Return the type of location marker for the event.
*/
QString getLocationAssetType() const;
static QString locationAssetType(const Quotient::RoomEvent *event);
private:
const NeoChatRoom *m_room = nullptr;
const Quotient::RoomEvent *m_event = nullptr;
static QString getBody(const NeoChatRoom *room, const Quotient::RoomEvent *event, Qt::TextFormat format, bool stripNewlines);
static QString getMessageBody(const NeoChatRoom *room, const Quotient::RoomMessageEvent &event, Qt::TextFormat format, bool stripNewlines);
KFormat m_format;
QString getBody(const Quotient::RoomEvent *event, Qt::TextFormat format, bool stripNewlines) const;
QString getMessageBody(const Quotient::RoomMessageEvent &event, Qt::TextFormat format, bool stripNewlines) const;
QVariantMap getMediaInfoForEvent(const Quotient::RoomEvent *event) const;
QVariantMap
getMediaInfoFromFileInfo(const Quotient::EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail = false, bool isSticker = false) const;
static QVariantMap getMediaInfoForEvent(const NeoChatRoom *room, const Quotient::RoomEvent *event);
QVariantMap static getMediaInfoFromFileInfo(const NeoChatRoom *room,
const Quotient::EventContent::FileContentBase *fileContent,
const QString &eventId,
bool isThumbnail = false,
bool isSticker = false);
static QVariantMap getMediaInfoFromTumbnail(const NeoChatRoom *room, const Quotient::EventContent::Thumbnail &thumbnail, const QString &eventId);
};

View File

@@ -3,7 +3,6 @@
#include "imagepackevent.h"
#include <QJsonObject>
#include <Quotient/omittable.h>
using namespace Quotient;
@@ -11,10 +10,10 @@ ImagePackEventContent::ImagePackEventContent(const QJsonObject &json)
{
if (json.contains(QStringLiteral("pack"))) {
pack = ImagePackEventContent::Pack{
fromJson<Omittable<QString>>(json["pack"_ls].toObject()["display_name"_ls]),
fromJson<Omittable<QUrl>>(json["pack"_ls].toObject()["avatar_url"_ls]),
fromJson<Omittable<QStringList>>(json["pack"_ls].toObject()["usage"_ls]),
fromJson<Omittable<QString>>(json["pack"_ls].toObject()["attribution"_ls]),
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;
@@ -31,9 +30,9 @@ ImagePackEventContent::ImagePackEventContent(const QJsonObject &json)
images += ImagePackImage{
k,
fromJson<QUrl>(json["images"_ls][k]["url"_ls].toString()),
fromJson<Omittable<QString>>(json["images"_ls][k]["body"_ls]),
fromJson<std::optional<QString>>(json["images"_ls][k]["body"_ls]),
info,
fromJson<Omittable<QStringList>>(json["images"_ls][k]["usage"_ls]),
fromJson<std::optional<QStringList>>(json["images"_ls][k]["usage"_ls]),
};
}
}

View File

@@ -7,25 +7,13 @@
#include <Quotient/accountregistry.h>
#include <Quotient/e2ee/sssshandler.h>
#include <Quotient/keyimport.h>
#include <Quotient/keyverificationsession.h>
#include <Quotient/roommember.h>
#include "controller.h"
#include "neochatconfig.h"
struct ForeignConfig {
Q_GADGET
QML_FOREIGN(NeoChatConfig)
QML_NAMED_ELEMENT(Config)
QML_SINGLETON
public:
static NeoChatConfig *create(QQmlEngine *, QJSEngine *)
{
QQmlEngine::setObjectOwnership(NeoChatConfig::self(), QQmlEngine::CppOwnership);
return NeoChatConfig::self();
}
};
struct ForeignAccountRegistry {
Q_GADGET
QML_FOREIGN(Quotient::AccountRegistry)
@@ -52,8 +40,9 @@ struct ForeignSSSSHandler {
QML_NAMED_ELEMENT(SSSSHandler)
};
struct RoomMemberForeign {
struct ForeignKeyImport {
Q_GADGET
QML_FOREIGN(Quotient::RoomMember)
QML_NAMED_ELEMENT(RoomMember)
QML_SINGLETON
QML_FOREIGN(Quotient::KeyImport)
QML_NAMED_ELEMENT(KeyImport)
};

View File

@@ -5,7 +5,7 @@
using namespace Quotient;
NeochatAdd3PIdJob::NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const Omittable<QJsonObject> &auth)
NeochatAdd3PIdJob::NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const std::optional<QJsonObject> &auth)
: BaseJob(HttpVerb::Post, QStringLiteral("Add3PIDJob"), makePath("/_matrix/client/v3", "/account/3pid/add"))
{
QJsonObject _dataJson;

View File

@@ -4,10 +4,9 @@
#pragma once
#include <Quotient/jobs/basejob.h>
#include <Quotient/omittable.h>
class NeochatAdd3PIdJob : public Quotient::BaseJob
{
public:
explicit NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const Quotient::Omittable<QJsonObject> &auth = {});
explicit NeochatAdd3PIdJob(const QString &clientSecret, const QString &sid, const std::optional<QJsonObject> &auth = {});
};

View File

@@ -5,7 +5,7 @@
using namespace Quotient;
NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const Omittable<QJsonObject> &auth)
NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const std::optional<QJsonObject> &auth)
: BaseJob(HttpVerb::Post, QStringLiteral("ChangePasswordJob"), "/_matrix/client/r0/account/password")
{
QJsonObject _data;

View File

@@ -5,10 +5,8 @@
#include <Quotient/jobs/basejob.h>
#include <Quotient/omittable.h>
class NeochatChangePasswordJob : public Quotient::BaseJob
{
public:
explicit NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const Quotient::Omittable<QJsonObject> &auth = {});
explicit NeochatChangePasswordJob(const QString &newPassword, bool logoutDevices, const std::optional<QJsonObject> &auth = {});
};

View File

@@ -5,7 +5,7 @@
using namespace Quotient;
NeoChatDeactivateAccountJob::NeoChatDeactivateAccountJob(const Omittable<QJsonObject> &auth)
NeoChatDeactivateAccountJob::NeoChatDeactivateAccountJob(const std::optional<QJsonObject> &auth)
: BaseJob(HttpVerb::Post, QStringLiteral("DisableDeviceJob"), "_matrix/client/v3/account/deactivate")
{
QJsonObject data;

View File

@@ -4,10 +4,9 @@
#pragma once
#include <Quotient/jobs/basejob.h>
#include <Quotient/omittable.h>
class NeoChatDeactivateAccountJob : public Quotient::BaseJob
{
public:
explicit NeoChatDeactivateAccountJob(const Quotient::Omittable<QJsonObject> &auth = {});
explicit NeoChatDeactivateAccountJob(const std::optional<QJsonObject> &auth = {});
};

View File

@@ -5,7 +5,7 @@
using namespace Quotient;
NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const Omittable<QJsonObject> &auth)
NeochatDeleteDeviceJob::NeochatDeleteDeviceJob(const QString &deviceId, const std::optional<QJsonObject> &auth)
: BaseJob(HttpVerb::Delete, QStringLiteral("DeleteDeviceJob"), QStringLiteral("/_matrix/client/r0/devices/%1").arg(deviceId).toLatin1())
{
QJsonObject _data;

View File

@@ -4,10 +4,9 @@
#pragma once
#include <Quotient/jobs/basejob.h>
#include <Quotient/omittable.h>
class NeochatDeleteDeviceJob : public Quotient::BaseJob
{
public:
explicit NeochatDeleteDeviceJob(const QString &deviceId, const Quotient::Omittable<QJsonObject> &auth = {});
explicit NeochatDeleteDeviceJob(const QString &deviceId, const std::optional<QJsonObject> &auth = {});
};

View File

@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "neochatgetcommonroomsjob.h"
using namespace Quotient;
NeochatGetCommonRoomsJob::NeochatGetCommonRoomsJob(const QString &userId)
: BaseJob(HttpVerb::Get,
QStringLiteral("GetCommonRoomsJob"),
QStringLiteral("/_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms").toLatin1(),
QUrlQuery({{QStringLiteral("user_id"), userId}}))
{
}

View File

@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <Quotient/jobs/basejob.h>
// TODO: Upstream to libQuotient
class NeochatGetCommonRoomsJob : public Quotient::BaseJob
{
public:
explicit NeochatGetCommonRoomsJob(const QString &userId);
};

View File

@@ -40,7 +40,7 @@ float LocationHelper::zoomToFit(const QRectF &r, float mapWidth, float mapHeight
const auto zy = std::log2((mapHeight / (p2.y() - p1.y())));
const auto z = std::min(zx, zy);
return std::clamp(z, 5.0, 18.0);
return z;
}
#include "moc_locationhelper.cpp"

View File

@@ -25,8 +25,7 @@ void LoginHelper::init()
m_connection = new NeoChatConnection();
m_matrixId = QString();
m_password = QString();
m_deviceName = QStringLiteral("NeoChat %1 %2 %3 %4")
.arg(QSysInfo::machineHostName(), QSysInfo::productType(), QSysInfo::productVersion(), QSysInfo::currentCpuArchitecture());
m_deviceName = QStringLiteral("NeoChat");
m_supportsSso = false;
m_supportsPassword = false;
m_ssoUrl = QUrl();
@@ -78,16 +77,14 @@ void LoginHelper::init()
account.setHomeserver(m_connection->homeserver());
account.setDeviceId(m_connection->deviceId());
account.setDeviceName(m_deviceName);
if (!Controller::instance().saveAccessTokenToKeyChain(account.userId(), m_connection->accessToken())) {
qWarning() << "Couldn't save access token";
}
Controller::instance().saveAccessTokenToKeyChain(account.userId(), m_connection->accessToken());
account.sync();
Controller::instance().addConnection(m_connection);
Controller::instance().setActiveConnection(m_connection);
m_connection = nullptr;
});
connect(m_connection, &Connection::networkError, this, [this](QString error, const QString &, int, int) {
Q_EMIT Controller::instance().errorOccured(i18n("Network Error"), std::move(error));
Q_EMIT m_connection->errorOccured(i18n("Network Error: %1", std::move(error)));
m_isLoggingIn = false;
Q_EMIT isLoggingInChanged();
});
@@ -95,14 +92,14 @@ void LoginHelper::init()
if (error == QStringLiteral("Invalid username or password")) {
setInvalidPassword(true);
} else {
Q_EMIT errorOccured(i18n("Login Failed: %1", error));
Q_EMIT loginErrorOccured(i18n("Login Failed: %1", error));
}
m_isLoggingIn = false;
Q_EMIT isLoggingInChanged();
});
connect(m_connection, &Connection::resolveError, this, [](QString error) {
Q_EMIT Controller::instance().errorOccured(i18n("Network Error"), std::move(error));
connect(m_connection, &Connection::resolveError, this, [this](QString error) {
Q_EMIT m_connection->errorOccured(i18n("Network Error: %1", std::move(error)));
});
connect(

View File

@@ -130,7 +130,7 @@ Q_SIGNALS:
void loginFlowsChanged();
void ssoUrlChanged();
void connected();
void errorOccured(const QString &message);
void loginErrorOccured(const QString &message);
void testingChanged();
void isLoggingInChanged();
void isLoggedInChanged();

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