-
Daniel Göbel authoredDaniel Göbel authored
UploadObjectModal.vue 7.23 KiB
<script setup lang="ts">
import BootstrapModal from "@/components/modals/BootstrapModal.vue";
import { computed, onMounted, reactive, ref, watch } from "vue";
import { Modal, Toast } from "bootstrap";
import { partial } from "filesize";
import { useS3ObjectStore } from "@/stores/s3objects";
const fsize = partial({ base: 2, standard: "jedec" });
const objectRepository = useS3ObjectStore();
const props = defineProps<{
modalID: string;
bucketName: string;
keyPrefix: string;
editObjectFileName?: string;
}>();
const randomIDSuffix = Math.random().toString(16).substring(2, 8);
const objectFileInput = ref<HTMLInputElement | undefined>(undefined);
let uploadModal: Modal | null = null;
let successToast: Toast | null = null;
let errorToast: Toast | null = null;
const currentFolders = computed<string[]>(() => props.keyPrefix.split("/"));
watch(
() => props.editObjectFileName,
(nextFileName) => {
formState.key = nextFileName ?? "";
},
);
const formState = reactive({
file: {},
key: "",
uploading: false,
uploadDone: 0,
uploadTotal: 1,
} as {
file: File;
key: string;
uploading: boolean;
uploadDone: number;
uploadTotal: number;
});
const uploadProgress = computed<number>(() =>
Math.round((100 * formState.uploadDone) / formState.uploadTotal),
);
const editObject = computed<boolean>(
() => props.editObjectFileName !== undefined,
);
function uploadObject() {
const key =
props.keyPrefix.length > 0
? props.keyPrefix + "/" + formState.key
: formState.key;
formState.uploadDone = 0;
formState.uploading = true;
objectRepository
.uploadObjectFile(props.bucketName, key, formState.file, (progress) => {
if (progress.loaded != null && progress.total != null) {
formState.uploadDone = progress.loaded;
formState.uploadTotal = progress.total;
}
})
.then(() => {
uploadModal?.hide();
successToast?.show();
formState.key = "";
if (objectFileInput.value != undefined) {
objectFileInput.value.value = "";
}
})
.catch((e) => {
console.error(e);
errorToast?.show();
})
.finally(() => {
formState.uploading = false;
});
}
function fileChange() {
if (objectFileInput.value != undefined) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
formState.file = objectFileInput.value.files![0];
if (!editObject.value) {
formState.key = formState.file.name;
}
}
}
onMounted(() => {
uploadModal = new Modal("#" + props.modalID);
successToast = new Toast("#successToast-" + randomIDSuffix);
errorToast = new Toast("#errorToast-" + randomIDSuffix);
});
</script>
<template>
<div class="toast-container position-fixed top-toast end-0 p-3">
<div
role="alert"
aria-live="assertive"
aria-atomic="true"
class="toast text-bg-success align-items-center border-0"
data-bs-autohide="true"
:id="'successToast-' + randomIDSuffix"
>
<div class="d-flex">
<div class="toast-body">Successfully uploaded file</div>
<button
type="button"
class="btn-close btn-close-white me-2 m-auto"
data-bs-dismiss="toast"
aria-label="Close"
></button>
</div>
</div>
</div>
<div class="toast-container position-fixed top-toast end-0 p-3">
<div
role="alert"
aria-live="assertive"
aria-atomic="true"
class="toast text-bg-danger align-items-center border-0"
data-bs-autohide="true"
:id="'errorToast-' + randomIDSuffix"
>
<div class="d-flex">
<div class="toast-body">
There has been some Error.<br />
Try again later
</div>
<button
type="button"
class="btn-close btn-close-white me-2 m-auto"
data-bs-dismiss="toast"
aria-label="Close"
></button>
</div>
</div>
</div>
<bootstrap-modal
:modalID="modalID"
:static-backdrop="true"
modal-label="Upload Object Modal"
>
<template v-slot:header>
<h4>Upload file to</h4>
<ol class="breadcrumb">
<li class="breadcrumb-item">{{ props.bucketName }}</li>
<li
class="breadcrumb-item"
v-for="folder in currentFolders"
:key="folder"
>
{{ folder }}
</li>
</ol>
</template>
<template v-slot:body>
<div class="container-fluid">
<div class="row">
<form
class="col-7"
:id="'uploadObjectForm' + randomIDSuffix"
@submit.prevent="uploadObject"
>
<div class="mb-3">
<label
:for="'objectFile' + randomIDSuffix"
class="form-label"
v-if="editObject"
>
New File Content *
</label>
<label
:for="'objectFile' + randomIDSuffix"
class="form-label"
v-else
>
File *
</label>
<input
class="form-control"
type="file"
:id="'objectFile' + randomIDSuffix"
ref="objectFileInput"
required
@change="fileChange"
/>
</div>
<div class="mb-3">
<label :for="'objectKey' + randomIDSuffix" class="form-label"
>Filename</label
>
<input
type="text"
:class="{
'form-control-plaintext': editObject,
'form-control': !editObject,
}"
:id="'objectKey' + randomIDSuffix"
required
:disabled="editObject"
v-model="formState.key"
/>
</div>
</form>
<div class="col-5">
Note: Delimiters ('/') are allowed in the file name to place the new
file into a folder that will be created when the file is uploaded
(to any depth of folders).
</div>
</div>
</div>
</template>
<template v-slot:footer>
<div class="w-50 me-auto" v-if="formState.uploading">
<div class="progress">
<div
class="progress-bar bg-info"
role="progressbar"
aria-label="Basic example"
:style="{ width: uploadProgress + '%' }"
:aria-valuenow="uploadProgress"
aria-valuemin="0"
aria-valuemax="100"
>
{{ uploadProgress }}%
</div>
</div>
<span v-if="formState.uploadDone > 0">
{{ fsize(formState.uploadDone) }} /
{{ fsize(formState.uploadTotal) }}
</span>
</div>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
Close
</button>
<button
:disabled="formState.uploading"
type="submit"
:form="'uploadObjectForm' + randomIDSuffix"
class="btn btn-primary"
>
<span
v-if="formState.uploading"
class="spinner-border spinner-border-sm"
role="status"
aria-hidden="true"
></span>
Upload
</button>
</template>
</bootstrap-modal>
</template>
<style scoped></style>