Implement sharing with Purpose (export)

This provide both a mobile and desktop view

Fix #181
This commit is contained in:
Carl Schwan
2022-02-05 16:30:02 +00:00
parent 3e6f38c8ea
commit ca8a21c0eb
14 changed files with 531 additions and 63 deletions

View File

@@ -0,0 +1,71 @@
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
import QtQuick 2.7
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.15 as Controls
import org.kde.kirigami 2.14 as Kirigami
/**
* Action that allows an user to share data with other apps and service
* installed on their computer. The goal of this high level API is to
* adapte itself for each platform and adopt the native component.
*
* TODO add Android support
*/
Kirigami.Action {
id: shareAction
iconName: "emblem-shared-symbolic"
text: i18n("Share")
tooltip: i18n("Share the selected media")
property var doBeforeSharing: () => {}
visible: false
/**
* This property holds the input data for purpose.
*
* @code{.qml}
* Purpose.ShareAction {
* inputData: {
* 'urls': ['file://home/notroot/Pictures/mypicture.png'],
* 'mimeType': ['image/png']
* }
* }
* @endcode
*/
property var inputData: ({})
property Instantiator _instantiator: Instantiator {
Component.onCompleted: {
const purposeModel = Qt.createQmlObject('import org.kde.purpose 1.0 as Purpose;
Purpose.PurposeAlternativesModel {
pluginType: "Export"
}', shareAction._instantiator);
purposeModel.inputData = Qt.binding(function() {
return shareAction.inputData;
});
_instantiator.model = purposeModel;
shareAction.visible = true;
}
delegate: Kirigami.Action {
property int index
text: model.display
icon.name: model.iconName
onTriggered: {
doBeforeSharing();
applicationWindow().pageStack.pushDialogLayer('qrc:/imports/NeoChat/Menu/ShareDialog.qml', {
title: shareAction.tooltip,
index: index,
model: shareAction._instantiator.model
})
}
}
onObjectAdded: {
object.index = index;
shareAction.children.push(object)
}
onObjectRemoved: shareAction.children = Array.from(shareAction.children).filter(obj => obj.pluginId !== object.pluginId)
}
}

View File

@@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
import org.kde.kirigami 2.14 as Kirigami
Kirigami.Action {
property var inputData: ({})
property var doBeforeSharing: () => {}
visible: false
}

View File

@@ -0,0 +1,69 @@
/*
* SPDX-FileCopyrightText: 2017 Atul Sharma <atulsharma406@gmail.com>
* SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.7
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.15 as Controls
import org.kde.purpose 1.0 as Purpose
import org.kde.notification 1.0
import org.kde.kirigami 2.14 as Kirigami
Kirigami.Page {
id: window
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
property alias index: jobView.index
property alias model: jobView.model
Controls.Action {
shortcut: 'Escape'
onTriggered: window.closeDialog()
}
Notification {
id: sharingFailed
eventId: "sharingFailed"
text: i18n("Sharing failed")
urgency: Notification.NormalUrgency
}
Notification {
id: sharingSuccess
eventId: "sharingSuccess"
flags: Notification.Persistent
}
Component.onCompleted: jobView.start()
contentItem: Purpose.JobView {
id: jobView
onStateChanged: {
if (state === Purpose.PurposeJobController.Finished) {
if (jobView.job.output.url !== "") {
// Show share url
// TODO no needed anymore in purpose > 5.90
sharingSuccess.text = i18n("Shared url for image is <a href='%1'>%1</a>", jobView.output.url);
sharingSuccess.sendEvent();
Clipboard.saveText(jobView.output.url);
}
window.closeDialog()
} else if (state === Purpose.PurposeJobController.Error) {
// Show failure notification
sharingFailed.sendEvent();
window.closeDialog()
} else if (state === Purpose.PurposeJobController.Cancelled) {
// Do nothing
window.closeDialog()
}
}
}
}

View File

@@ -16,6 +16,7 @@ MessageDelegateContextMenu {
required property var file
required property var progressInfo
required property string mimeType
property list<Kirigami.Action> actions: [
Kirigami.Action {
@@ -74,6 +75,27 @@ MessageDelegateContextMenu {
}
}
]
property list<Kirigami.Action> nestedActions: [
ShareAction {
id: shareAction
inputData: {
'urls': [],
'mimeType': [mimeType]
}
property string filename: StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId);
doBeforeSharing: () => {
currentRoom.downloadFile(eventId, filename)
}
Component.onCompleted: {
shareAction.inputData = {
urls: [filename],
mimeType: [mimeType]
};
}
}
]
Component {
id: saveAsDialog
FileDialog {

View File

@@ -21,6 +21,8 @@ Loader {
required property string source
property string selectedText: ""
property list<Kirigami.Action> nestedActions
property list<Kirigami.Action> actions: [
Kirigami.Action {
text: i18n("Edit")
@@ -63,9 +65,36 @@ Loader {
id: regularMenu
QQC2.Menu {
id: menu
Instantiator {
model: loadRoot.nestedActions
delegate: QQC2.Menu {
id: menuItem
visible: modelData.visible
title: modelData.text
Instantiator {
model: modelData.children
delegate: QQC2.MenuItem {
text: modelData.text
icon.name: modelData.icon.name
onTriggered: modelData.trigger()
}
onObjectAdded: {
menuItem.insertItem(0, object)
}
}
}
onObjectAdded: {
object.visible = false;
menu.addMenu(object)
}
}
Repeater {
model: loadRoot.actions
QQC2.MenuItem {
id: menuItem
visible: modelData.visible
action: modelData
onClicked: loadRoot.item.close();
@@ -106,7 +135,7 @@ Loader {
Kirigami.OverlayDrawer {
id: drawer
height: popupContent.implicitHeight
height: stackView.implicitHeight
edge: Qt.BottomEdge
padding: 0
leftPadding: 0
@@ -116,85 +145,146 @@ Loader {
parent: applicationWindow().overlay
ColumnLayout {
id: popupContent
QQC2.StackView {
id: stackView
width: parent.width
spacing: 0
RowLayout {
id: headerLayout
Layout.fillWidth: true
Layout.margins: Kirigami.Units.largeSpacing
spacing: Kirigami.Units.largeSpacing
Kirigami.Avatar {
id: avatar
source: author.avatarMediaId ? ("image://mxc/" + author.avatarMediaId) : ""
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
Layout.preferredHeight: Kirigami.Units.gridUnit * 3
Layout.alignment: Qt.AlignTop
}
implicitHeight: currentItem.implicitHeight
Component {
id: nestedActionsComponent
ColumnLayout {
Layout.fillWidth: true
Kirigami.Heading {
level: 3
Layout.fillWidth: true
text: currentRoom.htmlSafeMemberName(author.id)
wrapMode: Text.WordWrap
id: actionLayout
property string title: ""
property list<Kirigami.Action> actions
width: parent.width
spacing: 0
RowLayout {
QQC2.ToolButton {
icon.name: 'draw-arrow-back'
onClicked: stackView.pop()
}
Kirigami.Heading {
level: 3
Layout.fillWidth: true
text: actionLayout.title
wrapMode: Text.WordWrap
}
}
QQC2.Label {
text: message
Layout.fillWidth: true
wrapMode: Text.WordWrap
Repeater {
id: listViewAction
model: actionLayout.actions
onLinkActivated: RoomManager.openResource(link);
Kirigami.BasicListItem {
icon: modelData.icon.name
iconColor: modelData.icon.color ?? undefined
enabled: modelData.enabled
visible: modelData.visible
text: modelData.text
onClicked: {
modelData.triggered()
loadRoot.item.close();
}
implicitHeight: visible ? Kirigami.Units.gridUnit * 3 : 0
}
}
}
}
Kirigami.Separator {
Layout.fillWidth: true
}
RowLayout {
initialItem: ColumnLayout {
id: popupContent
width: parent.width
spacing: 0
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 2.5
Repeater {
model: ["👍", "👎️", "😄", "🎉", "🚀", "👀"]
delegate: QQC2.ItemDelegate {
RowLayout {
id: headerLayout
Layout.fillWidth: true
Layout.margins: Kirigami.Units.largeSpacing
spacing: Kirigami.Units.largeSpacing
Kirigami.Avatar {
id: avatar
source: author.avatarMediaId ? ("image://mxc/" + author.avatarMediaId) : ""
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
Layout.preferredHeight: Kirigami.Units.gridUnit * 3
Layout.alignment: Qt.AlignTop
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
contentItem: Kirigami.Heading {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.family: "emoji"
text: modelData
Kirigami.Heading {
level: 3
Layout.fillWidth: true
text: currentRoom.htmlSafeMemberName(author.id)
wrapMode: Text.WordWrap
}
QQC2.Label {
text: message
Layout.fillWidth: true
wrapMode: Text.WordWrap
onLinkActivated: RoomManager.openResource(link);
}
}
}
Kirigami.Separator {
Layout.fillWidth: true
}
RowLayout {
spacing: 0
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 2.5
Repeater {
model: ["👍", "👎️", "😄", "🎉", "🚀", "👀"]
delegate: QQC2.ItemDelegate {
Layout.fillWidth: true
Layout.fillHeight: true
contentItem: Kirigami.Heading {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.family: "emoji"
text: modelData
}
onClicked: {
currentRoom.toggleReaction(eventId, modelData);
loadRoot.item.close();
}
}
}
}
Kirigami.Separator {
Layout.fillWidth: true
}
Repeater {
id: listViewAction
model: loadRoot.actions
Kirigami.BasicListItem {
icon: modelData.icon.name
iconColor: modelData.icon.color ?? undefined
enabled: modelData.enabled
visible: modelData.visible
text: modelData.text
onClicked: {
currentRoom.toggleReaction(eventId, modelData);
modelData.triggered()
loadRoot.item.close();
}
implicitHeight: visible ? Kirigami.Units.gridUnit * 3 : 0
}
}
}
Kirigami.Separator {
Layout.fillWidth: true
}
Repeater {
id: listViewAction
model: loadRoot.actions
Kirigami.BasicListItem {
icon: modelData.icon.name
iconColor: modelData.icon.color ?? undefined
enabled: modelData.enabled
visible: modelData.visible
text: modelData.text
onClicked: {
modelData.triggered()
loadRoot.item.close();
Repeater {
model: loadRoot.nestedActions
Kirigami.BasicListItem {
action: modelData
visible: modelData.visible
implicitHeight: visible ? Kirigami.Units.gridUnit * 3 : 0
onClicked: {
stackView.push(nestedActionsComponent, {
title: modelData.text,
actions: modelData.children
});
}
}
implicitHeight: visible ? Kirigami.Units.gridUnit * 3 : 0
}
}
}

View File

@@ -2,3 +2,5 @@ module NeoChat.Menu
RoomListContextMenu 1.0 RoomListContextMenu.qml
GlobalMenu 1.0 GlobalMenu.qml
EditMenu 1.0 EditMenu.qml
ShareAction 1.0 ShareAction.qml
ShareDialog 1.0 ShareDialog.qml