diff --git a/SecureSend/ClientApp/src/components/FileUploadForm/FileInput.vue b/SecureSend/ClientApp/src/components/FileUploadForm/FileInput.vue index aff3953..45df4e4 100644 --- a/SecureSend/ClientApp/src/components/FileUploadForm/FileInput.vue +++ b/SecureSend/ClientApp/src/components/FileUploadForm/FileInput.vue @@ -7,12 +7,11 @@ import { ref } from "vue"; import { useDropZone } from "@/utils/composables/useDropZone"; import TrashIcon from "@/assets/icons/TrashIcon.vue"; import PlusIcon from "@/assets/icons/PlusIcon.vue"; -import { UploadStatus } from "@/models/enums/UploadStatus"; -import { inject } from "vue"; import CloseIcon from "@/assets/icons/CloseIcon.vue"; import PauseIcon from "@/assets/icons/PauseIcon.vue"; import PlayIcon from "@/assets/icons/PlayIcon.vue"; import OptionsDropdown from "@/components/OptionsDropdown.vue"; +import { UploadState, type UploadStateTuple } from "@/models/UploadStateTuple"; const emit = defineEmits<{ onFielsChange: [files: File[] | null]; @@ -23,7 +22,7 @@ const emit = defineEmits<{ }>(); defineProps<{ - files: Map; + files: Map; isUploadSetup: boolean; }>(); @@ -33,8 +32,6 @@ const onDrop = (files: File[] | null) => { emit("onFielsChange", files); }; -const isLoading = inject("isLoading"); - const { isOverDropZone } = useDropZone(fileDropZone, { onDrop }); @@ -81,7 +78,7 @@ const { isOverDropZone } = useDropZone(fileDropZone, { onDrop }); > diff --git a/SecureSend/ClientApp/src/models/UploadStateTuple.ts b/SecureSend/ClientApp/src/models/UploadStateTuple.ts new file mode 100644 index 0000000..0ef8b05 --- /dev/null +++ b/SecureSend/ClientApp/src/models/UploadStateTuple.ts @@ -0,0 +1,11 @@ +export enum UploadState { + NewFile = 0, + InProgress = 1, + Completed = 2, + Failed = 3, + Paused = 4, + Cancelled = 5, + Merging = 6, +} + +export type UploadStateTuple = [string, UploadState]; diff --git a/SecureSend/ClientApp/src/models/enums/UploadStatus.ts b/SecureSend/ClientApp/src/models/enums/UploadStatus.ts deleted file mode 100644 index 62a67d8..0000000 --- a/SecureSend/ClientApp/src/models/enums/UploadStatus.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum UploadStatus { - finishing = "Finishing upload...", - completed = "Upload completed", - cancelled = "Upload cancelled", - error = "Error with uploading file", - paused = "Upload paused", -} diff --git a/SecureSend/ClientApp/src/services/SecureSendService.ts b/SecureSend/ClientApp/src/services/SecureSendService.ts index 6d5ad0c..519acfb 100644 --- a/SecureSend/ClientApp/src/services/SecureSendService.ts +++ b/SecureSend/ClientApp/src/services/SecureSendService.ts @@ -28,7 +28,6 @@ export abstract class SecureSendService { ): Promise => { const formData = new FormData(); formData.append("chunk", new Blob([chunk], { type: fileType }), name); - chunkNumber = +chunkNumber + 1; const requestOptions: RequestInit = { method: "POST", body: formData, diff --git a/SecureSend/ClientApp/src/views/FileUploadView.vue b/SecureSend/ClientApp/src/views/FileUploadView.vue index f910a9c..2d293fa 100644 --- a/SecureSend/ClientApp/src/views/FileUploadView.vue +++ b/SecureSend/ClientApp/src/views/FileUploadView.vue @@ -119,9 +119,8 @@ import SchemaInput from "@/components/SchemaInput.vue"; import { SecureSendService } from "@/services/SecureSendService"; import AuthenticatedSecretKeyCryptographyService from "@/utils/AuthenticatedSecretKeyCryptographyService"; import splitFile from "@/utils/splitFile"; -import { ref, type Ref } from "vue"; +import { computed, inject, ref, type Ref } from "vue"; import { useForm } from "vee-validate"; -import { computed } from "vue"; import FileInput from "@/components/FileUploadForm/FileInput.vue"; import FormStepper from "@/components/FileUploadForm/FormStepper.vue"; import StyledButton from "@/components/StyledButton.vue"; @@ -129,11 +128,10 @@ import { ButtonType } from "@/models/enums/ButtonType"; import ConfirmModalVue from "@/components/ConfirmModal.vue"; import { useConfirmDialog } from "@/utils/composables/useConfirmDialog"; import SimpleInput from "@/components/SimpleInput.vue"; -import { inject } from "vue"; import LoadingIndicator from "@/components/LoadingIndicator.vue"; import { useAlert } from "@/utils/composables/useAlert"; -import { UploadStatus } from "@/models/enums/UploadStatus"; import CheckboxSchemaInput from "@/components/CheckboxSchemaInput.vue"; +import { UploadState, type UploadStateTuple } from "@/models/UploadStateTuple"; interface IMappedFormValues { expiryDate: string; @@ -153,7 +151,7 @@ const step = ref(0); let uuid = self.crypto.randomUUID(); -const files = ref(new Map()); +const files = ref(new Map()); const fileKeys = new Map(); const pausedFiles = new Map void)>(); const controllers = new Map(); @@ -208,9 +206,10 @@ const handleUploadCallback = async ( ) => { try { await handlePause(file); + const currentChunk = num + 1; await SecureSendService.uploadChunk( uuid, - num, + currentChunk, totalChunks, file.name, chunk, @@ -218,27 +217,31 @@ const handleUploadCallback = async ( file.size, controllers.get(file.name)?.signal ); - files.value.set( - file, - num + 1 === totalChunks - ? true - : Math.ceil(((num + 1) / totalChunks) * 100) - ); + const progress = Math.ceil((currentChunk / totalChunks) * 100); + const stateTuple: UploadStateTuple = + currentChunk === totalChunks + ? ["Upload completed", UploadState.Completed] + : currentChunk === totalChunks - 1 + ? ["Finishing upload...", UploadState.Merging] + : [`${progress}%`, UploadState.InProgress]; + files.value.set(file, stateTuple); } catch (error: any) { if ( - error === UploadStatus.cancelled || + error === UploadState.Cancelled || error.code === DOMException.ABORT_ERR ) { - files.value.set(file, UploadStatus.cancelled); + files.value.set(file, ["Upload cancelled.", UploadState.Cancelled]); } else { - files.value.set(file, UploadStatus.error); + files.value.set(file, ["Error with uploading file.", UploadState.Failed]); } throw error; } }; const handleUploadResult = async () => { - if ([...files.value.values()].find((file) => file === true)) { + if ( + [...files.value.values()].find((file) => file[1] === UploadState.Completed) + ) { const { data } = await reveal(); if (data) { formReset(); @@ -260,7 +263,7 @@ const handlePause = async (file: File) => { controllers.set(file.name, controller); } if (pausedFiles.get(file)) { - files.value.set(file, UploadStatus.paused); + files.value.set(file, ["Upload paused", UploadState.Paused]); const promise = new Promise((res) => { pausedFiles.set(file, res); }); @@ -282,11 +285,15 @@ const onSubmit = handleSubmit(async () => { isLoading!.value = false; await handleUploadResult(); } catch (error) { - if (![...files.value.values()].find((file) => file === true)) { + if ( + ![...files.value.values()].find( + (file) => file[1] === UploadState.Completed + ) + ) { openDanger("Upload failed, try again."); formReset(); } else { - handleUploadResult(); + await handleUploadResult(); } } } else { @@ -296,7 +303,7 @@ const onSubmit = handleSubmit(async () => { const onCancel = async (fileObj: File) => { const controller = controllers.get(fileObj.name); if (controller) { - controller.abort(UploadStatus.cancelled); + controller.abort(UploadState.Cancelled); onResume(fileObj); await SecureSendService.cancelUpload({ id: uuid, fileName: fileObj.name }); } @@ -322,7 +329,7 @@ const onFilesChange = (formFiles: File[] | null) => { for (let i = 0; i < formFiles.length; i++) { const file = formFiles[i]; if (!fileKeys.has(file.name)) { - files.value.set(file, 0); + files.value.set(file, [`0%`, UploadState.NewFile]); fileKeys.set(file.name, true); } } @@ -350,7 +357,7 @@ const encryptFile = async () => { (promise) => promise.status === "rejected" && promise.reason.code !== DOMException.ABORT_ERR && - promise.reason !== UploadStatus.cancelled + promise.reason !== UploadState.Cancelled ) ) { throw new Error("Upload error");