Skip to content

Commit

Permalink
Merge pull request #4025 from LibreSign/feat/delete-multiple-files
Browse files Browse the repository at this point in the history
feat: delete multiple files
  • Loading branch information
vitormattos authored Nov 25, 2024
2 parents cd91f2a + 308c242 commit ce7e832
Show file tree
Hide file tree
Showing 10 changed files with 544 additions and 13 deletions.
21 changes: 16 additions & 5 deletions src/store/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,24 @@ export const useFilesStore = function(...args) {
await axios.delete(generateOcsUrl(url, {
fileId: file.nodeId,
}))
.then(() => {
del(this.files, file.nodeId)
const index = this.ordered.indexOf(file.nodeId)
if (index > -1) {
this.ordered.splice(index, 1)
}
})
}
del(this.files, file.nodeId)

const index = this.ordered.indexOf(file.nodeId)
if (index > -1) {
this.ordered.splice(index, 1)
}
},
async deleteMultiple(nodeIds, deleteFile) {
this.loading = true
nodeIds.forEach(async nodeId => {
await this.delete(this.files[nodeId], deleteFile)
})
const toRemove = nodeIds.filter(nodeId => (!this.files[nodeId]?.uuid))
del(this.files, ...toRemove)
this.loading = false
},
async getAllFiles(filter) {
if (this.loading || this.loadedAll) {
Expand Down
47 changes: 47 additions & 0 deletions src/store/keyboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* SPDX-FileCopyrightText: 2024 LibreCode coop and contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { defineStore } from 'pinia'
import { set } from 'vue'

/**
* Observe various events and save the current
* special keys states. Useful for checking the
* current status of a key when executing a method.
* @param {...any} args properties
*/
export const useKeyboardStore = function(...args) {
const store = defineStore('keyboard', {
state: () => ({
altKey: false,
ctrlKey: false,
metaKey: false,
shiftKey: false,
}),

actions: {
onEvent(event) {
if (!event) {
event = window.event
}
set(this, 'altKey', !!event.altKey)
set(this, 'ctrlKey', !!event.ctrlKey)
set(this, 'metaKey', !!event.metaKey)
set(this, 'shiftKey', !!event.shiftKey)
},
},
})

const keyboardStore = store(...args)
// Make sure we only register the listeners once
if (!keyboardStore._initialized) {
window.addEventListener('keydown', keyboardStore.onEvent)
window.addEventListener('keyup', keyboardStore.onEvent)
window.addEventListener('mousemove', keyboardStore.onEvent)

keyboardStore._initialized = true
}

return keyboardStore
}
57 changes: 57 additions & 0 deletions src/store/selection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* SPDX-FileCopyrightText: 2024 LibreCode coop and contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { defineStore } from 'pinia'
import { set } from 'vue'

import { subscribe } from '@nextcloud/event-bus'

export const useSelectionStore = function(...args) {
const store = defineStore('selection', {
state: () => ({
selected: [],
lastSelection: [],
lastSelectedIndex: null,
}),

actions: {
/**
* Set the selection of fileIds
* @param {Array} selection Selected files
*/
set(selection = []) {
set(this, 'selected', [...new Set(selection)])
},

/**
* Set the last selected index
* @param {number | null} lastSelectedIndex Position of last selected file
*/
setLastIndex(lastSelectedIndex = null) {
// Update the last selection if we provided a new selection starting point
set(this, 'lastSelection', lastSelectedIndex ? this.selected : [])
set(this, 'lastSelectedIndex', lastSelectedIndex)
},

/**
* Reset the selection
*/
reset() {
set(this, 'selected', [])
set(this, 'lastSelection', [])
set(this, 'lastSelectedIndex', null)
},
},
})

const selectionStore = store(...args)

// Make sure we only register the listeners once
if (!selectionStore._initialized) {
subscribe('libresign:filters:update', selectionStore.reset)
selectionStore._initialized = true
}

return selectionStore
}
20 changes: 13 additions & 7 deletions src/views/FilesList/FileEntry/FileEntry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
<template>
<tr class="files-list__row"
@contextmenu="onRightClick">
<!-- Checkbox -->
<FileEntryCheckbox :is-loading="filesStore.loading"
:source="source" />

<td class="files-list__row-name"
@click="openDetailsIfAvailable">
<FileEntryPreview :source="source" />
Expand All @@ -13,6 +17,13 @@
:extension="'.pdf'" />
</td>

<!-- Actions -->
<FileEntryActions ref="actions"
:class="`files-list__row-actions-${source.nodeId}`"
:opened.sync="openedMenu"
:source="source"
:loading="loading" />

<!-- Status -->
<td class="files-list__row-status"
@click="openDetailsIfAvailable">
Expand All @@ -26,20 +37,14 @@
@click="openDetailsIfAvailable">
<NcDateTime v-if="source.request_date" :timestamp="mtime" :ignore-seconds="true" />
</td>

<!-- Actions -->
<FileEntryActions ref="actions"
:class="`files-list__row-actions-${source.nodeId}`"
:opened.sync="openedMenu"
:source="source"
:loading="loading" />
</tr>
</template>

<script>
import NcDateTime from '@nextcloud/vue/dist/Components/NcDateTime.js'

import FileEntryActions from './FileEntryActions.vue'
import FileEntryCheckbox from './FileEntryCheckbox.vue'
import FileEntryName from './FileEntryName.vue'
import FileEntryPreview from './FileEntryPreview.vue'
import FileEntryStatus from './FileEntryStatus.vue'
Expand All @@ -53,6 +58,7 @@ export default {
components: {
NcDateTime,
FileEntryActions,
FileEntryCheckbox,
FileEntryName,
FileEntryPreview,
FileEntryStatus,
Expand Down
113 changes: 113 additions & 0 deletions src/views/FilesList/FileEntry/FileEntryCheckbox.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<!--
- SPDX-FileCopyrightText: 2024 LibreCode coop and contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<td class="files-list__row-checkbox"
@keyup.esc.exact="resetSelection">
<NcLoadingIcon v-if="isLoading" :name="loadingLabel" />
<NcCheckboxRadioSwitch v-else
:aria-label="ariaLabel"
:checked="isSelected"
@update:checked="onSelectionChange" />
</td>
</template>

<script>
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'

import logger from '../../../logger.js'
import { useFilesStore } from '../../../store/files.js'
import { useKeyboardStore } from '../../../store/keyboard.js'
import { useSelectionStore } from '../../../store/selection.js'

export default {
name: 'FileEntryCheckbox',

components: {
NcCheckboxRadioSwitch,
NcLoadingIcon,
},

props: {
isLoading: {
type: Boolean,
default: false,
},
source: {
type: Object,
required: true,
},
},

setup() {
const filesStore = useFilesStore()
const keyboardStore = useKeyboardStore()
const selectionStore = useSelectionStore()
return {
filesStore,
keyboardStore,
selectionStore,
}
},

computed: {
selectedFiles() {
return this.selectionStore.selected
},
isSelected() {
return this.selectedFiles.includes(this.source.nodeId)
},
index() {
return this.filesStore.ordered.findIndex(nodeId => Number(nodeId) === this.source.nodeId)
},
ariaLabel() {
return t('libresign', 'Toggle selection for file "{displayName}"', { displayName: this.source.basename })
},
loadingLabel() {
return t('libresign', 'File is loading')
},
},

methods: {
onSelectionChange(selected) {
const newSelectedIndex = this.index
const lastSelectedIndex = this.selectionStore.lastSelectedIndex

// Get the last selected and select all files in between
if (this.keyboardStore?.shiftKey && lastSelectedIndex !== null) {
const isAlreadySelected = this.selectedFiles.includes(this.source.nodeId)

const start = Math.min(newSelectedIndex, lastSelectedIndex)
const end = Math.max(lastSelectedIndex, newSelectedIndex)

const lastSelection = this.selectionStore.lastSelection
const filesToSelect = this.filesStore.ordered
.slice(start, end + 1)

// If already selected, update the new selection _without_ the current file
const selection = [...new Set([...lastSelection, ...filesToSelect])]
.filter(nodeId => !isAlreadySelected || nodeId !== this.source.nodeId)

logger.debug('Shift key pressed, selecting all files in between', { start, end, filesToSelect, isAlreadySelected })
// Keep previous lastSelectedIndex to be use for further shift selections
this.selectionStore.set(selection)
return
}

const selection = selected
? [...this.selectedFiles, this.source.nodeId]
: this.selectedFiles.filter(nodeId => nodeId !== this.source.nodeId)

logger.debug('Updating selection', { selection })
this.selectionStore.set(selection)
this.selectionStore.setLastIndex(newSelectedIndex)
},

resetSelection() {
this.selectionStore.reset()
},
},
}
</script>
6 changes: 6 additions & 0 deletions src/views/FilesList/FileEntry/FileEntryGrid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
:extension="'.pdf'" />
</td>

<!-- Checkbox -->
<FileEntryCheckbox :is-loading="filesStore.loading"
:source="source" />

<!-- Status -->
<td class="files-list__row-status"
@click="openDetailsIfAvailable">
Expand Down Expand Up @@ -40,6 +44,7 @@
import NcDateTime from '@nextcloud/vue/dist/Components/NcDateTime.js'

import FileEntryActions from './FileEntryActions.vue'
import FileEntryCheckbox from './FileEntryCheckbox.vue'
import FileEntryName from './FileEntryName.vue'
import FileEntryPreview from './FileEntryPreview.vue'
import FileEntryStatus from './FileEntryStatus.vue'
Expand All @@ -53,6 +58,7 @@ export default {
components: {
NcDateTime,
FileEntryActions,
FileEntryCheckbox,
FileEntryName,
FileEntryPreview,
FileEntryStatus,
Expand Down
Loading

0 comments on commit ce7e832

Please sign in to comment.