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