Compare commits

..

1 Commits

Author SHA1 Message Date
Justin Zobel
ae155805e9 CI - Flatpak - Update Runtime/SDK to 6.7 2024-04-07 22:49:04 +00:00
225 changed files with 25025 additions and 30628 deletions

View File

@@ -2,7 +2,7 @@
"id": "org.kde.neochat",
"branch": "master",
"runtime": "org.kde.Platform",
"runtime-version": "6.6",
"runtime-version": "6.7",
"sdk": "org.kde.Sdk",
"command": "neochat",
"tags": [
@@ -110,7 +110,7 @@
{
"type": "git",
"url": "https://github.com/quotient-im/libQuotient.git",
"branch": "0.8.x",
"branch": "dev",
"disable-submodules": true
}
],

1
.gitignore vendored
View File

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

View File

@@ -28,7 +28,6 @@ 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

@@ -9,7 +9,7 @@ cmake_minimum_required(VERSION 3.16)
# KDE Applications version, managed by release script.
set(RELEASE_SERVICE_VERSION_MAJOR "24")
set(RELEASE_SERVICE_VERSION_MINOR "04")
set(RELEASE_SERVICE_VERSION_MICRO "80")
set(RELEASE_SERVICE_VERSION_MICRO "70")
set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
project(NeoChat VERSION ${RELEASE_SERVICE_VERSION})
@@ -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 Crash)
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS QQC2DesktopStyle KIO WindowSystem StatusNotifierItem)
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-2024 Tobias Fella <tobias.fella@kde.org>
SPDX-FileCopyrightText: 2020-2021 Tobias Fella <tobias.fella@kde.org>
SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
SPDX-License-Identifier: CC0-1.0
-->
@@ -16,18 +16,19 @@ A Qt/QML based Matrix client.
## Introduction
NeoChat is a client for [Matrix](https://matrix.org), the decentralized communication protocol for instant
messaging.
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.
NeoChat is based on KDE frameworks and as [libQuotient](https://github.com/quotient-im/libQuotient), a
NeoChat also make use of other KDE Frameworks as well 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 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.
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.
Due to the nature of the Matrix specification development NeoChat also supports numerous unstable features. Currently these are:
- Polls - MSC3381
@@ -38,9 +39,26 @@ 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).
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).
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.
## Building NeoChat
@@ -51,18 +69,14 @@ 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 [appiumtests](appiumtests).
Tests are in the repository under [autotests](autotests) and should all pass for any contribution.
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)
@@ -86,9 +100,9 @@ The best place to reach the maintainers is on the KDE Matrix instance in the Neo
## Acknowledgement
NeoChat utilizes [libQuotient](https://github.com/quotient-im/libQuotient/) as its Matrix SDK.
This program utilizes [libQuotient](https://github.com/quotient-im/libQuotient/) as its Matrix SDK.
NeoChat is a fork of [Spectral](https://gitlab.com/spectral-im/spectral/).
This program is a fork of [Spectral](https://gitlab.com/spectral-im/spectral/).
## License

View File

@@ -39,10 +39,6 @@ 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 two percentages are
* We expect a default return except in the case where the the two percentages are
* equal as that case can be calculated without dividing by zero.
*/
void DelegateSizeHelperTest::equalBreakpoint()

View File

@@ -32,6 +32,8 @@ private Q_SLOTS:
void linkPreviewsReject_data();
void linkPreviewsReject();
void editedLink();
};
void LinkPreviewerTest::initTestCase()
@@ -57,7 +59,7 @@ void LinkPreviewerTest::linkPreviewsMatch()
QFETCH(QUrl, testOutputLink);
auto event = TestUtils::loadEventFromFile<RoomMessageEvent>(eventSource);
auto linkPreviewer = LinkPreviewer(LinkPreviewer::linkPreview(event.get()), connection);
auto linkPreviewer = LinkPreviewer(room, event.get());
QCOMPARE(linkPreviewer.empty(), false);
QCOMPARE(linkPreviewer.url(), testOutputLink);
@@ -77,7 +79,22 @@ void LinkPreviewerTest::linkPreviewsReject()
QFETCH(QString, eventSource);
auto event = TestUtils::loadEventFromFile<RoomMessageEvent>(eventSource);
auto linkPreviewer = LinkPreviewer(LinkPreviewer::linkPreview(event.get()), connection);
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"));
QCOMPARE(linkPreviewer.empty(), true);
QCOMPARE(linkPreviewer.url(), QUrl());

View File

@@ -394,7 +394,6 @@
<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>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -174,9 +174,6 @@ 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
@@ -186,7 +183,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
@@ -203,7 +200,9 @@ 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
qml/QuickSwitcher.qml
qml/HoverActions.qml
@@ -214,6 +213,19 @@ 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
@@ -279,13 +291,14 @@ 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
)
add_subdirectory(settings)
add_subdirectory(timeline)
add_subdirectory(devtools)
add_subdirectory(login)
if(UNIX)
qt_target_qml_sources(neochat QML_FILES qml/ShareAction.qml)
@@ -377,7 +390,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 loginplugin)
target_link_libraries(neochat PRIVATE settingsplugin timelineplugin devtoolsplugin)
target_link_libraries(neochat PUBLIC
Qt::Core
Qt::Quick
@@ -401,10 +414,6 @@ 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)

View File

@@ -14,7 +14,6 @@
#include "models/actionsmodel.h"
#include "neochatconfig.h"
#include "texthandler.h"
#include "utils.h"
using namespace Quotient;
@@ -146,26 +145,6 @@ void ActionsHandler::handleMessage(const QString &text, QString handledText, Cha
return;
}
// We want to add back the <mx-reply> if it's in the original message but not in the edit, to preserve the reply.
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()) {
QString originalString;
if (event->content()) {
originalString = static_cast<const Quotient::EventContent::TextContent *>(event->content())->body;
} else {
originalString = event->plainBody();
}
const QRegularExpression exp(TextRegex::removeRichReply);
const auto match = exp.match(originalString);
if (match.hasCaptured(0) && !handledText.contains(TextRegex::removeRichReply)) {
handledText.prepend(match.captured(0));
}
}
}
}
m_room->postMessage(text, handledText, messageType, chatBarCache->replyId(), chatBarCache->editId(), chatBarCache->threadId());
}

