diff --git a/packages/web-pkg/src/composables/actions/files/index.ts b/packages/web-pkg/src/composables/actions/files/index.ts index ccf393cc0a..ba68fdac87 100644 --- a/packages/web-pkg/src/composables/actions/files/index.ts +++ b/packages/web-pkg/src/composables/actions/files/index.ts @@ -28,3 +28,4 @@ export * from './useFileActionsShowDetails' export * from './useFileActionsShowShares' export * from './useFileActionsToggleHideShare' export * from './useFileActionsUndoDelete' +export * from './useFileActionFallbackToDownload' diff --git a/packages/web-pkg/src/composables/actions/files/useFileActionFallbackToDownload.ts b/packages/web-pkg/src/composables/actions/files/useFileActionFallbackToDownload.ts new file mode 100644 index 0000000000..af94f1b3c2 --- /dev/null +++ b/packages/web-pkg/src/composables/actions/files/useFileActionFallbackToDownload.ts @@ -0,0 +1,44 @@ +import { FileAction, FileActionOptions } from '../types' +import { computed, unref } from 'vue' +import { useGettext } from 'vue3-gettext' +import { useDownloadFile } from '../../download' +import { useModals } from '../../piniaStores' +import { useFileActionsDownloadFile } from './useFileActionsDownloadFile' + +export const useFileActionFallbackToDownload = () => { + const { $gettext } = useGettext() + const { actions: downloadFileActions } = useFileActionsDownloadFile() + const { downloadFile } = useDownloadFile() + const { dispatchModal } = useModals() + const handler = ({ space, resources }: FileActionOptions) => { + dispatchModal({ + title: $gettext('No preview available for »%{name}«', { name: resources[0].name }), + confirmText: $gettext('Download'), + message: $gettext( + 'There is no preview available for this file. Do you want to download it instead?' + ), + onConfirm: () => { + downloadFile(space, resources[0]) + } + }) + } + + const actions = computed((): FileAction[] => [ + { + name: 'fallback-to-download', + icon: 'file-download', + handler, + label: () => { + return $gettext('Download') + }, + isVisible: (options) => { + return unref(downloadFileActions)[0].isVisible(options) + }, + class: 'oc-files-actions-fallback-to-download-trigger' + } + ]) + + return { + actions + } +} diff --git a/packages/web-pkg/src/composables/actions/files/useFileActions.ts b/packages/web-pkg/src/composables/actions/files/useFileActions.ts index e9e196ca2e..75e91ba931 100644 --- a/packages/web-pkg/src/composables/actions/files/useFileActions.ts +++ b/packages/web-pkg/src/composables/actions/files/useFileActions.ts @@ -9,6 +9,7 @@ import { Action, FileAction, FileActionOptions, + useFileActionFallbackToDownload, useIsFilesAppActive, useIsSearchActive, useWindowOpen @@ -68,6 +69,7 @@ export const useFileActions = () => { const { actions: disableSyncActions } = useFileActionsDisableSync() const { actions: downloadArchiveActions } = useFileActionsDownloadArchive() const { actions: downloadFileActions } = useFileActionsDownloadFile() + const { actions: fallbackToDownloadAction } = useFileActionFallbackToDownload() const { actions: favoriteActions } = useFileActionsFavorite() const { actions: moveActions } = useFileActionsMove() const { actions: navigateActions } = useFileActionsNavigate() @@ -248,8 +250,11 @@ export const useFileActions = () => { const getDefaultAction = (options: GetFileActionsOptions): Action | undefined => { const actions = getAllOpenWithActions(options) + if (actions.length) { - return actions[0] + return actions[0].name === unref(downloadFileActions)[0].name + ? unref(fallbackToDownloadAction)[0] + : actions[0] } return undefined } diff --git a/packages/web-pkg/tests/unit/composables/actions/files/useFileActionFallbackToDownload.spec.ts b/packages/web-pkg/tests/unit/composables/actions/files/useFileActionFallbackToDownload.spec.ts new file mode 100644 index 0000000000..018b9dff85 --- /dev/null +++ b/packages/web-pkg/tests/unit/composables/actions/files/useFileActionFallbackToDownload.spec.ts @@ -0,0 +1,98 @@ +import { mock } from 'vitest-mock-extended' +import { unref } from 'vue' +import { + defaultComponentMocks, + getComposableWrapper, + RouteLocation +} from '@opencloud-eu/web-test-helpers' +import { + useFileActionFallbackToDownload, + useModals, + useDownloadFile +} from '../../../../../src/composables' +import { Resource, SpaceResource } from '@opencloud-eu/web-client' + +vi.mock('../../../../../src/composables/download/useDownloadFile') + +describe('fallbackToDownload', () => { + describe('computed property "actions"', () => { + describe('method "handler"', () => { + it('creates a modal with download confirmation', () => { + getWrapper({ + setup: ({ actions }) => { + const resource = mock({ + name: 'test-file.png', + isFolder: false, + canDownload: () => true + }) + + const { dispatchModal } = useModals() + + unref(actions)[0].handler({ + resources: [resource], + space: mock() + }) + + expect(dispatchModal).toHaveBeenCalled() + } + }) + }) + + it('calls downloadFile when modal is confirmed', () => { + getWrapper({ + setup: ({ actions }) => { + const resource = mock({ + name: 'test-file.png', + isFolder: false, + canDownload: () => true + }) + + const { dispatchModal } = useModals() + + unref(actions)[0].handler({ + resources: [resource], + space: mock() + }) + + const modalCall = vi.mocked(dispatchModal).mock.calls[0][0] + modalCall.onConfirm(null) + + const downloadFileComposable = useDownloadFile() + expect(downloadFileComposable.downloadFile).toHaveBeenCalled() + } + }) + }) + }) + }) +}) + +function getWrapper({ + setup, + downloadFileMock = vi.fn() +}: { + setup: (instance: ReturnType) => void + downloadFileMock?: (...args: unknown[]) => unknown +}) { + vi.mocked(useDownloadFile).mockReturnValue({ + downloadFile: downloadFileMock + } as ReturnType) + + const mocks = { + ...defaultComponentMocks({ + currentRoute: mock({ name: 'files-spaces-generic' }) + }) + } + + return { + wrapper: getComposableWrapper( + () => { + const instance = useFileActionFallbackToDownload() + setup(instance) + }, + { + mocks, + provide: mocks + } + ) + } +}