From bbe7bc56891e88e01117c37f3fa8db4959b6a7c8 Mon Sep 17 00:00:00 2001 From: Jan Ackermann Date: Mon, 16 Sep 2024 16:51:34 +0200 Subject: [PATCH 01/10] Add save as --- .../components/AppTemplates/AppWrapper.vue | 18 +- .../src/components/Modals/SaveAsModal.vue | 154 ++++++++++++++++++ .../web-pkg/src/components/Modals/index.ts | 1 + .../src/composables/actions/files/index.ts | 1 + .../actions/files/useFileActionsSaveAs.ts | 53 ++++++ 5 files changed, 223 insertions(+), 4 deletions(-) create mode 100644 packages/web-pkg/src/components/Modals/SaveAsModal.vue create mode 100644 packages/web-pkg/src/composables/actions/files/useFileActionsSaveAs.ts diff --git a/packages/web-pkg/src/components/AppTemplates/AppWrapper.vue b/packages/web-pkg/src/components/AppTemplates/AppWrapper.vue index e593cb8ba32..ab3a169e1da 100644 --- a/packages/web-pkg/src/components/AppTemplates/AppWrapper.vue +++ b/packages/web-pkg/src/components/AppTemplates/AppWrapper.vue @@ -67,7 +67,8 @@ import { useFileActionsShowShares, FileActionOptions, FileAction, - useLoadingService + useLoadingService, + useFileActionsSaveAs } from '../../composables' import { Action, @@ -161,6 +162,8 @@ export default defineComponent({ const serverContent = ref() const currentContent = ref() + const { actions: saveAsActions } = useFileActionsSaveAs({ content: currentContent }) + const isEditor = computed(() => { return Boolean(props.wrappedComponent.emits?.includes('update:currentContent')) }) @@ -479,9 +482,16 @@ export default defineComponent({ } const menuItemsContext = computed(() => { - return [...unref(openWithAppActions), ...unref(fileActionsSave)].filter((item) => - item.isVisible(unref(actionOptions)) - ) + return [ + ...unref(openWithAppActions), + ...unref(fileActionsSave), + ...unref(saveAsActions).map((action) => { + return { + ...action, + isVisible: (args: FileActionOptions) => isEditor.value && action.isVisible(args) + } + }) + ].filter((item) => item.isVisible(unref(actionOptions))) }) const menuItemsShare = computed(() => { return [...unref(showSharesActions), ...unref(createQuickLinkActions)].filter((item) => diff --git a/packages/web-pkg/src/components/Modals/SaveAsModal.vue b/packages/web-pkg/src/components/Modals/SaveAsModal.vue new file mode 100644 index 00000000000..083fcab1631 --- /dev/null +++ b/packages/web-pkg/src/components/Modals/SaveAsModal.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/packages/web-pkg/src/components/Modals/index.ts b/packages/web-pkg/src/components/Modals/index.ts index ab3922b7dcf..607684e2c33 100644 --- a/packages/web-pkg/src/components/Modals/index.ts +++ b/packages/web-pkg/src/components/Modals/index.ts @@ -2,3 +2,4 @@ export { default as ResourceConflictModal } from './ResourceConflictModal.vue' export { default as SpaceMoveInfoModal } from './SpaceMoveInfoModal.vue' export { default as EmojiPickerModal } from './EmojiPickerModal.vue' export { default as FilePickerModal } from './FilePickerModal.vue' +export { default as SaveAsModal } from './SaveAsModal.vue' diff --git a/packages/web-pkg/src/composables/actions/files/index.ts b/packages/web-pkg/src/composables/actions/files/index.ts index cd92f89c3a5..f9b42b934a5 100644 --- a/packages/web-pkg/src/composables/actions/files/index.ts +++ b/packages/web-pkg/src/composables/actions/files/index.ts @@ -25,3 +25,4 @@ export * from './useFileActionsCreateNewShortcut' export * from './useFileActionsOpenShortcut' export * from './useFileActionsCreateLink' export * from './useFileActionsOpenWithApp' +export * from './useFileActionsSaveAs' diff --git a/packages/web-pkg/src/composables/actions/files/useFileActionsSaveAs.ts b/packages/web-pkg/src/composables/actions/files/useFileActionsSaveAs.ts new file mode 100644 index 00000000000..c600ca5381e --- /dev/null +++ b/packages/web-pkg/src/composables/actions/files/useFileActionsSaveAs.ts @@ -0,0 +1,53 @@ +import { FileAction, FileActionOptions } from '../types' +import { computed, unref, Ref } from 'vue' +import { useGettext } from 'vue3-gettext' +import { useAppsStore, useModals } from '../../piniaStores' +import { storeToRefs } from 'pinia' +import SaveAsModal from '../../../components/Modals/SaveAsModal.vue' +import { useFolderLink } from '../../folderLink' +import { useIsFilesAppActive } from '../helpers' + +export const useFileActionsSaveAs = ({ content }: { content: Ref }) => { + const { $gettext } = useGettext() + const isFilesAppActive = useIsFilesAppActive() + const { dispatchModal } = useModals() + const appsStore = useAppsStore() + const { apps } = storeToRefs(appsStore) + const { getParentFolderLink } = useFolderLink() + + const handler = ({ resources }: FileActionOptions) => { + const parentFolderLink = getParentFolderLink(resources[0]) + + dispatchModal({ + elementClass: 'save-as-modal', + title: $gettext('Save as'), + customComponent: SaveAsModal, + hideActions: true, + customComponentAttrs: () => ({ + content: unref(content), + parentFolderLink, + originalResource: resources[0] + }), + focusTrapInitial: false + }) + } + + const actions = computed((): FileAction[] => [ + { + name: 'save-as', + icon: 'save-2', + handler, + label: () => { + return $gettext('Save as') + }, + isVisible: ({ resources }) => { + return !unref(isFilesAppActive) || resources.length !== 1 + }, + class: 'oc-files-actions-save-as-trigger' + } + ]) + + return { + actions + } +} From 0fbbd11b7bdce348a07e180a35a6122b4ad05fd1 Mon Sep 17 00:00:00 2001 From: Jan Ackermann Date: Tue, 17 Sep 2024 14:47:10 +0200 Subject: [PATCH 02/10] Disable trash in embed mode --- packages/web-app-files/src/index.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/web-app-files/src/index.ts b/packages/web-app-files/src/index.ts index c84091314ea..c0f369dd1b5 100644 --- a/packages/web-app-files/src/index.ts +++ b/packages/web-app-files/src/index.ts @@ -12,6 +12,7 @@ import { ApplicationInformation, defineWebApplication, useCapabilityStore, + useEmbedMode, useSpacesStore, useUserStore } from '@ownclouders/web-pkg' @@ -22,7 +23,7 @@ import { AppNavigationItem } from '@ownclouders/web-pkg' // dirty: importing view from other extension within project import SearchResults from '../../web-app-search/src/views/List.vue' import { isPersonalSpaceResource, isShareSpaceResource } from '@ownclouders/web-client' -import { ComponentCustomProperties } from 'vue' +import { ComponentCustomProperties, unref } from 'vue' import { extensionPoints } from './extensionPoints' // just a dummy function to trick gettext tools @@ -42,6 +43,7 @@ export const navItems = (context: ComponentCustomProperties): AppNavigationItem[ const spacesStores = useSpacesStore() const userStore = useUserStore() const capabilityStore = useCapabilityStore() + const { isEnabled: isEmbedModeEnabled } = useEmbedMode() return [ { @@ -117,7 +119,11 @@ export const navItems = (context: ComponentCustomProperties): AppNavigationItem[ }, activeFor: [{ path: `/${appInfo.id}/trash` }], isVisible() { - return capabilityStore.davTrashbin === '1.0' && capabilityStore.filesUndelete + return ( + capabilityStore.davTrashbin === '1.0' && + capabilityStore.filesUndelete && + !unref(isEmbedModeEnabled) + ) }, priority: 50 } From 1e9812a8a13e1d232876ee2eb1a363d61edc9be3 Mon Sep 17 00:00:00 2001 From: Jan Ackermann Date: Tue, 17 Sep 2024 14:58:39 +0200 Subject: [PATCH 03/10] Fix bug where hide-logo query is not persistent --- .../web-pkg/src/composables/piniaStores/config/config.ts | 3 ++- packages/web-pkg/src/composables/piniaStores/config/types.ts | 3 ++- packages/web-runtime/src/components/Topbar/TopBar.vue | 5 +---- packages/web-runtime/src/container/bootstrap.ts | 3 ++- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/web-pkg/src/composables/piniaStores/config/config.ts b/packages/web-pkg/src/composables/piniaStores/config/config.ts index 201c1c3d583..5511545eb9a 100644 --- a/packages/web-pkg/src/composables/piniaStores/config/config.ts +++ b/packages/web-pkg/src/composables/piniaStores/config/config.ts @@ -36,7 +36,8 @@ const defaultOptions = { }, runningOnEos: false, tokenStorageLocal: true, - userListRequiresFilter: false + userListRequiresFilter: false, + hideLogo: false } satisfies Partial export const useConfigStore = defineStore('config', () => { diff --git a/packages/web-pkg/src/composables/piniaStores/config/types.ts b/packages/web-pkg/src/composables/piniaStores/config/types.ts index 7480306d819..3f3c56a6bb8 100644 --- a/packages/web-pkg/src/composables/piniaStores/config/types.ts +++ b/packages/web-pkg/src/composables/piniaStores/config/types.ts @@ -116,7 +116,8 @@ const OptionsConfigSchema = z.object({ companionUrl: z.string().optional() }) .optional(), - userListRequiresFilter: z.boolean().optional() + userListRequiresFilter: z.boolean().optional(), + hideLogo: z.boolean().optional() }) export type OptionsConfig = z.infer diff --git a/packages/web-runtime/src/components/Topbar/TopBar.vue b/packages/web-runtime/src/components/Topbar/TopBar.vue index 3791759eef7..b2510fc663c 100644 --- a/packages/web-runtime/src/components/Topbar/TopBar.vue +++ b/packages/web-runtime/src/components/Topbar/TopBar.vue @@ -98,10 +98,7 @@ export default { }) const logoWidth = ref('150px') - const hideLogoQuery = useRouteQuery('hide-logo', 'false') - const hideLogo = computed(() => { - return queryItemAsString(unref(hideLogoQuery)) === 'true' - }) + const hideLogo = computed(() => unref(configOptions).hideLogo) const isNotificationBellEnabled = computed(() => { return ( diff --git a/packages/web-runtime/src/container/bootstrap.ts b/packages/web-runtime/src/container/bootstrap.ts index 4cb26dc3787..5e12f0e85c8 100644 --- a/packages/web-runtime/src/container/bootstrap.ts +++ b/packages/web-runtime/src/container/bootstrap.ts @@ -149,7 +149,8 @@ export const announceConfiguration = async ({ rawConfig.options = { ...rawConfig.options, - embed: { ...rawConfig.options?.embed, ...embedConfigFromQuery } + embed: { ...rawConfig.options?.embed, ...embedConfigFromQuery }, + hideLogo: getQueryParam('hide-logo') === 'true' } configStore.loadConfig(rawConfig) From 9384c3c9f246592048db6c292f919a9cf19d0bfb Mon Sep 17 00:00:00 2001 From: Jan Ackermann Date: Wed, 18 Sep 2024 11:12:08 +0200 Subject: [PATCH 04/10] Add name input --- .../components/EmbedActions/EmbedActions.vue | 135 ++++++++++++------ .../EmbedActions/EmbedActions.spec.ts | 1 + .../src/components/Modals/SaveAsModal.vue | 15 +- .../src/composables/embedMode/useEmbedMode.ts | 15 ++ .../composables/piniaStores/config/types.ts | 4 +- .../src/components/Topbar/TopBar.vue | 2 - .../web-runtime/src/container/bootstrap.ts | 12 ++ 7 files changed, 135 insertions(+), 49 deletions(-) diff --git a/packages/web-app-files/src/components/EmbedActions/EmbedActions.vue b/packages/web-app-files/src/components/EmbedActions/EmbedActions.vue index 0cf7bd383e2..0e2b7af4b14 100644 --- a/packages/web-app-files/src/components/EmbedActions/EmbedActions.vue +++ b/packages/web-app-files/src/components/EmbedActions/EmbedActions.vue @@ -1,39 +1,52 @@ - diff --git a/packages/web-app-files/tests/unit/components/EmbedActions/EmbedActions.spec.ts b/packages/web-app-files/tests/unit/components/EmbedActions/EmbedActions.spec.ts index 2a0257aa3d5..765e5937260 100644 --- a/packages/web-app-files/tests/unit/components/EmbedActions/EmbedActions.spec.ts +++ b/packages/web-app-files/tests/unit/components/EmbedActions/EmbedActions.spec.ts @@ -134,6 +134,7 @@ function getWrapper( mock>({ isLocationPicker: ref(isLocationPicker), isFilePicker: ref(isFilePicker), + chooseFileName: ref(false), postMessage: postMessageMock }) ) diff --git a/packages/web-pkg/src/components/Modals/SaveAsModal.vue b/packages/web-pkg/src/components/Modals/SaveAsModal.vue index 083fcab1631..ad6b05d05fc 100644 --- a/packages/web-pkg/src/components/Modals/SaveAsModal.vue +++ b/packages/web-pkg/src/components/Modals/SaveAsModal.vue @@ -16,6 +16,7 @@