Compare commits

...

132 Commits

Author SHA1 Message Date
Tomaz Canabrava
7cc68d2658 Adapt to newer API 2024-04-19 10:42:03 +02:00
Tomaz Canabrava
f4b87caefe Adapt to new AUP 2024-04-19 10:18:39 +02:00
Tomaz Canabrava
07a9497f4c Fix bad commit 2024-04-19 10:18:31 +02:00
Tomaz Canabrava
35f1ace458 Adapt to newer API
TODO: There are some missing methods and I do not know what to do there
2024-04-19 10:04:13 +02:00
Tomaz Canabrava
11dd0ee151 Adapt to new Avatar API 2024-04-19 10:03:49 +02:00
Tomaz Canabrava
07e200c74f Adapt to breaking changes in libQuotient
localUser() -> localMember()
2024-04-19 09:45:11 +02:00
l10n daemon script
ee405fbff6 GIT_SILENT Sync po/docbooks with svn 2024-04-19 01:25:40 +00:00
James Graham
c87c6fbabb Add kitemmodels back to Permissions.qml 2024-04-18 20:48:24 +01:00
Tobias Fella
096b36b89b Try fixing test some more 2024-04-18 19:19:31 +02:00
Tobias Fella
c3db90d2e3 Make DrKonqi work with NeoChat 2024-04-18 18:49:12 +02:00
Tobias Fella
b7df10aa45 Try fixing appium test 2024-04-18 18:15:15 +02:00
l10n daemon script
fea5e02e7d GIT_SILENT Sync po/docbooks with svn 2024-04-18 01:18:34 +00:00
Tobias Fella
cb94261727 Fix compatibility with libQuotient dev branch 2024-04-17 13:01:32 +02:00
l10n daemon script
efac7e4860 GIT_SILENT Sync po/docbooks with svn 2024-04-17 01:25:31 +00:00
Tobias Fella
0f6fd3adee Remove outdated event registrations 2024-04-16 22:39:16 +02:00
James Graham
e5e0405401 Fix typo 2024-04-16 21:16:21 +01:00
Tobias Fella
bb35e9ce15 Add option to show all rooms in "uncategorized" tab 2024-04-16 20:49:17 +02:00
Tobias Fella
5881db4e55 Add missing import to global menu 2024-04-16 20:25:04 +02:00
Tobias Fella
89f7167b08 Remember previous downloads after restarts
Fixes #636
2024-04-16 17:55:26 +02:00
l10n daemon script
3b39fcff84 GIT_SILENT Sync po/docbooks with svn 2024-04-16 01:27:26 +00:00
Tobias Fella
0daf45a465 Fix opening account editor 2024-04-15 17:48:26 +02:00
Tobias Fella
c0d7b96e79 Mark ThreePIdModel as uncreatable 2024-04-15 17:15:01 +02:00
Laurent Montel
a247e40865 Add missing includes moc 2024-04-15 08:10:50 +02:00
Laurent Montel
c3e7a99bca Add missing #pragma once 2024-04-15 08:10:22 +02:00
l10n daemon script
9080d8be6a GIT_SILENT Sync po/docbooks with svn 2024-04-15 01:20:15 +00:00
James Graham
b7ee83f6b6 Add visualisation of the account's third party IDs in the account editor.
A category won't be shown if there are no relevant IDs (will add the ability to add new ones later).

Part of network/neochat#565

![image](/uploads/7da00b0b4acf90d145c09969ac2a91e1/image.png)
2024-04-14 16:37:34 +00:00
James Graham
1e24bde9a9 Add hover button for maximising a code block
Forgot to add it with the maximize mr
2024-04-14 16:32:12 +00:00
l10n daemon script
ba82df1152 GIT_SILENT Sync po/docbooks with svn 2024-04-14 01:20:38 +00:00
Tobias Fella
8980fe7838 Improve README 2024-04-13 21:59:16 +02:00
James Graham
ef34ed7c20 Fix Verification Window Sizing
Update the layouts in the device verifcation process to make sure that all possible window sizes can be handled

BUG: 485309
2024-04-13 18:51:06 +00:00
James Graham
17d60b79ca Fix showing the unread count for DM section when not selected 2024-04-13 15:15:27 +00:00
James Graham
ab0a32c339 Add a debug options page to devtools with the option to always allow verification for now
Closes network/neochat#646
2024-04-13 15:11:25 +00:00
James Graham
697778df8d Fix feature flag devtools page by adding necohat import as this is where config comes from now 2024-04-13 15:28:26 +01:00
Yuri Chornoivan
55caf84b94 Fix minor typos 2024-04-13 08:47:48 +03:00
l10n daemon script
335c012f1b GIT_SILENT Sync po/docbooks with svn 2024-04-13 01:25:05 +00:00
Tobias Fella
3c4c538de8 Use declarative type registration for remaining types 2024-04-12 22:17:39 +02:00
Tobias Fella
c344a3ee55 Devtools: Implement changing room state 2024-04-12 22:08:33 +02:00
l10n daemon script
d2695947ed GIT_SILENT Sync po/docbooks with svn 2024-04-12 01:22:49 +00:00
Tobias Fella
21beeef920 Load Main qml component from module 2024-04-11 21:01:55 +02:00
James Graham
a4630a53fa Create QML module for login 2024-04-11 18:56:08 +00:00
Tobias Fella
f5aef8d0c3 Use Qt.createComponent in non-weird way
Fixed #647
2024-04-11 18:16:19 +02:00
l10n daemon script
e044e66030 GIT_SILENT Sync po/docbooks with svn 2024-04-11 01:23:29 +00:00
James Graham
88bfacd386 Show QR code for room in drawer 2024-04-10 18:19:05 +00:00
James Graham
c61c73088f Add button to get a QR code for the local user to the account editor page 2024-04-10 17:19:11 +00:00
l10n daemon script
2887263f26 GIT_SILENT Sync po/docbooks with svn 2024-04-10 01:24:51 +00:00
James Graham
72b90bdf5c Fix gaps at the top and bottom of SpaceHomePage witht he wrong background colour.
Note: the bottom gap only happened after a room was loaded for the first time
2024-04-09 19:03:24 +00:00
James Graham
163b02f023 Add image info for stickers
Closes network/neochat#584
2024-04-09 18:54:59 +00:00
James Graham
1a96899336 Linkpreviewer Improvements
- Have LinkPreviewers stored in NeoChatConnection so that they don't have to be reloaded everytime the MessageContentModel is refreshed
- This means the link is never changed (it will be swiched for a new previewer with the new link)
- LinkPreviewers are stored by URL so they can be re-used by any event with the same URL

BUG: 484927  (because the offending code is ripped out)
2024-04-09 18:35:16 +00:00
l10n daemon script
554c086aba GIT_SILENT Sync po/docbooks with svn 2024-04-09 01:25:03 +00:00
Heiko Becker
1fad9bf7db GIT_SILENT Update Appstream for new release
(cherry picked from commit 86e8dc2e40)
2024-04-08 17:57:25 +02:00
l10n daemon script
22d922e451 GIT_SILENT Sync po/docbooks with svn 2024-04-08 01:23:31 +00:00
snow flurry
70bff21632 Render custom emoji icons in the completion pane 2024-04-07 19:40:02 +00:00
James Graham
f58c390a47 Re-add requirement for having devtools active for the show message source action
Re-add requirement for having devtools active for the show message source action

BUG: 485140
2024-04-07 08:40:14 +00:00
l10n daemon script
089a9abcb4 GIT_SILENT Sync po/docbooks with svn 2024-04-07 01:23:56 +00:00
Joshua Goins
bf1c76d0a6 Force the choose room dialog's search dialog to be focused
This makes it possible to open the share dialog and start typing to find
the room immediately.
2024-04-06 15:40:03 -04:00
Joshua Goins
879da627b1 Fix the share dialog not showing up
Seems to be a leftover from the refactor to use modules.
2024-04-06 15:39:36 -04:00
James Graham
9b93eb44d5 Show a verified icon for verified devices rather than a verify option 2024-04-06 14:19:38 +00:00
l10n daemon script
b30220eca9 GIT_SILENT Sync po/docbooks with svn 2024-04-06 01:23:47 +00:00
l10n daemon script
d270d4e5e1 GIT_SILENT Sync po/docbooks with svn 2024-04-05 01:21:52 +00:00
l10n daemon script
21da6cb0f4 GIT_SILENT Sync po/docbooks with svn 2024-04-04 01:24:44 +00:00
l10n daemon script
6ac75df935 GIT_SILENT Sync po/docbooks with svn 2024-04-03 01:24:31 +00:00
l10n daemon script
f29781349c SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-04-03 01:14:45 +00:00
Tobias Fella
bb776d5c2b Only ask for URL opening confirmation for QR codes
BUG: 484870
2024-04-02 19:47:07 +02:00
James Graham
6cfab9e3ea Tree Model 2 Electric Boogaloo
This draws heavily on what @carlschwan did in network/neochat!1579 but I found it easier to start again and grab the bits as I needed them plus some other copying from what I did in the Space tree model.

From my current limited testing this seems to work nicely try and break it.
2024-04-02 14:44:20 +00:00
l10n daemon script
6373186c15 GIT_SILENT Sync po/docbooks with svn 2024-04-02 01:18:19 +00:00
Nate Graham
e342de3bc1 Make ConfirmUrlDialog HIG-compliant
* "URL" is an acronym; make it all caps
* Use descriptive context-appropriate buttons instead of "Yes" and "No"
* Use KUIT markup for styling the link
2024-04-01 11:32:27 -06:00
Tobias Fella
4cd7b69ea5 Fix QML warning 2024-04-01 15:48:42 +02:00
Tobias Fella
988e8529da Remove leftover signal handler 2024-04-01 15:43:31 +02:00
James Graham
6a32d1e961 Create component for showing a maximize version of a code snippet 2024-04-01 11:20:10 +00:00
James Graham
0552c798fb Create qml module for devtools 2024-04-01 10:58:29 +00:00
l10n daemon script
a53ad41879 GIT_SILENT Sync po/docbooks with svn 2024-04-01 01:17:50 +00:00
Tobias Fella
92351edcd0 Fix location delegates
- Mark OSMLocationPlugina as singleton in cmake
- Use this plugin for the LocationChooser
2024-03-31 20:57:58 +02:00
James Graham
878eb48cb0 Shut qt up about models passed to QML
Shutup qt about below

```
SortFilterRoomListModel is neither a QObject, nor default- and copy-constructible, nor uncreatable. You should not use it as a QML type.
SortFilterRoomTreeModel is neither a QObject, nor default- and copy-constructible, nor uncreatable. You should not use it as a QML type.
SortFilterSpaceListModel is neither a QObject, nor default- and copy-constructible, nor uncreatable. You should not use it as a QML type.
```
2024-03-31 17:49:35 +00:00
James Graham
053ca6bed8 Move the various room models into RoomManager
Move the various room models into RoomManager. This means the same room models are always used and is a base from which further logic can be moved from QML to cpp.
2024-03-31 12:56:27 +00:00
Tobias Fella
78ae14ab2f Stay in DM tab when selecting a DM 2024-03-31 14:31:22 +02:00
l10n daemon script
5fdc2ad765 GIT_SILENT Sync po/docbooks with svn 2024-03-31 01:30:45 +00:00
Tobias Fella
b75dbe8d5c Rework roommanager for improved stability
Fixes #645

