aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/src/components/FileEntry.vue
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@protonmail.com>2023-09-27 10:30:55 +0200
committerJohn Molakvoæ <skjnldsv@protonmail.com>2023-10-10 15:28:52 +0200
commit35aed73edeffebb9b924cdd13e8b9881f1cd07ab (patch)
tree3956665427f1b98fd5c84ef1920361366a5fdf85 /apps/files/src/components/FileEntry.vue
parent9de246d74f72e290197efd0335aacc6f854cbc9a (diff)
downloadnextcloud-server-35aed73edeffebb9b924cdd13e8b9881f1cd07ab.tar.gz
nextcloud-server-35aed73edeffebb9b924cdd13e8b9881f1cd07ab.zip
feat: allow external drop and add dropzone
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
Diffstat (limited to 'apps/files/src/components/FileEntry.vue')
-rw-r--r--apps/files/src/components/FileEntry.vue78
1 files changed, 47 insertions, 31 deletions
diff --git a/apps/files/src/components/FileEntry.vue b/apps/files/src/components/FileEntry.vue
index e6592f7ba0c..ff71eaeff9a 100644
--- a/apps/files/src/components/FileEntry.vue
+++ b/apps/files/src/components/FileEntry.vue
@@ -189,12 +189,13 @@
<script lang="ts">
import type { PropType } from 'vue'
-import { emit } from '@nextcloud/event-bus'
-import { extname } from 'path'
+import { emit, subscribe } from '@nextcloud/event-bus'
+import { extname, join } from 'path'
import { generateUrl } from '@nextcloud/router'
-import { getFileActions, DefaultType, FileType, formatFileSize, Permission, Folder, File, FileAction, NodeStatus, Node } from '@nextcloud/files'
+import { getFileActions, DefaultType, FileType, formatFileSize, Permission, Folder, File as NcFile, FileAction, NodeStatus, Node } from '@nextcloud/files'
+import { getUploader } from '@nextcloud/upload'
import { showError, showSuccess } from '@nextcloud/dialogs'
-import { translate } from '@nextcloud/l10n'
+import { translate as t } from '@nextcloud/l10n'
import { Type as ShareType } from '@nextcloud/sharing'
import { vOnClickOutside } from '@vueuse/components'
import axios from '@nextcloud/axios'
@@ -278,7 +279,7 @@ export default Vue.extend({
default: false,
},
source: {
- type: [Folder, File, Node] as PropType<Node>,
+ type: [Folder, NcFile, Node] as PropType<Node>,
required: true,
},
index: {
@@ -369,7 +370,7 @@ export default Vue.extend({
size() {
const size = parseInt(this.source.size, 10) || 0
if (typeof size !== 'number' || size < 0) {
- return this.t('files', 'Pending')
+ return t('files', 'Pending')
}
return formatFileSize(size, true)
},
@@ -391,7 +392,7 @@ export default Vue.extend({
if (this.source.mtime) {
return moment(this.source.mtime).fromNow()
}
- return this.t('files_trashbin', 'A long time ago')
+ return t('files_trashbin', 'A long time ago')
},
mtimeOpacity() {
const maxOpacityTime = 31 * 24 * 60 * 60 * 1000 // 31 days
@@ -457,7 +458,7 @@ export default Vue.extend({
linkTo() {
if (this.source.attributes.failed) {
return {
- title: this.t('files', 'This node is unavailable'),
+ title: t('files', 'This node is unavailable'),
is: 'span',
}
}
@@ -475,7 +476,7 @@ export default Vue.extend({
return {
download: this.source.basename,
href: this.source.source,
- title: this.t('files', 'Download file {name}', { name: this.displayName }),
+ title: t('files', 'Download file {name}', { name: this.displayName }),
}
}
@@ -508,7 +509,7 @@ export default Vue.extend({
try {
const previewUrl = this.source.attributes.previewUrl
- || generateUrl('/core/preview?fileid={fileid}', {
+ || generateUrl('/core/preview?fileId={fileid}', {
fileid: this.fileid,
})
const url = new URL(window.location.origin + previewUrl)
@@ -699,13 +700,13 @@ export default Vue.extend({
}
if (success) {
- showSuccess(this.t('files', '"{displayName}" action executed successfully', { displayName }))
+ showSuccess(t('files', '"{displayName}" action executed successfully', { displayName }))
return
}
- showError(this.t('files', '"{displayName}" action failed', { displayName }))
+ showError(t('files', '"{displayName}" action failed', { displayName }))
} catch (e) {
logger.error('Error while executing action', { action, e })
- showError(this.t('files', '"{displayName}" action failed', { displayName }))
+ showError(t('files', '"{displayName}" action failed', { displayName }))
} finally {
// Reset the loading marker
this.loading = ''
@@ -803,15 +804,15 @@ export default Vue.extend({
isFileNameValid(name) {
const trimmedName = name.trim()
if (trimmedName === '.' || trimmedName === '..') {
- throw new Error(this.t('files', '"{name}" is an invalid file name.', { name }))
+ throw new Error(t('files', '"{name}" is an invalid file name.', { name }))
} else if (trimmedName.length === 0) {
- throw new Error(this.t('files', 'File name cannot be empty.'))
+ throw new Error(t('files', 'File name cannot be empty.'))
} else if (trimmedName.indexOf('/') !== -1) {
- throw new Error(this.t('files', '"/" is not allowed inside a file name.'))
+ throw new Error(t('files', '"/" is not allowed inside a file name.'))
} else if (trimmedName.match(OC.config.blacklist_files_regex)) {
- throw new Error(this.t('files', '"{name}" is not an allowed filetype.', { name }))
+ throw new Error(t('files', '"{name}" is not an allowed filetype.', { name }))
} else if (this.checkIfNodeExists(name)) {
- throw new Error(this.t('files', '{newName} already exists.', { newName: name }))
+ throw new Error(t('files', '{newName} already exists.', { newName: name }))
}
const toCheck = trimmedName.split('')
@@ -859,7 +860,7 @@ export default Vue.extend({
const oldEncodedSource = this.source.encodedSource
const newName = this.newName.trim?.() || ''
if (newName === '') {
- showError(this.t('files', 'Name cannot be empty'))
+ showError(t('files', 'Name cannot be empty'))
return
}
@@ -870,7 +871,7 @@ export default Vue.extend({
// Checking if already exists
if (this.checkIfNodeExists(newName)) {
- showError(this.t('files', 'Another entry with the same name already exists'))
+ showError(t('files', 'Another entry with the same name already exists'))
return
}
@@ -894,7 +895,7 @@ export default Vue.extend({
// Success 🎉
emit('files:node:updated', this.source)
emit('files:node:renamed', this.source)
- showSuccess(this.t('files', 'Renamed "{oldName}" to "{newName}"', { oldName, newName }))
+ showSuccess(t('files', 'Renamed "{oldName}" to "{newName}"', { oldName, newName }))
// Reset the renaming store
this.stopRenaming()
@@ -908,15 +909,15 @@ export default Vue.extend({
// TODO: 409 means current folder does not exist, redirect ?
if (error?.response?.status === 404) {
- showError(this.t('files', 'Could not rename "{oldName}", it does not exist any more', { oldName }))
+ showError(t('files', 'Could not rename "{oldName}", it does not exist any more', { oldName }))
return
} else if (error?.response?.status === 412) {
- showError(this.t('files', 'The name "{newName}" is already used in the folder "{dir}". Please choose a different name.', { newName, dir: this.currentDir }))
+ showError(t('files', 'The name "{newName}" is already used in the folder "{dir}". Please choose a different name.', { newName, dir: this.currentDir }))
return
}
// Unknown error
- showError(this.t('files', 'Could not rename "{oldName}"', { oldName }))
+ showError(t('files', 'Could not rename "{oldName}"', { oldName }))
} finally {
this.loading = false
Vue.set(this.source, 'status', undefined)
@@ -945,8 +946,6 @@ export default Vue.extend({
onDragOver(event: DragEvent) {
this.dragover = this.canDrop
if (!this.canDrop) {
- event.preventDefault()
- event.stopPropagation()
event.dataTransfer.dropEffect = 'none'
return
}
@@ -959,9 +958,13 @@ export default Vue.extend({
}
},
onDragLeave(event: DragEvent) {
- if (this.$el.contains(event.target) && event.target !== this.$el) {
+ // Counter bubbling, make sure we're ending the drag
+ // only when we're leaving the current element
+ const currentTarget = event.currentTarget as HTMLElement
+ if (currentTarget?.contains(event.relatedTarget as HTMLElement)) {
return
}
+
this.dragover = false
},
@@ -990,7 +993,7 @@ export default Vue.extend({
.map(fileid => this.filesStore.getNode(fileid)) as Node[]
const image = await getDragAndDropPreview(nodes)
- event.dataTransfer.setDragImage(image, -10, -10)
+ event.dataTransfer?.setDragImage(image, -10, -10)
},
onDragEnd() {
this.draggingStore.reset()
@@ -999,6 +1002,9 @@ export default Vue.extend({
},
async onDrop(event) {
+ event.preventDefault()
+ event.stopPropagation()
+
// If another button is pressed, cancel it
// This allows cancelling the drag with the right click
if (!this.canDrop || event.button !== 0) {
@@ -1010,6 +1016,16 @@ export default Vue.extend({
logger.debug('Dropped', { event, selection: this.draggingFiles })
+ // Check whether we're uploading files
+ if (event.dataTransfer?.files?.length > 0) {
+ const uploader = getUploader()
+ event.dataTransfer.files.forEach((file: File) => {
+ uploader.upload(join(this.source.path, file.name), file)
+ })
+ logger.debug(`Uploading files to ${this.source.path}`)
+ return
+ }
+
const nodes = this.draggingFiles.map(fileid => this.filesStore.getNode(fileid)) as Node[]
nodes.forEach(async (node: Node) => {
Vue.set(node, 'status', NodeStatus.LOADING)
@@ -1019,9 +1035,9 @@ export default Vue.extend({
} catch (error) {
logger.error('Error while moving file', { error })
if (isCopy) {
- showError(this.t('files', 'Could not copy {file}. {message}', { file: node.basename, message: error.message || '' }))
+ showError(t('files', 'Could not copy {file}. {message}', { file: node.basename, message: error.message || '' }))
} else {
- showError(this.t('files', 'Could not move {file}. {message}', { file: node.basename, message: error.message || '' }))
+ showError(t('files', 'Could not move {file}. {message}', { file: node.basename, message: error.message || '' }))
}
} finally {
Vue.set(node, 'status', undefined)
@@ -1036,7 +1052,7 @@ export default Vue.extend({
}
},
- t: translate,
+ t,
formatFileSize,
},
})