View File

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

View File

@@ -19,6 +19,7 @@
#include <signal.h>
#include <Quotient/accountregistry.h>
#include <Quotient/connection.h>
#include <Quotient/csapi/logout.h>
#include <Quotient/csapi/notifications.h>
#include <Quotient/eventstats.h>
@@ -156,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 account being logged out is currently active
// Only set the connection if the the account being logged out is currently active
if (c == activeConnection()) {
setActiveConnection(dynamic_cast<NeoChatConnection *>(accounts().accounts()[0]));
}

View File

@@ -117,7 +117,7 @@ private:
Quotient::AccountRegistry m_accountRegistry;
QStringList m_accountsLoading;
QMap<QString, QPointer<NeoChatConnection>> m_connectionsLoading;
QMap<QString, QPointer<Quotient::Connection>> m_connectionsLoading;
QString m_endpoint;
private Q_SLOTS:

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'), {
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
sourceText: root.connection.accountDataJsonString(modelData)
}, {
title: i18nc("@title:window", "Event Source"),

View File

@@ -8,7 +8,6 @@ qt_add_qml_module(devtools
QML_FILES
DevtoolsPage.qml
AccountData.qml
DebugOptions.qml
FeatureFlagPage.qml
RoomData.qml
ServerData.qml

View File

@@ -1,42 +0,0 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick
import QtQuick.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 {
text: i18nc("@option:check", "Show hidden events in the timeline")
checked: Config.showAllEvents
onToggled: Config.showAllEvents = checked
}
FormCard.FormCheckDelegate {
id: roomAccountDataVisibleCheck
text: i18nc("@option:check Enable the matrix 'threads' feature", "Always allow device verification")
description: i18n("Allow the user to start a verification session with devices that were already verified")
checked: Config.alwaysVerifyDevice
onToggled: Config.alwaysVerifyDevice = checked
}
FormCard.FormCheckDelegate {
text: i18nc("@option:check", "Show focus in window header")
checked: Config.windowTitleFocus
onToggled: {
Config.windowTitleFocus = checked;
Config.save();
}
}
}
}

View File

@@ -25,11 +25,6 @@ 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")
@@ -57,7 +52,6 @@ FormCard.FormCardPage {
currentIndex: tabBar.currentIndex
DebugOptions {}
RoomData {
room: root.room
connection: root.connection

View File

@@ -7,7 +7,7 @@ import QtQuick.Layouts
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

View File

@@ -25,10 +25,9 @@ ColumnLayout {
text: i18n("Room")
textRole: "escapedDisplayName"
valueRole: "roomId"
displayText: RoomManager.roomListModel.data(RoomManager.roomListModel.index(currentIndex, 0), RoomListModel.EscapedDisplayNameRole)
displayText: RoomManager.roomListModel.data(RoomManager.roomListModel.index(currentIndex, 0), RoomListModel.DisplayNameRole)
model: RoomManager.roomListModel
currentIndex: 0
displayMode: FormCard.FormComboBoxDelegate.Page
Component.onCompleted: currentIndex = RoomManager.roomListModel.rowForRoom(root.room)
onCurrentValueChanged: root.room = RoomManager.roomListModel.roomByAliasOrId(roomComboBox.currentValue)
}
@@ -47,7 +46,7 @@ ColumnLayout {
model: root.room.accountDataEventTypes
delegate: FormCard.FormButtonDelegate {
text: modelData
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
sourceText: root.room.roomAcountDataJson(text)
}, {
title: i18n("Event Source"),
@@ -75,9 +74,14 @@ 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) {
openEventSource(model.type, model.stateKey);
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
})
} else {
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'StateKeys'), {
pageStack.pushDialogLayer(stateKeysComponent, {
room: root.room,
eventType: model.type
}, {
@@ -87,17 +91,9 @@ ColumnLayout {
}
}
}
}
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
});
Component {
id: stateKeysComponent
StateKeys {}
}
}
}

View File

@@ -31,21 +31,13 @@ FormCard.FormCardPage {
delegate: FormCard.FormButtonDelegate {
text: model.stateKey
onClicked: openEventSource(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
})
}
}
}
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

