Compare commits
51 Commits
work/jz/fl
...
work/adapt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cc68d2658 | ||
|
|
f4b87caefe | ||
|
|
07a9497f4c | ||
|
|
35f1ace458 | ||
|
|
11dd0ee151 | ||
|
|
07e200c74f | ||
|
|
ee405fbff6 | ||
|
|
c87c6fbabb | ||
|
|
096b36b89b | ||
|
|
c3db90d2e3 | ||
|
|
b7df10aa45 | ||
|
|
fea5e02e7d | ||
|
|
cb94261727 | ||
|
|
efac7e4860 | ||
|
|
0f6fd3adee | ||
|
|
e5e0405401 | ||
|
|
bb35e9ce15 | ||
|
|
5881db4e55 | ||
|
|
89f7167b08 | ||
|
|
3b39fcff84 | ||
|
|
0daf45a465 | ||
|
|
c0d7b96e79 | ||
|
|
a247e40865 | ||
|
|
c3e7a99bca | ||
|
|
9080d8be6a | ||
|
|
b7ee83f6b6 | ||
|
|
1e24bde9a9 | ||
|
|
ba82df1152 | ||
|
|
8980fe7838 | ||
|
|
ef34ed7c20 | ||
|
|
17d60b79ca | ||
|
|
ab0a32c339 | ||
|
|
697778df8d | ||
|
|
55caf84b94 | ||
|
|
335c012f1b | ||
|
|
3c4c538de8 | ||
|
|
c344a3ee55 | ||
|
|
d2695947ed | ||
|
|
21beeef920 | ||
|
|
a4630a53fa | ||
|
|
f5aef8d0c3 | ||
|
|
e044e66030 | ||
|
|
88bfacd386 | ||
|
|
c61c73088f | ||
|
|
2887263f26 | ||
|
|
72b90bdf5c | ||
|
|
163b02f023 | ||
|
|
1a96899336 | ||
|
|
554c086aba | ||
|
|
1fad9bf7db | ||
|
|
22d922e451 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,3 +12,4 @@ kate.project.ctags.*
|
||||
.idea/
|
||||
cmake-build-*
|
||||
src/res.generated.qrc
|
||||
.qmlls.ini
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -84,7 +84,7 @@ 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
|
||||
|
||||
48
README.md
48
README.md
@@ -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/).
|
||||
|
||||