- Active space handling is moved from QML to RoomManager
- Active tab in SpaceDrawer (space / no space / DM) is unified in a single variable
- RoomList & RoomPage loading is simplified: We're always pushing a RoomPage now; if there is no room, a placeholder is shown
- SpaceHomePage is moved into RoomPage; This replaces the entire push/replace room/spacehome logic
- If the current room is a space, the space home is shown, otherwise the timeline
- The concept of "previous room" is removed entirely. If we're leaving the active room, the placeholder room page is shown
- When clicking on a space in the list, the space room list is switched and the space home page is shown

In short, these changes should (after some initial regressions) lead to a less crashy NeoChat :)
2024-03-31 00:22:23 +01:00
James Graham
eaf4663c84 RoomManger connection
RoomManger should just get it's connection from Controller, no need to involve QML
2024-03-30 19:48:34 +00:00
James Graham
64b8cd5bcc Space Search
Allow to refine searches to spaces only in the main exlore function.
Show which rooms are spaces in the search page.

Closes #577
2024-03-30 19:37:46 +00:00
James Graham
482d61ee47 Fix marking messages as read when the window is thin
Make sure that messages are not marked as read when going back to the roomlist after entering a room when neochat is thin and only showing a single page

Fixes #642
2024-03-30 19:32:19 +00:00
l10n daemon script
276dcce95e GIT_SILENT Sync po/docbooks with svn 2024-03-30 01:18:07 +00:00
Tobias Fella
217f9e2e02 Set OUTPUT_DIRECTORY for qml modules
This fixes some cmake warning and might make qmlls happier
2024-03-29 20:06:26 +01:00
Tobias Fella
f40a0a6f5f Remove unused property 2024-03-29 16:43:06 +01:00
Tobias Fella
9bd67acc2f Remove leftover signal 2024-03-29 16:05:07 +01:00
James Graham
e87da0feb0 Add pagination to space hierarchy cache
Add pagination to space hierarchy cache to ensure all rooms get cached.
2024-03-29 15:03:50 +00:00
Tobias Fella
2608d879fa Add "Leave room" button to sidebar
BUG: 484425
2024-03-29 13:53:59 +01:00
Tobias Fella
6ab61fd41f Fix opening the last room active room if it's not in a space
At the moment, the saved room was effectively always overridden by the first room in the list
2024-03-29 13:29:41 +01:00
Tobias Fella
30dd6297ee Make sure we're switching out of the space home page when leaving the currently opened space 2024-03-29 11:57:06 +01:00
Tobias Fella
ce02183f82 Fix plural handling in space member strings 2024-03-29 11:56:29 +01:00
Tobias Fella
7c74a6cbe1 Improve space management strings 2024-03-29 11:56:06 +01:00
Tobias Fella
e6a11b2ad8 Make various models more robust against deleted rooms 2024-03-29 11:55:14 +01:00
James Graham
158942d1b5 UserInfo compact
Make UserInfo work in compact mode. This includes showing the account switch popup in a dialog

BUG: 482261
2024-03-29 09:09:13 +00:00
l10n daemon script
aaa97ec029 GIT_SILENT Sync po/docbooks with svn 2024-03-29 01:30:58 +00:00
Tobias Fella
882ead5715 Remove external room window feature
At its best, this worked ok-ish, though it was always missing basic features.

It's also a massive memory leak and significantly complicates the codebase.
(Which is not yet cleaned up by this commit)

Currently, it is entirely broken and noone noticed or cared enough to report or fix that.

BUG: 455984
2024-03-28 22:05:50 +01:00
Tobias Fella
ab4519dedd Show custom delegate for in-room user verification
This is independent of the in-room verification actually working, but prevents a fallback from appearing
2024-03-28 22:03:58 +01:00
Tobias Fella
c3fd2428a2 Add QR code scanner 2024-03-28 22:02:55 +01:00
James Graham
fbb4b962fa Support selected text for replies in the right click menu
Support selected text for replies in the right click menu

BUG: 463885
2024-03-28 21:01:21 +00:00
Tobias Fella
9bf65de649 Use custom room drawer icons 2024-03-28 21:34:47 +01:00
Tobias Fella
75f069cb7d SpaceChildrenModel: Handle space being deleted 2024-03-28 16:46:38 +01:00
l10n daemon script
87d50125ab GIT_SILENT Sync po/docbooks with svn 2024-03-28 01:38:17 +00:00
l10n daemon script
dc2cf21cb8 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-03-28 01:18:33 +00:00
Tobias Fella
bae14ecd35 Simplify spacedrawer width code 2024-03-27 21:09:27 +01:00
Gary Wang
b48c1c3b80 Allow show sender detail from message context menu
When curtain user is spamming a lot of messages in a short amount
of time, mod need to scroll all the way up to the first spam message to
know who send those spam message, thus start banning them.

This patch add a context menu to open the sender detail dialog, it could
make banning the spam user and batch-deleting spam messages easier.
2024-03-27 19:39:29 +00:00
James Graham
0f9eb4beeb Fix logout current connection crash
Make sure that the neochat can handle switching connection when the current one is logged out. This is mostly about using QPointer to handle use after free issues due to room objects being deleted.
2024-03-27 15:25:24 +00:00
l10n daemon script
0ab8624d79 GIT_SILENT Sync po/docbooks with svn 2024-03-27 09:54:21 +00:00
l10n daemon script
e872c934c3 GIT_SILENT Sync po/docbooks with svn 2024-03-27 01:31:49 +00:00
James Graham
c3d5d18aae Use unique pointers for space child items 2024-03-26 13:41:03 +00:00
James Graham
ff5853a850 Create a QML module for settings 2024-03-26 13:23:43 +00:00
l10n daemon script
f772906324 GIT_SILENT Sync po/docbooks with svn 2024-03-26 01:31:34 +00:00
l10n daemon script
07eabb2dc1 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-03-26 01:13:52 +00:00
l10n daemon script
b3c88763a4 GIT_SILENT Sync po/docbooks with svn 2024-03-25 01:31:49 +00:00
Tobias Fella
f7081f8829 Always encrypt DMs when creating them 2024-03-24 11:36:21 +01:00
James Graham
ceef2167fd Don't Maximize Stickers
Make sure that sticker don't open the maximize component as they aren't in the media model

BUG: 482701
2024-03-24 10:01:41 +00:00
James Graham
1dcfd94328 Fix Message Components for Tags with Attributes
Don't assume that the close tag is the length of the start tag +1

BUG: 482331
2024-03-24 10:01:00 +00:00
l10n daemon script
a1aa0804e2 GIT_SILENT Sync po/docbooks with svn 2024-03-24 01:20:31 +00:00
l10n daemon script
77176478eb SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-03-24 01:14:27 +00:00
l10n daemon script
bf4ebfa7a8 GIT_SILENT made messages (after extraction) 2024-03-24 00:38:05 +00:00
Tobias Fella
6e7d622b41 Fix crash when visiting user
We're adding the "join" action so that rooms are joined.
libQuotient doesn't like it when we the action is join and the uri is for a user.

BUG: 483744
2024-03-23 20:46:56 +01:00
Tobias Fella
8398b7d24d Fix manual user search dialog 2024-03-23 20:45:15 +01:00
James Graham
aef9b7375a Fix Opening Maximized Media
Make sure the Image and Video Components can correctly get the index for opening the Maximize Media component.
2024-03-23 17:52:29 +00:00
James Graham
ba45318b56 Improved itinerary delegates
Steal the look of itinerary items from itinerary but simplified. Also includes new support for flights and restaurants

![image](/uploads/a574d2362edad52ecf91ce89a1849f27/image.png)
2024-03-23 09:33:51 +00:00
l10n daemon script
7d4f8780ad GIT_SILENT Sync po/docbooks with svn 2024-03-23 01:19:12 +00:00
l10n daemon script
b504c990f8 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-03-23 01:13:26 +00:00
Tobias Fella
e07b876677 Add UI for entering key backup passphrase 2024-03-22 22:04:07 +01:00
Tobias Fella
a0bfd34951 Fix removing a parent space when we're not joined 2024-03-21 21:25:26 +01:00
l10n daemon script
b173714bbe GIT_SILENT Sync po/docbooks with svn 2024-03-21 01:20:56 +00:00
l10n daemon script
db4021b601 SVN_SILENT made messages (.desktop file) - always resolve ours
In case of conflict in i18n, keep the version of the branch "ours"
To resolve a particular conflict, "git checkout --ours path/to/file.desktop"
2024-03-21 01:14:53 +00:00
l10n daemon script
2f46fd1d2c GIT_SILENT Sync po/docbooks with svn 2024-03-20 01:22:05 +00:00
Tobias Fella
1f85f848e2 Clean up button 2024-03-19 21:31:11 +01:00
Tobias Fella
d7e0954e86 Fix opening manual room dialog 2024-03-19 21:27:18 +01:00
James Graham
1671e05d12 More file previews
This adds previews for downloaded pdfs and code files.

![image](/uploads/9c199e91a1b4ea296c9b82a76e11038b/image.png)

![image](/uploads/17ea3869469417ee78e650ce750dbeb7/image.png)
2024-03-19 20:06:32 +00:00
Tobias Fella
33c55d1563 Add back errouneously removed import 2024-03-19 21:00:48 +01:00
320 changed files with 99755 additions and 78547 deletions

1
.gitignore vendored
View File

@@ -12,3 +12,4 @@ kate.project.ctags.*
.idea/
cmake-build-*
src/res.generated.qrc
.qmlls.ini

View File

@@ -28,6 +28,7 @@ Dependencies:
'frameworks/kio': '@latest-kf6'
'frameworks/kwindowsystem': '@latest-kf6'
'frameworks/kstatusnotifieritem': '@latest-kf6'
'frameworks/kcrash': '@latest-kf6'
- 'on': ['Linux', 'FreeBSD']
'require':
'frameworks/kdbusaddons': '@latest-kf6'

View File

@@ -84,7 +84,8 @@ if(ANDROID)
)
else()
find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Widgets)
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem)
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem Crash)
find_package(KF6SyntaxHighlighting ${KF_MIN_VERSION} REQUIRED)
set_package_properties(KF6QQC2DesktopStyle PROPERTIES
TYPE RUNTIME
)

View File

