1
0
mirror of https://gitlab.com/bramw/baserow.git synced 2024-11-24 16:36:46 +00:00
bramw_baserow/web-frontend/modules/database/components/row/RowEditFieldFile.vue
2024-07-05 09:35:08 +00:00

237 lines
6.8 KiB
Vue

<template>
<div class="control__elements">
<ul class="field-file__list">
<li
v-for="(file, index) in files"
:key="file.name + '-' + index"
class="field-file__item"
>
<FileInProgress
v-if="file.state === 'loading'"
:file="file"
:read-only="readOnly"
:icon-class="getIconClass(file.mime_type)"
@delete="forceRemoveFile(getFileInProgressIndex(file.id))"
/>
<FileFailed
v-else-if="file.state === 'failed'"
:file="file"
:read-only="readOnly"
:icon-class="getIconClass(file.mime_type)"
@delete="forceRemoveFile(getFileInProgressIndex(file.id))"
/>
<FileUploaded
v-else
:ref="`file-uploaded-${index}`"
:file="file"
:read-only="readOnly"
:icon-class="getIconClass(file.mime_type)"
@delete="removeFile(value, index)"
@rename="(name) => renameFile(value, index, name)"
@click="$refs.fileModal.show(index)"
/>
</li>
</ul>
<UploadFileDropzone v-if="!readOnly" @input="filesAdded($event)" />
<ButtonText
v-if="!readOnly"
icon="iconoir-plus"
@click.prevent="showModal()"
>
{{ $t('rowEditFieldFile.addFile') }}
</ButtonText>
<UserFilesModal
v-if="!readOnly"
ref="uploadModal"
:upload-file="uploadFile"
:user-file-upload-types="userFileUploadTypes"
@uploaded="addFiles(value, $event)"
></UserFilesModal>
<div v-show="touched && !valid" class="error">
{{ error }}
</div>
<FileFieldModal
v-if="Boolean(value)"
ref="fileModal"
:files="value"
:read-only="readOnly"
@removed="removeFile(value, $event)"
@renamed="renameFile(value, $event.index, $event.value)"
></FileFieldModal>
</div>
</template>
<script>
import { uuid } from '@baserow/modules/core/utils/string'
import FileFieldModal from '@baserow/modules/database/components/field/FileFieldModal'
import rowEditField from '@baserow/modules/database/mixins/rowEditField'
import fileField from '@baserow/modules/database/mixins/fileField'
import UploadFileDropzone from '@baserow/modules/core/components/files/UploadFileDropzone'
import UserFileService from '@baserow/modules/core/services/userFile'
import { UploadFileUserFileUploadType } from '@baserow/modules/core/userFileUploadTypes'
import UserFilesModal from '@baserow/modules/core/components/files/UserFilesModal'
import axios from 'axios'
import FileInProgress from '@baserow/modules/core/components/files/FileInProgress'
import FileFailed from '@baserow/modules/core/components/files/FileFailed'
import FileUploaded from '@baserow/modules/core/components/files/FileUploaded'
import { IMAGE_FILE_TYPES } from '@baserow/modules/core/enums'
export default {
components: {
FileUploaded,
FileFailed,
FileInProgress,
UploadFileDropzone,
FileFieldModal,
UserFilesModal,
},
mixins: [rowEditField, fileField],
props: {
uploadFile: {
type: Function,
required: false,
default: null,
},
userFileUploadTypes: {
type: Array,
required: false,
default: null,
},
},
data() {
return {
filesInProgress: [],
currentFileUploading: null,
cancelToken: null,
}
},
computed: {
files() {
return this.value?.concat(this.filesInProgress)
},
uploadFileFunction() {
return this.uploadFile || UserFileService(this.$client).uploadFile
},
},
methods: {
showModal() {
this.$refs.uploadModal.show(UploadFileUserFileUploadType.getType())
},
async filesAdded(event) {
const files = event.target.files || event.dataTransfer?.files
if (!files) {
return
}
let successfulUploads = []
const filesWithData = Array.from(files).map((file) => ({
file,
fileData: this.forceAddFile(file, { state: 'loading' }),
}))
for (const { file, fileData } of filesWithData) {
// File has been removed in the meantime
if (this.getFileInProgressIndex(fileData.id) === -1) {
continue
}
const progress = (event) => {
fileData.percentage = Math.round((event.loaded * 100) / event.total)
}
try {
this.currentFileUploading = fileData
this.cancelToken = axios.CancelToken.source()
const { data } = await this.uploadFileFunction(
file,
progress,
this.cancelToken.token
)
successfulUploads.push({
id: fileData.id,
order: this.getFileInProgressIndex(fileData.id),
data,
})
} catch (error) {
const message = error.handler.getMessage('userFile')
error.handler.handled()
this.forceUpdateFile(fileData.id, {
state: 'failed',
error: message.message,
})
}
}
// Make sure a file has not been removed after it has been uploaded
successfulUploads = successfulUploads.filter(
({ id }) => this.getFileInProgressIndex(id) !== -1
)
// Make sure to re-establish the order in which the files have been submitted
successfulUploads.sort((a, b) => a.order - b.order)
successfulUploads.forEach(({ id }) =>
this.forceRemoveFile(this.getFileInProgressIndex(id))
)
this.addFiles(
this.value,
successfulUploads.map(({ data }) => data)
)
},
forceUpdateFile(id, values) {
const fileIndex = this.getFileInProgressIndex(id)
this.$set(this.filesInProgress, fileIndex, {
...this.files[fileIndex],
...values,
})
},
forceRemoveFile(index) {
if (this.getFileInProgressIndex(this.currentFileUploading.id) === index) {
this.cancelToken.cancel()
}
this.filesInProgress.splice(index, 1)
},
forceAddFile(file, additionalData = {}) {
const id = uuid()
const fileData = {
id,
visible_name: file.name,
isImage: IMAGE_FILE_TYPES.includes(file.type),
percentage: 0,
...additionalData,
}
this.filesInProgress.push(fileData)
return fileData
},
removeFile(...args) {
fileField.methods.removeFile.call(this, ...args)
this.touch()
},
addFiles(...args) {
fileField.methods.addFiles.call(this, ...args)
this.touch()
},
renameFile(value, index, newName) {
const success = fileField.methods.renameFile.call(
this,
value,
index,
newName
)
if (!success) {
this.$refs[`file-uploaded-${index}`][0].resetName()
}
this.touch()
},
getFileInProgressIndex(id) {
return this.filesInProgress.findIndex((file) => file.id === id)
},
},
}
</script>