diff options
author | F. E Noel Nfebe <fenn25.fn@gmail.com> | 2024-07-31 14:21:22 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-31 14:21:22 +0100 |
commit | 656828a8bb65bf9c3234fcc3533e54a6a7630837 (patch) | |
tree | 76ca6ab449542937cb10734a07165e00f291383f /apps | |
parent | 24d5c22ba523ff812e8828fdaca98b92778de269 (diff) | |
parent | 52ff995a445c131c2b9607b0c9f0335cfc26ba3d (diff) | |
download | nextcloud-server-656828a8bb65bf9c3234fcc3533e54a6a7630837.tar.gz nextcloud-server-656828a8bb65bf9c3234fcc3533e54a6a7630837.zip |
Merge pull request #46909 from nextcloud/backport/46452/stable28
[stable28] feat(editLocallyAction): Handle possible no local client scenario
Diffstat (limited to 'apps')
-rw-r--r-- | apps/files/src/actions/editLocallyAction.spec.ts | 47 | ||||
-rw-r--r-- | apps/files/src/actions/editLocallyAction.ts | 57 |
2 files changed, 92 insertions, 12 deletions
diff --git a/apps/files/src/actions/editLocallyAction.spec.ts b/apps/files/src/actions/editLocallyAction.spec.ts index e7102b8defb..3d32930c273 100644 --- a/apps/files/src/actions/editLocallyAction.spec.ts +++ b/apps/files/src/actions/editLocallyAction.spec.ts @@ -22,14 +22,35 @@ import { action } from './editLocallyAction' import { expect } from '@jest/globals' import { File, Permission, View, FileAction } from '@nextcloud/files' -import * as ncDialogs from '@nextcloud/dialogs' +import { DialogBuilder, showError } from '@nextcloud/dialogs' import axios from '@nextcloud/axios' +const dialogBuilder = { + setName: jest.fn().mockReturnThis(), + setText: jest.fn().mockReturnThis(), + setButtons: jest.fn().mockReturnThis(), + build: jest.fn().mockReturnValue({ + show: jest.fn().mockResolvedValue(true), + }), +} as unknown as DialogBuilder + +jest.mock('@nextcloud/dialogs', () => ({ + DialogBuilder: jest.fn(() => dialogBuilder), + showError: jest.fn(), +})) + const view = { id: 'files', name: 'Files', } as View +// Mock webroot variable +beforeAll(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (window as any)._oc_webroot = ''; + (window as any).OCA = { Viewer: { open: jest.fn() } } +}) + describe('Edit locally action conditions tests', () => { test('Default values', () => { expect(action).toBeInstanceOf(FileAction) @@ -55,7 +76,7 @@ describe('Edit locally action enabled tests', () => { expect(action.enabled!([file], view)).toBe(true) }) - test('Disabled for non-dav ressources', () => { + test('Disabled for non-dav resources', () => { const file = new File({ id: 1, source: 'https://domain.com/data/foobar.txt', @@ -115,8 +136,11 @@ describe('Edit locally action enabled tests', () => { describe('Edit locally action execute tests', () => { test('Edit locally opens proper URL', async () => { - jest.spyOn(axios, 'post').mockImplementation(async () => ({ data: { ocs: { data: { token: 'foobar' } } } })) - jest.spyOn(ncDialogs, 'showError') + jest.spyOn(axios, 'post').mockImplementation(async () => ({ + data: { ocs: { data: { token: 'foobar' } } } + })) + const mockedShowError = jest.mocked(showError) + const spyDialogBuilder = jest.spyOn(dialogBuilder, 'build') const file = new File({ id: 1, @@ -128,17 +152,20 @@ describe('Edit locally action execute tests', () => { const exec = await action.exec(file, view, '/') + expect(spyDialogBuilder).toBeCalled() + // Silent action expect(exec).toBe(null) expect(axios.post).toBeCalledTimes(1) expect(axios.post).toBeCalledWith('http://localhost/ocs/v2.php/apps/files/api/v1/openlocaleditor?format=json', { path: '/foobar.txt' }) - expect(ncDialogs.showError).toBeCalledTimes(0) + expect(mockedShowError).toBeCalledTimes(0) expect(window.location.href).toBe('nc://open/test@localhost/foobar.txt?token=foobar') }) - test('Edit locally fails and show error', async () => { + test('Edit locally fails and shows error', async () => { jest.spyOn(axios, 'post').mockImplementation(async () => ({})) - jest.spyOn(ncDialogs, 'showError') + const mockedShowError = jest.mocked(showError) + const spyDialogBuilder = jest.spyOn(dialogBuilder, 'build') const file = new File({ id: 1, @@ -150,12 +177,14 @@ describe('Edit locally action execute tests', () => { const exec = await action.exec(file, view, '/') + expect(spyDialogBuilder).toBeCalled() + // Silent action expect(exec).toBe(null) expect(axios.post).toBeCalledTimes(1) expect(axios.post).toBeCalledWith('http://localhost/ocs/v2.php/apps/files/api/v1/openlocaleditor?format=json', { path: '/foobar.txt' }) - expect(ncDialogs.showError).toBeCalledTimes(1) - expect(ncDialogs.showError).toBeCalledWith('Failed to redirect to client') + expect(mockedShowError).toBeCalledTimes(1) + expect(mockedShowError).toBeCalledWith('Failed to redirect to client') expect(window.location.href).toBe('http://localhost/') }) }) diff --git a/apps/files/src/actions/editLocallyAction.ts b/apps/files/src/actions/editLocallyAction.ts index f52f8191df8..38393b919d5 100644 --- a/apps/files/src/actions/editLocallyAction.ts +++ b/apps/files/src/actions/editLocallyAction.ts @@ -23,11 +23,62 @@ 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 } from '@nextcloud/dialogs' +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 IconCancel from '@mdi/svg/svg/cancel.svg?raw' +import IconCheck from '@mdi/svg/svg/check.svg?raw' + +const confirmLocalEditDialog = ( + localEditCallback: (openingLocally: boolean) => void = () => {}, +) => { + let callbackCalled = false + + return (new DialogBuilder()) + .setName(t('files', 'Edit file locally')) + .setText(t('files', 'The file should now open locally. If you don\'t see this happening, make sure that the desktop client is installed on your system.')) + .setButtons([ + { + label: t('files', 'Retry local edit'), + icon: IconCancel, + callback: () => { + callbackCalled = true + localEditCallback(false) + }, + }, + { + label: t('files', 'Edit online'), + icon: IconCheck, + type: 'primary', + callback: () => { + callbackCalled = true + localEditCallback(true) + }, + }, + ]) + .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' @@ -60,7 +111,7 @@ export const action = new FileAction({ }, async exec(node: Node) { - openLocalClient(node.path) + attemptOpenLocalClient(node.path) return null }, |