@@ -1,6 +1,6 @@
<!--
SPDX-FileCopyrightText: 2020-2021 Carl Schwan <carlschwan@kde.org>
SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
SPDX-FileCopyrightText: 2020-2024 Tobias Fella <tobias.fella@kde.org>
SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
SPDX-License-Identifier: CC0-1.0
-->
@@ -16,19 +16,18 @@ A Qt/QML based Matrix client.
## Introduction
NeoChat is a client for [Matrix](https://matrix.org), the decentralized communication protocol for instant
messaging. It is a fork of Spectral, using KDE frameworks, most notably [Kirigami](https://invent.kde.org/frameworks/kirigami)
to provide a convergent experience across multiple platforms.
messaging.
NeoChat also make use of other KDE Frameworks as well as [libQuotient](https://github.com/quotient-im/libQuotient), a
NeoChat is based on KDE frameworks and as [libQuotient](https://github.com/quotient-im/libQuotient), a
Qt-based SDK for the [Matrix Protocol](https://spec.matrix.org/).
![Timeline](https://cdn.kde.org/screenshots/neochat/application.png)
## Features
NeoChat aims to be a fully featured application for the Matrix specification. As such everything in the current stable specification with the notable exceptions
of VoIP, threads and some aspects of End-to-End Encryption are supported. There are a few other smaller omissions due to the fact that the Matrix spec is constantly
evolving but the aim remains to provide eventual support for the entire spec.
NeoChat aims to be a fully featured application for the Matrix specification. As such most parts of the current specification are supported, with the notable exceptions
of VoIP, threads, and some aspects of End-to-End Encryption. There are a few other smaller omissions due to the fact that the Matrix spec is constantly
evolving, but the aim remains to provide eventual support for the entire spec.
Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:
- Polls - MSC3381
@@ -39,26 +38,9 @@ Due to the nature of the Matrix specification development NeoChat also supports
Details where to find stable releases for NeoChat can be found on its [homepage](https://apps.kde.org/neochat).
In addition to the stable builds, unstable nightly builds are available for all platforms. These can be downloaded
from the [binary factory](https://binary-factory.kde.org/). There are unstable versions for the following platforms
in addition to stable ones:
- Android
- MacOS
- Windows
Additionally the nightly Flatpak version can be obtained from the nightly Flatpak repo using the following commands in your terminal:
```
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --if-not-exists kdeapps --from https://distribute.kde.org/kdeapps.flatpakrepo
flatpak install kdeapps org.kde.neochat
```
The unstable Android version can also be obtained from the [KDE nightly F-Droid repo](https://community.kde.org/Android/FDroid).
## Running
Just start the executable in your preferred way - either from the build directory or from the installed location.
Nightly builds for linux and windows can be downloaded from [cdn.kde.org](https://cdn.kde.org/ci-builds/network/neochat/).
Nightly builds for android are available from [KDE's nightly F-Droid repository](https://community.kde.org/Android/F-Droid).
Nightly Flatpaks are available from [KDE's nightly Flatpak repository](https://userbase.kde.org/Tutorials/Flatpak).
## Building NeoChat
@@ -69,14 +51,18 @@ is primarily aimed at Linux development.
For Windows and Android [Craft](https://invent.kde.org/packaging/craft) is the primary choice. There are guides for setting up
development environments for [Windows](https://community.kde.org/Get_Involved/development/Windows) and [Android](https://develop.kde.org/docs/packaging/android/building_applications/).
## Running
Just start the executable in your preferred way - either from the build directory or from the installed location.
## Tests
Tests are in the repository under [autotests](autotests) and should all pass for any contribution.
Tests are in the repository under [autotests](autotests) and [appiumtests](appiumtests).
The project has CI setup to test new commits to the repository. All tests are expected to pass for a merge request to
be complete.
Current build status
## Current build status
![coverage](https://invent.kde.org/network/neochat/badges/master/pipeline.svg)
@@ -100,9 +86,9 @@ The best place to reach the maintainers is on the KDE Matrix instance in the Neo
## Acknowledgement
This program utilizes [libQuotient](https://github.com/quotient-im/libQuotient/) as its Matrix SDK.
NeoChat utilizes [libQuotient](https://github.com/quotient-im/libQuotient/) as its Matrix SDK.
This program is a fork of [Spectral](https://gitlab.com/spectral-im/spectral/).
NeoChat is a fork of [Spectral](https://gitlab.com/spectral-im/spectral/).
## License

View File

@@ -39,6 +39,10 @@ class OpenUserDetailsTest(unittest.TestCase):
def test_open_sheet(self):
self.driver.find_element(by=AppiumBy.NAME, value="@user:localhost:1234").click()
try:
self.driver.find_element(by=AppiumBy.NAME, value="Expand Normal").click()
except:
pass
self.driver.find_element(by=AppiumBy.NAME, value="Empty room (!room_id_1234:localhost:1234)").click()
self.driver.find_element(by=AppiumBy.NAME, value="A Display Name").click()
self.driver.find_element(by=AppiumBy.NAME, value="Account Details")

View File

@@ -130,7 +130,7 @@ void DelegateSizeHelperTest::equalBreakpoint_data()
}
/**
* We expect a default return except in the case where the the two percentages are
* We expect a default return except in the case where the two percentages are
* equal as that case can be calculated without dividing by zero.
*/
void DelegateSizeHelperTest::equalBreakpoint()

View File

@@ -105,7 +105,7 @@ void EventHandlerTest::author()
auto eventHandlerAuthor = eventHandler.getAuthor();
QCOMPARE(eventHandlerAuthor["isLocalUser"_ls], author->id() == room->localUser()->id());
QCOMPARE(eventHandlerAuthor["isLocalUser"_ls], author->id() == room->localMember().id());
QCOMPARE(eventHandlerAuthor["id"_ls], author->id());
QCOMPARE(eventHandlerAuthor["displayName"_ls], author->displayname(room));
QCOMPARE(eventHandlerAuthor["avatarSource"_ls], room->avatarForMember(author));
@@ -390,7 +390,7 @@ void EventHandlerTest::replyAuthor()
auto eventHandlerReplyAuthor = eventHandler.getReplyAuthor();
QCOMPARE(eventHandlerReplyAuthor["isLocalUser"_ls], replyAuthor->id() == room->localUser()->id());
QCOMPARE(eventHandlerReplyAuthor["isLocalUser"_ls], replyAuthor->id() == room->localMember().id());
QCOMPARE(eventHandlerReplyAuthor["id"_ls], replyAuthor->id());
QCOMPARE(eventHandlerReplyAuthor["displayName"_ls], replyAuthor->displayname(room));
QCOMPARE(eventHandlerReplyAuthor["avatarSource"_ls], room->avatarForMember(replyAuthor));

View File

@@ -32,8 +32,6 @@ private Q_SLOTS:
void linkPreviewsReject_data();
void linkPreviewsReject();
void editedLink();
};
void LinkPreviewerTest::initTestCase()
@@ -59,7 +57,7 @@ void LinkPreviewerTest::linkPreviewsMatch()
QFETCH(QUrl, testOutputLink);
auto event = TestUtils::loadEventFromFile<RoomMessageEvent>(eventSource);
auto linkPreviewer = LinkPreviewer(room, event.get());
auto linkPreviewer = LinkPreviewer(LinkPreviewer::linkPreview(event.get()), connection);
QCOMPARE(linkPreviewer.empty(), false);
QCOMPARE(linkPreviewer.url(), testOutputLink);
@@ -79,22 +77,7 @@ void LinkPreviewerTest::linkPreviewsReject()
QFETCH(QString, eventSource);
auto event = TestUtils::loadEventFromFile<RoomMessageEvent>(eventSource);
auto linkPreviewer = LinkPreviewer(room, event.get());
QCOMPARE(linkPreviewer.empty(), true);
QCOMPARE(linkPreviewer.url(), QUrl());
}
void LinkPreviewerTest::editedLink()
{
room->syncNewEvents(QStringLiteral("test-linkpreviewerintial-sync.json"));
auto event = eventCast<const RoomMessageEvent>(room->messageEvents().at(0).get());
auto linkPreviewer = LinkPreviewer(room, event);
QCOMPARE(linkPreviewer.empty(), false);
QCOMPARE(linkPreviewer.url(), QUrl("https://kde.org"_ls));
room->syncNewEvents(QStringLiteral("test-linkpreviewerreplace-sync.json"));
auto linkPreviewer = LinkPreviewer(LinkPreviewer::linkPreview(event.get()), connection);
QCOMPARE(linkPreviewer.empty(), true);
QCOMPARE(linkPreviewer.url(), QUrl());

View File

@@ -529,6 +529,19 @@ void TextHandlerTest::componentOutput_data()
QTest::newRow("inline code single block") << QStringLiteral("<code>https://kde.org</code>")
<< QList<MessageComponent>{
MessageComponent{MessageComponentType::Text, QStringLiteral("<code>https://kde.org</code>"), {}}};
QTest::newRow("long start tag")
<< QStringLiteral(
"Ah, you mean something like<br/><pre data-md=\"```\"><code class=\"language-qml\"># main.qml\nimport CustomQml\n...\nControls.TextField { id: "
"someField }\nCustomQml {\n someTextProperty: someField.text\n}\n</code></pre>Sure you can, it's still local to the same file where you "
"defined the id")
<< QList<MessageComponent>{
MessageComponent{MessageComponentType::Text, QStringLiteral("Ah, you mean something like"), {}},
MessageComponent{
MessageComponentType::Code,
QStringLiteral(
"# main.qml\nimport CustomQml\n...\nControls.TextField { id: someField }\nCustomQml {\n someTextProperty: someField.text\n}"),
QVariantMap{{QStringLiteral("class"), QStringLiteral("qml")}}},
MessageComponent{MessageComponentType::Text, QStringLiteral("Sure you can, it's still local to the same file where you defined the id"), {}}};
}
void TextHandlerTest::componentOutput()

View File

@@ -32,6 +32,7 @@
<name xml:lang="it">NeoChat</name>
<name xml:lang="ka">NeoChat</name>
<name xml:lang="ko">NeoChat</name>
<name xml:lang="lv">NeoChat</name>
<name xml:lang="nl">NeoChat</name>
<name xml:lang="nn">NeoChat</name>
<name xml:lang="pa">ਨਿਓ-ਚੈਟ</name>
@@ -64,6 +65,7 @@
<summary xml:lang="it">Conversa con i tuoi contatti su matrix</summary>
<summary xml:lang="ka">ესაუბრეთ მეგობრებს Matrix-ზე</summary>
<summary xml:lang="ko">Matrix를 사용하여 친구들과 대화하기</summary>
<summary xml:lang="lv">Tērzējiet ar saviem draugiem „Matrix“ tīklā</summary>
<summary xml:lang="nl">Met uw vrienden chatten op matrix</summary>
<summary xml:lang="nn">Prat med vennar på Matrix</summary>
<summary xml:lang="pl">Rozmawiaj ze swoimi znajomymi w Matriksie</summary>
@@ -87,6 +89,7 @@
<p xml:lang="ia">NeoChat es un app de conversation que te permitte prender avantage plen del rete Matrix. Il te forni un modo secur de inviar messages de texto, videos e files audio a tui familia, collegas e amicos.</p>
<p xml:lang="it">NeoChat è un'applicazione di chat che ti consente di sfruttare appieno la rete Matrix. Ti fornisce un modo sicuro per inviare messaggi di testo, video e file audio a familiari, colleghi e amici.</p>
<p xml:lang="ka">NeoChat ჩატის აპია, რომელიც საშუალება გაძლევთ, Matrix-ის ქსელის საშუალებები ბოლომდე გამოიყენოთ. ის გაძლევთ უსაფრთხო გზას, გააგზავნოთ ტექსტური შეტყობინებები, ვიდეოებ და აუდიოფაილები თქვენს ოჯახთან, კოლეგებთან და მეგობრებთან.</p>
<p xml:lang="lv">„NeoChat“ ir tērzēšanas programma, kas ļauj pilnvērtīgi izmantot „Matrix“ tīklu. Tā sniedz drošu veidu teksta ziņu, video un audio sūtīšanai ģimenes locekļiem, kolēģiem un draugiem.</p>
<p xml:lang="nl">NeoChat is een chat-toepassing die u het volledige voordeel van het Matrix-netwerk laat genieten. Het levert u op een veilige manier tekstberichten, video's en geluidsbestanden naar uw familie, collega's en vrienden te verzenden.</p>
<p xml:lang="pl">NoeChat to aplikacja do rozmów, która umożliwia wykorzystanie wszystkich możliwości Matriksa. Umożliwia wysyłanie wiadomości tekstowych, filmów i dźwięków w bezpieczny sposób do twojej rodziny, kolegów i przyjaciół.</p>
<p xml:lang="sl">NeoChat je aplikacija za klepet, ki vam omogoča, da v celoti izkoristite omrežje Matrix. Zagotavlja vam varen način za pošiljanje besedilnih sporočil, videoposnetkov in zvočnih datotek vaši družini, sodelavcem in prijateljem.</p>
@@ -110,6 +113,7 @@
<p xml:lang="it">NeoChat mira ad essere un'applicazione completa per le specifiche Matrix. Pertanto, sono supportati tutti gli elementi dell'attuale specifica stabile con le notevoli eccezioni di VoIP, conversazioni e alcuni aspetti della cifratura end-to-end. Ci sono alcune altre piccole omissioni dovute al fatto che le specifiche Matrix sono in continua evoluzione, ma l'obiettivo rimane quello di fornire un eventuale supporto per l'intera specifica.</p>
<p xml:lang="ka">NeoChat მიზნად ისახავს Matrix სპეციფიკაციის სრული განხორციელება ჰქონდეს. როგორც ასეთი, ყველაფერი მიმდინარე სპეციფიკაციიდან, VoIP-ის, ძაფებისა და გამჭოლი დაშიფვრის ზოგიერთი ასპექტის გარდა, მხარდაჭერილია. შეძლება ასევე იყოს მცირე ლაფსუსებიც იმის გამო, რომ Matrix-ის სპეციფიკაცია მუდმივად ვითარდება, მაგრამ ჩვენი მიზანი მისი სრული მხარდაჭერაა.</p>
<p xml:lang="ko">NeoChat은 Matrix 표준을 따르는 프로그램을 목표로 합니다. 현재 안정 버전의 표준에서 제공하는 기능의 대부분을 지원하며, VoIP, 스레드, 일부 종단간 암호화와 같은 기능은 아직 지원하지 않습니다. Matrix 표준은 계속하여 진화 중이기 때문에 일부 기능이 빠져 있을 수도 있지만 장기적으로는 전체 표준을 지원하는 것이 목표입니다.</p>
<p xml:lang="lv">„NeoChat“ mērķis ir piedāvāt plašas iespējas atbilstoši „Matrix“ specifikācijai. Līdz ar to programma atbalsta visu pašreizējā stabilajā specifikācijā, izņemot VoIP, pavedienus un dažos aspektos galšifrēšanu. Pastāv citas atsevišķas sīkas neieviestas daļas, jo „Matrix“ specifikācija nepārtraukti attīstās, tomēr mērķis ir ar laiku nodrošināt atbalstu pilnai specifikācijai.</p>
<p xml:lang="nl">NeoChat richt zich op het volledig bieden van alle mogelijkheden van de Matrix-specificatie. Alles in de huidige stabiele specificatie met merkbare uitzondering van VoIP, gekoppelde discussies en sommige aspecten van eind-tot-eind versleuteling worden ondersteund. Er zijn een paar andere kleinere omissies vanwege het feit dat de Matrix specificatie constant evolueert maar het doel blijft het eventueel bieden van ondersteuning van de gehele specificatie.</p>
<p xml:lang="nn">NeoChat har som mål å støtta all funksjonalitet i Matrix-spesifikasjonen. Førebels er alt i den gjeldande stabile spesifikasjonen støtta, med unntak av VoIP, trådar og nokre delar av ende-til-kryptering. Det finst òg andre småting som ikkje er støtta, sidan Matrix-spesifikasjon er i stadig endring, men målet er altså støtte for alt.</p>
<p xml:lang="pl">NeoChat w zamyśle ma być pełnowartościową aplikacją wg wytycznych Matriksa. Z tego powodu, wszystko, co jest obecnie w stabilnych wytycznych z pominięciem VoIP, wątków i niektórych części szyfrowania Użytkownik-do-Użytkownika są obecnie obsługiwane. Pominięto też kilka mniejszych rzeczy ze względu na ciągły rozwój wytycznych Matriksa, lecz celem nadal jest zapewnienie obsługi wszystkich wytycznych.</p>
@@ -136,6 +140,7 @@
<p xml:lang="it">A causa della natura dello sviluppo delle specifiche Matrix, NeoChat supporta anche numerose funzionalità instabili. Attualmente queste sono:</p>
<p xml:lang="ka">Matrix-ის სპეციფიკაციის განვითარების ბუნების გამო NeoChat-ს ასევე აქვს უამრავი არასტაბილური ფუნქციაც. ახლა ისინია:</p>
<p xml:lang="ko">Matrix 표준 개발의 특징으로 인하여 NeoChat은 일부 실험적인 기능을 지원합니다. 현재 지원하는 기능은 다음과 같습니다.</p>
<p xml:lang="lv">„Matrix“ specifikācijas veida dēļ „NeoChat“ attīstība atbalsta arī vairākas nestabilas iespējas, šobrīd šādas ir:</p>
<p xml:lang="nl">Vanwege de aard van de ontwikkeling van de Matrix specificatie ondersteunt NeoChat ook talloze onstabiele mogelijkheden. Dit zijn nu:</p>
<p xml:lang="nn">På grunn av måten Matrix-spesifikasjonen vert utvikla på, støttar NeoChat òg nokre uferdige funksjonar:</p>
<p xml:lang="pl">Ze względu na sposób rozwoju Matriksa, NeoChat obsługuje także kilka niestabilnych możliwości. Obecnie są to:</p>
@@ -164,6 +169,7 @@
<li xml:lang="it">Sondaggi - MSC3381</li>
<li xml:lang="ka">Polls - MSC3381</li>
<li xml:lang="ko">투표 - MSC3381</li>
<li xml:lang="lv">Aptaujas — MSC3381</li>
<li xml:lang="nl">Polls - MSC3381</li>
<li xml:lang="nn">Avstemmingar  MSC3381</li>
<li xml:lang="pl">Ankiety - MSC3381</li>
@@ -191,6 +197,7 @@
<li xml:lang="it">Pacchetti di adesivi - MSC2545</li>
<li xml:lang="ka">სტიკერების პაკეტები - MSC2545</li>
<li xml:lang="ko">스티커 팩 - MSC2545</li>
<li xml:lang="lv">Uzlīmju pakas — MSC2545</li>
<li xml:lang="nl">Sticker Packs - MSC2545</li>
<li xml:lang="nn">Klistremerke-pakkar  MSC2545</li>
<li xml:lang="pl">Paczki naklejek - MSC2545</li>
@@ -218,6 +225,7 @@
<li xml:lang="it">Località eventi - MSC3488</li>
<li xml:lang="ka">მდებარეობის მოვლენები - MSC3488</li>
<li xml:lang="ko">위치 이벤트 - MSC3488</li>
<li xml:lang="lv">Atrašanās vietas notikumi — MSC3488</li>
<li xml:lang="nl">Locatie gebeurtenissen - MSC3488</li>
<li xml:lang="nn">Posisjonshendingar  MSC3488</li>
<li xml:lang="pl">Wydarzenia w miejscach - MSC3488</li>
@@ -278,6 +286,7 @@
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
<caption xml:lang="ka">მთავარი ხედი სურათების სიით, ჩატით და ოთახის ინფორმაციით</caption>
<caption xml:lang="ko">대화방 목록, 채팅, 대화방 정보가 표시된 주 보기</caption>
<caption xml:lang="lv">Pamata skats ar istabu sarakstu, tērzēšanu un istabas informāciju</caption>
<caption xml:lang="nl">Hoofdweergave met lijst met rooms, chat en roominformatie</caption>
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
@@ -303,6 +312,7 @@
<caption xml:lang="ia">Discoperi nove communitate con Matrix Spaces (Spatios de Matrix)</caption>
<caption xml:lang="it">Scopri nuove comunità con Matrix Spaces</caption>
<caption xml:lang="ka">აღმოაჩინეთ ახალი საზოგადოებები Matrix Spaces-თან ერთად</caption>
<caption xml:lang="lv">Atklājiet jaunas kopienas ar „Matrix“ telpām</caption>
<caption xml:lang="nl">Ontdek nieuwe gemeenschappen met Matrix-ruimten</caption>
<caption xml:lang="pl">Odkrywaj nowe społeczności w Przestrzeniach Matriksa</caption>
<caption xml:lang="sl">Odkrijte nove skupnosti z Matrix Spaces</caption>
@@ -335,6 +345,7 @@
<caption xml:lang="it">Vista principale con elenco delle stanze, chat e informazioni sulla stanza</caption>
<caption xml:lang="ka">მთავარი ხედი სურათების სიით, ჩატით და ოთახის ინფორმაციით</caption>
<caption xml:lang="ko">대화방 목록, 채팅, 대화방 정보가 표시된 주 보기</caption>
<caption xml:lang="lv">Pamata skats ar istabu sarakstu, tērzēšanu un istabas informāciju</caption>
<caption xml:lang="nl">Hoofdweergave met lijst met rooms, chat en roominformatie</caption>
<caption xml:lang="nn">Hovudvising med romliste, pratevindauge og rominformasjon</caption>
<caption xml:lang="pl">Główny widok z wykazem pokojów, rozmowami i szczegółami pokojów</caption>
@@ -365,6 +376,7 @@
<caption xml:lang="it">Schermata di accesso</caption>
<caption xml:lang="ka">შესვლის ეკრანი</caption>
<caption xml:lang="ko">로그인 화면</caption>
<caption xml:lang="lv">Ierakstīšanās logs</caption>
<caption xml:lang="nl">Aanmeldscherm</caption>
<caption xml:lang="nn">Innloggingsbilete</caption>
<caption xml:lang="pl">Ekran logowania</caption>
@@ -382,6 +394,7 @@
<content_attribute id="social-chat">intense</content_attribute>
</content_rating>
<releases>
<release version="24.02.2" date="2024-04-11"/>
<release version="24.02.1" date="2024-03-21"/>
<release version="24.02.0" date="2024-02-28">
<url>https://kde.org/announcements/megarelease/6/#neochat</url>

View File

@@ -26,6 +26,7 @@ Name[it]=NeoChat
Name[ka]=NeoChat
Name[ko]=NeoChat
Name[lt]=NeoChat
Name[lv]=NeoChat
Name[nl]=NeoChat
Name[nn]=NeoChat
Name[pa]=ਨਿਓ-ਚੈਟ
@@ -66,6 +67,7 @@ GenericName[it]=Client Matrix
GenericName[ka]=Matrix -ის კლიენტი
GenericName[ko]=Matrix 클라이언트
GenericName[lt]=Matrix kliento programa
GenericName[lv]=„Matrix“ klients
GenericName[nl]=Matrix-client
GenericName[nn]=Matrix-klient
GenericName[pa]=ਮੈਟਰਿਕਸ ਕਲਾਈਂਟ
@@ -105,6 +107,7 @@ Comment[it]=Client per il protocollo Matrix
Comment[ka]=კლიენტი Matrix-ის პროტოკოლისთვის
Comment[ko]=Matrix 프로토콜용 클라이언트
Comment[lt]=Matrix protokolo kliento programa
Comment[lv]=Klients „Matrix“ protokolam
Comment[nl]=Client voor het Matrix-protocol
Comment[nn]=Klient for Matrix-protokollen
Comment[pa]=ਮੈਟਰਿਕਸ ਪਰੋਟੋਕਾਲ ਲਈ ਕਲਾਈਂਟ ਹੈ

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

5247
po/lv/neochat.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -126,6 +126,7 @@ add_library(neochat STATIC
events/pollevent.cpp
pollhandler.cpp
utils.h
utils.cpp
registration.cpp
neochatconnection.cpp
neochatconnection.h
@@ -171,11 +172,21 @@ add_library(neochat STATIC
models/statekeysmodel.h
sharehandler.cpp
sharehandler.h
models/roomtreeitem.cpp
models/roomtreeitem.h
foreigntypes.h
models/threepidmodel.cpp
models/threepidmodel.h
)
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
QT_QML_SINGLETON_TYPE TRUE
)
qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat
QML_FILES
qml/main.qml
qml/Main.qml
qml/AccountMenu.qml
qml/ExploreComponent.qml
qml/ExploreComponentMobile.qml
@@ -187,18 +198,11 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
qml/UserInfo.qml
qml/UserInfoDesktop.qml
qml/RoomPage.qml
qml/RoomWindow.qml
qml/ExploreRoomsPage.qml
qml/ManualRoomDialog.qml
qml/ExplorerDelegate.qml
qml/InviteUserPage.qml
qml/ImageEditorPage.qml
qml/WelcomePage.qml
qml/General.qml
qml/RoomSecurity.qml
qml/PushNotification.qml
qml/Categories.qml
qml/Permissions.qml
qml/NeochatMaximizeComponent.qml
qml/FancyEffectsContainer.qml
qml/TypingPane.qml
@@ -210,22 +214,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
qml/CompletionMenu.qml
qml/PieProgressBar.qml
qml/QuickFormatBar.qml
qml/RoomData.qml
qml/ServerData.qml
qml/EmojiPicker.qml
qml/LoginStep.qml
qml/Login.qml
qml/Homeserver.qml
qml/Username.qml
qml/RegisterPassword.qml
qml/Captcha.qml
qml/Terms.qml
qml/Email.qml
qml/Password.qml
qml/LoginRegister.qml
qml/Loading.qml
qml/LoginMethod.qml
qml/Sso.qml
qml/UserDetailDialog.qml
qml/CreateRoomDialog.qml
qml/EmojiDialog.qml
@@ -245,26 +234,6 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
qml/FileDelegateContextMenu.qml
qml/MessageSourceSheet.qml
qml/ReportSheet.qml
qml/SettingsPage.qml
qml/ThemeRadioButton.qml
qml/ColorScheme.qml
qml/GeneralSettingsPage.qml
qml/EmoticonsPage.qml
qml/EmoticonEditorPage.qml
qml/EmoticonFormCard.qml
qml/GlobalNotificationsPage.qml
qml/NotificationRuleItem.qml
qml/AppearanceSettingsPage.qml
qml/AccountsPage.qml
qml/AccountEditorPage.qml
qml/DevicesPage.qml
qml/DeviceDelegate.qml
qml/DevicesCard.qml
qml/About.qml
qml/AboutKDE.qml
qml/SonnetConfigPage.qml
qml/NetworkProxyPage.qml
qml/DevtoolsPage.qml
qml/ConfirmEncryptionDialog.qml
qml/RemoveSheet.qml
qml/BanSheet.qml
@@ -292,7 +261,6 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
qml/SpaceHierarchyDelegate.qml
qml/RemoveChildDialog.qml
qml/SelectParentDialog.qml
qml/Security.qml
qml/QrCodeMaximizeComponent.qml
qml/SelectSpacesDialog.qml
qml/AttachDialog.qml
@@ -305,16 +273,23 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
qml/RoomTreeSection.qml
qml/DelegateContextMenu.qml
qml/ShareDialog.qml
qml/FeatureFlagPage.qml
qml/IgnoredUsersDialog.qml
qml/AccountData.qml
qml/StateKeys.qml
qml/UnlockSSSSDialog.qml
qml/QrScannerPage.qml
qml/JoinRoomDialog.qml
qml/ConfirmUrlDialog.qml
qml/AccountSwitchDialog.qml
qml/ConfirmLeaveDialog.qml
qml/CodeMaximizeComponent.qml
qml/EditStateDialog.qml
RESOURCES
qml/confetti.png
qml/glowdot.png
)
add_subdirectory(settings)
add_subdirectory(timeline)
add_subdirectory(devtools)
add_subdirectory(login)
if(UNIX)
qt_target_qml_sources(neochat QML_FILES qml/ShareAction.qml)
@@ -406,7 +381,7 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
endif()
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
target_link_libraries(neochat PRIVATE timelineplugin)
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin devtoolsplugin loginplugin)
target_link_libraries(neochat PUBLIC
Qt::Core
Qt::Quick
@@ -430,6 +405,10 @@ target_link_libraries(neochat PUBLIC
QCoro::Network
)
if (TARGET KF6::Crash)
target_link_libraries(neochat PUBLIC KF6::Crash)
endif()
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
if(NEOCHAT_FLATPAK)
@@ -532,7 +511,7 @@ if(ANDROID)
)
ecm_add_android_apk(neochat-app ANDROID_DIR ${CMAKE_SOURCE_DIR}/android)
else()
target_link_libraries(neochat PUBLIC Qt::Widgets KF6::KIOWidgets)
target_link_libraries(neochat PUBLIC Qt::Widgets KF6::KIOWidgets KF6::SyntaxHighlighting)
install(FILES neochat.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
endif()

View File

@@ -80,6 +80,7 @@ QString ActionsHandler::handleMentions(QString handledText, QList<Mention> *ment
void ActionsHandler::handleMessage(const QString &text, QString handledText, ChatBarCache *chatBarCache)
{
Q_ASSERT(m_room);
if (NeoChatConfig::allowQuickEdit()) {
QRegularExpression sed(QStringLiteral("^s/([^/]*)/([^/]*)(/g)?$"));
auto match = sed.match(text);
@@ -90,7 +91,7 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, Cha
for (auto it = m_room->messageEvents().crbegin(); it != m_room->messageEvents().crend(); it++) {
if (const auto event = eventCast<const RoomMessageEvent>(&**it)) {
if (event->senderId() == m_room->localUser()->id() && event->hasTextContent()) {
if (event->senderId() == m_room->localMember().id() && event->hasTextContent()) {
QString originalString;
if (event->content()) {
originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content())->body;

View File

@@ -58,7 +58,7 @@ public Q_SLOTS:
void handleMessageEvent(ChatBarCache *chatBarCache);
private:
NeoChatRoom *m_room = nullptr;
QPointer<NeoChatRoom> m_room;
void checkEffects(const QString &text);
QString handleMentions(QString handledText, QList<Mention> *mentions);

View File

@@ -91,7 +91,7 @@ class ChatDocumentHandler : public QObject
Q_PROPERTY(CompletionModel *completionModel READ completionModel CONSTANT)
/**
* @brief The current room that the the text document is being handled for.
* @brief The current room that the text document is being handled for.
*/
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)

View File

@@ -156,6 +156,15 @@ void Controller::addConnection(NeoChatConnection *c)
c->saveState();
});
connect(c, &NeoChatConnection::loggedOut, this, [this, c] {
if (accounts().count() > 1) {
// Only set the connection if the account being logged out is currently active
if (c == activeConnection()) {
setActiveConnection(dynamic_cast<NeoChatConnection *>(accounts().accounts()[0]));
}
} else {
setActiveConnection(nullptr);
}
dropConnection(c);
});
connect(c, &NeoChatConnection::badgeNotificationCountChanged, this, &Controller::updateBadgeNotificationCount);
@@ -309,7 +318,7 @@ void Controller::setActiveConnection(NeoChatConnection *connection)
updateBadgeNotificationCount(m_connection, m_connection->badgeNotificationCount());
}
Q_EMIT activeConnectionChanged();
Q_EMIT activeConnectionChanged(m_connection);
}
void Controller::listenForNotifications()
@@ -377,6 +386,14 @@ AccountRegistry &Controller::accounts()
return m_accountRegistry;
}
QString Controller::loadFileContent(const QString &path) const
{
QUrl url(path);
QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString());
file.open(QFile::ReadOnly);
return QString::fromLatin1(file.readAll());
}
#include "moc_controller.cpp"
void Controller::setTestMode(bool test)
@@ -393,3 +410,12 @@ void Controller::removeConnection(const QString &userId)
SettingsGroup("Accounts"_ls).remove(userId);
}
}
bool Controller::ssssSupported() const
{
#if __has_include("Quotient/e2ee/sssshandler.h")
return true;
#else
return false;
#endif
}

View File

@@ -56,6 +56,8 @@ class Controller : public QObject
Q_PROPERTY(QStringList accountsLoading MEMBER m_accountsLoading NOTIFY accountsLoadingChanged)
Q_PROPERTY(bool ssssSupported READ ssssSupported CONSTANT)
public:
static Controller &instance();
static Controller *create(QQmlEngine *engine, QJSEngine *)
@@ -92,12 +94,16 @@ public:
*/
static void listenForNotifications();
Q_INVOKABLE QString loadFileContent(const QString &path) const;
Quotient::AccountRegistry &accounts();
static void setTestMode(bool testMode);
Q_INVOKABLE void removeConnection(const QString &userId);
bool ssssSupported() const;
private:
explicit Controller(QObject *parent = nullptr);
@@ -123,6 +129,6 @@ Q_SIGNALS:
void errorOccured(const QString &error, const QString &detail);
void connectionAdded(NeoChatConnection *connection);
void connectionDropped(NeoChatConnection *connection);
void activeConnectionChanged();
void activeConnectionChanged(NeoChatConnection *connection);
void accountsLoadingChanged();
};

View File

@@ -23,7 +23,7 @@ ColumnLayout {
model: root.connection.accountDataEventTypes
delegate: FormCard.FormButtonDelegate {
text: modelData
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
sourceText: root.connection.accountDataJsonString(modelData)
}, {
title: i18nc("@title:window", "Event Source"),

View File

@@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
# SPDX-License-Identifier: BSD-2-Clause
qt_add_library(devtools STATIC)
qt_add_qml_module(devtools
URI org.kde.neochat.devtools
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/devtools
QML_FILES
DevtoolsPage.qml
AccountData.qml
DebugOptions.qml
FeatureFlagPage.qml
RoomData.qml
ServerData.qml
StateKeys.qml
)

View File

@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.neochat
FormCard.FormCardPage {
id: root
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing
FormCard.FormCheckDelegate {
id: roomAccountDataVisibleCheck
text: i18nc("@option:check Enable the matrix 'threads' feature", "Always allow device verification")
description: i18n("Allow the user to start a verification session with devices that were already verified")
checked: Config.alwaysVerifyDevice
onToggled: Config.alwaysVerifyDevice = checked
}
}
}

View File

@@ -25,6 +25,11 @@ FormCard.FormCardPage {
readonly property real tabWidth: tabBar.width / tabBar.count
QQC2.TabButton {
text: i18nc("@title:tab", "Debug Options")
implicitWidth: tabBar.tabWidth
}
QQC2.TabButton {
text: qsTr("Room Data")
@@ -52,6 +57,7 @@ FormCard.FormCardPage {
currentIndex: tabBar.currentIndex
DebugOptions {}
RoomData {
room: root.room
connection: root.connection

View File

@@ -8,7 +8,6 @@ import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.neochat
import org.kde.neochat.config
FormCard.FormCardPage {
id: root
@@ -23,5 +22,11 @@ FormCard.FormCardPage {
onToggled: Config.threads = checked
}
FormCard.FormCheckDelegate {
text: i18nc("@option:check Enable the matrix 'secret backup' feature", "Secret Backup")
checked: Config.secretBackup
onToggled: Config.secretBackup = checked
}
}
}

View File

@@ -25,14 +25,11 @@ ColumnLayout {
text: i18n("Room")
textRole: "escapedDisplayName"
valueRole: "roomId"
displayText: roomListModel.data(roomListModel.index(currentIndex, 0), RoomListModel.DisplayNameRole)
model: RoomListModel {
id: roomListModel
connection: root.connection
}
displayText: RoomManager.roomListModel.data(RoomManager.roomListModel.index(currentIndex, 0), RoomListModel.DisplayNameRole)
model: RoomManager.roomListModel
currentIndex: 0
Component.onCompleted: currentIndex = roomListModel.rowForRoom(root.room)
onCurrentValueChanged: root.room = roomListModel.roomByAliasOrId(roomComboBox.currentValue)
Component.onCompleted: currentIndex = RoomManager.roomListModel.rowForRoom(root.room)
onCurrentValueChanged: root.room = RoomManager.roomListModel.roomByAliasOrId(roomComboBox.currentValue)
}
FormCard.FormTextDelegate {
text: i18n("Room Id: %1", root.room.id)
@@ -49,7 +46,7 @@ ColumnLayout {
model: root.room.accountDataEventTypes
delegate: FormCard.FormButtonDelegate {
text: modelData
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
sourceText: root.room.roomAcountDataJson(text)
}, {
title: i18n("Event Source"),
@@ -77,14 +74,9 @@ ColumnLayout {
description: i18ncp("'Event' being some JSON data, not something physically happening.", "%1 event of this type", "%1 events of this type", model.eventCount)
onClicked: {
if (model.eventCount === 1) {
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
sourceText: stateModel.stateEventJson(stateModel.index(model.index, 0))
}, {
title: i18n("Event Source"),
width: Kirigami.Units.gridUnit * 25
})
openEventSource(model.type, model.stateKey);
} else {
pageStack.pushDialogLayer(stateKeysComponent, {
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'StateKeys'), {
room: root.room,
eventType: model.type
}, {
@@ -94,9 +86,17 @@ ColumnLayout {
}
}
}
Component {
id: stateKeysComponent
StateKeys {}
}
}
function openEventSource(type: string, stateKey: string): void {
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
model: stateModel,
allowEdit: true,
room: root.room,
type: type,
stateKey: stateKey,
}, {
title: i18n("Event Source"),
width: Kirigami.Units.gridUnit * 25
});
}
}

View File

@@ -31,13 +31,21 @@ FormCard.FormCardPage {
delegate: FormCard.FormButtonDelegate {
text: model.stateKey
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
sourceText: stateKeysModel.stateEventJson(stateKeysModel.index(model.index, 0))
}, {
title: i18nc("@title:window", "Event Source"),
width: Kirigami.Units.gridUnit * 25
})
onClicked: openEventSource(model.stateKey)
}
}
}
function openEventSource(stateKey: string): void {
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
model: stateKeysModel,
allowEdit: true,
room: root.room,
type: root.eventType,
stateKey: stateKey
}, {
title: i18nc("@title:window", "Event Source"),
width: Kirigami.Units.gridUnit * 25
});
}
}

View File

@@ -40,7 +40,8 @@ public:
Code, /**< A code section. */
Quote, /**< A quote section. */
File, /**< A message that is a file. */
Itinerary, /**< A preview for a file that can integrate with KDE itinerary.. */
Itinerary, /**< A preview for a file that can integrate with KDE itinerary. */
Pdf, /**< A preview for a PDF file. */
Poll, /**< The initial event for a poll. */
Location, /**< A location event. */
LiveLocation, /**< The initial event of a shared live location (i.e., the place where this is supposed to be shown in the timeline). */
@@ -50,6 +51,7 @@ public:
LinkPreview, /**< A preview of a URL in the message. */
LinkPreviewLoad, /**< A loading dialog for a link preview. */
Edit, /**< A text edit for editing a message. */
Verification, /**< A user verification session start message. */
Other, /**< Anything that cannot be classified as another type. */
};
Q_ENUM(Type);

View File

@@ -29,6 +29,7 @@ public:
Deprioritized, /**< The room is set as low priority. */
Space, /**< The room is a space. */
AddDirect, /**< So we can show the add friend delegate. */
TypesCount, /**< Number of different types (this should always be last). */
};
Q_ENUM(Types);

View File

@@ -73,7 +73,7 @@ QVariantMap EventHandler::getAuthor(bool isPending) const
return m_room->getUser(nullptr);
}
const auto author = isPending ? m_room->localUser() : m_room->user(m_event->senderId());
const auto author = isPending ? m_room->localMember() : m_room->user(m_event->senderId());
return m_room->getUser(author);
}
@@ -96,7 +96,7 @@ QString EventHandler::getAuthorDisplayName(bool isPending) const
}
return previousDisplayName;
} else {
const auto author = isPending ? m_room->localUser() : m_room->user(m_event->senderId());
const auto author = isPending ? m_room->localMember() : m_room->user(m_event->senderId());
return m_room->htmlSafeMemberName(author->id());
}
}
@@ -112,7 +112,7 @@ QString EventHandler::singleLineAuthorDisplayname(bool isPending) const
return {};
}
const auto author = isPending ? m_room->localUser() : m_room->user(m_event->senderId());
const auto author = isPending ? m_room->localMember() : m_room->user(m_event->senderId());
auto displayName = m_room->safeMemberName(author->id());
displayName.replace(QStringLiteral("<br>\n"), QStringLiteral(" "));
displayName.replace(QStringLiteral("<br>"), QStringLiteral(" "));
@@ -656,6 +656,7 @@ QVariantMap EventHandler::getMediaInfoForEvent(const Quotient::RoomEvent *event)
// Get the file info for the event.
const EventContent::FileInfo *fileInfo;
bool isSticker = false;
if (event->is<RoomMessageEvent>()) {
auto roomMessageEvent = eventCast<const RoomMessageEvent>(event);
if (!roomMessageEvent->hasFileContent()) {
@@ -665,14 +666,15 @@ QVariantMap EventHandler::getMediaInfoForEvent(const Quotient::RoomEvent *event)
} else if (event->is<StickerEvent>()) {
auto stickerEvent = eventCast<const StickerEvent>(event);
fileInfo = &stickerEvent->image();
isSticker = true;
} else {
return {};
}
return getMediaInfoFromFileInfo(fileInfo, eventId);
return getMediaInfoFromFileInfo(fileInfo, eventId, false, isSticker);
}
QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail) const
QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail, bool isSticker) const
{
QVariantMap mediaInfo;
@@ -699,6 +701,8 @@ QVariantMap EventHandler::getMediaInfoFromFileInfo(const EventContent::FileInfo
// Add media size if available.
mediaInfo["size"_ls] = fileInfo->payloadSize;
mediaInfo["isSticker"_ls] = isSticker;
// Add parameter depending on media type.
if (mimeType.name().contains(QStringLiteral("image"))) {
if (auto castInfo = static_cast<const EventContent::ImageContent *>(fileInfo)) {
@@ -959,7 +963,7 @@ bool EventHandler::hasReadMarkers() const
}
auto userIds = m_room->userIdsAtEvent(m_event->id());
userIds.remove(m_room->localUser()->id());
userIds.remove(m_room->localMember().id());
return userIds.size() > 0;
}
@@ -975,7 +979,7 @@ QVariantList EventHandler::getReadMarkers(int maxMarkers) const
}
auto userIds_temp = m_room->userIdsAtEvent(m_event->id());
userIds_temp.remove(m_room->localUser()->id());
userIds_temp.remove(m_room->localMember().id());
auto userIds = userIds_temp.values();
if (userIds.count() > maxMarkers) {
@@ -1004,7 +1008,7 @@ QString EventHandler::getNumberExcessReadMarkers(int maxMarkers) const
}
auto userIds = m_room->userIdsAtEvent(m_event->id());
userIds.remove(m_room->localUser()->id());
userIds.remove(m_room->localMember().id());
if (userIds.count() > maxMarkers) {
return QStringLiteral("+ ") + QString::number(userIds.count() - maxMarkers);
@@ -1025,7 +1029,7 @@ QString EventHandler::getReadMarkersString() const
}
auto userIds = m_room->userIdsAtEvent(m_event->id());
userIds.remove(m_room->localUser()->id());
userIds.remove(m_room->localMember().id());
/**
* The string ends up in the form

View File

@@ -229,6 +229,7 @@ public:
* - width - The width in pixels of the audio media.
* - height - The height in pixels of the audio media.
* - tempInfo - mediaInfo (with the same properties as this except no tempInfo) for a temporary image while the file downloads.
* - isSticker - Whether the image is a sticker or not
*/
QVariantMap getMediaInfo() const;
@@ -320,6 +321,7 @@ public:
* - width - The width in pixels of the audio media.
* - height - The height in pixels of the audio media.
* - tempInfo - mediaInfo (with the same properties as this except no tempInfo) for a temporary image while the file downloads.
* - isSticker - Whether the image is a sticker or not
*/
QVariantMap getReplyMediaInfo() const;
@@ -405,5 +407,6 @@ private:
QString getMessageBody(const Quotient::RoomMessageEvent &event, Qt::TextFormat format, bool stripNewlines) const;
QVariantMap getMediaInfoForEvent(const Quotient::RoomEvent *event) const;
QVariantMap getMediaInfoFromFileInfo(const Quotient::EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail = false) const;
QVariantMap
getMediaInfoFromFileInfo(const Quotient::EventContent::FileInfo *fileInfo, const QString &eventId, bool isThumbnail = false, bool isSticker = false) const;
};

