From f9f95b08704afc63a480ab97a8eccb09b4b93e1d Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Thu, 21 May 2026 09:46:07 +0200 Subject: [PATCH 1/6] feat: redesign batch actions --- .../components/OcBreadcrumb/OcBreadcrumb.vue | 7 +- .../__snapshots__/OcBreadcrumb.spec.ts.snap | 4 +- .../src/components/AppBar/CreateAndUpload.vue | 219 ------------------ .../src/components/AppBar/PasteActions.vue | 133 +++++++++++ .../web-app-files/src/composables/index.ts | 1 + .../src/composables/useFileUpload.ts | 110 +++++++++ .../src/views/spaces/GenericSpace.vue | 17 +- .../web-pkg/src/components/AppBar/AppBar.vue | 60 +++-- .../web-pkg/src/components/BatchActions.vue | 5 +- .../ContextActions/ActionMenuItem.vue | 1 - 10 files changed, 301 insertions(+), 256 deletions(-) delete mode 100644 packages/web-app-files/src/components/AppBar/CreateAndUpload.vue create mode 100644 packages/web-app-files/src/components/AppBar/PasteActions.vue create mode 100644 packages/web-app-files/src/composables/useFileUpload.ts diff --git a/packages/design-system/src/components/OcBreadcrumb/OcBreadcrumb.vue b/packages/design-system/src/components/OcBreadcrumb/OcBreadcrumb.vue index cf61af290c..0a5e578088 100644 --- a/packages/design-system/src/components/OcBreadcrumb/OcBreadcrumb.vue +++ b/packages/design-system/src/components/OcBreadcrumb/OcBreadcrumb.vue @@ -70,6 +70,9 @@ :aria-current="getAriaCurrent(index)" :to="item.to as RouteLocationRaw" class="first:text-base text-xl text-role-on-surface h-5 inline-flex items-center" + :class="{ + 'font-bold': index === displayItems.length - 1 + }" > {{ item.text @@ -93,7 +96,7 @@ 'leading-[1.2]', 'max-w-3xs', { - 'oc-breadcrumb-item-text-last': index === displayItems.length - 1 + 'oc-breadcrumb-item-text-last font-bold': index === displayItems.length - 1 } ]" v-text="item.text" @@ -164,7 +167,7 @@ 'justify-center': displayItems.length > 1 }" > - + diff --git a/packages/design-system/src/components/OcBreadcrumb/__snapshots__/OcBreadcrumb.spec.ts.snap b/packages/design-system/src/components/OcBreadcrumb/__snapshots__/OcBreadcrumb.spec.ts.snap index 4f9976a16b..d1c219b9a2 100644 --- a/packages/design-system/src/components/OcBreadcrumb/__snapshots__/OcBreadcrumb.spec.ts.snap +++ b/packages/design-system/src/components/OcBreadcrumb/__snapshots__/OcBreadcrumb.spec.ts.snap @@ -36,7 +36,7 @@ exports[`OcBreadcrumb > displays all items 1`] = ` -
Deeper ellipsize in responsive mode
" +
Deeper ellipsize in responsive mode
" `; exports[`OcBreadcrumb > sets correct variation 1`] = ` @@ -75,5 +75,5 @@ exports[`OcBreadcrumb > sets correct variation 1`] = ` -
Deeper ellipsize in responsive mode
" +
Deeper ellipsize in responsive mode
" `; diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue deleted file mode 100644 index 523e471397..0000000000 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ /dev/null @@ -1,219 +0,0 @@ - - - diff --git a/packages/web-app-files/src/components/AppBar/PasteActions.vue b/packages/web-app-files/src/components/AppBar/PasteActions.vue new file mode 100644 index 0000000000..eb8e8615e0 --- /dev/null +++ b/packages/web-app-files/src/components/AppBar/PasteActions.vue @@ -0,0 +1,133 @@ + + + diff --git a/packages/web-app-files/src/composables/index.ts b/packages/web-app-files/src/composables/index.ts index 38b2d94bef..6d9d475fa9 100644 --- a/packages/web-app-files/src/composables/index.ts +++ b/packages/web-app-files/src/composables/index.ts @@ -1,2 +1,3 @@ export * from './actions' export * from './resourcesViewDefaults' +export * from './useFileUpload' diff --git a/packages/web-app-files/src/composables/useFileUpload.ts b/packages/web-app-files/src/composables/useFileUpload.ts new file mode 100644 index 0000000000..142bc08c62 --- /dev/null +++ b/packages/web-app-files/src/composables/useFileUpload.ts @@ -0,0 +1,110 @@ +import { + useMessages, + useResourcesStore, + useRoute, + useSpacesStore, + useUserStore, + useClientService +} from '@opencloud-eu/web-pkg' +import { computed, onMounted, onBeforeUnmount, unref, watch, Ref } from 'vue' +import { SpaceResource, isPublicSpaceResource } from '@opencloud-eu/web-client' +import { useService, useUpload, UppyService, UploadResult } from '@opencloud-eu/web-pkg' +import { HandleUpload } from '../HandleUpload' +import { useGettext } from 'vue3-gettext' +import { storeToRefs } from 'pinia' + +export const useFileUpload = (space: Ref) => { + const uppyService = useService('$uppyService') + const clientService = useClientService() + const userStore = useUserStore() + const spacesStore = useSpacesStore() + const messageStore = useMessages() + const route = useRoute() + const language = useGettext() + + const resourcesStore = useResourcesStore() + const { currentFolder } = storeToRefs(resourcesStore) + + useUpload({ uppyService }) + + if (!uppyService.getPlugin('HandleUpload')) { + uppyService.addPlugin(HandleUpload, { + clientService, + language, + route, + space, + userStore, + spacesStore, + messageStore, + resourcesStore, + uppyService + }) + } + + let uploadCompletedSub: string + + const canUpload = computed(() => { + return unref(currentFolder)?.canUpload({ user: userStore.user }) + }) + + const onUploadComplete = async (result: UploadResult) => { + const file = result.successful?.[0] + if (!file) { + return + } + + const { spaceId, driveType } = file.meta + if (!isPublicSpaceResource(unref(space))) { + const isOwnSpace = spacesStore.spaces + .find(({ id }) => id === spaceId) + ?.isOwner(userStore.user) + + if (driveType === 'project' || isOwnSpace) { + const client = clientService.graphAuthenticated + const updatedSpace = await client.drives.getDrive(spaceId) + spacesStore.updateSpaceField({ + id: updatedSpace.id, + field: 'spaceQuota', + value: updatedSpace.spaceQuota + }) + } + } + + if (!unref(currentFolder) || spaceId !== unref(space).id) { + return + } + + const { children } = await clientService.webdav.listFiles(unref(space), { + path: unref(currentFolder).path + }) + + const existingIds = new Set(resourcesStore.resources.map((r) => r.id)) + const newResources = children.filter((child) => !existingIds.has(child.id)) + resourcesStore.upsertResources(newResources) + } + + onMounted(() => { + uploadCompletedSub = uppyService.subscribe('uploadCompleted', onUploadComplete) + }) + + onBeforeUnmount(() => { + uppyService.removePlugin(uppyService.getPlugin('HandleUpload')) + uppyService.unsubscribe('uploadCompleted', uploadCompletedSub) + uppyService.removeDropTarget() + }) + + watch( + canUpload, + () => { + const targetSelector = '#files-view' + const target = document.querySelector(targetSelector) + + if (target && unref(canUpload)) { + uppyService.useDropTarget({ targetSelector }) + } else { + uppyService.removeDropTarget() + } + }, + { immediate: true } + ) +} diff --git a/packages/web-app-files/src/views/spaces/GenericSpace.vue b/packages/web-app-files/src/views/spaces/GenericSpace.vue index ba6b5d0fe9..d4e1aab66a 100644 --- a/packages/web-app-files/src/views/spaces/GenericSpace.vue +++ b/packages/web-app-files/src/views/spaces/GenericSpace.vue @@ -12,15 +12,8 @@ :view-modes="viewModes" @item-dropped="fileDropped" > -