Add QR code scanner
This commit is contained in:
@@ -126,6 +126,7 @@ add_library(neochat STATIC
|
|||||||
events/pollevent.cpp
|
events/pollevent.cpp
|
||||||
pollhandler.cpp
|
pollhandler.cpp
|
||||||
utils.h
|
utils.h
|
||||||
|
utils.cpp
|
||||||
registration.cpp
|
registration.cpp
|
||||||
neochatconnection.cpp
|
neochatconnection.cpp
|
||||||
neochatconnection.h
|
neochatconnection.h
|
||||||
@@ -284,6 +285,9 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
|||||||
qml/AccountData.qml
|
qml/AccountData.qml
|
||||||
qml/StateKeys.qml
|
qml/StateKeys.qml
|
||||||
qml/UnlockSSSSDialog.qml
|
qml/UnlockSSSSDialog.qml
|
||||||
|
qml/QrScannerPage.qml
|
||||||
|
qml/JoinRoomDialog.qml
|
||||||
|
qml/ConfirmUrlDialog.qml
|
||||||
RESOURCES
|
RESOURCES
|
||||||
qml/confetti.png
|
qml/confetti.png
|
||||||
qml/glowdot.png
|
qml/glowdot.png
|
||||||
|
|||||||
34
src/qml/ConfirmUrlDialog.qml
Normal file
34
src/qml/ConfirmUrlDialog.qml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls as QQC2
|
||||||
|
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
|
Kirigami.Dialog {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property url link
|
||||||
|
|
||||||
|
width: Kirigami.Units.gridUnit * 24
|
||||||
|
height: Kirigami.Units.gridUnit * 8
|
||||||
|
|
||||||
|
title: i18nc("@title", "Open Url")
|
||||||
|
standardButtons: Kirigami.Dialog.Yes | Kirigami.Dialog.No
|
||||||
|
|
||||||
|
contentItem: QQC2.Label {
|
||||||
|
text: i18nc("Do you want to open <link>", "Do you want to open <b>%1</b>?", root.link)
|
||||||
|
wrapMode: QQC2.Label.Wrap
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
verticalAlignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
Qt.openUrlExternally(root.link);
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
onRejected: {
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -68,6 +68,16 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property Kirigami.Action scanAction: Kirigami.Action {
|
||||||
|
text: i18n("Scan a QR Code")
|
||||||
|
icon.name: "view-barcode-qr"
|
||||||
|
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "QrScannerPage.qml"), {
|
||||||
|
connection: root.connection
|
||||||
|
}, {
|
||||||
|
title: i18nc("@title", "Scan a QR Code")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Emitted when the text is changed in the search field.
|
* @brief Emitted when the text is changed in the search field.
|
||||||
*/
|
*/
|
||||||
@@ -130,6 +140,9 @@ RowLayout {
|
|||||||
QQC2.MenuItem {
|
QQC2.MenuItem {
|
||||||
action: spaceAction
|
action: spaceAction
|
||||||
}
|
}
|
||||||
|
QQC2.MenuItem {
|
||||||
|
action: scanAction
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Component {
|
Component {
|
||||||
@@ -177,6 +190,11 @@ RowLayout {
|
|||||||
onClicked: menuRoot.close()
|
onClicked: menuRoot.close()
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
Delegates.RoundedItemDelegate {
|
||||||
|
action: scanAction
|
||||||
|
onClicked: menuRoot.close()
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
75
src/qml/JoinRoomDialog.qml
Normal file
75
src/qml/JoinRoomDialog.qml
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls as QQC2
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
import org.kde.kirigamiaddons.components as KirigamiComponents
|
||||||
|
import org.kde.kirigamiaddons.formcard as FormCard
|
||||||
|
import org.kde.prison
|
||||||
|
|
||||||
|
import org.kde.neochat
|
||||||
|
|
||||||
|
Kirigami.Dialog {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string room
|
||||||
|
property NeoChatConnection connection
|
||||||
|
|
||||||
|
leftPadding: 0
|
||||||
|
rightPadding: 0
|
||||||
|
topPadding: 0
|
||||||
|
bottomPadding: 0
|
||||||
|
|
||||||
|
standardButtons: Kirigami.Dialog.NoButton
|
||||||
|
|
||||||
|
width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24)
|
||||||
|
title: i18nc("@title", "Join Room")
|
||||||
|
|
||||||
|
contentItem: ColumnLayout {
|
||||||
|
spacing: 0
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||||
|
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||||
|
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||||
|
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||||
|
spacing: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
KirigamiComponents.Avatar {
|
||||||
|
id: avatar
|
||||||
|
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
|
||||||
|
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
|
||||||
|
|
||||||
|
name: root.room.slice(1, -1)
|
||||||
|
initialsMode: KirigamiComponents.Avatar.UseInitials
|
||||||
|
}
|
||||||
|
|
||||||
|
Kirigami.Heading {
|
||||||
|
level: 1
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.bold: true
|
||||||
|
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
text: root.room
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Kirigami.Separator {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
FormCard.FormButtonDelegate {
|
||||||
|
text: i18nc("@action:button", "Join room")
|
||||||
|
icon.name: "irc-join-channel"
|
||||||
|
onClicked: {
|
||||||
|
RoomManager.resolveResource(root.room, "join");
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/qml/QrScannerPage.qml
Normal file
58
src/qml/QrScannerPage.qml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls as QQC2
|
||||||
|
import QtMultimedia
|
||||||
|
|
||||||
|
import org.kde.kirigami as Kirigami
|
||||||
|
import org.kde.prison.scanner as Prison
|
||||||
|
|
||||||
|
import org.kde.neochat
|
||||||
|
|
||||||
|
Kirigami.Page {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
title: i18nc("@title", "Scan a QR Code")
|
||||||
|
|
||||||
|
required property NeoChatConnection connection
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
Component.onCompleted: camera.start()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: root.QQC2.ApplicationWindow.window
|
||||||
|
function onClosing() {
|
||||||
|
root.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoOutput {
|
||||||
|
id: viewFinder
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
Prison.VideoScanner {
|
||||||
|
id: scanner
|
||||||
|
property string previousText: ""
|
||||||
|
formats: Prison.Format.QRCode | Prison.Format.Aztec
|
||||||
|
onResultChanged: {
|
||||||
|
if (result.text.length > 0 && result.text != scanner.previousText) {
|
||||||
|
RoomManager.resolveResource(result.text, "");
|
||||||
|
scanner.previousText = result.text;
|
||||||
|
}
|
||||||
|
root.closeDialog();
|
||||||
|
}
|
||||||
|
videoSink: viewFinder.videoSink
|
||||||
|
}
|
||||||
|
|
||||||
|
CaptureSession {
|
||||||
|
camera: Camera {
|
||||||
|
id: camera
|
||||||
|
}
|
||||||
|
imageCapture: ImageCapture {
|
||||||
|
id: imageCapture
|
||||||
|
}
|
||||||
|
videoOutput: viewFinder
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -238,9 +238,6 @@ Kirigami.Page {
|
|||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: RoomManager
|
target: RoomManager
|
||||||
function onShowUserDetail(user) {
|
|
||||||
root.showUserDetail(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onShowEventSource(eventId) {
|
function onShowEventSource(eventId) {
|
||||||
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
|
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet.qml'), {
|
||||||
@@ -286,18 +283,6 @@ Kirigami.Page {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showUserDetail(user) {
|
|
||||||
userDetailDialog.createObject(QQC2.ApplicationWindow.overlay, {
|
|
||||||
room: root.currentRoom,
|
|
||||||
user: root.currentRoom.getUser(user.id)
|
|
||||||
}).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: userDetailDialog
|
|
||||||
UserDetailDialog {}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: messageDelegateContextMenu
|
id: messageDelegateContextMenu
|
||||||
MessageDelegateContextMenu {
|
MessageDelegateContextMenu {
|
||||||
|
|||||||
@@ -16,9 +16,13 @@ import org.kde.neochat
|
|||||||
Kirigami.Dialog {
|
Kirigami.Dialog {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
// This dialog is sometimes used outside the context of a room, e.g., when scanning a user's QR code.
|
||||||
|
// Make sure that code is prepared to deal with this property being null
|
||||||
property NeoChatRoom room
|
property NeoChatRoom room
|
||||||
property var user
|
property var user
|
||||||
|
|
||||||
|
property NeoChatConnection connection
|
||||||
|
|
||||||
parent: applicationWindow().overlay
|
parent: applicationWindow().overlay
|
||||||
|
|
||||||
leftPadding: 0
|
leftPadding: 0
|
||||||
@@ -102,19 +106,19 @@ Kirigami.Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormButtonDelegate {
|
FormCard.FormButtonDelegate {
|
||||||
visible: !root.user.isLocalUser
|
visible: !root.user.isLocalUser && !!root.user.object
|
||||||
action: Kirigami.Action {
|
action: Kirigami.Action {
|
||||||
text: room.connection.isIgnored(root.user.object) ? i18n("Unignore this user") : i18n("Ignore this user")
|
text: !!root.user.object && root.connection.isIgnored(root.user.object) ? i18n("Unignore this user") : i18n("Ignore this user")
|
||||||
icon.name: "im-invisible-user"
|
icon.name: "im-invisible-user"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
root.close();
|
root.close();
|
||||||
room.connection.isIgnored(root.user.object) ? room.connection.removeFromIgnoredUsers(root.user.object) : room.connection.addToIgnoredUsers(root.user.object);
|
root.connection.isIgnored(root.user.object) ? root.connection.removeFromIgnoredUsers(root.user.object) : root.connection.addToIgnoredUsers(root.user.object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormButtonDelegate {
|
FormCard.FormButtonDelegate {
|
||||||
visible: !root.user.isLocalUser && room.canSendState("kick") && room.containsUser(root.user.id) && room.getUserPowerLevel(root.user.id) < room.getUserPowerLevel(root.room.connection.localUser.id)
|
visible: root.room && !root.user.isLocalUser && room.canSendState("kick") && room.containsUser(root.user.id) && room.getUserPowerLevel(root.user.id) < room.getUserPowerLevel(root.connection.localUser.id)
|
||||||
|
|
||||||
action: Kirigami.Action {
|
action: Kirigami.Action {
|
||||||
text: i18n("Kick this user")
|
text: i18n("Kick this user")
|
||||||
@@ -127,10 +131,10 @@ Kirigami.Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormButtonDelegate {
|
FormCard.FormButtonDelegate {
|
||||||
visible: !root.user.isLocalUser && room.canSendState("invite") && !room.containsUser(root.user.id)
|
visible: root.room && !root.user.isLocalUser && room.canSendState("invite") && !room.containsUser(root.user.id)
|
||||||
|
|
||||||
action: Kirigami.Action {
|
action: Kirigami.Action {
|
||||||
enabled: !room.isUserBanned(root.user.id)
|
enabled: root.room && !root.room.isUserBanned(root.user.id)
|
||||||
text: i18n("Invite this user")
|
text: i18n("Invite this user")
|
||||||
icon.name: "list-add-user"
|
icon.name: "list-add-user"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
@@ -141,7 +145,7 @@ Kirigami.Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormButtonDelegate {
|
FormCard.FormButtonDelegate {
|
||||||
visible: !root.user.isLocalUser && room.canSendState("ban") && !room.isUserBanned(root.user.id) && room.getUserPowerLevel(root.user.id) < room.getUserPowerLevel(root.room.connection.localUser.id)
|
visible: root.room && !root.user.isLocalUser && room.canSendState("ban") && !room.isUserBanned(root.user.id) && room.getUserPowerLevel(root.user.id) < room.getUserPowerLevel(root.room.connection.localUser.id)
|
||||||
|
|
||||||
action: Kirigami.Action {
|
action: Kirigami.Action {
|
||||||
text: i18n("Ban this user")
|
text: i18n("Ban this user")
|
||||||
@@ -161,7 +165,7 @@ Kirigami.Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormButtonDelegate {
|
FormCard.FormButtonDelegate {
|
||||||
visible: !root.user.isLocalUser && room.canSendState("ban") && room.isUserBanned(root.user.id)
|
visible: root.room && !root.user.isLocalUser && room.canSendState("ban") && room.isUserBanned(root.user.id)
|
||||||
|
|
||||||
action: Kirigami.Action {
|
action: Kirigami.Action {
|
||||||
text: i18n("Unban this user")
|
text: i18n("Unban this user")
|
||||||
@@ -175,7 +179,7 @@ Kirigami.Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormButtonDelegate {
|
FormCard.FormButtonDelegate {
|
||||||
visible: room.canSendState("m.room.power_levels")
|
visible: root.room && room.canSendState("m.room.power_levels")
|
||||||
action: Kirigami.Action {
|
action: Kirigami.Action {
|
||||||
text: i18n("Set user power level")
|
text: i18n("Set user power level")
|
||||||
icon.name: "visibility"
|
icon.name: "visibility"
|
||||||
@@ -199,7 +203,7 @@ Kirigami.Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FormCard.FormButtonDelegate {
|
FormCard.FormButtonDelegate {
|
||||||
visible: root.user.isLocalUser || room.canSendState("redact")
|
visible: root.room && (root.user.isLocalUser || room.canSendState("redact"))
|
||||||
|
|
||||||
action: Kirigami.Action {
|
action: Kirigami.Action {
|
||||||
text: i18n("Remove recent messages by this user")
|
text: i18n("Remove recent messages by this user")
|
||||||
@@ -221,10 +225,10 @@ Kirigami.Dialog {
|
|||||||
FormCard.FormButtonDelegate {
|
FormCard.FormButtonDelegate {
|
||||||
visible: !root.user.isLocalUser
|
visible: !root.user.isLocalUser
|
||||||
action: Kirigami.Action {
|
action: Kirigami.Action {
|
||||||
text: root.room.connection.directChatExists(root.user.object) ? i18nc("%1 is the name of the user.", "Chat with %1", root.user.escapedDisplayName) : i18n("Invite to private chat")
|
text: root.connection.directChatExists(root.user.object) ? i18nc("%1 is the name of the user.", "Chat with %1", root.user.escapedDisplayName) : i18n("Invite to private chat")
|
||||||
icon.name: "document-send"
|
icon.name: "document-send"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
root.room.connection.openOrCreateDirectChat(root.user.object);
|
root.connection.openOrCreateDirectChat(root.user.object);
|
||||||
root.close();
|
root.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,6 +136,17 @@ Kirigami.ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onAskJoinRoom(room) {
|
||||||
|
joinRoomDialog.createObject(applicationWindow(), {
|
||||||
|
room: room,
|
||||||
|
connection: root.connection
|
||||||
|
}).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onShowUserDetail(user) {
|
||||||
|
root.showUserDetail(user);
|
||||||
|
}
|
||||||
|
|
||||||
function onPushSpaceHome(room) {
|
function onPushSpaceHome(room) {
|
||||||
root.spaceHomePage = pageStack.push(Qt.createComponent('org.kde.neochat', 'SpaceHomePage.qml'));
|
root.spaceHomePage = pageStack.push(Qt.createComponent('org.kde.neochat', 'SpaceHomePage.qml'));
|
||||||
root.spaceHomePage.forceActiveFocus();
|
root.spaceHomePage.forceActiveFocus();
|
||||||
@@ -189,6 +200,11 @@ Kirigami.ApplicationWindow {
|
|||||||
user: user
|
user: user
|
||||||
}).open();
|
}).open();
|
||||||
}
|
}
|
||||||
|
function onExternalUrl(url) {
|
||||||
|
let dialog = Qt.createComponent("org.kde.neochat", "ConfirmUrlDialog.qml").createObject(applicationWindow());
|
||||||
|
dialog.link = url;
|
||||||
|
dialog.open();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function pushReplaceLayer(page, args) {
|
function pushReplaceLayer(page, args) {
|
||||||
@@ -404,6 +420,11 @@ Kirigami.ApplicationWindow {
|
|||||||
RoomWindow {}
|
RoomWindow {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: joinRoomDialog
|
||||||
|
JoinRoomDialog {}
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: askDirectChatConfirmationComponent
|
id: askDirectChatConfirmationComponent
|
||||||
|
|
||||||
@@ -481,4 +502,16 @@ Kirigami.ApplicationWindow {
|
|||||||
dialog.closeDialog()
|
dialog.closeDialog()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
function showUserDetail(user) {
|
||||||
|
userDetailDialog.createObject(root.QQC2.ApplicationWindow.window, {
|
||||||
|
room: RoomManager.currentRoom ? RoomManager.currentRoom : null,
|
||||||
|
user: RoomManager.currentRoom ? RoomManager.currentRoom.getUser(user.id) : QmlUtils.getUser(user),
|
||||||
|
connection: root.connection
|
||||||
|
}).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: userDetailDialog
|
||||||
|
UserDetailDialog {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,9 @@ void RoomManager::resolveResource(const QString &idOrUri, const QString &action)
|
|||||||
|
|
||||||
const auto result = visitResource(m_connection, uri);
|
const auto result = visitResource(m_connection, uri);
|
||||||
if (result == Quotient::CouldNotResolve) {
|
if (result == Quotient::CouldNotResolve) {
|
||||||
Q_EMIT warning(i18n("Room not found"), i18n("There's no room %1 in the room list. Check the spelling and the account.", idOrUri));
|
if (uri.type() == Uri::RoomAlias || uri.type() == Uri::RoomId) {
|
||||||
|
Q_EMIT askJoinRoom(uri.primaryId());
|
||||||
|
}
|
||||||
} else { // Invalid cases should have been eliminated earlier
|
} else { // Invalid cases should have been eliminated earlier
|
||||||
Q_ASSERT(result == Quotient::UriResolved);
|
Q_ASSERT(result == Quotient::UriResolved);
|
||||||
|
|
||||||
@@ -355,19 +357,7 @@ void RoomManager::knockRoom(Quotient::Connection *account, const QString &roomAl
|
|||||||
|
|
||||||
bool RoomManager::visitNonMatrix(const QUrl &url)
|
bool RoomManager::visitNonMatrix(const QUrl &url)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_ANDROID
|
Q_EMIT externalUrl(url);
|
||||||
if (!QDesktopServices::openUrl(url)) {
|
|
||||||
Q_EMIT warning(i18n("No application for the link"), i18n("Your operating system could not find an application for the link."));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
auto *job = new KIO::OpenUrlJob(url);
|
|
||||||
connect(job, &KJob::finished, this, [this](KJob *job) {
|
|
||||||
if (job->error()) {
|
|
||||||
Q_EMIT warning(i18n("Could not open URL"), job->errorString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
job->start();
|
|
||||||
#endif
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -215,6 +215,9 @@ public:
|
|||||||
void setConnection(NeoChatConnection *connection);
|
void setConnection(NeoChatConnection *connection);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
/** Ask the user whether the room should be joined. */
|
||||||
|
void askJoinRoom(const QString &nameOrId);
|
||||||
|
|
||||||
void currentRoomChanged();
|
void currentRoomChanged();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -337,6 +340,8 @@ Q_SIGNALS:
|
|||||||
void directChatsActiveChanged();
|
void directChatsActiveChanged();
|
||||||
void lastSpaceIdChanged();
|
void lastSpaceIdChanged();
|
||||||
|
|
||||||
|
void externalUrl(const QUrl &url);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void openRoomForActiveConnection();
|
void openRoomForActiveConnection();
|
||||||
|
|
||||||
|
|||||||
43
src/utils.cpp
Normal file
43
src/utils.cpp
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||||
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
using namespace Quotient;
|
||||||
|
|
||||||
|
static const QVariantMap emptyUser = {
|
||||||
|
{"isLocalUser"_ls, false},
|
||||||
|
{"id"_ls, QString()},
|
||||||
|
{"displayName"_ls, QString()},
|
||||||
|
{"avatarSource"_ls, QUrl()},
|
||||||
|
{"avatarMediaId"_ls, QString()},
|
||||||
|
{"color"_ls, QColor()},
|
||||||
|
{"object"_ls, QVariant()},
|
||||||
|
};
|
||||||
|
|
||||||
|
QVariantMap QmlUtils::getUser(User *user) const
|
||||||
|
{
|
||||||
|
if (user == nullptr) {
|
||||||
|
return emptyUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &url = user->avatarUrl();
|
||||||
|
if (url.isEmpty() || url.scheme() != "mxc"_ls) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto avatarSource = user->connection()->makeMediaUrl(url);
|
||||||
|
if (!avatarSource.isValid() || avatarSource.scheme() != QStringLiteral("mxc")) {
|
||||||
|
avatarSource = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariantMap{
|
||||||
|
{QStringLiteral("isLocalUser"), user->id() == user->connection()->user()->id()},
|
||||||
|
{QStringLiteral("id"), user->id()},
|
||||||
|
{QStringLiteral("displayName"), user->displayname()},
|
||||||
|
{QStringLiteral("escapedDisplayName"), user->displayname().toHtmlEscaped()},
|
||||||
|
{QStringLiteral("avatarSource"), avatarSource},
|
||||||
|
{QStringLiteral("avatarMediaId"), user->avatarMediaId()},
|
||||||
|
{QStringLiteral("color"), Utils::getUserColor(user->hueF())},
|
||||||
|
{QStringLiteral("object"), QVariant::fromValue(user)},
|
||||||
|
};
|
||||||
|
}
|
||||||
29
src/utils.h
29
src/utils.h
@@ -4,8 +4,37 @@
|
|||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
|
#include <QQmlEngine>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
|
#include <Quotient/connection.h>
|
||||||
|
#include <Quotient/user.h>
|
||||||
|
|
||||||
|
class QmlUtils : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
QML_SINGLETON
|
||||||
|
|
||||||
|
public:
|
||||||
|
static QmlUtils *create(QQmlEngine *, QJSEngine *)
|
||||||
|
{
|
||||||
|
QQmlEngine::setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
|
||||||
|
return &instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
static QmlUtils &instance()
|
||||||
|
{
|
||||||
|
static QmlUtils _instance;
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE QVariantMap getUser(Quotient::User *user) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QmlUtils() = default;
|
||||||
|
};
|
||||||
|
|
||||||
namespace Utils
|
namespace Utils
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user