View File

@@ -74,6 +74,9 @@ void ImagePackEventContent::fillJson(QJsonObject *o) const
}
imageJson["usage"_ls] = usageJson;
}
if (image.info.has_value()) {
imageJson["info"_ls] = Quotient::EventContent::toInfoJson(*image.info);
}
imagesJson[image.shortcode] = imageJson;
}
(*o)["images"_ls] = imagesJson;

View File

@@ -89,6 +89,4 @@ public:
QUO_EVENT(ImagePackEvent, "im.ponies.room_emotes")
using KeyedStateEventBase::KeyedStateEventBase;
};
REGISTER_EVENT_TYPE(ImagePackEvent)
}

View File

@@ -40,5 +40,4 @@ public:
*/
QJsonArray allow() const;
};
REGISTER_EVENT_TYPE(JoinRulesEvent)
}

View File

@@ -41,6 +41,12 @@ FileType::~FileType() noexcept
{
}
FileType &FileType::instance()
{
static FileType _instance;
return _instance;
}
QMimeType FileType::mimeTypeForName(const QString &nameOrAlias) const
{
Q_D(const FileType);
@@ -113,4 +119,10 @@ QStringList FileType::supportedAnimatedImageFormats() const
return d->supportedAnimatedImageFormats;
}
bool FileType::fileHasImage(const QUrl &file) const
{
const auto mimeType = mimeTypeForFile(file.toString());
return mimeType.isValid() && supportedImageFormats().contains(mimeType.preferredSuffix());
}
#include "moc_filetype.cpp"

