|
@@ -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,
|
|
|
},
|
|
|
})
|