Skip to content
Snippets Groups Projects
UploadObjectModal.vue 7.23 KiB
Newer Older
  • Learn to ignore specific revisions
  • <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,
    
      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>
    
    Daniel Göbel's avatar
    Daniel Göbel committed
      <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>
    
    Daniel Göbel's avatar
    Daniel Göbel committed
      <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>