View File

@@ -41,8 +41,13 @@ class FileType : public QObject
Q_PROPERTY(QStringList supportedAnimatedImageFormats READ supportedAnimatedImageFormats CONSTANT FINAL)
public:
explicit FileType(QObject *parent = nullptr);
~FileType();
static FileType &instance();
static FileType *create(QQmlEngine *engine, QJSEngine *)
{
engine->setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
return &instance();
}
/**
* @brief Returns a MIME type for nameOrAlias or an invalid one if none found.
@@ -120,7 +125,11 @@ public:
QStringList supportedImageFormats() const;
QStringList supportedAnimatedImageFormats() const;
bool fileHasImage(const QUrl &file) const;
private:
explicit FileType(QObject *parent = nullptr);
const QScopedPointer<FileTypePrivate> d_ptr;
Q_DECLARE_PRIVATE(FileType)
Q_DISABLE_COPY(FileType)

56
src/foreigntypes.h Normal file
View File

@@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once
#include <QQmlEngine>
#include <Quotient/accountregistry.h>
#include <Quotient/keyverificationsession.h>
#if __has_include("Quotient/e2ee/sssshandler.h")
#include <Quotient/e2ee/sssshandler.h>
#endif
#include "controller.h"
#include "neochatconfig.h"
struct ForeignConfig {
Q_GADGET
QML_FOREIGN(NeoChatConfig)
QML_NAMED_ELEMENT(Config)
QML_SINGLETON
public:
static NeoChatConfig *create(QQmlEngine *, QJSEngine *)
{
QQmlEngine::setObjectOwnership(NeoChatConfig::self(), QQmlEngine::CppOwnership);
return NeoChatConfig::self();
}
};
struct ForeignAccountRegistry {
Q_GADGET
QML_FOREIGN(Quotient::AccountRegistry)
QML_NAMED_ELEMENT(AccountRegistry)
QML_SINGLETON
public:
static Quotient::AccountRegistry *create(QQmlEngine *, QJSEngine *)
{
QQmlEngine::setObjectOwnership(&Controller::instance().accounts(), QQmlEngine::CppOwnership);
return &Controller::instance().accounts();
}
};
struct ForeignKeyVerificationSession {
Q_GADGET
QML_FOREIGN(Quotient::KeyVerificationSession)
QML_NAMED_ELEMENT(KeyVerificationSession)
QML_UNCREATABLE("")
};
#if __has_include("Quotient/e2ee/sssshandler.h")
struct ForeignSSSSHandler {
Q_GADGET
QML_FOREIGN(Quotient::SSSSHandler)
QML_NAMED_ELEMENT(SSSSHandler)
};
#endif

View File

@@ -8,34 +8,22 @@
#include <Quotient/events/roommessageevent.h>
#include "neochatconfig.h"
#include "neochatroom.h"
#include "neochatconnection.h"
#include "utils.h"
using namespace Quotient;
LinkPreviewer::LinkPreviewer(const NeoChatRoom *room, const Quotient::RoomMessageEvent *event, QObject *parent)
LinkPreviewer::LinkPreviewer(const QUrl &url, QObject *parent)
: QObject(parent)
, m_currentRoom(room)
, m_event(event)
, m_loaded(false)
, m_url(linkPreview(event))
, m_url(url)
{
connect(this, &LinkPreviewer::urlChanged, this, &LinkPreviewer::emptyChanged);
Q_ASSERT(dynamic_cast<Connection *>(this->parent()));
if (m_event != nullptr && m_currentRoom != nullptr) {
loadUrlPreview();
connect(m_currentRoom, &NeoChatRoom::urlPreviewEnabledChanged, this, &LinkPreviewer::loadUrlPreview);
// Make sure that we react to edits
connect(m_currentRoom, &NeoChatRoom::replacedEvent, this, [this](const Quotient::RoomEvent *newEvent) {
if (m_event->id() == newEvent->id()) {
m_event = eventCast<const Quotient::RoomMessageEvent>(newEvent);
m_url = linkPreview(m_event);
Q_EMIT urlChanged();
loadUrlPreview();
}
});
}
connect(this, &LinkPreviewer::urlChanged, this, &LinkPreviewer::emptyChanged);
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, &LinkPreviewer::loadUrlPreview);
loadUrlPreview();
}
bool LinkPreviewer::loaded() const
@@ -65,14 +53,14 @@ QUrl LinkPreviewer::url() const
void LinkPreviewer::loadUrlPreview()
{
if (!m_currentRoom || !NeoChatConfig::showLinkPreview() || !m_currentRoom->urlPreviewEnabled()) {
return;
}
if (m_url.scheme() == QStringLiteral("https")) {
m_loaded = false;
Q_EMIT loadedChanged();
auto conn = m_currentRoom->connection();
auto conn = dynamic_cast<Connection *>(this->parent());
if (conn == nullptr) {
return;
}
GetUrlPreviewJob *job = conn->callApi<GetUrlPreviewJob>(m_url);
connect(job, &BaseJob::success, this, [this, job, conn]() {

View File

@@ -53,14 +53,15 @@ class LinkPreviewer : public QObject
Q_PROPERTY(QUrl imageSource READ imageSource NOTIFY imageSourceChanged)
/**
* @brief Whether the there is a link to preview.
* @brief Whether there is a link to preview.
*
* A linkPreviwer is empty if the URL is empty.
*/
Q_PROPERTY(bool empty READ empty NOTIFY emptyChanged)
public:
explicit LinkPreviewer(const NeoChatRoom *room = nullptr, const Quotient::RoomMessageEvent *event = nullptr, QObject *parent = nullptr);
LinkPreviewer() = default;
explicit LinkPreviewer(const QUrl &url, QObject *parent = nullptr);
[[nodiscard]] QUrl url() const;
[[nodiscard]] bool loaded() const;
@@ -76,18 +77,6 @@ public:
*/
static bool hasPreviewableLinks(const Quotient::RoomMessageEvent *event);
private:
const NeoChatRoom *m_currentRoom;
const Quotient::RoomMessageEvent *m_event;
bool m_loaded;
QString m_title = QString();
QString m_description = QString();
QUrl m_imageSource = QUrl();
QUrl m_url;
void loadUrlPreview();
/**
* @brief Return the link to be previewed from the given event.
*
@@ -96,6 +85,15 @@ private:
*/
static QUrl linkPreview(const Quotient::RoomMessageEvent *event);
private:
bool m_loaded;
QString m_title = QString();
QString m_description = QString();
QUrl m_imageSource = QUrl();
QUrl m_url;
void loadUrlPreview();
Q_SIGNALS:
void loadedChanged();
void titleChanged();