@@ -21,6 +21,7 @@ public:
* @brief Defines the room list categories a room can be assigned.
*/
enum Types {
Search = 0, /**< So we can show a search delegate if needed, e.g. collapsed mode. */
Invited, /**< The user has been invited to the room. */
Favorite, /**< The room is set as a favourite. */
Direct, /**< The room is a direct chat. */
@@ -67,6 +68,8 @@ public:
return i18n("Low priority");
case NeoChatRoomType::Space:
return i18n("Spaces");
case NeoChatRoomType::Search:
return i18n("Search");
default:
return {};
}
@@ -86,6 +89,8 @@ public:
return QStringLiteral("object-order-lower");
case NeoChatRoomType::Space:
return QStringLiteral("group");
case NeoChatRoomType::Search:
return QStringLiteral("search");
default:
return QStringLiteral("tools-report-bug");
}

View File

@@ -74,9 +74,6 @@ 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,4 +89,6 @@ public:
QUO_EVENT(ImagePackEvent, "im.ponies.room_emotes")
using KeyedStateEventBase::KeyedStateEventBase;
};
REGISTER_EVENT_TYPE(ImagePackEvent)
}

View File

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

View File

@@ -1,56 +0,0 @@
// 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,22 +8,34 @@
#include <Quotient/events/roommessageevent.h>
#include "neochatconfig.h"
#include "neochatconnection.h"
#include "neochatroom.h"
#include "utils.h"
using namespace Quotient;
LinkPreviewer::LinkPreviewer(const QUrl &url, QObject *parent)
LinkPreviewer::LinkPreviewer(const NeoChatRoom *room, const Quotient::RoomMessageEvent *event, QObject *parent)
: QObject(parent)
, m_currentRoom(room)
, m_event(event)
, m_loaded(false)
, m_url(url)
, m_url(linkPreview(event))
{
Q_ASSERT(dynamic_cast<Connection *>(this->parent()));
connect(this, &LinkPreviewer::urlChanged, this, &LinkPreviewer::emptyChanged);
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, &LinkPreviewer::loadUrlPreview);
loadUrlPreview();
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(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, &LinkPreviewer::loadUrlPreview);
}
bool LinkPreviewer::loaded() const
@@ -53,14 +65,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 = dynamic_cast<Connection *>(this->parent());
if (conn == nullptr) {
return;
}
auto conn = m_currentRoom->connection();
GetUrlPreviewJob *job = conn->callApi<GetUrlPreviewJob>(m_url);
connect(job, &BaseJob::success, this, [this, job, conn]() {

View File

@@ -53,15 +53,14 @@ class LinkPreviewer : public QObject
Q_PROPERTY(QUrl imageSource READ imageSource NOTIFY imageSourceChanged)
/**
* @brief Whether there is a link to preview.
* @brief Whether the there is a link to preview.
*
* A linkPreviwer is empty if the URL is empty.
*/
Q_PROPERTY(bool empty READ empty NOTIFY emptyChanged)
public:
LinkPreviewer() = default;
explicit LinkPreviewer(const QUrl &url, QObject *parent = nullptr);
explicit LinkPreviewer(const NeoChatRoom *room = nullptr, const Quotient::RoomMessageEvent *event = nullptr, QObject *parent = nullptr);
[[nodiscard]] QUrl url() const;
[[nodiscard]] bool loaded() const;
@@ -77,15 +76,10 @@ public:
*/
static bool hasPreviewableLinks(const Quotient::RoomMessageEvent *event);
/**
* @brief Return the link to be previewed from the given event.
*
* This function is designed to give only links that should be previewed so
* http, https or something starting with www. The first valid link is returned.
*/
static QUrl linkPreview(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();
@@ -94,6 +88,14 @@ private:
void loadUrlPreview();
/**
* @brief Return the link to be previewed from the given event.
*
* This function is designed to give only links that should be previewed so
* http, https or something starting with www. The first valid link is returned.
*/
static QUrl linkPreview(const Quotient::RoomMessageEvent *event);
Q_SIGNALS:
void loadedChanged();
void titleChanged();

View File

@@ -4,6 +4,7 @@
#include "login.h"
#include <Quotient/accountregistry.h>
#include <Quotient/connection.h>
#include <Quotient/qt_connection_util.h>
#include "controller.h"
@@ -53,7 +54,7 @@ void LoginHelper::init()
m_connection = new NeoChatConnection();
}
m_connection->resolveServer(m_matrixId);
connectSingleShot(m_connection.get(), &Connection::loginFlowsChanged, this, [this]() {
connectSingleShot(m_connection, &Connection::loginFlowsChanged, this, [this]() {
setHomeserverReachable(true);
m_testing = false;
Q_EMIT testingChanged();
@@ -99,7 +100,7 @@ void LoginHelper::init()
Q_EMIT Controller::instance().errorOccured(i18n("Network Error"), std::move(error));
});
connectSingleShot(m_connection.get(), &Connection::syncDone, this, [this]() {
connectSingleShot(m_connection, &Connection::syncDone, this, [this]() {
Q_EMIT loaded();
});
}
@@ -181,7 +182,7 @@ QUrl LoginHelper::ssoUrl() const
void LoginHelper::loginWithSso()
{
m_connection->resolveServer(m_matrixId);
connectSingleShot(m_connection.get(), &Connection::loginFlowsChanged, this, [this]() {
connectSingleShot(m_connection, &Connection::loginFlowsChanged, this, [this]() {
SsoSession *session = m_connection->prepareForSso(m_deviceName);
m_ssoUrl = session->ssoUrl();
Q_EMIT ssoUrlChanged();

View File

@@ -146,7 +146,7 @@ private:
QString m_deviceName;
bool m_supportsSso = false;
bool m_supportsPassword = false;
QPointer<NeoChatConnection> m_connection;
NeoChatConnection *m_connection = nullptr;
QUrl m_ssoUrl;
bool m_testing = false;
bool m_isLoggingIn = false;

View File

@@ -1,23 +0,0 @@
# 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

@@ -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,10 +167,6 @@ 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);
@@ -235,9 +231,15 @@ 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;
@@ -288,7 +290,7 @@ int main(int argc, char *argv[])
engine.addImageProvider(QLatin1String("mxc"), MatrixImageProvider::create(&engine, &engine));
engine.addImageProvider(QLatin1String("blurhash"), new BlurhashImageProvider);
engine.loadFromModule("org.kde.neochat", "Main");
engine.load(QUrl(QStringLiteral("qrc:/qt/qml/org/kde/neochat/qml/main.qml")));
if (engine.rootObjects().isEmpty()) {
return -1;
}

View File

@@ -14,6 +14,8 @@
#include "neochatconnection.h"
#include <Quotient/connection.h>
using namespace Quotient;
ThumbnailResponse::ThumbnailResponse(QString id, QSize size, NeoChatConnection *connection)

View File

@@ -35,7 +35,7 @@ private:
QSize requestedSize;
const QString localFile;
Quotient::MediaThumbnailJob *job = nullptr;
QPointer<NeoChatConnection> m_connection;
NeoChatConnection *m_connection;
QImage image;
QString errorStr;
@@ -75,6 +75,6 @@ public:
QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
private:
QPointer<NeoChatConnection> m_connection;
NeoChatConnection *m_connection = nullptr;
MatrixImageProvider() = default;
};

View File

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

View File

@@ -3,15 +3,9 @@
#include "accountemoticonmodel.h"
#include <QImage>
#include <QMimeDatabase>
#include <Quotient/csapi/content-repo.h>
#include <Quotient/events/eventcontent.h>
#include <qcoro/qcorosignal.h>
#include "neochatconnection.h"
using namespace Quotient;
AccountEmoticonModel::AccountEmoticonModel(QObject *parent)
@@ -79,12 +73,12 @@ QHash<int, QByteArray> AccountEmoticonModel::roleNames() const
};
}
NeoChatConnection *AccountEmoticonModel::connection() const
Connection *AccountEmoticonModel::connection() const
{
return m_connection;
}
void AccountEmoticonModel::setConnection(NeoChatConnection *connection)
void AccountEmoticonModel::setConnection(Connection *connection)
{
if (m_connection) {
disconnect(m_connection, nullptr, this, nullptr);
@@ -168,15 +162,7 @@ QCoro::Task<void> AccountEmoticonModel::doSetEmoticonImage(int index, QUrl sourc
co_return;
}
m_images->images[index].url = job->contentUri();
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;
m_images->images[index].info = none;
QJsonObject data;
m_images->fillJson(&data);
m_connection->setAccountData("im.ponies.user_emotes"_ls, data);
@@ -189,21 +175,11 @@ 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,
info,
none,
QStringList{type},
});
QJsonObject data;

View File

@@ -12,7 +12,7 @@
#include <QPointer>
#include <QQmlEngine>
class NeoChatConnection;
#include <Quotient/connection.h>
/**
* @class AccountEmoticonModel
@@ -29,7 +29,7 @@ class AccountEmoticonModel : public QAbstractListModel
/**
* @brief The connection to get emoticons from.
*/
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
Q_PROPERTY(Quotient::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
public:
enum Roles {
@@ -63,8 +63,8 @@ public:
*/
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
[[nodiscard]] NeoChatConnection *connection() const;
void setConnection(NeoChatConnection *connection);
[[nodiscard]] Quotient::Connection *connection() const;
void setConnection(Quotient::Connection *connection);
/**
* @brief Deletes the emoticon at the given index.
@@ -96,7 +96,7 @@ Q_SIGNALS:
private:
std::optional<Quotient::ImagePackEventContent> m_images;
QPointer<NeoChatConnection> m_connection;
QPointer<Quotient::Connection> m_connection;
QCoro::Task<void> doSetEmoticonImage(int index, QUrl source);
QCoro::Task<void> doAddEmoticon(QUrl source, QString shortcode, QString description, QString type);

View File

@@ -4,7 +4,6 @@
#include "actionsmodel.h"
#include "chatbarcache.h"
#include "neochatconnection.h"
#include "neochatroom.h"
#include "roommanager.h"
#include <Quotient/events/roommemberevent.h>
@@ -261,7 +260,7 @@ QList<ActionsModel::Action> actions{
return QString();
}
Q_EMIT room->showMessage(NeoChatRoom::Info, i18nc("Knocking room <roomname>.", "Knocking room %1.", text));
auto connection = dynamic_cast<NeoChatConnection *>(room->connection());
auto connection = room->connection();
const auto knownServer = roomName.mid(roomName.indexOf(":"_ls) + 1);
if (parts.length() >= 2) {
RoomManager::instance().knockRoom(connection, roomName, parts[1], QStringList{knownServer});

View File

@@ -110,7 +110,7 @@ Q_SIGNALS:
private:
explicit CustomEmojiModel(QObject *parent = nullptr);
QList<CustomEmoji> m_emojis;
QPointer<NeoChatConnection> m_connection;
NeoChatConnection *m_connection = nullptr;
void fetchEmojis();
};

View File

@@ -5,8 +5,7 @@
#include "customemojimodel.h"
#include <QRegularExpression>
class NeoChatConnection;
#include <connection.h>
struct CustomEmoji {
QString name; // with :semicolons:
@@ -15,6 +14,6 @@ struct CustomEmoji {
};
struct CustomEmojiModel::Private {
QPointer<NeoChatConnection> connection;
Quotient::Connection *conn = nullptr;
QList<CustomEmoji> emojies;
};

View File

@@ -11,10 +11,9 @@
#include <KLocalizedString>
#include <Quotient/csapi/device_management.h>
#include <Quotient/connection.h>
#include <Quotient/user.h>
#include "neochatconnection.h"
using namespace Quotient;
DevicesModel::DevicesModel(QObject *parent)
@@ -153,12 +152,12 @@ void DevicesModel::setName(const QString &deviceId, const QString &name)
});
}
NeoChatConnection *DevicesModel::connection() const
Connection *DevicesModel::connection() const
{
return m_connection;
}
void DevicesModel::setConnection(NeoChatConnection *connection)
void DevicesModel::setConnection(Connection *connection)
{
if (m_connection) {
disconnect(m_connection, nullptr, this, nullptr);

View File

@@ -9,7 +9,10 @@
#include <Quotient/csapi/definitions/client_device.h>
class NeoChatConnection;
namespace Quotient
{
class Connection;
}
/**
* @class DevicesModel
@@ -28,7 +31,7 @@ class DevicesModel : public QAbstractListModel
/**
* @brief The current connection that the model is getting its devices from.
*/
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged REQUIRED)
Q_PROPERTY(Quotient::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged REQUIRED)
public:
/**
@@ -85,8 +88,9 @@ public:
explicit DevicesModel(QObject *parent = nullptr);
[[nodiscard]] NeoChatConnection *connection() const;
void setConnection(NeoChatConnection *connection);
[[nodiscard]] Quotient::Connection *connection() const;
void setConnection(Quotient::Connection *connection);
Q_SIGNALS:
void connectionChanged();
@@ -95,5 +99,5 @@ Q_SIGNALS:
private:
void fetchDevices();
QList<Quotient::Device> m_devices;
QPointer<NeoChatConnection> m_connection;
QPointer<Quotient::Connection> m_connection;
};

View File

@@ -208,5 +208,3 @@ void ItineraryModel::sendToItinerary()
job->start();
#endif
}
#include "moc_itinerarymodel.cpp"

View File

@@ -62,5 +62,3 @@ void LineModel::resetModel()
beginResetModel();
endResetModel();
}
#include "moc_linemodel.cpp"

View File

@@ -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,12 +23,9 @@
#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)
@@ -79,21 +76,6 @@ 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) {
@@ -110,11 +92,23 @@ MessageContentModel::MessageContentModel(const Quotient::RoomEvent *event, NeoCh
endResetModel();
}
});
connect(m_room, &NeoChatRoom::urlPreviewEnabledChanged, this, &MessageContentModel::updateLinkPreviewer);
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, &MessageContentModel::updateLinkPreviewer);
}
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();
}
});
}
}
updateComponents();
}
@@ -161,7 +155,14 @@ QVariant MessageContentModel::data(const QModelIndex &index, int role) const
return eventHandler.getMediaInfo();
}
if (role == FileTransferInfoRole) {
return QVariant::fromValue(fileInfo());
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()));
}
}
if (role == ItineraryModelRole) {
return QVariant::fromValue<ItineraryModel *>(m_itineraryModel);
@@ -246,124 +247,78 @@ void MessageContentModel::updateComponents(bool isEditing)
if (eventCast<const Quotient::RoomMessageEvent>(m_event)
&& eventCast<const Quotient::RoomMessageEvent>(m_event)->rawMsgtype() == QStringLiteral("m.key.verification.request")) {
m_components += MessageComponent{MessageComponentType::Verification, QString(), {}};
endResetModel();
return;
}
if (m_event->isRedacted()) {
m_components += MessageComponent{MessageComponentType::Text, QString(), {}};
endResetModel();
return;
}
EventHandler eventHandler(m_room, m_event);
if (eventHandler.hasReply()) {
if (m_room->findInTimeline(eventHandler.getReplyId()) == m_room->historyEdge()) {
m_components += MessageComponent{MessageComponentType::ReplyLoad, QString(), {}};
m_room->loadReply(m_event->id(), eventHandler.getReplyId());
} else {
m_components += MessageComponent{MessageComponentType::Reply, QString(), {}};
}
}
if (isEditing) {
m_components += MessageComponent{MessageComponentType::Edit, QString(), {}};
} else {
m_components.append(componentsForType(eventHandler.messageComponentType()));
}
EventHandler eventHandler(m_room, m_event);
if (eventHandler.hasReply()) {
if (m_room->findInTimeline(eventHandler.getReplyId()) == m_room->historyEdge()) {
m_components += MessageComponent{MessageComponentType::ReplyLoad, QString(), {}};
m_room->loadReply(m_event->id(), eventHandler.getReplyId());
} else {
m_components += MessageComponent{MessageComponentType::Reply, QString(), {}};
}
}
if (m_linkPreviewer != nullptr) {
if (m_linkPreviewer->loaded()) {
m_components += MessageComponent{MessageComponentType::LinkPreview, QString(), {}};
if (isEditing) {
m_components += MessageComponent{MessageComponentType::Edit, QString(), {}};
} else if (m_event->isRedacted()) {
m_components += MessageComponent{MessageComponentType::Text, QString(), {}};
} else {
m_components += MessageComponent{MessageComponentType::LinkPreviewLoad, QString(), {}};
if (eventHandler.messageComponentType() == MessageComponentType::Text) {
const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event);
auto body = EventHandler::rawMessageBody(*event);
m_components.append(TextHandler().textComponents(body, EventHandler::messageBodyInputFormat(*event), m_room, event, event->isReplaced()));
} 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());
}
#ifndef Q_OS_ANDROID
KSyntaxHighlighting::Repository repository;
const auto definitionForFile = repository.definitionForFileName(fileTransferInfo.localPath.toString());
if (definitionForFile.isValid() || QFileInfo(fileTransferInfo.localPath.path()).suffix() == QStringLiteral("txt")) {
QFile file(fileTransferInfo.localPath.path());
file.open(QIODevice::ReadOnly);
m_components += MessageComponent{MessageComponentType::Code,
QString::fromStdString(file.readAll().toStdString()),
{{QStringLiteral("class"), definitionForFile.name()}}};
}
#endif
if (FileType::instance().fileHasImage(fileTransferInfo.localPath)) {
QImageReader reader(fileTransferInfo.localPath.path());
m_components += MessageComponent{MessageComponentType::Pdf, QString(), {{QStringLiteral("size"), reader.size()}}};
}
} else {
updateItineraryModel();
if (m_itineraryModel != nullptr) {
m_components += MessageComponent{MessageComponentType::Itinerary, QString(), {}};
}
}
} else {
m_components += MessageComponent{eventHandler.messageComponentType(), QString(), {}};
}
}
if (m_linkPreviewer != nullptr) {
if (m_linkPreviewer->loaded()) {
m_components += MessageComponent{MessageComponentType::LinkPreview, QString(), {}};
} else {
m_components += MessageComponent{MessageComponentType::LinkPreviewLoad, QString(), {}};
}
}
}
endResetModel();
}
QList<MessageComponent> MessageContentModel::componentsForType(MessageComponentType::Type type)
{
switch (type) {
case MessageComponentType::Text: {
const auto event = eventCast<const Quotient::RoomMessageEvent>(m_event);
auto body = EventHandler::rawMessageBody(*event);
return TextHandler().textComponents(body, EventHandler::messageBodyInputFormat(*event), m_room, event, event->isReplaced());
}
case MessageComponentType::File: {
QList<MessageComponent> components;
components += MessageComponent{MessageComponentType::File, QString(), {}};
if (m_emptyItinerary) {
auto fileTransferInfo = fileInfo();
#ifndef Q_OS_ANDROID
KSyntaxHighlighting::Repository repository;
const auto definitionForFile = repository.definitionForFileName(fileTransferInfo.localPath.toString());
if (definitionForFile.isValid() || QFileInfo(fileTransferInfo.localPath.path()).suffix() == QStringLiteral("txt")) {
QFile file(fileTransferInfo.localPath.path());
file.open(QIODevice::ReadOnly);
components += MessageComponent{MessageComponentType::Code,
QString::fromStdString(file.readAll().toStdString()),
{{QStringLiteral("class"), definitionForFile.name()}}};
}
#endif
if (FileType::instance().fileHasImage(fileTransferInfo.localPath)) {
QImageReader reader(fileTransferInfo.localPath.path());
components += MessageComponent{MessageComponentType::Pdf, QString(), {{QStringLiteral("size"), reader.size()}}};
}
} else {
updateItineraryModel();
if (m_itineraryModel != nullptr) {
components += MessageComponent{MessageComponentType::Itinerary, QString(), {}};
}
}
return components;
}
default:
return {MessageComponent{type, QString(), {}}};
}
}
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) {
@@ -372,7 +327,7 @@ void MessageContentModel::updateItineraryModel()
if (auto event = eventCast<const Quotient::RoomMessageEvent>(m_event)) {
if (event->hasFileContent()) {
auto filePath = fileInfo().localPath;
auto filePath = m_room->fileTransferInfo(event->id()).localPath;
if (filePath.isEmpty() && m_itineraryModel != nullptr) {
delete m_itineraryModel;
m_itineraryModel = nullptr;
@@ -399,43 +354,3 @@ 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"

View File

@@ -6,8 +6,6 @@
#include <QAbstractListModel>
#include <QQmlEngine>
#include <Quotient/room.h>
#include "enums/messagecomponenttype.h"
#include "eventhandler.h"
#include "itinerarymodel.h"
@@ -95,14 +93,9 @@ private:
QList<MessageComponent> m_components;
void updateComponents(bool isEditing = false);
QPointer<LinkPreviewer> m_linkPreviewer;
LinkPreviewer *m_linkPreviewer = nullptr;
ItineraryModel *m_itineraryModel = nullptr;
QList<MessageComponent> componentsForType(MessageComponentType::Type type);
void updateLinkPreviewer();
void updateItineraryModel();
bool m_emptyItinerary = false;
Quotient::FileTransferInfo fileInfo() const;
};

View File

@@ -7,6 +7,7 @@
#include "neochatconfig.h"
#include <Quotient/connection.h>
#include <Quotient/csapi/rooms.h>
#include <Quotient/events/redactionevent.h>
#include <Quotient/events/roommessageevent.h>
@@ -116,6 +117,34 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
if (message != nullptr) {
createEventObjects(message);
if (NeoChatConfig::self()->showFancyEffects()) {
QString planBody = message->plainBody();
// snowflake
const QString snowlakeEmoji = QString::fromUtf8("\xE2\x9D\x84");
if (planBody.contains(snowlakeEmoji)) {
Q_EMIT fancyEffectsReasonFound(QStringLiteral("snowflake"));
}
// fireworks
const QString fireworksEmoji = QString::fromUtf8("\xF0\x9F\x8E\x86");
if (planBody.contains(fireworksEmoji)) {
Q_EMIT fancyEffectsReasonFound(QStringLiteral("fireworks"));
}
// sparkler
const QString sparklerEmoji = QString::fromUtf8("\xF0\x9F\x8E\x87");
if (planBody.contains(sparklerEmoji)) {
Q_EMIT fancyEffectsReasonFound(QStringLiteral("fireworks"));
}
// party pooper
const QString partyEmoji = QString::fromUtf8("\xF0\x9F\x8E\x89");
if (planBody.contains(partyEmoji)) {
Q_EMIT fancyEffectsReasonFound(QStringLiteral("confetti"));
}
// confetti ball
const QString confettiEmoji = QString::fromUtf8("\xF0\x9F\x8E\x8A");
if (planBody.contains(confettiEmoji)) {
Q_EMIT fancyEffectsReasonFound(QStringLiteral("confetti"));
}
}
}
if (event->is<PollStartEvent>()) {
m_currentRoom->createPollHandler(eventCast<const PollStartEvent>(event.get()));

View File

@@ -140,4 +140,5 @@ private:
Q_SIGNALS:
void roomChanged();
void fancyEffectsReasonFound(const QString &fancyEffect);
};

View File

@@ -36,14 +36,6 @@ MessageFilterModel::MessageFilterModel(QObject *parent, TimelineModel *sourceMod
}
bool MessageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
if (NeoChatConfig::self()->showAllEvents()) {
return true;
}
return eventIsVisible(sourceRow, sourceParent);
}
bool MessageFilterModel::eventIsVisible(int sourceRow, const QModelIndex &sourceParent) const
{
const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
@@ -67,8 +59,9 @@ bool MessageFilterModel::eventIsVisible(int sourceRow, const QModelIndex &source
// Don't show state events that are not the first in a consecutive group on the
// same day as they will be grouped as a single delegate.
const bool notLastRow = sourceRow < sourceModel()->rowCount() - 1;
const bool previousEventIsState =
notLastRow ? sourceModel()->data(sourceModel()->index(sourceRow + 1, 0), MessageEventModel::DelegateTypeRole) == DelegateType::State : false;
const bool previousEventIsState = notLastRow
? sourceModel()->data(sourceModel()->index(sourceRow + 1, 0), MessageEventModel::DelegateTypeRole) == DelegateType::State
: false;
const bool newDay = sourceModel()->data(sourceModel()->index(sourceRow, 0), MessageEventModel::ShowSectionRole).toBool();
if (eventType == DelegateType::State && notLastRow && previousEventIsState && !newDay) {
return false;
@@ -79,11 +72,7 @@ bool MessageFilterModel::eventIsVisible(int sourceRow, const QModelIndex &source
QVariant MessageFilterModel::data(const QModelIndex &index, int role) const
{
if (role == MessageEventModel::DelegateTypeRole && NeoChatConfig::self()->showAllEvents()) {
if (!eventIsVisible(index.row(), index.parent())) {
return DelegateType::Other;
}
} else if (role == AggregateDisplayRole) {
if (role == AggregateDisplayRole) {
return aggregateEventToString(mapToSource(index).row());
} else if (role == StateEventsRole) {
return stateEventsList(mapToSource(index).row());

View File

@@ -60,8 +60,6 @@ public:
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
private:
bool eventIsVisible(int sourceRow, const QModelIndex &sourceParent) const;
/**
* @brief Aggregation of the text of consecutive state events starting at row.
*

View File

@@ -3,6 +3,7 @@
#include "notificationsmodel.h"
#include <Quotient/connection.h>
#include <Quotient/events/event.h>
#include <Quotient/uri.h>
@@ -161,5 +162,3 @@ QString NotificationsModel::nextToken() const
{
return m_nextToken;
}
#include "moc_notificationsmodel.cpp"

View File

@@ -3,7 +3,8 @@
#include "publicroomlistmodel.h"
#include "neochatconnection.h"
#include <Quotient/connection.h>
#include "publicroomlist_logging.h"
using namespace Quotient;
@@ -13,14 +14,14 @@ PublicRoomListModel::PublicRoomListModel(QObject *parent)
{
}
NeoChatConnection *PublicRoomListModel::connection() const
Quotient::Connection *PublicRoomListModel::connection() const
{
return m_connection;
}
void PublicRoomListModel::setConnection(NeoChatConnection *connection)
void PublicRoomListModel::setConnection(Connection *conn)
{
if (m_connection == connection) {
if (m_connection == conn) {
return;
}
@@ -37,7 +38,7 @@ void PublicRoomListModel::setConnection(NeoChatConnection *connection)
endResetModel();
m_connection = connection;
m_connection = conn;
if (job) {
job->abandon();

View File

@@ -9,7 +9,10 @@
#include <Quotient/csapi/list_public_rooms.h>
class NeoChatConnection;
namespace Quotient
{
class Connection;
}
/**
* @class PublicRoomListModel
@@ -30,7 +33,7 @@ class PublicRoomListModel : public QAbstractListModel
/**
* @brief The current connection that the model is getting its rooms from.
*/
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
Q_PROPERTY(Quotient::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
/**
* @brief The server to get the public room list from.
@@ -92,8 +95,8 @@ public:
*/
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
[[nodiscard]] NeoChatConnection *connection() const;
void setConnection(NeoChatConnection *connection);
[[nodiscard]] Quotient::Connection *connection() const;
void setConnection(Quotient::Connection *conn);
[[nodiscard]] QString server() const;
void setServer(const QString &value);
@@ -114,7 +117,7 @@ public:
Q_INVOKABLE void search(int limit = 50);
private:
QPointer<NeoChatConnection> m_connection = nullptr;
QPointer<Quotient::Connection> m_connection = nullptr;
QString m_server;
QString m_searchText;
bool m_showOnlySpaces = false;

View File

@@ -5,13 +5,13 @@
#include <QDebug>
#include <Quotient/connection.h>
#include <Quotient/converters.h>
#include <Quotient/csapi/definitions/push_ruleset.h>
#include <Quotient/csapi/pushrules.h>
#include <Quotient/jobs/basejob.h>
#include "neochatconfig.h"
#include "neochatconnection.h"
#include <KLazyLocalizedString>
@@ -454,7 +454,7 @@ void PushRuleModel::setConnection(NeoChatConnection *connection)
Q_EMIT connectionChanged();
if (m_connection) {
connect(m_connection, &NeoChatConnection::accountDataChanged, this, &PushRuleModel::updateNotificationRules);
connect(m_connection, &Quotient::Connection::accountDataChanged, this, &PushRuleModel::updateNotificationRules);
updateNotificationRules(QStringLiteral("m.push_rules"));
}
}

View File

@@ -126,7 +126,7 @@ private Q_SLOTS:
private:
PushRuleAction::Action m_defaultKeywordAction;
QList<Rule> m_rules;
QPointer<NeoChatConnection> m_connection;
NeoChatConnection *m_connection;
void setRules(QList<Quotient::PushRule> rules, PushRuleKind::Kind kind);

View File

@@ -25,12 +25,12 @@ RoomListModel::RoomListModel(QObject *parent)
RoomListModel::~RoomListModel() = default;
NeoChatConnection *RoomListModel::connection() const
Quotient::Connection *RoomListModel::connection() const
{
return m_connection;
}
void RoomListModel::setConnection(NeoChatConnection *connection)
void RoomListModel::setConnection(Connection *connection)
{
if (connection == m_connection) {
return;

View File

@@ -12,11 +12,10 @@ class NeoChatRoom;
namespace Quotient
{
class Connection;
class Room;
}
class NeoChatConnection;
/**
* @class RoomListModel
*
@@ -30,7 +29,7 @@ class RoomListModel : public QAbstractListModel
/**
* @brief The current connection that the model is getting its rooms from.
*/
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
Q_PROPERTY(Quotient::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
public:
/**
@@ -61,8 +60,8 @@ public:
explicit RoomListModel(QObject *parent = nullptr);
~RoomListModel() override;
[[nodiscard]] NeoChatConnection *connection() const;
void setConnection(NeoChatConnection *connection);
[[nodiscard]] Quotient::Connection *connection() const;
void setConnection(Quotient::Connection *connection);
/**
* @brief Get the given role value at the given index.
@@ -110,7 +109,7 @@ private Q_SLOTS:
void refresh(NeoChatRoom *room, const QList<int> &roles = {});
private:
QPointer<NeoChatConnection> m_connection;
Quotient::Connection *m_connection = nullptr;
QList<NeoChatRoom *> m_rooms;
QString m_activeSpaceId;

View File

@@ -3,6 +3,7 @@
#include "roomtreemodel.h"
#include <Quotient/connection.h>
#include <Quotient/room.h>
#include "eventhandler.h"
@@ -299,6 +300,9 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
return NeoChatRoomType::typeName(index.row());
}
if (role == DelegateTypeRole) {
if (index.row() == NeoChatRoomType::Search) {
return QStringLiteral("search");
}
if (index.row() == NeoChatRoomType::AddDirect) {
return QStringLiteral("addDirect");
}

View File

@@ -10,6 +10,7 @@
#include <QGuiApplication>
#include <Quotient/connection.h>
#include <Quotient/events/stickerevent.h>
#include <KLocalizedString>

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