aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/src/actions/openLocallyAction.ts
blob: a80cf0cbeed9bc8195dd6ca18b012e1174addd6a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/**
 * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */
import { encodePath } from '@nextcloud/paths'
import { generateOcsUrl } from '@nextcloud/router'
import { getCurrentUser } from '@nextcloud/auth'
import { FileAction, Permission, type Node } from '@nextcloud/files'
import { showError, DialogBuilder } from '@nextcloud/dialogs'
import { translate as t } from '@nextcloud/l10n'
import axios from '@nextcloud/axios'
import LaptopSvg from '@mdi/svg/svg/laptop.svg?raw'
import IconWeb from '@mdi/svg/svg/web.svg?raw'
import { isPublicShare } from '@nextcloud/sharing/public'

const confirmLocalEditDialog = (
	localEditCallback: (openingLocally: boolean) => void = () => {},
) => {
	let callbackCalled = false

	return (new DialogBuilder())
		.setName(t('files', 'Open file locally'))
		.setText(t('files', 'The file should now open on your device. If it doesn\'t, please check that you have the desktop app installed.'))
		.setButtons([
			{
				label: t('files', 'Retry and close'),
				type: 'secondary',
				callback: () => {
					callbackCalled = true
					localEditCallback(true)
				},
			},
			{
				label: t('files', 'Open online'),
				icon: IconWeb,
				type: 'primary',
				callback: () => {
					callbackCalled = true
					localEditCallback(false)
				},
			},
		])
		.build()
		.show()
		.then(() => {
			// Ensure the callback is called even if the dialog is dismissed in other ways
			if (!callbackCalled) {
				localEditCallback(false)
			}
		})
}

const attemptOpenLocalClient = async (path: string) => {
	openLocalClient(path)
	confirmLocalEditDialog(
		(openLocally: boolean) => {
			if (!openLocally) {
				window.OCA.Viewer.open({ path })
				return
			}
			openLocalClient(path)
		},
	)
}

const openLocalClient = async function(path: string) {
	const link = generateOcsUrl('apps/files/api/v1') + '/openlocaleditor?format=json'

	try {
		const result = await axios.post(link, { path })
		const uid = getCurrentUser()?.uid
		let url = `nc://open/${uid}@` + window.location.host + encodePath(path)
		url += '?token=' + result.data.ocs.data.token

		window.open(url, '_self')
	} catch (error) {
		showError(t('files', 'Failed to redirect to client'))
	}
}

export const action = new FileAction({
	id: 'edit-locally',
	displayName: () => t('files', 'Open locally'),
	iconSvgInline: () => LaptopSvg,

	// Only works on single files
	enabled(nodes: Node[]) {
		// Only works on single node
		if (nodes.length !== 1) {
			return false
		}

		// does not work with shares
		if (isPublicShare()) {
			return false
		}

		return (nodes[0].permissions & Permission.UPDATE) !== 0
	},

	async exec(node: Node) {
		attemptOpenLocalClient(node.path)
		return null
	},

	order: 25,
})