23
src/login/CMakeLists.txt Normal file
View File

@@ -0,0 +1,23 @@
# SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
# SPDX-License-Identifier: BSD-2-Clause
qt_add_library(login STATIC)
qt_add_qml_module(login
URI org.kde.neochat.login
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/login
QML_FILES
WelcomePage.qml
LoginStep.qml
Captcha.qml
Email.qml
Homeserver.qml
Loading.qml
Login.qml
LoginMethod.qml
LoginRegister.qml
Password.qml
RegisterPassword.qml
Sso.qml
Terms.qml
Username.qml
)

View File

@@ -43,6 +43,6 @@ LoginStep {
}
}
previousAction: Kirigami.Action {
onTriggered: root.processed("Username.qml")
onTriggered: root.processed("Username")
}
}

View File

@@ -55,6 +55,6 @@ LoginStep {
}
}
previousAction: Kirigami.Action {
onTriggered: root.processed("Username.qml")
onTriggered: root.processed("Username")
}
}

View File

@@ -40,9 +40,9 @@ LoginStep {
nextAction: Kirigami.Action {
text: Registration.testing ? i18n("Loading") : null
enabled: Registration.status > Registration.ServerNoRegistration
onTriggered: root.processed("Username.qml")
onTriggered: root.processed("Username")
}
previousAction: Kirigami.Action {
onTriggered: root.processed("LoginRegister.qml")
onTriggered: root.processed("LoginRegister")
}
}

