refactor as setup

This commit is contained in:
Ramires Viana 2025-08-01 15:02:18 -03:00
parent 551631d8e2
commit 7c713f27fa

View File

@ -3,6 +3,7 @@ import { useFileStore } from "./file";
import { files as api } from "@/api"; import { files as api } from "@/api";
import { throttle } from "lodash-es"; import { throttle } from "lodash-es";
import buttons from "@/utils/buttons"; import buttons from "@/utils/buttons";
import { computed, ref } from "vue";
// TODO: make this into a user setting // TODO: make this into a user setting
const UPLOADS_LIMIT = 5; const UPLOADS_LIMIT = 5;
@ -25,186 +26,224 @@ function formatSize(bytes: number): string {
return (bytes / k ** i).toFixed(2) + " " + sizes[i]; return (bytes / k ** i).toFixed(2) + " " + sizes[i];
} }
export const useUploadStore = defineStore("upload", { export const useUploadStore = defineStore("upload", () => {
// convert to a function //
state: (): { // STATE
id: number; //
sizes: number[];
progress: number[]; const id = ref<number>(0);
queue: UploadItem[]; const sizes = ref<number[]>([]);
uploads: Uploads; const progress = ref<number[]>([]);
error: Error | null; const queue = ref<UploadItem[]>([]);
} => ({ const uploads = ref<Uploads>({});
id: 0, const error = ref<Error | null>(null);
sizes: [],
progress: [], //
queue: [], // GETTERS
uploads: {}, //
error: null,
}), const getProgress = computed(() => {
getters: { if (progress.value.length === 0) {
// user and jwt getter removed, no longer needed return 0;
getProgress: (state) => { }
if (state.progress.length === 0) {
return 0; const totalSize = sizes.value.reduce((a, b) => a + b, 0);
const sum = progress.value.reduce((a, b) => a + b, 0);
return Math.ceil((sum / totalSize) * 100);
});
const getProgressDecimal = computed(() => {
if (progress.value.length === 0) {
return 0;
}
const totalSize = sizes.value.reduce((a, b) => a + b, 0);
const sum = progress.value.reduce((a, b) => a + b, 0);
return ((sum / totalSize) * 100).toFixed(2);
});
const getTotalProgressBytes = computed(() => {
if (progress.value.length === 0 || sizes.value.length === 0) {
return "0 Bytes";
}
const sum = progress.value.reduce((a, b) => a + b, 0);
return formatSize(sum);
});
const getTotalProgress = computed(() => {
return progress.value.reduce((a, b) => a + b, 0);
});
const getTotalSize = computed(() => {
if (sizes.value.length === 0) {
return "0 Bytes";
}
const totalSize = sizes.value.reduce((a, b) => a + b, 0);
return formatSize(totalSize);
});
const getTotalBytes = computed(() => {
return sizes.value.reduce((a, b) => a + b, 0);
});
const filesInUploadCount = computed(() => {
return Object.keys(uploads.value).length + queue.value.length;
});
const filesInUpload = computed(() => {
const files = [];
for (const index in uploads.value) {
const upload = uploads.value[index];
const id = upload.id;
const type = upload.type;
const name = upload.file.name;
const size = sizes.value[id];
const isDir = upload.file.isDir;
const p = isDir ? 100 : Math.ceil((progress.value[id] / size) * 100);
files.push({
id,
name,
progress: p,
type,
isDir,
});
}
return files.sort((a, b) => a.progress - b.progress);
});
//
// ACTIONS
//
const setProgress = ({ id, loaded }: { id: number; loaded: number }) => {
progress.value[id] = loaded;
};
const setError = (err: Error) => {
error.value = err;
};
const reset = () => {
id.value = 0;
sizes.value = [];
progress.value = [];
queue.value = [];
uploads.value = {};
error.value = null;
};
const addJob = (item: UploadItem) => {
queue.value.push(item);
sizes.value[id.value] = item.file.size;
id.value++;
};
const moveJob = () => {
const item = queue.value[0];
queue.value.shift();
uploads.value[item.id] = item;
};
const removeJob = (id: number) => {
delete uploads.value[id];
};
const upload = (item: UploadItem) => {
const uploadsCount = Object.keys(uploads.value).length;
const isQueueEmpty = queue.value.length == 0;
const isUploadsEmpty = uploadsCount == 0;
if (isQueueEmpty && isUploadsEmpty) {
window.addEventListener("beforeunload", beforeUnload);
buttons.loading("upload");
}
addJob(item);
processUploads();
};
const finishUpload = (item: UploadItem) => {
setProgress({ id: item.id, loaded: item.file.size });
removeJob(item.id);
processUploads();
};
const processUploads = async () => {
const uploadsCount = Object.keys(uploads.value).length;
const isBelowLimit = uploadsCount < UPLOADS_LIMIT;
const isQueueEmpty = queue.value.length == 0;
const isUploadsEmpty = uploadsCount == 0;
const isFinished = isQueueEmpty && isUploadsEmpty;
const canProcess = isBelowLimit && !isQueueEmpty;
if (isFinished) {
const fileStore = useFileStore();
window.removeEventListener("beforeunload", beforeUnload);
buttons.success("upload");
reset();
fileStore.reload = true;
}
if (canProcess) {
const item = queue.value[0];
moveJob();
if (item.file.isDir) {
await api.post(item.path).catch(setError);
} else {
const onUpload = throttle(
(event: ProgressEvent) =>
setProgress({
id: item.id,
loaded: event.loaded,
}),
100,
{ leading: true, trailing: false }
);
await api
.post(item.path, item.file.file as File, item.overwrite, onUpload)
.catch(setError);
} }
const totalSize = state.sizes.reduce((a, b) => a + b, 0); finishUpload(item);
const sum = state.progress.reduce((a, b) => a + b, 0); }
return Math.ceil((sum / totalSize) * 100); };
},
getProgressDecimal: (state) => {
if (state.progress.length === 0) {
return 0;
}
const totalSize = state.sizes.reduce((a, b) => a + b, 0); return {
const sum = state.progress.reduce((a, b) => a + b, 0); // STATE
return ((sum / totalSize) * 100).toFixed(2); id,
}, sizes,
getTotalProgressBytes: (state) => { progress,
if (state.progress.length === 0 || state.sizes.length === 0) { queue,
return "0 Bytes"; uploads,
} error,
const sum = state.progress.reduce((a, b) => a + b, 0);
return formatSize(sum);
},
getTotalProgress: (state) => {
return state.progress.reduce((a, b) => a + b, 0);
},
getTotalSize: (state) => {
if (state.sizes.length === 0) {
return "0 Bytes";
}
const totalSize = state.sizes.reduce((a, b) => a + b, 0);
return formatSize(totalSize);
},
getTotalBytes: (state) => {
return state.sizes.reduce((a, b) => a + b, 0);
},
filesInUploadCount: (state) => {
return Object.keys(state.uploads).length + state.queue.length;
},
filesInUpload: (state) => {
const files = [];
for (const index in state.uploads) { // GETTERS
const upload = state.uploads[index]; getProgress,
const id = upload.id; getProgressDecimal,
const type = upload.type; getTotalProgressBytes,
const name = upload.file.name; getTotalProgress,
const size = state.sizes[id]; getTotalSize,
const isDir = upload.file.isDir; getTotalBytes,
const progress = isDir filesInUploadCount,
? 100 filesInUpload,
: Math.ceil((state.progress[id] / size) * 100);
files.push({ // ACTIONS
id, setProgress,
name, setError,
progress, reset,
type, addJob,
isDir, moveJob,
}); removeJob,
} upload,
finishUpload,
return files.sort((a, b) => a.progress - b.progress); processUploads,
}, };
},
actions: {
// no context as first argument, use `this` instead
setProgress({ id, loaded }: { id: number; loaded: number }) {
this.progress[id] = loaded;
},
setError(error: Error) {
this.error = error;
},
reset() {
this.id = 0;
this.sizes = [];
this.progress = [];
this.queue = [];
this.uploads = {};
this.error = null;
},
addJob(item: UploadItem) {
this.queue.push(item);
this.sizes[this.id] = item.file.size;
this.id++;
},
moveJob() {
const item = this.queue[0];
this.queue.shift();
this.uploads[item.id] = item;
},
removeJob(id: number) {
delete this.uploads[id];
},
upload(item: UploadItem) {
const uploadsCount = Object.keys(this.uploads).length;
const isQueueEmpty = this.queue.length == 0;
const isUploadsEmpty = uploadsCount == 0;
if (isQueueEmpty && isUploadsEmpty) {
window.addEventListener("beforeunload", beforeUnload);
buttons.loading("upload");
}
this.addJob(item);
this.processUploads();
},
finishUpload(item: UploadItem) {
this.setProgress({ id: item.id, loaded: item.file.size });
this.removeJob(item.id);
this.processUploads();
},
async processUploads() {
const uploadsCount = Object.keys(this.uploads).length;
const isBelowLimit = uploadsCount < UPLOADS_LIMIT;
const isQueueEmpty = this.queue.length == 0;
const isUploadsEmpty = uploadsCount == 0;
const isFinished = isQueueEmpty && isUploadsEmpty;
const canProcess = isBelowLimit && !isQueueEmpty;
if (isFinished) {
const fileStore = useFileStore();
window.removeEventListener("beforeunload", beforeUnload);
buttons.success("upload");
this.reset();
fileStore.reload = true;
}
if (canProcess) {
const item = this.queue[0];
this.moveJob();
if (item.file.isDir) {
await api.post(item.path).catch(this.setError);
} else {
const onUpload = throttle(
(event: ProgressEvent) =>
this.setProgress({
id: item.id,
loaded: event.loaded,
}),
100,
{ leading: true, trailing: false }
);
await api
.post(item.path, item.file.file as File, item.overwrite, onUpload)
.catch(this.setError);
}
this.finishUpload(item);
}
},
// easily reset state using `$reset`
clearUpload() {
this.$reset();
},
},
}); });