feat: migrate to vue 3 (#2689)
--------- Co-authored-by: Joep <jcbuhre@gmail.com> Co-authored-by: Omar Hussein <omarmohammad1951@gmail.com> Co-authored-by: Oleg Lobanov <oleg@lobanov.me>
This commit is contained in:
41
frontend/src/stores/auth.ts
Normal file
41
frontend/src/stores/auth.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { detectLocale, setLocale } from "@/i18n";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
|
||||
export const useAuthStore = defineStore("auth", {
|
||||
// convert to a function
|
||||
state: (): {
|
||||
user: IUser | null;
|
||||
jwt: string;
|
||||
} => ({
|
||||
user: null,
|
||||
jwt: "",
|
||||
}),
|
||||
getters: {
|
||||
// user and jwt getter removed, no longer needed
|
||||
isLoggedIn: (state) => state.user !== null,
|
||||
},
|
||||
actions: {
|
||||
// no context as first argument, use `this` instead
|
||||
setUser(user: IUser) {
|
||||
if (user === null) {
|
||||
this.user = null;
|
||||
return;
|
||||
}
|
||||
|
||||
setLocale(user.locale || detectLocale());
|
||||
this.user = user;
|
||||
},
|
||||
updateUser(user: Partial<IUser>) {
|
||||
if (user.locale) {
|
||||
setLocale(user.locale);
|
||||
}
|
||||
|
||||
this.user = { ...this.user, ...cloneDeep(user) } as IUser;
|
||||
},
|
||||
// easily reset state using `$reset`
|
||||
clearUser() {
|
||||
this.$reset();
|
||||
},
|
||||
},
|
||||
});
|
||||
24
frontend/src/stores/clipboard.ts
Normal file
24
frontend/src/stores/clipboard.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export const useClipboardStore = defineStore("clipboard", {
|
||||
// convert to a function
|
||||
state: (): {
|
||||
key: string;
|
||||
items: ClipItem[];
|
||||
path?: string;
|
||||
} => ({
|
||||
key: "",
|
||||
items: [],
|
||||
path: undefined,
|
||||
}),
|
||||
getters: {
|
||||
// user and jwt getter removed, no longer needed
|
||||
},
|
||||
actions: {
|
||||
// no context as first argument, use `this` instead
|
||||
// easily reset state using `$reset`
|
||||
resetClipboard() {
|
||||
this.$reset();
|
||||
},
|
||||
},
|
||||
});
|
||||
63
frontend/src/stores/file.ts
Normal file
63
frontend/src/stores/file.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export const useFileStore = defineStore("file", {
|
||||
// convert to a function
|
||||
state: (): {
|
||||
req: Resource | null;
|
||||
oldReq: Resource | null;
|
||||
reload: boolean;
|
||||
selected: number[];
|
||||
multiple: boolean;
|
||||
isFiles: boolean;
|
||||
} => ({
|
||||
req: null,
|
||||
oldReq: null,
|
||||
reload: false,
|
||||
selected: [],
|
||||
multiple: false,
|
||||
isFiles: false,
|
||||
}),
|
||||
getters: {
|
||||
selectedCount: (state) => state.selected.length,
|
||||
// route: () => {
|
||||
// const routerStore = useRouterStore();
|
||||
// return routerStore.router.currentRoute;
|
||||
// },
|
||||
// isFiles: (state) => {
|
||||
// const layoutStore = useLayoutStore();
|
||||
// return !layoutStore.loading && state.route._value.name === "Files";
|
||||
// },
|
||||
isListing: (state) => {
|
||||
return state.isFiles && state?.req?.isDir;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// no context as first argument, use `this` instead
|
||||
toggleMultiple() {
|
||||
this.multiple = !this.multiple;
|
||||
},
|
||||
updateRequest(value: Resource | null) {
|
||||
const selectedItems = this.selected.map((i) => this.req?.items[i]);
|
||||
this.oldReq = this.req;
|
||||
this.req = value;
|
||||
|
||||
this.selected = [];
|
||||
|
||||
if (!this.req?.items) return;
|
||||
this.selected = this.req.items
|
||||
.filter((item) =>
|
||||
selectedItems.some((rItem) => rItem?.url === item.url)
|
||||
)
|
||||
.map((item) => item.index);
|
||||
},
|
||||
removeSelected(value: any) {
|
||||
const i = this.selected.indexOf(value);
|
||||
if (i === -1) return;
|
||||
this.selected.splice(i, 1);
|
||||
},
|
||||
// easily reset state using `$reset`
|
||||
clearFile() {
|
||||
this.$reset();
|
||||
},
|
||||
},
|
||||
});
|
||||
12
frontend/src/stores/index.ts
Normal file
12
frontend/src/stores/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { createPinia as _createPinia } from "pinia";
|
||||
import { markRaw } from "vue";
|
||||
import { Router } from "vue-router";
|
||||
|
||||
export default function createPinia(router: Router) {
|
||||
const pinia = _createPinia();
|
||||
pinia.use(({ store }) => {
|
||||
store.router = markRaw(router);
|
||||
});
|
||||
|
||||
return pinia;
|
||||
}
|
||||
74
frontend/src/stores/layout.ts
Normal file
74
frontend/src/stores/layout.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { defineStore } from "pinia";
|
||||
// import { useAuthPreferencesStore } from "./auth-preferences";
|
||||
// import { useAuthEmailStore } from "./auth-email";
|
||||
|
||||
export const useLayoutStore = defineStore("layout", {
|
||||
// convert to a function
|
||||
state: (): {
|
||||
loading: boolean;
|
||||
prompts: PopupProps[];
|
||||
showShell: boolean | null;
|
||||
} => ({
|
||||
loading: false,
|
||||
prompts: [],
|
||||
showShell: false,
|
||||
}),
|
||||
getters: {
|
||||
currentPrompt(state) {
|
||||
return state.prompts.length > 0
|
||||
? state.prompts[state.prompts.length - 1]
|
||||
: null;
|
||||
},
|
||||
currentPromptName(): string | null | undefined {
|
||||
return this.currentPrompt?.prompt;
|
||||
},
|
||||
// user and jwt getter removed, no longer needed
|
||||
},
|
||||
actions: {
|
||||
// no context as first argument, use `this` instead
|
||||
toggleShell() {
|
||||
this.showShell = !this.showShell;
|
||||
},
|
||||
showHover(value: PopupProps | string) {
|
||||
if (typeof value !== "object") {
|
||||
this.prompts.push({
|
||||
prompt: value,
|
||||
confirm: null,
|
||||
action: undefined,
|
||||
props: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.prompts.push({
|
||||
prompt: value.prompt,
|
||||
confirm: value?.confirm,
|
||||
action: value?.action,
|
||||
props: value?.props,
|
||||
});
|
||||
},
|
||||
showError() {
|
||||
this.prompts.push({
|
||||
prompt: "error",
|
||||
confirm: null,
|
||||
action: undefined,
|
||||
props: null,
|
||||
});
|
||||
},
|
||||
showSuccess() {
|
||||
this.prompts.push({
|
||||
prompt: "success",
|
||||
confirm: null,
|
||||
action: undefined,
|
||||
props: null,
|
||||
});
|
||||
},
|
||||
closeHovers() {
|
||||
this.prompts.pop();
|
||||
},
|
||||
// easily reset state using `$reset`
|
||||
clearLayout() {
|
||||
this.$reset();
|
||||
},
|
||||
},
|
||||
});
|
||||
14
frontend/src/stores/router.ts
Normal file
14
frontend/src/stores/router.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
/**
|
||||
* Dummy store for exposing router to be used in other stores
|
||||
* @example
|
||||
* // route: () => {
|
||||
* // const routerStore = useRouterStore();
|
||||
* // return routerStore.router.currentRoute;
|
||||
* // },
|
||||
*/
|
||||
export const useRouterStore = defineStore("routerStore", () => {
|
||||
// can be an empty definition
|
||||
return {};
|
||||
});
|
||||
187
frontend/src/stores/upload.ts
Normal file
187
frontend/src/stores/upload.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { useFileStore } from "./file";
|
||||
import { files as api } from "@/api";
|
||||
import throttle from "lodash/throttle";
|
||||
import buttons from "@/utils/buttons";
|
||||
|
||||
// TODO: make this into a user setting
|
||||
const UPLOADS_LIMIT = 5;
|
||||
|
||||
const beforeUnload = (event: Event) => {
|
||||
event.preventDefault();
|
||||
// To remove >> is deprecated
|
||||
// event.returnValue = "";
|
||||
};
|
||||
|
||||
export const useUploadStore = defineStore("upload", {
|
||||
// convert to a function
|
||||
state: (): {
|
||||
id: number;
|
||||
sizes: number[];
|
||||
progress: Progress[];
|
||||
queue: UploadItem[];
|
||||
uploads: Uploads;
|
||||
speedMbyte: number;
|
||||
eta: number;
|
||||
error: Error | null;
|
||||
} => ({
|
||||
id: 0,
|
||||
sizes: [],
|
||||
progress: [],
|
||||
queue: [],
|
||||
uploads: {},
|
||||
speedMbyte: 0,
|
||||
eta: 0,
|
||||
error: null,
|
||||
}),
|
||||
getters: {
|
||||
// user and jwt getter removed, no longer needed
|
||||
getProgress: (state) => {
|
||||
if (state.progress.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const totalSize = state.sizes.reduce((a, b) => a + b, 0);
|
||||
|
||||
// TODO: this looks ugly but it works with ts now
|
||||
const sum = state.progress.reduce((acc, val) => +acc + +val) as number;
|
||||
return Math.ceil((sum / totalSize) * 100);
|
||||
},
|
||||
filesInUploadCount: (state) => {
|
||||
return Object.keys(state.uploads).length + state.queue.length;
|
||||
},
|
||||
filesInUpload: (state) => {
|
||||
const files = [];
|
||||
|
||||
for (const index in state.uploads) {
|
||||
const upload = state.uploads[index];
|
||||
const id = upload.id;
|
||||
const type = upload.type;
|
||||
const name = upload.file.name;
|
||||
const size = state.sizes[id];
|
||||
const isDir = upload.file.isDir;
|
||||
const progress = isDir
|
||||
? 100
|
||||
: Math.ceil(((state.progress[id] as number) / size) * 100);
|
||||
|
||||
files.push({
|
||||
id,
|
||||
name,
|
||||
progress,
|
||||
type,
|
||||
isDir,
|
||||
});
|
||||
}
|
||||
|
||||
return files.sort((a, b) => a.progress - b.progress);
|
||||
},
|
||||
uploadSpeed: (state) => {
|
||||
return state.speedMbyte;
|
||||
},
|
||||
getETA: (state) => state.eta,
|
||||
},
|
||||
actions: {
|
||||
// no context as first argument, use `this` instead
|
||||
setProgress({ id, loaded }: { id: number; loaded: Progress }) {
|
||||
this.progress[id] = loaded;
|
||||
},
|
||||
setError(error: Error) {
|
||||
this.error = error;
|
||||
},
|
||||
reset() {
|
||||
this.id = 0;
|
||||
this.sizes = [];
|
||||
this.progress = [];
|
||||
this.queue = [];
|
||||
this.uploads = {};
|
||||
this.speedMbyte = 0;
|
||||
this.eta = 0;
|
||||
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 > 0 });
|
||||
this.removeJob(item.id);
|
||||
this.processUploads();
|
||||
},
|
||||
async processUploads() {
|
||||
const uploadsCount = Object.keys(this.uploads).length;
|
||||
|
||||
const isBellowLimit = uploadsCount < UPLOADS_LIMIT;
|
||||
const isQueueEmpty = this.queue.length == 0;
|
||||
const isUploadsEmpty = uploadsCount == 0;
|
||||
|
||||
const isFinished = isQueueEmpty && isUploadsEmpty;
|
||||
const canProcess = isBellowLimit && !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);
|
||||
}
|
||||
},
|
||||
setUploadSpeed(value: number) {
|
||||
this.speedMbyte = value;
|
||||
},
|
||||
setETA(value: number) {
|
||||
this.eta = value;
|
||||
},
|
||||
// easily reset state using `$reset`
|
||||
clearUpload() {
|
||||
this.$reset();
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user