View File

@@ -38,18 +38,18 @@ LoginStep {
text: LoginHelper.isLoggedIn ? i18n("Already logged in") : (LoginHelper.testing && matrixIdField.acceptableInput) ? i18n("Loading…") : i18nc("@action:button", "Continue")
onTriggered: {
if (LoginHelper.supportsSso && LoginHelper.supportsPassword) {
processed("LoginMethod.qml");
processed("LoginMethod");
} else if (LoginHelper.supportsSso) {
processed("Sso.qml");
processed("Sso");
} else {
processed("Password.qml");
processed("Password");
}
}
enabled: LoginHelper.homeserverReachable
}
previousAction: Kirigami.Action {
onTriggered: {
root.processed("LoginRegister.qml");
root.processed("LoginRegister");
}
}
}

View File

@@ -18,12 +18,12 @@ LoginStep {
FormCard.FormButtonDelegate {
id: loginPasswordButton
text: i18nc("@action:button", "Login with password")
onClicked: processed("Password.qml")
onClicked: processed("Password")
}
FormCard.FormButtonDelegate {
id: loginSsoButton
text: i18nc("@action:button", "Login with single sign-on")
onClicked: processed("Sso.qml")
onClicked: processed("Sso")
}
}

View File

@@ -22,13 +22,13 @@ LoginStep {
FormCard.FormButtonDelegate {
id: loginButton
text: i18nc("@action:button", "Login")
onClicked: root.processed("Login.qml")
onClicked: root.processed("Login")
}
FormCard.FormDelegateSeparator {}
FormCard.FormButtonDelegate {
text: i18nc("@action:button", "Register")
onClicked: root.processed("Homeserver.qml")
onClicked: root.processed("Homeserver")
}
}

View File

@@ -15,7 +15,7 @@ LoginStep {
Connections {
target: LoginHelper
function onConnected() {
processed("Loading.qml");
processed("Loading");
}
}
@@ -46,6 +46,6 @@ LoginStep {
}
}
previousAction: Kirigami.Action {
onTriggered: processed("Login.qml")
onTriggered: processed("Login")
}
}

View File

@@ -47,6 +47,6 @@ LoginStep {
}
previousAction: Kirigami.Action {
onTriggered: root.processed("Username.qml")
onTriggered: root.processed("Username")
}
}

View File

