Converted view/Share.vue to composition api
This commit is contained in:
parent
22854bf699
commit
ef5c8b0d8e
@ -3,10 +3,14 @@
|
|||||||
"env": {
|
"env": {
|
||||||
"node": true
|
"node": true
|
||||||
},
|
},
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
"extends": [
|
"extends": [
|
||||||
"plugin:vue/vue3-essential",
|
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:vue/vue3-essential",
|
||||||
"@vue/eslint-config-prettier"
|
"@vue/eslint-config-prettier"
|
||||||
|
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"vue/multi-word-component-names": "off",
|
"vue/multi-word-component-names": "off",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { fetchURL, removePrefix, createURL } from "./utils";
|
import { fetchURL, removePrefix, createURL } from "./utils";
|
||||||
import { baseURL } from "@/utils/constants";
|
import { baseURL } from "@/utils/constants";
|
||||||
|
|
||||||
export async function fetch(url: apiUrl, password: string = "") {
|
export async function fetch(url: ApiUrl, password: string = "") {
|
||||||
url = removePrefix(url);
|
url = removePrefix(url);
|
||||||
|
|
||||||
const res = await fetchURL(
|
const res = await fetchURL(
|
||||||
@ -33,35 +33,35 @@ export async function fetch(url: apiUrl, password: string = "") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Is this redundant code?
|
// Is this redundant code?
|
||||||
// export function download(format, hash, token, ...files) {
|
export function download(format: any, hash: string, token: string, ...files: any) {
|
||||||
// let url = `${baseURL}/api/public/dl/${hash}`;
|
let url = `${baseURL}/api/public/dl/${hash}`;
|
||||||
|
|
||||||
// if (files.length === 1) {
|
if (files.length === 1) {
|
||||||
// url += encodeURIComponent(files[0]) + "?";
|
url += encodeURIComponent(files[0]) + "?";
|
||||||
// } else {
|
} else {
|
||||||
// let arg = "";
|
let arg = "";
|
||||||
|
|
||||||
// for (let file of files) {
|
for (let file of files) {
|
||||||
// arg += encodeURIComponent(file) + ",";
|
arg += encodeURIComponent(file) + ",";
|
||||||
// }
|
}
|
||||||
|
|
||||||
// arg = arg.substring(0, arg.length - 1);
|
arg = arg.substring(0, arg.length - 1);
|
||||||
// arg = encodeURIComponent(arg);
|
arg = encodeURIComponent(arg);
|
||||||
// url += `/?files=${arg}&`;
|
url += `/?files=${arg}&`;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (format) {
|
if (format) {
|
||||||
// url += `algo=${format}&`;
|
url += `algo=${format}&`;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (token) {
|
if (token) {
|
||||||
// url += `token=${token}&`;
|
url += `token=${token}&`;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// window.open(url);
|
window.open(url);
|
||||||
// }
|
}
|
||||||
|
|
||||||
export function getDownloadURL(share: share, inline = false) {
|
export function getDownloadURL(share: IFile, inline = false) {
|
||||||
const params = {
|
const params = {
|
||||||
...(inline && { inline: "true" }),
|
...(inline && { inline: "true" }),
|
||||||
...(share.token && { token: share.token }),
|
...(share.token && { token: share.token }),
|
||||||
|
|||||||
@ -3,7 +3,7 @@ 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: apiUrl, opts: apiOpts, auth = true) {
|
export async function fetchURL(url: ApiUrl, opts: ApiOpts, auth = true) {
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
@ -46,7 +46,7 @@ export async function fetchURL(url: apiUrl, opts: apiOpts, auth = true) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchJSON(url: apiUrl, opts?: any) {
|
export async function fetchJSON(url: ApiUrl, opts?: any) {
|
||||||
const res = await fetchURL(url, opts);
|
const res = await fetchURL(url, opts);
|
||||||
|
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
@ -56,7 +56,7 @@ export async function fetchJSON(url: apiUrl, opts?: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removePrefix(url: apiUrl) {
|
export function removePrefix(url: ApiUrl) {
|
||||||
url = url.split("/").splice(2).join("/");
|
url = url.split("/").splice(2).join("/");
|
||||||
|
|
||||||
if (url === "") url = "/";
|
if (url === "") url = "/";
|
||||||
@ -64,7 +64,7 @@ export function removePrefix(url: apiUrl) {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createURL(endpoint: apiUrl, params = {}, auth = true) {
|
export function createURL(endpoint: ApiUrl, params = {}, auth = true) {
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
let prefix = baseURL;
|
let prefix = baseURL;
|
||||||
|
|||||||
@ -13,6 +13,8 @@ import dayjs from "dayjs";
|
|||||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||||
import relativeTime from "dayjs/plugin/relativeTime";
|
import relativeTime from "dayjs/plugin/relativeTime";
|
||||||
import duration from "dayjs/plugin/duration";
|
import duration from "dayjs/plugin/duration";
|
||||||
|
import "./css/styles.css";
|
||||||
|
|
||||||
// register dayjs plugins globally
|
// register dayjs plugins globally
|
||||||
dayjs.extend(localizedFormat);
|
dayjs.extend(localizedFormat);
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
|||||||
@ -3,15 +3,15 @@ import { defineStore } from "pinia";
|
|||||||
export const useFileStore = defineStore("file", {
|
export const useFileStore = defineStore("file", {
|
||||||
// convert to a function
|
// convert to a function
|
||||||
state: (): {
|
state: (): {
|
||||||
req: req,
|
req: IFile | null,
|
||||||
oldReq: req,
|
oldReq: IFile | null,
|
||||||
reload: boolean,
|
reload: boolean,
|
||||||
selected: any[],
|
selected: any[],
|
||||||
multiple: boolean,
|
multiple: boolean,
|
||||||
isFiles: boolean
|
isFiles: boolean
|
||||||
} => ({
|
} => ({
|
||||||
req: {},
|
req: null,
|
||||||
oldReq: {},
|
oldReq: null,
|
||||||
reload: false,
|
reload: false,
|
||||||
selected: [],
|
selected: [],
|
||||||
multiple: false,
|
multiple: false,
|
||||||
@ -28,7 +28,7 @@ export const useFileStore = defineStore("file", {
|
|||||||
// return !layoutStore.loading && state.route._value.name === "Files";
|
// return !layoutStore.loading && state.route._value.name === "Files";
|
||||||
// },
|
// },
|
||||||
isListing: (state) => {
|
isListing: (state) => {
|
||||||
return state.isFiles && state.req.isDir;
|
return state.isFiles && state?.req?.isDir;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
@ -36,7 +36,7 @@ export const useFileStore = defineStore("file", {
|
|||||||
toggleMultiple() {
|
toggleMultiple() {
|
||||||
this.multiple = !this.multiple;
|
this.multiple = !this.multiple;
|
||||||
},
|
},
|
||||||
updateRequest(value: req) {
|
updateRequest(value: IFile) {
|
||||||
this.oldReq = this.req;
|
this.oldReq = this.req;
|
||||||
this.req = value;
|
this.req = value;
|
||||||
},
|
},
|
||||||
|
|||||||
@ -7,7 +7,7 @@ export const useLayoutStore = defineStore("layout", {
|
|||||||
state: (): {
|
state: (): {
|
||||||
loading: boolean,
|
loading: boolean,
|
||||||
show: string | null | boolean,
|
show: string | null | boolean,
|
||||||
showConfirm: boolean | null,
|
showConfirm: Function | null,
|
||||||
showAction: boolean | null,
|
showAction: boolean | null,
|
||||||
showShell: boolean | null
|
showShell: boolean | null
|
||||||
} => ({
|
} => ({
|
||||||
|
|||||||
11
frontend/src/types/api.d.ts
vendored
11
frontend/src/types/api.d.ts
vendored
@ -1,14 +1,13 @@
|
|||||||
type apiUrl = string // Can also be set as a path eg: "path1" | "path2"
|
type ApiUrl = string // Can also be set as a path eg: "path1" | "path2"
|
||||||
|
|
||||||
type resourcePath = string
|
type resourcePath = string
|
||||||
|
|
||||||
type apiMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH"
|
type ApiMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH"
|
||||||
|
|
||||||
// type apiContent = string | Blob | File
|
type ApiContent = Blob | File | Pick<ReadableStreamDefaultReader<any>, "read"> | ""
|
||||||
type apiContent = Blob | File | Pick<ReadableStreamDefaultReader<any>, "read"> | ""
|
|
||||||
|
|
||||||
interface apiOpts {
|
interface ApiOpts {
|
||||||
method?: apiMethod,
|
method?: ApiMethod,
|
||||||
headers?: object,
|
headers?: object,
|
||||||
body?: any
|
body?: any
|
||||||
}
|
}
|
||||||
|
|||||||
41
frontend/src/types/file.d.ts
vendored
41
frontend/src/types/file.d.ts
vendored
@ -1,5 +1,5 @@
|
|||||||
|
interface IFile {
|
||||||
interface file {
|
index?: number
|
||||||
name: string,
|
name: string,
|
||||||
modified: string,
|
modified: string,
|
||||||
path: string,
|
path: string,
|
||||||
@ -7,27 +7,32 @@ interface file {
|
|||||||
isDir: boolean,
|
isDir: boolean,
|
||||||
size: number,
|
size: number,
|
||||||
fullPath: string,
|
fullPath: string,
|
||||||
type: uploadType
|
type: uploadType,
|
||||||
|
items: IFile[]
|
||||||
|
token?: string,
|
||||||
|
hash: string,
|
||||||
|
url?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface item {
|
|
||||||
id: number,
|
|
||||||
path: string,
|
|
||||||
file: file,
|
|
||||||
url?: string,
|
|
||||||
dir?: boolean,
|
|
||||||
from?: string,
|
|
||||||
to?: string,
|
|
||||||
name?: string,
|
|
||||||
type?: uploadType
|
|
||||||
overwrite: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
type uploadType = "video" | "audio" | "image" | "pdf" | "text" | "blob"
|
type uploadType = "video" | "audio" | "image" | "pdf" | "text" | "blob"
|
||||||
|
|
||||||
interface req {
|
type req = {
|
||||||
isDir?: boolean
|
path: string
|
||||||
}
|
name: string
|
||||||
|
size: number
|
||||||
|
extension: string
|
||||||
|
modified: string
|
||||||
|
mode: number
|
||||||
|
isDir: boolean
|
||||||
|
isSymlink: boolean
|
||||||
|
type: string
|
||||||
|
url: string
|
||||||
|
hash: string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface uploads {
|
interface uploads {
|
||||||
[key: string]: upload
|
[key: string]: upload
|
||||||
|
|||||||
3
frontend/src/types/global.d.ts
vendored
3
frontend/src/types/global.d.ts
vendored
@ -5,4 +5,7 @@ declare global {
|
|||||||
FileBrowser: any;
|
FileBrowser: any;
|
||||||
grecaptcha: any
|
grecaptcha: any
|
||||||
}
|
}
|
||||||
|
interface HTMLAttributes extends HTMLAttributes {
|
||||||
|
title: any
|
||||||
|
}
|
||||||
}
|
}
|
||||||
6
frontend/src/types/layout.d.ts
vendored
6
frontend/src/types/layout.d.ts
vendored
@ -1,5 +1,5 @@
|
|||||||
interface LayoutValue {
|
interface LayoutValue {
|
||||||
prompt: boolean,
|
prompt: string,
|
||||||
confirm: boolean,
|
confirm: Function,
|
||||||
action: boolean,
|
action?: boolean,
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<header-bar v-if="error || req.type == null" showMenu showLogo />
|
<header-bar v-if="error || req?.type === null" showMenu showLogo />
|
||||||
|
|
||||||
<breadcrumbs base="/files" />
|
<breadcrumbs base="/files" />
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ export default {
|
|||||||
uploadError: "error",
|
uploadError: "error",
|
||||||
}),
|
}),
|
||||||
currentView() {
|
currentView() {
|
||||||
if (this.req.type == undefined) {
|
if (this.req?.type == undefined) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +129,6 @@ export default {
|
|||||||
let url = this.$route.path;
|
let url = this.$route.path;
|
||||||
if (url === "") url = "/";
|
if (url === "") url = "/";
|
||||||
if (url[0] !== "/") url = "/" + url;
|
if (url[0] !== "/") url = "/" + url;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await api.fetch(url);
|
const res = await api.fetch(url);
|
||||||
|
|
||||||
|
|||||||
@ -3,32 +3,18 @@
|
|||||||
<header-bar showMenu showLogo>
|
<header-bar showMenu showLogo>
|
||||||
<title />
|
<title />
|
||||||
|
|
||||||
<action
|
<action v-if="fileStore.selectedCount" icon="file_download" :label="$t('buttons.download')" @action="download"
|
||||||
v-if="selectedCount"
|
:counter="fileStore.selectedCount" />
|
||||||
icon="file_download"
|
<button v-if="isSingleFile()" class="action copy-clipboard" :data-clipboard-text="linkSelected()"
|
||||||
:label="$t('buttons.download')"
|
:aria-label="$t('buttons.copyDownloadLinkToClipboard')" :data-title="$t('buttons.copyDownloadLinkToClipboard')">
|
||||||
@action="download"
|
|
||||||
:counter="selectedCount"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
v-if="isSingleFile()"
|
|
||||||
class="action copy-clipboard"
|
|
||||||
:data-clipboard-text="linkSelected()"
|
|
||||||
:aria-label="$t('buttons.copyDownloadLinkToClipboard')"
|
|
||||||
:title="$t('buttons.copyDownloadLinkToClipboard')"
|
|
||||||
>
|
|
||||||
<i class="material-icons">content_paste</i>
|
<i class="material-icons">content_paste</i>
|
||||||
</button>
|
</button>
|
||||||
<action
|
<action icon="check_circle" :label="$t('buttons.selectMultiple')" @action="toggleMultipleSelection" />
|
||||||
icon="check_circle"
|
|
||||||
:label="$t('buttons.selectMultiple')"
|
|
||||||
@action="toggleMultipleSelection"
|
|
||||||
/>
|
|
||||||
</header-bar>
|
</header-bar>
|
||||||
|
|
||||||
<breadcrumbs :base="'/share/' + hash" />
|
<breadcrumbs :base="'/share/' + hash" />
|
||||||
|
|
||||||
<div v-if="loading">
|
<div v-if="layoutStore.loading">
|
||||||
<h2 class="message delayed">
|
<h2 class="message delayed">
|
||||||
<div class="spinner">
|
<div class="spinner">
|
||||||
<div class="bounce1"></div>
|
<div class="bounce1"></div>
|
||||||
@ -49,21 +35,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<input
|
<input v-focus type="password" :placeholder="$t('login.password')" v-model="password"
|
||||||
v-focus
|
@keyup.enter="fetchData" />
|
||||||
type="password"
|
|
||||||
:placeholder="$t('login.password')"
|
|
||||||
v-model="password"
|
|
||||||
@keyup.enter="fetchData"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<button
|
<button class="button button--flat" @click="fetchData" :aria-label="$t('buttons.submit')"
|
||||||
class="button button--flat"
|
:data-title="$t('buttons.submit')">
|
||||||
@click="fetchData"
|
|
||||||
:aria-label="$t('buttons.submit')"
|
|
||||||
:title="$t('buttons.submit')"
|
|
||||||
>
|
|
||||||
{{ $t("buttons.submit") }}
|
{{ $t("buttons.submit") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -71,14 +48,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<errors v-else :errorCode="error.status" />
|
<errors v-else :errorCode="error.status" />
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else-if="req !== null">
|
||||||
<div class="share">
|
<div class="share">
|
||||||
<div class="share__box share__box__info">
|
<div class="share__box share__box__info">
|
||||||
<div class="share__box__header">
|
<div class="share__box__header">
|
||||||
{{
|
{{
|
||||||
req.isDir
|
req.isDir
|
||||||
? $t("download.downloadFolder")
|
? $t("download.downloadFolder")
|
||||||
: $t("download.downloadFile")
|
: $t("download.downloadFile")
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
<div class="share__box__element share__box__center share__box__icon">
|
<div class="share__box__element share__box__center share__box__icon">
|
||||||
@ -87,7 +64,7 @@
|
|||||||
<div class="share__box__element">
|
<div class="share__box__element">
|
||||||
<strong>{{ $t("prompts.displayName") }}</strong> {{ req.name }}
|
<strong>{{ $t("prompts.displayName") }}</strong> {{ req.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="share__box__element" :title="modTime">
|
<div class="share__box__element" :data-title="modTime">
|
||||||
<strong>{{ $t("prompts.lastModified") }}:</strong> {{ humanTime }}
|
<strong>{{ $t("prompts.lastModified") }}:</strong> {{ humanTime }}
|
||||||
</div>
|
</div>
|
||||||
<div class="share__box__element">
|
<div class="share__box__element">
|
||||||
@ -96,19 +73,12 @@
|
|||||||
<div class="share__box__element share__box__center">
|
<div class="share__box__element share__box__center">
|
||||||
<a target="_blank" :href="link" class="button button--flat">
|
<a target="_blank" :href="link" class="button button--flat">
|
||||||
<div>
|
<div>
|
||||||
<i class="material-icons">file_download</i
|
<i class="material-icons">file_download</i>{{ $t("buttons.download") }}
|
||||||
>{{ $t("buttons.download") }}
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a target="_blank" :href="inlineLink" class="button button--flat" v-if="!req.isDir">
|
||||||
target="_blank"
|
|
||||||
:href="inlineLink"
|
|
||||||
class="button button--flat"
|
|
||||||
v-if="!req.isDir"
|
|
||||||
>
|
|
||||||
<div>
|
<div>
|
||||||
<i class="material-icons">open_in_new</i
|
<i class="material-icons">open_in_new</i>{{ $t("buttons.openFile") }}
|
||||||
>{{ $t("buttons.openFile") }}
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -116,56 +86,31 @@
|
|||||||
<qrcode-vue :value="link" :size="200" level="M"></qrcode-vue>
|
<qrcode-vue :value="link" :size="200" level="M"></qrcode-vue>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div v-if="req.isDir && req.items.length > 0" class="share__box share__box__items">
|
||||||
v-if="req.isDir && req.items.length > 0"
|
|
||||||
class="share__box share__box__items"
|
|
||||||
>
|
|
||||||
<div class="share__box__header" v-if="req.isDir">
|
<div class="share__box__header" v-if="req.isDir">
|
||||||
{{ $t("files.files") }}
|
{{ $t("files.files") }}
|
||||||
</div>
|
</div>
|
||||||
<div id="listing" class="list file-icons">
|
<div id="listing" class="list file-icons">
|
||||||
<item
|
<item v-for="item in req.items.slice(0, showLimit)" :key="base64(item.name)" v-bind:index="item.index"
|
||||||
v-for="item in req.items.slice(0, this.showLimit)"
|
v-bind:name="item.name" v-bind:isDir="item.isDir" v-bind:url="item.url" v-bind:modified="item.modified"
|
||||||
:key="base64(item.name)"
|
v-bind:type="item.type" v-bind:size="item.size" readOnly>
|
||||||
v-bind:index="item.index"
|
|
||||||
v-bind:name="item.name"
|
|
||||||
v-bind:isDir="item.isDir"
|
|
||||||
v-bind:url="item.url"
|
|
||||||
v-bind:modified="item.modified"
|
|
||||||
v-bind:type="item.type"
|
|
||||||
v-bind:size="item.size"
|
|
||||||
readOnly
|
|
||||||
>
|
|
||||||
</item>
|
</item>
|
||||||
<div
|
<div v-if="req.items.length > showLimit" class="item" @click="showLimit += 100">
|
||||||
v-if="req.items.length > showLimit"
|
|
||||||
class="item"
|
|
||||||
@click="showLimit += 100"
|
|
||||||
>
|
|
||||||
<div>
|
<div>
|
||||||
<p class="name">+ {{ req.items.length - showLimit }}</p>
|
<p class="name">+ {{ req.items.length - showLimit }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :class="{ active: multiple }" id="multiple-selection">
|
<div :class="{ active: fileStore.multiple }" id="multiple-selection">
|
||||||
<p>{{ $t("files.multipleSelectionEnabled") }}</p>
|
<p>{{ $t("files.multipleSelectionEnabled") }}</p>
|
||||||
<div
|
<div @click="() => (fileStore.multiple = false)" tabindex="0" role="button" :data-title="$t('files.clear')"
|
||||||
@click="() => (multiple = false)"
|
:aria-label="$t('files.clear')" class="action">
|
||||||
tabindex="0"
|
|
||||||
role="button"
|
|
||||||
:title="$t('files.clear')"
|
|
||||||
:aria-label="$t('files.clear')"
|
|
||||||
class="action"
|
|
||||||
>
|
|
||||||
<i class="material-icons">clear</i>
|
<i class="material-icons">clear</i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div v-else-if="req.isDir && req.items.length === 0" class="share__box share__box__items">
|
||||||
v-else-if="req.isDir && req.items.length === 0"
|
|
||||||
class="share__box share__box__items"
|
|
||||||
>
|
|
||||||
<h2 class="message">
|
<h2 class="message">
|
||||||
<i class="material-icons">sentiment_dissatisfied</i>
|
<i class="material-icons">sentiment_dissatisfied</i>
|
||||||
<span>{{ $t("files.lonely") }}</span>
|
<span>{{ $t("files.lonely") }}</span>
|
||||||
@ -176,9 +121,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { mapState, mapActions, mapWritableState } from "pinia";
|
import { pub as api, files } from "@/api";
|
||||||
import { pub as api } from "@/api";
|
|
||||||
import { filesize } from "filesize";
|
import { filesize } from "filesize";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { Base64 } from "js-base64";
|
import { Base64 } from "js-base64";
|
||||||
@ -192,171 +136,160 @@ import Item from "@/components/files/ListingItem.vue";
|
|||||||
import Clipboard from "clipboard";
|
import Clipboard from "clipboard";
|
||||||
import { useFileStore } from "@/stores/file";
|
import { useFileStore } from "@/stores/file";
|
||||||
import { useLayoutStore } from "@/stores/layout";
|
import { useLayoutStore } from "@/stores/layout";
|
||||||
|
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
export default {
|
const error = ref<null | any>(null)
|
||||||
name: "share",
|
const showLimit = ref<number>(100)
|
||||||
components: {
|
const password = ref<string>("")
|
||||||
HeaderBar,
|
const attemptedPasswordLogin = ref<boolean>(false)
|
||||||
Action,
|
const hash = ref<any>(null)
|
||||||
Breadcrumbs,
|
const token = ref<any>(null)
|
||||||
Item,
|
const clip = ref<any>(null)
|
||||||
QrcodeVue,
|
|
||||||
Errors,
|
|
||||||
},
|
|
||||||
data: () => ({
|
|
||||||
error: null,
|
|
||||||
showLimit: 100,
|
|
||||||
password: "",
|
|
||||||
attemptedPasswordLogin: false,
|
|
||||||
hash: null,
|
|
||||||
token: null,
|
|
||||||
clip: null,
|
|
||||||
}),
|
|
||||||
inject: ["$showSuccess"],
|
|
||||||
watch: {
|
|
||||||
$route: function () {
|
|
||||||
this.showLimit = 100;
|
|
||||||
|
|
||||||
this.fetchData();
|
const route = useRoute()
|
||||||
},
|
const fileStore = useFileStore()
|
||||||
},
|
const layoutStore = useLayoutStore()
|
||||||
created: async function () {
|
|
||||||
const hash = this.$route.params.path[0];
|
watch(route, (newValue, oldValue) => {
|
||||||
this.hash = hash;
|
showLimit.value = 100
|
||||||
await this.fetchData();
|
fetchData();
|
||||||
},
|
})
|
||||||
mounted() {
|
|
||||||
window.addEventListener("keydown", this.keyEvent);
|
const req = computed(() => fileStore.req)
|
||||||
this.clip = new Clipboard(".copy-clipboard");
|
|
||||||
this.clip.on("success", () => {
|
// inject: ["$showSuccess"],
|
||||||
this.$showSuccess(this.$t("success.linkCopied"));
|
|
||||||
});
|
|
||||||
},
|
// Define computes
|
||||||
beforeUnmount() {
|
|
||||||
window.removeEventListener("keydown", this.keyEvent);
|
const icon = computed(() => {
|
||||||
this.clip.destroy();
|
if (req.value === null) return "insert_drive_file"
|
||||||
},
|
if (req.value.isDir) return "folder";
|
||||||
computed: {
|
if (req.value.type === "image") return "insert_photo";
|
||||||
...mapState(useFileStore, ["req", "selectedCount"]),
|
if (req.value.type === "audio") return "volume_up";
|
||||||
...mapWritableState(useFileStore, ["reload", "multiple", "selected"]),
|
if (req.value.type === "video") return "movie";
|
||||||
...mapWritableState(useLayoutStore, ["loading"]),
|
return "insert_drive_file";
|
||||||
icon: function () {
|
})
|
||||||
if (this.req.isDir) return "folder";
|
|
||||||
if (this.req.type === "image") return "insert_photo";
|
const link = computed(() => (req.value ? api.getDownloadURL(req.value) : ""))
|
||||||
if (this.req.type === "audio") return "volume_up";
|
const inlineLink = computed(() => (req.value ? api.getDownloadURL(req.value, true) : ""))
|
||||||
if (this.req.type === "video") return "movie";
|
const humanSize = computed(() => {
|
||||||
return "insert_drive_file";
|
if (req.value) {
|
||||||
},
|
return (req.value.isDir ? req.value.items.length : filesize(req.value.size ?? 0))
|
||||||
link: function () {
|
} else {
|
||||||
return api.getDownloadURL(this.req);
|
return ""
|
||||||
},
|
}
|
||||||
inlineLink: function () {
|
})
|
||||||
return api.getDownloadURL(this.req, true);
|
const humanTime = computed(() => dayjs(req.value?.modified).fromNow())
|
||||||
},
|
const modTime = computed(() => (req.value ? new Date(Date.parse(req.value.modified)).toLocaleString() : new Date()))
|
||||||
humanSize: function () {
|
|
||||||
if (this.req.isDir) {
|
// Functions
|
||||||
return this.req.items.length;
|
const base64 = (name: any) => Base64.encodeURI(name);
|
||||||
|
const fetchData = async () => {
|
||||||
|
fileStore.reload = false;
|
||||||
|
fileStore.selected = [];
|
||||||
|
fileStore.multiple = false;
|
||||||
|
// fileStore.closeHovers();
|
||||||
|
|
||||||
|
// Set loading to true and reset the error.
|
||||||
|
layoutStore.loading = true;
|
||||||
|
error.value = null;
|
||||||
|
if (password.value !== "") {
|
||||||
|
attemptedPasswordLogin.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let url = route.path;
|
||||||
|
if (url === "") url = "/";
|
||||||
|
if (url[0] !== "/") url = "/" + url;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let file = await api.fetch(url, password.value);
|
||||||
|
file.hash = hash.value;
|
||||||
|
|
||||||
|
|
||||||
|
token.value = file.token || "";
|
||||||
|
|
||||||
|
fileStore.updateRequest(file);
|
||||||
|
document.title = `${file.name} - ${document.title}`;
|
||||||
|
} catch (e) {
|
||||||
|
error.value = e;
|
||||||
|
} finally {
|
||||||
|
layoutStore.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyEvent = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
// If we're on a listing, unselect all
|
||||||
|
// files and folders.
|
||||||
|
if (fileStore.selectedCount > 0) {
|
||||||
|
fileStore.selected = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleMultipleSelection = () => {
|
||||||
|
// toggle
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSingleFile = () => fileStore.selectedCount === 1 && !req.value?.items[fileStore.selected[0]].isDir
|
||||||
|
|
||||||
|
const download = () => {
|
||||||
|
if (isSingleFile()) {
|
||||||
|
api.download(
|
||||||
|
null,
|
||||||
|
hash.value,
|
||||||
|
token.value,
|
||||||
|
req.value?.items[fileStore.selected[0]].path
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
layoutStore.showHover({
|
||||||
|
prompt: "download",
|
||||||
|
confirm: (format: any) => {
|
||||||
|
if (req.value === null) return false
|
||||||
|
layoutStore.closeHovers();
|
||||||
|
|
||||||
|
let files: string[] = [];
|
||||||
|
|
||||||
|
for (let i of fileStore.selected) {
|
||||||
|
files.push(req.value.items[i].path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return filesize(this.req.size);
|
// @ts-ignore
|
||||||
|
api.download(format, hash.value, token.value, ...files);
|
||||||
},
|
},
|
||||||
humanTime: function () {
|
});
|
||||||
return dayjs(this.req.modified).fromNow();
|
}
|
||||||
},
|
|
||||||
modTime: function () {
|
|
||||||
return new Date(Date.parse(this.req.modified)).toLocaleString();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions(useFileStore, ["updateRequest", "toggleMultiple"]),
|
|
||||||
...mapActions(useLayoutStore, ["showHover", "closeHovers"]),
|
|
||||||
base64: function (name) {
|
|
||||||
return Base64.encodeURI(name);
|
|
||||||
},
|
|
||||||
fetchData: async function () {
|
|
||||||
// Reset view information.
|
|
||||||
this.reload = false;
|
|
||||||
this.selected = [];
|
|
||||||
this.multiple = false;
|
|
||||||
this.closeHovers();
|
|
||||||
|
|
||||||
// Set loading to true and reset the error.
|
const linkSelected = () => {
|
||||||
this.loading = true;
|
return isSingleFile() && req.value
|
||||||
this.error = null;
|
// @ts-ignore
|
||||||
|
? api.getDownloadURL({
|
||||||
|
hash: hash.value,
|
||||||
|
path: req.value.items[fileStore.selected[0]].path,
|
||||||
|
})
|
||||||
|
: "";
|
||||||
|
}
|
||||||
|
|
||||||
if (this.password !== "") {
|
|
||||||
this.attemptedPasswordLogin = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let url = this.$route.path;
|
onMounted(async () => {
|
||||||
if (url === "") url = "/";
|
// Created
|
||||||
if (url[0] !== "/") url = "/" + url;
|
hash.value = route.params.path[0];
|
||||||
|
await fetchData();
|
||||||
|
|
||||||
try {
|
|
||||||
let file = await api.fetch(url, this.password);
|
|
||||||
file.hash = this.hash;
|
|
||||||
|
|
||||||
this.token = file.token || "";
|
// window.addEventListener("keydown", this.keyEvent);
|
||||||
|
// this.clip = new Clipboard(".copy-clipboard");
|
||||||
|
// this.clip.on("success", () => {
|
||||||
|
// this.$showSuccess(this.$t("success.linkCopied"));
|
||||||
|
// });
|
||||||
|
})
|
||||||
|
|
||||||
this.updateRequest(file);
|
onBeforeUnmount(() => {
|
||||||
document.title = `${file.name} - ${document.title}`;
|
// window.removeEventListener("keydown", this.keyEvent);
|
||||||
} catch (e) {
|
// this.clip.destroy();
|
||||||
this.error = e;
|
})
|
||||||
} finally {
|
|
||||||
this.loading = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
keyEvent(event) {
|
|
||||||
if (event.key === "Escape") {
|
|
||||||
// If we're on a listing, unselect all
|
|
||||||
// files and folders.
|
|
||||||
if (this.selectedCount > 0) {
|
|
||||||
this.selected = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toggleMultipleSelection() {
|
|
||||||
this.toggleMultiple();
|
|
||||||
},
|
|
||||||
isSingleFile: function () {
|
|
||||||
return (
|
|
||||||
this.selectedCount === 1 && !this.req.items[this.selected[0]].isDir
|
|
||||||
);
|
|
||||||
},
|
|
||||||
download() {
|
|
||||||
if (this.isSingleFile()) {
|
|
||||||
api.download(
|
|
||||||
null,
|
|
||||||
this.hash,
|
|
||||||
this.token,
|
|
||||||
this.req.items[this.selected[0]].path
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.showHover({
|
|
||||||
prompt: "download",
|
|
||||||
confirm: (format) => {
|
|
||||||
this.closeHovers();
|
|
||||||
|
|
||||||
let files = [];
|
|
||||||
|
|
||||||
for (let i of this.selected) {
|
|
||||||
files.push(this.req.items[i].path);
|
|
||||||
}
|
|
||||||
|
|
||||||
api.download(format, this.hash, this.token, ...files);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
linkSelected: function () {
|
|
||||||
return this.isSingleFile()
|
|
||||||
? api.getDownloadURL({
|
|
||||||
hash: this.hash,
|
|
||||||
path: this.req.items[this.selected[0]].path,
|
|
||||||
})
|
|
||||||
: "";
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
@ -616,7 +616,7 @@ export default {
|
|||||||
if (!items) return;
|
if (!items) return;
|
||||||
|
|
||||||
let columns = Math.floor(
|
let columns = Math.floor(
|
||||||
document.querySelector("main").offsetWidth / this.columnWidth
|
document.querySelector("main")?.offsetWidth / this.columnWidth
|
||||||
);
|
);
|
||||||
if (columns === 0) columns = 1;
|
if (columns === 0) columns = 1;
|
||||||
items.style.width = `calc(${100 / columns}% - 1em)`;
|
items.style.width = `calc(${100 / columns}% - 1em)`;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user