Skip to content

Commit

Permalink
Merge pull request #12046 from owncloud/feat/disable-resources-in-del…
Browse files Browse the repository at this point in the history
…ete-queue

feat: add delete queue
  • Loading branch information
LukasHirt authored Dec 19, 2024
2 parents c647807 + c2a5b33 commit 505c210
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enhancement: Disable resources in delete queue

We've added a new delete queue which is used to disable resources that are being currently deleted. When the resource is in delete queue, it also replaces select checkbox with a spinner to better hint that there is an action in progress.

https://github.com/owncloud/web/pull/12046
https://github.com/owncloud/web/issues/11956
25 changes: 20 additions & 5 deletions packages/web-pkg/src/components/FilesList/ResourceTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,14 @@
</div>
</template>
<template v-if="!isLocationPicker && !isFilePicker" #select="{ item }">
<oc-spinner
v-if="isResourceInDeleteQueue(item.id)"
class="resource-table-activity-indicator"
size="medium"
/>

<oc-checkbox
v-else
:id="`resource-table-select-${resourceDomSelector(item)}`"
:label="getResourceCheckboxLabel(item)"
:label-hidden="true"
Expand Down Expand Up @@ -307,6 +314,7 @@ import CollapsibleOcTable from './../../cern/components/CollapsibleOcTable.vue'
import { storeToRefs } from 'pinia'
import { OcButton, OcTable } from '@ownclouders/design-system/components'
import { FieldType } from '@ownclouders/design-system/helpers'
import { OcSpinner } from '@ownclouders/design-system/components'
const TAGS_MINIMUM_SCREEN_WIDTH = 850
Expand All @@ -317,7 +325,8 @@ export default defineComponent({
ResourceGhostElement,
ResourceListItem,
ResourceSize,
OcTable
OcTable,
OcSpinner
},
props: {
/**
Expand Down Expand Up @@ -555,7 +564,7 @@ export default defineComponent({
const { userContextReady } = storeToRefs(authStore)
const resourcesStore = useResourcesStore()
const { areFileExtensionsShown, latestSelectedId } = storeToRefs(resourcesStore)
const { areFileExtensionsShown, latestSelectedId, deleteQueue } = storeToRefs(resourcesStore)
const dragItem = ref<Resource>()
const ghostElement = ref()
Expand All @@ -581,7 +590,7 @@ export default defineComponent({
!resource.isFolder
)
}
return resource.processing === true
return resource.processing === true || isResourceInDeleteQueue(resource.id)
}
const disabledResources: ComputedRef<Array<Resource['id']>> = computed(() => {
Expand Down Expand Up @@ -643,6 +652,10 @@ export default defineComponent({
return action.route({ space, resources: [resource] })
}
const isResourceInDeleteQueue = (id: string): boolean => {
return unref(deleteQueue).includes(id)
}
return {
router,
configOptions,
Expand Down Expand Up @@ -680,7 +693,8 @@ export default defineComponent({
latestSelectedId,
isResourceClickable,
getResourceLink,
isSticky
isSticky,
isResourceInDeleteQueue
}
},
data() {
Expand Down Expand Up @@ -1252,7 +1266,8 @@ export default defineComponent({
vertical-align: text-bottom;
}
&-edit-name {
&-edit-name,
&-activity-indicator {
display: inline-flex;
margin-left: var(--oc-space-xsmall);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ export const useFileActionsDeleteResources = () => {

const filesList_delete = (resources: Resource[]) => {
resourcesToDelete.value = [...resources]
resourcesStore.addResourcesIntoDeleteQueue(unref(resourcesToDelete).map(({ id }) => id))
unref(resourcesToDelete).forEach(({ id }) => resourcesStore.removeSelection(id))

const resourceSpaceMapping = unref(resources).reduce<
Record<string, { space: SpaceResource; resources: Resource[] }>
Expand Down Expand Up @@ -193,6 +195,9 @@ export const useFileActionsDeleteResources = () => {
messageStore.showMessage({ title })
}

resourcesStore.removeResourcesFromDeleteQueue(failed.map(({ resource }) => resource.id))
resourcesStore.removeResourcesFromDeleteQueue(successful.map(({ id }) => id))

failed.forEach(({ error, resource }) => {
let title = $gettext('Failed to delete "%{resource}"', { resource: resource.name })
if (error.statusCode === 423) {
Expand All @@ -208,10 +213,6 @@ export const useFileActionsDeleteResources = () => {
if (originalCurrentFolderId === unref(currentFolder)?.id) {
resourcesStore.removeResources(successful)

successful.forEach(({ id }) => {
resourcesStore.removeSelection(id)
})

const activeFilesCount = resourcesStore.activeResources.length
const pageCount = Math.ceil(unref(activeFilesCount) / unref(itemsPerPage))
if (unref(currentPage) > 1 && unref(currentPage) > pageCount) {
Expand Down
17 changes: 16 additions & 1 deletion packages/web-pkg/src/composables/piniaStores/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const useResourcesStore = defineStore('resources', () => {
const resources = ref<Resource[]>([]) as Ref<Resource[]>
const currentFolder = ref<Resource>()
const ancestorMetaData = ref<AncestorMetaData>({})
const deleteQueue = ref<string[]>([])

const activeResources = computed(() => {
let res = unref(resources)
Expand Down Expand Up @@ -313,6 +314,16 @@ export const useResourcesStore = defineStore('resources', () => {
return Object.values(unref(ancestorMetaData)).find((a) => id === a.id)
}

const addResourcesIntoDeleteQueue = (ids: string[]): void => {
deleteQueue.value = deleteQueue.value.concat(
ids.filter((id) => !unref(deleteQueue).includes(id))
)
}

const removeResourcesFromDeleteQueue = (ids: string[]): void => {
deleteQueue.value = deleteQueue.value.filter((id) => !ids.includes(id))
}

return {
resources,
currentFolder,
Expand Down Expand Up @@ -355,7 +366,11 @@ export const useResourcesStore = defineStore('resources', () => {
setAncestorMetaData,
updateAncestorField,
loadAncestorMetaData,
getAncestorById
getAncestorById,

deleteQueue,
addResourcesIntoDeleteQueue,
removeResourcesFromDeleteQueue
}
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,31 @@ const resourcesWithAllFields = [
canRename: vi.fn(),
getDomSelector: () => extractDomSelector('another-one=='),
canDownload: () => true
},
{
id: 'in-delete-queue==',
driveId: 'another-one==',
name: 'In delete queue',
path: '/In delete queue',
indicators,
isFolder: true,
type: 'folder',
size: '237895',
mdate: getCurrentDate(),
sdate: getCurrentDate(),
owner,
sharedBy,
sharedWith,
hidden: false,
syncEnabled: true,
outgoing: false,
shareRoles: [],
sharePermissions: [],
shareTypes: [],
tags: [],
canRename: vi.fn(),
getDomSelector: () => extractDomSelector('in-delete-queue=='),
canDownload: () => true
}
] as IncomingShareResource[]

Expand Down Expand Up @@ -294,7 +319,7 @@ describe('ResourceTable', () => {
resourceDomSelector: (resource: Resource) => ['custom', resource.getDomSelector()].join('-')
}
})
resourcesWithAllFields.forEach((resource) => {
resourcesWithAllFields.slice(0, -1).forEach((resource) => {
;['.oc-tbody-tr', '#resource-table-select', '#context-menu-drop'].forEach((baseSelector) => {
expect(
wrapper.find([baseSelector, 'custom', resource.getDomSelector()].join('-')).exists()
Expand Down Expand Up @@ -327,12 +352,18 @@ describe('ResourceTable', () => {
processingResourcesWithAllFields[1].id
)
})
it('should replace checkbox with spinner when resource is in delete queue', () => {
const { wrapper } = getMountedWrapper()
expect(wrapper.find('.oc-tbody-tr-in-delete-queue .oc-checkbox').exists()).toBe(false)
expect(wrapper.find('.oc-tbody-tr-in-delete-queue .oc-spinner').exists()).toBe(true)
})

describe('all rows already selected', () => {
it('de-selects all resources via the select-all checkbox', async () => {
const { wrapper } = getMountedWrapper({
props: {
selectedIds: resourcesWithAllFields.map((resource) => resource.id)
selectedIds: resourcesWithAllFields.map((resource) => resource.id),
resources: resourcesWithAllFields.slice(0, -1)
}
})

Expand Down Expand Up @@ -472,6 +503,11 @@ describe('ResourceTable', () => {

expect(wrapper.emitted('update:selectedIds')).toBeUndefined()
})

it('should disable resource when it is in delete queue', () => {
const { wrapper } = getMountedWrapper()
expect(wrapper.find('.oc-tbody-tr-in-delete-queue.oc-table-disabled').exists()).toBe(true)
})
})

describe('context menu', () => {
Expand Down Expand Up @@ -672,7 +708,8 @@ function getMountedWrapper({
...defaultPlugins({
piniaOptions: {
authState: { userContextReady },
capabilityState: { capabilities }
capabilityState: { capabilities },
resourcesStore: { deleteQueue: ['in-delete-queue=='] }
}
})
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from '@ownclouders/web-test-helpers'
import { useDeleteWorker } from '../../../../../src/composables/webWorkers/deleteWorker'
import { useGetMatchingSpace } from '../../../../../src/composables/spaces/useGetMatchingSpace'
import { useResourcesStore } from '../../../../../src/composables/piniaStores'

vi.mock('../../../../../src/composables/webWorkers/deleteWorker')
vi.mock('../../../../../src/composables/spaces/useGetMatchingSpace')
Expand Down Expand Up @@ -44,6 +45,20 @@ describe('deleteResources', () => {
}
})
})

it('should push resources into delete queue', () => {
const filesToDelete = [{ id: '2', path: '/folder/fileToDelete.txt' }]
getWrapper({
currentFolder,
result: filesToDelete,
setup: ({ filesList_delete }) => {
filesList_delete(filesToDelete)
}
})

const { addResourcesIntoDeleteQueue } = useResourcesStore()
expect(addResourcesIntoDeleteQueue).toHaveBeenCalledWith(['2'])
})
})
})

Expand Down
1 change: 1 addition & 0 deletions packages/web-test-helpers/src/mocks/pinia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export type PiniaMockOptions = {
selectedIds?: string[]
areFileExtensionsShown?: boolean
areHiddenFilesShown?: boolean
deleteQueue?: string[]
}
sharesState?: {
collaboratorShares?: CollaboratorShare[]
Expand Down

0 comments on commit 505c210

Please sign in to comment.