Compare commits

...

245 Commits

Author SHA1 Message Date
Bhushan Shah
2839d44ea8 GIT_SILENT: bump version to 22.09 2022-09-27 20:53:11 +05:30
l10n daemon script
5f32ae79c1 GIT_SILENT Sync po/docbooks with svn 2022-09-27 01:50:19 +00:00
Tobias Fella
0fc94310c3 Implement reporting events
BUG: 458856
2022-09-26 14:38:45 +00:00
l10n daemon script
7f26651edf GIT_SILENT Sync po/docbooks with svn 2022-09-26 01:58:14 +00:00
l10n daemon script
36880d001c GIT_SILENT Sync po/docbooks with svn 2022-09-25 01:53:43 +00:00
Tobias Fella
e0e289d424 Fix dialogbuttonbox 2022-09-24 15:28:17 +02:00
Tobias Fella
b79956871f Fix build without E2EE 2022-09-24 15:27:19 +02:00
l10n daemon script
917f77152d GIT_SILENT Sync po/docbooks with svn 2022-09-24 01:53:35 +00:00
l10n daemon script
e28d1918f5 GIT_SILENT Sync po/docbooks with svn 2022-09-23 01:50:01 +00:00
Tobias Fella
149b11ba6f Set application domain before using any translated strings 2022-09-23 00:42:22 +02:00
Tobias Fella
c007961ef6 Fix some compilation warnings 2022-09-23 00:21:08 +02:00
James Graham
ef40f5a747 Fix Stickers
Fix so that stickers are rendered and that the context menu works for them.
2022-09-22 19:03:06 +00:00
Bharadwaj Raju
37780c2e3b Link Previews
Uses Matrix's preview API to generate embedded link previews.

Only title and description for now.

![image](/uploads/2c5d632480073fe54345cdbe22ea54dc/image.png)
2022-09-22 15:42:53 +00:00
James Graham
53b9f42399 Fix Slash Commands in the Middle of a Message
Make sure that a slash command only activates when the slash is at the beginning of the message.

BUG: 457474
2022-09-22 15:11:56 +00:00
l10n daemon script
d2ed1cfb2e GIT_SILENT Sync po/docbooks with svn 2022-09-22 01:51:02 +00:00
Carl Schwan
c7d4b1a529 Update Flatpak.cmake 2022-09-21 12:07:44 +00:00
Carl Schwan
a8045f2134 Install emoji fonts to correct location
See https://wiki.archlinux.org/title/font_configuration#Fontconfig_configuration
and https://invent.kde.org/network/neochat/-/issues/341#note_525953
2022-09-21 12:07:44 +00:00
Tobias Fella
b8262fef92 Implement device verification 2022-09-21 11:49:11 +00:00
l10n daemon script
3071901a47 GIT_SILENT Sync po/docbooks with svn 2022-09-21 01:54:16 +00:00
l10n daemon script
396260549a GIT_SILENT Sync po/docbooks with svn 2022-09-20 01:55:30 +00:00
l10n daemon script
50c2de8d52 GIT_SILENT Sync po/docbooks with svn 2022-09-19 01:52:03 +00:00
Ahmad Samir
d4e067d57c Use KDE_INSTALL_TARGETS_DEFAULT_ARGS
The KF_* variant is for KF repos

GIT_SILENT
2022-09-17 00:45:00 +02:00
l10n daemon script
3797b0129a GIT_SILENT Sync po/docbooks with svn 2022-09-15 01:50:41 +00:00
l10n daemon script
59b4146c63 GIT_SILENT Sync po/docbooks with svn 2022-09-14 01:50:01 +00:00
Bharadwaj Raju
c76524d540 Fix CI tripping on .reuse/dep5
The dep5 parser in the suse_tumbleweed_qt515 CI doesn't like duplicate Copyright entries.

cc @carlschwan
2022-09-13 13:40:54 +00:00
Bharadwaj Raju
10030efd08 Fix text selection with context menu
Make the context menu actually work with selected text in a message.
2022-09-13 10:04:45 +00:00
l10n daemon script
918bd5439c GIT_SILENT Sync po/docbooks with svn 2022-09-13 01:50:39 +00:00
Bharadwaj Raju
e3ff50bbe8 Use Kirigami LoadingPlaceholder everywhere
Replace the custom Placeholder + BusyIndicator with the new LoadingPlaceholder component.

Also remove the "Loading…" page title on first load, as that is redundant to the existing front-and-center "Loading…" message and hence doesn't look nice.
2022-09-12 20:41:23 +00:00
Bharadwaj Raju
76edc858aa Add proper tooltip delays
Split from !521
2022-09-12 20:35:40 +00:00
Bharadwaj Raju
2a2c117ac1 Use selected text colors from theme
When using Breeze light:

| Before | After |
| ------ | ------ |
| ![Screenshot_20220913_005141](/uploads/9b15c79acaaa92e23855def9ca6bc6ab/Screenshot_20220913_005141.png) | ![Screenshot_20220913_005049](/uploads/06cb614edae21f7ddb027bebaf732edc/Screenshot_20220913_005049.png) |
2022-09-12 20:34:47 +00:00
l10n daemon script
d3e4640af9 GIT_SILENT Sync po/docbooks with svn 2022-09-12 01:49:03 +00:00
James Graham
7e1f0f4ea7 PushRule RoomList Room Menu Option
Add an option to the roomlist room menu to set the room push rule override.

Rename default to "follow global setting" for clarity.
2022-09-11 10:29:05 +00:00
l10n daemon script
80bf279321 GIT_SILENT Sync po/docbooks with svn 2022-09-11 01:49:10 +00:00
l10n daemon script
6eb4b8c9d2 GIT_SILENT Sync po/docbooks with svn 2022-09-10 01:49:47 +00:00
James Graham
4bba505da6 Initial work to add push rule support
This commit adds the ability to set the master push rule and set push rules for individual rooms as per the matrix spec. See https://spec.matrix.org/v1.3/client-server-api/#push-rules.

The master push rule is just on/off and uses the existing notification setting in general setting to enable/disable the server default master push rule .m.rule.master.

For each room there is now a page in the room setting that allows the following to be set:
- Default
- All messages
- @mentions and keywords
- off

New room or override rules are added/removed to achieve this.

There is also functionality to check the master/room notification state whenever the setting menu is entered. This allows the status to be updated if changed in another client or get the initial state for a room as it isn't stored.

Note - There is currently no menu items in the room list for setting the room push rule settings. This will be added in a later commit, the aim is to focus on making sure the technical implementation is good for now.
2022-09-09 16:41:03 +00:00
l10n daemon script
c2fc4e44a7 GIT_SILENT Sync po/docbooks with svn 2022-09-09 01:54:13 +00:00
Tobias Fella
274bf824e3 Don't quote property names 2022-09-09 00:23:18 +02:00
Tobias Fella
d70a8a652a Always use system-style includes for libQuotient 2022-09-09 00:16:39 +02:00
Jan Bidler
8c436075d8 Changes room access into radio buttons 2022-09-08 17:31:37 +00:00
James Graham
828032ba06 RoomDrawer Cleanup
This started off as me just wanting to adjust the margin of the search bar but grew a touch ...

- Cleanup margins especially make the member search bar have the same padding as everything else
- Move room information title to header and align all buttons right.
- Make the room name actually bold
- Remove highlight from the user list item after the menu is closed
- Always use actual width so that the drawer isn't wider when modal
- Use control instead of pane for the search bar as the padding works more consistently
- Use BasicListItem instead of AbstractListItem for the member list as this has the layout predefined

![image](/uploads/ab2c4066479b0510bcb2fe6ae91f7bc3/image.png)
2022-09-08 17:30:58 +00:00
l10n daemon script
c30abfefa9 GIT_SILENT Sync po/docbooks with svn 2022-09-08 01:56:22 +00:00
Jan Bidler
5d4efad2f8 Make "show avatar" in settings localizable 2022-09-07 13:55:26 +00:00
l10n daemon script
3a391aafe8 GIT_SILENT Sync po/docbooks with svn 2022-09-07 01:53:26 +00:00
l10n daemon script
7b5b8197bc 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"
2022-09-07 01:48:36 +00:00
Jan Bidler
55659e488f unify custom emojis text 2022-09-06 19:42:53 +00:00
Jan Bidler
c2c235eff1 Use " in all i18n functions instead of ' 2022-09-06 19:42:53 +00:00
l10n daemon script
f6f4edec86 GIT_SILENT Sync po/docbooks with svn 2022-09-06 01:51:42 +00:00
Nicolas Fella
55847cb9cc Refactor window handling code
Currently when we want to show/raise the window in reaction to the tray icon/notification being clicked etc we do this by emitting a signal on the controller.
This is connected to in main.qml, which does some things, then calls back to controller to do more things.

This is quite convoluted. Instead introduce a new class WindowController that is responsible for all things window, in particular showing/raising and config saving
2022-09-05 19:27:55 +00:00
Nicolas Fella
736c4b02ed Don't call show() on visible windows
It messes with the geometry
2022-09-05 19:27:55 +00:00
Nicolas Fella
4cf5b516d0 Extract code for obtaining a window from the QML engine into a function 2022-09-05 19:27:55 +00:00
Nicolas Fella
c379a7fa27 Fix Android packaging
In the AndroidManifest we specify 'neochat' as the executable

That causes the lib to be used instead of the executable, which breaks things

Shuffle around the names so that the executable is picked
2022-09-05 18:42:27 +00:00
Tobias Fella
2318fb95d9 Adapt to libQuotient API changes 2022-09-05 20:37:55 +02:00
James Graham
14e57e7833 Fix show author after state regression
The avatar should be shown after a state message

Fixes network/neochat#553
2022-09-03 14:33:58 +00:00
l10n daemon script
8c9ea72a9b GIT_SILENT Sync po/docbooks with svn 2022-09-02 01:50:06 +00:00
l10n daemon script
efd3f2e8d0 GIT_SILENT Sync po/docbooks with svn 2022-08-30 01:49:54 +00:00
l10n daemon script
ea70782771 GIT_SILENT Sync po/docbooks with svn 2022-08-28 01:50:03 +00:00
James Graham
6cca3dbe3a Add the right click menu for image delegates in fullscreen mode
Add the right click menu for image delegates in fullscreen mode

BUG: 455147
2022-08-27 11:52:45 +00:00
l10n daemon script
696b34e094 GIT_SILENT Sync po/docbooks with svn 2022-08-27 01:57:39 +00:00
l10n daemon script
6ce74bbd0c GIT_SILENT made messages (after extraction) 2022-08-27 00:47:18 +00:00
Carl Schwan
3c7e85fbbf Fix loading state detection in devices page
Signed-off-by: Carl Schwan <carl@carlschwan.eu>
2022-08-26 23:39:05 +02:00
Carl Schwan
6c6e408497 Cleanup account page
- Fix small padding on the right when they is not enough elements for a
  scrollbar to appear (possibly due to a kirigami regression)
- Move account editor to a seperate page
- Cleanup a bit the code style
- Add tooltip to toolbuttons

Signed-off-by: Carl Schwan <carl@carlschwan.eu>
2022-08-26 23:29:29 +02:00
Jan Bidler
1ac79273b6 Fix emoji add button being unavailable 2022-08-26 20:35:09 +00:00
Tobias Fella
d4d99284cc Correctly hide spoiler links
BUG: 458311
2022-08-26 22:08:22 +02:00
Tobias Fella
25226aa61f Don't save connection state when destructing the controller
This causes neochat to crash and is done automatically by the connections

BUG: 458353
2022-08-26 22:00:38 +02:00
James Graham
6748a2d21d Fix the reply an edit text being shown in all chat rooms when multiple windows are open
Fix the reply an edit text being shown in all chat rooms when multiple windows are open. This is done by changing chatBoxHelper from a singleton to being instantiated for each instance of roompage.

BUG: 454963
2022-08-26 19:33:20 +00:00
Carl Schwan
fdb424e65e Improve contrast of new notification background
This is required to be accessible

Signed-off-by: Carl Schwan <carl@carlschwan.eu>
2022-08-26 21:24:07 +02:00
Jan Bidler
8cd0b12c4a Make typing indocator anchor on the left 2022-08-26 19:09:45 +00:00
James Graham
d133c4fab7 Improve showAuthor behaviour
2 changes to showAuthor role; the first removes the same eventRole condition as currently a video or image followed by text will both show the avatar, second is to fix it so that the avatar is actually shown if messages are 10 mins apart as this is currently broken.
2022-08-26 19:00:02 +00:00
Jan Bidler
2f8303348b Make NeoChat room a link 2022-08-26 18:43:56 +00:00
Tobias Fella
7dfac8a9f7 Fix audio playback
Since the preparation for encrypted events landed, audio playback was broken since QtMultimedia (gstreamer)
doesn't use our custom QNAM. Instead of letting QtMultimedia download the media, we thus need to manually download it
and point QML Audio to the local file. On the positive side, this also allows encrypted Audio files to be played and enables us to seek in the audio delegate :)
It does however mean that we do need to do an annoying bit of manual state management.

BUG: 457687
2022-08-26 18:37:18 +00:00
Jan Bidler
3c98a8fac4 Make clearing reply not remove your text 2022-08-26 18:29:19 +00:00
Jan Bidler
57b1bb659c Remove normal PageUp and PageDown from switching rooms 2022-08-26 18:22:17 +00:00
Jan Bidler
da8c8c48bc Fix CTRL+Page moving into wrong direction 2022-08-26 18:22:17 +00:00
Tobias Fella
07c5cd8016 Consider nested space when populating space hierarchy 2022-08-26 20:21:35 +02:00
Jan Bidler
a23ef130ca Make unread messages use an ascii circle 2022-08-26 12:33:43 +00:00
Jan Bidler
2f4116796a Rework notification colors 2022-08-26 12:33:43 +00:00
Tobias Fella
f004f9e3c8 Fix build 2022-08-26 12:52:46 +02:00
l10n daemon script
9a3726f9ff GIT_SILENT Sync po/docbooks with svn 2022-08-26 01:50:05 +00:00
Snehit Sah
91d1f6ffeb Show spaces horizontal bar
### Summary

This merge request adds a horizontal bar at top of room list, which shows spaces. By clicking on a space, user can filter out rooms belonging only to that specific space.

### Pending/ Help needed

#### Segfault when loading active connection on startup

Refer `void SortFilterRoomListModel::cacheSpaceHierarchy()` ([link](8c372800d7 (b969e462c30df43ef3714ea441948d8d8027f6a0_117_126))) in `src/sortfilterroomlistmodel.cpp`.

On [line 129](8c372800d7 (b969e462c30df43ef3714ea441948d8d8027f6a0_117_129)), I have called `connection->allRooms()`, which segfaults if the active connection hasn't been loaded yet. Is there a way to ensure that `Controller::instance().activeConnection()` on line 128 waits till connection is loaded?

#### Avatars

Avatars on space horizontal bar aren't aligned to vertical middle. I'll need help with that. 

Using the code below doesn't help with padding

```qml
delegate: QQC2.Control {
    topPadding: 10
    contentItem: Kirigami.Avatar { ..... }
}
```

This complains about uninitialized properties in `Kirigami.Avatar`. (`id`, `currentRoom`, `avatar`, `index` are properties utilized during run time)

After we get around these two issue, this MR will be ready from my side.
2022-08-25 13:46:09 +00:00
Tobias Fella
cd895f1b06 QML warnings -= 2 2022-08-23 21:24:41 +02:00
Tobias Fella
fead1a69b3 QML warning-- 2022-08-23 21:22:02 +02:00
James Graham
63af4cae77 Fix timeline layout for non text messages
Change the target of the hover component and delegate layout to timeline container so it applies to all delegates not just text messages.

BUG: 457689
2022-08-22 17:38:50 +00:00
l10n daemon script
89090690b4 GIT_SILENT Sync po/docbooks with svn 2022-08-21 01:56:52 +00:00
l10n daemon script
6a16334eab GIT_SILENT Sync po/docbooks with svn 2022-08-20 01:49:36 +00:00
l10n daemon script
5b05622058 GIT_SILENT Sync po/docbooks with svn 2022-08-16 01:54:13 +00:00
l10n daemon script
1eb705c17f GIT_SILENT Sync po/docbooks with svn 2022-08-14 01:52:46 +00:00
l10n daemon script
f9ad0e8426 GIT_SILENT Sync po/docbooks with svn 2022-08-13 01:50:50 +00:00
l10n daemon script
ef1ff04ef2 GIT_SILENT Sync po/docbooks with svn 2022-08-10 01:57:08 +00:00
James Graham
9b54aff3d5 Mark unread messages in the room as read when all messages are visible
This is an alternative to network/neochat!467. When discussing in the matrix channel this option seemed more popular so I implemented it.

Mark the unread messages in the room as read when all messages are visible to the user after a short timer. This happens on entry and when new messages come in as long as Neochat is active. If neochat isn't active, the room hasn't loaded or the read marker hasn't loaded the timer is for this is reset until all 3 conditions are false.
2022-08-09 16:42:32 +00:00
l10n daemon script
52a093d449 GIT_SILENT Sync po/docbooks with svn 2022-08-09 01:51:02 +00:00
Nate Graham
619369e148 Add hackaround for Qt bug to all non-horizontally-scrollable scrollviews
https://bugreports.qt.io/browse/QTBUG-83890 has been open for years with
a patch that's been stalled for years. There's no indication that it's
going to be fixed anytime soon, and it generates bug reports for us.
Let's add the typical hackaround for all non-horizontally-scrollable
scrollviews.

BUG: 457584
2022-08-08 15:02:09 -06:00
l10n daemon script
e63a9a9be1 GIT_SILENT Sync po/docbooks with svn 2022-08-08 01:58:51 +00:00
l10n daemon script
5c97e67404 GIT_SILENT Sync po/docbooks with svn 2022-08-04 01:52:39 +00:00
l10n daemon script
84a265b7f9 GIT_SILENT Sync po/docbooks with svn 2022-07-31 01:58:40 +00:00
l10n daemon script
61201a7097 GIT_SILENT Sync po/docbooks with svn 2022-07-29 01:56:35 +00:00
Snehit Sah
b9630ad2f2 Add Flatpak CI Support
Signed-off-by: Snehit Sah <snehitsah@protonmail.com>
2022-07-27 10:01:35 +00:00
l10n daemon script
179a201113 GIT_SILENT Sync po/docbooks with svn 2022-07-24 01:50:54 +00:00
l10n daemon script
6eef58e57d GIT_SILENT Sync po/docbooks with svn 2022-07-23 01:55:00 +00:00
l10n daemon script
94f325609a GIT_SILENT Sync po/docbooks with svn 2022-07-20 01:49:27 +00:00
l10n daemon script
a40ba493b6 GIT_SILENT Sync po/docbooks with svn 2022-07-19 01:46:04 +00:00
Tobias Fella
916e7465f1 Don't show a link preview for empty links
When hovering over a link without a target, qt5 will report the link target to be "1", which is wrong.
To work around this, we manually check if the link is "1" and if it is, we discard it.
In theory, this means that we won't get a preview for any link that actually *is* "1", but why would any link be "1"?

It's not worth reporting this to Qt since it seems fixed in Qt6

BUG: 456877
2022-07-18 21:18:48 +02:00
Tobias Fella
8257a9d65e Refactor delegates and improve context menu opening
This unifies the context menu opening and makes sure that clicking *anywhere* on the delegate opens the context menu, not just on the content
2022-07-18 20:12:26 +02:00
l10n daemon script
a75048761b GIT_SILENT Sync po/docbooks with svn 2022-07-18 01:47:32 +00:00
Carl Schwan
f21822aba7 Fix checkbox label on small screen
See https://invent.kde.org/frameworks/qqc2-desktop-style/-/merge_requests/173
2022-07-17 14:46:18 +00:00
l10n daemon script
75eb5a51af GIT_SILENT Sync po/docbooks with svn 2022-07-17 01:54:19 +00:00
l10n daemon script
7e37c31011 GIT_SILENT made messages (after extraction) 2022-07-17 00:45:21 +00:00
l10n daemon script
f80039a5c4 GIT_SILENT Sync po/docbooks with svn 2022-07-16 01:56:39 +00:00
James Graham
400d86a1e9 Precise time on hover
Implement showing a long datetime as a tooltip when hovering over the timestamp for a message. The timestamp is also moved to just after the username of the poster this means it will not be under the hover buttons unless the message is very short.

Also some cleanup of the alignment of items in the bubble. The reply text is now indented the same amount as the username and message and the padding isn't removed from the username and message when avatars are disabled.

Implements: network/neochat#223
2022-07-15 15:39:33 +00:00
l10n daemon script
b20b1c10d0 GIT_SILENT Sync po/docbooks with svn 2022-07-15 01:55:09 +00:00
Tobias Fella
a37fd0713f Convert C++ parts into a static library
This allows us to start adding unit tests
2022-07-14 15:27:29 +00:00
Tobias Fella
b1581a54d1 Allow sending encrypted messages if build supports it 2022-07-14 16:59:37 +02:00
l10n daemon script
ded60b906b GIT_SILENT Sync po/docbooks with svn 2022-07-14 01:58:39 +00:00
James Graham
6957dd0fa2 Apply margin in SectionDelegate both for Compact and Bubble mode 2022-07-13 17:27:13 +00:00
Tobias Fella
7de4014b28 Update formatting 2022-07-13 17:27:13 +00:00
James Graham
63e7ec1bd7 Spell like an American 2022-07-13 17:27:13 +00:00
James Graham
f24428fab3 Apply margin in ReadMarkerDelegate both for Compact and Bubble mode 2022-07-13 17:27:13 +00:00
James Graham
c3a5a767c2 Fix alignment of hover buttons when user message on the right is thin. In this case the buttons sould align to the right of the message not the left so they don't go off screen. 2022-07-13 17:27:13 +00:00
James Graham
5fb311b509 Implement new delegate behaviour on ReadMarkerDelegate
Make sure that the messgae hover buttons account for the delegate x displacement
2022-07-13 17:27:13 +00:00
James Graham
f0d832f756 Make sure extra width is never less than 0 2022-07-13 17:27:13 +00:00
James Graham
a7c137ca39 Allow the delegate and bubble widths to grow when the ListView is very wide.
Disable user message on the right setting when in compact mode as it doesn't work anyway.
2022-07-13 17:27:13 +00:00
James Graham
a96e8958c9 Centre the timline when using bubbles but not in compact mode 2022-07-13 17:27:13 +00:00
James Graham
7dc951d2cd Support user messages on the right even when wide
Limit maximum delegate width to ensure that the gap between user and non-user messages isn't too large
2022-07-13 17:27:13 +00:00
Tobias Fella
c3ee277ede Fix opening room using touch
The previous fix wasn't enough for non-mobile touch devices. Now, we limit the TapHandler to mouse instead
2022-07-13 14:59:16 +02:00
Tobias Fella
78d62e9376 Revert "Disable opening context menu by right-clicking on mobile"
This reverts commit 51efecaa25.
2022-07-13 14:55:55 +02:00
l10n daemon script
11e9eaf3e9 GIT_SILENT Sync po/docbooks with svn 2022-07-13 01:52:14 +00:00
James Graham
4337d0d5d8 Removing all \n is incorrect as these are used to show linebreaks in the html formatted body. Instead use the CMARK_OPT_HARDBREAKS options so these softbreaks in the markdown string are converted to hard breaks <br/> in the html.
Also remove 2 step process to replace <!-- raw HTML omitted --> and straight replace with "" to ensure no real breaks are removed
2022-07-12 14:03:21 +00:00
l10n daemon script
a07537367f GIT_SILENT Sync po/docbooks with svn 2022-07-12 01:55:45 +00:00
Tobias Fella
51efecaa25 Disable opening context menu by right-clicking on mobile
Apparently TapHandlers interpret a tap as a right click, which causes rooms to not open reliably
2022-07-11 12:41:27 +02:00
l10n daemon script
830a47c5ff GIT_SILENT Sync po/docbooks with svn 2022-07-11 01:57:02 +00:00
Tobias Fella
24748d42d8 Port C++ to Qt6
QML is still broken
2022-07-10 21:43:57 +00:00
l10n daemon script
19fe439e95 GIT_SILENT Sync po/docbooks with svn 2022-07-10 01:57:44 +00:00
Tobias Fella
2bcd7118f4 Ensure that text isn't formatted in context menu 2022-07-09 23:01:13 +02:00
l10n daemon script
27e660178e GIT_SILENT Sync po/docbooks with svn 2022-07-09 01:57:03 +00:00
Tobias Fella
e0df553a72 Remove unused imports & includes 2022-07-08 13:16:07 +02:00
l10n daemon script
53f040cb28 GIT_SILENT Sync po/docbooks with svn 2022-07-08 01:54:23 +00:00
Nicolas Fella
28cc7cf616 Add FreeBSD CI 2022-07-07 12:51:33 +02:00
l10n daemon script
d224df8aa2 GIT_SILENT Sync po/docbooks with svn 2022-07-07 01:48:15 +00:00
l10n daemon script
1dff2b8273 GIT_SILENT Sync po/docbooks with svn 2022-07-06 01:49:30 +00:00
Nicolas Fella
722aa422e7 Fix activating browser windows on Wayland
QDesktopServices::openUrl does not have XDG activation support yet so it can't raise an existing browser window when opening URLs