|
||||
|
||||
## 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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -394,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>
|
||||
|
||||
1022
po/ar/neochat.po
1022
po/ar/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1051
po/az/neochat.po
1051
po/az/neochat.po
File diff suppressed because it is too large
Load Diff
1015
po/ca/neochat.po
1015
po/ca/neochat.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1008
po/cs/neochat.po
1008
po/cs/neochat.po
File diff suppressed because it is too large
Load Diff
1037
po/da/neochat.po
1037
po/da/neochat.po
File diff suppressed because it is too large
Load Diff
1025
po/de/neochat.po
1025
po/de/neochat.po
File diff suppressed because it is too large
Load Diff
1022
po/el/neochat.po
1022
po/el/neochat.po
File diff suppressed because it is too large
Load Diff
1024
po/en_GB/neochat.po
1024
po/en_GB/neochat.po
File diff suppressed because it is too large
Load Diff
1015
po/eo/neochat.po
1015
po/eo/neochat.po
File diff suppressed because it is too large
Load Diff
1016
po/es/neochat.po
1016
po/es/neochat.po
File diff suppressed because it is too large
Load Diff
1020
po/eu/neochat.po
1020
po/eu/neochat.po
File diff suppressed because it is too large
Load Diff
1022
po/fi/neochat.po
1022
po/fi/neochat.po
File diff suppressed because it is too large
Load Diff
990
po/fr/neochat.po
990
po/fr/neochat.po
File diff suppressed because it is too large
Load Diff
1021
po/hu/neochat.po
1021
po/hu/neochat.po
File diff suppressed because it is too large
Load Diff
1020
po/ia/neochat.po
1020
po/ia/neochat.po
File diff suppressed because it is too large
Load Diff
1024
po/id/neochat.po
1024
po/id/neochat.po
File diff suppressed because it is too large
Load Diff
999
po/ie/neochat.po
999
po/ie/neochat.po
File diff suppressed because it is too large
Load Diff
1021
po/it/neochat.po
1021
po/it/neochat.po
File diff suppressed because it is too large
Load Diff
990
po/ja/neochat.po
990
po/ja/neochat.po
File diff suppressed because it is too large
Load Diff
1018
po/ka/neochat.po
1018
po/ka/neochat.po
File diff suppressed because it is too large
Load Diff
1021
po/ko/neochat.po
1021
po/ko/neochat.po
File diff suppressed because it is too large
Load Diff
990
po/lt/neochat.po
990
po/lt/neochat.po
File diff suppressed because it is too large
Load Diff
1015
po/lv/neochat.po
1015
po/lv/neochat.po
File diff suppressed because it is too large
Load Diff
1020
po/nl/neochat.po
1020
po/nl/neochat.po
File diff suppressed because it is too large
Load Diff
1001
po/nn/neochat.po
1001
po/nn/neochat.po
File diff suppressed because it is too large
Load Diff
1053
po/pa/neochat.po
1053
po/pa/neochat.po
File diff suppressed because it is too large
Load Diff
1017
po/pl/neochat.po
1017
po/pl/neochat.po
File diff suppressed because it is too large
Load Diff
1022
po/pt/neochat.po
1022
po/pt/neochat.po
File diff suppressed because it is too large
Load Diff
1053
po/pt_BR/neochat.po
1053
po/pt_BR/neochat.po
File diff suppressed because it is too large
Load Diff
1012
po/ru/neochat.po
1012
po/ru/neochat.po
File diff suppressed because it is too large
Load Diff
1049
po/sk/neochat.po
1049
po/sk/neochat.po
File diff suppressed because it is too large
Load Diff
1011
po/sl/neochat.po
1011
po/sl/neochat.po
File diff suppressed because it is too large
Load Diff
1008
po/sv/neochat.po
1008
po/sv/neochat.po
File diff suppressed because it is too large
Load Diff
1047
po/ta/neochat.po
1047
po/ta/neochat.po
File diff suppressed because it is too large
Load Diff
1036
po/tok/neochat.po
1036
po/tok/neochat.po
File diff suppressed because it is too large
Load Diff
1013
po/tr/neochat.po
1013
po/tr/neochat.po
File diff suppressed because it is too large
Load Diff
1018
po/uk/neochat.po
1018
po/uk/neochat.po
File diff suppressed because it is too large
Load Diff
1024
po/zh_CN/neochat.po
1024
po/zh_CN/neochat.po
File diff suppressed because it is too large
Load Diff
1086
po/zh_TW/neochat.po
1086
po/zh_TW/neochat.po
File diff suppressed because it is too large
Load Diff
@@ -174,6 +174,9 @@ add_library(neochat STATIC
|
||||
sharehandler.h
|
||||
models/roomtreeitem.cpp
|
||||
models/roomtreeitem.h
|
||||
foreigntypes.h
|
||||
models/threepidmodel.cpp
|
||||
models/threepidmodel.h
|
||||
)
|
||||
|
||||
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||
@@ -183,7 +186,7 @@ set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
|
||||
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
|
||||
@@ -200,7 +203,6 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/ExplorerDelegate.qml
|
||||
qml/InviteUserPage.qml
|
||||
qml/ImageEditorPage.qml
|
||||
qml/WelcomePage.qml
|
||||
qml/NeochatMaximizeComponent.qml
|
||||
qml/FancyEffectsContainer.qml
|
||||
qml/TypingPane.qml
|
||||
@@ -213,19 +215,6 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/PieProgressBar.qml
|
||||
qml/QuickFormatBar.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
|
||||
@@ -291,6 +280,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/AccountSwitchDialog.qml
|
||||
qml/ConfirmLeaveDialog.qml
|
||||
qml/CodeMaximizeComponent.qml
|
||||
qml/EditStateDialog.qml
|
||||
RESOURCES
|
||||
qml/confetti.png
|
||||
qml/glowdot.png
|
||||
@@ -299,6 +289,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
add_subdirectory(settings)
|
||||
add_subdirectory(timeline)
|
||||
add_subdirectory(devtools)
|
||||
add_subdirectory(login)
|
||||
|
||||
if(UNIX)
|
||||
qt_target_qml_sources(neochat QML_FILES qml/ShareAction.qml)
|
||||
@@ -390,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 settingsplugin timelineplugin devtoolsplugin)
|
||||
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin devtoolsplugin loginplugin)
|
||||
target_link_libraries(neochat PUBLIC
|
||||
Qt::Core
|
||||
Qt::Quick
|
||||
@@ -414,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)
|
||||
|
||||
@@ -91,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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ void Controller::addConnection(NeoChatConnection *c)
|
||||
});
|
||||
connect(c, &NeoChatConnection::loggedOut, this, [this, c] {
|
||||
if (accounts().count() > 1) {
|
||||
// Only set the connection if the the account being logged out is currently active
|
||||
// Only set the connection if the account being logged out is currently active
|
||||
if (c == activeConnection()) {
|
||||
setActiveConnection(dynamic_cast<NeoChatConnection *>(accounts().accounts()[0]));
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -8,6 +8,7 @@ qt_add_qml_module(devtools
|
||||
QML_FILES
|
||||
DevtoolsPage.qml
|
||||
AccountData.qml
|
||||
DebugOptions.qml
|
||||
FeatureFlagPage.qml
|
||||
RoomData.qml
|
||||
ServerData.qml
|
||||
|
||||
27
src/devtools/DebugOptions.qml
Normal file
27
src/devtools/DebugOptions.qml
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -7,7 +7,7 @@ import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat.config
|
||||
import org.kde.neochat
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
|
||||
@@ -46,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"),
|
||||
@@ -74,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
|
||||
}, {
|
||||
@@ -91,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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(" "));
|
||||
@@ -963,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;
|
||||
}
|
||||
|
||||
@@ -979,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) {
|
||||
@@ -1008,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);
|
||||
@@ -1029,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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -89,6 +89,4 @@ public:
|
||||
QUO_EVENT(ImagePackEvent, "im.ponies.room_emotes")
|
||||
using KeyedStateEventBase::KeyedStateEventBase;
|
||||
};
|
||||
|
||||
REGISTER_EVENT_TYPE(ImagePackEvent)
|
||||
}
|
||||
|
||||
@@ -40,5 +40,4 @@ public:
|
||||
*/
|
||||
QJsonArray allow() const;
|
||||
};
|
||||
REGISTER_EVENT_TYPE(JoinRulesEvent)
|
||||
}
|
||||
|
||||
56
src/foreigntypes.h
Normal file
56
src/foreigntypes.h
Normal 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
|
||||
@@ -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]() {
|
||||
|
||||
@@ -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
23
src/login/CMakeLists.txt
Normal 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
|
||||
)
|
||||
@@ -43,6 +43,6 @@ LoginStep {
|
||||
}
|
||||
}
|
||||
previousAction: Kirigami.Action {
|
||||
onTriggered: root.processed("Username.qml")
|
||||
onTriggered: root.processed("Username")
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,6 @@ LoginStep {
|
||||
}
|
||||
}
|
||||
previousAction: Kirigami.Action {
|
||||
onTriggered: root.processed("Username.qml")
|
||||
onTriggered: root.processed("Username")
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,6 @@ LoginStep {
|
||||
}
|
||||
|
||||
previousAction: Kirigami.Action {
|
||||
onTriggered: root.processed("Username.qml")
|
||||
onTriggered: root.processed("Username")
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -33,6 +33,6 @@ LoginStep {
|
||||
}
|
||||
}
|
||||
previousAction: Kirigami.Action {
|
||||
onTriggered: root.processed("Username.qml")
|
||||
onTriggered: root.processed("Username")
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import org.kde.kirigamiaddons.formcard as FormCard
|
||||
|
||||
import org.kde.neochat
|
||||
import org.kde.neochat.settings
|
||||
import org.kde.neochat.accounts
|
||||
|
||||
FormCard.FormCardPage {
|
||||
id: root
|
||||
@@ -19,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
|
||||
|
||||
@@ -129,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;
|
||||
@@ -167,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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,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.settings", "NetworkProxyPage.qml"), {}, {
|
||||
onClicked: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat.settings", "NetworkProxyPage"), {}, {
|
||||
title: i18nc("@title:window", "Proxy Settings")
|
||||
});
|
||||
}
|
||||
24
src/main.cpp
24
src/main.cpp
@@ -30,16 +30,16 @@
|
||||
#ifdef HAVE_WINDOWSYSTEM
|
||||
#include <KWindowSystem>
|
||||
#endif
|
||||
|
||||
#if __has_include("KCrash")
|
||||
#include <KCrash>
|
||||
#endif
|
||||
|
||||
#include <KLocalizedContext>
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include "neochat-version.h"
|
||||
|
||||
#include <Quotient/accountregistry.h>
|
||||
#if __has_include("Quotient/e2ee/sssshandler.h")
|
||||
#include <Quotient/e2ee/sssshandler.h>
|
||||
#endif
|
||||
#include <Quotient/keyverificationsession.h>
|
||||
#include <Quotient/networkaccessmanager.h>
|
||||
|
||||
#include "blurhashimageprovider.h"
|
||||
@@ -167,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);
|
||||
@@ -231,15 +235,9 @@ int main(int argc, char *argv[])
|
||||
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", {});
|
||||
#if __has_include("Quotient/e2ee/sssshandler.h")
|
||||
qmlRegisterType<SSSSHandler>("com.github.quotient_im.libquotient", 1, 0, "SSSSHandler");
|
||||
#endif
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
@@ -290,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;
|
||||
}
|
||||
|
||||
@@ -7,3 +7,5 @@ void MediaManager::startPlayback()
|
||||
{
|
||||
Q_EMIT playbackStarted();
|
||||
}
|
||||
|
||||
#include "moc_mediamanager.cpp"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]));
|
||||
|
||||
@@ -208,3 +208,5 @@ void ItineraryModel::sendToItinerary()
|
||||
job->start();
|
||||
#endif
|
||||
}
|
||||
|
||||
#include "moc_itinerarymodel.cpp"
|
||||
|
||||
@@ -62,3 +62,5 @@ void LineModel::resetModel()
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
#include "moc_linemodel.cpp"
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
#include "messagecontentmodel.h"
|
||||
#include "neochatconfig.h"
|
||||
|
||||
#include <QImageReader>
|
||||
|
||||
#include <Quotient/events/redactionevent.h>
|
||||
#include <Quotient/events/roommessageevent.h>
|
||||
#include <Quotient/events/stickerevent.h>
|
||||
#include <Quotient/room.h>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
@@ -23,9 +23,12 @@
|
||||
#include "filetype.h"
|
||||
#include "itinerarymodel.h"
|
||||
#include "linkpreviewer.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroom.h"
|
||||
#include "texthandler.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
MessageContentModel::MessageContentModel(const Quotient::RoomEvent *event, NeoChatRoom *room)
|
||||
: QAbstractListModel(nullptr)
|
||||
, m_room(room)
|
||||
@@ -76,6 +79,21 @@ MessageContentModel::MessageContentModel(const Quotient::RoomEvent *event, NeoCh
|
||||
if (m_event != nullptr && eventId == m_event->id()) {
|
||||
updateComponents();
|
||||
Q_EMIT dataChanged(index(0), index(rowCount() - 1), {FileTransferInfoRole});
|
||||
|
||||
QString mxcUrl;
|
||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
if (event->hasFileContent()) {
|
||||
mxcUrl = event->content()->fileInfo()->url().toString();
|
||||
}
|
||||
} else if (auto event = eventCast<const Quotient::StickerEvent>(m_event)) {
|
||||
mxcUrl = event->image().fileInfo()->url().toString();
|
||||
}
|
||||
if (mxcUrl.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
auto localPath = m_room->fileTransferInfo(m_event->id()).localPath.toLocalFile();
|
||||
auto config = KSharedConfig::openStateConfig(QStringLiteral("neochatdownloads"))->group(QStringLiteral("downloads"));
|
||||
config.writePathEntry(mxcUrl.mid(6), localPath);
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::fileTransferFailed, this, [this](const QString &eventId) {
|
||||
@@ -92,23 +110,11 @@ MessageContentModel::MessageContentModel(const Quotient::RoomEvent *event, NeoCh
|
||||
endResetModel();
|
||||
}
|
||||
});
|
||||
connect(m_room, &NeoChatRoom::urlPreviewEnabledChanged, this, &MessageContentModel::updateLinkPreviewer);
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, &MessageContentModel::updateLinkPreviewer);
|
||||
}
|
||||
|
||||
if (const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
if (LinkPreviewer::hasPreviewableLinks(event)) {
|
||||
m_linkPreviewer = new LinkPreviewer(m_room, event, this);
|
||||
|
||||
connect(m_linkPreviewer, &LinkPreviewer::loadedChanged, [this]() {
|
||||
if (m_linkPreviewer->loaded()) {
|
||||
// HACK: Because DelegateChooser can't switch the delegate on dataChanged it has to think there is a new delegate.
|
||||
beginResetModel();
|
||||
m_components[m_components.size() - 1].type = MessageComponentType::LinkPreview;
|
||||
endResetModel();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateLinkPreviewer();
|
||||
updateComponents();
|
||||
}
|
||||
|
||||
@@ -155,14 +161,7 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
|
||||
return eventHandler.getMediaInfo();
|
||||
}
|
||||
if (role == FileTransferInfoRole) {
|
||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
if (event->hasFileContent()) {
|
||||
return QVariant::fromValue(m_room->fileTransferInfo(event->id()));
|
||||
}
|
||||
}
|
||||
if (auto event = eventCast<const Quotient::StickerEvent>(m_event)) {
|
||||
return QVariant::fromValue(m_room->fileTransferInfo(event->id()));
|
||||
}
|
||||
return QVariant::fromValue(fileInfo());
|
||||
}
|
||||
if (role == ItineraryModelRole) {
|
||||
return QVariant::fromValue<ItineraryModel *>(m_itineraryModel);
|
||||
@@ -270,15 +269,7 @@ void MessageContentModel::updateComponents(bool isEditing)
|
||||
} else if (eventHandler.messageComponentType() == MessageComponentType::File) {
|
||||
m_components += MessageComponent{MessageComponentType::File, QString(), {}};
|
||||
if (m_emptyItinerary) {
|
||||
Quotient::FileTransferInfo fileTransferInfo;
|
||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
if (event->hasFileContent()) {
|
||||
fileTransferInfo = m_room->fileTransferInfo(event->id());
|
||||
}
|
||||
}
|
||||
if (auto event = eventCast<const Quotient::StickerEvent>(m_event)) {
|
||||
fileTransferInfo = m_room->fileTransferInfo(event->id());
|
||||
}
|
||||
auto fileTransferInfo = fileInfo();
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
KSyntaxHighlighting::Repository repository;
|
||||
@@ -319,6 +310,44 @@ void MessageContentModel::updateComponents(bool isEditing)
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void MessageContentModel::updateLinkPreviewer()
|
||||
{
|
||||
if (m_room == nullptr || m_event == nullptr) {
|
||||
if (m_linkPreviewer != nullptr) {
|
||||
m_linkPreviewer->disconnect(this);
|
||||
m_linkPreviewer = nullptr;
|
||||
updateComponents();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!m_room->urlPreviewEnabled()) {
|
||||
if (m_linkPreviewer != nullptr) {
|
||||
m_linkPreviewer->disconnect(this);
|
||||
m_linkPreviewer = nullptr;
|
||||
updateComponents();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
if (LinkPreviewer::hasPreviewableLinks(event)) {
|
||||
m_linkPreviewer = dynamic_cast<NeoChatConnection *>(m_room->connection())->previewerForLink(LinkPreviewer::linkPreview(event));
|
||||
updateComponents();
|
||||
|
||||
if (m_linkPreviewer != nullptr) {
|
||||
connect(m_linkPreviewer, &LinkPreviewer::loadedChanged, [this]() {
|
||||
if (m_linkPreviewer != nullptr && m_linkPreviewer->loaded()) {
|
||||
// HACK: Because DelegateChooser can't switch the delegate on dataChanged it has to think there is a new delegate.
|
||||
beginResetModel();
|
||||
m_components[m_components.size() - 1].type = MessageComponentType::LinkPreview;
|
||||
endResetModel();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessageContentModel::updateItineraryModel()
|
||||
{
|
||||
if (m_room == nullptr || m_event == nullptr) {
|
||||
@@ -327,7 +356,7 @@ void MessageContentModel::updateItineraryModel()
|
||||
|
||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
if (event->hasFileContent()) {
|
||||
auto filePath = m_room->fileTransferInfo(event->id()).localPath;
|
||||
auto filePath = fileInfo().localPath;
|
||||
if (filePath.isEmpty() && m_itineraryModel != nullptr) {
|
||||
delete m_itineraryModel;
|
||||
m_itineraryModel = nullptr;
|
||||
@@ -354,3 +383,43 @@ void MessageContentModel::updateItineraryModel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileTransferInfo MessageContentModel::fileInfo() const
|
||||
{
|
||||
if (m_room == nullptr || m_event == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QString mxcUrl;
|
||||
int total;
|
||||
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
|
||||
if (event->hasFileContent()) {
|
||||
mxcUrl = event->content()->fileInfo()->url().toString();
|
||||
total = event->content()->fileInfo()->payloadSize;
|
||||
}
|
||||
} else if (auto event = eventCast<const Quotient::StickerEvent>(m_event)) {
|
||||
mxcUrl = event->image().fileInfo()->url().toString();
|
||||
total = event->image().fileInfo()->payloadSize;
|
||||
}
|
||||
auto config = KSharedConfig::openStateConfig(QStringLiteral("neochatdownloads"))->group(QStringLiteral("downloads"));
|
||||
if (!config.hasKey(mxcUrl.mid(6))) {
|
||||
return m_room->fileTransferInfo(m_event->id());
|
||||
}
|
||||
const auto path = config.readPathEntry(mxcUrl.mid(6), QString());
|
||||
QFileInfo info(path);
|
||||
if (!info.isFile()) {
|
||||
config.deleteEntry(mxcUrl);
|
||||
return m_room->fileTransferInfo(m_event->id());
|
||||
}
|
||||
// TODO: we could check the hash here
|
||||
return FileTransferInfo{
|
||||
.status = FileTransferInfo::Completed,
|
||||
.isUpload = false,
|
||||
.progress = total,
|
||||
.total = total,
|
||||
.localDir = QUrl(info.dir().path()),
|
||||
.localPath = QUrl::fromLocalFile(path),
|
||||
};
|
||||
}
|
||||
|
||||
#include "moc_messagecontentmodel.cpp"
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <QAbstractListModel>
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include <Quotient/room.h>
|
||||
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "eventhandler.h"
|
||||
#include "itinerarymodel.h"
|
||||
@@ -93,9 +95,12 @@ private:
|
||||
QList<MessageComponent> m_components;
|
||||
void updateComponents(bool isEditing = false);
|
||||
|
||||
LinkPreviewer *m_linkPreviewer = nullptr;
|
||||
QPointer<LinkPreviewer> m_linkPreviewer;
|
||||
ItineraryModel *m_itineraryModel = nullptr;
|
||||
|
||||
void updateLinkPreviewer();
|
||||
void updateItineraryModel();
|
||||
bool m_emptyItinerary = false;
|
||||
|
||||
Quotient::FileTransferInfo fileInfo() const;
|
||||
};
|
||||
|
||||
@@ -251,7 +251,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
});
|
||||
qCDebug(MessageEvent) << "Connected to room" << room->id() << "as" << room->localUser()->id();
|
||||
qCDebug(MessageEvent) << "Connected to room" << room->id() << "as" << room->localMember().id();
|
||||
} else {
|
||||
lastReadEventId.clear();
|
||||
}
|
||||
@@ -621,7 +621,7 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
if (role == IsEditableRole) {
|
||||
return eventHandler.messageComponentType() == MessageComponentType::Text && evt.senderId() == m_currentRoom->localUser()->id();
|
||||
return eventHandler.messageComponentType() == MessageComponentType::Text && evt.senderId() == m_currentRoom->localMember().id();
|
||||
}
|
||||
|
||||
return {};
|
||||
|
||||
@@ -162,3 +162,5 @@ QString NotificationsModel::nextToken() const
|
||||
{
|
||||
return m_nextToken;
|
||||
}
|
||||
|
||||
#include "moc_notificationsmodel.cpp"
|
||||
|
||||
@@ -90,7 +90,7 @@ QVariant ReactionModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
if (role == HasLocalUser) {
|
||||
for (auto author : reaction.authors) {
|
||||
if (author.toMap()[QStringLiteral("id")] == m_room->localUser()->id()) {
|
||||
if (author.toMap()[QStringLiteral("id")] == m_room->localMember().id()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "neochatconfig.h"
|
||||
#include "neochatconnection.h"
|
||||
#include "neochatroomtype.h"
|
||||
#include "roommanager.h"
|
||||
#include "roomtreemodel.h"
|
||||
#include "spacehierarchycache.h"
|
||||
|
||||
@@ -32,6 +33,12 @@ SortFilterRoomTreeModel::SortFilterRoomTreeModel(RoomTreeModel *sourceModel, QOb
|
||||
});
|
||||
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::CollapsedChanged, this, &SortFilterRoomTreeModel::invalidateFilter);
|
||||
connect(NeoChatConfig::self(), &NeoChatConfig::AllRoomsInHomeChanged, this, [this]() {
|
||||
invalidateFilter();
|
||||
if (NeoChatConfig::self()->allRoomsInHome()) {
|
||||
RoomManager::instance().resetState();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SortFilterRoomTreeModel::setRoomSortOrder(SortFilterRoomTreeModel::RoomSortOrder sortOrder)
|
||||
@@ -154,6 +161,11 @@ bool SortFilterRoomTreeModel::filterAcceptsRow(int source_row, const QModelIndex
|
||||
return false;
|
||||
}
|
||||
|
||||
static auto config = NeoChatConfig::self();
|
||||
if (config->allRoomsInHome() && RoomManager::instance().currentSpace().isEmpty()) {
|
||||
return acceptRoom;
|
||||
}
|
||||
|
||||
if (m_activeSpaceId.isEmpty()) {
|
||||
if (!SpaceHierarchyCache::instance().isChild(sourceModel()->data(index, RoomTreeModel::RoomIdRole).toString())) {
|
||||
return acceptRoom;
|
||||
|
||||
@@ -95,7 +95,11 @@ void SpaceChildrenModel::refreshModel()
|
||||
});
|
||||
}
|
||||
|
||||
#if Quotient_VERSION_MINOR >= 9
|
||||
void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::SpaceHierarchyRoomsChunk> children, const QModelIndex &parent)
|
||||
#else
|
||||
void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::ChildRoomsChunk> children, const QModelIndex &parent)
|
||||
#endif
|
||||
{
|
||||
SpaceTreeItem *parentItem = getItem(parent);
|
||||
|
||||
|
||||
@@ -143,5 +143,10 @@ private:
|
||||
SpaceTreeItem *getItem(const QModelIndex &index) const;
|
||||
|
||||
void refreshModel();
|
||||
|
||||
#if Quotient_VERSION_MINOR >= 9
|
||||
void insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::SpaceHierarchyRoomsChunk> children, const QModelIndex &parent = QModelIndex());
|
||||
#else
|
||||
void insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::ChildRoomsChunk> children, const QModelIndex &parent = QModelIndex());
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -77,12 +77,14 @@ void StateKeysModel::setEventType(const QString &eventType)
|
||||
loadState();
|
||||
}
|
||||
|
||||
QByteArray StateKeysModel::stateEventJson(const QModelIndex &index)
|
||||
QByteArray StateKeysModel::stateEventJson(const QString &type, const QString &stateKey)
|
||||
{
|
||||
const auto row = index.row();
|
||||
const auto event = m_stateKeys[row];
|
||||
const auto json = event->fullJson();
|
||||
return QJsonDocument(json).toJson();
|
||||
return QJsonDocument(m_room->currentState().get(type, stateKey)->fullJson()).toJson();
|
||||
}
|
||||
|
||||
QByteArray StateKeysModel::stateEventContentJson(const QString &type, const QString &stateKey)
|
||||
{
|
||||
return QJsonDocument(m_room->currentState().get(type, stateKey)->contentJson()).toJson();
|
||||
}
|
||||
|
||||
#include "moc_statekeysmodel.cpp"
|
||||
|
||||
@@ -69,7 +69,12 @@ public:
|
||||
/**
|
||||
* @brief Get the full JSON for an event.
|
||||
*/
|
||||
Q_INVOKABLE QByteArray stateEventJson(const QModelIndex &index);
|
||||
Q_INVOKABLE QByteArray stateEventJson(const QString &type, const QString &stateKey);
|
||||
|
||||
/**
|
||||
* @brief Get the content JSON for an event.
|
||||
*/
|
||||
Q_INVOKABLE QByteArray stateEventContentJson(const QString &type, const QString &stateKey);
|
||||
|
||||
Q_SIGNALS:
|
||||
void roomChanged();
|
||||
|
||||
@@ -10,7 +10,11 @@ StateModel::StateModel(QObject *parent)
|
||||
|
||||
QHash<int, QByteArray> StateModel::roleNames() const
|
||||
{
|
||||
return {{TypeRole, "type"}, {EventCountRole, "eventCount"}};
|
||||
return {
|
||||
{TypeRole, "type"},
|
||||
{EventCountRole, "eventCount"},
|
||||
{StateKeyRole, "stateKey"},
|
||||
};
|
||||
}
|
||||
QVariant StateModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
@@ -20,6 +24,8 @@ QVariant StateModel::data(const QModelIndex &index, int role) const
|
||||
return m_stateEvents.keys()[row];
|
||||
case EventCountRole:
|
||||
return m_stateEvents.values()[row].count();
|
||||
case StateKeyRole:
|
||||
return m_stateEvents.values()[row][0];
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -63,14 +69,14 @@ void StateModel::setRoom(NeoChatRoom *room)
|
||||
});
|
||||
}
|
||||
|
||||
QByteArray StateModel::stateEventJson(const QModelIndex &index)
|
||||
QByteArray StateModel::stateEventJson(const QString &type, const QString &stateKey)
|
||||
{
|
||||
auto row = index.row();
|
||||
const auto type = m_stateEvents.keys()[row];
|
||||
const auto stateKey = m_stateEvents.values()[row][0];
|
||||
const auto event = m_room->currentState().get(type, stateKey);
|
||||
return QJsonDocument(m_room->currentState().get(type, stateKey)->fullJson()).toJson();
|
||||
}
|
||||
|
||||
return QJsonDocument(event->fullJson()).toJson();
|
||||
QByteArray StateModel::stateEventContentJson(const QString &type, const QString &stateKey)
|
||||
{
|
||||
return QJsonDocument(m_room->currentState().get(type, stateKey)->contentJson()).toJson();
|
||||
}
|
||||
|
||||
#include "moc_statemodel.cpp"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user