Compare commits
1 Commits
work/multi
...
work/pushn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0272dcf46 |
@@ -68,6 +68,8 @@ set_package_properties(KF6Kirigami2 PROPERTIES
|
|||||||
)
|
)
|
||||||
find_package(KF6KirigamiAddons 0.7.2 REQUIRED)
|
find_package(KF6KirigamiAddons 0.7.2 REQUIRED)
|
||||||
|
|
||||||
|
find_package(KUnifiedPush)
|
||||||
|
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
find_package(OpenSSL)
|
find_package(OpenSSL)
|
||||||
set_package_properties(OpenSSL PROPERTIES
|
set_package_properties(OpenSSL PROPERTIES
|
||||||
|
|||||||
@@ -333,6 +333,11 @@ endif()
|
|||||||
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
|
target_include_directories(neochat PRIVATE ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models ${CMAKE_CURRENT_SOURCE_DIR}/enums)
|
||||||
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF6::I18n KF6::Kirigami2 KF6::Notifications KF6::ConfigCore KF6::ConfigGui KF6::CoreAddons KF6::SonnetCore KF6::ItemModels QuotientQt6 cmark::cmark QCoro::Core)
|
target_link_libraries(neochat PUBLIC Qt::Core Qt::Quick Qt::Qml Qt::Gui Qt::Multimedia Qt::Network Qt::QuickControls2 KF6::I18n KF6::Kirigami2 KF6::Notifications KF6::ConfigCore KF6::ConfigGui KF6::CoreAddons KF6::SonnetCore KF6::ItemModels QuotientQt6 cmark::cmark QCoro::Core)
|
||||||
|
|
||||||
|
if(TARGET KUnifiedPush)
|
||||||
|
target_link_libraries(neochat PUBLIC KUnifiedPush)
|
||||||
|
target_compile_definitions(neochat PUBLIC -DHAVE_KUNIFIEDPUSH)
|
||||||
|
endif()
|
||||||
|
|
||||||
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
kconfig_add_kcfg_files(neochat GENERATE_MOC neochatconfig.kcfgc)
|
||||||
|
|
||||||
if(NEOCHAT_FLATPAK)
|
if(NEOCHAT_FLATPAK)
|
||||||
@@ -438,6 +443,9 @@ endif()
|
|||||||
|
|
||||||
install(TARGETS neochat-app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
|
install(TARGETS neochat-app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||||
|
|
||||||
|
configure_file(org.kde.neochat.notifier.service.in ${CMAKE_CURRENT_BINARY_DIR}/org.kde.neochat.notifier.service)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.neochat.notifier.service DESTINATION ${KDE_INSTALL_DBUSSERVICEDIR})
|
||||||
|
|
||||||
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE)
|
||||||
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins)
|
install(FILES plasma-runner-neochat.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -14,6 +14,13 @@
|
|||||||
#include <KWindowEffects>
|
#include <KWindowEffects>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_KUNIFIEDPUSH
|
||||||
|
#include <KUnifiedPush/Connector>
|
||||||
|
#include <QCoroNetworkReply>
|
||||||
|
#include <QDBusConnection>
|
||||||
|
#include <qcoro/qcorosignal.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
@@ -41,6 +48,11 @@
|
|||||||
#include "roommanager.h"
|
#include "roommanager.h"
|
||||||
#include "windowcontroller.h"
|
#include "windowcontroller.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_KUNIFIEDPUSH
|
||||||
|
#include <Quotient/csapi/pusher.h>
|
||||||
|
#include <Quotient/networkaccessmanager.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||||
#include "trayicon.h"
|
#include "trayicon.h"
|
||||||
#elif !defined(Q_OS_ANDROID)
|
#elif !defined(Q_OS_ANDROID)
|
||||||
@@ -49,9 +61,54 @@
|
|||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
|
#ifdef HAVE_KUNIFIEDPUSH
|
||||||
|
QCoro::Task<void> Controller::setupPush(const QString &endpoint)
|
||||||
|
{
|
||||||
|
while (!activeConnection()) {
|
||||||
|
co_await qCoro(this, &Controller::activeConnectionChanged);
|
||||||
|
}
|
||||||
|
QUrl gatewayEndpoint(endpoint);
|
||||||
|
gatewayEndpoint.setPath("/_matrix/push/v1/notify");
|
||||||
|
QNetworkRequest checkGateway(gatewayEndpoint);
|
||||||
|
auto reply = co_await NetworkAccessManager::instance()->get(checkGateway);
|
||||||
|
const QJsonObject replyJson = QJsonDocument::fromJson(reply->readAll()).object();
|
||||||
|
if (replyJson["unifiedpush"]["gateway"].toString() == QStringLiteral("matrix")) {
|
||||||
|
Controller::instance().activeConnection()->callApi<PostPusherJob>(endpoint,
|
||||||
|
"http",
|
||||||
|
"neochat-foo1",
|
||||||
|
"NeoChat",
|
||||||
|
"Device 1",
|
||||||
|
"en",
|
||||||
|
PostPusherJob::PusherData{{gatewayEndpoint.toString()}, " "});
|
||||||
|
} else {
|
||||||
|
qWarning() << "There's no gateway";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Controller::Controller(QObject *parent)
|
Controller::Controller(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_KUNIFIEDPUSH
|
||||||
|
const auto serviceName = QStringLiteral("org.kde.neochat.notifier");
|
||||||
|
if (!QDBusConnection::sessionBus().registerService(serviceName)) {
|
||||||
|
qCritical() << "Service name already in use";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto connector = new KUnifiedPush::Connector(serviceName);
|
||||||
|
connect(connector, &KUnifiedPush::Connector::stateChanged, [](auto state) {
|
||||||
|
// TODO ?
|
||||||
|
});
|
||||||
|
connect(connector, &KUnifiedPush::Connector::messageReceived, [](const auto &msg) {
|
||||||
|
NotificationsManager::instance().postPushNotification(msg);
|
||||||
|
});
|
||||||
|
connect(connector, &KUnifiedPush::Connector::endpointChanged, [this](const auto &endpoint) {
|
||||||
|
setupPush(endpoint);
|
||||||
|
});
|
||||||
|
|
||||||
|
connector->registerClient(i18n("Receiving Matrix messages"));
|
||||||
|
#endif
|
||||||
|
|
||||||
Connection::setRoomType<NeoChatRoom>();
|
Connection::setRoomType<NeoChatRoom>();
|
||||||
|
|
||||||
setApplicationProxy();
|
setApplicationProxy();
|
||||||
|
|||||||
@@ -15,6 +15,10 @@
|
|||||||
#include <Quotient/jobs/basejob.h>
|
#include <Quotient/jobs/basejob.h>
|
||||||
#include <Quotient/settings.h>
|
#include <Quotient/settings.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_KUNIFIEDPUSH
|
||||||
|
#include <qcoro/task.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
class NeoChatRoom;
|
class NeoChatRoom;
|
||||||
class TrayIcon;
|
class TrayIcon;
|
||||||
class QQuickTextDocument;
|
class QQuickTextDocument;
|
||||||
@@ -177,6 +181,11 @@ private:
|
|||||||
explicit Controller(QObject *parent = nullptr);
|
explicit Controller(QObject *parent = nullptr);
|
||||||
|
|
||||||
QPointer<NeoChatConnection> m_connection;
|
QPointer<NeoChatConnection> m_connection;
|
||||||
|
|
||||||
|
#ifdef HAVE_KUNIFIEDPUSH
|
||||||
|
QCoro::Task<void> setupPush(const QString &endpoint);
|
||||||
|
#endif
|
||||||
|
|
||||||
TrayIcon *m_trayIcon = nullptr;
|
TrayIcon *m_trayIcon = nullptr;
|
||||||
|
|
||||||
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const Quotient::AccountSettings &account);
|
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const Quotient::AccountSettings &account);
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
#include <KLocalizedString>
|
#include <KLocalizedString>
|
||||||
#include <KNotification>
|
#include <KNotification>
|
||||||
@@ -326,4 +328,33 @@ QPixmap NotificationsManager::createNotificationImage(const QImage &icon, NeoCha
|
|||||||
return QPixmap::fromImage(roundedImage);
|
return QPixmap::fromImage(roundedImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NotificationsManager::postPushNotification(const QByteArray &message)
|
||||||
|
{
|
||||||
|
KNotification *notification = new KNotification("message"_ls);
|
||||||
|
|
||||||
|
const auto json = QJsonDocument::fromJson(message).object();
|
||||||
|
|
||||||
|
auto sender = json["notification"_ls]["sender_display_name"_ls].toString();
|
||||||
|
auto roomName = json["notification"_ls]["room_name"_ls].toString();
|
||||||
|
auto roomId = json["notification"_ls]["room_id"_ls].toString();
|
||||||
|
auto text = json["notification"_ls]["content"_ls]["body"_ls].toString();
|
||||||
|
|
||||||
|
if (sender == roomName) {
|
||||||
|
notification->setTitle(sender);
|
||||||
|
} else {
|
||||||
|
notification->setTitle(i18n("%1 (%2)", sender, roomName));
|
||||||
|
}
|
||||||
|
|
||||||
|
notification->setText(text.toHtmlEscaped());
|
||||||
|
|
||||||
|
notification->setDefaultAction(i18n("Open NeoChat in this room"));
|
||||||
|
connect(notification, &KNotification::defaultActivated, this, [=]() {
|
||||||
|
WindowController::instance().showAndRaiseWindow(notification->xdgActivationToken());
|
||||||
|
});
|
||||||
|
|
||||||
|
notification->sendEvent();
|
||||||
|
|
||||||
|
m_notifications.insert(roomId, notification);
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_notificationsmanager.cpp"
|
#include "moc_notificationsmanager.cpp"
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ public:
|
|||||||
* Nothing happens if the given room doesn't have an invite notification.
|
* Nothing happens if the given room doesn't have an invite notification.
|
||||||
*/
|
*/
|
||||||
void clearInvitationNotification(const QString &roomId);
|
void clearInvitationNotification(const QString &roomId);
|
||||||
|
void postPushNotification(const QByteArray &message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handle the notifications for the given connection.
|
* @brief Handle the notifications for the given connection.
|
||||||
|
|||||||
5
src/org.kde.neochat.notifier.service.in
Normal file
5
src/org.kde.neochat.notifier.service.in
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# SPDX-FileCopyrightText: none
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
[D-BUS Service]
|
||||||
|
Name=org.kde.neochat.notifier
|
||||||
|
Exec=@CMAKE_INSTALL_PREFIX@/bin/neochat
|
||||||
@@ -43,18 +43,13 @@ TextEdit {
|
|||||||
*/
|
*/
|
||||||
property bool spoilerRevealed: !hasSpoiler.test(textMessage)
|
property bool spoilerRevealed: !hasSpoiler.test(textMessage)
|
||||||
|
|
||||||
property bool isDelegate: false
|
|
||||||
|
|
||||||
ListView.onReused: Qt.binding(() => !hasSpoiler.test(textMessage))
|
ListView.onReused: Qt.binding(() => !hasSpoiler.test(textMessage))
|
||||||
|
|
||||||
persistentSelection: true
|
persistentSelection: true
|
||||||
|
|
||||||
// Work around QTBUG 93281
|
// Work around QTBUG 93281
|
||||||
Component.onCompleted: {
|
Component.onCompleted: if (text.includes("<img")) {
|
||||||
updateSelection()
|
Controller.forceRefreshTextDocument(root.textDocument, root)
|
||||||
if (text.includes("<img")) {
|
|
||||||
Controller.forceRefreshTextDocument(root.textDocument, root)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
text: "<style>
|
text: "<style>
|
||||||
@@ -121,26 +116,4 @@ a{
|
|||||||
enabled: !parent.hoveredLink && !spoilerRevealed
|
enabled: !parent.hoveredLink && !spoilerRevealed
|
||||||
onTapped: spoilerRevealed = true
|
onTapped: spoilerRevealed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: selectionArea
|
|
||||||
enabled: root.isDelegate
|
|
||||||
function onSelectionChanged() {
|
|
||||||
updateSelection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateSelection() {
|
|
||||||
if (index < selectionArea.lowerIndex || index > selectionArea.upperIndex) {
|
|
||||||
root.select(0, 0);
|
|
||||||
} else if (index > selectionArea.lowerIndex && index < selectionArea.upperIndex) {
|
|
||||||
root.selectAll();
|
|
||||||
} else if (index === selectionArea.selectionStartIndex && index === selectionArea.selectionEndIndex) {
|
|
||||||
root.select(selectionArea.upperPos, selectionArea.lowerPos);
|
|
||||||
} else if (index === selectionArea.upperIndex) {
|
|
||||||
root.select(selectionArea.upperPos, root.length);
|
|
||||||
} else if (index === selectionArea.lowerIndex) {
|
|
||||||
root.select(0, selectionArea.lowerPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,14 +17,6 @@ import org.kde.neochat.config
|
|||||||
MessageDelegate {
|
MessageDelegate {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
function positionAt(x, y) {
|
|
||||||
let point = label.mapFromItem(root, x, y)
|
|
||||||
return label.positionAt(point.x, point.y)
|
|
||||||
}
|
|
||||||
|
|
||||||
property alias selectedText: label.selectedText
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The link preview properties.
|
* @brief The link preview properties.
|
||||||
*
|
*
|
||||||
@@ -62,7 +54,6 @@ MessageDelegate {
|
|||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
onLongPressed: root.openContextMenu()
|
onLongPressed: root.openContextMenu()
|
||||||
}
|
}
|
||||||
isDelegate: true
|
|
||||||
}
|
}
|
||||||
Loader {
|
Loader {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|||||||
@@ -341,66 +341,6 @@ QQC2.ScrollView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function indexAtRelative(x, y) {
|
|
||||||
return indexAt(x + contentX, y + contentY)
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: selectionArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
property int selectionStartIndex
|
|
||||||
property int selectionEndIndex
|
|
||||||
property int selectionStartPos
|
|
||||||
property int selectionEndPos
|
|
||||||
|
|
||||||
property int upperIndex: selectionStartIndex > selectionEndIndex ? selectionStartIndex : selectionEndIndex
|
|
||||||
property int upperPos: selectionStartIndex > selectionEndIndex ? selectionStartPos : (selectionStartIndex == selectionEndIndex ? (selectionStartPos > selectionEndPos ? selectionEndPos : selectionStartPos) : selectionEndPos)
|
|
||||||
property int lowerIndex: selectionStartIndex > selectionEndIndex ? selectionEndIndex : selectionStartIndex
|
|
||||||
property int lowerPos: selectionStartIndex > selectionEndIndex ? selectionEndPos : (selectionStartIndex == selectionEndIndex ? (selectionStartPos > selectionEndPos ? selectionStartPos : selectionEndPos) : selectionStartPos)
|
|
||||||
|
|
||||||
signal selectionChanged
|
|
||||||
|
|
||||||
function indexAndPos(x, y) {
|
|
||||||
const index = messageListView.indexAtRelative(x, y);
|
|
||||||
if (index == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const item = messageListView.itemAtIndex(index);
|
|
||||||
const relItemY = item.y - messageListView.contentY;
|
|
||||||
const pos = item.positionAt(x, y - relItemY);
|
|
||||||
return [index, pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
onPressed: {
|
|
||||||
[selectionArea.selectionEndIndex, selectionArea.selectionEndPos] = selectionArea.indexAndPos(mouse.x, mouse.y);
|
|
||||||
[selectionArea.selectionStartIndex, selectionArea.selectionStartPos] = selectionArea.indexAndPos(mouse.x, mouse.y);
|
|
||||||
selectionChanged();
|
|
||||||
}
|
|
||||||
onPositionChanged: {
|
|
||||||
if (!pressed) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
[selectionEndIndex, selectionEndPos] = selectionArea.indexAndPos(mouse.x, mouse.y);
|
|
||||||
selectionChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Kirigami.Action {
|
|
||||||
onTriggered: {
|
|
||||||
var text = ""
|
|
||||||
for (let i = selectionArea.upperIndex; i >= selectionArea.lowerIndex; i--) {
|
|
||||||
text += messageListView.itemAtIndex(i).selectedText
|
|
||||||
if (i > selectionArea.lowerIndex) {
|
|
||||||
text += " "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Clipboard.saveText(text)
|
|
||||||
}
|
|
||||||
shortcut: "Ctrl+C"
|
|
||||||
}
|
|
||||||
|
|
||||||
function showMaximizedMedia(index) {
|
function showMaximizedMedia(index) {
|
||||||
var popup = maximizeComponent.createObject(QQC2.ApplicationWindow.overlay, {
|
var popup = maximizeComponent.createObject(QQC2.ApplicationWindow.overlay, {
|
||||||
initialIndex: index
|
initialIndex: index
|
||||||
|
|||||||
Reference in New Issue
Block a user