Instead use KIO::OpenUrlJob, which does support that
2022-07-05 22:55:12 +00:00
Akseli Lahtinen
70de0dc624 add settings button to room context menu 2022-07-05 21:26:12 +00:00
l10n daemon script
9d804e6ea7 GIT_SILENT Sync po/docbooks with svn 2022-07-05 01:46:44 +00:00
Volker Krause
52d552650d Use product screenshots from CDN rather than expensive direct Gitlab links 2022-07-03 12:16:43 +02:00
l10n daemon script
0c5007fd56 GIT_SILENT Sync po/docbooks with svn 2022-07-03 02:06:37 +00:00
l10n daemon script
af19829225 GIT_SILENT Sync po/docbooks with svn 2022-06-28 01:49:26 +00:00
l10n daemon script
0be8828dd4 GIT_SILENT Sync po/docbooks with svn 2022-06-27 01:47:51 +00:00
l10n daemon script
729b6bd354 GIT_SILENT Sync po/docbooks with svn 2022-06-26 01:48:37 +00:00
Heiko Becker
ef10042179 Sonnet is only a runtime dependency
Since 98571cb37d.
2022-06-25 15:16:54 +02:00
l10n daemon script
846d430947 GIT_SILENT Sync po/docbooks with svn 2022-06-25 02:08:13 +00:00
l10n daemon script
cce4a3ebdf GIT_SILENT made messages (after extraction) 2022-06-25 00:49:02 +00:00
l10n daemon script
5b848961bc GIT_SILENT Sync po/docbooks with svn 2022-06-24 01:46:09 +00:00
Tobias Fella
51574f5125 Fix actions in ListItems
Apparently the combination of SwipeListItem and BasicListItem is evil, so port away from it to ensure that actions show up again.
2022-06-23 23:07:10 +00:00
Tobias Fella
a779907500 Add changelog for 22.06 2022-06-23 18:06:42 +00:00
Tobias Fella
f195db323d Fix matrix room link in appdata 2022-06-23 18:31:39 +02:00
Bhushan Shah
da47d76a7f GIT_SILENT: bump version to 22.06 2022-06-23 21:11:20 +05:30
l10n daemon script
6dc8c4976c GIT_SILENT Sync po/docbooks with svn 2022-06-23 01:48:38 +00:00
l10n daemon script
fec7680068 GIT_SILENT Sync po/docbooks with svn 2022-06-22 01:48:25 +00:00
l10n daemon script
b84264891d GIT_SILENT Sync po/docbooks with svn 2022-06-21 01:49:20 +00:00
l10n daemon script
465a981033 GIT_SILENT Sync po/docbooks with svn 2022-06-20 02:04:09 +00:00
l10n daemon script
06b4c40b33 GIT_SILENT Sync po/docbooks with svn 2022-06-19 01:48:06 +00:00
Tobias Fella
efae510fda Don't use PublicRoomsChunk::aliases
Doesn't seem to be part of the spec and currently isn't in libQuotient; if it comes back, we can revert this.
Until then, this fixes the build
2022-06-18 15:10:25 +02:00
l10n daemon script
f9fc8c5c0b GIT_SILENT Sync po/docbooks with svn 2022-06-18 01:48:49 +00:00
Weng Xuetian
49c9c63bf5 Fix the switch room direction
Down should be next and Up should be Previous
2022-06-17 18:11:24 +00:00
James Graham
90cee0f437 Clear the text from the user list filter in the room drawer when the room is changed 2022-06-17 18:59:33 +01:00
l10n daemon script
5bc9362fde GIT_SILENT Sync po/docbooks with svn 2022-06-17 01:47:27 +00:00
l10n daemon script
10922aeb52 GIT_SILENT Sync po/docbooks with svn 2022-06-16 02:02:06 +00:00
Tobias Fella
f9a96ccdab It's 2022 2022-06-15 16:16:51 +02:00
l10n daemon script
04056d9ed1 GIT_SILENT Sync po/docbooks with svn 2022-06-15 01:52:09 +00:00
l10n daemon script
ecf373e317 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"
2022-06-15 01:45:58 +00:00
James Graham
9c2e0669f6 Fixe Reply or Edit from Chatbar
Fixes it so that the cleaned text is shown when using the shortcuts to reply or edit from the chatbar. Also ensures that the correct eventids are passed when the message is an edit.

This also fixes the issue of having html pasted into the chatbar when editing and edit.

Fixes network/neochat#448

BUG: 455016
2022-06-14 13:47:13 +00:00
l10n daemon script
2b8aa9f975 GIT_SILENT Sync po/docbooks with svn 2022-06-14 01:58:53 +00:00
l10n daemon script
2f61090413 GIT_SILENT Sync po/docbooks with svn 2022-06-13 02:51:21 +00:00
l10n daemon script
aa9daad704 GIT_SILENT made messages (after extraction) 2022-06-13 01:02:32 +00:00
l10n daemon script
0e79d3506d GIT_SILENT Sync po/docbooks with svn 2022-06-12 01:51:48 +00:00
l10n daemon script
72994d0349 GIT_SILENT Sync po/docbooks with svn 2022-06-11 01:45:58 +00:00
l10n daemon script
0ee5ba76c9 GIT_SILENT Sync po/docbooks with svn 2022-06-09 02:19:15 +00:00
l10n daemon script
d1398f6726 GIT_SILENT made messages (after extraction) 2022-06-09 00:52:50 +00:00
l10n daemon script
9084817450 GIT_SILENT Sync po/docbooks with svn 2022-06-08 01:49:25 +00:00
Jan Bidler
083a2f9772 Compact Mode improvements
BUG: 454897
2022-06-07 12:33:14 +00:00
Jan Bidler
b44e81c849 Make Right Click on Room bring up Context Menu
BUG: 454892
2022-06-07 08:42:39 +00:00
l10n daemon script
014826bd09 GIT_SILENT Sync po/docbooks with svn 2022-06-07 02:33:47 +00:00
l10n daemon script
2858fcfad2 GIT_SILENT made messages (after extraction) 2022-06-07 00:53:49 +00:00
l10n daemon script
b6db36a9f2 GIT_SILENT Sync po/docbooks with svn 2022-06-06 01:48:51 +00:00
James Graham
ede860c99f For all html messages \n needs to be replaces with <br> or the linebreaks are lost 2022-06-05 12:36:33 +01:00
l10n daemon script
f8951fc760 GIT_SILENT Sync po/docbooks with svn 2022-06-05 01:49:04 +00:00
Tobias Fella
525015fe78 Fix fix 2022-06-05 00:41:59 +02:00
Akseli Lahtinen
b834510be0 Don't show notifications if application is active and same room is active 2022-06-04 23:24:24 +02:00
Tobias Fella
8700611235 Fix hoverActions 2022-06-04 20:32:49 +02:00
l10n daemon script
15ddcef115 GIT_SILENT Sync po/docbooks with svn 2022-06-04 01:49:22 +00:00
l10n daemon script
7216da8b6f GIT_SILENT Sync po/docbooks with svn 2022-06-03 01:47:11 +00:00
Tobias Fella
7bd4aac692 Fix custom emoji creation 2022-06-02 13:19:12 +02:00
l10n daemon script
10e17d9f0f GIT_SILENT Sync po/docbooks with svn 2022-06-02 01:48:24 +00:00
James Graham
db5e328869 Add automatic room sidebar hiding option
Add menu option to change whether the room information drawer is opened automatically or not. This also adds some code to switch off the dim effect during the first animation after modal is changed as this looked bad.

Implements network/neochat#243

Slight cleanup removing the edge option for context drawer from main.qml as this is duplicated from RoomDrawer.qml
2022-06-01 20:09:47 +00:00
l10n daemon script
921667565e GIT_SILENT Sync po/docbooks with svn 2022-06-01 02:05:25 +00:00
l10n daemon script
9cd8a380ed GIT_SILENT Sync po/docbooks with svn 2022-05-31 02:13:37 +00:00
James Graham
29816730e4 Add space after an autocomplete
Adds a space automatically after an autocomplete if the last char isn't one.

Implements network/neochat#132
2022-05-30 19:17:47 +00:00
Tobias Fella
7214936eaa Fix compilation against libQuotient 0.6 2022-05-30 14:41:00 +02:00
Tobias Fella
5a7c3295dc Revert "Linkify urls"
This seems to mess up user mentions. Probably a bug in the regex;
Reverting for now

This reverts commit 1763dc13c5.
2022-05-30 14:28:35 +02:00
Tobias Fella
dce4a409c7 Adapt to libQuotient API changes 2022-05-30 14:23:29 +02:00
l10n daemon script
bd27904f17 GIT_SILENT Sync po/docbooks with svn 2022-05-29 01:52:07 +00:00
l10n daemon script
731b234dda GIT_SILENT Sync po/docbooks with svn 2022-05-28 02:35:16 +00:00
l10n daemon script
ce0fc637c4 GIT_SILENT made messages (after extraction) 2022-05-28 00:56:00 +00:00
Yuri Chornoivan
070fe45a2d Fix XML 2022-05-27 09:27:48 +03:00
l10n daemon script
3a8d078e6c GIT_SILENT Sync po/docbooks with svn 2022-05-27 01:47:34 +00:00
Tobias Fella
fb9183e5c3 Update bug reporting urls 2022-05-26 17:18:29 +02:00
l10n daemon script
853113df3f GIT_SILENT Sync po/docbooks with svn 2022-05-22 01:48:20 +00:00
James Graham
e62288e6f1 Adds some basic mouse contorls to the quickswitcher. The icons can now be clicked to select the room and the highlight is moved to the current hovered room. 2022-05-21 10:55:43 +00:00
Tobias Fella
4f978a950b Appstream: define launchable 2022-05-20 13:13:36 +02:00
l10n daemon script
36b2868933 GIT_SILENT Sync po/docbooks with svn 2022-05-20 01:47:21 +00:00
Tobias Fella
1763dc13c5 Linkify urls 2022-05-19 23:14:33 +02:00
l10n daemon script
b7e4c2c6a2 GIT_SILENT Sync po/docbooks with svn 2022-05-19 01:47:37 +00:00
l10n daemon script
5969612ead 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"
2022-05-19 01:41:56 +00:00
Tobias Fella
0d00d4200c Don't crash while trying to load last message by own user
Fixes #535
2022-05-17 13:50:54 +02:00
l10n daemon script
35f30c293b GIT_SILENT Sync po/docbooks with svn 2022-05-17 01:50:13 +00:00
Tobias Fella
77e20ec446 Don't escape html while posting messages 2022-05-16 20:29:44 +02:00
Tobias Fella
101b57c581 Revert "Fix double quoting and missing new lines in message sent"
This reverts commit f2cf82ee8e.
2022-05-16 20:26:35 +02:00
l10n daemon script
b994907be4 GIT_SILENT Sync po/docbooks with svn 2022-05-16 01:56:18 +00:00
l10n daemon script
97ce81daca 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"
2022-05-16 01:45:56 +00:00
Nicolas Fella
4e61c5e53c Fix rasing window when activating notifications
This was done for invite notifications but not regular notifications
2022-05-15 23:28:13 +02:00
Tobias Fella
6871ed051c Always send messages as HTML
This works around limitations of Qt's rich text detection and prevents
some messages from being shown wrong, like in #532.

In theory it is not ideal to send every message as HTML, however it's
not a significant problem and we already do it for edits and replies
(which also explains why edited messages are sometimes magically
rendered correctly while the original is not).
2022-05-15 20:31:18 +00:00
James Graham
10da870ab3 Fix search item being behind the roomlist in collapsed mode by moving code into ListView. Now the search item is always at the top of the list. 2022-05-15 14:17:38 +01:00
l10n daemon script
6b5f76296a GIT_SILENT Sync po/docbooks with svn 2022-05-15 02:04:12 +00:00
l10n daemon script
93a4930301 GIT_SILENT made messages (after extraction) 2022-05-15 00:48:20 +00:00
l10n daemon script
7b393f2681 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"
2022-05-14 01:41:26 +00:00
l10n daemon script
fb6266fa15 GIT_SILENT made messages (after extraction) 2022-05-14 00:44:34 +00:00
l10n daemon script
334b245669 GIT_SILENT Sync po/docbooks with svn 2022-05-13 01:51:00 +00:00
l10n daemon script
3a969189b8 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"
2022-05-13 01:44:55 +00:00
James Graham
cef5d11130 Fix scrollbar behaviour in Room List
In the Room List there is always a gap left for the scrollbar in normal mode whether it is visible or not. This commit makes the gap disappear when the scrollbar is not visible by using the verticalscrollbarpolicy of the scrollpage.

Fixes network/neochat#518
2022-05-12 19:16:40 +00:00
l10n daemon script
216c751d81 GIT_SILENT Sync po/docbooks with svn 2022-05-12 01:46:13 +00:00
l10n daemon script
154109dde1 GIT_SILENT Sync po/docbooks with svn 2022-05-10 01:50:40 +00:00
l10n daemon script
312db10439 GIT_SILENT Sync po/docbooks with svn 2022-05-09 01:49:41 +00:00
l10n daemon script
f4f540e805 GIT_SILENT Sync po/docbooks with svn 2022-05-08 01:47:31 +00:00
l10n daemon script
b3ca71580f GIT_SILENT Sync po/docbooks with svn 2022-05-07 01:49:11 +00:00
l10n daemon script
2fc2ac113e GIT_SILENT Sync po/docbooks with svn 2022-05-06 01:49:57 +00:00
l10n daemon script
2ea95ea080 GIT_SILENT Sync po/docbooks with svn 2022-05-05 01:51:03 +00:00
l10n daemon script
22168dcef9 GIT_SILENT Sync po/docbooks with svn 2022-05-04 01:49:52 +00:00
l10n daemon script
e25ffd0c41 GIT_SILENT Sync po/docbooks with svn 2022-05-03 01:53:55 +00:00
Tobias Fella
5595d8f896 Allow disabling sending of typing notifications 2022-05-02 10:36:48 +00:00
l10n daemon script
abed37518d GIT_SILENT Sync po/docbooks with svn 2022-05-02 01:44:26 +00:00
l10n daemon script
57493e87ee GIT_SILENT Sync po/docbooks with svn 2022-05-01 02:01:04 +00:00
Tobias Fella
1bcff6503f Fix typo 2022-04-30 19:40:07 +02:00
Tobias Fella
98571cb37d Remove leftover spellchecking files 2022-04-30 14:48:28 +02:00
l10n daemon script
b64cd3c1b8 GIT_SILENT Sync po/docbooks with svn 2022-04-29 01:48:29 +00:00
l10n daemon script
69ced8406b GIT_SILENT Sync po/docbooks with svn 2022-04-27 01:51:49 +00:00
Nicolas Fella
1f551b5f59 Use proper reuse CI job 2022-04-26 00:27:23 +02:00
l10n daemon script
48a2a793c8 GIT_SILENT Sync po/docbooks with svn 2022-04-25 01:56:50 +00:00
154 changed files with 30114 additions and 13772 deletions

157
.flatpak-manifest.json Normal file
View File

@@ -0,0 +1,157 @@
{
"id": "org.kde.neochat",
"branch": "master",
"runtime": "org.kde.Platform",
"runtime-version": "5.15-21.08",
"sdk": "org.kde.Sdk",
"command": "neochat",
"tags": [
"nightly"
],
"desktop-file-name-suffix": " (Nightly)",
"finish-args": [
"--share=network",
"--share=ipc",
"--socket=x11",
"--socket=wayland",
"--device=dri",
"--filesystem=xdg-download",
"--talk-name=org.freedesktop.Notifications",
"--talk-name=org.kde.kwalletd5",
"--talk-name=org.kde.StatusNotifierWatcher",
"--own-name=org.kde.StatusNotifierItem-2-2"
],
"modules": [
{
"name": "kquickimageeditor",
"buildsystem": "cmake-ninja",
"sources": [
{
"type": "git",
"url": "https://invent.kde.org/libraries/kquickimageeditor"
}
]
},
{
"name": "olm",
"buildsystem": "cmake-ninja",
"sources": [
{
"type": "git",
"url": "https://gitlab.matrix.org/matrix-org/olm.git",
"tag": "3.2.10",
"x-checker-data": {
"type": "git",
"tag-pattern": "^([\\d.]+)$"
},
"commit": "9908862979147a71dc6abaecd521be526ae77be1"
}
]
},
{
"name": "libsecret",
"buildsystem": "meson",
"config-opts": [
"-Dmanpage=false",
"-Dvapi=false",
"-Dgtk_doc=false",
"-Dintrospection=false",
"-Dgcrypt=false"
],
"sources": [
{
"type": "archive",
"url": "https://download.gnome.org/sources/libsecret/0.20/libsecret-0.20.5.tar.xz",
"sha256": "3fb3ce340fcd7db54d87c893e69bfc2b1f6e4d4b279065ffe66dac9f0fd12b4d",
"x-checker-data": {
"type": "gnome",
"name": "libsecret",
"stable-only": true
}
}
]
},
{
"name": "qtkeychain",
"buildsystem": "cmake-ninja",
"sources": [
{
"type": "archive",
"url": "https://github.com/frankosterfeld/qtkeychain/archive/v0.13.2.tar.gz",
"sha256": "20beeb32de7c4eb0af9039b21e18370faf847ac8697ab3045906076afbc4caa5",
"x-checker-data": {
"type": "anitya",
"project-id": 4138,
"stable-only": true,
"url-template": "https://github.com/frankosterfeld/qtkeychain/archive/v$version.tar.gz"
}
}
],
"config-opts": [
"-DCMAKE_INSTALL_LIBDIR=/app/lib",
"-DLIB_INSTALL_DIR=/app/lib",
"-DBUILD_TRANSLATIONS=NO"
]
},
{
"name": "libQuotient",
"buildsystem": "cmake-ninja",
"sources": [
{
"type": "git",
"url": "https://github.com/quotient-im/libQuotient.git",
"branch": "dev"
}
],
"config-opts": [
"-DQuotient_ENABLE_E2EE=ON"
]
},
{
"name": "cmark",
"buildsystem": "cmake-ninja",
"sources": [
{
"type": "git",
"url": "https://github.com/commonmark/cmark.git"
}
],
"config-opts": [
"-DCMARK_TESTS=OFF",
"-DCMAKE_BUILD_TYPE=Release",
"-DCMAKE_INSTALL_PREFIX=/app"
],
"builddir": true
},
{
"name": "qcoro",
"buildsystem": "cmake-ninja",
"sources": [
{
"type": "archive",
"url": "https://github.com/danvratil/qcoro/archive/refs/tags/v0.4.0.tar.gz",
"sha256": "0e68b3f0ce7bf521ffbdd731464d2d60d8d7a39a749b551ed26855a1707d86d1",
"x-checker-data": {
"type": "anitya",
"project-id": 236236,
"stable-only": true,
"url-template": "https://github.com/danvratil/qcoro/archive/refs/tags/v$version.tar.gz"
}
}
]
},
{
"name": "neochat",
"buildsystem": "cmake-ninja",
"sources": [
{
"type": "dir",
"path": "."
}
],
"config-opts": [
"-DNEOCHAT_FLATPAK=ON"
]
}
]
}

View File

@@ -2,7 +2,11 @@
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
include: include:
- https://invent.kde.org/sysadmin/ci-tooling/raw/master/invent/ci-reuse.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/reuse-lint.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml
# TODO enable once we can have qt6 libQuotient on the CI
# - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd.yml
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/flatpak.yml

View File

@@ -31,8 +31,7 @@ Copyright: 2021 Carl Schwan <carlschwan@kde.org>
License: BSD-2-Clause License: BSD-2-Clause
Files: src/neochatconfig.kcfg Files: src/neochatconfig.kcfg
Copyright: 2020-2021 Carl Schwan <carlschwan@kde.org> Copyright: 2020-2021 Carl Schwan <carlschwan@kde.org>, Tobias Fella <fella@posteo.de>
Copyright: 2020-2021 Tobias Fella <fella@posteo.de>
License: BSD-2-Clause License: BSD-2-Clause
Files: src/neochat.notifyrc Files: src/neochat.notifyrc
@@ -42,3 +41,7 @@ License: BSD-2-Clause
Files: imports/NeoChat/Component/confetti.png imports/NeoChat/Component/glowdot.png Files: imports/NeoChat/Component/confetti.png imports/NeoChat/Component/glowdot.png
Copyright: 2021 Alexey Andreyev <aa13q@ya.ru> Copyright: 2021 Alexey Andreyev <aa13q@ya.ru>
License: CC0-1.0 License: CC0-1.0
Files: .flatpak-manifest.json
Copyright: 2020-2022 Tobias Fella <fella@posteo.de>
License: BSD-2-Clause

View File