@@ -22,7 +22,7 @@ LoginStep {
UrlHelper.openUrl(LoginHelper.ssoUrl);
}
function onConnected() {
processed("Loading.qml");
processed("Loading");
}
}
@@ -31,7 +31,7 @@ LoginStep {
}
previousAction: Kirigami.Action {
onTriggered: processed("Login.qml")
onTriggered: processed("Login")
}
nextAction: Kirigami.Action {

View File

@@ -33,6 +33,6 @@ LoginStep {
}
}
previousAction: Kirigami.Action {
onTriggered: root.processed("Username.qml")
onTriggered: root.processed("Username")
}
}

View File

@@ -37,11 +37,11 @@ LoginStep {
nextAction: Kirigami.Action {
text: Registration.status === Registration.TestingUsername ? i18n("Loading") : null
onTriggered: root.processed("RegisterPassword.qml")
onTriggered: root.processed("RegisterPassword")
enabled: Registration.status === Registration.Ready
}
previousAction: Kirigami.Action {
onTriggered: root.processed("Homeserver.qml")
onTriggered: root.processed("Homeserver")
}
}

View File

@@ -9,7 +9,7 @@ import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.neochat
import org.kde.neochat.accounts
import org.kde.neochat.settings
FormCard.FormCardPage {
id: root
@@ -18,7 +18,7 @@ FormCard.FormCardPage {
property bool _showExisting: showExisting && root.currentStepString === root.initialStep
property alias currentStep: module.item
property string currentStepString: initialStep
property string initialStep: "LoginRegister.qml"
property string initialStep: "LoginRegister"
signal connectionChosen
@@ -128,14 +128,14 @@ FormCard.FormCardPage {
Loader {
id: module
Layout.fillWidth: true
sourceComponent: Qt.createComponent('org.kde.neochat', root.initialStep)
sourceComponent: Qt.createComponent('org.kde.neochat.login', root.initialStep)
Connections {
id: stepConnections
target: currentStep
function onProcessed(nextStep: string): void {
module.source = nextStep;
module.source = nextStep + ".qml";
root.currentStepString = nextStep;
headerMessage.text = "";
headerMessage.visible = false;
@@ -166,16 +166,16 @@ FormCard.FormCardPage {
target: Registration
function onNextStepChanged() {
if (Registration.nextStep === "m.login.recaptcha") {
stepConnections.onProcessed("Captcha.qml");
stepConnections.onProcessed("Captcha");
}
if (Registration.nextStep === "m.login.terms") {
stepConnections.onProcessed("Terms.qml");
stepConnections.onProcessed("Terms");
}
if (Registration.nextStep === "m.login.email.identity") {
stepConnections.onProcessed("Email.qml");
stepConnections.onProcessed("Email");
}
if (Registration.nextStep === "loading") {
stepConnections.onProcessed("Loading.qml");
stepConnections.onProcessed("Loading");
}
}
}
@@ -216,7 +216,7 @@ FormCard.FormCardPage {
FormCard.FormButtonDelegate {
text: i18nc("@action:button", "Open proxy settings")
icon.name: "settings-configure"
onClicked: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "NetworkProxyPage.qml"), {}, {
onClicked: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat.settings", "NetworkProxyPage"), {}, {
title: i18nc("@title:window", "Proxy Settings")
});
}

View File

@@ -30,6 +30,11 @@
#ifdef HAVE_WINDOWSYSTEM
#include <KWindowSystem>
#endif
#if __has_include("KCrash")
#include <KCrash>
#endif
#include <KLocalizedContext>
#include <KLocalizedString>
@@ -162,6 +167,10 @@ int main(int argc, char *argv[])
KAboutData::setApplicationData(about);
QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("org.kde.neochat")));
#if __has_include("KCrash")
KCrash::initialize();
#endif
initLogging();
Connection::setEncryptionDefault(true);
@@ -223,13 +232,12 @@ int main(int argc, char *argv[])
KDBusService service(KDBusService::Unique);
#endif
Q_IMPORT_QML_PLUGIN(org_kde_neochat_settingsPlugin)
Q_IMPORT_QML_PLUGIN(org_kde_neochat_timelinePlugin)
Q_IMPORT_QML_PLUGIN(org_kde_neochat_devtoolsPlugin)
Q_IMPORT_QML_PLUGIN(org_kde_neochat_loginPlugin)
qml_register_types_org_kde_neochat();
qmlRegisterSingletonInstance("org.kde.neochat.config", 1, 0, "Config", NeoChatConfig::self());
qmlRegisterSingletonInstance("org.kde.neochat.accounts", 1, 0, "AccountRegistry", &Controller::instance().accounts());
qmlRegisterUncreatableType<KeyVerificationSession>("com.github.quotient_im.libquotient", 1, 0, "KeyVerificationSession", {});
QQmlApplicationEngine engine;
@@ -280,7 +288,7 @@ int main(int argc, char *argv[])
engine.addImageProvider(QLatin1String("mxc"), MatrixImageProvider::create(&engine, &engine));
engine.addImageProvider(QLatin1String("blurhash"), new BlurhashImageProvider);
engine.load(QUrl(QStringLiteral("qrc:/qt/qml/org/kde/neochat/qml/main.qml")));
engine.loadFromModule("org.kde.neochat", "Main");
if (engine.rootObjects().isEmpty()) {
return -1;
}

View File

@@ -7,3 +7,5 @@ void MediaManager::startPlayback()
{
Q_EMIT playbackStarted();
}
#include "moc_mediamanager.cpp"

View File

@@ -3,7 +3,11 @@
#include "accountemoticonmodel.h"
#include <QImage>
#include <QMimeDatabase>
#include <Quotient/csapi/content-repo.h>
#include <Quotient/events/eventcontent.h>
#include <qcoro/qcorosignal.h>
using namespace Quotient;
@@ -162,7 +166,15 @@ QCoro::Task<void> AccountEmoticonModel::doSetEmoticonImage(int index, QUrl sourc
co_return;
}
m_images->images[index].url = job->contentUri();
m_images->images[index].info = none;
auto mime = QMimeDatabase().mimeTypeForUrl(source);
source.setScheme("file"_ls);
QFileInfo fileInfo(source.isLocalFile() ? source.toLocalFile() : source.toString());
EventContent::ImageInfo info;
if (mime.name().startsWith("image/"_ls)) {
QImage image(source.toLocalFile());
info = EventContent::ImageInfo(source, fileInfo.size(), mime, image.size(), fileInfo.fileName());
}
m_images->images[index].info = info;
QJsonObject data;
m_images->fillJson(&data);
m_connection->setAccountData("im.ponies.user_emotes"_ls, data);
@@ -175,11 +187,21 @@ QCoro::Task<void> AccountEmoticonModel::doAddEmoticon(QUrl source, QString short
if (job->error() != BaseJob::NoError) {
co_return;
}
auto mime = QMimeDatabase().mimeTypeForUrl(source);
source.setScheme("file"_ls);
QFileInfo fileInfo(source.isLocalFile() ? source.toLocalFile() : source.toString());
EventContent::ImageInfo info;
if (mime.name().startsWith("image/"_ls)) {
QImage image(source.toLocalFile());
info = EventContent::ImageInfo(source, fileInfo.size(), mime, image.size(), fileInfo.fileName());
}
m_images->images.append(ImagePackEventContent::ImagePackImage{
shortcode,
job->contentUri(),
description,
none,
info,
QStringList{type},
});
QJsonObject data;

View File

@@ -201,7 +201,7 @@ QList<ActionsModel::Action> actions{
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("<user> is banned from this room.", "%1 is banned from this room.", text));
return QString();
}
if (room->localUser()->id() == text) {
if (room->localMember().id() == text) {
Q_EMIT room->showMessage(NeoChatRoom::Positive, i18n("You are already in this room."));
return QString();
}
@@ -430,11 +430,11 @@ QList<ActionsModel::Action> actions{
if (!plEvent) {
return QString();
}
if (plEvent->ban() > plEvent->powerLevelForUser(room->localUser()->id())) {
if (plEvent->ban() > plEvent->powerLevelForUser(room->localMember().id())) {
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to ban users from this room."));
return QString();
}
if (plEvent->powerLevelForUser(room->localUser()->id()) <= plEvent->powerLevelForUser(parts[0])) {
if (plEvent->powerLevelForUser(room->localMember().id()) <= plEvent->powerLevelForUser(parts[0])) {
Q_EMIT room->showMessage(
NeoChatRoom::Error,
i18nc("You are not allowed to ban <username> from this room.", "You are not allowed to ban %1 from this room.", parts[0]));
@@ -463,7 +463,7 @@ QList<ActionsModel::Action> actions{
if (!plEvent) {
return QString();
}
if (plEvent->ban() > plEvent->powerLevelForUser(room->localUser()->id())) {
if (plEvent->ban() > plEvent->powerLevelForUser(room->localMember().id())) {
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to unban users from this room."));
return QString();
}
@@ -494,7 +494,7 @@ QList<ActionsModel::Action> actions{
i18nc("'<text>' does not look like a matrix id.", "'%1' does not look like a matrix id.", parts[0]));
return QString();
}
if (parts[0] == room->localUser()->id()) {
if (parts[0] == room->localMember().id()) {
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You cannot kick yourself from the room."));
return QString();
}
@@ -507,11 +507,11 @@ QList<ActionsModel::Action> actions{
return QString();
}
auto kick = plEvent->kick();
if (plEvent->powerLevelForUser(room->localUser()->id()) < kick) {
if (plEvent->powerLevelForUser(room->localMember().id()) < kick) {
Q_EMIT room->showMessage(NeoChatRoom::Error, i18n("You are not allowed to kick users from this room."));
return QString();
}
if (plEvent->powerLevelForUser(room->localUser()->id()) <= plEvent->powerLevelForUser(parts[0])) {
if (plEvent->powerLevelForUser(room->localMember().id()) <= plEvent->powerLevelForUser(parts[0])) {
Q_EMIT room->showMessage(
NeoChatRoom::Error,
i18nc("You are not allowed to kick <username> from this room", "You are not allowed to kick %1 from this room.", parts[0]));

View File

@@ -91,7 +91,9 @@ QVariant CompletionModel::data(const QModelIndex &index, int role) const
if (mediaId.isEmpty()) {
return QVariant();
}
return m_room->connection()->makeMediaUrl(QUrl(QStringLiteral("mxc://%1").arg(mediaId)));
if (m_room) {
return m_room->connection()->makeMediaUrl(QUrl(QStringLiteral("mxc://%1").arg(mediaId)));
}
}
}
if (m_autoCompletionType == Emoji) {

View File

@@ -118,7 +118,7 @@ private:
QString m_text;
QString m_fullText;
CompletionProxyModel *m_filterModel;
NeoChatRoom *m_room = nullptr;
QPointer<NeoChatRoom> m_room;
AutoCompletionType m_autoCompletionType = None;
void updateCompletion();

View File

@@ -161,7 +161,7 @@ QVariant CustomEmojiModel::data(const QModelIndex &idx, int role) const
case Roles::ImageURL:
return QUrl(QStringLiteral("image://mxc/") + data.url.mid(6));
case Roles::MxcUrl:
return data.url.mid(6);
return m_connection->makeMediaUrl(QUrl(data.url));
default:
return {};
}

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