Migration to vue3 continued
Replace vuex with pinia Fix some routing issues Fix most vue3 breaking changes
This commit is contained in:
parent
b9574d9e62
commit
7c91ba03b7
@ -4,7 +4,7 @@
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended",
|
||||
"@vue/eslint-config-prettier"
|
||||
],
|
||||
|
||||
@ -8,25 +8,19 @@
|
||||
content="width=device-width, initial-scale=1, user-scalable=no"
|
||||
/>
|
||||
|
||||
[{[ if .ReCaptcha -]}]
|
||||
<script src="[{[ .ReCaptchaHost ]}]/recaptcha/api.js?render=explicit"></script>
|
||||
[{[ end ]}]
|
||||
|
||||
<title>
|
||||
[{[ if .Name -]}][{[ .Name ]}][{[ else ]}]File Browser[{[ end ]}]
|
||||
</title>
|
||||
<title>File Browser</title>
|
||||
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="[{[ .StaticURL ]}]/img/icons/favicon-32x32.png"
|
||||
href="/img/icons/favicon-32x32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="[{[ .StaticURL ]}]/img/icons/favicon-16x16.png"
|
||||
href="/img/icons/favicon-16x16.png"
|
||||
/>
|
||||
|
||||
<!-- Add to home screen for Android and modern mobile browsers -->
|
||||
@ -35,49 +29,63 @@
|
||||
id="manifestPlaceholder"
|
||||
crossorigin="use-credentials"
|
||||
/>
|
||||
<meta
|
||||
name="theme-color"
|
||||
content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]"
|
||||
/>
|
||||
<meta name="theme-color" content="#2979ff" />
|
||||
|
||||
<!-- Add to home screen for Safari on iOS/iPadOS -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
<meta name="apple-mobile-web-app-title" content="assets" />
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
href="[{[ .StaticURL ]}]/img/icons/apple-touch-icon.png"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="/img/icons/apple-touch-icon.png" />
|
||||
|
||||
<!-- Add to home screen for Windows -->
|
||||
<meta
|
||||
name="msapplication-TileImage"
|
||||
content="[{[ .StaticURL ]}]/img/icons/mstile-144x144.png"
|
||||
/>
|
||||
<meta
|
||||
name="msapplication-TileColor"
|
||||
content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]"
|
||||
content="/img/icons/mstile-144x144.png"
|
||||
/>
|
||||
<meta name="msapplication-TileColor" content="#2979ff" />
|
||||
|
||||
<!-- Inject Some Variables and generate the manifest json -->
|
||||
<script>
|
||||
<!-- Json is actually a JS object, assign it directly -->
|
||||
window.FileBrowser = [{[ .Json ]}];
|
||||
<!-- Global function to prepend static url -->
|
||||
// We can assign JSON directly
|
||||
window.FileBrowser = {
|
||||
AuthMethod: "json",
|
||||
BaseURL: "",
|
||||
CSS: false,
|
||||
Color: "",
|
||||
DisableExternal: false,
|
||||
DisableUsedPercentage: false,
|
||||
EnableExec: true,
|
||||
EnableThumbs: true,
|
||||
LoginPage: true,
|
||||
Name: "",
|
||||
NoAuth: false,
|
||||
ReCaptcha: false,
|
||||
ResizePreview: true,
|
||||
Signup: false,
|
||||
StaticURL: "",
|
||||
Theme: "",
|
||||
TusSettings: { chunkSize: 10485760, retryCount: 5 },
|
||||
Version: "(untracked)",
|
||||
};
|
||||
// Global function to prepend static url
|
||||
window.__prependStaticUrl = (url) => {
|
||||
return `${window.FileBrowser.StaticURL}/${url.replace(/^\/+/, '')}`;
|
||||
return `${window.FileBrowser.StaticURL}/${url.replace(/^\/+/, "")}`;
|
||||
};
|
||||
var dynamicManifest = {
|
||||
name: window.FileBrowser.Name || "File Browser",
|
||||
short_name: window.FileBrowser.Name || "File Browser",
|
||||
icons: [
|
||||
{
|
||||
src: window.__prependStaticUrl("/img/icons/android-chrome-192x192.png"),
|
||||
src: window.__prependStaticUrl(
|
||||
"/img/icons/android-chrome-192x192.png"
|
||||
),
|
||||
sizes: "192x192",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
src: window.__prependStaticUrl("/img/icons/android-chrome-512x512.png"),
|
||||
src: window.__prependStaticUrl(
|
||||
"/img/icons/android-chrome-512x512.png"
|
||||
),
|
||||
sizes: "512x512",
|
||||
type: "image/png",
|
||||
},
|
||||
@ -180,14 +188,5 @@
|
||||
</div>
|
||||
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
|
||||
[{[ if .Theme -]}]
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="[{[ .StaticURL ]}]/themes/[{[ .Theme ]}].css"
|
||||
/>
|
||||
[{[ end ]}] [{[ if .CSS -]}]
|
||||
<link rel="stylesheet" href="[{[ .StaticURL ]}]/custom.css" />
|
||||
[{[ end ]}]
|
||||
</body>
|
||||
</html>
|
||||
|
||||
10
frontend/jsconfig.json
Normal file
10
frontend/jsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
1653
frontend/package-lock.json
generated
1653
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,46 +14,48 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/compat": "^3.3.4",
|
||||
"ace-builds": "^1.23.4",
|
||||
"@vueuse/core": "^10.4.1",
|
||||
"ace-builds": "^1.24.1",
|
||||
"clipboard": "^2.0.11",
|
||||
"core-js": "^3.32.0",
|
||||
"core-js": "^3.32.1",
|
||||
"css-vars-ponyfill": "^2.4.8",
|
||||
"filesize": "^10.0.8",
|
||||
"filesize": "^10.0.12",
|
||||
"js-base64": "^3.7.5",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"material-icons": "^1.13.9",
|
||||
"material-icons": "^1.13.10",
|
||||
"moment": "^2.29.4",
|
||||
"normalize.css": "^8.0.1",
|
||||
"noty": "^3.2.0-beta",
|
||||
"pinia": "^2.1.6",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
"qrcode.vue": "^3.4.1",
|
||||
"tus-js-client": "^3.1.1",
|
||||
"utif": "^3.1.0",
|
||||
"vue": "^3.3.4",
|
||||
"vue-async-computed": "^3.9.0",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-lazyload": "^1.3.5",
|
||||
"vue-lazyload": "^3.0.0",
|
||||
"vue-router": "^4.2.4",
|
||||
"vue-simple-progress": "^1.1.1",
|
||||
"vuex": "^4.1.0",
|
||||
"whatwg-fetch": "^3.6.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@intlify/unplugin-vue-i18n": "^0.12.2",
|
||||
"@intlify/unplugin-vue-i18n": "^0.12.3",
|
||||
"@vitejs/plugin-legacy": "^4.1.1",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vitejs/plugin-vue": "^4.3.3",
|
||||
"@vue/eslint-config-prettier": "^8.0.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"eslint": "^8.46.0",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"eslint": "^8.48.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-vue": "^9.16.1",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"jsdom": "^22.1.0",
|
||||
"postcss": "^8.4.27",
|
||||
"prettier": "^3.0.1",
|
||||
"postcss": "^8.4.28",
|
||||
"prettier": "^3.0.2",
|
||||
"terser": "^5.19.2",
|
||||
"vite": "^4.4.9",
|
||||
"vite-plugin-compression2": "^0.10.3"
|
||||
"vite-plugin-compression2": "^0.10.4",
|
||||
"vite-plugin-rewrite-all": "^1.0.1"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
||||
@ -1,144 +1,193 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, user-scalable=no"
|
||||
/>
|
||||
|
||||
[{[ if .ReCaptcha -]}]
|
||||
[{[ if .ReCaptcha -]}]
|
||||
<script src="[{[ .ReCaptchaHost ]}]/recaptcha/api.js?render=explicit"></script>
|
||||
[{[ end ]}]
|
||||
[{[ end ]}]
|
||||
|
||||
<title>[{[ if .Name -]}][{[ .Name ]}][{[ else ]}]File Browser[{[ end ]}]</title>
|
||||
<title>
|
||||
[{[ if .Name -]}][{[ .Name ]}][{[ else ]}]File Browser[{[ end ]}]
|
||||
</title>
|
||||
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="[{[ .StaticURL ]}]/img/icons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="[{[ .StaticURL ]}]/img/icons/favicon-16x16.png">
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="[{[ .StaticURL ]}]/img/icons/favicon-32x32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="[{[ .StaticURL ]}]/img/icons/favicon-16x16.png"
|
||||
/>
|
||||
|
||||
<!-- Add to home screen for Android and modern mobile browsers -->
|
||||
<link rel="manifest" id="manifestPlaceholder" crossorigin="use-credentials">
|
||||
<meta name="theme-color" content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]">
|
||||
<!-- Add to home screen for Android and modern mobile browsers -->
|
||||
<link
|
||||
rel="manifest"
|
||||
id="manifestPlaceholder"
|
||||
crossorigin="use-credentials"
|
||||
/>
|
||||
<meta
|
||||
name="theme-color"
|
||||
content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]"
|
||||
/>
|
||||
|
||||
<!-- Add to home screen for Safari on iOS/iPadOS -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="assets">
|
||||
<link rel="apple-touch-icon" href="[{[ .StaticURL ]}]/img/icons/apple-touch-icon.png">
|
||||
<!-- Add to home screen for Safari on iOS/iPadOS -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
<meta name="apple-mobile-web-app-title" content="assets" />
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
href="[{[ .StaticURL ]}]/img/icons/apple-touch-icon.png"
|
||||
/>
|
||||
|
||||
<!-- Add to home screen for Windows -->
|
||||
<meta name="msapplication-TileImage" content="[{[ .StaticURL ]}]/img/icons/mstile-144x144.png">
|
||||
<meta name="msapplication-TileColor" content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]">
|
||||
<!-- Add to home screen for Windows -->
|
||||
<meta
|
||||
name="msapplication-TileImage"
|
||||
content="[{[ .StaticURL ]}]/img/icons/mstile-144x144.png"
|
||||
/>
|
||||
<meta
|
||||
name="msapplication-TileColor"
|
||||
content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]"
|
||||
/>
|
||||
|
||||
<!-- Inject Some Variables and generate the manifest json -->
|
||||
<script>
|
||||
window.FileBrowser = JSON.parse('[{[ .Json ]}]');
|
||||
<!-- Inject Some Variables and generate the manifest json -->
|
||||
<script>
|
||||
// We can assign JSON directly
|
||||
window.FileBrowser = [{[ .Json ]}];
|
||||
// Global function to prepend static url
|
||||
window.__prependStaticUrl = (url) => {
|
||||
return `${window.FileBrowser.StaticURL}/${url.replace(/^\/+/, "")}`;
|
||||
};
|
||||
var dynamicManifest = {
|
||||
name: window.FileBrowser.Name || "File Browser",
|
||||
short_name: window.FileBrowser.Name || "File Browser",
|
||||
icons: [
|
||||
{
|
||||
src: window.__prependStaticUrl("/img/icons/android-chrome-192x192.png"),
|
||||
sizes: "192x192",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
src: window.__prependStaticUrl("/img/icons/android-chrome-512x512.png"),
|
||||
sizes: "512x512",
|
||||
type: "image/png",
|
||||
},
|
||||
],
|
||||
start_url: window.location.origin + window.FileBrowser.BaseURL,
|
||||
display: "standalone",
|
||||
background_color: "#ffffff",
|
||||
theme_color: window.FileBrowser.Color || "#455a64",
|
||||
};
|
||||
|
||||
var fullStaticURL = window.location.origin + window.FileBrowser.StaticURL;
|
||||
var dynamicManifest = {
|
||||
"name": window.FileBrowser.Name || 'File Browser',
|
||||
"short_name": window.FileBrowser.Name || 'File Browser',
|
||||
"icons": [
|
||||
{
|
||||
"src": fullStaticURL + "/img/icons/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": fullStaticURL + "/img/icons/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
const stringManifest = JSON.stringify(dynamicManifest);
|
||||
const blob = new Blob([stringManifest], { type: "application/json" });
|
||||
const manifestURL = URL.createObjectURL(blob);
|
||||
document
|
||||
.querySelector("#manifestPlaceholder")
|
||||
.setAttribute("href", manifestURL);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#loading {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
z-index: 9999;
|
||||
transition: 0.1s ease opacity;
|
||||
-webkit-transition: 0.1s ease opacity;
|
||||
}
|
||||
|
||||
#loading.done {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#loading .spinner {
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
#loading .spinner > div {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: #333;
|
||||
border-radius: 100%;
|
||||
display: inline-block;
|
||||
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
#loading .spinner .bounce1 {
|
||||
-webkit-animation-delay: -0.32s;
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
#loading .spinner .bounce2 {
|
||||
-webkit-animation-delay: -0.16s;
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-bouncedelay {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
-webkit-transform: scale(0);
|
||||
}
|
||||
],
|
||||
"start_url": window.location.origin + window.FileBrowser.BaseURL,
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": window.FileBrowser.Color || "#455a64"
|
||||
}
|
||||
40% {
|
||||
-webkit-transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
const stringManifest = JSON.stringify(dynamicManifest);
|
||||
const blob = new Blob([stringManifest], {type: 'application/json'});
|
||||
const manifestURL = URL.createObjectURL(blob);
|
||||
document.querySelector('#manifestPlaceholder').setAttribute('href', manifestURL);
|
||||
</script>
|
||||
@keyframes sk-bouncedelay {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
}
|
||||
40% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<style>
|
||||
#loading {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
z-index: 9999;
|
||||
transition: .1s ease opacity;
|
||||
-webkit-transition: .1s ease opacity;
|
||||
}
|
||||
|
||||
#loading.done {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#loading .spinner {
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
#loading .spinner > div {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: #333;
|
||||
border-radius: 100%;
|
||||
display: inline-block;
|
||||
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
#loading .spinner .bounce1 {
|
||||
-webkit-animation-delay: -0.32s;
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
#loading .spinner .bounce2 {
|
||||
-webkit-animation-delay: -0.16s;
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes sk-bouncedelay {
|
||||
0%, 80%, 100% { -webkit-transform: scale(0) }
|
||||
40% { -webkit-transform: scale(1.0) }
|
||||
}
|
||||
|
||||
@keyframes sk-bouncedelay {
|
||||
0%, 80%, 100% {
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
} 40% {
|
||||
-webkit-transform: scale(1.0);
|
||||
transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<div id="loading">
|
||||
<div class="spinner">
|
||||
<div class="bounce1"></div>
|
||||
<div class="bounce2"></div>
|
||||
<div class="bounce3"></div>
|
||||
<div id="loading">
|
||||
<div class="spinner">
|
||||
<div class="bounce1"></div>
|
||||
<div class="bounce2"></div>
|
||||
<div class="bounce3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
[{[ if .Theme -]}]
|
||||
<link rel="stylesheet" href="[{[ .StaticURL ]}]/themes/[{[ .Theme ]}].css" />
|
||||
[{[ end ]}]
|
||||
[{[ if .CSS -]}]
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
|
||||
[{[ if .Theme -]}]
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="[{[ .StaticURL ]}]/themes/[{[ .Theme ]}].css"
|
||||
/>
|
||||
[{[ end ]}] [{[ if .CSS -]}]
|
||||
<link rel="stylesheet" href="[{[ .StaticURL ]}]/custom.css" />
|
||||
[{[ end ]}]
|
||||
</body>
|
||||
[{[ end ]}]
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -5,9 +5,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// eslint-disable-next-line no-undef
|
||||
// __webpack_public_path__ = window.FileBrowser.StaticURL + "/";
|
||||
|
||||
export default {
|
||||
name: "app",
|
||||
mounted() {
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
import { removePrefix } from "./utils";
|
||||
import { baseURL } from "@/utils/constants";
|
||||
import store from "@/store";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
|
||||
const ssl = window.location.protocol === "https:";
|
||||
const protocol = ssl ? "wss:" : "ws:";
|
||||
|
||||
export default function command(url, command, onmessage, onclose) {
|
||||
const authStore = useAuthStore();
|
||||
|
||||
url = removePrefix(url);
|
||||
url = `${protocol}//${window.location.host}${baseURL}/api/command${url}?auth=${store.state.jwt}`;
|
||||
url = `${protocol}//${window.location.host}${baseURL}/api/command${url}?auth=${authStore.jwt}`;
|
||||
|
||||
let conn = new window.WebSocket(url);
|
||||
conn.onopen = () => conn.send(command);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { createURL, fetchURL, removePrefix } from "./utils";
|
||||
import { baseURL } from "@/utils/constants";
|
||||
import store from "@/store";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { upload as postTus, useTus } from "./tus";
|
||||
|
||||
export async function fetch(url) {
|
||||
@ -71,8 +71,9 @@ export function download(format, ...files) {
|
||||
url += `algo=${format}&`;
|
||||
}
|
||||
|
||||
if (store.state.jwt) {
|
||||
url += `auth=${store.state.jwt}&`;
|
||||
const authStore = useAuthStore();
|
||||
if (authStore.jwt) {
|
||||
url += `auth=${authStore.jwt}&`;
|
||||
}
|
||||
|
||||
window.open(url);
|
||||
@ -104,6 +105,7 @@ async function postResources(url, content = "", overwrite = false, onupload) {
|
||||
bufferContent = await new Response(content).arrayBuffer();
|
||||
}
|
||||
|
||||
const authStore = useAuthStore();
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = new XMLHttpRequest();
|
||||
request.open(
|
||||
@ -111,7 +113,7 @@ async function postResources(url, content = "", overwrite = false, onupload) {
|
||||
`${baseURL}/api/resources${url}?override=${overwrite}`,
|
||||
true
|
||||
);
|
||||
request.setRequestHeader("X-Auth", store.state.jwt);
|
||||
request.setRequestHeader("X-Auth", authStore.jwt);
|
||||
|
||||
if (typeof onupload === "function") {
|
||||
request.upload.onprogress = onupload;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import * as tus from "tus-js-client";
|
||||
import { baseURL, tusEndpoint, tusSettings } from "@/utils/constants";
|
||||
import store from "@/store";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { removePrefix } from "@/api/utils";
|
||||
import { fetchURL } from "./utils";
|
||||
|
||||
@ -23,6 +23,7 @@ export async function upload(
|
||||
|
||||
await createUpload(resourcePath);
|
||||
|
||||
const authStore = useAuthStore();
|
||||
return new Promise((resolve, reject) => {
|
||||
let upload = new tus.Upload(content, {
|
||||
uploadUrl: `${baseURL}${resourcePath}`,
|
||||
@ -31,7 +32,7 @@ export async function upload(
|
||||
parallelUploads: 1,
|
||||
storeFingerprintForResuming: false,
|
||||
headers: {
|
||||
"X-Auth": store.state.jwt,
|
||||
"X-Auth": authStore.jwt,
|
||||
},
|
||||
onError: function (error) {
|
||||
reject("Upload failed: " + error);
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import store from "@/store";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { renew, logout } from "@/utils/auth";
|
||||
import { baseURL } from "@/utils/constants";
|
||||
import { encodePath } from "@/utils/url";
|
||||
|
||||
export async function fetchURL(url, opts, auth = true) {
|
||||
const authStore = useAuthStore();
|
||||
|
||||
opts = opts || {};
|
||||
opts.headers = opts.headers || {};
|
||||
|
||||
@ -12,7 +14,7 @@ export async function fetchURL(url, opts, auth = true) {
|
||||
try {
|
||||
res = await fetch(`${baseURL}${url}`, {
|
||||
headers: {
|
||||
"X-Auth": store.state.jwt,
|
||||
"X-Auth": authStore.jwt,
|
||||
...headers,
|
||||
},
|
||||
...rest,
|
||||
@ -25,7 +27,7 @@ export async function fetchURL(url, opts, auth = true) {
|
||||
}
|
||||
|
||||
if (auth && res.headers.get("X-Renew-Token") === "true") {
|
||||
await renew(store.state.jwt);
|
||||
await renew(authStore.jwt);
|
||||
}
|
||||
|
||||
if (res.status < 200 || res.status > 299) {
|
||||
@ -61,6 +63,8 @@ export function removePrefix(url) {
|
||||
}
|
||||
|
||||
export function createURL(endpoint, params = {}, auth = true) {
|
||||
const authStore = useAuthStore();
|
||||
|
||||
let prefix = baseURL;
|
||||
if (!prefix.endsWith("/")) {
|
||||
prefix = prefix + "/";
|
||||
@ -68,7 +72,7 @@ export function createURL(endpoint, params = {}, auth = true) {
|
||||
const url = new URL(prefix + encodePath(endpoint), origin);
|
||||
|
||||
const searchParams = {
|
||||
...(auth && { auth: store.state.jwt }),
|
||||
...(auth && { auth: authStore.jwt }),
|
||||
...params,
|
||||
};
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@
|
||||
</template>
|
||||
<ul v-show="results.length > 0">
|
||||
<li v-for="(s, k) in filteredResults" :key="k">
|
||||
<router-link @click.native="close" :to="s.url">
|
||||
<router-link v-on:click="close" :to="s.url">
|
||||
<i v-if="s.dir" class="material-icons">folder</i>
|
||||
<i v-else class="material-icons">insert_drive_file</i>
|
||||
<span>./{{ s.path }}</span>
|
||||
@ -65,7 +65,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters, mapMutations } from "vuex";
|
||||
import { mapActions, mapState, mapWritableState } from "pinia";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import url from "@/utils/url";
|
||||
import { search } from "@/api";
|
||||
|
||||
@ -95,7 +98,7 @@ export default {
|
||||
|
||||
if (old === "search" && !this.active) {
|
||||
if (this.reload) {
|
||||
this.setReload(true);
|
||||
this.sReload = true;
|
||||
}
|
||||
|
||||
document.body.style.overflow = "auto";
|
||||
@ -116,8 +119,9 @@ export default {
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user", "show"]),
|
||||
...mapGetters(["isListing"]),
|
||||
...mapState(useFileStore, ["isListing"]),
|
||||
...mapState(useLayoutStore, ["show"]),
|
||||
...mapWritableState(useFileStore, { sReload: "reload" }),
|
||||
boxes() {
|
||||
return boxes;
|
||||
},
|
||||
@ -148,7 +152,7 @@ export default {
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["showHover", "closeHovers", "setReload"]),
|
||||
...mapActions(useLayoutStore, ["showHover", "closeHovers"]),
|
||||
open() {
|
||||
this.showHover("search");
|
||||
},
|
||||
@ -199,3 +203,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/file@/stores/layout
|
||||
|
||||
@ -20,9 +20,9 @@
|
||||
tabindex="0"
|
||||
ref="input"
|
||||
class="shell__text"
|
||||
contenteditable="true"
|
||||
@keydown.prevent.38="historyUp"
|
||||
@keydown.prevent.40="historyDown"
|
||||
:contenteditable="true"
|
||||
@keydown.prevent.arrow-up="historyUp"
|
||||
@keydown.prevent.arrow-down="historyDown"
|
||||
@keypress.prevent.enter="submit"
|
||||
/>
|
||||
</div>
|
||||
@ -30,14 +30,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapMutations, mapState, mapGetters } from "vuex";
|
||||
import { mapState, mapActions } from "pinia";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import { commands } from "@/api";
|
||||
|
||||
export default {
|
||||
name: "shell",
|
||||
computed: {
|
||||
...mapState(["user", "showShell"]),
|
||||
...mapGetters(["isFiles", "isLogged"]),
|
||||
...mapState(useLayoutStore, ["showShell"]),
|
||||
...mapState(useFileStore, ["isFiles"]),
|
||||
path: function () {
|
||||
if (this.isFiles) {
|
||||
return this.$route.path;
|
||||
@ -53,7 +56,7 @@ export default {
|
||||
canInput: true,
|
||||
}),
|
||||
methods: {
|
||||
...mapMutations(["toggleShell"]),
|
||||
...mapActions(useLayoutStore, ["toggleShell"]),
|
||||
scroll: function () {
|
||||
this.$refs.scrollable.scrollTop = this.$refs.scrollable.scrollHeight;
|
||||
},
|
||||
@ -126,3 +129,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/file@/stores/layout
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<nav :class="{ active }">
|
||||
<template v-if="isLogged">
|
||||
<template v-if="isLoggedIn">
|
||||
<button
|
||||
class="action"
|
||||
@click="toRoot"
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
<div v-if="user.perm.create">
|
||||
<button
|
||||
@click="$store.commit('showHover', 'newDir')"
|
||||
@click="showHover('newDir')"
|
||||
class="action"
|
||||
:aria-label="$t('sidebar.newFolder')"
|
||||
:title="$t('sidebar.newFolder')"
|
||||
@ -23,7 +23,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
@click="$store.commit('showHover', 'newFile')"
|
||||
@click="showHover('newFile')"
|
||||
class="action"
|
||||
:aria-label="$t('sidebar.newFile')"
|
||||
:title="$t('sidebar.newFile')"
|
||||
@ -83,7 +83,7 @@
|
||||
<div
|
||||
class="credits"
|
||||
v-if="
|
||||
$router.currentRoute.path.includes('/files/') && !disableUsedPercentage
|
||||
$router.currentRoute.path?.includes('/files/') && !disableUsedPercentage
|
||||
"
|
||||
style="width: 90%; margin: 2em 2.5em 3em 2.5em"
|
||||
>
|
||||
@ -112,7 +112,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import * as auth from "@/utils/auth";
|
||||
import {
|
||||
version,
|
||||
@ -132,10 +135,10 @@ export default {
|
||||
ProgressBar,
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user"]),
|
||||
...mapGetters(["isLogged"]),
|
||||
...mapState(useAuthStore, ["user", "isLoggedIn"]),
|
||||
...mapState(useLayoutStore, ["show"]),
|
||||
active() {
|
||||
return this.$store.state.show === "sidebar";
|
||||
return this.show === "sidebar";
|
||||
},
|
||||
signup: () => signup,
|
||||
version: () => version,
|
||||
@ -172,18 +175,20 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["closeHovers", "showHover"]),
|
||||
toRoot() {
|
||||
this.$router.push({ path: "/files/" }, () => {});
|
||||
this.$store.commit("closeHovers");
|
||||
this.$router.push({ path: "/files/" });
|
||||
this.closeHovers();
|
||||
},
|
||||
toSettings() {
|
||||
this.$router.push({ path: "/settings" }, () => {});
|
||||
this.$store.commit("closeHovers");
|
||||
this.$router.push({ path: "/settings" });
|
||||
this.closeHovers();
|
||||
},
|
||||
help() {
|
||||
this.$store.commit("showHover", "help");
|
||||
this.showHover("help");
|
||||
},
|
||||
logout: auth.logout,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/auth@/stores/layout
|
||||
|
||||
@ -10,12 +10,7 @@
|
||||
@mouseup="mouseUp"
|
||||
@wheel="wheelMove"
|
||||
>
|
||||
<img
|
||||
src=""
|
||||
class="image-ex-img image-ex-img-center"
|
||||
ref="imgex"
|
||||
@load="onLoad"
|
||||
/>
|
||||
<img class="image-ex-img image-ex-img-center" ref="imgex" @load="onLoad" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
@ -73,7 +68,7 @@ export default {
|
||||
|
||||
window.addEventListener("resize", this.onResize);
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
window.removeEventListener("resize", this.onResize);
|
||||
document.removeEventListener("mouseup", this.onMouseUp);
|
||||
},
|
||||
|
||||
@ -35,8 +35,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapActions, mapWritableState } from "pinia";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import { enableThumbs } from "@/utils/constants";
|
||||
import { mapMutations, mapGetters, mapState } from "vuex";
|
||||
import { filesize } from "filesize";
|
||||
import moment from "moment";
|
||||
import { files as api } from "@/api";
|
||||
@ -44,6 +48,9 @@ import * as upload from "@/utils/upload";
|
||||
|
||||
export default {
|
||||
name: "item",
|
||||
compatConfig: {
|
||||
ATTR_FALSE_VALUE: "suppress-warning",
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
touches: 0,
|
||||
@ -61,8 +68,9 @@ export default {
|
||||
"path",
|
||||
],
|
||||
computed: {
|
||||
...mapState(["user", "selected", "req", "jwt"]),
|
||||
...mapGetters(["selectedCount"]),
|
||||
...mapState(useAuthStore, ["user", "jwt"]),
|
||||
...mapState(useFileStore, ["req", "selectedCount", "multiple"]),
|
||||
...mapWritableState(useFileStore, ["reload", "selected"]),
|
||||
singleClick() {
|
||||
return this.readOnly == undefined && this.user.singleClick;
|
||||
},
|
||||
@ -96,7 +104,8 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["addSelected", "removeSelected", "resetSelected"]),
|
||||
...mapActions(useFileStore, ["removeSelected"]),
|
||||
...mapActions(useLayoutStore, ["showHover", "closeHovers"]),
|
||||
humanSize: function () {
|
||||
return this.type == "invalid_link" ? "invalid link" : filesize(this.size);
|
||||
},
|
||||
@ -108,13 +117,13 @@ export default {
|
||||
},
|
||||
dragStart: function () {
|
||||
if (this.selectedCount === 0) {
|
||||
this.addSelected(this.index);
|
||||
this.selected.push(this.index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isSelected) {
|
||||
this.resetSelected();
|
||||
this.addSelected(this.index);
|
||||
this.selected = [];
|
||||
this.selected.push(this.index);
|
||||
}
|
||||
},
|
||||
dragOver: function (event) {
|
||||
@ -162,7 +171,7 @@ export default {
|
||||
api
|
||||
.move(items, overwrite, rename)
|
||||
.then(() => {
|
||||
this.$store.commit("setReload", true);
|
||||
this.reload = true;
|
||||
})
|
||||
.catch(this.$showError);
|
||||
};
|
||||
@ -173,14 +182,14 @@ export default {
|
||||
let rename = false;
|
||||
|
||||
if (conflict) {
|
||||
this.$store.commit("showHover", {
|
||||
this.showHover({
|
||||
prompt: "replace-rename",
|
||||
confirm: (event, option) => {
|
||||
overwrite = option == "overwrite";
|
||||
rename = option == "rename";
|
||||
|
||||
event.preventDefault();
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
action(overwrite, rename);
|
||||
},
|
||||
});
|
||||
@ -191,7 +200,7 @@ export default {
|
||||
action(overwrite, rename);
|
||||
},
|
||||
itemClick: function (event) {
|
||||
if (this.singleClick && !this.$store.state.multiple) this.open();
|
||||
if (this.singleClick && !this.multiple) this.open();
|
||||
else this.click(event);
|
||||
},
|
||||
click: function (event) {
|
||||
@ -206,7 +215,7 @@ export default {
|
||||
this.open();
|
||||
}
|
||||
|
||||
if (this.$store.state.selected.indexOf(this.index) !== -1) {
|
||||
if (this.selected.indexOf(this.index) !== -1) {
|
||||
this.removeSelected(this.index);
|
||||
return;
|
||||
}
|
||||
@ -224,8 +233,8 @@ export default {
|
||||
}
|
||||
|
||||
for (; fi <= la; fi++) {
|
||||
if (this.$store.state.selected.indexOf(fi) == -1) {
|
||||
this.addSelected(fi);
|
||||
if (this.selected.indexOf(fi) == -1) {
|
||||
this.selected.push(fi);
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,10 +245,11 @@ export default {
|
||||
!this.singleClick &&
|
||||
!event.ctrlKey &&
|
||||
!event.metaKey &&
|
||||
!this.$store.state.multiple
|
||||
)
|
||||
this.resetSelected();
|
||||
this.addSelected(this.index);
|
||||
!this.multiple
|
||||
) {
|
||||
this.selected = [];
|
||||
}
|
||||
this.selected.push(this.index);
|
||||
},
|
||||
open: function () {
|
||||
this.$router.push({ path: this.url });
|
||||
@ -247,3 +257,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/auth@/stores/file@/stores/layout
|
||||
|
||||
@ -7,13 +7,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions } from "pinia";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
export default {
|
||||
name: "action",
|
||||
props: ["icon", "label", "counter", "show"],
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["showHover"]),
|
||||
action: function () {
|
||||
if (this.show) {
|
||||
this.$store.commit("showHover", this.show);
|
||||
this.showHover(this.show);
|
||||
}
|
||||
|
||||
this.$emit("action");
|
||||
@ -23,3 +27,4 @@ export default {
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@/stores/layout
|
||||
|
||||
@ -6,12 +6,12 @@
|
||||
class="menu-button"
|
||||
icon="menu"
|
||||
:label="$t('buttons.toggleSidebar')"
|
||||
@action="openSidebar()"
|
||||
@action="showHover('sidebar')"
|
||||
/>
|
||||
|
||||
<slot />
|
||||
|
||||
<div id="dropdown" :class="{ active: this.$store.state.show === 'more' }">
|
||||
<div id="dropdown" :class="{ active: this.show === 'more' }">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
|
||||
@ -20,18 +20,17 @@
|
||||
id="more"
|
||||
icon="more_vert"
|
||||
:label="$t('buttons.more')"
|
||||
@action="$store.commit('showHover', 'more')"
|
||||
@action="showHover('more')"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="overlay"
|
||||
v-show="this.$store.state.show == 'more'"
|
||||
@click="$store.commit('closeHovers')"
|
||||
/>
|
||||
<div class="overlay" v-show="this.show == 'more'" @click="closeHovers" />
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import { logoURL } from "@/utils/constants";
|
||||
|
||||
import Action from "@/components/header/Action.vue";
|
||||
@ -47,12 +46,14 @@ export default {
|
||||
logoURL,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(useLayoutStore, ["show"]),
|
||||
},
|
||||
methods: {
|
||||
openSidebar() {
|
||||
this.$store.commit("showHover", "sidebar");
|
||||
},
|
||||
...mapActions(useLayoutStore, ["showHover", "closeHovers"]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@/stores/layout
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
<div class="card-action">
|
||||
<button
|
||||
class="button button--flat button--grey"
|
||||
@click="$store.commit('closeHovers')"
|
||||
@click="closeHovers"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')"
|
||||
>
|
||||
@ -31,11 +31,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import { mapActions, mapState, mapWritableState } from "pinia";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import FileList from "./FileList.vue";
|
||||
import { files as api } from "@/api";
|
||||
import buttons from "@/utils/buttons";
|
||||
import * as upload from "@/utils/upload";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
export default {
|
||||
name: "copy",
|
||||
@ -46,8 +48,12 @@ export default {
|
||||
dest: null,
|
||||
};
|
||||
},
|
||||
computed: mapState(["req", "selected"]),
|
||||
computed: {
|
||||
...mapState(useFileStore, ["req", "selected"]),
|
||||
...mapWritableState(useFileStore, ["reload"]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["showHover", "closeHovers"]),
|
||||
copy: async function (event) {
|
||||
event.preventDefault();
|
||||
let items = [];
|
||||
@ -70,7 +76,7 @@ export default {
|
||||
buttons.success("copy");
|
||||
|
||||
if (this.$route.path === this.dest) {
|
||||
this.$store.commit("setReload", true);
|
||||
this.reload = true;
|
||||
|
||||
return;
|
||||
}
|
||||
@ -84,7 +90,7 @@ export default {
|
||||
};
|
||||
|
||||
if (this.$route.path === this.dest) {
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
action(false, true);
|
||||
|
||||
return;
|
||||
@ -97,14 +103,14 @@ export default {
|
||||
let rename = false;
|
||||
|
||||
if (conflict) {
|
||||
this.$store.commit("showHover", {
|
||||
this.showHover({
|
||||
prompt: "replace-rename",
|
||||
confirm: (event, option) => {
|
||||
overwrite = option == "overwrite";
|
||||
rename = option == "rename";
|
||||
|
||||
event.preventDefault();
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
action(overwrite, rename);
|
||||
},
|
||||
});
|
||||
@ -117,3 +123,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/file@/stores/layout
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<button
|
||||
@click="$store.commit('closeHovers')"
|
||||
@click="closeHovers"
|
||||
class="button button--flat button--grey"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')"
|
||||
@ -30,18 +30,26 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapMutations, mapState } from "vuex";
|
||||
import { mapActions, mapState, mapWritableState } from "pinia";
|
||||
import { files as api } from "@/api";
|
||||
import buttons from "@/utils/buttons";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
export default {
|
||||
name: "delete",
|
||||
computed: {
|
||||
...mapGetters(["isListing", "selectedCount"]),
|
||||
...mapState(["req", "selected", "showConfirm"]),
|
||||
...mapState(useFileStore, [
|
||||
"isListing",
|
||||
"selectedCount",
|
||||
"req",
|
||||
"selected",
|
||||
]),
|
||||
...mapWritableState(useFileStore, ["reload"]),
|
||||
...mapState(useLayoutStore, ["showConfirm"]),
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["closeHovers"]),
|
||||
...mapActions(useLayoutStore, ["closeHovers"]),
|
||||
submit: async function () {
|
||||
buttons.loading("delete");
|
||||
|
||||
@ -68,13 +76,14 @@ export default {
|
||||
|
||||
await Promise.all(promises);
|
||||
buttons.success("delete");
|
||||
this.$store.commit("setReload", true);
|
||||
this.reload = true;
|
||||
} catch (e) {
|
||||
buttons.done("delete");
|
||||
this.$showError(e);
|
||||
if (this.isListing) this.$store.commit("setReload", true);
|
||||
if (this.isListing) this.reload = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/file@/stores/layout
|
||||
|
||||
@ -21,7 +21,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import { mapState } from "pinia";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
export default {
|
||||
name: "download",
|
||||
@ -38,6 +39,7 @@ export default {
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: mapState(["showConfirm"]),
|
||||
computed: mapState(useLayoutStore, ["showConfirm"]),
|
||||
};
|
||||
</script>
|
||||
@/stores/layout
|
||||
|
||||
@ -25,7 +25,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import { mapState } from "pinia";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
|
||||
import url from "@/utils/url";
|
||||
import { files } from "@/api";
|
||||
|
||||
@ -43,7 +46,8 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["req", "user"]),
|
||||
...mapState(useAuthStore, ["user"]),
|
||||
...mapState(useFileStore, ["req"]),
|
||||
nav() {
|
||||
return decodeURIComponent(this.current);
|
||||
},
|
||||
@ -136,3 +140,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/auth@/stores/file
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
<div class="card-action">
|
||||
<button
|
||||
type="submit"
|
||||
@click="$store.commit('closeHovers')"
|
||||
@click="closeHovers"
|
||||
class="button button--flat"
|
||||
:aria-label="$t('buttons.ok')"
|
||||
:title="$t('buttons.ok')"
|
||||
@ -33,5 +33,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default { name: "help" };
|
||||
import { mapActions } from "pinia";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
export default {
|
||||
name: "help",
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["closeHovers"]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/layout
|
||||
|
||||
@ -68,7 +68,7 @@
|
||||
<div class="card-action">
|
||||
<button
|
||||
type="submit"
|
||||
@click="$store.commit('closeHovers')"
|
||||
@click="closeHovers"
|
||||
class="button button--flat"
|
||||
:aria-label="$t('buttons.ok')"
|
||||
:title="$t('buttons.ok')"
|
||||
@ -80,7 +80,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import { filesize } from "filesize";
|
||||
import moment from "moment";
|
||||
import { files as api } from "@/api";
|
||||
@ -88,8 +90,12 @@ import { files as api } from "@/api";
|
||||
export default {
|
||||
name: "info",
|
||||
computed: {
|
||||
...mapState(["req", "selected"]),
|
||||
...mapGetters(["selectedCount", "isListing"]),
|
||||
...mapState(useFileStore, [
|
||||
"req",
|
||||
"selected",
|
||||
"selectedCount",
|
||||
"isListing",
|
||||
]),
|
||||
humanSize: function () {
|
||||
if (this.selectedCount === 0 || !this.isListing) {
|
||||
return filesize(this.req.size);
|
||||
@ -128,6 +134,7 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["closeHovers"]),
|
||||
checksum: async function (event, algo) {
|
||||
event.preventDefault();
|
||||
|
||||
@ -142,7 +149,7 @@ export default {
|
||||
try {
|
||||
const hash = await api.checksum(link, algo);
|
||||
// eslint-disable-next-line
|
||||
event.target.innerHTML = hash
|
||||
event.target.innerHTML = hash;
|
||||
} catch (e) {
|
||||
this.$showError(e);
|
||||
}
|
||||
@ -150,3 +157,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/file@/stores/layout
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
<div class="card-action">
|
||||
<button
|
||||
class="button button--flat button--grey"
|
||||
@click="$store.commit('closeHovers')"
|
||||
@click="closeHovers"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')"
|
||||
>
|
||||
@ -31,11 +31,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import FileList from "./FileList.vue";
|
||||
import { files as api } from "@/api";
|
||||
import buttons from "@/utils/buttons";
|
||||
import * as upload from "@/utils/upload";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
export default {
|
||||
name: "move",
|
||||
@ -46,8 +48,9 @@ export default {
|
||||
dest: null,
|
||||
};
|
||||
},
|
||||
computed: mapState(["req", "selected"]),
|
||||
computed: mapState(useFileStore, ["req", "selected"]),
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["showHover", "closeHovers"]),
|
||||
move: async function (event) {
|
||||
event.preventDefault();
|
||||
let items = [];
|
||||
@ -82,14 +85,14 @@ export default {
|
||||
let rename = false;
|
||||
|
||||
if (conflict) {
|
||||
this.$store.commit("showHover", {
|
||||
this.showHover({
|
||||
prompt: "replace-rename",
|
||||
confirm: (event, option) => {
|
||||
overwrite = option == "overwrite";
|
||||
rename = option == "rename";
|
||||
|
||||
event.preventDefault();
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
action(overwrite, rename);
|
||||
},
|
||||
});
|
||||
@ -102,3 +105,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/file@/stores/layout
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<div class="card-action">
|
||||
<button
|
||||
class="button button--flat button--grey"
|
||||
@click="$store.commit('closeHovers')"
|
||||
@click="closeHovers"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')"
|
||||
>
|
||||
@ -37,7 +37,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import { files as api } from "@/api";
|
||||
import url from "@/utils/url";
|
||||
|
||||
@ -49,9 +52,10 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["isFiles", "isListing"]),
|
||||
...mapState(useFileStore, ["isFiles", "isListing"]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["closeHovers"]),
|
||||
submit: async function (event) {
|
||||
event.preventDefault();
|
||||
if (this.new === "") return;
|
||||
@ -73,8 +77,9 @@ export default {
|
||||
this.$showError(e);
|
||||
}
|
||||
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/file@/stores/layout
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<div class="card-action">
|
||||
<button
|
||||
class="button button--flat button--grey"
|
||||
@click="$store.commit('closeHovers')"
|
||||
@click="closeHovers"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')"
|
||||
>
|
||||
@ -37,7 +37,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import { files as api } from "@/api";
|
||||
import url from "@/utils/url";
|
||||
|
||||
@ -49,9 +52,10 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["isFiles", "isListing"]),
|
||||
...mapState(useFileStore, ["isFiles", "isListing"]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["closeHovers"]),
|
||||
submit: async function (event) {
|
||||
event.preventDefault();
|
||||
if (this.new === "") return;
|
||||
@ -73,8 +77,9 @@ export default {
|
||||
this.$showError(e);
|
||||
}
|
||||
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/file@/stores/layout
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<component ref="currentComponent" :is="currentComponent"></component>
|
||||
<div v-show="showOverlay" @click="resetPrompts" class="overlay"></div>
|
||||
<div v-show="showOverlay" @click="closeHovers" class="overlay"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import Help from "./Help.vue";
|
||||
import Info from "./Info.vue";
|
||||
import Delete from "./Delete.vue";
|
||||
@ -20,7 +23,6 @@ import ReplaceRename from "./ReplaceRename.vue";
|
||||
import Share from "./Share.vue";
|
||||
import Upload from "./Upload.vue";
|
||||
import ShareDelete from "./ShareDelete.vue";
|
||||
import { mapState } from "vuex";
|
||||
import buttons from "@/utils/buttons";
|
||||
|
||||
export default {
|
||||
@ -45,8 +47,6 @@ export default {
|
||||
return {
|
||||
pluginData: {
|
||||
buttons,
|
||||
store: this.$store,
|
||||
router: this.$router,
|
||||
},
|
||||
};
|
||||
},
|
||||
@ -59,7 +59,7 @@ export default {
|
||||
// Esc!
|
||||
if (event.keyCode === 27) {
|
||||
event.stopImmediatePropagation();
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
}
|
||||
|
||||
// Enter
|
||||
@ -82,7 +82,7 @@ export default {
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
...mapState(["show", "plugins"]),
|
||||
...mapState(useLayoutStore, ["show", "showConfirm"]),
|
||||
currentComponent: function () {
|
||||
const matched =
|
||||
[
|
||||
@ -111,9 +111,8 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
resetPrompts() {
|
||||
this.$store.commit("closeHovers");
|
||||
},
|
||||
...mapActions(useLayoutStore, ["closeHovers"]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/layout
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
<div class="card-action">
|
||||
<button
|
||||
class="button button--flat button--grey"
|
||||
@click="$store.commit('closeHovers')"
|
||||
@click="closeHovers"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')"
|
||||
>
|
||||
@ -41,7 +41,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import { mapActions, mapState, mapWritableState } from "pinia";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import url from "@/utils/url";
|
||||
import { files as api } from "@/api";
|
||||
|
||||
@ -56,12 +58,18 @@ export default {
|
||||
this.name = this.oldName();
|
||||
},
|
||||
computed: {
|
||||
...mapState(["req", "selected", "selectedCount"]),
|
||||
...mapGetters(["isListing"]),
|
||||
...mapState(useFileStore, [
|
||||
"req",
|
||||
"selected",
|
||||
"selectedCount",
|
||||
"isListing",
|
||||
]),
|
||||
...mapWritableState(useFileStore, ["reload"]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["closeHovers"]),
|
||||
cancel: function () {
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
},
|
||||
oldName: function () {
|
||||
if (!this.isListing) {
|
||||
@ -95,13 +103,14 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.commit("setReload", true);
|
||||
this.reload = true;
|
||||
} catch (e) {
|
||||
this.$showError(e);
|
||||
}
|
||||
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/file@/stores/layout
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
<div class="card-action">
|
||||
<button
|
||||
class="button button--flat button--grey"
|
||||
@click="$store.commit('closeHovers')"
|
||||
@click="closeHovers"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')"
|
||||
>
|
||||
@ -38,10 +38,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
export default {
|
||||
name: "replace",
|
||||
computed: mapState(["showConfirm", "showAction"]),
|
||||
computed: {
|
||||
...mapState(useLayoutStore, ["showConfirm", "showAction"]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["closeHovers"]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/layout
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
<div class="card-action">
|
||||
<button
|
||||
class="button button--flat button--grey"
|
||||
@click="$store.commit('closeHovers')"
|
||||
@click="closeHovers"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')"
|
||||
>
|
||||
@ -38,10 +38,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
export default {
|
||||
name: "replace-rename",
|
||||
computed: mapState(["showConfirm"]),
|
||||
computed: {
|
||||
...mapState(useLayoutStore, ["showConfirm"]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["closeHovers"]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/layout
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
<div class="card-action">
|
||||
<button
|
||||
class="button button--flat button--grey"
|
||||
@click="$store.commit('closeHovers')"
|
||||
@click="closeHovers"
|
||||
:aria-label="$t('buttons.close')"
|
||||
:title="$t('buttons.close')"
|
||||
>
|
||||
@ -126,10 +126,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { share as api, pub as pub_api } from "@/api";
|
||||
import moment from "moment";
|
||||
import Clipboard from "clipboard";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
export default {
|
||||
name: "share",
|
||||
@ -144,8 +146,12 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["req", "selected", "selectedCount"]),
|
||||
...mapGetters(["isListing"]),
|
||||
...mapState(useFileStore, [
|
||||
"req",
|
||||
"selected",
|
||||
"selectedCount",
|
||||
"isListing",
|
||||
]),
|
||||
url() {
|
||||
if (!this.isListing) {
|
||||
return this.$route.path;
|
||||
@ -178,10 +184,11 @@ export default {
|
||||
this.$showSuccess(this.$t("success.linkCopied"));
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
this.clip.destroy();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["closeHovers"]),
|
||||
submit: async function () {
|
||||
let isPermanent = !this.time || this.time == 0;
|
||||
|
||||
@ -242,7 +249,7 @@ export default {
|
||||
},
|
||||
switchListing() {
|
||||
if (this.links.length == 0 && !this.listing) {
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
}
|
||||
|
||||
this.listing = !this.listing;
|
||||
@ -250,3 +257,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/file@/stores/layout
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<button
|
||||
@click="$store.commit('closeHovers')"
|
||||
@click="closeHovers"
|
||||
class="button button--flat button--grey"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')"
|
||||
@ -25,17 +25,20 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
export default {
|
||||
name: "share-delete",
|
||||
computed: {
|
||||
...mapState(["showConfirm"]),
|
||||
...mapState(useLayoutStore, ["showConfirm"]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["closeHovers"]),
|
||||
submit: function () {
|
||||
this.showConfirm();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/layout
|
||||
|
||||
@ -42,7 +42,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
import { mapState } from "pinia";
|
||||
import { useUploadStore } from "@/stores/upload";
|
||||
|
||||
export default {
|
||||
name: "uploadFiles",
|
||||
@ -52,7 +53,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["filesInUpload", "filesInUploadCount"]),
|
||||
...mapState(useUploadStore, ["filesInUpload", "filesInUploadCount"]),
|
||||
},
|
||||
methods: {
|
||||
toggle: function () {
|
||||
@ -61,3 +62,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/upload
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
<languages
|
||||
class="input input--block"
|
||||
id="locale"
|
||||
:locale.sync="user.locale"
|
||||
v-model:locale="user.locale"
|
||||
></languages>
|
||||
</p>
|
||||
|
||||
@ -55,13 +55,13 @@
|
||||
{{ $t("settings.lockPassword") }}
|
||||
</p>
|
||||
|
||||
<permissions :perm.sync="user.perm" />
|
||||
<commands v-if="isExecEnabled" :commands.sync="user.commands" />
|
||||
<permissions v-model:perm="user.perm" />
|
||||
<commands v-if="isExecEnabled" v-model:commands="user.commands" />
|
||||
|
||||
<div v-if="!isDefault">
|
||||
<h3>{{ $t("settings.rules") }}</h3>
|
||||
<p class="small">{{ $t("settings.rulesHelp") }}</p>
|
||||
<rules :rules.sync="user.rules" />
|
||||
<rules v-model:rules="user.rules" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -103,7 +103,7 @@ const removeEmpty = (obj) =>
|
||||
|
||||
export const rtlLanguages = ["he", "ar"];
|
||||
|
||||
const i18n = createI18n({
|
||||
export const i18n = createI18n({
|
||||
locale: detectLocale(),
|
||||
fallbackLocale: "en",
|
||||
messages: {
|
||||
|
||||
@ -1,51 +1,36 @@
|
||||
import "whatwg-fetch";
|
||||
import cssVars from "css-vars-ponyfill";
|
||||
import { createApp, configureCompat } from "vue";
|
||||
import store from "@/store";
|
||||
import { createApp } from "vue";
|
||||
import VueLazyload from "vue-lazyload";
|
||||
import createPinia from "@/stores";
|
||||
import router from "@/router";
|
||||
import i18n from "@/i18n";
|
||||
import { recaptcha, loginPage } from "@/utils/constants";
|
||||
import { login, validateLogin } from "@/utils/auth";
|
||||
import App from "@/App.vue";
|
||||
|
||||
cssVars();
|
||||
|
||||
configureCompat({
|
||||
MODE: 2,
|
||||
});
|
||||
const pinia = createPinia(router);
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(store);
|
||||
app.use(router);
|
||||
app.use(VueLazyload);
|
||||
app.use(i18n);
|
||||
app.use(pinia);
|
||||
app.use(router);
|
||||
|
||||
async function start() {
|
||||
try {
|
||||
if (loginPage) {
|
||||
await validateLogin();
|
||||
} else {
|
||||
await login("", "", "");
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
app.mixin({
|
||||
mounted() {
|
||||
// expose vue instance to components
|
||||
this.$el.__vue__ = this;
|
||||
},
|
||||
});
|
||||
|
||||
if (recaptcha) {
|
||||
await new Promise((resolve) => {
|
||||
const check = () => {
|
||||
if (typeof window.grecaptcha === "undefined") {
|
||||
setTimeout(check, 100);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
// provide v-focus for components
|
||||
app.directive("focus", {
|
||||
mounted: (el) => {
|
||||
// initiate focus for the element
|
||||
el.focus();
|
||||
},
|
||||
});
|
||||
|
||||
check();
|
||||
});
|
||||
}
|
||||
|
||||
router.isReady().then(() => app.mount("#app"));
|
||||
}
|
||||
|
||||
start();
|
||||
router.isReady().then(() => app.mount("#app"));
|
||||
|
||||
@ -10,9 +10,11 @@ import GlobalSettings from "@/views/settings/Global.vue";
|
||||
import ProfileSettings from "@/views/settings/Profile.vue";
|
||||
import Shares from "@/views/settings/Shares.vue";
|
||||
import Errors from "@/views/Errors.vue";
|
||||
import store from "@/store";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { baseURL, name } from "@/utils/constants";
|
||||
import i18n, { rtlLanguages } from "@/i18n";
|
||||
import { i18n, rtlLanguages } from "@/i18n";
|
||||
import { recaptcha, loginPage } from "@/utils/constants";
|
||||
import { login, validateLogin } from "@/utils/auth";
|
||||
|
||||
const titles = {
|
||||
Login: "sidebar.login",
|
||||
@ -34,20 +36,13 @@ const routes = [
|
||||
path: "/login",
|
||||
name: "Login",
|
||||
component: Login,
|
||||
beforeEnter: (to, from, next) => {
|
||||
if (store.getters.isLogged) {
|
||||
return next({ path: "/files" });
|
||||
}
|
||||
|
||||
next();
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/share",
|
||||
component: Layout,
|
||||
children: [
|
||||
{
|
||||
path: ":pathMatch(.*)*",
|
||||
path: ":path*",
|
||||
name: "Share",
|
||||
component: Share,
|
||||
},
|
||||
@ -56,20 +51,23 @@ const routes = [
|
||||
{
|
||||
path: "/files",
|
||||
component: Layout,
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: ":pathMatch(.*)*",
|
||||
path: ":path*",
|
||||
name: "Files",
|
||||
component: Files,
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/settings",
|
||||
component: Layout,
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
@ -78,9 +76,6 @@ const routes = [
|
||||
redirect: {
|
||||
path: "/settings/profile",
|
||||
},
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "profile",
|
||||
@ -159,21 +154,50 @@ const routes = [
|
||||
},
|
||||
];
|
||||
|
||||
async function start() {
|
||||
try {
|
||||
if (loginPage) {
|
||||
await validateLogin();
|
||||
} else {
|
||||
await login("", "", "");
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
if (recaptcha) {
|
||||
await new Promise((resolve) => {
|
||||
const check = () => {
|
||||
if (typeof window.grecaptcha === "undefined") {
|
||||
setTimeout(check, 100);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
|
||||
check();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(baseURL),
|
||||
routes,
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
// const title = i18n.t(titles[to.name]);
|
||||
const title = titles[to.name];
|
||||
router.beforeResolve(async (to, from, next) => {
|
||||
let title;
|
||||
try {
|
||||
title = i18n.global.t(titles[to.name]);
|
||||
} catch (error) {
|
||||
title = to.name;
|
||||
}
|
||||
// const title = titles[to.name];
|
||||
document.title = title + " - " + name;
|
||||
|
||||
console.log({ from, to });
|
||||
|
||||
/*** RTL related settings per route ****/
|
||||
const rtlSet = document.querySelector("body").classList.contains("rtl");
|
||||
const shouldSetRtl = rtlLanguages.includes(i18n.locale);
|
||||
const shouldSetRtl = rtlLanguages.includes(i18n.global.locale);
|
||||
switch (true) {
|
||||
case shouldSetRtl && !rtlSet:
|
||||
document.querySelector("body").classList.add("rtl");
|
||||
@ -183,8 +207,19 @@ router.beforeEach((to, from, next) => {
|
||||
break;
|
||||
}
|
||||
|
||||
const authStore = useAuthStore();
|
||||
|
||||
if (from.name == null) {
|
||||
await start();
|
||||
}
|
||||
|
||||
if (to.path.endsWith("/login") && authStore.isLoggedIn) {
|
||||
next({ path: "/files/" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (to.matched.some((record) => record.meta.requiresAuth)) {
|
||||
if (!store.getters.isLogged) {
|
||||
if (!authStore.isLoggedIn) {
|
||||
next({
|
||||
path: "/login",
|
||||
query: { redirect: to.fullPath },
|
||||
@ -194,7 +229,7 @@ router.beforeEach((to, from, next) => {
|
||||
}
|
||||
|
||||
if (to.matched.some((record) => record.meta.requiresAdmin)) {
|
||||
if (!store.state.user.perm.admin) {
|
||||
if (!authStore.user.perm.admin) {
|
||||
next({ path: "/403" });
|
||||
return;
|
||||
}
|
||||
@ -204,4 +239,4 @@ router.beforeEach((to, from, next) => {
|
||||
next();
|
||||
});
|
||||
|
||||
export default router;
|
||||
export { router, router as default };
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
const getters = {
|
||||
isLogged: (state) => state.user !== null,
|
||||
isFiles: (state) => !state.loading && state.route.name === "Files",
|
||||
isListing: (state, getters) => getters.isFiles && state.req.isDir,
|
||||
selectedCount: (state) => state.selected.length,
|
||||
progress: (state) => {
|
||||
if (state.upload.progress.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let totalSize = state.upload.sizes.reduce((a, b) => a + b, 0);
|
||||
|
||||
let sum = state.upload.progress.reduce((acc, val) => acc + val);
|
||||
return Math.ceil((sum / totalSize) * 100);
|
||||
},
|
||||
filesInUploadCount: (state) => {
|
||||
let total =
|
||||
Object.keys(state.upload.uploads).length + state.upload.queue.length;
|
||||
return total;
|
||||
},
|
||||
filesInUpload: (state) => {
|
||||
let files = [];
|
||||
|
||||
for (let index in state.upload.uploads) {
|
||||
let upload = state.upload.uploads[index];
|
||||
let id = upload.id;
|
||||
let type = upload.type;
|
||||
let name = upload.file.name;
|
||||
let size = state.upload.sizes[id];
|
||||
let isDir = upload.file.isDir;
|
||||
let progress = isDir
|
||||
? 100
|
||||
: Math.ceil((state.upload.progress[id] / size) * 100);
|
||||
|
||||
files.push({
|
||||
id,
|
||||
name,
|
||||
progress,
|
||||
type,
|
||||
isDir,
|
||||
});
|
||||
}
|
||||
|
||||
return files.sort((a, b) => a.progress - b.progress);
|
||||
},
|
||||
};
|
||||
|
||||
export default getters;
|
||||
@ -1,38 +0,0 @@
|
||||
import { createStore } from "vuex";
|
||||
import mutations from "./mutations";
|
||||
import getters from "./getters";
|
||||
import upload from "./modules/upload";
|
||||
import router from "@/router";
|
||||
|
||||
const state = {
|
||||
user: null,
|
||||
req: {},
|
||||
oldReq: {},
|
||||
clipboard: {
|
||||
key: "",
|
||||
items: [],
|
||||
},
|
||||
jwt: "",
|
||||
progress: 0,
|
||||
loading: false,
|
||||
reload: false,
|
||||
selected: [],
|
||||
multiple: false,
|
||||
show: null,
|
||||
showShell: false,
|
||||
showConfirm: null,
|
||||
showAction: null,
|
||||
get route() {
|
||||
return router.currentRoute.value;
|
||||
},
|
||||
};
|
||||
|
||||
const store = createStore({
|
||||
strict: true,
|
||||
state,
|
||||
getters,
|
||||
mutations,
|
||||
modules: { upload },
|
||||
});
|
||||
|
||||
export default store;
|
||||
@ -1,110 +0,0 @@
|
||||
import Vue from "vue";
|
||||
import { files as api } from "@/api";
|
||||
import throttle from "lodash.throttle";
|
||||
import buttons from "@/utils/buttons";
|
||||
|
||||
const UPLOADS_LIMIT = 5;
|
||||
|
||||
const state = {
|
||||
id: 0,
|
||||
sizes: [],
|
||||
progress: [],
|
||||
queue: [],
|
||||
uploads: {},
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
setProgress(state, { id, loaded }) {
|
||||
Vue.set(state.progress, id, loaded);
|
||||
},
|
||||
reset: (state) => {
|
||||
state.id = 0;
|
||||
state.sizes = [];
|
||||
state.progress = [];
|
||||
},
|
||||
addJob: (state, item) => {
|
||||
state.queue.push(item);
|
||||
state.sizes[state.id] = item.file.size;
|
||||
state.id++;
|
||||
},
|
||||
moveJob(state) {
|
||||
const item = state.queue[0];
|
||||
state.queue.shift();
|
||||
Vue.set(state.uploads, item.id, item);
|
||||
},
|
||||
removeJob(state, id) {
|
||||
Vue.delete(state.uploads, id);
|
||||
delete state.uploads[id];
|
||||
},
|
||||
};
|
||||
|
||||
const beforeUnload = (event) => {
|
||||
event.preventDefault();
|
||||
event.returnValue = "";
|
||||
};
|
||||
|
||||
const actions = {
|
||||
upload: (context, item) => {
|
||||
let uploadsCount = Object.keys(context.state.uploads).length;
|
||||
|
||||
let isQueueEmpty = context.state.queue.length == 0;
|
||||
let isUploadsEmpty = uploadsCount == 0;
|
||||
|
||||
if (isQueueEmpty && isUploadsEmpty) {
|
||||
window.addEventListener("beforeunload", beforeUnload);
|
||||
buttons.loading("upload");
|
||||
}
|
||||
|
||||
context.commit("addJob", item);
|
||||
context.dispatch("processUploads");
|
||||
},
|
||||
finishUpload: (context, item) => {
|
||||
context.commit("setProgress", { id: item.id, loaded: item.file.size });
|
||||
context.commit("removeJob", item.id);
|
||||
context.dispatch("processUploads");
|
||||
},
|
||||
processUploads: async (context) => {
|
||||
let uploadsCount = Object.keys(context.state.uploads).length;
|
||||
|
||||
let isBellowLimit = uploadsCount < UPLOADS_LIMIT;
|
||||
let isQueueEmpty = context.state.queue.length == 0;
|
||||
let isUploadsEmpty = uploadsCount == 0;
|
||||
|
||||
let isFinished = isQueueEmpty && isUploadsEmpty;
|
||||
let canProcess = isBellowLimit && !isQueueEmpty;
|
||||
|
||||
if (isFinished) {
|
||||
window.removeEventListener("beforeunload", beforeUnload);
|
||||
buttons.success("upload");
|
||||
context.commit("reset");
|
||||
context.commit("setReload", true, { root: true });
|
||||
}
|
||||
|
||||
if (canProcess) {
|
||||
const item = context.state.queue[0];
|
||||
context.commit("moveJob");
|
||||
|
||||
if (item.file.isDir) {
|
||||
await api.post(item.path).catch(Vue.prototype.$showError);
|
||||
} else {
|
||||
let onUpload = throttle(
|
||||
(event) =>
|
||||
context.commit("setProgress", {
|
||||
id: item.id,
|
||||
loaded: event.loaded,
|
||||
}),
|
||||
100,
|
||||
{ leading: true, trailing: false }
|
||||
);
|
||||
|
||||
await api
|
||||
.post(item.path, item.file, item.overwrite, onUpload)
|
||||
.catch(Vue.prototype.$showError);
|
||||
}
|
||||
|
||||
context.dispatch("finishUpload", item);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default { state, mutations, actions, namespaced: true };
|
||||
@ -1,91 +0,0 @@
|
||||
import * as i18n from "@/i18n";
|
||||
import moment from "moment";
|
||||
|
||||
const mutations = {
|
||||
closeHovers: (state) => {
|
||||
state.show = null;
|
||||
state.showConfirm = null;
|
||||
state.showAction = null;
|
||||
},
|
||||
toggleShell: (state) => {
|
||||
state.showShell = !state.showShell;
|
||||
},
|
||||
showHover: (state, value) => {
|
||||
if (typeof value !== "object") {
|
||||
state.show = value;
|
||||
return;
|
||||
}
|
||||
|
||||
state.show = value.prompt;
|
||||
state.showConfirm = value.confirm;
|
||||
if (value.action !== undefined) {
|
||||
state.showAction = value.action;
|
||||
}
|
||||
},
|
||||
showError: (state) => {
|
||||
state.show = "error";
|
||||
},
|
||||
showSuccess: (state) => {
|
||||
state.show = "success";
|
||||
},
|
||||
setLoading: (state, value) => {
|
||||
state.loading = value;
|
||||
},
|
||||
setReload: (state, value) => {
|
||||
state.reload = value;
|
||||
},
|
||||
setUser: (state, value) => {
|
||||
if (value === null) {
|
||||
state.user = null;
|
||||
return;
|
||||
}
|
||||
|
||||
let locale = value.locale;
|
||||
|
||||
if (locale === "") {
|
||||
locale = i18n.detectLocale();
|
||||
}
|
||||
|
||||
moment.locale(locale);
|
||||
i18n.default.locale = locale;
|
||||
state.user = value;
|
||||
},
|
||||
setJWT: (state, value) => (state.jwt = value),
|
||||
multiple: (state, value) => (state.multiple = value),
|
||||
addSelected: (state, value) => state.selected.push(value),
|
||||
removeSelected: (state, value) => {
|
||||
let i = state.selected.indexOf(value);
|
||||
if (i === -1) return;
|
||||
state.selected.splice(i, 1);
|
||||
},
|
||||
resetSelected: (state) => {
|
||||
state.selected = [];
|
||||
},
|
||||
updateUser: (state, value) => {
|
||||
if (typeof value !== "object") return;
|
||||
|
||||
for (let field in value) {
|
||||
if (field === "locale") {
|
||||
moment.locale(value[field]);
|
||||
i18n.default.locale = value[field];
|
||||
}
|
||||
|
||||
state.user[field] = value[field];
|
||||
}
|
||||
},
|
||||
updateRequest: (state, value) => {
|
||||
state.oldReq = state.req;
|
||||
state.req = value;
|
||||
},
|
||||
updateClipboard: (state, value) => {
|
||||
state.clipboard.key = value.key;
|
||||
state.clipboard.items = value.items;
|
||||
state.clipboard.path = value.path;
|
||||
},
|
||||
resetClipboard: (state) => {
|
||||
state.clipboard.key = "";
|
||||
state.clipboard.items = [];
|
||||
},
|
||||
};
|
||||
|
||||
export default mutations;
|
||||
45
frontend/src/stores/auth.js
Normal file
45
frontend/src/stores/auth.js
Normal file
@ -0,0 +1,45 @@
|
||||
import { defineStore } from "pinia";
|
||||
// import { useAuthPreferencesStore } from "./auth-preferences";
|
||||
// import { useAuthEmailStore } from "./auth-email";
|
||||
|
||||
export const useAuthStore = defineStore("auth", {
|
||||
// convert to a function
|
||||
state: () => ({
|
||||
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(value) {
|
||||
if (value === null) {
|
||||
this.user = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// const locale = value.locale || i18n.detectLocale();
|
||||
// moment.locale(locale);
|
||||
// i18n.default.locale = locale;
|
||||
this.user = value;
|
||||
},
|
||||
updateUser(value) {
|
||||
if (typeof value !== "object") return;
|
||||
|
||||
for (let field in value) {
|
||||
// if (field === "locale") {
|
||||
// moment.locale(value[field]);
|
||||
// i18n.default.locale = value[field];
|
||||
// }
|
||||
|
||||
this.user[field] = structuredClone(value[field]);
|
||||
}
|
||||
},
|
||||
// easily reset state using `$reset`
|
||||
clearUser() {
|
||||
this.$reset();
|
||||
},
|
||||
},
|
||||
});
|
||||
25
frontend/src/stores/clipboard.js
Normal file
25
frontend/src/stores/clipboard.js
Normal file
@ -0,0 +1,25 @@
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export const useClipboardStore = defineStore("clipboard", {
|
||||
// convert to a function
|
||||
state: () => ({
|
||||
key: "",
|
||||
items: [],
|
||||
path: undefined,
|
||||
}),
|
||||
getters: {
|
||||
// user and jwt getter removed, no longer needed
|
||||
},
|
||||
actions: {
|
||||
// no context as first argument, use `this` instead
|
||||
updateClipboard(value) {
|
||||
this.key = value.key;
|
||||
this.items = value.items;
|
||||
this.path = value.path;
|
||||
},
|
||||
// easily reset state using `$reset`
|
||||
resetClipboard() {
|
||||
this.$reset();
|
||||
},
|
||||
},
|
||||
});
|
||||
48
frontend/src/stores/file.js
Normal file
48
frontend/src/stores/file.js
Normal file
@ -0,0 +1,48 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { useRouterStore } from "./router";
|
||||
// import { useAuthPreferencesStore } from "./auth-preferences";
|
||||
// import { useAuthEmailStore } from "./auth-email";
|
||||
|
||||
export const useFileStore = defineStore("file", {
|
||||
// convert to a function
|
||||
state: () => ({
|
||||
req: {},
|
||||
oldReq: {},
|
||||
reload: false,
|
||||
selected: [],
|
||||
multiple: false,
|
||||
}),
|
||||
getters: {
|
||||
// user and jwt getter removed, no longer needed
|
||||
selectedCount: (state) => state.selected.length,
|
||||
route: () => {
|
||||
const routerStore = useRouterStore();
|
||||
return routerStore.router.currentRoute;
|
||||
},
|
||||
isFiles(state) {
|
||||
return !state.loading && this.route.name === "Files";
|
||||
},
|
||||
isListing(state) {
|
||||
return this.isFiles && state.req.isDir;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// no context as first argument, use `this` instead
|
||||
toggleMultiple() {
|
||||
this.multiple = !this.multiple;
|
||||
},
|
||||
updateRequest(value) {
|
||||
this.oldReq = this.req;
|
||||
this.req = value;
|
||||
},
|
||||
removeSelected(value) {
|
||||
let i = this.selected.indexOf(value);
|
||||
if (i === -1) return;
|
||||
this.selected.splice(i, 1);
|
||||
},
|
||||
// easily reset state using `$reset`
|
||||
clearFile() {
|
||||
this.$reset();
|
||||
},
|
||||
},
|
||||
});
|
||||
11
frontend/src/stores/index.js
Normal file
11
frontend/src/stores/index.js
Normal file
@ -0,0 +1,11 @@
|
||||
import { createPinia as _createPinia } from "pinia";
|
||||
import { markRaw } from "vue";
|
||||
|
||||
export default function createPinia(router) {
|
||||
const pinia = _createPinia();
|
||||
pinia.use(({ store }) => {
|
||||
store.router = markRaw(router);
|
||||
});
|
||||
|
||||
return pinia;
|
||||
}
|
||||
50
frontend/src/stores/layout.js
Normal file
50
frontend/src/stores/layout.js
Normal file
@ -0,0 +1,50 @@
|
||||
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: false,
|
||||
show: null,
|
||||
showConfirm: null,
|
||||
showAction: null,
|
||||
showShell: false,
|
||||
}),
|
||||
getters: {
|
||||
// user and jwt getter removed, no longer needed
|
||||
},
|
||||
actions: {
|
||||
// no context as first argument, use `this` instead
|
||||
toggleShell() {
|
||||
this.showShell = !this.showShell;
|
||||
},
|
||||
showHover(value) {
|
||||
if (typeof value !== "object") {
|
||||
this.show = value;
|
||||
return;
|
||||
}
|
||||
|
||||
this.show = value.prompt;
|
||||
this.showConfirm = value.confirm;
|
||||
if (value.action !== undefined) {
|
||||
this.showAction = value.action;
|
||||
}
|
||||
},
|
||||
showError() {
|
||||
this.show = "error";
|
||||
},
|
||||
showSuccess() {
|
||||
this.show = "success";
|
||||
},
|
||||
closeHovers() {
|
||||
this.show = null;
|
||||
this.showConfirm = null;
|
||||
this.showAction = null;
|
||||
},
|
||||
// easily reset state using `$reset`
|
||||
clearLayout() {
|
||||
this.$reset();
|
||||
},
|
||||
},
|
||||
});
|
||||
6
frontend/src/stores/router.js
Normal file
6
frontend/src/stores/router.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export const useRouterStore = defineStore("routerStore", () => {
|
||||
// can be an empty definition
|
||||
return {};
|
||||
});
|
||||
157
frontend/src/stores/upload.js
Normal file
157
frontend/src/stores/upload.js
Normal file
@ -0,0 +1,157 @@
|
||||
import Vue from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import { useFileStore } from "./file";
|
||||
import { files as api } from "@/api";
|
||||
import throttle from "lodash.throttle";
|
||||
import buttons from "@/utils/buttons";
|
||||
|
||||
const UPLOADS_LIMIT = 5;
|
||||
|
||||
const beforeUnload = (event) => {
|
||||
event.preventDefault();
|
||||
event.returnValue = "";
|
||||
};
|
||||
|
||||
export const useUploadStore = defineStore("upload", {
|
||||
// convert to a function
|
||||
state: () => ({
|
||||
id: 0,
|
||||
sizes: [],
|
||||
progress: [],
|
||||
queue: [],
|
||||
uploads: {},
|
||||
}),
|
||||
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);
|
||||
|
||||
const sum = state.progress.reduce((acc, val) => acc + val);
|
||||
return Math.ceil((sum / totalSize) * 100);
|
||||
},
|
||||
filesInUploadCount: (state) => {
|
||||
const total = Object.keys(state.uploads).length + state.queue.length;
|
||||
return total;
|
||||
},
|
||||
filesInUpload: (state) => {
|
||||
const files = [];
|
||||
|
||||
for (let 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] / size) * 100);
|
||||
|
||||
files.push({
|
||||
id,
|
||||
name,
|
||||
progress,
|
||||
type,
|
||||
isDir,
|
||||
});
|
||||
}
|
||||
|
||||
return files.sort((a, b) => a.progress - b.progress);
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// no context as first argument, use `this` instead
|
||||
setProgress({ id, loaded }) {
|
||||
Vue.set(this.progress, id, loaded);
|
||||
},
|
||||
reset() {
|
||||
this.id = 0;
|
||||
this.sizes = [];
|
||||
this.progress = [];
|
||||
},
|
||||
addJob(item) {
|
||||
this.queue.push(item);
|
||||
this.sizes[this.id] = item.file.size;
|
||||
this.id++;
|
||||
},
|
||||
moveJob() {
|
||||
const item = this.queue[0];
|
||||
this.queue.shift();
|
||||
Vue.set(this.uploads, item.id, item);
|
||||
},
|
||||
removeJob(id) {
|
||||
Vue.delete(this.uploads, id);
|
||||
delete this.uploads[id];
|
||||
},
|
||||
upload(item) {
|
||||
let uploadsCount = Object.keys(this.uploads).length;
|
||||
|
||||
let isQueueEmpty = this.queue.length == 0;
|
||||
let isUploadsEmpty = uploadsCount == 0;
|
||||
|
||||
if (isQueueEmpty && isUploadsEmpty) {
|
||||
window.addEventListener("beforeunload", beforeUnload);
|
||||
buttons.loading("upload");
|
||||
}
|
||||
|
||||
this.addJob(item);
|
||||
this.processUploads();
|
||||
},
|
||||
finishUpload(item) {
|
||||
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 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(Vue.prototype.$showError);
|
||||
} else {
|
||||
let onUpload = throttle(
|
||||
(event) =>
|
||||
this.setProgress({
|
||||
id: item.id,
|
||||
loaded: event.loaded,
|
||||
}),
|
||||
100,
|
||||
{ leading: true, trailing: false }
|
||||
);
|
||||
|
||||
await api
|
||||
.post(item.path, item.file, item.overwrite, onUpload)
|
||||
.catch(Vue.prototype.$showError);
|
||||
}
|
||||
|
||||
this.finishUpload(item);
|
||||
}
|
||||
},
|
||||
// easily reset state using `$reset`
|
||||
clearUpload() {
|
||||
this.$reset();
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -1,22 +1,20 @@
|
||||
import store from "@/store";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import router from "@/router";
|
||||
import { Base64 } from "js-base64";
|
||||
import jwt_decode from "jwt-decode";
|
||||
import { baseURL } from "@/utils/constants";
|
||||
|
||||
export function parseToken(token) {
|
||||
const parts = token.split(".");
|
||||
// falsy or malformed jwt will throw InvalidTokenError
|
||||
const data = jwt_decode(token);
|
||||
console.log(data);
|
||||
|
||||
if (parts.length !== 3) {
|
||||
throw new Error("token malformed");
|
||||
}
|
||||
|
||||
const data = JSON.parse(Base64.decode(parts[1]));
|
||||
|
||||
document.cookie = `auth=${token}; path=/`;
|
||||
document.cookie = `auth=${token}; Path=/; SameSite=Strict;`;
|
||||
|
||||
localStorage.setItem("jwt", token);
|
||||
store.commit("setJWT", token);
|
||||
store.commit("setUser", data.user);
|
||||
|
||||
const authStore = useAuthStore();
|
||||
authStore.jwt = token;
|
||||
authStore.setUser(data.user);
|
||||
}
|
||||
|
||||
export async function validateLogin() {
|
||||
@ -25,7 +23,7 @@ export async function validateLogin() {
|
||||
await renew(localStorage.getItem("jwt"));
|
||||
}
|
||||
} catch (_) {
|
||||
console.warn('Invalid JWT token in storage') // eslint-disable-line
|
||||
console.warn("Invalid JWT token in storage"); // eslint-disable-line
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,10 +81,11 @@ export async function signup(username, password) {
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
document.cookie = "auth=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/";
|
||||
document.cookie = "auth=; Max-Age=0; Path=/; SameSite=Strict;";
|
||||
|
||||
const authStore = useAuthStore();
|
||||
authStore.clearUser();
|
||||
|
||||
store.commit("setJWT", "");
|
||||
store.commit("setUser", null);
|
||||
localStorage.setItem("jwt", null);
|
||||
router.push({ path: "/login" });
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import store from "@/store";
|
||||
import { useUploadStore } from "@/stores/upload";
|
||||
import url from "@/utils/url";
|
||||
|
||||
export function checkConflict(files, items) {
|
||||
@ -110,8 +110,10 @@ function detectType(mimetype) {
|
||||
}
|
||||
|
||||
export function handleFiles(files, base, overwrite = false) {
|
||||
const uploadStore = useUploadStore();
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
let id = store.state.upload.id;
|
||||
let id = uploadStore.id;
|
||||
let path = base;
|
||||
let file = files[i];
|
||||
|
||||
@ -133,6 +135,6 @@ export function handleFiles(files, base, overwrite = false) {
|
||||
...(!file.isDir && { type: detectType(file.type) }),
|
||||
};
|
||||
|
||||
store.dispatch("upload/upload", item);
|
||||
uploadStore.upload(item);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,18 +7,24 @@ export function removeLastDir(url) {
|
||||
return arr.join("/");
|
||||
}
|
||||
|
||||
// this code borrow from mozilla
|
||||
// this function is taken from mozilla
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#Examples
|
||||
export function encodeRFC5987ValueChars(str) {
|
||||
return (
|
||||
encodeURIComponent(str)
|
||||
// Note that although RFC3986 reserves "!", RFC5987 does not,
|
||||
// so we do not need to escape it
|
||||
.replace(/['()]/g, escape) // i.e., %27 %28 %29
|
||||
.replace(/\*/g, "%2A")
|
||||
// The following creates the sequences %27 %28 %29 %2A (Note that
|
||||
// the valid encoding of "*" is %2A, which necessitates calling
|
||||
// toUpperCase() to properly encode). Although RFC3986 reserves "!",
|
||||
// RFC5987 does not, so we do not need to escape it.
|
||||
.replace(
|
||||
/['()*]/g,
|
||||
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
|
||||
)
|
||||
// The following are not required for percent-encoding per RFC5987,
|
||||
// so we can allow for a little better readability over the wire: |`^
|
||||
.replace(/%(?:7C|60|5E)/g, unescape)
|
||||
.replace(/%(7C|60|5E)/g, (str, hex) =>
|
||||
String.fromCharCode(parseInt(hex, 16))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,9 @@
|
||||
|
||||
<script>
|
||||
import { files as api } from "@/api";
|
||||
import { mapState, mapMutations } from "vuex";
|
||||
import { mapState, mapActions, mapWritableState } from "pinia";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||
import Breadcrumbs from "@/components/Breadcrumbs.vue";
|
||||
@ -50,7 +52,14 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["req", "reload", "loading", "show"]),
|
||||
...mapWritableState(useFileStore, [
|
||||
"req",
|
||||
"reload",
|
||||
"selected",
|
||||
"multiple",
|
||||
]),
|
||||
...mapState(useLayoutStore, ["show", "showShell"]),
|
||||
...mapWritableState(useLayoutStore, ["loading"]),
|
||||
currentView() {
|
||||
if (this.req.type == undefined) {
|
||||
return null;
|
||||
@ -82,26 +91,27 @@ export default {
|
||||
mounted() {
|
||||
window.addEventListener("keydown", this.keyEvent);
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
window.removeEventListener("keydown", this.keyEvent);
|
||||
},
|
||||
destroyed() {
|
||||
if (this.$store.state.showShell) {
|
||||
this.$store.commit("toggleShell");
|
||||
unmounted() {
|
||||
if (this.showShell) {
|
||||
this.toggleShell();
|
||||
}
|
||||
this.$store.commit("updateRequest", {});
|
||||
this.updateRequest({});
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["setLoading"]),
|
||||
...mapActions(useLayoutStore, ["toggleShell", "showHover", "closeHovers"]),
|
||||
...mapActions(useFileStore, ["updateRequest"]),
|
||||
async fetchData() {
|
||||
// Reset view information.
|
||||
this.$store.commit("setReload", false);
|
||||
this.$store.commit("resetSelected");
|
||||
this.$store.commit("multiple", false);
|
||||
this.$store.commit("closeHovers");
|
||||
this.reload = false;
|
||||
this.selected = [];
|
||||
this.multiple = false;
|
||||
this.closeHovers();
|
||||
|
||||
// Set loading to true and reset the error.
|
||||
this.setLoading(true);
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
let url = this.$route.path;
|
||||
@ -111,25 +121,29 @@ export default {
|
||||
try {
|
||||
const res = await api.fetch(url);
|
||||
|
||||
if (clean(res.path) !== clean(`/${this.$route.params.pathMatch}`)) {
|
||||
return;
|
||||
if (
|
||||
clean(res.path) !==
|
||||
clean(`/${this.$route.params.path}`).replace(/,/g, "/")
|
||||
) {
|
||||
throw new Error("Data Mismatch!");
|
||||
}
|
||||
|
||||
this.$store.commit("updateRequest", res);
|
||||
this.updateRequest(res);
|
||||
document.title = `${res.name} - ${document.title}`;
|
||||
} catch (e) {
|
||||
this.error = e;
|
||||
} finally {
|
||||
this.setLoading(false);
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
keyEvent(event) {
|
||||
// F1!
|
||||
if (event.keyCode === 112) {
|
||||
event.preventDefault();
|
||||
this.$store.commit("showHover", "help");
|
||||
this.showHover("help");
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/file@/stores/layout
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<sidebar></sidebar>
|
||||
<main>
|
||||
<router-view></router-view>
|
||||
<shell v-if="isExecEnabled && isLogged && user.perm.execute" />
|
||||
<shell v-if="isExecEnabled && isLoggedIn && user.perm.execute" />
|
||||
</main>
|
||||
<prompts></prompts>
|
||||
<upload-files></upload-files>
|
||||
@ -14,11 +14,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import { mapActions, mapState, mapWritableState } from "pinia";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import Sidebar from "@/components/Sidebar.vue";
|
||||
import Prompts from "@/components/prompts/Prompts.vue";
|
||||
import Shell from "@/components/Shell.vue";
|
||||
import UploadFiles from "../components/prompts/UploadFiles.vue";
|
||||
import UploadFiles from "@/components/prompts/UploadFiles.vue";
|
||||
import { enableExec } from "@/utils/constants";
|
||||
|
||||
export default {
|
||||
@ -30,17 +33,23 @@ export default {
|
||||
UploadFiles,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["isLogged", "progress"]),
|
||||
...mapState(["user"]),
|
||||
...mapState(useAuthStore, ["isLoggedIn", "user"]),
|
||||
...mapState(useLayoutStore, ["progress", "show"]),
|
||||
...mapWritableState(useFileStore, ["selected", "multiple"]),
|
||||
isExecEnabled: () => enableExec,
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["closeHovers"]),
|
||||
},
|
||||
watch: {
|
||||
$route: function () {
|
||||
this.$store.commit("resetSelected");
|
||||
this.$store.commit("multiple", false);
|
||||
if (this.$store.state.show !== "success")
|
||||
this.$store.commit("closeHovers");
|
||||
this.selected = [];
|
||||
this.multiple = false;
|
||||
if (this.show !== "success") {
|
||||
this.closeHovers();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/auth@/stores/layout@/stores/file
|
||||
|
||||
@ -117,6 +117,7 @@ export default {
|
||||
await auth.login(this.username, this.password, captcha);
|
||||
this.$router.push({ path: redirect });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
if (e.message == 409) {
|
||||
this.error = this.$t("login.usernameTaken");
|
||||
} else {
|
||||
|
||||
@ -50,9 +50,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
|
||||
import { mapState } from "pinia";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
export default {
|
||||
name: "settings",
|
||||
@ -60,7 +61,9 @@ export default {
|
||||
HeaderBar,
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user", "loading"]),
|
||||
...mapState(useAuthStore, ["user"]),
|
||||
...mapState(useLayoutStore, ["loading"]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/auth@/stores/file
|
||||
|
||||
@ -147,13 +147,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
:class="{ active: $store.state.multiple }"
|
||||
id="multiple-selection"
|
||||
>
|
||||
<div :class="{ active: multiple }" id="multiple-selection">
|
||||
<p>{{ $t("files.multipleSelectionEnabled") }}</p>
|
||||
<div
|
||||
@click="$store.commit('multiple', false)"
|
||||
@click="() => (multiple = false)"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
:title="$t('files.clear')"
|
||||
@ -180,10 +177,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations, mapGetters } from "vuex";
|
||||
import { mapState, mapActions, mapWritableState } from "pinia";
|
||||
import { pub as api } from "@/api";
|
||||
import { filesize } from "filesize";
|
||||
import moment from "moment";
|
||||
import { Base64 } from "js-base64";
|
||||
|
||||
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||
import Action from "@/components/header/Action.vue";
|
||||
@ -192,6 +190,8 @@ import Errors from "@/views/Errors.vue";
|
||||
import QrcodeVue from "qrcode.vue";
|
||||
import Item from "@/components/files/ListingItem.vue";
|
||||
import Clipboard from "clipboard";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
export default {
|
||||
name: "share",
|
||||
@ -231,13 +231,14 @@ export default {
|
||||
this.$showSuccess(this.$t("success.linkCopied"));
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
window.removeEventListener("keydown", this.keyEvent);
|
||||
this.clip.destroy();
|
||||
},
|
||||
computed: {
|
||||
...mapState(["req", "loading", "multiple", "selected"]),
|
||||
...mapGetters(["selectedCount"]),
|
||||
...mapState(useFileStore, ["req", "selectedCount"]),
|
||||
...mapWritableState(useFileStore, ["reload", "multiple", "selected"]),
|
||||
...mapWritableState(useLayoutStore, ["loading"]),
|
||||
icon: function () {
|
||||
if (this.req.isDir) return "folder";
|
||||
if (this.req.type === "image") return "insert_photo";
|
||||
@ -266,19 +267,20 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["resetSelected", "updateRequest", "setLoading"]),
|
||||
...mapActions(useFileStore, ["updateRequest", "toggleMultiple"]),
|
||||
...mapActions(useLayoutStore, ["showHover", "closeHovers"]),
|
||||
base64: function (name) {
|
||||
return window.btoa(unescape(encodeURIComponent(name)));
|
||||
return Base64.encodeURI(name);
|
||||
},
|
||||
fetchData: async function () {
|
||||
// Reset view information.
|
||||
this.$store.commit("setReload", false);
|
||||
this.$store.commit("resetSelected");
|
||||
this.$store.commit("multiple", false);
|
||||
this.$store.commit("closeHovers");
|
||||
this.reload = false;
|
||||
this.selected = [];
|
||||
this.multiple = false;
|
||||
this.closeHovers();
|
||||
|
||||
// Set loading to true and reset the error.
|
||||
this.setLoading(true);
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
if (this.password !== "") {
|
||||
@ -300,7 +302,7 @@ export default {
|
||||
} catch (e) {
|
||||
this.error = e;
|
||||
} finally {
|
||||
this.setLoading(false);
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
keyEvent(event) {
|
||||
@ -309,12 +311,12 @@ export default {
|
||||
// If we're on a listing, unselect all
|
||||
// files and folders.
|
||||
if (this.selectedCount > 0) {
|
||||
this.resetSelected();
|
||||
this.selected = [];
|
||||
}
|
||||
}
|
||||
},
|
||||
toggleMultipleSelection() {
|
||||
this.$store.commit("multiple", !this.multiple);
|
||||
this.toggleMultiple();
|
||||
},
|
||||
isSingleFile: function () {
|
||||
return (
|
||||
@ -332,10 +334,10 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.commit("showHover", {
|
||||
this.showHover({
|
||||
prompt: "download",
|
||||
confirm: (format) => {
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
|
||||
let files = [];
|
||||
|
||||
@ -358,3 +360,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/file@/stores/layout
|
||||
|
||||
@ -20,18 +20,21 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { files as api } from "@/api";
|
||||
import { theme } from "@/utils/constants";
|
||||
import buttons from "@/utils/buttons";
|
||||
import url from "@/utils/url";
|
||||
|
||||
import { version as ace_version } from "ace-builds";
|
||||
import ace from "ace-builds/src-min-noconflict/ace.js";
|
||||
import modelist from "ace-builds/src-min-noconflict/ext-modelist.js";
|
||||
|
||||
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||
import Action from "@/components/header/Action.vue";
|
||||
import Breadcrumbs from "@/components/Breadcrumbs.vue";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
|
||||
export default {
|
||||
name: "editor",
|
||||
@ -44,7 +47,8 @@ export default {
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["req", "user"]),
|
||||
...mapState(useAuthStore, ["user"]),
|
||||
...mapState(useFileStore, ["req"]),
|
||||
breadcrumbs() {
|
||||
let parts = this.$route.path.split("/");
|
||||
|
||||
@ -78,13 +82,18 @@ export default {
|
||||
created() {
|
||||
window.addEventListener("keydown", this.keyEvent);
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
window.removeEventListener("keydown", this.keyEvent);
|
||||
this.editor.destroy();
|
||||
},
|
||||
mounted: function () {
|
||||
const fileContent = this.req.content || "";
|
||||
|
||||
ace.config.set(
|
||||
"basePath",
|
||||
`https://cdn.jsdelivr.net/npm/ace-builds@${ace_version}/src-min-noconflict/`
|
||||
);
|
||||
|
||||
this.editor = ace.edit("editor", {
|
||||
value: fileContent,
|
||||
showPrintMargin: false,
|
||||
@ -99,6 +108,7 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useFileStore, ["updateRequest"]),
|
||||
back() {
|
||||
let uri = url.removeLastDir(this.$route.path) + "/";
|
||||
this.$router.push({ path: uri });
|
||||
@ -128,7 +138,7 @@ export default {
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.$store.commit("updateRequest", {});
|
||||
this.updateRequest({});
|
||||
|
||||
let uri = url.removeLastDir(this.$route.path) + "/";
|
||||
this.$router.push({ path: uri });
|
||||
@ -136,3 +146,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/auth@/stores/file
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
v-if="headerButtons.shell"
|
||||
icon="code"
|
||||
:label="$t('buttons.shell')"
|
||||
@action="$store.commit('toggleShell')"
|
||||
@action="toggleShell"
|
||||
/>
|
||||
<action
|
||||
:icon="viewIcon"
|
||||
@ -248,10 +248,10 @@
|
||||
multiple
|
||||
/>
|
||||
|
||||
<div :class="{ active: $store.state.multiple }" id="multiple-selection">
|
||||
<div :class="{ active: multiple }" id="multiple-selection">
|
||||
<p>{{ $t("files.multipleSelectionEnabled") }}</p>
|
||||
<div
|
||||
@click="$store.commit('multiple', false)"
|
||||
@click="() => (multiple = false)"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
:title="$t('files.clear')"
|
||||
@ -268,12 +268,18 @@
|
||||
|
||||
<script>
|
||||
import Vue from "vue";
|
||||
import { mapState, mapGetters, mapMutations } from "vuex";
|
||||
import { mapState, mapWritableState, mapActions, mapStores } from "pinia";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useClipboardStore } from "@/stores/clipboard";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import { users, files as api } from "@/api";
|
||||
import { enableExec } from "@/utils/constants";
|
||||
import * as upload from "@/utils/upload";
|
||||
import css from "@/utils/css";
|
||||
import throttle from "lodash.throttle";
|
||||
import { Base64 } from "js-base64";
|
||||
|
||||
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||
import Action from "@/components/header/Action.vue";
|
||||
@ -298,16 +304,17 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
...mapStores(useClipboardStore),
|
||||
...mapState(useAuthStore, ["user"]),
|
||||
...mapState(useFileStore, ["selectedCount", "toggleMultiple"]),
|
||||
...mapState(useLayoutStore, ["show"]),
|
||||
...mapWritableState(useFileStore, [
|
||||
"req",
|
||||
"selected",
|
||||
"user",
|
||||
"show",
|
||||
"multiple",
|
||||
"selected",
|
||||
"loading",
|
||||
"reload",
|
||||
]),
|
||||
...mapGetters(["selectedCount"]),
|
||||
nameSorted() {
|
||||
return this.req.sorting.by === "name";
|
||||
},
|
||||
@ -425,7 +432,7 @@ export default {
|
||||
document.addEventListener("dragleave", this.dragLeave);
|
||||
document.addEventListener("drop", this.drop);
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
// Remove event listeners before destroying this page.
|
||||
window.removeEventListener("keydown", this.keyEvent);
|
||||
window.removeEventListener("scroll", this.scrollEvent);
|
||||
@ -438,9 +445,10 @@ export default {
|
||||
document.removeEventListener("drop", this.drop);
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["updateUser", "addSelected"]),
|
||||
...mapActions(useAuthStore, ["updateUser"]),
|
||||
...mapActions(useLayoutStore, ["showHover", "closeHovers", "toggleShell"]),
|
||||
base64: function (name) {
|
||||
return window.btoa(unescape(encodeURIComponent(name)));
|
||||
return Base64.encodeURI(name);
|
||||
},
|
||||
keyEvent(event) {
|
||||
// No prompts are shown
|
||||
@ -451,7 +459,7 @@ export default {
|
||||
// Esc!
|
||||
if (event.keyCode === 27) {
|
||||
// Reset files selection.
|
||||
this.$store.commit("resetSelected");
|
||||
this.selected = [];
|
||||
}
|
||||
|
||||
// Del!
|
||||
@ -459,7 +467,7 @@ export default {
|
||||
if (!this.user.perm.delete || this.selectedCount == 0) return;
|
||||
|
||||
// Show delete prompt.
|
||||
this.$store.commit("showHover", "delete");
|
||||
this.showHover("delete");
|
||||
}
|
||||
|
||||
// F2!
|
||||
@ -467,7 +475,7 @@ export default {
|
||||
if (!this.user.perm.rename || this.selectedCount !== 1) return;
|
||||
|
||||
// Show rename prompt.
|
||||
this.$store.commit("showHover", "rename");
|
||||
this.showHover("rename");
|
||||
}
|
||||
|
||||
// Ctrl is pressed
|
||||
@ -480,7 +488,7 @@ export default {
|
||||
switch (key) {
|
||||
case "f":
|
||||
event.preventDefault();
|
||||
this.$store.commit("showHover", "search");
|
||||
this.showHover("search");
|
||||
break;
|
||||
case "c":
|
||||
case "x":
|
||||
@ -492,13 +500,13 @@ export default {
|
||||
case "a":
|
||||
event.preventDefault();
|
||||
for (let file of this.items.files) {
|
||||
if (this.$store.state.selected.indexOf(file.index) === -1) {
|
||||
this.addSelected(file.index);
|
||||
if (this.selected.indexOf(file.index) === -1) {
|
||||
this.selected.push(file.index);
|
||||
}
|
||||
}
|
||||
for (let dir of this.items.dirs) {
|
||||
if (this.$store.state.selected.indexOf(dir.index) === -1) {
|
||||
this.addSelected(dir.index);
|
||||
if (this.selected.indexOf(dir.index) === -1) {
|
||||
this.selected.push(dir.index);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -530,7 +538,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.commit("updateClipboard", {
|
||||
this.clipboardStore.updateClipboard({
|
||||
key: key,
|
||||
items: items,
|
||||
path: this.$route.path,
|
||||
@ -543,7 +551,7 @@ export default {
|
||||
|
||||
let items = [];
|
||||
|
||||
for (let item of this.$store.state.clipboard.items) {
|
||||
for (let item of this.clipboardStore.items) {
|
||||
const from = item.from.endsWith("/")
|
||||
? item.from.slice(0, -1)
|
||||
: item.from;
|
||||
@ -559,24 +567,24 @@ export default {
|
||||
api
|
||||
.copy(items, overwrite, rename)
|
||||
.then(() => {
|
||||
this.$store.commit("setReload", true);
|
||||
this.reload = true;
|
||||
})
|
||||
.catch(this.$showError);
|
||||
};
|
||||
|
||||
if (this.$store.state.clipboard.key === "x") {
|
||||
if (this.clipboardStore.key === "x") {
|
||||
action = (overwrite, rename) => {
|
||||
api
|
||||
.move(items, overwrite, rename)
|
||||
.then(() => {
|
||||
this.$store.commit("resetClipboard");
|
||||
this.$store.commit("setReload", true);
|
||||
this.clipboardStore.resetClipboard();
|
||||
this.reload = true;
|
||||
})
|
||||
.catch(this.$showError);
|
||||
};
|
||||
}
|
||||
|
||||
if (this.$store.state.clipboard.path == this.$route.path) {
|
||||
if (this.clipboardStore.path == this.$route.path) {
|
||||
action(false, true);
|
||||
|
||||
return;
|
||||
@ -588,14 +596,14 @@ export default {
|
||||
let rename = false;
|
||||
|
||||
if (conflict) {
|
||||
this.$store.commit("showHover", {
|
||||
this.showHover({
|
||||
prompt: "replace-rename",
|
||||
confirm: (event, option) => {
|
||||
overwrite = option == "overwrite";
|
||||
rename = option == "rename";
|
||||
|
||||
event.preventDefault();
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
action(overwrite, rename);
|
||||
},
|
||||
});
|
||||
@ -695,16 +703,16 @@ export default {
|
||||
let conflict = upload.checkConflict(files, items);
|
||||
|
||||
if (conflict) {
|
||||
this.$store.commit("showHover", {
|
||||
this.showHover({
|
||||
prompt: "replace",
|
||||
action: (event) => {
|
||||
event.preventDefault();
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
upload.handleFiles(files, path, false);
|
||||
},
|
||||
confirm: (event) => {
|
||||
event.preventDefault();
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
upload.handleFiles(files, path, true);
|
||||
},
|
||||
});
|
||||
@ -715,7 +723,7 @@ export default {
|
||||
upload.handleFiles(files, path);
|
||||
},
|
||||
uploadInput(event) {
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
|
||||
let files = event.currentTarget.files;
|
||||
let folder_upload =
|
||||
@ -735,16 +743,16 @@ export default {
|
||||
let conflict = upload.checkConflict(files, this.req.items);
|
||||
|
||||
if (conflict) {
|
||||
this.$store.commit("showHover", {
|
||||
this.showHover({
|
||||
prompt: "replace",
|
||||
action: (event) => {
|
||||
event.preventDefault();
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
upload.handleFiles(files, path, false);
|
||||
},
|
||||
confirm: (event) => {
|
||||
event.preventDefault();
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
upload.handleFiles(files, path, true);
|
||||
},
|
||||
});
|
||||
@ -786,14 +794,14 @@ export default {
|
||||
this.$showError(e);
|
||||
}
|
||||
|
||||
this.$store.commit("setReload", true);
|
||||
this.reload = true;
|
||||
},
|
||||
openSearch() {
|
||||
this.$store.commit("showHover", "search");
|
||||
this.showHover("search");
|
||||
},
|
||||
toggleMultipleSelection() {
|
||||
this.$store.commit("multiple", !this.multiple);
|
||||
this.$store.commit("closeHovers");
|
||||
this.toggleMultiple();
|
||||
this.closeHovers();
|
||||
},
|
||||
windowsResize: throttle(function () {
|
||||
this.colunmsResize();
|
||||
@ -814,10 +822,10 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.commit("showHover", {
|
||||
this.showHover({
|
||||
prompt: "download",
|
||||
confirm: (format) => {
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
|
||||
let files = [];
|
||||
|
||||
@ -834,7 +842,7 @@ export default {
|
||||
});
|
||||
},
|
||||
switchView: async function () {
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
|
||||
const modes = {
|
||||
list: "mosaic",
|
||||
@ -850,7 +858,7 @@ export default {
|
||||
users.update(data, ["viewMode"]).catch(this.$showError);
|
||||
|
||||
// Await ensures correct value for setItemWeight()
|
||||
await this.$store.commit("updateUser", data);
|
||||
await this.updateUser(data);
|
||||
|
||||
this.setItemWeight();
|
||||
this.fillWindow();
|
||||
@ -860,7 +868,7 @@ export default {
|
||||
typeof window.DataTransferItem !== "undefined" &&
|
||||
typeof DataTransferItem.prototype.webkitGetAsEntry !== "undefined"
|
||||
) {
|
||||
this.$store.commit("showHover", "upload");
|
||||
this.showHover("upload");
|
||||
} else {
|
||||
document.getElementById("upload-input").click();
|
||||
}
|
||||
@ -897,3 +905,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/auth@/stores/clipboard@/stores/file@/stores/layout
|
||||
|
||||
@ -143,7 +143,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import { mapActions, mapState } from "pinia";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import { files as api } from "@/api";
|
||||
import { resizePreview } from "@/utils/constants";
|
||||
import url from "@/utils/url";
|
||||
@ -177,7 +181,9 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["req", "user", "oldReq", "jwt", "loading", "show"]),
|
||||
...mapState(useAuthStore, ["user", "jwt"]),
|
||||
...mapState(useFileStore, ["req", "oldReq", "loading"]),
|
||||
...mapState(useLayoutStore, ["show"]),
|
||||
hasPrevious() {
|
||||
return this.previousLink !== "";
|
||||
},
|
||||
@ -195,7 +201,7 @@ export default {
|
||||
return api.getDownloadURL(this.req, true);
|
||||
},
|
||||
showMore() {
|
||||
return this.$store.state.show === "more";
|
||||
return this.show === "more";
|
||||
},
|
||||
isResizeEnabled() {
|
||||
return resizePreview;
|
||||
@ -218,12 +224,14 @@ export default {
|
||||
this.listing = this.oldReq.items;
|
||||
this.updatePreview();
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
window.removeEventListener("keydown", this.key);
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useFileStore, ["updateRequest"]),
|
||||
...mapActions(useLayoutStore, ["showHover", "closeHovers"]),
|
||||
deleteFile() {
|
||||
this.$store.commit("showHover", {
|
||||
this.showHover({
|
||||
prompt: "delete",
|
||||
confirm: () => {
|
||||
this.listing = this.listing.filter((item) => item.name !== this.name);
|
||||
@ -320,10 +328,10 @@ export default {
|
||||
: api.getPreviewURL(item, "big");
|
||||
},
|
||||
openMore() {
|
||||
this.$store.commit("showHover", "more");
|
||||
this.showHover("more");
|
||||
},
|
||||
resetPrompts() {
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
},
|
||||
toggleSize() {
|
||||
this.fullSize = !this.fullSize;
|
||||
@ -341,7 +349,7 @@ export default {
|
||||
}, 1500);
|
||||
}, 500),
|
||||
close() {
|
||||
this.$store.commit("updateRequest", {});
|
||||
this.updateRequest({});
|
||||
|
||||
let uri = url.removeLastDir(this.$route.path) + "/";
|
||||
this.$router.push({ path: uri });
|
||||
@ -352,3 +360,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/auth@/stores/file@/stores/layout
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
|
||||
<h3>{{ $t("settings.rules") }}</h3>
|
||||
<p class="small">{{ $t("settings.globalRules") }}</p>
|
||||
<rules :rules.sync="settings.rules" />
|
||||
<rules v-model:rules="settings.rules" />
|
||||
|
||||
<div v-if="isExecEnabled">
|
||||
<h3>{{ $t("settings.executeOnShell") }}</h3>
|
||||
@ -75,7 +75,7 @@
|
||||
<label for="theme">{{ $t("settings.themes.title") }}</label>
|
||||
<themes
|
||||
class="input input--block"
|
||||
:theme.sync="settings.branding.theme"
|
||||
v-model:theme="settings.branding.theme"
|
||||
id="theme"
|
||||
></themes>
|
||||
</p>
|
||||
@ -156,7 +156,7 @@
|
||||
<user-form
|
||||
:isNew="false"
|
||||
:isDefault="true"
|
||||
:user.sync="settings.defaults"
|
||||
v-model:user="settings.defaults"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -220,7 +220,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations } from "vuex";
|
||||
import { mapState, mapWritableState } from "pinia";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import { settings as api } from "@/api";
|
||||
import { enableExec } from "@/utils/constants";
|
||||
import UserForm from "@/components/settings/UserForm.vue";
|
||||
@ -245,7 +247,8 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user", "loading"]),
|
||||
...mapState(useAuthStore, ["user"]),
|
||||
...mapWritableState(useLayoutStore, ["loading"]),
|
||||
isExecEnabled: () => enableExec,
|
||||
formattedChunkSize: {
|
||||
get() {
|
||||
@ -268,7 +271,7 @@ export default {
|
||||
},
|
||||
async created() {
|
||||
try {
|
||||
this.setLoading(true);
|
||||
this.loading = true;
|
||||
|
||||
const original = await api.get();
|
||||
let settings = { ...original, commands: [] };
|
||||
@ -287,11 +290,10 @@ export default {
|
||||
} catch (e) {
|
||||
this.error = e;
|
||||
} finally {
|
||||
this.setLoading(false);
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["setLoading"]),
|
||||
capitalize(name, where = "_") {
|
||||
if (where === "caps") where = /(?=[A-Z])/;
|
||||
let splitted = name.split(where);
|
||||
@ -358,7 +360,7 @@ export default {
|
||||
return `${size}${units[unitIndex]}`;
|
||||
},
|
||||
// Clear the debounce timeout when the component is destroyed
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
if (this.debounceTimeout) {
|
||||
clearTimeout(this.debounceTimeout);
|
||||
}
|
||||
@ -366,3 +368,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/auth@/stores/file
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
<h3>{{ $t("settings.language") }}</h3>
|
||||
<languages
|
||||
class="input input--block"
|
||||
:locale.sync="locale"
|
||||
v-model:locale="locale"
|
||||
></languages>
|
||||
</div>
|
||||
|
||||
@ -72,7 +72,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations } from "vuex";
|
||||
import { mapActions, mapState, mapWritableState } from "pinia";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import { users as api } from "@/api";
|
||||
import Languages from "@/components/settings/Languages.vue";
|
||||
import i18n, { rtlLanguages } from "@/i18n";
|
||||
@ -93,7 +95,8 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user"]),
|
||||
...mapState(useAuthStore, ["user"]),
|
||||
...mapWritableState(useLayoutStore, ["loading"]),
|
||||
passwordClass() {
|
||||
const baseClass = "input input--block";
|
||||
|
||||
@ -109,14 +112,14 @@ export default {
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.setLoading(false);
|
||||
this.loading = true;
|
||||
this.locale = this.user.locale;
|
||||
this.hideDotfiles = this.user.hideDotfiles;
|
||||
this.singleClick = this.user.singleClick;
|
||||
this.dateFormat = this.user.dateFormat;
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["updateUser", "setLoading"]),
|
||||
...mapActions(useAuthStore, ["updateUser"]),
|
||||
async updatePassword(event) {
|
||||
event.preventDefault();
|
||||
|
||||
@ -165,3 +168,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/auth@/stores/file
|
||||
|
||||
@ -61,8 +61,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapWritableState } from "pinia";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import { share as api, users } from "@/api";
|
||||
import { mapState, mapMutations } from "vuex";
|
||||
import moment from "moment";
|
||||
import Clipboard from "clipboard";
|
||||
import Errors from "@/views/Errors.vue";
|
||||
@ -72,7 +74,10 @@ export default {
|
||||
components: {
|
||||
Errors,
|
||||
},
|
||||
computed: mapState(["user", "loading"]),
|
||||
computed: {
|
||||
...mapState(useAuthStore, ["user"]),
|
||||
...mapWritableState(useLayoutStore, ["loading"]),
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
error: null,
|
||||
@ -81,7 +86,7 @@ export default {
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
this.setLoading(true);
|
||||
this.loading = true;
|
||||
|
||||
try {
|
||||
let links = await api.list();
|
||||
@ -98,7 +103,7 @@ export default {
|
||||
} catch (e) {
|
||||
this.error = e;
|
||||
} finally {
|
||||
this.setLoading(false);
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -107,18 +112,17 @@ export default {
|
||||
this.$showSuccess(this.$t("success.linkCopied"));
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
beforeUnmount() {
|
||||
this.clip.destroy();
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["setLoading"]),
|
||||
deleteLink: async function (event, link) {
|
||||
event.preventDefault();
|
||||
|
||||
this.$store.commit("showHover", {
|
||||
this.showHover({
|
||||
prompt: "share-delete",
|
||||
confirm: () => {
|
||||
this.$store.commit("closeHovers");
|
||||
this.closeHovers();
|
||||
|
||||
try {
|
||||
api.remove(link.hash);
|
||||
@ -139,3 +143,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/auth@/stores/file
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
|
||||
<div class="card-content">
|
||||
<user-form
|
||||
:user.sync="user"
|
||||
:createUserDir.sync="createUserDir"
|
||||
v-model:user="user"
|
||||
v-model:createUserDir="createUserDir"
|
||||
:isDefault="false"
|
||||
:isNew="isNew"
|
||||
/>
|
||||
@ -37,7 +37,7 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div v-if="$store.state.show === 'deleteUser'" class="card floating">
|
||||
<div v-if="show === 'deleteUser'" class="card floating">
|
||||
<div class="card-content">
|
||||
<p>Are you sure you want to delete this user?</p>
|
||||
</div>
|
||||
@ -61,7 +61,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations } from "vuex";
|
||||
import { mapActions, mapState, mapWritableState } from "pinia";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import { users as api, settings } from "@/api";
|
||||
import UserForm from "@/components/settings/UserForm.vue";
|
||||
import Errors from "@/views/Errors.vue";
|
||||
@ -85,10 +87,12 @@ export default {
|
||||
this.fetchData();
|
||||
},
|
||||
computed: {
|
||||
...mapState(useAuthStore, ["user"]),
|
||||
...mapState(useLayoutStore, ["show"]),
|
||||
...mapWritableState(useLayoutStore, ["loading"]),
|
||||
isNew() {
|
||||
return this.$route.path === "/settings/users/new";
|
||||
},
|
||||
...mapState(["loading"]),
|
||||
},
|
||||
watch: {
|
||||
$route: "fetchData",
|
||||
@ -98,9 +102,10 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["closeHovers", "showHover", "setUser", "setLoading"]),
|
||||
...mapActions(useAuthStore, ["setUser"]),
|
||||
...mapActions(useLayoutStore, ["closeHovers", "showHover"]),
|
||||
async fetchData() {
|
||||
this.setLoading(true);
|
||||
this.loading = true;
|
||||
|
||||
try {
|
||||
if (this.isNew) {
|
||||
@ -121,7 +126,7 @@ export default {
|
||||
} catch (e) {
|
||||
this.error = e;
|
||||
} finally {
|
||||
this.setLoading(false);
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
deletePrompt() {
|
||||
@ -155,7 +160,7 @@ export default {
|
||||
} else {
|
||||
await api.update(user);
|
||||
|
||||
if (user.id === this.$store.state.user.id) {
|
||||
if (user.id === this.user.id) {
|
||||
this.setUser({ ...deepClone(user) });
|
||||
}
|
||||
|
||||
@ -168,3 +173,4 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/auth@/stores/file@/stores/layout
|
||||
|
||||
@ -42,7 +42,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapMutations } from "vuex";
|
||||
import { mapWritableState } from "pinia";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import { users as api } from "@/api";
|
||||
import Errors from "@/views/Errors.vue";
|
||||
|
||||
@ -58,21 +59,19 @@ export default {
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
this.setLoading(true);
|
||||
this.loading = true;
|
||||
|
||||
try {
|
||||
this.users = await api.getAll();
|
||||
} catch (e) {
|
||||
this.error = e;
|
||||
} finally {
|
||||
this.setLoading(false);
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(["loading"]),
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(["setLoading"]),
|
||||
...mapWritableState(useLayoutStore, ["loading"]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@/stores/file
|
||||
|
||||
@ -4,6 +4,7 @@ import { defineConfig } from "vite";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite";
|
||||
import { compression } from "vite-plugin-compression2";
|
||||
import pluginRewriteAll from "vite-plugin-rewrite-all";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
@ -19,6 +20,7 @@ export default defineConfig({
|
||||
}),
|
||||
VueI18nPlugin(),
|
||||
compression({ include: /\.js$/i, deleteOriginalAssets: true }),
|
||||
pluginRewriteAll(), // Fixes 404 error with paths containing dot (will be fixed in Vite 5)
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
@ -26,6 +28,15 @@ export default defineConfig({
|
||||
"@/": fileURLToPath(new URL("./src/", import.meta.url)),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
"/api/command": {
|
||||
target: "ws://127.0.0.1:8080",
|
||||
ws: true,
|
||||
},
|
||||
"/api": "http://127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
base: "",
|
||||
experimental: {
|
||||
renderBuiltUrl(filename, { hostType }) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user