@@ -7,9 +7,9 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(NeoChat) project(NeoChat)
set(PROJECT_VERSION "22.04") set(PROJECT_VERSION "22.09")
set(KF5_MIN_VERSION "5.88.0") set(KF5_MIN_VERSION "5.91.0")
set(QT_MIN_VERSION "5.15.2") set(QT_MIN_VERSION "5.15.2")
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
@@ -25,12 +25,14 @@ include(FeatureSummary)
include(ECMSetupVersion) include(ECMSetupVersion)
include(KDEInstallDirs) include(KDEInstallDirs)
include(ECMFindQmlModule) include(ECMFindQmlModule)
include(KDEClangFormat)
include(KDECMakeSettings) include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECompilerSettings NO_POLICY_SCOPE)
include(ECMAddAppIcon) include(ECMAddAppIcon)
include(KDEGitCommitHooks) include(KDEGitCommitHooks)
include(ECMCheckOutboundLicense) include(ECMCheckOutboundLicense)
if (NOT ANDROID)
include(KDEClangFormat)
endif()
if(NEOCHAT_FLATPAK) if(NEOCHAT_FLATPAK)
include(cmake/Flatpak.cmake) include(cmake/Flatpak.cmake)
@@ -41,8 +43,8 @@ ecm_setup_version(${PROJECT_VERSION}
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
) )
find_package(Qt5 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg) find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg)
set_package_properties(Qt5 PROPERTIES set_package_properties(Qt${QT_MAJOR_VERSION} PROPERTIES
TYPE REQUIRED TYPE REQUIRED
PURPOSE "Basic application components" PURPOSE "Basic application components"
) )
@@ -56,8 +58,8 @@ set_package_properties(KF5Kirigami2 PROPERTIES
PURPOSE "Kirigami application UI framework" PURPOSE "Kirigami application UI framework"
) )
find_package(Qt5Keychain) find_package(Qt${QT_MAJOR_VERSION}Keychain)
set_package_properties(Qt5Keychain PROPERTIES set_package_properties(Qt${QT_MAJOR_VERSION}Keychain PROPERTIES
TYPE REQUIRED TYPE REQUIRED
PURPOSE "Secure storage of account secrets" PURPOSE "Secure storage of account secrets"
) )
@@ -69,11 +71,12 @@ if(ANDROID)
PURPOSE "Encrypted communications" PURPOSE "Encrypted communications"
) )
else() else()
find_package(Qt5 ${QT_MIN_VERSION} COMPONENTS Widgets) find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} COMPONENTS Widgets)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle ConfigWidgets KIO WindowSystem Sonnet) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle ConfigWidgets KIO WindowSystem)
set_package_properties(KF5QQC2DesktopStyle PROPERTIES set_package_properties(KF5QQC2DesktopStyle PROPERTIES
TYPE RUNTIME TYPE RUNTIME
) )
ecm_find_qmlmodule(org.kde.sonnet 1.0)
ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0) ecm_find_qmlmodule(org.kde.syntaxhighlighting 1.0)
endif() endif()
@@ -84,7 +87,7 @@ endif()
find_package(Quotient 0.6) find_package(Quotient 0.6)
set_package_properties(Quotient PROPERTIES set_package_properties(Quotient PROPERTIES
TYPE REQUIRED TYPE REQUIRED
DESCRIPTION "Qt wrapper arround Matrix API" DESCRIPTION "Qt wrapper around Matrix API"
URL "https://github.com/quotient-im/libQuotient/" URL "https://github.com/quotient-im/libQuotient/"
PURPOSE "Talk with matrix server" PURPOSE "Talk with matrix server"
) )
@@ -108,8 +111,8 @@ set_package_properties(KQuickImageEditor PROPERTIES
PURPOSE "Add image editing capability to image attachments" PURPOSE "Add image editing capability to image attachments"
) )
find_package(QCoro5 COMPONENTS Core QUIET) find_package(QCoro${QT_MAJOR_VERSION} COMPONENTS Core QUIET)
if(NOT QCoro5_FOUND) if(NOT QCoro${QT_MAJOR_VERSION}_FOUND)
find_package(QCoro REQUIRED) find_package(QCoro REQUIRED)
endif() endif()
@@ -136,11 +139,12 @@ add_subdirectory(src)
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES src/*.cpp src/*.h) if (NOT ANDROID)
kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES}) file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES src/*.cpp src/*.h)
kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
endif()
file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.h *.qml) file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.h *.qml)
# CI installs dependency headers to _install and _build, which break the reuse check # CI installs dependency headers to _install and _build, which break the reuse check
# Fixes the test by excluding this directory # Fixes the test by excluding this directory

View File

@@ -31,7 +31,7 @@ and can also directly be downloaded from the [binary factory](https://binary-fac
Nightly builds for [Windows](https://binary-factory.kde.org/job/NeoChat_Nightly_win64/), [MacOS](https://binary-factory.kde.org/job/NeoChat_Nightly_macos/) and [AppImages](https://binary-factory.kde.org/job/NeoChat_Nightly_appimage/) can also be downloaded from the [binary factory](https://binary-factory.kde.org/search/?q=neochat). Nightly builds for [Windows](https://binary-factory.kde.org/job/NeoChat_Nightly_win64/), [MacOS](https://binary-factory.kde.org/job/NeoChat_Nightly_macos/) and [AppImages](https://binary-factory.kde.org/job/NeoChat_Nightly_appimage/) can also be downloaded from the [binary factory](https://binary-factory.kde.org/search/?q=neochat).
![Timeline](https://invent.kde.org/websites/product-screenshots/-/raw/master/neochat/application.png) ![Timeline](https://cdn.kde.org/screenshots/neochat/application.png)
## Features ## Features
@@ -55,7 +55,7 @@ We welcome contributions in this direction.
## Contact ## Contact
You can reach the maintainers at #neochat:kde.org, if you are already on Matrix. You can reach the maintainers at [#neochat:kde.org](https://matrix.to/#/#neochat:kde.org), if you are already on Matrix.
Development happens in http://invent.kde.org/network/neochat (not in GitHub). Development happens in http://invent.kde.org/network/neochat (not in GitHub).
## Acknowledgement ## Acknowledgement

View File

@@ -21,7 +21,7 @@
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
<meta-data android:name="android.app.lib_name" android:value="neochat"/> <meta-data android:name="android.app.lib_name" android:value="neochat-app"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/> <meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/> <meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/> <meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>

View File

@@ -9,6 +9,6 @@ install(
FILES FILES
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Flatpak/99-noto-mono-color-emoji.conf ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Flatpak/99-noto-mono-color-emoji.conf
DESTINATION DESTINATION
${CMAKE_INSTALL_SYSCONFDIR}/fonts/conf.d/ ${CMAKE_INSTALL_SYSCONFDIR}/fonts/local.conf
) )

View File

@@ -14,14 +14,14 @@ import NeoChat.Page 1.0
Loader { Loader {
id: root id: root
property var attachmentMimetype: FileType.mimeTypeForUrl(ChatBoxHelper.attachmentPath) property var attachmentMimetype: FileType.mimeTypeForUrl(chatBoxHelper.attachmentPath)
readonly property bool hasImage: attachmentMimetype.valid && FileType.supportedImageFormats.includes(attachmentMimetype.preferredSuffix) readonly property bool hasImage: attachmentMimetype.valid && FileType.supportedImageFormats.includes(attachmentMimetype.preferredSuffix)
active: visible active: visible
sourceComponent: Component { sourceComponent: Component {
Pane { Pane {
id: attachmentPane id: attachmentPane
property string baseFileName: ChatBoxHelper.attachmentPath.toString().substring(ChatBoxHelper.attachmentPath.toString().lastIndexOf('/') + 1, ChatBoxHelper.attachmentPath.length) property string baseFileName: chatBoxHelper.attachmentPath.toString().substring(chatBoxHelper.attachmentPath.toString().lastIndexOf('/') + 1, chatBoxHelper.attachmentPath.length)
Kirigami.Theme.colorSet: Kirigami.Theme.View Kirigami.Theme.colorSet: Kirigami.Theme.View
contentItem: Item { contentItem: Item {
@@ -46,7 +46,7 @@ Loader {
asynchronous: true asynchronous: true
cache: false // Cache is not needed. Images will rarely be shown repeatedly. cache: false // Cache is not needed. Images will rarely be shown repeatedly.
smooth: height == preferredHeight && parent.height == parent.implicitHeight // Don't smooth until height animation stops smooth: height == preferredHeight && parent.height == parent.implicitHeight // Don't smooth until height animation stops
source: hasImage ? ChatBoxHelper.attachmentPath : "" source: hasImage ? chatBoxHelper.attachmentPath : ""
visible: hasImage visible: hasImage
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
@@ -162,14 +162,14 @@ Loader {
Component { Component {
id: imageEditorPage id: imageEditorPage
ImageEditorPage { ImageEditorPage {
imagePath: ChatBoxHelper.attachmentPath imagePath: chatBoxHelper.attachmentPath
} }
} }
onClicked: { onClicked: {
let imageEditor = applicationWindow().pageStack.layers.push(imageEditorPage); let imageEditor = applicationWindow().pageStack.layers.push(imageEditorPage);
imageEditor.newPathChanged.connect(function(newPath) { imageEditor.newPathChanged.connect(function(newPath) {
applicationWindow().pageStack.layers.pop(); applicationWindow().pageStack.layers.pop();
ChatBoxHelper.attachmentPath = newPath; chatBoxHelper.attachmentPath = newPath;
}); });
} }
ToolTip.text: text ToolTip.text: text
@@ -180,7 +180,7 @@ Loader {
icon.name: "dialog-cancel" icon.name: "dialog-cancel"
text: i18n("Cancel") text: i18n("Cancel")
display: AbstractButton.IconOnly display: AbstractButton.IconOnly
onClicked: ChatBoxHelper.clearAttachment(); onClicked: chatBoxHelper.clearAttachment();
ToolTip.text: text ToolTip.text: text
ToolTip.visible: hovered ToolTip.visible: hovered
} }

View File

@@ -63,6 +63,9 @@ ToolBar {
Layout.maximumHeight: fontMetrics.lineSpacing * 8 - fontMetrics.leading Layout.maximumHeight: fontMetrics.lineSpacing * 8 - fontMetrics.leading
+ inputField.topPadding + inputField.bottomPadding + inputField.topPadding + inputField.bottomPadding
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
FontMetrics { FontMetrics {
id: fontMetrics id: fontMetrics
font: inputField.font font: inputField.font
@@ -94,11 +97,11 @@ ToolBar {
//property int lineHeight: contentHeight / lineCount //property int lineHeight: contentHeight / lineCount
text: inputFieldText text: inputFieldText
placeholderText: currentRoom.usesEncryption ? i18n("This room is encrypted. Sending encrypted messages is not yet supported.") : editEventId.length > 0 ? i18n("Edit Message") : i18n("Write your message...") placeholderText: readOnly ? i18n("This room is encrypted. Sending encrypted messages is not yet supported.") : editEventId.length > 0 ? i18n("Edit Message") : currentRoom.usesEncryption ? i18n("Send an encrypted message…") : i18n("Send a message")
verticalAlignment: TextEdit.AlignVCenter verticalAlignment: TextEdit.AlignVCenter
horizontalAlignment: TextEdit.AlignLeft horizontalAlignment: TextEdit.AlignLeft
wrapMode: Text.Wrap wrapMode: Text.Wrap
readOnly: currentRoom.usesEncryption readOnly: currentRoom.usesEncryption && !Controller.encryptionSupported
Kirigami.Theme.colorSet: Kirigami.Theme.View Kirigami.Theme.colorSet: Kirigami.Theme.View
Kirigami.Theme.inherit: false Kirigami.Theme.inherit: false
@@ -145,11 +148,7 @@ ToolBar {
} }
Keys.onPressed: { Keys.onPressed: {
if (event.key === Qt.Key_PageDown) { if (event.key === Qt.Key_V && event.modifiers & Qt.ControlModifier) {
switchRoomDown();
} else if (event.key === Qt.Key_PageUp) {
switchRoomUp();
} else if (event.key === Qt.Key_V && event.modifiers & Qt.ControlModifier) {
chatBar.pasteImage(); chatBar.pasteImage();
} else if (event.key === Qt.Key_Up && event.modifiers & Qt.ControlModifier) { } else if (event.key === Qt.Key_Up && event.modifiers & Qt.ControlModifier) {
replyPreviousUserMessage(); replyPreviousUserMessage();
@@ -229,7 +228,7 @@ ToolBar {
} }
onTextChanged: { onTextChanged: {
if (!repeatTimer.running) { if (!repeatTimer.running && Config.typingNotifications) {
currentRoom.sendTypingNotification(true) currentRoom.sendTypingNotification(true)
} }
repeatTimer.start() repeatTimer.start()
@@ -278,14 +277,14 @@ ToolBar {
} }
Item { Item {
visible: !ChatBoxHelper.isReplying && (!ChatBoxHelper.hasAttachment || uploadingBusySpinner.running) visible: !chatBoxHelper.isReplying && (!chatBoxHelper.hasAttachment || uploadingBusySpinner.running)
implicitWidth: uploadButton.implicitWidth implicitWidth: uploadButton.implicitWidth
implicitHeight: uploadButton.implicitHeight implicitHeight: uploadButton.implicitHeight
ToolButton { ToolButton {
id: uploadButton id: uploadButton
anchors.fill: parent anchors.fill: parent
// Matrix does not allow sending attachments in replies // Matrix does not allow sending attachments in replies
visible: !ChatBoxHelper.isReplying && !ChatBoxHelper.hasAttachment && !uploadingBusySpinner.running visible: !chatBoxHelper.isReplying && !chatBoxHelper.hasAttachment && !uploadingBusySpinner.running
icon.name: "mail-attachment" icon.name: "mail-attachment"
text: i18n("Attach an image or file") text: i18n("Attach an image or file")
display: AbstractButton.IconOnly display: AbstractButton.IconOnly
@@ -297,7 +296,7 @@ ToolBar {
var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay) var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay)
fileDialog.chosen.connect((path) => { fileDialog.chosen.connect((path) => {
if (!path) { return } if (!path) { return }
ChatBoxHelper.attachmentPath = path; chatBoxHelper.attachmentPath = path;
}) })
fileDialog.open() fileDialog.open()
} }
@@ -380,16 +379,16 @@ ToolBar {
if (!Clipboard.saveImage(localPath)) { if (!Clipboard.saveImage(localPath)) {
return; return;
} }
ChatBoxHelper.attachmentPath = localPath; chatBoxHelper.attachmentPath = localPath;
} }
function postMessage() { function postMessage() {
checkForFancyEffectsReason(); checkForFancyEffectsReason();
if (ChatBoxHelper.hasAttachment) { if (chatBoxHelper.hasAttachment) {
// send attachment but don't reset the text // send attachment but don't reset the text
actionsHandler.postMessage("", ChatBoxHelper.attachmentPath, actionsHandler.postMessage("", chatBoxHelper.attachmentPath,
ChatBoxHelper.replyEventId, ChatBoxHelper.editEventId, {}, this.customEmojiModel); chatBoxHelper.replyEventId, chatBoxHelper.editEventId, {}, this.customEmojiModel);
currentRoom.markAllMessagesAsRead(); currentRoom.markAllMessagesAsRead();
messageSent(); messageSent();
return; return;
@@ -401,8 +400,8 @@ ToolBar {
actionsHandler.postEdit(inputField.text); actionsHandler.postEdit(inputField.text);
} else { } else {
// send normal message // send normal message
actionsHandler.postMessage(inputField.text.trim(), ChatBoxHelper.attachmentPath, actionsHandler.postMessage(inputField.text.trim(), chatBoxHelper.attachmentPath,
ChatBoxHelper.replyEventId, ChatBoxHelper.editEventId, userAutocompleted, this.customEmojiModel); chatBoxHelper.replyEventId, chatBoxHelper.editEventId, userAutocompleted, this.customEmojiModel);
} }
currentRoom.markAllMessagesAsRead(); currentRoom.markAllMessagesAsRead();
inputField.clear(); inputField.clear();

View File

@@ -127,8 +127,8 @@ Item {
ReplyPane { ReplyPane {
id: replyPane id: replyPane
visible: ChatBoxHelper.isReplying || ChatBoxHelper.isEditing visible: chatBoxHelper.isReplying || chatBoxHelper.isEditing
user: ChatBoxHelper.replyUser user: chatBoxHelper.replyUser
width: parent.width width: parent.width
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
anchors.bottom: attachmentSeparator.top anchors.bottom: attachmentSeparator.top
@@ -154,7 +154,7 @@ Item {
AttachmentPane { AttachmentPane {
id: attachmentPane id: attachmentPane
visible: ChatBoxHelper.hasAttachment visible: chatBoxHelper.hasAttachment
width: parent.width width: parent.width
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
anchors.bottom: chatBarSeparator.top anchors.bottom: chatBarSeparator.top
@@ -248,7 +248,7 @@ Item {
} }
Connections { Connections {
target: ChatBoxHelper target: chatBoxHelper
function onShouldClearText() { function onShouldClearText() {
root.inputFieldText = ""; root.inputFieldText = "";
@@ -277,7 +277,7 @@ Item {
} }
function closeAll() { function closeAll() {
ChatBoxHelper.clear(); chatBoxHelper.clear();
chatBar.emojiPaneOpened = false; chatBar.emojiPaneOpened = false;
} }
} }

View File

@@ -47,6 +47,7 @@ Popup {
implicitHeight: Math.min(completionListView.contentHeight, Kirigami.Units.gridUnit * 10) implicitHeight: Math.min(completionListView.contentHeight, Kirigami.Units.gridUnit * 10)
contentItem: ScrollView { contentItem: ScrollView {
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ListView { ListView {
id: completionListView id: completionListView

View File

@@ -12,7 +12,7 @@ import org.kde.neochat 1.0
Loader { Loader {
id: root id: root
readonly property bool isEdit: ChatBoxHelper.isEditing readonly property bool isEdit: chatBoxHelper.isEditing
property var user: null property var user: null
property string avatarMediaUrl: user ? "image://mxc/" + user.avatarMediaId : "" property string avatarMediaUrl: user ? "image://mxc/" + user.avatarMediaId : ""
@@ -71,6 +71,10 @@ Loader {
Layout.alignment: Qt.AlignLeft | Qt.AlignTop Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumHeight: fontMetrics.lineSpacing * 8 - fontMetrics.leading Layout.maximumHeight: fontMetrics.lineSpacing * 8 - fontMetrics.leading
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
TextArea { TextArea {
id: textArea id: textArea
leftPadding: 0 leftPadding: 0
@@ -79,7 +83,7 @@ Loader {
bottomPadding: 0 bottomPadding: 0
text: { text: {
const stylesheet = "<style> a{color:"+Kirigami.Theme.linkColor+";}.user-pill{}</style>"; const stylesheet = "<style> a{color:"+Kirigami.Theme.linkColor+";}.user-pill{}</style>";
const content = ChatBoxHelper.isReplying ? ChatBoxHelper.replyEventContent : ChatBoxHelper.editContent; const content = chatBoxHelper.isReplying ? chatBoxHelper.replyEventContent : chatBoxHelper.editContent;
return stylesheet + content; return stylesheet + content;
} }
selectByMouse: true selectByMouse: true
@@ -102,7 +106,7 @@ Loader {
text: i18n("Cancel") text: i18n("Cancel")
display: AbstractButton.IconOnly display: AbstractButton.IconOnly
onClicked: { onClicked: {
ChatBoxHelper.clearEditReply(); chatBoxHelper.clear();
root.replyCancelled(); root.replyCancelled();
} }
ToolTip.text: text ToolTip.text: text

View File

@@ -14,6 +14,7 @@ ApplicationWindow {
property string blurhash: "" property string blurhash: ""
property int imageWidth: -1 property int imageWidth: -1
property int imageHeight: -1 property int imageHeight: -1
property var modelData
flags: Qt.FramelessWindowHint | Qt.WA_TranslucentBackground flags: Qt.FramelessWindowHint | Qt.WA_TranslucentBackground
@@ -52,6 +53,24 @@ ApplicationWindow {
source: root.blurhash !== "" ? ("image://blurhash/" + root.blurhash) : "" source: root.blurhash !== "" ? ("image://blurhash/" + root.blurhash) : ""
visible: root.blurhash !== "" && parent.status !== Image.Ready visible: root.blurhash !== "" && parent.status !== Image.Ready
} }
TapHandler {
acceptedButtons: Qt.RightButton
onTapped: {
const contextMenu = fileDelegateContextMenu.createObject(parent, {
author: modelData.author,
message: modelData.message,
eventId: modelData.eventId,
source: modelData.source,
file: root.parent,
mimeType: modelData.mimeType,
progressInfo: modelData.progressInfo,
plainMessage: modelData.message,
});
contextMenu.closeFullscreen.connect(root.destroy)
contextMenu.open();
}
}
} }
Button { Button {

View File

@@ -20,7 +20,7 @@ LoginStep {
Connections { Connections {
target: LoginHelper target: LoginHelper
function onSsoUrlChanged() { function onSsoUrlChanged() {
Qt.openUrlExternally(LoginHelper.ssoUrl) UrlHelper.openUrl(LoginHelper.ssoUrl)
} }
function onConnected() { function onConnected() {
processed("qrc:/imports/NeoChat/Component/Login/Loading.qml") processed("qrc:/imports/NeoChat/Component/Login/Loading.qml")

View File

@@ -24,10 +24,12 @@ QQC2.Popup {
quickSearch.forceActiveFocus() quickSearch.forceActiveFocus()
quickSearch.text = "" quickSearch.text = ""
} }
anchors.centerIn: QQC2.Overlay.overlay anchors.centerIn: QQC2.Overlay.overlay
background: Kirigami.Card {} background: Kirigami.Card {}
height: 2 * Math.round(implicitHeight / 2) height: 2 * Math.round(implicitHeight / 2)
padding: Kirigami.Units.largeSpacing * 2 padding: Kirigami.Units.largeSpacing * 2
contentItem: ColumnLayout { contentItem: ColumnLayout {
spacing: Kirigami.Units.largeSpacing * 2 spacing: Kirigami.Units.largeSpacing * 2
@@ -77,11 +79,30 @@ QQC2.Popup {
required property string avatar required property string avatar
required property var currentRoom required property var currentRoom
required property int index
// When an item is hovered set the currentIndex of listview to it so that it is highlighted
onHoveredChanged: {
if (!hovered) {
return
}
cView.currentIndex = index
}
actions.main: Kirigami.Action {
id: enterRoomAction
onTriggered: {
RoomManager.enterRoom(currentRoom);
_popup.close()
}
}
source: avatar != "" ? "image://mxc/" + avatar : "" source: avatar != "" ? "image://mxc/" + avatar : ""
} }
} }
} }
modal: true modal: true
focus: true focus: true
} }

View File

@@ -1,71 +1,116 @@
// SPDX-FileCopyrightText: 2019-2020 Black Hat <bhat@encom.eu.org> // SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
import Qt.labs.platform 1.1 as Platform
import QtMultimedia 5.15 import QtMultimedia 5.15
import org.kde.kirigami 2.15 as Kirigami import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0 import org.kde.neochat 1.0
import NeoChat.Component 1.0
import NeoChat.Dialog 1.0
import NeoChat.Menu.Timeline 1.0
TimelineContainer { TimelineContainer {
id: audioDelegate id: audioDelegate
width: ListView.view.width
onReplyClicked: ListView.view.goToEvent(eventID) onReplyClicked: ListView.view.goToEvent(eventID)
onOpenContextMenu: openFileContext(model, audioDelegate)
readonly property bool downloaded: model.progressInfo && model.progressInfo.completed
onDownloadedChanged: audio.play()
hoverComponent: hoverActions hoverComponent: hoverActions
innerObject: Control { innerObject: Control {
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumWidth: audioDelegate.bubbleMaxWidth Layout.maximumWidth: audioDelegate.contentMaxWidth
Audio { Audio {
id: audio id: audio
source: currentRoom.urlToMxcUrl(content.url) source: model.progressInfo.localPath
autoLoad: false autoLoad: false
} }
TapHandler { states: [
acceptedButtons: Qt.RightButton State {
onTapped: openFileContext(model, parent) name: "notDownloaded"
} when: !model.progressInfo.completed && !model.progressInfo.active
TapHandler {
acceptedButtons: Qt.LeftButton PropertyChanges {
onLongPressed: openFileContext(model, parent) target: playButton
} icon.name: "media-playback-start"
onClicked: currentRoom.downloadFile(model.eventId)
}
},
State {
name: "downloading"
when: model.progressInfo.active && !model.progressInfo.completed
PropertyChanges {
target: downloadBar
visible: true
}
PropertyChanges {
target: playButton
icon.name: "media-playback-stop"
onClicked: {
currentRoom.cancelFileTransfer(model.eventId)
}
}
},
State {
name: "paused"
when: model.progressInfo.completed && (audio.playbackState === Audio.StoppedState || audio.playbackState === Audio.PausedState)
PropertyChanges {
target: playButton
icon.name: "media-playback-start"
onClicked: {
audio.play()
}
}
},
State {
name: "playing"
when: model.progressInfo.completed && audio.playbackState === Audio.PlayingState
PropertyChanges {
target: playButton
icon.name: "media-playback-pause"
onClicked: audio.pause()
}
}
]
contentItem: ColumnLayout { contentItem: ColumnLayout {
RowLayout { RowLayout {
ToolButton { ToolButton {
icon.name: audio.playbackState == Audio.PlayingState ? "media-playback-pause" : "media-playback-start" id: playButton
onClicked: {
if (audio.playbackState == Audio.PlayingState) {
audio.pause()
} else {
audio.play()
}
}
} }
Label { Label {
text: model.display text: model.display
wrapMode: Text.Wrap
Layout.fillWidth: true
} }
} }
ProgressBar {
id: downloadBar
visible: false
Layout.fillWidth: true
from: 0
to: model.content.info.size
value: model.progressInfo.progress
}
RowLayout { RowLayout {
visible: audio.hasAudio visible: audio.hasAudio
Layout.leftMargin: Kirigami.Units.largeSpacing Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.rightMargin: Kirigami.Units.largeSpacing Layout.rightMargin: Kirigami.Units.largeSpacing
// Server doesn't support seeking, so use ProgressBar instead of Slider :(
ProgressBar { Slider {
from: 0 from: 0
to: audio.duration to: audio.duration
value: audio.position value: audio.position
onMoved: audio.seek(value)
} }
Label { Label {

View File

@@ -10,17 +10,18 @@ import org.kde.neochat 1.0
TimelineContainer { TimelineContainer {
id: encryptedDelegate id: encryptedDelegate
width: ListView.view.width
innerObject: TextEdit { innerObject: TextEdit {
text: i18n("This message is encrypted and the sender has not shared the key with this device.") text: i18n("This message is encrypted and the sender has not shared the key with this device.")
color: Kirigami.Theme.disabledTextColor color: Kirigami.Theme.disabledTextColor
selectedTextColor: Kirigami.Theme.highlightedTextColor
selectionColor: Kirigami.Theme.highlightColor
font.pointSize: Kirigami.Theme.defaultFont.pointSize font.pointSize: Kirigami.Theme.defaultFont.pointSize
selectByMouse: !Kirigami.Settings.isMobile selectByMouse: !Kirigami.Settings.isMobile
readOnly: true readOnly: true
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
textFormat: Text.RichText textFormat: Text.RichText
Layout.maximumWidth: encryptedDelegate.bubbleMaxWidth Layout.maximumWidth: encryptedDelegate.contentMaxWidth
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0 Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
} }
} }

View File

@@ -42,9 +42,7 @@ DelegateChooser {
DelegateChoice { DelegateChoice {
roleValue: "sticker" roleValue: "sticker"
delegate: ImageDelegate { delegate: ImageDelegate {}
cardBackground: false
}
} }
DelegateChoice { DelegateChoice {

View File

@@ -4,7 +4,6 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2 import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
import Qt.labs.platform 1.1 import Qt.labs.platform 1.1
import org.kde.kirigami 2.15 as Kirigami import org.kde.kirigami 2.15 as Kirigami
@@ -16,11 +15,12 @@ import NeoChat.Menu.Timeline 1.0
TimelineContainer { TimelineContainer {
id: fileDelegate id: fileDelegate
width: ListView.view.width
onReplyClicked: ListView.view.goToEvent(eventID) onReplyClicked: ListView.view.goToEvent(eventID)
hoverComponent: hoverActions hoverComponent: hoverActions
onOpenContextMenu: openFileContext(model, fileDelegate)
readonly property bool downloaded: progressInfo && progressInfo.completed readonly property bool downloaded: progressInfo && progressInfo.completed
function saveFileAs() { function saveFileAs() {
@@ -30,14 +30,14 @@ TimelineContainer {
} }
function openSavedFile() { function openSavedFile() {
if (Qt.openUrlExternally(progressInfo.localPath)) return; if (UrlHelper.openUrl(progressInfo.localPath)) return;
if (Qt.openUrlExternally(progressInfo.localDir)) return; if (UrlHelper.openUrl(progressInfo.localDir)) return;
} }
innerObject: RowLayout { innerObject: RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumWidth: fileDelegate.bubbleMaxWidth Layout.maximumWidth: fileDelegate.contentMaxWidth
Layout.margins: Kirigami.Units.largeSpacing Layout.margins: Kirigami.Units.largeSpacing
spacing: Kirigami.Units.largeSpacing spacing: Kirigami.Units.largeSpacing
@@ -53,6 +53,7 @@ TimelineContainer {
icon.name: "document-open" icon.name: "document-open"
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; offers ability to open its downloaded file with an appropriate application", "Open File") QQC2.ToolTip.text: i18nc("tooltip for a button on a message; offers ability to open its downloaded file with an appropriate application", "Open File")
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
onClicked: openSavedFile() onClicked: openSavedFile()
} }
@@ -70,6 +71,7 @@ TimelineContainer {
icon.name: "media-playback-stop" icon.name: "media-playback-stop"
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; stops downloading the message's file", "Stop Download") QQC2.ToolTip.text: i18nc("tooltip for a button on a message; stops downloading the message's file", "Stop Download")
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
onClicked: currentRoom.cancelFileTransfer(eventId) onClicked: currentRoom.cancelFileTransfer(eventId)
} }
}, },
@@ -131,14 +133,5 @@ TimelineContainer {
} }
} }
} }
TapHandler {
acceptedButtons: Qt.RightButton
onTapped: openFileContext(model, parent)
}
TapHandler {
acceptedButtons: Qt.LeftButton
onLongPressed: openFileContext(model, parent)
}
} }
} }

View File

@@ -4,7 +4,6 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
import Qt.labs.platform 1.1 import Qt.labs.platform 1.1
import org.kde.neochat 1.0 import org.kde.neochat 1.0
@@ -16,11 +15,11 @@ import NeoChat.Menu.Timeline 1.0
TimelineContainer { TimelineContainer {
id: imageDelegate id: imageDelegate
width: ListView.view.width
onReplyClicked: ListView.view.goToEvent(eventID) onReplyClicked: ListView.view.goToEvent(eventID)
hoverComponent: hoverActions hoverComponent: hoverActions
onOpenContextMenu: openFileContext(model, imageDelegate)
property var content: model.content property var content: model.content
readonly property bool isAnimated: contentType === "image/gif" readonly property bool isAnimated: contentType === "image/gif"
@@ -35,8 +34,8 @@ TimelineContainer {
innerObject: Image { innerObject: Image {
id: img id: img
Layout.maximumWidth: imageDelegate.bubbleMaxWidth Layout.maximumWidth: imageDelegate.contentMaxWidth
Layout.maximumHeight: imageDelegate.bubbleMaxWidth / imageDelegate.info.w * imageDelegate.info.h Layout.maximumHeight: imageDelegate.contentMaxWidth / imageDelegate.info.w * imageDelegate.info.h
Layout.preferredWidth: imageDelegate.info.w Layout.preferredWidth: imageDelegate.info.w
Layout.preferredHeight: imageDelegate.info.h Layout.preferredHeight: imageDelegate.info.h
source: model.mediaUrl source: model.mediaUrl
@@ -86,21 +85,17 @@ TimelineContainer {
} }
} }
TapHandler {
acceptedButtons: Qt.RightButton
onTapped: openFileContext(model, parent)
}
TapHandler { TapHandler {
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
onLongPressed: openFileContext(model, parent) onLongPressed: openFileContext(model, parent)
onTapped: { onTapped: {
fullScreenImage.createObject(parent, { fullScreenImage.createObject(parent, {
filename: eventId, filename: eventId,
source: model.mediaUrl, source: mediaUrl,
blurhash: model.content.info["xyz.amorgan.blurhash"], blurhash: model.content.info["xyz.amorgan.blurhash"],
imageWidth: content.info.w, imageWidth: content.info.w,
imageHeight: content.info.h imageHeight: content.info.h,
modelData: model
}).showFullScreen(); }).showFullScreen();
} }
} }
@@ -115,8 +110,8 @@ TimelineContainer {
} }
function openSavedFile() { function openSavedFile() {
if (Qt.openUrlExternally(progressInfo.localPath)) return; if (UrlHelper.openUrl(progressInfo.localPath)) return;
if (Qt.openUrlExternally(progressInfo.localDir)) return; if (UrlHelper.openUrl(progressInfo.localDir)) return;
} }
} }
} }

View File

@@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: 2022 Bharadwaj Raju <bharadwaj.raju777@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-or-later OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
RowLayout {
id: row
property var links: model.display.match(/(\bhttps?:\/\/[^\s\<\>\"\']*[^\s\<\>\"\'])/g)
// don't show previews for room links or user mentions
.filter(link => !link.includes("https://matrix.to"))
// remove ending fullstops and commas
.map(link => (link.length && [".", ","].includes(link[link.length-1])) ? link.substring(0, link.length-1) : link)
LinkPreviewer {
id: lp
url: links[0]
}
visible: lp.loaded && lp.title
Rectangle {
Layout.fillHeight: true
width: Kirigami.Units.smallSpacing
visible: lp.loaded && lp.title
color: Kirigami.Theme.highlightColor
}
Image {
visible: lp.imageSource
Layout.maximumHeight: Kirigami.Units.gridUnit * 5
Layout.maximumWidth: Kirigami.Units.gridUnit * 5
source: lp.imageSource.replace("mxc://", "image://mxc/")
fillMode: Image.PreserveAspectFit
}
ColumnLayout {
id: column
spacing: Kirigami.Units.smallSpacing
Kirigami.Heading {
Layout.maximumWidth: messageDelegate.bubbleMaxWidth
Layout.fillWidth: true
level: 4
wrapMode: Text.Wrap
textFormat: Text.RichText
text: "<style>
a {
text-decoration: none;
}
</style>
<a href=\"" + links[0] + "\">" + lp.title.replace("&ndash;", "—") + "</a>"
visible: lp.loaded
onLinkActivated: RoomManager.openResource(link)
}
Label {
text: lp.description
Layout.maximumWidth: messageDelegate.bubbleMaxWidth
Layout.fillWidth: true
wrapMode: Text.Wrap
visible: lp.loaded && lp.description
}
}
}

View File

@@ -13,24 +13,28 @@ import org.kde.neochat 1.0
TimelineContainer { TimelineContainer {
id: messageDelegate id: messageDelegate
width: ListView.view.width
property bool isEmote: false property bool isEmote: false
onOpenContextMenu: openMessageContext(model, label.selectedText, Controller.plainText(label.textDocument))
onReplyClicked: ListView.view.goToEvent(eventID) onReplyClicked: ListView.view.goToEvent(eventID)
hoverComponent: hoverActions hoverComponent: hoverActions
innerObject: RichLabel { innerObject: ColumnLayout {
isEmote: messageDelegate.isEmote RichLabel {
Layout.maximumWidth: messageDelegate.bubbleMaxWidth id: label
isEmote: messageDelegate.isEmote
TapHandler { Layout.maximumWidth: messageDelegate.bubbleMaxWidth
acceptedButtons: Qt.RightButton
onTapped: openMessageContext(model, parent.selectedText)
} }
Loader {
TapHandler { id: linkPreviewLoader
acceptedButtons: Qt.LeftButton Layout.rightMargin: Kirigami.Units.largeSpacing
onLongPressed: openMessageContext(model, parent.selectedText) Layout.leftMargin: Kirigami.Units.largeSpacing
height: active ? item.implicitHeight : 0
active: !currentRoom.usesEncryption && model.display && model.display.includes("http")
visible: active
sourceComponent: LinkPreviewDelegate {
anchors.verticalCenter: parent.verticalCenter
}
} }
} }
} }

View File

@@ -11,11 +11,49 @@ import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0 import org.kde.neochat 1.0
QQC2.ItemDelegate { QQC2.ItemDelegate {
id: readMarkerDelegate
padding: Kirigami.Units.largeSpacing padding: Kirigami.Units.largeSpacing
topInset: Kirigami.Units.largeSpacing topInset: Kirigami.Units.largeSpacing
topPadding: Kirigami.Units.largeSpacing * 2 topPadding: Kirigami.Units.largeSpacing * 2
width: ListView.view.width - Kirigami.Units.gridUnit
x: Kirigami.Units.gridUnit / 2 // extraWidth defines how the delegate can grow after the listView gets very wide
readonly property int extraWidth: messageListView.width >= Kirigami.Units.gridUnit * 46 ? Math.min((messageListView.width - Kirigami.Units.gridUnit * 46), Kirigami.Units.gridUnit * 20) : 0
readonly property int delegateMaxWidth: Config.compactLayout ? messageListView.width - Kirigami.Units.largeSpacing * 2 : Math.min(messageListView.width - Kirigami.Units.largeSpacing * 2, Kirigami.Units.gridUnit * 40 + extraWidth)
width: delegateMaxWidth
anchors.leftMargin: Kirigami.Units.largeSpacing
anchors.rightMargin: Kirigami.Units.largeSpacing
state: Config.compactLayout ? "alignLeft" : "alignCenter"
// Align left when in compact mode and center when using bubbles
states: [
State {
name: "alignLeft"
AnchorChanges {
target: readMarkerDelegate
anchors.horizontalCenter: undefined
anchors.left: parent ? parent.left : undefined
}
},
State {
name: "alignCenter"
AnchorChanges {
target: readMarkerDelegate
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
anchors.left: undefined
}
}
]
transitions: [
Transition {
AnchorAnimation {
duration: Kirigami.Units.longDuration
easing.type: Easing.OutCubic
}
}
]
contentItem: QQC2.Label { contentItem: QQC2.Label {
text: i18nc("Relative time since the room was last read", "Last read: %1", time) text: i18nc("Relative time since the room was last read", "Last read: %1", time)
} }

View File

@@ -15,11 +15,11 @@ MouseArea {
id: replyButton id: replyButton
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: replyName.implicitHeight + (loader.item ? loader.item.height : 0) + Kirigami.Units.largeSpacing implicitHeight: replyName.implicitHeight + (loader.item ? loader.item.height : 0) + Kirigami.Units.largeSpacing
implicitWidth: Math.min(bubbleMaxWidth, Math.max((loader.item ? loader.item.width + Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing : 0), replyName.implicitWidth)) + Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 3 implicitWidth: Math.min(contentMaxWidth, Math.max((loader.item ? loader.item.width + Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing : 0), replyName.implicitWidth)) + Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 3
Component.onCompleted: { Component.onCompleted: {
parent.Layout.fillWidth = true; parent.Layout.fillWidth = true;
parent.Layout.preferredWidth = Qt.binding(function() { return implicitWidth; }) parent.Layout.preferredWidth = Qt.binding(function() { return implicitWidth; })
parent.Layout.maximumWidth = Qt.binding(function() { return bubbleMaxWidth + Kirigami.Units.largeSpacing * 2; }) parent.Layout.maximumWidth = Qt.binding(function() { return contentMaxWidth + Kirigami.Units.largeSpacing * 2; })
} }
Rectangle { Rectangle {
id: replyLeftBorder id: replyLeftBorder
@@ -79,7 +79,7 @@ MouseArea {
id: replyText id: replyText
textMessage: reply.display textMessage: reply.display
textFormat: Text.RichText textFormat: Text.RichText
width: Math.min(implicitWidth, bubbleMaxWidth - Kirigami.Units.largeSpacing * 3) width: Math.min(implicitWidth, contentMaxWidth - Kirigami.Units.largeSpacing * 3)
x: Kirigami.Units.smallSpacing * 3 + replyAvatar.width x: Kirigami.Units.smallSpacing * 3 + replyAvatar.width
} }
} }
@@ -94,7 +94,7 @@ MouseArea {
readonly property string mediaId: isThumbnail ? content.thumbnailMediaId : content.mediaId readonly property string mediaId: isThumbnail ? content.thumbnailMediaId : content.mediaId
source: "image://mxc/" + mediaId source: "image://mxc/" + mediaId
width: bubbleMaxWidth * 0.75 - Kirigami.Units.smallSpacing * 5 - replyAvatar.width width: contentMaxWidth * 0.75 - Kirigami.Units.smallSpacing * 5 - replyAvatar.width
height: reply.content.info.h / reply.content.info.w * width height: reply.content.info.h / reply.content.info.w * width
x: Kirigami.Units.smallSpacing * 3 + replyAvatar.width x: Kirigami.Units.smallSpacing * 3 + replyAvatar.width
} }

View File

@@ -22,7 +22,9 @@ TextEdit {
Layout.fillWidth: Config.compactLayout Layout.fillWidth: Config.compactLayout
Layout.rightMargin: Kirigami.Units.largeSpacing Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0 Layout.leftMargin: Kirigami.Units.largeSpacing
persistentSelection: true
text: "<style> text: "<style>
table { table {
@@ -44,6 +46,10 @@ a{
text-decoration: none; text-decoration: none;
} }
" + (!spoilerRevealed ? " " + (!spoilerRevealed ? "
[data-mx-spoiler] a {
color: transparent;
background: " + Kirigami.Theme.textColor + ";
}
[data-mx-spoiler] { [data-mx-spoiler] {
color: transparent; color: transparent;
background: " + Kirigami.Theme.textColor + "; background: " + Kirigami.Theme.textColor + ";
@@ -52,14 +58,19 @@ a{
</style>" + (isEmote ? "* <a href='https://matrix.to/#/" + author.id + "' style='color: " + author.color + "'>" + author.displayName + "</a> " : "") + textMessage + (isEdited ? (" <span style=\"color: " + Kirigami.Theme.disabledTextColor + "\">" + "<span style='font-size: " + Kirigami.Theme.defaultFont.pixelSize +"px'>" + i18n(" (edited)") + "</span>") : "") </style>" + (isEmote ? "* <a href='https://matrix.to/#/" + author.id + "' style='color: " + author.color + "'>" + author.displayName + "</a> " : "") + textMessage + (isEdited ? (" <span style=\"color: " + Kirigami.Theme.disabledTextColor + "\">" + "<span style='font-size: " + Kirigami.Theme.defaultFont.pixelSize +"px'>" + i18n(" (edited)") + "</span>") : "")
color: Kirigami.Theme.textColor color: Kirigami.Theme.textColor
selectedTextColor: Kirigami.Theme.highlightedTextColor
selectionColor: Kirigami.Theme.highlightColor
font.pointSize: model.reply === undefined && isEmoji.test(model.display) ? Kirigami.Theme.defaultFont.pointSize * 4 : Kirigami.Theme.defaultFont.pointSize font.pointSize: model.reply === undefined && isEmoji.test(model.display) ? Kirigami.Theme.defaultFont.pointSize * 4 : Kirigami.Theme.defaultFont.pointSize
selectByMouse: !Kirigami.Settings.isMobile selectByMouse: !Kirigami.Settings.isMobile
readOnly: true readOnly: true
wrapMode: Text.Wrap wrapMode: Text.Wrap
textFormat: Text.RichText textFormat: Text.RichText
onLinkActivated: RoomManager.openResource(link) onLinkActivated: {
onHoveredLinkChanged: if (hoveredLink.length > 0) { spoilerRevealed = true
RoomManager.openResource(link)
}
onHoveredLinkChanged: if (hoveredLink.length > 0 && hoveredLink !== "1") {
applicationWindow().hoverLinkIndicator.text = hoveredLink; applicationWindow().hoverLinkIndicator.text = hoveredLink;
} else { } else {
applicationWindow().hoverLinkIndicator.text = ""; applicationWindow().hoverLinkIndicator.text = "";

View File

@@ -7,19 +7,53 @@ import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
import NeoChat.Component 1.0 import NeoChat.Component 1.0
import NeoChat.Dialog 1.0 import NeoChat.Dialog 1.0
Control { Control {
x: Kirigami.Units.gridUnit * 1.5 + Kirigami.Units.smallSpacing id: stateDelegate
width: ListView.view.width - Kirigami.Units.largeSpacing - x // extraWidth defines how the delegate can grow after the listView gets very wide
readonly property int extraWidth: messageListView.width >= Kirigami.Units.gridUnit * 46 ? Math.min((messageListView.width - Kirigami.Units.gridUnit * 46), Kirigami.Units.gridUnit * 20) : 0
readonly property int delegateMaxWidth: Config.compactLayout ? messageListView.width: Math.min(messageListView.width, Kirigami.Units.gridUnit * 40 + extraWidth)
width: delegateMaxWidth
// anchors.leftMargin: Kirigami.Units.largeSpacing
// anchors.rightMargin: Kirigami.Units.largeSpacing
state: Config.compactLayout ? "alignLeft" : "alignCenter"
// Align left when in compact mode and center when using bubbles
states: [
State {
name: "alignLeft"
AnchorChanges {
target: stateDelegate
anchors.horizontalCenter: undefined
anchors.left: parent ? parent.left : undefined
}
},
State {
name: "alignCenter"
AnchorChanges {
target: stateDelegate
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
anchors.left: undefined
}
}
]
transitions: [
Transition {
AnchorAnimation{duration: Kirigami.Units.longDuration; easing.type: Easing.OutCubic}
}
]
height: sectionDelegate.height + rowLayout.height height: sectionDelegate.height + rowLayout.height
SectionDelegate { SectionDelegate {
id: sectionDelegate id: sectionDelegate
width: parent.width
anchors.top: parent.top anchors.top: parent.top
anchors.leftMargin: Kirigami.Units.smallSpacing anchors.left: parent.left
anchors.right: parent.right
visible: model.showSection visible: model.showSection
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
} }
@@ -27,8 +61,11 @@ Control {
RowLayout { RowLayout {
id: rowLayout id: rowLayout
height: label.contentHeight height: label.contentHeight
width: parent.width
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: Kirigami.Units.gridUnit * 1.5 + Kirigami.Units.smallSpacing + (Config.compactLayout ? Kirigami.Units.largeSpacing * 1.25 : 0)
anchors.rightMargin: Kirigami.Units.largeSpacing
Kirigami.Avatar { Kirigami.Avatar {
id: icon id: icon

View File

@@ -4,7 +4,6 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2 import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.12
import org.kde.kirigami 2.15 as Kirigami import org.kde.kirigami 2.15 as Kirigami
@@ -13,19 +12,24 @@ import NeoChat.Component 1.0
import NeoChat.Dialog 1.0 import NeoChat.Dialog 1.0
QQC2.ItemDelegate { QQC2.ItemDelegate {
id: messageDelegate id: timelineContainer
default property alias innerObject : column.children default property alias innerObject : column.children
// readonly property bool failed: marks == EventStatus.SendingFailed // readonly property bool failed: marks == EventStatus.SendingFailed
property bool isEmote: false property bool isEmote: false
property bool cardBackground: true property bool cardBackground: true
readonly property int bubbleMaxWidth: Config.compactLayout && !Config.showAvatarInTimeline ? width : (Config.compactLayout ? width - Kirigami.Units.gridUnit * 2 - Kirigami.Units.largeSpacing * 4 : Math.min(width - Kirigami.Units.gridUnit * 2 - Kirigami.Units.largeSpacing * 6, Kirigami.Units.gridUnit * 20)) signal openContextMenu
// The bubble and delegate widths are allowed to grow once the ListView gets beyond a certain size
// extraWidth defines this as the excess after a certain ListView width, capped to a max value
readonly property int extraWidth: messageListView.width >= Kirigami.Units.gridUnit * 46 ? Math.min((messageListView.width - Kirigami.Units.gridUnit * 46), Kirigami.Units.gridUnit * 20) : 0
readonly property int bubbleMaxWidth: Kirigami.Units.gridUnit * 20 + extraWidth * 0.5
readonly property int delegateMaxWidth: Config.compactLayout ? messageListView.width : Math.min(messageListView.width, Kirigami.Units.gridUnit * 40 + extraWidth)
readonly property int contentMaxWidth: Config.compactLayout ? width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) - Kirigami.Units.largeSpacing * 4 : Math.min(width - Kirigami.Units.gridUnit * 2 - Kirigami.Units.largeSpacing * 6, bubbleMaxWidth)
property bool showUserMessageOnRight: Config.showLocalMessagesOnRight && property bool showUserMessageOnRight: Config.showLocalMessagesOnRight &&
model.author.isLocalUser && model.author.isLocalUser && !Config.compactLayout
!applicationWindow().wideScreen &&
!Config.compactLayout
signal openExternally() signal openExternally()
signal replyClicked(string eventID) signal replyClicked(string eventID)
@@ -38,7 +42,10 @@ QQC2.ItemDelegate {
topPadding: 0 topPadding: 0
bottomPadding: 0 bottomPadding: 0
width: delegateMaxWidth
height: sectionDelegate.height + Math.max(model.showAuthor ? avatar.height : 0, bubble.implicitHeight) + loader.height + (showAuthor ? Kirigami.Units.largeSpacing : 0)
background: null background: null
property Item hoverComponent property Item hoverComponent
// show hover actions // show hover actions
@@ -51,13 +58,39 @@ QQC2.ItemDelegate {
// updates the global hover component to point to this delegate, and update its position // updates the global hover component to point to this delegate, and update its position
function updateHoverComponent() { function updateHoverComponent() {
if (hoverComponent) { if (hoverComponent) {
hoverComponent.delegate = timelineContainer
hoverComponent.bubble = bubble hoverComponent.bubble = bubble
hoverComponent.updateFunction = updateHoverComponent; hoverComponent.updateFunction = updateHoverComponent;
hoverComponent.event = model hoverComponent.event = model
} }
} }
height: sectionDelegate.height + Math.max(model.showAuthor ? avatar.height : 0, bubble.implicitHeight) + loader.height + (showAuthor ? Kirigami.Units.largeSpacing : 0) state: Config.compactLayout ? "alignLeft" : "alignCenter"
// Align left when in compact mode and center when using bubbles
states: [
State {
name: "alignLeft"
AnchorChanges {
target: timelineContainer
anchors.horizontalCenter: undefined
anchors.left: parent ? parent.left : undefined
}
},
State {
name: "alignCenter"
AnchorChanges {
target: timelineContainer
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
anchors.left: undefined
}
}
]
transitions: [
Transition {
AnchorAnimation{duration: Kirigami.Units.longDuration; easing.type: Easing.OutCubic}
}
]
SectionDelegate { SectionDelegate {
id: sectionDelegate id: sectionDelegate
@@ -105,24 +138,21 @@ QQC2.ItemDelegate {
QQC2.Control { QQC2.Control {
id: bubble id: bubble
topPadding: !Config.compactLayout ? Kirigami.Units.largeSpacing : 0 topPadding: Config.compactLayout ? Kirigami.Units.smallSpacing / 2 : Kirigami.Units.largeSpacing
bottomPadding: !Config.compactLayout ? Kirigami.Units.largeSpacing : 0 bottomPadding: Config.compactLayout ? Kirigami.Units.mediumSpacing / 2 : Kirigami.Units.largeSpacing
leftPadding: Kirigami.Units.smallSpacing leftPadding: Kirigami.Units.smallSpacing
rightPadding: Config.compactLayout ? Kirigami.Units.largeSpacing : Kirigami.Units.smallSpacing rightPadding: Config.compactLayout ? Kirigami.Units.largeSpacing : Kirigami.Units.smallSpacing
hoverEnabled: true hoverEnabled: true
// state: Config.compactLayout ? "compactLayout" : "default"
state: showUserMessageOnRight ? "userMessageOnRight" : "userMessageOnLeft"
anchors { anchors {
top: avatar.top top: avatar.top
leftMargin: Kirigami.Units.largeSpacing leftMargin: Kirigami.Units.largeSpacing
rightMargin: showUserMessageOnRight ? Kirigami.Units.smallSpacing : Kirigami.Units.largeSpacing rightMargin: showUserMessageOnRight ? Kirigami.Units.smallSpacing : Kirigami.Units.largeSpacing
} }
// HACK: anchoring didn't reset anchors.right when switching from parent.right to undefined reliably // HACK: anchoring didn't reset anchors.right when switching from parent.right to undefined reliably
width: Config.compactLayout ? messageDelegate.width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) + Kirigami.Units.largeSpacing * 2 : implicitWidth width: Config.compactLayout ? timelineContainer.width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) + Kirigami.Units.largeSpacing * 2 : implicitWidth
state: showUserMessageOnRight ? "userMessageOnRight" : "userMessageOnLeft"
// states for anchor animations on window resize // states for anchor animations on window resize
// as setting anchors to undefined did not work reliably // as setting anchors to undefined did not work reliably
states: [ states: [
@@ -160,7 +190,7 @@ QQC2.ItemDelegate {
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0 Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
Layout.rightMargin: Kirigami.Units.largeSpacing Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.preferredWidth: nameLabel.implicitWidth + timeLabel.implicitWidth + Kirigami.Units.largeSpacing * 2 Layout.preferredWidth: nameLabel.implicitWidth + timeLabel.implicitWidth + Kirigami.Units.largeSpacing * 2
Layout.maximumWidth: bubbleMaxWidth Layout.maximumWidth: contentMaxWidth
implicitHeight: visible ? nameLabel.implicitHeight : 0 implicitHeight: visible ? nameLabel.implicitHeight : 0
QQC2.Label { QQC2.Label {
@@ -168,15 +198,13 @@ QQC2.ItemDelegate {
topInset: 0 topInset: 0
visible: model.showAuthor && !isEmote visible: model.showAuthor && !isEmote
anchors.left: rowLayout.left width: Math.min(contentMaxWidth - timeLabel.width, implicitWidth)
anchors.right: timeLabel.left
anchors.rightMargin: Kirigami.Units.smallSpacing
text: visible ? author.displayName : "" text: visible ? author.displayName : ""
textFormat: Text.PlainText textFormat: Text.PlainText
font.weight: Font.Bold font.weight: Font.Bold
color: author.color color: author.color
wrapMode: Text.Wrap elide: Text.ElideRight
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
@@ -193,10 +221,19 @@ QQC2.ItemDelegate {
} }
QQC2.Label { QQC2.Label {
id: timeLabel id: timeLabel
anchors.right: rowLayout.right leftPadding: Kirigami.Units.largeSpacing
rightPadding: Kirigami.Units.largeSpacing
anchors.left: nameLabel.right
visible: model.showAuthor && !isEmote visible: model.showAuthor && !isEmote
text: visible ? time.toLocaleTimeString(Locale.ShortFormat) : "" text: visible ? time.toLocaleTimeString(Qt.locale(), Locale.ShortFormat) : ""
color: Kirigami.Theme.disabledTextColor color: Kirigami.Theme.disabledTextColor
QQC2.ToolTip.visible: hoverHandler.hovered
QQC2.ToolTip.text: time.toLocaleString(Qt.locale(), Locale.LongFormat)
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
HoverHandler {
id: hoverHandler
}
} }
} }
Loader { Loader {
@@ -206,6 +243,7 @@ QQC2.ItemDelegate {
visible: active visible: active
Layout.topMargin: Kirigami.Units.smallSpacing Layout.topMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Config.compactLayout ? 0 : Kirigami.Units.smallSpacing Layout.bottomMargin: Config.compactLayout ? 0 : Kirigami.Units.smallSpacing
Layout.leftMargin: Config.compactLayout ? 0 : Kirigami.Units.largeSpacing
Connections { Connections {
target: replyLoader.item target: replyLoader.item
@@ -218,7 +256,7 @@ QQC2.ItemDelegate {
background: Item { background: Item {
Rectangle { Rectangle {
visible: messageDelegate.hovered visible: timelineContainer.hovered
color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15) color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
radius: Kirigami.Units.smallSpacing radius: Kirigami.Units.smallSpacing
anchors.fill: parent anchors.fill: parent
@@ -250,7 +288,7 @@ QQC2.ItemDelegate {
left: bubble.left left: bubble.left
right: parent.right right: parent.right
top: bubble.bottom top: bubble.bottom
topMargin: active && !Config.compactLayout ? Kirigami.Units.smallSpacing : 0 topMargin: active && Config.compactLayout ? 0 : Kirigami.Units.smallSpacing
} }
height: active ? item.implicitHeight : 0 height: active ? item.implicitHeight : 0
//Layout.bottomMargin: readMarker ? Kirigami.Units.smallSpacing : 0 //Layout.bottomMargin: readMarker ? Kirigami.Units.smallSpacing : 0
@@ -258,4 +296,14 @@ QQC2.ItemDelegate {
visible: active visible: active
sourceComponent: ReactionDelegate { } sourceComponent: ReactionDelegate { }
} }
TapHandler {
acceptedButtons: Qt.RightButton
onTapped: timelineContainer.openContextMenu()
}
TapHandler {
acceptedButtons: Qt.LeftButton
onLongPressed: timelineContainer.openContextMenu()
}
} }

View File

@@ -4,7 +4,6 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
import QtMultimedia 5.15 import QtMultimedia 5.15
import Qt.labs.platform 1.1 as Platform import Qt.labs.platform 1.1 as Platform
@@ -18,8 +17,6 @@ import NeoChat.Menu.Timeline 1.0
TimelineContainer { TimelineContainer {
id: videoDelegate id: videoDelegate
width: ListView.view.width
onReplyClicked: ListView.view.goToEvent(eventID) onReplyClicked: ListView.view.goToEvent(eventID)
hoverComponent: hoverActions hoverComponent: hoverActions
@@ -29,6 +26,8 @@ TimelineContainer {
property bool supportStreaming: true property bool supportStreaming: true
readonly property int maxWidth: 1000 // TODO messageListView.width readonly property int maxWidth: 1000 // TODO messageListView.width
onOpenContextMenu: openFileContext(model, vid)
onDownloadedChanged: { onDownloadedChanged: {
if (downloaded) { if (downloaded) {
vid.source = progressInfo.localPath vid.source = progressInfo.localPath
@@ -43,7 +42,7 @@ TimelineContainer {
innerObject: Video { innerObject: Video {
id: vid id: vid
Layout.maximumWidth: videoDelegate.bubbleMaxWidth Layout.maximumWidth: videoDelegate.contentMaxWidth
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumHeight: Kirigami.Units.gridUnit * 15 Layout.maximumHeight: Kirigami.Units.gridUnit * 15
Layout.minimumHeight: Kirigami.Units.gridUnit * 5 Layout.minimumHeight: Kirigami.Units.gridUnit * 5
@@ -124,16 +123,6 @@ TimelineContainer {
videoDelegate.downloadAndPlay() videoDelegate.downloadAndPlay()
} }
} }
TapHandler {
acceptedButtons: Qt.RightButton
onTapped: openFileContext(model, parent)
}
TapHandler {
acceptedButtons: Qt.LeftButton
onLongPressed: openFileContext(model, parent)
}
} }
function downloadAndPlay() { function downloadAndPlay() {

View File

@@ -12,3 +12,4 @@ EncryptedDelegate 1.0 EncryptedDelegate.qml
EventDelegate 1.0 EventDelegate.qml EventDelegate 1.0 EventDelegate.qml
MessageDelegate 1.0 MessageDelegate.qml MessageDelegate 1.0 MessageDelegate.qml
ReadMarkerDelegate 1.0 ReadMarkerDelegate.qml ReadMarkerDelegate 1.0 ReadMarkerDelegate.qml
LinkPreviewDelegate 1.0 LinkPreviewDelegate.qml

View File

@@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQml 2.15
import org.kde.kirigami 2.19 as Kirigami
import org.kde.neochat 1.0
Column {
id: emojiItem
property string emoji
property string description
QQC2.Label {
id: emojiLabel
x: 0
y: 0
width: parent.width
height: parent.height * 0.75
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: emojiItem.emoji
font.family: "emoji"
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 4
}
QQC2.Label {
x: 0
y: parent.height * 0.75
width: parent.width
height: parent.height * 0.25
text: emojiItem.description
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}

View File

@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQml 2.15
import org.kde.kirigami 2.19 as Kirigami
import org.kde.neochat 1.0
Row {
id: emojiRow
property alias model: repeater.model
anchors.horizontalCenter: parent.horizontalCenter
Repeater {
id: repeater
delegate: EmojiItem {
emoji: modelData.emoji
description: modelData.description
width: emojiRow.height
height: width
}
}
}

View File

@@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQml 2.15
import org.kde.kirigami 2.19 as Kirigami
import org.kde.neochat 1.0
Column {
id: emojiSas
required property var model
signal accept()
signal reject()
visible: dialog.session.state === KeyVerificationSession.WAITINGFORVERIFICATION
anchors.centerIn: parent
spacing: Kirigami.Units.largeSpacing
QQC2.Label {
text: i18n("Confirm the emoji below are displayed on both devices, in the same order.")
}
EmojiRow {
anchors.horizontalCenter: parent.horizontalCenter
height: Kirigami.Units.gridUnit * 4
model: emojiSas.model.slice(0, 4)
}
EmojiRow {
anchors.horizontalCenter: parent.horizontalCenter
height: Kirigami.Units.gridUnit * 4
model: emojiSas.model.slice(4, 7)
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
QQC2.Button {
anchors.bottom: parent.bottom
text: i18n("They match")
icon.name: "dialog-ok"
onClicked: emojiSas.accept()
}
QQC2.Button {
anchors.bottom: parent.bottom
text: i18n("They don't match")
icon.name: "dialog-cancel"
onClicked: emojiSas.reject()
}
}
}

View File

@@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import QtQml 2.15
import org.kde.kirigami 2.19 as Kirigami
import org.kde.neochat 1.0
Kirigami.Page {
id: dialog
title: i18n("Session Verification")
required property var session
Item {
anchors.fill: parent
VerificationCanceled {
visible: dialog.session.state === KeyVerificationSession.CANCELED
anchors.centerIn: parent
reason: dialog.session.error
}
EmojiSas {
anchors.centerIn: parent
visible: dialog.session.state === KeyVerificationSession.WAITINGFORVERIFICATION
model: dialog.session.sasEmojis
onReject: dialog.session.cancelVerification(KeyVerificationSession.MISMATCHED_SAS)
onAccept: dialog.session.sendMac()
}
Message {
visible: dialog.session.state === KeyVerificationSession.WAITINGFORREADY
anchors.centerIn: parent
icon: "security-medium-symbolic"
text: i18n("Waiting for device to accept verification.")
}
Message {
visible: dialog.session.state === KeyVerificationSession.INCOMING
anchors.centerIn: parent
icon: "security-medium-symbolic"
text: i18n("Incoming key verification request from device **%1**", dialog.session.remoteDeviceId)
}
Message {
visible: dialog.session.state === KeyVerificationSession.WAITINGFORMAC
anchors.centerIn: parent
icon: "security-medium-symbolic"
text: i18n("Waiting for other party to verify.")
}
Kirigami.BasicListItem {
id: emojiVerification
text: "Emoji Verification"
visible: dialog.session.state === KeyVerificationSession.READY
subtitle: i18n("Compare a set of emoji on both devices")
onClicked: {
dialog.session.sendStartSas()
}
}
Message {
visible: dialog.session.state === KeyVerificationSession.DONE
anchors.centerIn: parent
text: i18n("Successfully verified device **%1**", dialog.session.remoteDeviceId)
icon: "security-high"
}
}
footer: QQC2.ToolBar {
visible: dialog.session.state === KeyVerificationSession.INCOMING
QQC2.DialogButtonBox {
anchors.fill: parent
Item { Layout.fillWidth: true }
QQC2.Button {
text: i18n("Accept")
icon.name: "dialog-ok"
onClicked: dialog.session.sendReady()
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
}
QQC2.Button {
text: i18n("Decline")
icon.name: "dialog-cancel"
onClicked: dialog.session.cancelVerification("m.user", "Declined")
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.CancelRole
}
}
}
}

View File

@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQml 2.15
import org.kde.kirigami 2.19 as Kirigami
import org.kde.neochat 1.0
Column {
id: message
required property string icon
required property string text
anchors.centerIn: parent
Kirigami.Icon {
width: Kirigami.Units.iconSizes.enormous
height: width
anchors.horizontalCenter: parent.horizontalCenter
source: message.icon
}
QQC2.Label {
text: message.text
textFormat: Text.MarkdownText
}
}

View File

@@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQml 2.15
import org.kde.kirigami 2.19 as Kirigami
import org.kde.neochat 1.0
Message {
id: verificationCanceled
required property int reason
anchors.centerIn: parent
icon: "security-low"
text: {
switch(verificationCanceled.reason) {
case KeyVerificationSession.NONE:
return i18n("The session verification was canceled for unknown reason.");
case KeyVerificationSession.TIMEOUT:
return i18n("The session verification timed out.");
case KeyVerificationSession.REMOTE_TIMEOUT:
return i18n("The session verification timed out for remote party.");
case KeyVerificationSession.USER:
return i18n("You canceled the session verification.");
case KeyVerificationSession.REMOTE_USER:
return i18n("The remote party canceled the session verification.");
case KeyVerificationSession.UNEXPECTED_MESSAGE:
return i18n("The session verification was canceled because we received an unexpected message.");
case KeyVerificationSession.REMOTE_UNEXPECTED_MESSAGE:
return i18n("The remote party canceled the session verification because it received an unexpected message.");
case KeyVerificationSession.UNKNOWN_TRANSACTION:
return i18n("The session verification was canceled because it received a message for an unknown session.");
case KeyVerificationSession.REMOTE_UNKNOWN_TRANSACTION:
return i18n("The remote party canceled the session verification because it received a message for an unknown session.");
case KeyVerificationSession.UNKNOWN_METHOD:
return i18n("The session verification was canceled because NeoChat is unable to handle this verification method.");
case KeyVerificationSession.REMOTE_UNKNOWN_METHOD:
return i18n("The remote party canceled the session verification because it is unable to handle this verification method.");
case KeyVerificationSession.KEY_MISMATCH:
return i18n("The session verification was canceled because the keys are incorrect.");
case KeyVerificationSession.REMOTE_KEY_MISMATCH:
return i18n("The remote party canceled the session verification because the keys are incorrect.");
case KeyVerificationSession.USER_MISMATCH:
return i18n("The session verification was canceled because it verifies an unexpected user.");
case KeyVerificationSession.REMOTE_USER_MISMATCH:
return i18n("The remote party canceled the session verification because it verifies an unexpected user.");
case KeyVerificationSession.INVALID_MESSAGE:
return i18n("The session verification was canceled because we received an invalid message.");
case KeyVerificationSession.REMOTE_INVALID_MESSAGE:
return i18n("The remote party canceled the session verification because it received an invalid message.");
case KeyVerificationSession.SESSION_ACCEPTED:
return i18n("The session was accepted on a different device"); //TODO this should not be visible
case KeyVerificationSession.REMOTE_SESSION_ACCEPTED:
return i18n("The session was accepted on a different device"); //TODO neither should this
case KeyVerificationSession.MISMATCHED_COMMITMENT:
return i18n("The session verification was canceled because of a mismatched key.");
case KeyVerificationSession.REMOTE_MISMATCHED_COMMITMENT:
return i18n("The remote party canceled the session verification because of a mismatched key.");
case KeyVerificationSession.MISMATCHED_SAS:
return i18n("The session verification was canceled because the keys do not match.");
case KeyVerificationSession.REMOTE_MISMATCHED_SAS:
return i18n("The remote party canceled the session verification because the keys do not match.");
default:
return i18n("The session verification was canceled due to an unknown error.");
}
}
}

View File

@@ -0,0 +1,7 @@
module NeoChat.Dialog.KeyVerification
KeyVerificationDialog 1.0 KeyVerificationDialog.qml
Message 1.0 Message.qml
VerificationCanceled 1.0 VerificationCanceled.qml
EmojiItem 1.0 EmojiItem.qml
EmojiRow 1.0 EmojiRow.qml
EmojiSas 1.0 EmojiSas.qml

View File

@@ -14,6 +14,8 @@ import NeoChat.Component 1.0
Kirigami.OverlaySheet { Kirigami.OverlaySheet {
id: root id: root
signal closed()
property var room property var room
property var user property var user
@@ -52,7 +54,7 @@ Kirigami.OverlaySheet {
onClicked: { onClicked: {
if (avatarMediaId) { if (avatarMediaId) {
fullScreenImage.createObject(parent, {"filename": displayName, "source": room.urlToMxcUrl(avatarUrl)}).showFullScreen() fullScreenImage.createObject(parent, {filename: displayName, source: room.urlToMxcUrl(avatarUrl)}).showFullScreen()
} }
} }
} }
@@ -164,5 +166,11 @@ Kirigami.OverlaySheet {
FullScreenImage {} FullScreenImage {}
} }
} }
onSheetOpenChanged: {
if (!sheetOpen) {
closed()
}
}
} }

View File

@@ -7,3 +7,4 @@ OpenFileDialog 1.0 OpenFileDialog.qml
ImageClipboardDialog 1.0 ImageClipboardDialog.qml ImageClipboardDialog 1.0 ImageClipboardDialog.qml
StartChatDialog 1.0 StartChatDialog.qml StartChatDialog 1.0 StartChatDialog.qml
EmojiDialog 1.0 EmojiDialog.qml EmojiDialog 1.0 EmojiDialog.qml
KeyVerificationDialog 1.0 KeyVerificationDialog.qml

View File

@@ -45,7 +45,7 @@ Labs.MenuBar {
Labs.MenuItem { Labs.MenuItem {
text: i18nc("menu", "New Private Chat…") text: i18nc("menu", "New Private Chat…")
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0 enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/StartChatPage.qml", {"connection": Controller.activeConnection}) onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/StartChatPage.qml", {connection: Controller.activeConnection})
} }
Labs.MenuItem { Labs.MenuItem {
text: i18nc("menu", "New Group…") text: i18nc("menu", "New Group…")
@@ -58,7 +58,7 @@ Labs.MenuBar {
} }
Labs.MenuItem { Labs.MenuItem {
text: i18nc("menu", "Browse Chats…") text: i18nc("menu", "Browse Chats…")
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/JoinRoomPage.qml", {"connection": Controller.activeConnection}) onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/JoinRoomPage.qml", {connection: Controller.activeConnection})
} }
} }
EditMenu { EditMenu {
@@ -92,7 +92,7 @@ Labs.MenuBar {
Labs.MenuItem { Labs.MenuItem {
text: i18nc("menu", "Matrix FAQ") text: i18nc("menu", "Matrix FAQ")
onTriggered: Qt.openUrlExternally("https://matrix.org/faq/") onTriggered: UrlHelper.openUrl("https://matrix.org/faq/")
} }
} }
} }

View File

@@ -24,7 +24,7 @@ Loader {
Menu { Menu {
MenuItem { MenuItem {
id: newWindow id: newWindow
text: i18n("Open in new window") text: i18n("Open in New Window")
onTriggered: RoomManager.openWindow(room); onTriggered: RoomManager.openWindow(room);
visible: !Kirigami.Settings.isMobile visible: !Kirigami.Settings.isMobile
} }
@@ -49,7 +49,7 @@ Loader {
} }
MenuItem { MenuItem {
text: i18nc("@action:inmenu", "Copy address to clipboard") text: i18nc("@action:inmenu", "Copy Address to Clipboard")
onTriggered: if (room.canonicalAlias.length === 0) { onTriggered: if (room.canonicalAlias.length === 0) {
Clipboard.saveText(room.id) Clipboard.saveText(room.id)
} else { } else {
@@ -57,6 +57,56 @@ Loader {
} }
} }
Menu {
title: i18n("Notification State")
MenuItem {
text: i18n("Follow Global Setting")
checkable: true
autoExclusive: true
checked: room.pushNotificationState === PushNotificationState.Default
enabled: room.pushNotificationState != PushNotificationState.Unknown
onTriggered: {
room.pushNotificationState = PushNotificationState.Default
}
}
MenuItem {
text: i18nc("As in 'notify for all messages'","All")
checkable: true
autoExclusive: true
checked: room.pushNotificationState === PushNotificationState.All
enabled: room.pushNotificationState != PushNotificationState.Unknown
onTriggered: {
room.pushNotificationState = PushNotificationState.All
}
}
MenuItem {
text: i18nc("As in 'notify when the user is mentioned or the message contains a set keyword'","@Mentions and Keywords")
checkable: true
autoExclusive: true
checked: room.pushNotificationState === PushNotificationState.MentionKeyword
enabled: room.pushNotificationState != PushNotificationState.Unknown
onTriggered: {
room.pushNotificationState = PushNotificationState.MentionKeyword
}
}
MenuItem {
text: i18nc("As in 'do not notify for any messages'","Off")
checkable: true
autoExclusive: true
checked: room.pushNotificationState === PushNotificationState.Mute
enabled: room.pushNotificationState != PushNotificationState.Unknown
onTriggered: {
room.pushNotificationState = PushNotificationState.Mute
}
}
}
MenuItem {
text: i18n("Room Settings")
onTriggered: ApplicationWindow.window.pageStack.pushDialogLayer('qrc:/imports/NeoChat/RoomSettings/Categories.qml', {room: room})
}
MenuSeparator {} MenuSeparator {}
MenuItem { MenuItem {

View File

@@ -14,6 +14,8 @@ import NeoChat.Menu 1.0
MessageDelegateContextMenu { MessageDelegateContextMenu {
id: root id: root
signal closeFullscreen
required property var file required property var file
required property var progressInfo required property var progressInfo
required property string mimeType required property string mimeType
@@ -24,13 +26,13 @@ MessageDelegateContextMenu {
icon.name: "document-open" icon.name: "document-open"
onTriggered: { onTriggered: {
if (file.downloaded) { if (file.downloaded) {
if (!Qt.openUrlExternally(progressInfo.localPath)) { if (!UrlHelper.openUrl(progressInfo.localPath)) {
Qt.openUrlExternally(progressInfo.localDir); UrlHelper.openUrl(progressInfo.localDir);
} }
} else { } else {
file.onDownloadedChanged.connect(function() { file.onDownloadedChanged.connect(function() {
if (!Qt.openUrlExternally(progressInfo.localPath)) { if (!UrlHelper.openUrl(progressInfo.localPath)) {
Qt.openUrlExternally(progressInfo.localDir); UrlHelper.openUrl(progressInfo.localDir);
} }
}); });
currentRoom.downloadFile(eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId)) currentRoom.downloadFile(eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId))
@@ -50,7 +52,8 @@ MessageDelegateContextMenu {
text: i18n("Reply") text: i18n("Reply")
icon.name: "mail-replied-symbolic" icon.name: "mail-replied-symbolic"
onTriggered: { onTriggered: {
ChatBoxHelper.replyToMessage(eventId, message, author); chatBoxHelper.replyToMessage(eventId, message, author);
root.closeFullscreen()
} }
}, },
Kirigami.Action { Kirigami.Action {
@@ -60,8 +63,18 @@ MessageDelegateContextMenu {
icon.color: "red" icon.color: "red"
onTriggered: { onTriggered: {
currentRoom.redactEvent(eventId); currentRoom.redactEvent(eventId);
root.closeFullscreen()
} }
}, },
Kirigami.Action {
text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report")
icon.name: "dialog-warning-symbolic"
visible: author.id !== currentRoom.localUser.id
onTriggered: applicationWindow().pageStack.pushDialogLayer("qrc:/imports/NeoChat/Menu/Timeline/ReportSheet.qml", {room: currentRoom, eventId: eventId}, {
title: i18nc("@title", "Report Message"),
width: Kirigami.Units.gridUnit * 25
})
},
Kirigami.Action { Kirigami.Action {
text: i18n("View Source") text: i18n("View Source")
icon.name: "code-context" icon.name: "code-context"
@@ -72,6 +85,7 @@ MessageDelegateContextMenu {
title: i18n("Message Source"), title: i18n("Message Source"),
width: Kirigami.Units.gridUnit * 25 width: Kirigami.Units.gridUnit * 25
}); });
root.closeFullscreen()
} }
} }
] ]

View File

@@ -20,6 +20,7 @@ Loader {
property string formattedBody: "" property string formattedBody: ""
required property string source required property string source
property string selectedText: "" property string selectedText: ""
required property string plainMessage
property list<Kirigami.Action> nestedActions property list<Kirigami.Action> nestedActions
@@ -27,13 +28,13 @@ Loader {
Kirigami.Action { Kirigami.Action {
text: i18n("Edit") text: i18n("Edit")
icon.name: "document-edit" icon.name: "document-edit"
onTriggered: ChatBoxHelper.edit(message, formattedBody, eventId); onTriggered: chatBoxHelper.edit(message, formattedBody, eventId);
visible: eventType.length > 0 && author.id === Controller.activeConnection.localUserId && (eventType === "emote" || eventType === "message") visible: eventType.length > 0 && author.id === Controller.activeConnection.localUserId && (eventType === "emote" || eventType === "message")
}, },
Kirigami.Action { Kirigami.Action {
text: i18n("Reply") text: i18n("Reply")
icon.name: "mail-replied-symbolic" icon.name: "mail-replied-symbolic"
onTriggered: ChatBoxHelper.replyToMessage(eventId, message, author); onTriggered: chatBoxHelper.replyToMessage(eventId, message, author);
}, },
Kirigami.Action { Kirigami.Action {
visible: author.id === currentRoom.localUser.id || currentRoom.canSendState("redact") visible: author.id === currentRoom.localUser.id || currentRoom.canSendState("redact")
@@ -45,7 +46,16 @@ Loader {
Kirigami.Action { Kirigami.Action {
text: i18n("Copy") text: i18n("Copy")
icon.name: "edit-copy" icon.name: "edit-copy"
onTriggered: Clipboard.saveText(loadRoot.selectedText === "" ? loadRoot.message : loadRoot.selectedText) onTriggered: Clipboard.saveText(loadRoot.selectedText === "" ? loadRoot.plainMessage : loadRoot.selectedText)
},
Kirigami.Action {
text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report")
icon.name: "dialog-warning-symbolic"
visible: author.id !== currentRoom.localUser.id
onTriggered: applicationWindow().pageStack.pushDialogLayer("qrc:/imports/NeoChat/Menu/Timeline/ReportSheet.qml", {room: currentRoom, eventId: eventId}, {
title: i18nc("@title", "Report Message"),
width: Kirigami.Units.gridUnit * 25
})
}, },
Kirigami.Action { Kirigami.Action {
text: i18n("View Source") text: i18n("View Source")
@@ -111,7 +121,7 @@ Loader {
Instantiator { Instantiator {
model: WebShortcutModel { model: WebShortcutModel {
id: webshortcutmodel id: webshortcutmodel
selectedText: loadRoot.selectedText ? loadRoot.selectedText : loadRoot.message selectedText: loadRoot.selectedText ? loadRoot.selectedText : loadRoot.plainMessage
onOpenUrl: RoomManager.visitNonMatrix(url) onOpenUrl: RoomManager.visitNonMatrix(url)
} }
delegate: QQC2.MenuItem { delegate: QQC2.MenuItem {

View File

@@ -22,6 +22,10 @@ Kirigami.Page {
ScrollView { ScrollView {
anchors.fill: parent anchors.fill: parent
contentWidth: availableWidth contentWidth: availableWidth
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
TextArea { TextArea {
id: sourceTextArea id: sourceTextArea
text: sourceText text: sourceText

View File

@@ -0,0 +1,47 @@
// SPDX-FileCopyrightText: 2022 Tobias Fella <fella@posteo.de>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.20 as Kirigami
Kirigami.Page {
id: reportSheet
property var room
property string eventId
title: i18n("Report Message")
QQC2.TextArea {
id: reason
placeholderText: i18n("Reason for reporting this message")
anchors.fill: parent
wrapMode: TextEdit.Wrap
}
footer: QQC2.ToolBar {
QQC2.DialogButtonBox {
anchors.fill: parent
Item {
Layout.fillWidth: true
}
QQC2.Button {
text: i18nc("@action:button 'Report' as in 'Report this event to the administrators'", "Report")
icon.name: "dialog-warning-symbolic"
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
onClicked: {
reportSheet.room.reportEvent(eventId, reason.text)
reportSheet.closeDialog()
}
}
QQC2.Button {
text: i18nc("@action", "Cancel")
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.RejectRole
onClicked: reportSheet.closeDialog()
}
}
}
}

View File

@@ -2,3 +2,4 @@ module NeoChat.Menu.Timeline
MessageDelegateContextMenu 1.0 MessageDelegateContextMenu.qml MessageDelegateContextMenu 1.0 MessageDelegateContextMenu.qml
FileDelegateContextMenu 1.0 FileDelegateContextMenu.qml FileDelegateContextMenu 1.0 FileDelegateContextMenu.qml
MessageSourceSheet 1.0 MessageSourceSheet.qml MessageSourceSheet 1.0 MessageSourceSheet.qml
ReportSheet 1.0 ReportSheet.qml

View File

@@ -4,7 +4,6 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2 import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
import Qt.labs.platform 1.1 as Platform import Qt.labs.platform 1.1 as Platform
import org.kde.kirigami 2.15 as Kirigami import org.kde.kirigami 2.15 as Kirigami

View File

@@ -3,18 +3,11 @@
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtQuick.Controls 2.12 as QQC2 import QtQuick.Controls 2.12 as QQC2
import org.kde.kirigami 2.12 as Kirigami import org.kde.kirigami 2.19 as Kirigami
Kirigami.Page { Kirigami.Page {
title: i18n("Loading…") Kirigami.LoadingPlaceholder {
Kirigami.PlaceholderMessage {
id: loadingIndicator id: loadingIndicator
anchors.centerIn: parent anchors.centerIn: parent
text: i18n("Loading…")
QQC2.BusyIndicator {
running: false
Layout.alignment: Qt.AlignHCenter
}
} }
} }

View File

@@ -15,6 +15,95 @@ import NeoChat.Component 1.0
import NeoChat.Menu 1.0 import NeoChat.Menu 1.0
Kirigami.ScrollablePage { Kirigami.ScrollablePage {
header: ColumnLayout {
visible: !page.collapsedMode
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 0
ListView {
id: spaceList
property string activeSpaceId: ''
orientation: Qt.Horizontal
spacing: Kirigami.Units.largeSpacing
clip:true
visible: spaceList.count > 0
Layout.preferredHeight: Kirigami.Units.gridUnit * 3
Layout.fillWidth: true
model: SortFilterSpaceListModel {
id: sortFilterSpaceListModel
sourceModel: RoomListModel {
id: spaceListModel
connection: Controller.activeConnection
}
}
Connections {
target: SpaceHierarchyCache
function onSpaceHierarchyChanged() {
if (spaceList.activeSpaceId !== '') {
sortFilterRoomListModel.activeSpaceRooms = SpaceHierarchyCache.getRoomListForSpace(spaceList.activeSpaceId, false);
}
}
}
header: QQC2.Control {
contentItem: QQC2.RoundButton {
id: homeButton
flat: true
padding: Kirigami.Units.gridUnit / 2
icon.name: "home"
text: i18nc("@action:button", "Show All Rooms")
display: QQC2.AbstractButton.IconOnly
onClicked: {
sortFilterRoomListModel.activeSpaceRooms = [];
spaceList.activeSpaceId = '';
listView.positionViewAtIndex(0, ListView.Beginning);
}
QQC2.ToolTip {
text: homeButton.text
}
}
}
delegate: QQC2.Control {
required property string avatar
required property var currentRoom
required property int index
required property string id
implicitWidth: ListView.view.headerItem.implicitWidth
implicitHeight: ListView.view.headerItem.implicitHeight
contentItem: Kirigami.Avatar {
id: del
actions.main: Kirigami.Action {
id: enterSpaceAction
onTriggered: {
spaceList.activeSpaceId = id;
sortFilterRoomListModel.activeSpaceRooms = SpaceHierarchyCache.getRoomListForSpace(id, true);
}
}
QQC2.ToolTip {
text: currentRoom.displayName
}
source: avatar !== "" ? "image://mxc/" + avatar : ""
}
}
}
Kirigami.Separator {
Layout.fillWidth: true
}
}
id: page id: page
title: i18n("Rooms") title: i18n("Rooms")
@@ -22,21 +111,10 @@ Kirigami.ScrollablePage {
property var enteredRoom property var enteredRoom
property bool collapsedMode: Config.roomListPageWidth === applicationWindow().collapsedPageWidth && applicationWindow().shouldUseSidebars property bool collapsedMode: Config.roomListPageWidth === applicationWindow().collapsedPageWidth && applicationWindow().shouldUseSidebars
verticalScrollBarPolicy: collapsedMode ? QQC2.ScrollBar.AlwaysOff : QQC2.ScrollBar.AsNeeded
onCollapsedModeChanged: if (collapsedMode) { onCollapsedModeChanged: if (collapsedMode) {
sortFilterRoomListModel.filterText = ""; sortFilterRoomListModel.filterText = "";
if (page.contentItem && page.contentItem.flickableItem && page.contentItem.flickableItem.QQC2.ScrollBar.vertical) {
page.contentItem.flickableItem.QQC2.ScrollBar.vertical.visible = false;
}
} else {
page.contentItem.flickableItem.QQC2.ScrollBar.vertical.visible = true;
}
// HACK: the scrollbar is created with a 0 timer, so we need to set the visible flag
// after it has been created
Timer {
running: true
interval: 200
onTriggered: page.contentItem.flickableItem.QQC2.ScrollBar.vertical.visible = !collapsedMode;
} }
Connections { Connections {
@@ -79,38 +157,39 @@ Kirigami.ScrollablePage {
} }
} }
header: QQC2.ItemDelegate {
visible: page.collapsedMode
action: Kirigami.Action {
id: enterRoomAction
onTriggered: quickView.item.open();
}
topPadding: Kirigami.Units.largeSpacing
leftPadding: Kirigami.Units.largeSpacing
rightPadding: Kirigami.Units.largeSpacing
bottomPadding: Kirigami.Units.largeSpacing
width: visible ? page.width : 0
height: visible ? Kirigami.Units.gridUnit * 2 : 0
Kirigami.Icon {
anchors.centerIn: parent
width: 22
height: 22
source: "search"
}
Kirigami.Separator {
width: parent.width
anchors.bottom: parent.bottom
}
}
ListView { ListView {
id: listView id: listView
activeFocusOnTab: true activeFocusOnTab: true
clip: accountList.count > 1 clip: accountList.count > 1
header: QQC2.ItemDelegate {
visible: page.collapsedMode
action: Kirigami.Action {
id: enterRoomAction
onTriggered: quickView.item.open();
}
topPadding: Kirigami.Units.largeSpacing
leftPadding: Kirigami.Units.largeSpacing
rightPadding: Kirigami.Units.largeSpacing
bottomPadding: Kirigami.Units.largeSpacing
width: visible ? page.width : 0
height: visible ? Kirigami.Units.gridUnit * 2 : 0
Kirigami.Icon {
anchors.centerIn: parent
width: 22
height: 22
source: "search"
}
Kirigami.Separator {
width: parent.width
anchors.bottom: parent.bottom
}
}
Layout.fillWidth: true
Kirigami.PlaceholderMessage { Kirigami.PlaceholderMessage {
anchors.centerIn: parent anchors.centerIn: parent
width: parent.width - (Kirigami.Units.largeSpacing * 4) width: parent.width - (Kirigami.Units.largeSpacing * 4)
@@ -240,14 +319,12 @@ Kirigami.ScrollablePage {
subtitle: subtitleText subtitle: subtitleText
subtitleItem.textFormat: Text.PlainText subtitleItem.textFormat: Text.PlainText
onPressAndHold: { onPressAndHold: {
const menu = roomListContextMenu.createObject(page, {"room": currentRoom}) createRoomListContextMenu()
configButton.visible = true }
configButton.down = true TapHandler {
menu.closed.connect(function() { acceptedButtons: Qt.RightButton
configButton.down = undefined acceptedDevices: PointerDevice.Mouse
configButton.visible = Qt.binding(function() { return roomListItem.hovered || Kirigami.Settings.isMobile }) onTapped: createRoomListContextMenu()
})
menu.open()
} }
leading: Kirigami.Avatar { leading: Kirigami.Avatar {
@@ -261,15 +338,17 @@ Kirigami.ScrollablePage {
trailing: RowLayout { trailing: RowLayout {
QQC2.Label { QQC2.Label {
text: notificationCount text: notificationCount > 0 ? notificationCount : "●"
visible: notificationCount > 0 visible: unreadCount > 0
padding: Kirigami.Units.smallSpacing padding: Kirigami.Units.smallSpacing
color: highlightCount > 0 ? "white" : Kirigami.Theme.textColor color: Kirigami.Theme.textColor
Layout.minimumWidth: height Layout.minimumWidth: height
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
background: Rectangle { background: Rectangle {
visible: notificationCount > 0
Kirigami.Theme.colorSet: Kirigami.Theme.Button Kirigami.Theme.colorSet: Kirigami.Theme.Button
color: highlightCount > 0 ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor color: highlightCount > 0 ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.disabledTextColor
opacity: highlightCount > 0 ? 1 : 0.3
radius: height / 2 radius: height / 2
} }
} }
@@ -282,18 +361,22 @@ Kirigami.ScrollablePage {
id: optionAction id: optionAction
icon.name: "configure" icon.name: "configure"
onTriggered: { onTriggered: {
const menu = roomListContextMenu.createObject(page, {"room": currentRoom}) createRoomListContextMenu()
configButton.visible = true
configButton.down = true
menu.closed.connect(function() {
configButton.down = undefined
configButton.visible = Qt.binding(function() { return roomListItem.hovered || Kirigami.Settings.isMobile })
})
menu.open()
} }
} }
} }
} }
function createRoomListContextMenu() {
const menu = roomListContextMenu.createObject(page, {room: currentRoom})
configButton.visible = true
configButton.down = true
menu.closed.connect(function() {
configButton.down = undefined
configButton.visible = Qt.binding(function() { return roomListItem.hovered || Kirigami.Settings.isMobile })
})
menu.open()
}
} }
} }
} }

View File

@@ -8,7 +8,7 @@ import QtQuick.Layouts 1.15
import Qt.labs.platform 1.1 as Platform import Qt.labs.platform 1.1 as Platform
import Qt.labs.qmlmodels 1.0 import Qt.labs.qmlmodels 1.0
import org.kde.kirigami 2.15 as Kirigami import org.kde.kirigami 2.19 as Kirigami
import org.kde.kitemmodels 1.0 import org.kde.kitemmodels 1.0
import org.kde.neochat 1.0 import org.kde.neochat 1.0
@@ -23,6 +23,7 @@ Kirigami.ScrollablePage {
/// It's not readonly because of the seperate window view. /// It's not readonly because of the seperate window view.
property var currentRoom: RoomManager.currentRoom property var currentRoom: RoomManager.currentRoom
property bool loading: page.currentRoom === null || (messageListView.count === 0 && !page.currentRoom.allHistoryLoaded && !page.currentRoom.isInvite)
/// Used to determine if scrolling to the bottom should mark the message as unread /// Used to determine if scrolling to the bottom should mark the message as unread
property bool hasScrolledUpBefore: false; property bool hasScrolledUpBefore: false;
@@ -52,7 +53,26 @@ Kirigami.ScrollablePage {
onCurrentRoomChanged: { onCurrentRoomChanged: {
hasScrolledUpBefore = false; hasScrolledUpBefore = false;
ChatBoxHelper.clearEditReply() chatBoxHelper.clearEditReply()
}
Connections {
target: messageEventModel
function onRowsInserted() {
markReadIfVisibleTimer.restart()
}
}
Timer {
id: markReadIfVisibleTimer
interval: 1000
onTriggered: {
if (loading || !currentRoom.readMarkerLoaded || !applicationWindow().active) {
restart()
} else {
markReadIfVisible()
}
}
} }
ActionsHandler { ActionsHandler {
@@ -61,6 +81,10 @@ Kirigami.ScrollablePage {
connection: Controller.activeConnection connection: Controller.activeConnection
} }
ChatBoxHelper {
id: chatBoxHelper
}
Shortcut { Shortcut {
sequence: StandardKey.Cancel sequence: StandardKey.Cancel
onActivated: applicationWindow().pageStack.get(0).forceActiveFocus() onActivated: applicationWindow().pageStack.get(0).forceActiveFocus()
@@ -81,17 +105,17 @@ Kirigami.ScrollablePage {
function onShowMessage(messageType, message) { function onShowMessage(messageType, message) {
page.header.contentItem.text = message; page.header.contentItem.text = message;
page.header.contentItem.type = messageType === ActionsHandler.Error ? Kirigami.MessageType.Error : Kirigami.MessageType.Information; page.header.contentItem.type = messageType === ActionsHandler.Error ? Kirigami.MessageType.Error : Kirigami.MessageType.Information;
page.header.contentItem.visible = true; page.header.visible = true;
} }
} }
header: QQC2.Control { header: QQC2.Control {
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
visible: contentItem.visible visible: false
padding: Kirigami.Units.smallSpacing padding: Kirigami.Units.smallSpacing
contentItem: Kirigami.InlineMessage { contentItem: Kirigami.InlineMessage {
showCloseButton: true showCloseButton: true
visible: false visible: true
} }
} }
@@ -120,15 +144,10 @@ Kirigami.ScrollablePage {
} }
} }
Kirigami.PlaceholderMessage { Kirigami.LoadingPlaceholder {
id: loadingIndicator id: loadingIndicator
anchors.centerIn: parent anchors.centerIn: parent
visible: page.currentRoom === null || (messageListView.count === 0 && !page.currentRoom.allHistoryLoaded && !page.currentRoom.isInvite) visible: loading
text: i18n("Loading…")
QQC2.BusyIndicator {
running: loadingIndicator.visible
Layout.alignment: Qt.AlignHCenter
}
} }
focus: true focus: true
@@ -147,9 +166,9 @@ Kirigami.ScrollablePage {
Keys.onPressed: { Keys.onPressed: {
if (event.key === Qt.Key_PageDown && (event.modifiers & Qt.ControlModifier)) { if (event.key === Qt.Key_PageDown && (event.modifiers & Qt.ControlModifier)) {
switchRoomUp();
} else if (event.key === Qt.Key_PageUp && (event.modifiers & Qt.ControlModifier)) {
switchRoomDown(); switchRoomDown();
} else if (event.key === Qt.Key_PageUp && (event.modifiers & Qt.ControlModifier)) {
switchRoomUp();
} else if (!(event.modifiers & Qt.ControlModifier) && event.key < Qt.Key_Escape) { } else if (!(event.modifiers & Qt.ControlModifier) && event.key < Qt.Key_Escape) {
event.accepted = true; event.accepted = true;
chatBox.addText(event.text); chatBox.addText(event.text);
@@ -158,6 +177,15 @@ Kirigami.ScrollablePage {
} }
} }
Connections {
target: currentRoom
function onPositiveMessage(message) {
page.header.contentItem.text = message;
page.header.contentItem.type = Kirigami.MessageType.Positive;
page.header.visible = true;
}
}
// hover actions on a delegate, activated in TimelineContainer.qml // hover actions on a delegate, activated in TimelineContainer.qml
Connections { Connections {
target: page.flickable target: page.flickable
@@ -167,80 +195,6 @@ Kirigami.ScrollablePage {
} }
} }
Item {
id: hoverActions
property var event: null
property bool showEdit: event && (event.author.id === Controller.activeConnection.localUserId && (event.eventType === "emote" || event.eventType === "message"))
property var bubble: null
property var hovered: bubble && bubble.hovered
property var visibleDelayed: (hovered || hoverHandler.hovered) && !Kirigami.Settings.isMobile
onVisibleDelayedChanged: if (visibleDelayed) {
visible = true;
} else {
// HACK: delay disapearing by 200ms, otherwise this can create some glitches
// See https://invent.kde.org/network/neochat/-/issues/333
hoverActionsTimer.restart();
}
Timer {
id: hoverActionsTimer
interval: 200
onTriggered: hoverActions.visible = hoverActions.visibleDelayed;
}
x: bubble ? (bubble.x + Kirigami.Units.largeSpacing + Math.max(bubble.width - childWidth, 0) - (Config.compactLayout ? Kirigami.Units.gridUnit * 3 : 0)) : 0
y: bubble ? bubble.mapToItem(parent, 0, 0).y - hoverActions.childHeight + Kirigami.Units.smallSpacing: 0;
visible: false
property var updateFunction
property alias childWidth: hoverActionsRow.width
property alias childHeight: hoverActionsRow.height
RowLayout {
id: hoverActionsRow
z: 4
spacing: 0
HoverHandler {
id: hoverHandler
margin: Kirigami.Units.smallSpacing
}
QQC2.Button {
QQC2.ToolTip.text: i18n("React")
QQC2.ToolTip.visible: hovered
icon.name: "preferences-desktop-emoticons"
onClicked: emojiDialog.open();
EmojiDialog {
id: emojiDialog
onReact: {
page.currentRoom.toggleReaction(hoverActions.event.eventId, emoji);
chatBox.focusInputField();
}
}
}
QQC2.Button {
QQC2.ToolTip.text: i18n("Edit")
QQC2.ToolTip.visible: hovered
visible: hoverActions.showEdit
icon.name: "document-edit"
onClicked: {
if (hoverActions.showEdit) {
ChatBoxHelper.edit(hoverActions.event.message, hoverActions.event.formattedBody, hoverActions.event.eventId)
}
chatBox.focusInputField();
}
}
QQC2.Button {
QQC2.ToolTip.text: i18n("Reply")
QQC2.ToolTip.visible: hovered
icon.name: "mail-replied-symbolic"
onClicked: {
ChatBoxHelper.replyToMessage(hoverActions.event.eventId, hoverActions.event.message, hoverActions.event.author);
chatBox.focusInputField();
}
}
}
}
CollapseStateProxyModel { CollapseStateProxyModel {
id: collapseStateProxyModel id: collapseStateProxyModel
sourceModel: sortedMessageEventModel sourceModel: sortedMessageEventModel
@@ -316,7 +270,7 @@ Kirigami.ScrollablePage {
fileDialog.chosen.connect(function(path) { fileDialog.chosen.connect(function(path) {
if (!path) return if (!path) return
ChatBoxHelper.attachmentPath = path; chatBoxHelper.attachmentPath = path;
}) })
fileDialog.open() fileDialog.open()
@@ -338,7 +292,7 @@ Kirigami.ScrollablePage {
if (!Clipboard.saveImage(localPath)) { if (!Clipboard.saveImage(localPath)) {
return; return;
} }
ChatBoxHelper.attachmentPath = localPath; chatBoxHelper.attachmentPath = localPath;
attachDialog.close(); attachDialog.close();
} }
} }
@@ -413,7 +367,7 @@ Kirigami.ScrollablePage {
DropArea { DropArea {
id: dropAreaFile id: dropAreaFile
anchors.fill: parent anchors.fill: parent
onDropped: ChatBoxHelper.attachmentPath = drop.urls[0] onDropped: chatBoxHelper.attachmentPath = drop.urls[0]
} }
QQC2.Pane { QQC2.Pane {
@@ -462,7 +416,7 @@ Kirigami.ScrollablePage {
currentRoom.usersTyping.length, currentRoom.usersTyping.length,
currentRoom.usersTyping.map(user => user.displayName).join(", ") currentRoom.usersTyping.map(user => user.displayName).join(", ")
) : "" ) : ""
anchors.right: parent.right anchors.left: parent.left
height: visible ? implicitHeight : 0 height: visible ? implicitHeight : 0
Behavior on height { Behavior on height {
NumberAnimation { NumberAnimation {
@@ -478,6 +432,100 @@ Kirigami.ScrollablePage {
function goToEvent(eventID) { function goToEvent(eventID) {
messageListView.positionViewAtIndex(eventToIndex(eventID), ListView.Contain) messageListView.positionViewAtIndex(eventToIndex(eventID), ListView.Contain)
} }
Item {
id: hoverActions
property var event: null
property bool userMsg: event && event.author.id === Controller.activeConnection.localUserId
property bool showEdit: event && (userMsg && (event.eventType === "emote" || event.eventType === "message"))
property var delegate: null
property var bubble: null
property var hovered: bubble && bubble.hovered
property var visibleDelayed: (hovered || hoverHandler.hovered) && !Kirigami.Settings.isMobile
onVisibleDelayedChanged: if (visibleDelayed) {
visible = true;
} else {
// HACK: delay disapearing by 200ms, otherwise this can create some glitches
// See https://invent.kde.org/network/neochat/-/issues/333
hoverActionsTimer.restart();
}
Timer {
id: hoverActionsTimer
interval: 200
onTriggered: hoverActions.visible = hoverActions.visibleDelayed;
}
property int childOffset: userMsg && Config.showLocalMessagesOnRight && !Config.compactLayout ? (bubble ? bubble.width : 0) - childWidth : Math.max((bubble ? bubble.width : 0) - childWidth, 0)
x: delegate && bubble ? (delegate.x + bubble.x + Kirigami.Units.largeSpacing + childOffset - (Config.compactLayout ? Kirigami.Units.gridUnit * 3 : 0)) : 0
y: bubble ? bubble.mapToItem(parent, 0, 0).y - hoverActions.childHeight + Kirigami.Units.smallSpacing: 0;
visible: false
property var updateFunction
property alias childWidth: hoverActionsRow.width
property alias childHeight: hoverActionsRow.height
RowLayout {
id: hoverActionsRow
z: 4
spacing: 0
HoverHandler {
id: hoverHandler
margin: Kirigami.Units.smallSpacing
}
Kirigami.Icon {
source: "security-high"
width: height
height: parent.height
visible: hoverActions.event.verified
HoverHandler {
id: hover
}
QQC2.ToolTip.text: i18n("This message was sent from a verified device")
QQC2.ToolTip.visible: hover.hovered
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
}
QQC2.Button {
QQC2.ToolTip.text: i18n("React")
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
icon.name: "preferences-desktop-emoticons"
onClicked: emojiDialog.open();
EmojiDialog {
id: emojiDialog
onReact: {
page.currentRoom.toggleReaction(hoverActions.event.eventId, emoji);
chatBox.focusInputField();
}
}
}
QQC2.Button {
QQC2.ToolTip.text: i18n("Edit")
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
visible: hoverActions.showEdit
icon.name: "document-edit"
onClicked: {
if (hoverActions.showEdit) {
chatBoxHelper.edit(hoverActions.event.message, hoverActions.event.formattedBody, hoverActions.event.eventId)
}
chatBox.focusInputField();
}
}
QQC2.Button {
QQC2.ToolTip.text: i18n("Reply")
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
icon.name: "mail-replied-symbolic"
onClicked: {
chatBoxHelper.replyToMessage(hoverActions.event.eventId, hoverActions.event.message, hoverActions.event.author);
chatBox.focusInputField();
}
}
}
}
} }
@@ -492,14 +540,14 @@ Kirigami.ScrollablePage {
onEditLastUserMessage: { onEditLastUserMessage: {
const targetMessage = messageEventModel.getLastLocalUserMessageEventId(); const targetMessage = messageEventModel.getLastLocalUserMessageEventId();
if (targetMessage) { if (targetMessage) {
ChatBoxHelper.edit(targetMessage["body"], targetMessage["body"], targetMessage["event_id"]); chatBoxHelper.edit(targetMessage["message"], targetMessage["formattedBody"], targetMessage["event_id"]);
chatBox.focusInputField(); chatBox.focusInputField();
} }
} }
onReplyPreviousUserMessage: { onReplyPreviousUserMessage: {
const replyResponse = messageEventModel.getLatestMessageFromIndex(0); const replyResponse = messageEventModel.getLatestMessageFromIndex(0);
if (replyResponse && replyResponse["event_id"]) { if (replyResponse && replyResponse["event_id"]) {
ChatBoxHelper.replyToMessage(replyResponse["event_id"], replyResponse["event"], replyResponse["sender_id"]); chatBoxHelper.replyToMessage(replyResponse["event_id"], replyResponse["message"], replyResponse["sender_id"]);
} }
} }
} }
@@ -543,7 +591,7 @@ Kirigami.ScrollablePage {
function warning(title, message) { function warning(title, message) {
page.header.contentItem.text = `${title}<br />${message}`; page.header.contentItem.text = `${title}<br />${message}`;
page.header.contentItem.type = Kirigami.MessageType.Warning; page.header.contentItem.type = Kirigami.MessageType.Warning;
page.header.contentItem.visible = true; page.header.visible = true;
} }
function showUserDetail(user) { function showUserDetail(user) {
@@ -588,6 +636,14 @@ Kirigami.ScrollablePage {
return index; return index;
} }
// Mark all messages as read if all unread messages are visible to the user
function markReadIfVisible() {
let readMarkerRow = eventToIndex(currentRoom.readMarkerEventId)
if (readMarkerRow > 0 && readMarkerRow < firstVisibleIndex()) {
currentRoom.markAllMessagesAsRead()
}
}
/// Open message context dialog for file and videos /// Open message context dialog for file and videos
function openFileContext(event, file) { function openFileContext(event, file) {
const contextMenu = fileDelegateContextMenu.createObject(page, { const contextMenu = fileDelegateContextMenu.createObject(page, {
@@ -598,12 +654,13 @@ Kirigami.ScrollablePage {
file: file, file: file,
mimeType: event.mimeType, mimeType: event.mimeType,
progressInfo: event.progressInfo, progressInfo: event.progressInfo,
plainMessage: event.message,
}); });
contextMenu.open(); contextMenu.open();
} }
/// Open context menu for normal message /// Open context menu for normal message
function openMessageContext(event, selectedText) { function openMessageContext(event, selectedText, plainMessage) {
const contextMenu = messageDelegateContextMenu.createObject(page, { const contextMenu = messageDelegateContextMenu.createObject(page, {
selectedText: selectedText, selectedText: selectedText,
author: event.author, author: event.author,
@@ -611,7 +668,8 @@ Kirigami.ScrollablePage {
eventId: event.eventId, eventId: event.eventId,
formattedBody: event.formattedBody, formattedBody: event.formattedBody,
source: event.source, source: event.source,
eventType: event.eventType eventType: event.eventType,
plainMessage: plainMessage,
}); });
contextMenu.open(); contextMenu.open();
} }

View File

@@ -18,7 +18,8 @@ Kirigami.OverlayDrawer {
id: roomDrawer id: roomDrawer
readonly property var room: RoomManager.currentRoom readonly property var room: RoomManager.currentRoom
width: modal ? undefined : actualWidth width: actualWidth
readonly property int minWidth: Kirigami.Units.gridUnit * 15 readonly property int minWidth: Kirigami.Units.gridUnit * 15
readonly property int maxWidth: Kirigami.Units.gridUnit * 25 readonly property int maxWidth: Kirigami.Units.gridUnit * 25
readonly property int defaultWidth: Kirigami.Units.gridUnit * 20 readonly property int defaultWidth: Kirigami.Units.gridUnit * 20
@@ -62,56 +63,84 @@ Kirigami.OverlayDrawer {
edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge
// If modal has been changed and the drawer is closed automatically then dim on popup open will have been switched off in main.qml so switch it back on after the animation completes.
// This is to avoid dim being active for a split second when the drawer is switched to modal which looks terrible.
onAnimatingChanged: if (dim === false) dim = undefined
topPadding: 0 topPadding: 0
leftPadding: 0 leftPadding: 0
rightPadding: 0 rightPadding: 0
Kirigami.Theme.colorSet: Kirigami.Theme.View Kirigami.Theme.colorSet: Kirigami.Theme.View
contentItem: Loader { contentItem: Loader {
id: loader
active: roomDrawer.drawerOpen active: roomDrawer.drawerOpen
sourceComponent: ColumnLayout { sourceComponent: ColumnLayout {
id: columnLayout id: columnLayout
property alias userSearchText: userListSearchField.text
property alias highlightedUser: userListView.currentIndex
spacing: 0 spacing: 0
Kirigami.AbstractApplicationHeader { Kirigami.AbstractApplicationHeader {
Layout.fillWidth: true Layout.fillWidth: true
topPadding: Kirigami.Units.smallSpacing / 2; topPadding: Kirigami.Units.smallSpacing / 2;
bottomPadding: Kirigami.Units.smallSpacing / 2; bottomPadding: Kirigami.Units.smallSpacing / 2;
rightPadding: Kirigami.Units.smallSpacing rightPadding: Kirigami.Units.largeSpacing
leftPadding: Kirigami.Units.smallSpacing leftPadding: Kirigami.Units.largeSpacing
RowLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
spacing: 0 spacing: Kirigami.Units.smallSpacing
Kirigami.Heading {
Layout.fillWidth: true
text: i18n("Room information")
level: 1
}
ToolButton { ToolButton {
id: inviteButton
Layout.alignment: Qt.AlignRight
icon.name: "list-add-user" icon.name: "list-add-user"
text: i18n("Invite") text: i18n("Invite user to room")
display: AbstractButton.IconOnly
onClicked: { onClicked: {
applicationWindow().pageStack.layers.push("qrc:/imports/NeoChat/Page/InviteUserPage.qml", {room: room}) applicationWindow().pageStack.layers.push("qrc:/imports/NeoChat/Page/InviteUserPage.qml", {room: room})
roomDrawer.close(); roomDrawer.close();
} }
}
Item {
// HACK otherwise rating item is not right aligned
Layout.fillWidth: true
}
ToolTip {
text: inviteButton.text
}
}
ToolButton { ToolButton {
id: favouriteButton
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
icon.name: room && room.isFavourite ? "rating" : "rating-unrated" icon.name: room && room.isFavourite ? "rating" : "rating-unrated"
checkable: true checkable: true
checked: room && room.isFavourite checked: room && room.isFavourite
text: room && room.isFavourite ? i18n("Remove room from favorites") : i18n("Make room favorite")
display: AbstractButton.IconOnly
onClicked: room.isFavourite ? room.removeTag("m.favourite") : room.addTag("m.favourite", 1.0) onClicked: room.isFavourite ? room.removeTag("m.favourite") : room.addTag("m.favourite", 1.0)
ToolTip { ToolTip {
text: room && room.isFavourite ? i18n("Remove room from favorites") : i18n("Make room favorite") text: favouriteButton.text
} }
} }
ToolButton { ToolButton {
id: settingsButton
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
icon.name: 'settings-configure' icon.name: 'settings-configure'
text: i18n("Room settings")
display: AbstractButton.IconOnly
onClicked: ApplicationWindow.window.pageStack.pushDialogLayer('qrc:/imports/NeoChat/RoomSettings/Categories.qml', {room: room}) onClicked: ApplicationWindow.window.pageStack.pushDialogLayer('qrc:/imports/NeoChat/RoomSettings/Categories.qml', {room: room})
ToolTip { ToolTip {
text: i18n("Room settings") text: settingsButton.text
} }
} }
} }
@@ -120,14 +149,11 @@ Kirigami.OverlayDrawer {
ColumnLayout { ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: Kirigami.Units.largeSpacing Layout.margins: Kirigami.Units.largeSpacing
Kirigami.Heading { spacing: Kirigami.Units.largeSpacing
text: i18n("Room information")
level: 3
}
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Layout.margins: Kirigami.Units.largeSpacing Layout.leftMargin: Kirigami.Units.smallSpacing
spacing: Kirigami.Units.largeSpacing spacing: Kirigami.Units.largeSpacing
Kirigami.Avatar { Kirigami.Avatar {
@@ -144,10 +170,9 @@ Kirigami.OverlayDrawer {
spacing: 0 spacing: 0
Kirigami.Heading { Kirigami.Heading {
Layout.maximumWidth: Kirigami.Units.gridUnit * 9
Layout.fillWidth: true Layout.fillWidth: true
level: 1 level: 1
font.bold: true type: Kirigami.Heading.Type.Primary
wrapMode: Label.Wrap wrapMode: Label.Wrap
text: room ? room.displayName : i18n("No name") text: room ? room.displayName : i18n("No name")
textFormat: Text.PlainText textFormat: Text.PlainText
@@ -158,6 +183,8 @@ Kirigami.OverlayDrawer {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
selectByMouse: true selectByMouse: true
color: Kirigami.Theme.textColor color: Kirigami.Theme.textColor
selectedTextColor: Kirigami.Theme.highlightedTextColor
selectionColor: Kirigami.Theme.highlightColor
readOnly: true readOnly: true
text: room && room.canonicalAlias ? room.canonicalAlias : i18n("No Canonical Alias") text: room && room.canonicalAlias ? room.canonicalAlias : i18n("No Canonical Alias")
} }
@@ -172,7 +199,9 @@ Kirigami.OverlayDrawer {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
selectByMouse: true selectByMouse: true
color: Kirigami.Theme.textColor color: Kirigami.Theme.textColor
onLinkActivated: Qt.openUrlExternally(link) selectedTextColor: Kirigami.Theme.highlightedTextColor
selectionColor: Kirigami.Theme.highlightColor
onLinkActivated: UrlHelper.openUrl(link)
readOnly: true readOnly: true
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
@@ -185,16 +214,23 @@ Kirigami.OverlayDrawer {
Kirigami.ListSectionHeader { Kirigami.ListSectionHeader {
label: i18n("Members") label: i18n("Members")
activeFocusOnTab: false activeFocusOnTab: false
Label { Label {
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
text: room ? i18np("%1 Member", "%1 Members", room.joinedCount) : i18n("No Member Count") text: room ? i18np("%1 Member", "%1 Members", room.joinedCount) : i18n("No Member Count")
} }
} }
Pane { Control {
padding: Kirigami.Units.smallSpacing Layout.fillWidth: true
implicitWidth: parent.width
z: 2 // Note need to set padding individually to guarantee it will always work
// see note - https://doc.qt.io/qt-6/qml-qtquick-controls2-control.html#padding-prop
topPadding: Kirigami.Units.smallSpacing
bottomPadding: Kirigami.Units.smallSpacing
rightPadding: Kirigami.Units.largeSpacing
leftPadding: Kirigami.Units.largeSpacing
background: Rectangle { background: Rectangle {
color: Kirigami.Theme.backgroundColor color: Kirigami.Theme.backgroundColor
Kirigami.Theme.inherit: false Kirigami.Theme.inherit: false
@@ -202,6 +238,7 @@ Kirigami.OverlayDrawer {
} }
contentItem: Kirigami.SearchField { contentItem: Kirigami.SearchField {
id: userListSearchField id: userListSearchField
onAccepted: sortedMessageEventModel.filterString = text; onAccepted: sortedMessageEventModel.filterString = text;
} }
} }
@@ -210,11 +247,12 @@ Kirigami.OverlayDrawer {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ListView { ListView {
id: userListView id: userListView
clip: true clip: true
headerPositioning: ListView.OverlayHeader
boundsBehavior: Flickable.DragOverBounds
activeFocusOnTab: true activeFocusOnTab: true
model: KSortFilterProxyModel { model: KSortFilterProxyModel {
@@ -229,58 +267,50 @@ Kirigami.OverlayDrawer {
filterCaseSensitivity: Qt.CaseInsensitive filterCaseSensitivity: Qt.CaseInsensitive
} }
delegate: Kirigami.AbstractListItem { delegate: Kirigami.BasicListItem {
width: userListView.width id: userListItem
implicitHeight: Kirigami.Units.gridUnit * 2 implicitHeight: Kirigami.Units.gridUnit * 2
z: 1 leftPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
contentItem: RowLayout { label: name
Kirigami.Avatar {
Layout.preferredWidth: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
Layout.preferredHeight: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
visible: Config.showAvatarInRoomDrawer
sourceSize.height: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
sourceSize.width: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
source: avatar ? ("image://mxc/" + avatar) : ""
name: name
}
Label { onClicked: {
Layout.fillWidth: true const popup = userDetailDialog.createObject(ApplicationWindow.overlay, {room: room, user: user, displayName: name, avatarMediaId: avatar})
popup.closed.connect(function() {
text: name userListItem.highlighted = false
textFormat: Text.PlainText })
elide: Text.ElideRight popup.open()
wrapMode: Text.NoWrap
}
Label {
visible: perm != UserType.Member
text: {
if (perm == UserType.Owner) {
return i18n("Owner")
}
if (perm == UserType.Admin) {
return i18n("Admin")
}
if (perm == UserType.Moderator) {
return i18n("Mod")
}
if (perm == UserType.Muted) {
return i18n("Muted")
}
return ""
}
color: Kirigami.Theme.disabledTextColor
font.pixelSize: 12
textFormat: Text.PlainText
wrapMode: Text.NoWrap
}
} }
action: Kirigami.Action { leading: Kirigami.Avatar {
onTriggered: userDetailDialog.createObject(ApplicationWindow.overlay, {"room": room, "user": user, "displayName": name, "avatarMediaId": avatar}).open() implicitWidth: height
sourceSize.height: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
sourceSize.width: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
source: avatar ? ("image://mxc/" + avatar) : ""
name: name
}
trailing: Label {
visible: perm != UserType.Member
text: {
switch (perm) {
case UserType.Owner:
return i18n("Owner");
case UserType.Admin:
return i18n("Admin");
case UserType.Moderator:
return i18n("Mod");
case UserType.Muted:
return i18n("Muted");
default:
return "";
}
}
color: Kirigami.Theme.disabledTextColor
textFormat: Text.PlainText
wrapMode: Text.NoWrap
} }
} }
} }
@@ -289,6 +319,10 @@ Kirigami.OverlayDrawer {
} }
onRoomChanged: { onRoomChanged: {
if (loader.active) {
loader.item.userSearchText = ""
loader.item.highlightedUser = -1
}
if (room == null) { if (room == null) {
close() close()
} }

View File

@@ -3,12 +3,12 @@
import QtQuick 2.15 import QtQuick 2.15
import org.kde.kirigami 2.18 as Kirigami import org.kde.kirigami 2.18 as Kirigami
import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
Kirigami.CategorizedSettings { Kirigami.CategorizedSettings {
id: root id: root
property var room property var room
objectName: "settingsPage" objectName: "settingsPage"
actions: [ actions: [
Kirigami.SettingAction { Kirigami.SettingAction {
@@ -30,6 +30,16 @@ Kirigami.CategorizedSettings {
room: root.room room: root.room
} }
} }
},
Kirigami.SettingAction {
text: i18n("Notifications")
icon.name: "notifications"
page: Qt.resolvedUrl("PushNotification.qml")
initialProperties: {
return {
room: root.room
}
}
} }
] ]
} }

View File

@@ -21,7 +21,7 @@ Kirigami.ScrollablePage {
readonly property bool canChangeTopic: room.canSendState("m.room.topic") readonly property bool canChangeTopic: room.canSendState("m.room.topic")
readonly property bool canChangeCanonicalAlias: room.canSendState("m.room.canonical_alias") readonly property bool canChangeCanonicalAlias: room.canSendState("m.room.canonical_alias")
title: i18n('General') title: i18n("General")
ColumnLayout { ColumnLayout {
Kirigami.FormLayout { Kirigami.FormLayout {

View File

@@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
Kirigami.ScrollablePage {
property var room
title: i18nc('@title:window', 'Notifications')
ColumnLayout {
Kirigami.FormLayout {
Layout.fillWidth: true
QQC2.RadioButton {
text: i18n("Follow global setting")
Kirigami.FormData.label: i18n("Room notifications setting:")
checked: room.pushNotificationState === PushNotificationState.Default
enabled: room.pushNotificationState != PushNotificationState.Unknown
onToggled: {
room.pushNotificationState = PushNotificationState.Default
}
}
QQC2.RadioButton {
text: i18nc("As in 'notify for all messages'","All")
checked: room.pushNotificationState === PushNotificationState.All
enabled: room.pushNotificationState != PushNotificationState.Unknown
onToggled: {
room.pushNotificationState = PushNotificationState.All
}
}
QQC2.RadioButton {
text: i18nc("As in 'notify when the user is mentioned or the message contains a set keyword'","@Mentions and Keywords")
checked: room.pushNotificationState === PushNotificationState.MentionKeyword
enabled: room.pushNotificationState != PushNotificationState.Unknown
onToggled: {
room.pushNotificationState = PushNotificationState.MentionKeyword
}
}
QQC2.RadioButton {
text: i18nc("As in 'do not notify for any messages'","Off")
checked: room.pushNotificationState === PushNotificationState.Mute
enabled: room.pushNotificationState != PushNotificationState.Unknown
onToggled: {
room.pushNotificationState = PushNotificationState.Mute
}
}
}
}
}

View File

@@ -16,13 +16,13 @@ Kirigami.ScrollablePage {
property var room property var room
title: i18n('Security') title: i18n("Security")
ColumnLayout { ColumnLayout {
Kirigami.FormLayout { Kirigami.FormLayout {
Layout.fillWidth: true Layout.fillWidth: true
CheckBox { RadioButton {
text: i18nc("@option:check", "Private (invite only)") text: i18nc("@option:check", "Private (invite only)")
Kirigami.FormData.label: i18nc("@option:check", "Access:") Kirigami.FormData.label: i18nc("@option:check", "Access:")
checked: room.joinRule === "invite" checked: room.joinRule === "invite"
@@ -32,7 +32,7 @@ Kirigami.ScrollablePage {
text: i18n("Only invited people can join.") text: i18n("Only invited people can join.")
font: Kirigami.Theme.smallFont font: Kirigami.Theme.smallFont
} }
CheckBox { RadioButton {
text: i18nc("@option:check", "Space members") text: i18nc("@option:check", "Space members")
checked: room.joinRule === "restricted" checked: room.joinRule === "restricted"
enabled: false enabled: false
@@ -41,7 +41,7 @@ Kirigami.ScrollablePage {
text: i18n("Anyone in a space can find and join.") text: i18n("Anyone in a space can find and join.")
font: Kirigami.Theme.smallFont font: Kirigami.Theme.smallFont
} }
CheckBox { RadioButton {
text: i18nc("@option:check", "Public") text: i18nc("@option:check", "Public")
checked: room.joinRule === "public" checked: room.joinRule === "public"
enabled: false enabled: false

View File

@@ -7,6 +7,6 @@ import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0 import org.kde.neochat 1.0
Kirigami.AboutPage { Kirigami.AboutPage {
title: i18nc('@title:window', 'About NeoChat') title: i18nc("@title:window", "About NeoChat")
aboutData: Controller.aboutData aboutData: Controller.aboutData
} }

View File

@@ -0,0 +1,130 @@
// SPDX-FileCopyrightText: 2020 Tobias Fella <fella@posteo.de>
// SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.15
import Qt.labs.platform 1.1
import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0
import NeoChat.Dialog 1.0
Kirigami.ScrollablePage {
id: root
title: i18n("Edit Account")
property var connection
ColumnLayout {
Kirigami.FormLayout {
RowLayout {
Kirigami.Avatar {
id: avatar
source: root.connection && root.connection.localUser.avatarMediaId ? ("image://mxc/" + root.connection.localUser.avatarMediaId) : ""
MouseArea {
id: mouseArea
anchors.fill: parent
property var fileDialog: null;
onClicked: {
if (fileDialog != null) {
return;
}
fileDialog = openFileDialog.createObject(Controls.ApplicationWindow.Overlay)
fileDialog.chosen.connect(function(receivedSource) {
mouseArea.fileDialog = null;
if (!receivedSource) {
return;
}
parent.source = receivedSource;
});
fileDialog.onRejected.connect(function() {
mouseArea.fileDialog = null;
});
fileDialog.open();
}
}
}
Controls.Button {
visible: avatar.source.toString().length !== 0
icon.name: "edit-clear"
onClicked: avatar.source = ""
}
Kirigami.FormData.label: i18n("Avatar:")
}
Controls.TextField {
id: name
text: root.connection ? root.connection.localUser.displayName : ""
Kirigami.FormData.label: i18n("Name:")
}
Controls.TextField {
id: accountLabel
text: root.connection ? root.connection.localUser.accountLabel : ""
Kirigami.FormData.label: i18n("Label:")
}
Controls.TextField {
id: currentPassword
Kirigami.FormData.label: i18n("Current Password:")
enabled: roto.connection !== undefined && root.connection.canChangePassword !== false
echoMode: TextInput.Password
}
Controls.TextField {
id: newPassword
Kirigami.FormData.label: i18n("New Password:")
enabled: root.connection !== undefined && root.connection.canChangePassword !== false
echoMode: TextInput.Password
}
Controls.TextField {
id: confirmPassword
Kirigami.FormData.label: i18n("Confirm new Password:")
enabled: root.connection !== undefined && root.connection.canChangePassword !== false
echoMode: TextInput.Password
}
}
}
footer: RowLayout {
Item {
Layout.fillWidth: true
}
Controls.Button {
text: i18n("Save")
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.topMargin: Kirigami.Units.smallSpacing
onClicked: {
if (!Controller.setAvatar(root.connection, avatar.source)) {
showPassiveNotification("The Avatar could not be set");
}
if (root.connection.localUser.displayName !== name.text) {
root.connection.localUser.rename(name.text);
}
if (root.connection.localUser.accountLabel !== accountLabel.text) {
root.connection.localUser.setAccountLabel(accountLabel.text);
}
if(currentPassword.text !== "" && newPassword.text !== "" && confirmPassword.text !== "") {
if(newPassword.text === confirmPassword.text) {
Controller.changePassword(root.connection, currentPassword.text, newPassword.text);
} else {
showPassiveNotification(i18n("Passwords do not match"));
return;
}
}
root.closeDialog();
}
}
Controls.Button {
text: i18n("Cancel")
Layout.rightMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.smallSpacing
Layout.topMargin: Kirigami.Units.smallSpacing
onClicked: root.closeDialog();
}
}
}

View File

@@ -23,42 +23,51 @@ Kirigami.ScrollablePage {
ListView { ListView {
model: AccountRegistry model: AccountRegistry
delegate: Kirigami.SwipeListItem { anchors.fill: parent
leftPadding: 0 delegate: Kirigami.BasicListItem {
rightPadding: 0 text: model.connection.localUser.displayName
Kirigami.BasicListItem { labelItem.textFormat: Text.PlainText
anchors.top: parent.top subtitle: model.connection.localUserId
anchors.bottom: parent.bottom icon: model.connection.localUser.avatarMediaId ? ("image://mxc/" + model.connection.localUser.avatarMediaId) : "im-user"
text: model.connection.localUser.displayName onClicked: {
labelItem.textFormat: Text.PlainText Controller.activeConnection = model.connection;
subtitle: model.connection.localUserId pageStack.layers.pop();
icon: model.connection.localUser.avatarMediaId ? ("image://mxc/" + model.connection.localUser.avatarMediaId) : "im-user" }
onClicked: { trailing: RowLayout {
Controller.activeConnection = model.connection Controls.ToolButton {
pageStack.layers.pop() display: Controls.AbstractButton.IconOnly
Controls.ToolTip {
text: parent.action.text
}
action: Kirigami.Action {
text: i18n("Edit this account")
iconName: "document-edit"
onTriggered: pageSettingStack.pushDialogLayer(Qt.resolvedUrl('./AccountEditorPage.qml'), {
connection: model.connection
}, {
title: i18n("Account editor")
});
}
}
Controls.ToolButton {
display: Controls.AbstractButton.IconOnly
Controls.ToolTip {
text: parent.action.text
}
action: Kirigami.Action {
text: i18n("Logout")
iconName: "im-kick-user"
onTriggered: {
Controller.logout(model.connection, true);
if (Controller.accountCount === 1) {
pageStack.layers.pop();
}
}
}
} }
} }
actions: [
Kirigami.Action {
text: i18n("Edit this account")
iconName: "document-edit"
onTriggered: {
userEditSheet.connection = model.connection
userEditSheet.open()
}
},
Kirigami.Action {
text: i18n("Logout")
iconName: "im-kick-user"
onTriggered: {
Controller.logout(model.connection, true)
if(Controller.accountCount === 1)
pageStack.layers.pop()
}
}
]
} }
} }
@@ -76,6 +85,7 @@ Kirigami.ScrollablePage {
} }
} }
} }
Connections { Connections {
target: Controller target: Controller
function onConnectionAdded() { function onConnectionAdded() {
@@ -83,134 +93,21 @@ Kirigami.ScrollablePage {
pageStack.layers.pop() pageStack.layers.pop()
} }
function onPasswordStatus(status) { function onPasswordStatus(status) {
if(status == Controller.Success) if (status === Controller.Success) {
showPassiveNotification(i18n("Password changed successfully")) showPassiveNotification(i18n("Password changed successfully"));
else if(status == Controller.Wrong) } else if (status === Controller.Wrong) {
showPassiveNotification(i18n("Wrong password entered")) showPassiveNotification(i18n("Wrong password entered"));
else } else {
showPassiveNotification(i18n("Unknown problem while trying to change password")) showPassiveNotification(i18n("Unknown problem while trying to change password"));
}
} }
} }
Component { property Component openFileDialog: Component {
id: openFileDialog id: openFileDialog
OpenFileDialog { OpenFileDialog {
folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation) folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
} }
} }
Kirigami.OverlaySheet {
id: userEditSheet
property var connection
title: i18n("Edit Account")
Kirigami.FormLayout {
RowLayout {
Kirigami.Avatar {
id: avatar
source: userEditSheet.connection && userEditSheet.connection.localUser.avatarMediaId ? ("image://mxc/" + userEditSheet.connection.localUser.avatarMediaId) : ""
MouseArea {
id: mouseArea
anchors.fill: parent
property var fileDialog: null;
onClicked: {
if (fileDialog != null) {
return;
}
fileDialog = openFileDialog.createObject(Controls.ApplicationWindow.Overlay)
fileDialog.chosen.connect(function(receivedSource) {
mouseArea.fileDialog = null;
if (!receivedSource) {
return;
}
parent.source = receivedSource;
});
fileDialog.onRejected.connect(function() {
mouseArea.fileDialog = null;
});
fileDialog.open();
}
}
}
Controls.Button {
visible: avatar.source.toString().length !== 0
icon.name: "edit-clear"
onClicked: avatar.source = ""
}
Kirigami.FormData.label: i18n("Avatar:")
}
Controls.TextField {
id: name
text: userEditSheet.connection ? userEditSheet.connection.localUser.displayName : ""
Kirigami.FormData.label: i18n("Name:")
}
Controls.TextField {
id: accountLabel
text: userEditSheet.connection ? userEditSheet.connection.localUser.accountLabel : ""
Kirigami.FormData.label: i18n("Label:")
}
Controls.TextField {
id: currentPassword
Kirigami.FormData.label: i18n("Current Password:")
enabled: userEditSheet.connection !== undefined && userEditSheet.connection.canChangePassword !== false
echoMode: TextInput.Password
}
Controls.TextField {
id: newPassword
Kirigami.FormData.label: i18n("New Password:")
enabled: userEditSheet.connection !== undefined && userEditSheet.connection.canChangePassword !== false
echoMode: TextInput.Password
}
Controls.TextField {
id: confirmPassword
Kirigami.FormData.label: i18n("Confirm new Password:")
enabled: userEditSheet.connection !== undefined && userEditSheet.connection.canChangePassword !== false
echoMode: TextInput.Password
}
RowLayout {
Controls.Button {
text: i18n("Save")
onClicked: {
if(!Controller.setAvatar(userEditSheet.connection, avatar.source))
showPassiveNotification("The Avatar could not be set")
if(userEditSheet.connection.localUser.displayName !== name.text)
userEditSheet.connection.localUser.rename(name.text)
if(userEditSheet.connection.localUser.accountLabel !== accountLabel.text)
userEditSheet.connection.localUser.setAccountLabel(accountLabel.text)
if(currentPassword.text !== "" && newPassword.text !== "" && confirmPassword.text !== "") {
if(newPassword.text === confirmPassword.text) {
Controller.changePassword(userEditSheet.connection, currentPassword.text, newPassword.text)
} else {
showPassiveNotification(i18n("Passwords do not match"))
return
}
}
userEditSheet.close()
currentPassword.text = ""
newPassword.text = ""
confirmPassword.text = ""
}
}
Controls.Button {
text: i18n("Cancel")
onClicked: {
userEditSheet.close()
avatar.source = userEditSheet.connection.localUser.avatarMediaId ? ("image://mxc/" + userEditSheet.connection.localUser.avatarMediaId) : ""
currentPassword.text = ""
newPassword.text = ""
confirmPassword.text = ""
}
}
}
}
}
} }

View File

@@ -12,7 +12,7 @@ import org.kde.neochat 1.0
import NeoChat.Settings 1.0 import NeoChat.Settings 1.0
Kirigami.ScrollablePage { Kirigami.ScrollablePage {
title: i18nc('@title:window', 'Appearance') title: i18nc("@title:window", "Appearance")
ColumnLayout { ColumnLayout {
RowLayout { RowLayout {
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
@@ -177,7 +177,7 @@ Kirigami.ScrollablePage {
Kirigami.FormLayout { Kirigami.FormLayout {
Layout.maximumWidth: parent.width Layout.maximumWidth: parent.width
QQC2.CheckBox { QQC2.CheckBox {
Kirigami.FormData.label: "Show Avatar:" Kirigami.FormData.label: i18n("Show Avatar:")
text: i18n("In Chat") text: i18n("In Chat")
checked: Config.showAvatarInTimeline checked: Config.showAvatarInTimeline
onToggled: { onToggled: {
@@ -237,6 +237,7 @@ Kirigami.ScrollablePage {
HoverHandler { id: sliderHover } HoverHandler { id: sliderHover }
QQC2.ToolTip.visible: sliderHover.hovered && !enabled QQC2.ToolTip.visible: sliderHover.hovered && !enabled
QQC2.ToolTip.text: i18n("Only enabled if the transparent chat page is enabled.") QQC2.ToolTip.text: i18n("Only enabled if the transparent chat page is enabled.")
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
} }
QQC2.Label { QQC2.Label {
text: Math.round(Config.transparency * 100) + "%" text: Math.round(Config.transparency * 100) + "%"
@@ -245,7 +246,7 @@ Kirigami.ScrollablePage {
QQC2.CheckBox { QQC2.CheckBox {
text: i18n("Show your messages on the right") text: i18n("Show your messages on the right")
checked: Config.showLocalMessagesOnRight checked: Config.showLocalMessagesOnRight
enabled: !Config.isShowLocalMessagesOnRightImmutable enabled: !Config.isShowLocalMessagesOnRightImmutable && !Config.compactLayout
onToggled: { onToggled: {
Config.showLocalMessagesOnRight = checked Config.showLocalMessagesOnRight = checked
Config.save() Config.save()

View File

@@ -5,7 +5,7 @@ import QtQuick 2.15
import QtQuick.Controls 2.15 as Controls import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami import org.kde.kirigami 2.19 as Kirigami
import org.kde.neochat 1.0 import org.kde.neochat 1.0
@@ -17,45 +17,53 @@ Kirigami.ScrollablePage {
id: devices id: devices
} }
Kirigami.PlaceholderMessage { anchors.fill: parent
visible: parent.model.count === 0 // We can assume 0 means loading since there is at least one device
Kirigami.LoadingPlaceholder {
visible: parent.count === 0 // We can assume 0 means loading since there is at least one device
anchors.centerIn: parent anchors.centerIn: parent
text: i18n("Loading…")
Controls.BusyIndicator {
running: parent.visible
}
} }
delegate: Kirigami.SwipeListItem { delegate: Kirigami.BasicListItem {
leftPadding: 0 text: model.displayName
rightPadding: 0 subtitle: model.id
Kirigami.BasicListItem { icon: "network-connect"
anchors.top: parent.top trailing: RowLayout {
anchors.bottom: parent.bottom Controls.ToolButton {
display: Controls.AbstractButton.IconOnly
text: model.displayName action: Kirigami.Action {
subtitle: model.id text: i18n("Edit device name")
icon: "network-connect" iconName: "document-edit"
} onTriggered: {
actions: [ renameSheet.index = model.index
Kirigami.Action { renameSheet.name = model.displayName
text: i18n("Edit device name") renameSheet.open()
iconName: "document-edit" }
onTriggered: {
renameSheet.index = model.index
renameSheet.name = model.displayName
renameSheet.open()
}
},
Kirigami.Action {
text: i18n("Logout device")
iconName: "edit-delete-remove"
onTriggered: {
passwordSheet.index = index
passwordSheet.open()
} }
} }
] Controls.ToolButton {
display: Controls.AbstractButton.IconOnly
visible: Controller.encryptionSupported
action: Kirigami.Action {
text: i18n("Verify device")
iconName: "security-low-symbolic"
onTriggered: {
devices.connection.startKeyVerificationSession(model.id)
}
}
}
Controls.ToolButton {
display: Controls.AbstractButton.IconOnly
action: Kirigami.Action {
text: i18n("Logout device")
iconName: "edit-delete-remove"
onTriggered: {
passwordSheet.index = index
passwordSheet.open()
}
}
}
}
} }
} }

View File

@@ -5,6 +5,8 @@ import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2 import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import Qt.labs.platform 1.1
import org.kde.kirigami 2.15 as Kirigami import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0 import org.kde.neochat 1.0
@@ -14,12 +16,13 @@ import NeoChat.Component 1.0 as Components
import NeoChat.Dialog 1.0 import NeoChat.Dialog 1.0
Kirigami.ScrollablePage { Kirigami.ScrollablePage {
title: i18nc('@title:window', 'Custom Emojis') title: i18nc("@title:window", "Custom Emojis")
ListView { ListView {
anchors.fill: parent
model: CustomEmojiModel { model: CustomEmojiModel {
id: emojiModel id: emojiModel
connection: Controller.activeConnection connection: Controller.activeConnection
} }
@@ -96,11 +99,10 @@ Kirigami.ScrollablePage {
this.fileDialog = openFileDialog.createObject(QQC2.Overlay.overlay) this.fileDialog = openFileDialog.createObject(QQC2.Overlay.overlay)
this.fileDialog.chosen.connect((url) => { this.fileDialog.chosen.connect((url) => {
emojiModel.addEmoji(emojiField.text, url) emojiModel.addEmoji(emojiCreator.name, url)
this.fileDialog = null this.fileDialog = null
}) })
this.fileDialog.onRejected.connect(() => { this.fileDialog.onRejected.connect(() => {
rej()
this.fileDialog = null this.fileDialog = null
}) })
this.fileDialog.open() this.fileDialog.open()

View File

@@ -11,9 +11,10 @@ import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0 import org.kde.neochat 1.0
Kirigami.ScrollablePage { Kirigami.ScrollablePage {
title: i18nc('@title:window', 'General') title: i18nc("@title:window", "General")
ColumnLayout { ColumnLayout {
Kirigami.FormLayout { Kirigami.FormLayout {
Layout.fillWidth: true
QQC2.CheckBox { QQC2.CheckBox {
Kirigami.FormData.label: i18n("General settings:") Kirigami.FormData.label: i18n("General settings:")
text: i18n("Close to system tray") text: i18n("Close to system tray")
@@ -45,6 +46,7 @@ Kirigami.ScrollablePage {
onToggled: { onToggled: {
Config.showNotifications = checked Config.showNotifications = checked
Config.save() Config.save()
NotificationsManager.globalNotificationsEnabled = checked
} }
} }
QQC2.CheckBox { QQC2.CheckBox {
@@ -96,19 +98,38 @@ Kirigami.ScrollablePage {
QQC2.CheckBox { QQC2.CheckBox {
id: quickEditCheckbox id: quickEditCheckbox
Layout.maximumWidth: parent.width Layout.maximumWidth: parent.width
contentItem: QQC2.Label { text: i18n("Use s/text/replacement syntax to edit your last message")
text: i18n("Use s/text/replacement syntax to edit your last message")
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
leftPadding: quickEditCheckbox.indicator.width + quickEditCheckbox.spacing
wrapMode: QQC2.Label.Wrap
}
checked: Config.allowQuickEdit checked: Config.allowQuickEdit
enabled: !Config.isAllowQuickEditImmutable enabled: !Config.isAllowQuickEditImmutable
onToggled: { onToggled: {
Config.allowQuickEdit = checked Config.allowQuickEdit = checked
Config.save() Config.save()
} }
// TODO KF5.97 remove this line
Component.onCompleted: this.contentItem.wrap = QQC2.Label.Wrap
}
QQC2.CheckBox {
text: i18n("Send Typing Notifications")
checked: Config.typingNotifications
enabled: !Config.isTypingNotificationsImmutable
onToggled: {
Config.typingNotifications = checked
Config.save()
}
}
QQC2.CheckBox {
text: i18n("Automatically hide/unhide the room information when resizing the window")
Layout.maximumWidth: parent.width
checked: Config.autoRoomInfoDrawer
enabled: !Config.isAutoRoomInfoDrawerImmutable
onToggled: {
Config.autoRoomInfoDrawer = checked
Config.save()
}
// TODO KF5.97 remove this line
Component.onCompleted: this.contentItem.wrap = QQC2.Label.Wrap
} }
} }
} }

View File

@@ -3,7 +3,6 @@
import QtQuick 2.15 import QtQuick 2.15
import org.kde.kirigami 2.18 as Kirigami import org.kde.kirigami 2.18 as Kirigami
import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
Kirigami.CategorizedSettings { Kirigami.CategorizedSettings {
@@ -25,7 +24,7 @@ Kirigami.CategorizedSettings {
page: Qt.resolvedUrl("AccountsPage.qml") page: Qt.resolvedUrl("AccountsPage.qml")
}, },
Kirigami.SettingAction { Kirigami.SettingAction {
text: i18n("Custom Emoji") text: i18n("Custom Emojis")
icon.name: "preferences-desktop-emoticons" icon.name: "preferences-desktop-emoticons"
page: Qt.resolvedUrl("Emoticons.qml") page: Qt.resolvedUrl("Emoticons.qml")
}, },

View File

@@ -47,7 +47,7 @@ Kirigami.Page {
dialog.close(); dialog.close();
} }
} }
title: i18nc('@window:title', 'Spellchecking') title: i18nc("@window:title", "Spellchecking")
QQC2.Dialog { QQC2.Dialog {
id: applyDialog id: applyDialog
@@ -190,6 +190,10 @@ Kirigami.Page {
Layout.fillHeight: true Layout.fillHeight: true
enabled: autodetectLanguageCheckbox.checked enabled: autodetectLanguageCheckbox.checked
Component.onCompleted: background.visible = wideMode Component.onCompleted: background.visible = wideMode
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
ListView { ListView {
clip: true clip: true
model: settings.dictionaryModel model: settings.dictionaryModel
@@ -254,6 +258,10 @@ Kirigami.Page {
} }
QQC2.ScrollView { QQC2.ScrollView {
anchors.fill: parent anchors.fill: parent
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
ListView { ListView {
model: settings.currentIgnoreList model: settings.currentIgnoreList
delegate: Kirigami.BasicListItem { delegate: Kirigami.BasicListItem {

View File

@@ -36,6 +36,7 @@
<name xml:lang="sl">NeoChat</name> <name xml:lang="sl">NeoChat</name>
<name xml:lang="sv">NeoChat</name> <name xml:lang="sv">NeoChat</name>
<name xml:lang="ta">நியோச்சாட்</name> <name xml:lang="ta">நியோச்சாட்</name>
<name xml:lang="tr">NeoChat</name>
<name xml:lang="uk">NeoChat</name> <name xml:lang="uk">NeoChat</name>
<name xml:lang="x-test">xxNeoChatxx</name> <name xml:lang="x-test">xxNeoChatxx</name>
<name xml:lang="zh-CN">NeoChat</name> <name xml:lang="zh-CN">NeoChat</name>
@@ -43,7 +44,7 @@
<summary xml:lang="ar">عميل لماتركس، ميفاق الاتصال اللامركزي</summary> <summary xml:lang="ar">عميل لماتركس، ميفاق الاتصال اللامركزي</summary>
<summary xml:lang="az">Matrix üçün müştəri, mərkəzləşməmiş kommunikasiya protokolu</summary> <summary xml:lang="az">Matrix üçün müştəri, mərkəzləşməmiş kommunikasiya protokolu</summary>
<summary xml:lang="ca">Un client per al Matrix, el protocol de comunicacions descentralitzat</summary> <summary xml:lang="ca">Un client per al Matrix, el protocol de comunicacions descentralitzat</summary>
<summary xml:lang="ca-valencia">Un client per al Matrix, el protocol de comunicacions descentralitzat</summary> <summary xml:lang="ca-valencia">Un client per a Matrix, el protocol de comunicacions descentralitzat</summary>
<summary xml:lang="cs">Klient pro decentralizovaný komunikační protokol matrix</summary> <summary xml:lang="cs">Klient pro decentralizovaný komunikační protokol matrix</summary>
<summary xml:lang="de">Ein Programm für Matrix, das dezentrale Kommunikationsprotokoll</summary> <summary xml:lang="de">Ein Programm für Matrix, das dezentrale Kommunikationsprotokoll</summary>
<summary xml:lang="en-GB">A client for matrix, the decentralised communication protocol</summary> <summary xml:lang="en-GB">A client for matrix, the decentralised communication protocol</summary>
@@ -65,6 +66,7 @@
<summary xml:lang="sk">Klient pre matrix, decentralizovaný komunikačný protokol</summary> <summary xml:lang="sk">Klient pre matrix, decentralizovaný komunikačný protokol</summary>
<summary xml:lang="sl">Odjemalec za matrix, decentralizirani komunikacijski protokol</summary> <summary xml:lang="sl">Odjemalec za matrix, decentralizirani komunikacijski protokol</summary>
<summary xml:lang="sv">En klient för Matrix, det decentraliserade kommunikationsprotokollet</summary> <summary xml:lang="sv">En klient för Matrix, det decentraliserade kommunikationsprotokollet</summary>
<summary xml:lang="tr">Merkezi olmayan iletişim protokolü Matrix için bir istemci</summary>
<summary xml:lang="uk">Клієнт matrix, децентралізованого протоколу обміну даними</summary> <summary xml:lang="uk">Клієнт matrix, децентралізованого протоколу обміну даними</summary>
<summary xml:lang="x-test">xxA client for matrix, the decentralized communication protocolxx</summary> <summary xml:lang="x-test">xxA client for matrix, the decentralized communication protocolxx</summary>
<summary xml:lang="zh-CN">分布式通讯协议 Matrix 的客户端</summary> <summary xml:lang="zh-CN">分布式通讯协议 Matrix 的客户端</summary>
@@ -73,7 +75,7 @@
<p xml:lang="ar">نيوتشات هو عميل ماتركس Matrix. يتيح لك إرسال رسائل نصية ومقاطع فيديو وملفات صوتية إلى عائلتك وزملائك وأصدقائك باستخدام بروتوكول ماتركس</p> <p xml:lang="ar">نيوتشات هو عميل ماتركس Matrix. يتيح لك إرسال رسائل نصية ومقاطع فيديو وملفات صوتية إلى عائلتك وزملائك وأصدقائك باستخدام بروتوكول ماتركس</p>
<p xml:lang="az">NeoChat Mtrix müştərisidir. O, Matrix protokolundan istifadə edərək, ailənizə, dostlarınıza, iş yoldaşlarınıza mətn, səsli və görüntülü ismarıclar göndərməyə imkan verir.</p> <p xml:lang="az">NeoChat Mtrix müştərisidir. O, Matrix protokolundan istifadə edərək, ailənizə, dostlarınıza, iş yoldaşlarınıza mətn, səsli və görüntülü ismarıclar göndərməyə imkan verir.</p>
<p xml:lang="ca">El NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics usant el protocol Matrix.</p> <p xml:lang="ca">El NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics usant el protocol Matrix.</p>
<p xml:lang="ca-valencia">NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics usant el protocol Matrix.</p> <p xml:lang="ca-valencia">NeoChat és un client de Matrix. Permet enviar missatges de text, fitxers de vídeo i d'àudio a la família, col·legues i amics utilitzant el protocol Matrix.</p>
<p xml:lang="de">NeoChat ist ein Matrix-Client. Er ermöglicht Ihnen das Senden von Textnachrichten, Videos und Audiodateien an Ihre Familie, Kollegen und Freunde unter Verwendung des Matrix-Protokolls.</p> <p xml:lang="de">NeoChat ist ein Matrix-Client. Er ermöglicht Ihnen das Senden von Textnachrichten, Videos und Audiodateien an Ihre Familie, Kollegen und Freunde unter Verwendung des Matrix-Protokolls.</p>
<p xml:lang="en-GB">NeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.</p> <p xml:lang="en-GB">NeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.</p>
<p xml:lang="es">NeoChat es un cliente para Matrix. Le permite enviar mensajes de texto, vídeos y archivos de sonido a su familia, compañeros de trabajo y amigos usando el protocolo Matrix.</p> <p xml:lang="es">NeoChat es un cliente para Matrix. Le permite enviar mensajes de texto, vídeos y archivos de sonido a su familia, compañeros de trabajo y amigos usando el protocolo Matrix.</p>
@@ -92,6 +94,7 @@
<p xml:lang="sk">NeoChat je Matrix klient. Umožňuje vám posielať textové správy, videá a zvukové súbory rodine, kolegom a priateľom pomocou protokolu Matrix.</p> <p xml:lang="sk">NeoChat je Matrix klient. Umožňuje vám posielať textové správy, videá a zvukové súbory rodine, kolegom a priateľom pomocou protokolu Matrix.</p>
<p xml:lang="sl">NeoChat je odjemalec Matrixa. Dovoljuje vam pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, kolegom in prijateljem z uporabo protokola Matrix.</p> <p xml:lang="sl">NeoChat je odjemalec Matrixa. Dovoljuje vam pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, kolegom in prijateljem z uporabo protokola Matrix.</p>
<p xml:lang="sv">NeoChat är en Matrix-klient. Den låter dig skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner med användning av Matrix-protokollet.</p> <p xml:lang="sv">NeoChat är en Matrix-klient. Den låter dig skicka textmeddelanden, videor och ljudfiler till din familj, kollegor och vänner med användning av Matrix-protokollet.</p>
<p xml:lang="tr">NeoChat, bir Matrix istemcisidir. Matrix protokolünü kullanarak ailenize, iş arkadaşlarınıza, arkadaşlarınıza ve başkalarına metin iletileri, video ve ses dosyaların göndermenize olanak verir.</p>
<p xml:lang="uk">NeoChat — клієнт мережі обміну повідомленнями Matrix. За допомогою цієї програми ви зможете надсилати текстові повідомлення, відео та звукові файли вашій родині, колегам та друзям за допомогою протоколу Matrix.</p> <p xml:lang="uk">NeoChat — клієнт мережі обміну повідомленнями Matrix. За допомогою цієї програми ви зможете надсилати текстові повідомлення, відео та звукові файли вашій родині, колегам та друзям за допомогою протоколу Matrix.</p>
<p xml:lang="x-test">xxNeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.xx</p> <p xml:lang="x-test">xxNeoChat is a Matrix client. It allows you to send text messages, videos and audio files to your family, colleagues and friends using the Matrix protocol.xx</p>
<p xml:lang="zh-CN">NeoChat 是一个 Matrix 客户端。 它允许您使用 Matrix 协议向您的家人、同事和朋友发送文本消息、视频和音频文件。</p> <p xml:lang="zh-CN">NeoChat 是一个 Matrix 客户端。 它允许您使用 Matrix 协议向您的家人、同事和朋友发送文本消息、视频和音频文件。</p>
@@ -118,6 +121,7 @@
<p xml:lang="sk">Matrix je decentralizovaný komunikačný protokol, ktorý používateľovi vracia kontrolu. V súčasnosti NeoChat implementuje veľkú časť protokolu s výnimkou šifrovaných chatov a videohovorov.</p> <p xml:lang="sk">Matrix je decentralizovaný komunikačný protokol, ktorý používateľovi vracia kontrolu. V súčasnosti NeoChat implementuje veľkú časť protokolu s výnimkou šifrovaných chatov a videohovorov.</p>
<p xml:lang="sl">Matrix je decentraliziran komunikacijski protokol, kjer ima uporabnik uporabnik kontrolo rabe. Trenutno ima NeoChat izveden velik del protokola z izjemo šifriranih klepetov in video klepetov.</p> <p xml:lang="sl">Matrix je decentraliziran komunikacijski protokol, kjer ima uporabnik uporabnik kontrolo rabe. Trenutno ima NeoChat izveden velik del protokola z izjemo šifriranih klepetov in video klepetov.</p>
<p xml:lang="sv">Matrix är ett decentraliserat kommunikationsprotokoll, som ger tillbaka kontrollen till användaren. För närvarande implementerar NeoChat en stor del av protokollet, med undantag för krypterad chatt och videochatt.</p> <p xml:lang="sv">Matrix är ett decentraliserat kommunikationsprotokoll, som ger tillbaka kontrollen till användaren. För närvarande implementerar NeoChat en stor del av protokollet, med undantag för krypterad chatt och videochatt.</p>
<p xml:lang="tr">Matrix; tam denetimi kullanıcıya bırakan, merkezi olmayan bir iletişim protokolüdür. Şu anda NeoChat, uçtan uca şifrelenmiş metin ve video sohbetleri dışında protokolün büyük bir bölümünü bünyesinde bulundurur.</p>
<p xml:lang="uk">Matrix — протокол децентралізованого спілкування, який передає контроль над даними користувачеві. У поточній версії NeoChat реалізовано більшу частину протоколу, окрім зашифрованого спілкування та відеоспілкування.</p> <p xml:lang="uk">Matrix — протокол децентралізованого спілкування, який передає контроль над даними користувачеві. У поточній версії NeoChat реалізовано більшу частину протоколу, окрім зашифрованого спілкування та відеоспілкування.</p>
<p xml:lang="x-test">xxMatrix is a decentralized communication protocol, putting the user back in control. Currently NeoChat implements large part of the protocol with the exception of encrypted chats and video chat.xx</p> <p xml:lang="x-test">xxMatrix is a decentralized communication protocol, putting the user back in control. Currently NeoChat implements large part of the protocol with the exception of encrypted chats and video chat.xx</p>
<p xml:lang="zh-CN">Matrix 是一个分布式通讯协议,使用户重新得到控制权。 目前NeoChat 实现了协议的大部分,除了加密聊天和视频聊天。</p> <p xml:lang="zh-CN">Matrix 是一个分布式通讯协议,使用户重新得到控制权。 目前NeoChat 实现了协议的大部分,除了加密聊天和视频聊天。</p>
@@ -125,7 +129,7 @@
<p xml:lang="ar">يعمل نيوتشات على كل من الأجهزة المحمولة وسطح المكتب مع توفير تجربة مستخدم متسقة.</p> <p xml:lang="ar">يعمل نيوتشات على كل من الأجهزة المحمولة وسطح المكتب مع توفير تجربة مستخدم متسقة.</p>
<p xml:lang="az">Vahid istifadəçi interfeysi ilə təmin olunan NeoChat, həm mobil telefonda həm də kompyuterlərdə işləyir.</p> <p xml:lang="az">Vahid istifadəçi interfeysi ilə təmin olunan NeoChat, həm mobil telefonda həm də kompyuterlərdə işləyir.</p>
<p xml:lang="ca">El NeoChat funciona en els mòbils i a l'escriptori, proporcionant una experiència d'usuari coherent.</p> <p xml:lang="ca">El NeoChat funciona en els mòbils i a l'escriptori, proporcionant una experiència d'usuari coherent.</p>
<p xml:lang="ca-valencia">NeoChat funciona en els mòbils i a l'escriptori, proporcionant una experiència d'usuari coherent.</p> <p xml:lang="ca-valencia">NeoChat funciona en els mòbils i en l'escriptori, proporcionant una experiència d'usuari coherent.</p>
<p xml:lang="de">NeoChat funktioniert sowohl auf dem Mobiltelefon als auch auf dem Arbeitsfläche und bietet ein einheitliches Benutzererlebnis. </p> <p xml:lang="de">NeoChat funktioniert sowohl auf dem Mobiltelefon als auch auf dem Arbeitsfläche und bietet ein einheitliches Benutzererlebnis. </p>
<p xml:lang="en-GB">NeoChat works both on mobile and desktop while providing a consistent user experience.</p> <p xml:lang="en-GB">NeoChat works both on mobile and desktop while providing a consistent user experience.</p>
<p xml:lang="es">NeoChat funciona en móviles y en el escritorio a la vez que proporciona una experiencia de usuario consistente.</p> <p xml:lang="es">NeoChat funciona en móviles y en el escritorio a la vez que proporciona una experiencia de usuario consistente.</p>
@@ -144,12 +148,13 @@
<p xml:lang="sk">NeoChat funguje na mobilných aj stolových počítačoch a poskytuje konzistentný používateľský zážitok.</p> <p xml:lang="sk">NeoChat funguje na mobilných aj stolových počítačoch a poskytuje konzistentný používateľský zážitok.</p>
<p xml:lang="sl">NeoChat deluje tako na mobilnih kot na namiznih platformah z zagotavljanjem konsistentne uporabniške izkušnje.</p> <p xml:lang="sl">NeoChat deluje tako na mobilnih kot na namiznih platformah z zagotavljanjem konsistentne uporabniške izkušnje.</p>
<p xml:lang="sv">NeoChat fungerar både på mobil och skrivbord och tillhandahåller en konsekvent användarupplevelse.</p> <p xml:lang="sv">NeoChat fungerar både på mobil och skrivbord och tillhandahåller en konsekvent användarupplevelse.</p>
<p xml:lang="tr">NeoChat, hem masaüstü hem de taşınabilir ortamlarda çalışarak tutarlı bir kullanıcı deneyimi sunar.</p>
<p xml:lang="uk">NeoChat працює на мобільних пристроях та звичайних комп'ютерах, маючи однорідний інтерфейс на усіх підтримуваних пристроях.</p> <p xml:lang="uk">NeoChat працює на мобільних пристроях та звичайних комп'ютерах, маючи однорідний інтерфейс на усіх підтримуваних пристроях.</p>
<p xml:lang="x-test">xxNeoChat works both on mobile and desktop while providing a consistent user experience.xx</p> <p xml:lang="x-test">xxNeoChat works both on mobile and desktop while providing a consistent user experience.xx</p>
<p xml:lang="zh-CN">NeoChat 在移动设备和桌面上均可用,并提供一致的用户体验。</p> <p xml:lang="zh-CN">NeoChat 在移动设备和桌面上均可用,并提供一致的用户体验。</p>
</description> </description>
<url type="homepage">https://apps.kde.org/neochat/</url> <url type="homepage">https://apps.kde.org/neochat/</url>
<url type="bugtracker">https://invent.kde.org/network/neochat/-/issues</url> <url type="bugtracker">https://bugs.kde.org/buglist.cgi?component=General&amp;product=NeoChat</url>
<categories> <categories>
<category>Network</category> <category>Network</category>
</categories> </categories>
@@ -179,12 +184,16 @@
<developer_name xml:lang="sk">KDE Komunita</developer_name> <developer_name xml:lang="sk">KDE Komunita</developer_name>
<developer_name xml:lang="sl">Skupnost KDE</developer_name> <developer_name xml:lang="sl">Skupnost KDE</developer_name>
<developer_name xml:lang="sv">KDE-gemenskapen</developer_name> <developer_name xml:lang="sv">KDE-gemenskapen</developer_name>
<developer_name xml:lang="tr">KDE Topluluğu</developer_name>
<developer_name xml:lang="uk">Спільнота KDE</developer_name> <developer_name xml:lang="uk">Спільнота KDE</developer_name>
<developer_name xml:lang="x-test">xxThe KDE Communityxx</developer_name> <developer_name xml:lang="x-test">xxThe KDE Communityxx</developer_name>
<developer_name xml:lang="zh-CN">KDE 社区</developer_name> <developer_name xml:lang="zh-CN">KDE 社区</developer_name>
<metadata_license>CC0-1.0</metadata_license> <metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0</project_license> <project_license>GPL-3.0</project_license>
<value key="KDE::matrix">#neochat:kde.org</value> <custom>
<value key="KDE::matrix">#neochat:kde.org</value>
</custom>
<launchable type="desktop-id">org.kde.neochat.desktop</launchable>
<screenshots> <screenshots>
<screenshot type="default"> <screenshot type="default">
<image>https://cdn.kde.org/screenshots/neochat/application-mobile.png</image> <image>https://cdn.kde.org/screenshots/neochat/application-mobile.png</image>
@@ -197,6 +206,26 @@
<content_attribute id="social-chat">intense</content_attribute> <content_attribute id="social-chat">intense</content_attribute>
</content_rating> </content_rating>
<releases> <releases>
<release version="22.09" date="2022-09-27">
<url>https://www.plasma-mobile.org/2022/09/27/plasma-mobile-gear-22-09/</url>
</release>
<release version="22.06" date="2022-06-24">
<url>https://www.plasma-mobile.org/2022/06/28/plasma-mobile-gear-22-06/</url>
<description>
<p>This release brings you various small bugfixes and improvements:</p>
<ul>
<li>Sending of typing notifications can now be disabled.</li>
<li>In the room list, the scrollbar will now disappear correctly when it is not needed.</li>
<li>On wayland, NeoChat will now raise correctly when clicking on a notification.</li>
<li>Several bugs have been fixed that would sometimes cause messages containing markdown and/or HTML elements to be sent incorrectly.</li>
<li>The quick switcher can now be controlled using the mouse.</li>
<li>There is now an option to disable automatic room sidebar opening when resizing the window.</li>
<li>Creation of custom emojis has been fixed.</li>
<li>Editing or replying to the last message using the keyboard shortcuts now works correctly.</li>
<li>When switching between rooms using the keyboard, the switching direction is now correct.</li>
</ul>
</description>
</release>
<release version="22.04" date="2022-04-26"> <release version="22.04" date="2022-04-26">
<url>https://www.plasma-mobile.org/2022/04/26/plasma-mobile-gear-22-04/</url> <url>https://www.plasma-mobile.org/2022/04/26/plasma-mobile-gear-22-04/</url>
<description> <description>

View File

@@ -31,6 +31,7 @@ Name[sk]=NeoChat
Name[sl]=NeoChat Name[sl]=NeoChat
Name[sv]=NeoChat Name[sv]=NeoChat
Name[ta]=நியோச்சாட் Name[ta]=நியோச்சாட்
Name[tr]=NeoChat
Name[uk]=NeoChat Name[uk]=NeoChat
Name[x-test]=xxNeoChatxx Name[x-test]=xxNeoChatxx
Name[zh_CN]=NeoChat Name[zh_CN]=NeoChat
@@ -63,6 +64,7 @@ GenericName[sk]=Matrix Client
GenericName[sl]=Odjemalec Matrix GenericName[sl]=Odjemalec Matrix
GenericName[sv]=Matrix-klient GenericName[sv]=Matrix-klient
GenericName[ta]=Matrix வாங்கி GenericName[ta]=Matrix வாங்கி
GenericName[tr]=Matrix İstemcisi
GenericName[uk]=Клієнт Matrix GenericName[uk]=Клієнт Matrix
GenericName[x-test]=xxMatrix Clientxx GenericName[x-test]=xxMatrix Clientxx
GenericName[zh_CN]=Matrix 客户端 GenericName[zh_CN]=Matrix 客户端
@@ -94,6 +96,7 @@ Comment[sk]=Klient protokolu Matrix
Comment[sl]=Odjemalec za protokol Matrix Comment[sl]=Odjemalec za protokol Matrix
Comment[sv]=Klient för protokollet Matrix Comment[sv]=Klient för protokollet Matrix
Comment[ta]=Matrix நெறிமுறைக்கான வாங்கி Comment[ta]=Matrix நெறிமுறைக்கான வாங்கி
Comment[tr]=Matrix protokolü için istemci
Comment[uk]=Клієнт протоколу Matrix Comment[uk]=Клієнт протоколу Matrix
Comment[x-test]=xxClient for the Matrix protocolxx Comment[x-test]=xxClient for the Matrix protocolxx
Comment[zh_CN]=为 Matrix 协议打造的客户端 Comment[zh_CN]=为 Matrix 协议打造的客户端

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2373
po/ru/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

View File

@@ -13,6 +13,7 @@ import NeoChat.Component 1.0
import NeoChat.Dialog 1.0 import NeoChat.Dialog 1.0
import NeoChat.Page 1.0 import NeoChat.Page 1.0
import NeoChat.Panel 1.0 import NeoChat.Panel 1.0
import NeoChat.Dialog.KeyVerification 1.0
Kirigami.ApplicationWindow { Kirigami.ApplicationWindow {
id: root id: root
@@ -51,7 +52,7 @@ Kirigami.ApplicationWindow {
Timer { Timer {
id: saveWindowGeometryTimer id: saveWindowGeometryTimer
interval: 1000 interval: 1000
onTriggered: Controller.saveWindowGeometry(root) onTriggered: Controller.saveWindowGeometry()
} }
Connections { Connections {
@@ -59,7 +60,7 @@ Kirigami.ApplicationWindow {
enabled: false // Disable on startup to avoid writing wrong values if the window is hidden enabled: false // Disable on startup to avoid writing wrong values if the window is hidden
target: root target: root
function onClosing() { Controller.saveWindowGeometry(root); } function onClosing() { Controller.saveWindowGeometry(); }
function onWidthChanged() { saveWindowGeometryTimer.restart(); } function onWidthChanged() { saveWindowGeometryTimer.restart(); }
function onHeightChanged() { saveWindowGeometryTimer.restart(); } function onHeightChanged() { saveWindowGeometryTimer.restart(); }
function onXChanged() { saveWindowGeometryTimer.restart(); } function onXChanged() { saveWindowGeometryTimer.restart(); }
@@ -106,7 +107,7 @@ Kirigami.ApplicationWindow {
function onOpenRoomInNewWindow(room) { function onOpenRoomInNewWindow(room) {
const secondayWindow = roomWindow.createObject(applicationWindow(), {currentRoom: room}); const secondayWindow = roomWindow.createObject(applicationWindow(), {currentRoom: room});
secondayWindow.width = root.width - pageStack.get(0).width; secondayWiroomWindowndow.width = root.width - pageStack.get(0).width;
secondayWindow.show(); secondayWindow.show();
} }
@@ -139,19 +140,16 @@ Kirigami.ApplicationWindow {
} }
} }
function showWindow() {
root.show()
root.raise()
root.requestActivate()
Controller.raiseWindow(root)
}
contextDrawer: RoomDrawer { contextDrawer: RoomDrawer {
id: contextDrawer id: contextDrawer
edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge
modal: !root.wideScreen || !enabled modal: !root.wideScreen || !enabled
onEnabledChanged: drawerOpen = enabled && !modal onEnabledChanged: drawerOpen = enabled && !modal
onModalChanged: drawerOpen = !modal onModalChanged: {
if (Config.autoRoomInfoDrawer) {
drawerOpen = !modal
dim = false
}
}
enabled: RoomManager.hasOpenRoom && pageStack.layers.depth < 2 && pageStack.depth < 3 enabled: RoomManager.hasOpenRoom && pageStack.layers.depth < 2 && pageStack.depth < 3
handleVisible: enabled && pageStack.layers.depth < 2 && pageStack.depth < 3 && (root.wideScreen || pageStack.currentIndex > 0) handleVisible: enabled && pageStack.layers.depth < 2 && pageStack.depth < 3 && (root.wideScreen || pageStack.currentIndex > 0)
} }
@@ -231,13 +229,13 @@ Kirigami.ApplicationWindow {
Kirigami.Action { Kirigami.Action {
text: i18n("Explore rooms") text: i18n("Explore rooms")
icon.name: "compass" icon.name: "compass"
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/JoinRoomPage.qml", {"connection": Controller.activeConnection}) onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/JoinRoomPage.qml", {connection: Controller.activeConnection})
enabled: pageStack.layers.currentItem.title !== i18n("Explore Rooms") && Controller.accountCount > 0 enabled: pageStack.layers.currentItem.title !== i18n("Explore Rooms") && Controller.accountCount > 0
}, },
Kirigami.Action { Kirigami.Action {
text: i18n("Start a Chat") text: i18n("Start a Chat")
icon.name: "irc-join-channel" icon.name: "irc-join-channel"
onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/StartChatPage.qml", {"connection": Controller.activeConnection}) onTriggered: pushReplaceLayer("qrc:/imports/NeoChat/Page/StartChatPage.qml", {connection: Controller.activeConnection})
enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0 enabled: pageStack.layers.currentItem.title !== i18n("Start a Chat") && Controller.accountCount > 0
}, },
Kirigami.Action { Kirigami.Action {
@@ -309,10 +307,10 @@ Kirigami.ApplicationWindow {
Connections { Connections {
target: root.roomPage target: root.roomPage
function onSwitchRoomUp() { function onSwitchRoomUp() {
roomList.goToNextRoom(); roomList.goToPreviousRoom();
} }
function onSwitchRoomDown() { function onSwitchRoomDown() {
roomList.goToPreviousRoom(); roomList.goToNextRoom();
} }
} }
} }
@@ -349,16 +347,6 @@ Kirigami.ApplicationWindow {
showPassiveNotification(i18n("%1: %2", error, detail)); showPassiveNotification(i18n("%1: %2", error, detail));
} }
function onShowWindow(token = null) {
root.showWindow()
if (token && KWindowSystem) {
KWindowSystem.setCurrentXdgActivationToken(basicNotification.xdgActivationToken)
KWindowSystem.activateWindow(root)
} else {
root.raise()
}
}
function onUserConsentRequired(url) { function onUserConsentRequired(url) {
consentSheet.url = url consentSheet.url = url
consentSheet.open() consentSheet.open()
@@ -380,11 +368,23 @@ Kirigami.ApplicationWindow {
} }
} }
Component {
id: keyVerificationDialogComponent
KeyVerificationDialog { }
}
Connections { Connections {
target: Controller.activeConnection target: Controller.activeConnection
function onDirectChatAvailable(directChat) { function onDirectChatAvailable(directChat) {
RoomManager.enterRoom(Controller.activeConnection.room(directChat.id)); RoomManager.enterRoom(Controller.activeConnection.room(directChat.id));
} }
function onNewKeyVerificationSession(session) {
applicationWindow().pageStack.pushDialogLayer(keyVerificationDialogComponent, {
session: session,
}, {
title: i18nc("@title:window", "Session Verification")
});
}
} }
Kirigami.OverlaySheet { Kirigami.OverlaySheet {
@@ -403,7 +403,7 @@ Kirigami.ApplicationWindow {
} }
footer: QQC2.Button { footer: QQC2.Button {
text: i18n("Open") text: i18n("Open")
onClicked: Qt.openUrlExternally(consentSheet.url) onClicked: UrlHelper.openUrl(consentSheet.url)
} }
} }

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