feat(web):add fetch button & modal
parent
7da134980e
commit
11ef778381
|
@ -0,0 +1,21 @@
|
|||
import { fetchURL, removePrefix } from "@/api/utils.ts";
|
||||
|
||||
export async function fetchUrlFile(
|
||||
savePath: string,
|
||||
saveName: string,
|
||||
fetchUrl: string,
|
||||
opts: ApiOpts
|
||||
) {
|
||||
const res = await fetchURL(`/api/download/`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
url: fetchUrl,
|
||||
pathname: removePrefix(savePath),
|
||||
filename: saveName,
|
||||
}),
|
||||
...opts,
|
||||
});
|
||||
const taskID = await res.text();
|
||||
console.log("on create download task: ", taskID);
|
||||
return taskID;
|
||||
}
|
|
@ -3,7 +3,8 @@ import * as share from "./share";
|
|||
import * as users from "./users";
|
||||
import * as settings from "./settings";
|
||||
import * as pub from "./pub";
|
||||
import * as fetcher from "./fetch.tsx";
|
||||
import search from "./search";
|
||||
import commands from "./commands";
|
||||
|
||||
export { files, share, users, settings, pub, commands, search };
|
||||
export { files, share, users, settings, pub, fetcher, commands, search };
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
<template>
|
||||
<div class="card floating" id="fetch">
|
||||
<div class="card-title">
|
||||
<h2>{{ t("prompts.fetch") }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ t("prompts.fetchUrl") }}</p>
|
||||
<input
|
||||
id="focus-prompt"
|
||||
class="input input--block"
|
||||
type="text"
|
||||
@keyup.enter="submit"
|
||||
v-model.trim="fetchUrl"
|
||||
tabindex="1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<p>{{ t("prompts.fetchSaveName") }}</p>
|
||||
<input
|
||||
class="input input--block"
|
||||
type="text"
|
||||
@keyup.enter="submit"
|
||||
v-model.trim="saveName"
|
||||
tabindex="2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
<button
|
||||
class="button button--flat button--grey"
|
||||
@click="layoutStore.closeHovers"
|
||||
:aria-label="t('buttons.cancel')"
|
||||
:title="t('buttons.cancel')"
|
||||
tabindex="3"
|
||||
>
|
||||
{{ t("buttons.cancel") }}
|
||||
</button>
|
||||
<button
|
||||
class="button button--flat"
|
||||
:aria-label="t('buttons.create')"
|
||||
:title="t('buttons.create')"
|
||||
@click="submit"
|
||||
tabindex="2"
|
||||
>
|
||||
{{ t("buttons.create") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useLayoutStore } from "@/stores/layout.ts";
|
||||
import { useFileStore } from "@/stores/file.ts";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { inject, ref, watch } from "vue";
|
||||
import url from "@/utils/url.ts";
|
||||
import { fetcher as api } from "@/api";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
const $showError = inject<IToastError>("$showError")!;
|
||||
|
||||
const layoutStore = useLayoutStore();
|
||||
const fileStore = useFileStore();
|
||||
const route = useRoute();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const fetchUrl = ref<string>("");
|
||||
const saveName = ref<string>("");
|
||||
const taskID = ref<string>("");
|
||||
|
||||
watch(fetchUrl, (value) => {
|
||||
try {
|
||||
if (saveName.value !== "") return;
|
||||
if (!(value.startsWith("http://") || value.startsWith("https://"))) return;
|
||||
saveName.value = new URL(value).pathname.split("/").pop() ?? "";
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e) {}
|
||||
});
|
||||
|
||||
const submit = async (event: Event) => {
|
||||
event.preventDefault();
|
||||
if (
|
||||
!(
|
||||
fetchUrl.value.startsWith("http://") ||
|
||||
fetchUrl.value.startsWith("https://")
|
||||
)
|
||||
)
|
||||
return;
|
||||
if (saveName.value === "") return;
|
||||
|
||||
// Build the path of the new directory.
|
||||
let uri = fileStore.isFiles ? route.path + "/" : "/";
|
||||
|
||||
if (!fileStore.isListing) {
|
||||
uri = url.removeLastDir(uri) + "/";
|
||||
}
|
||||
|
||||
try {
|
||||
const createdTaskID = await api.fetchUrlFile(
|
||||
uri,
|
||||
saveName.value,
|
||||
fetchUrl.value,
|
||||
{}
|
||||
);
|
||||
if (createdTaskID) {
|
||||
taskID.value = createdTaskID;
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
$showError(e);
|
||||
}
|
||||
}
|
||||
layoutStore.closeHovers();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -25,6 +25,7 @@ import Share from "./Share.vue";
|
|||
import ShareDelete from "./ShareDelete.vue";
|
||||
import Upload from "./Upload.vue";
|
||||
import DiscardEditorChanges from "./DiscardEditorChanges.vue";
|
||||
import Fetch from "@/components/prompts/Fetch.vue";
|
||||
|
||||
const layoutStore = useLayoutStore();
|
||||
|
||||
|
@ -47,6 +48,7 @@ const components = new Map<string, any>([
|
|||
["share-delete", ShareDelete],
|
||||
["deleteUser", DeleteUser],
|
||||
["discardEditorChanges", DiscardEditorChanges],
|
||||
["fetch", Fetch],
|
||||
]);
|
||||
|
||||
watch(currentPromptName, (newValue) => {
|
||||
|
|
|
@ -58,6 +58,12 @@
|
|||
:label="t('buttons.switchView')"
|
||||
@action="switchView"
|
||||
/>
|
||||
<action
|
||||
v-if="headerButtons.fetch"
|
||||
icon="cloud_download"
|
||||
:label="t('buttons.fetch')"
|
||||
@action="fetch"
|
||||
/>
|
||||
<action
|
||||
v-if="headerButtons.download"
|
||||
icon="file_download"
|
||||
|
@ -281,7 +287,7 @@ import { useClipboardStore } from "@/stores/clipboard";
|
|||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
|
||||
import { users, files as api } from "@/api";
|
||||
import { files as api, users } from "@/api";
|
||||
import { enableExec } from "@/utils/constants";
|
||||
import * as upload from "@/utils/upload";
|
||||
import css from "@/utils/css";
|
||||
|
@ -292,15 +298,7 @@ import HeaderBar from "@/components/header/HeaderBar.vue";
|
|||
import Action from "@/components/header/Action.vue";
|
||||
import Search from "@/components/Search.vue";
|
||||
import Item from "@/components/files/ListingItem.vue";
|
||||
import {
|
||||
computed,
|
||||
inject,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
watch,
|
||||
} from "vue";
|
||||
import { computed, inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { storeToRefs } from "pinia";
|
||||
|
@ -327,19 +325,19 @@ const { t } = useI18n();
|
|||
const listing = ref<HTMLElement | null>(null);
|
||||
|
||||
const nameSorted = computed(() =>
|
||||
fileStore.req ? fileStore.req.sorting.by === "name" : false
|
||||
fileStore.req ? fileStore.req.sorting.by === "name" : false,
|
||||
);
|
||||
|
||||
const sizeSorted = computed(() =>
|
||||
fileStore.req ? fileStore.req.sorting.by === "size" : false
|
||||
fileStore.req ? fileStore.req.sorting.by === "size" : false,
|
||||
);
|
||||
|
||||
const modifiedSorted = computed(() =>
|
||||
fileStore.req ? fileStore.req.sorting.by === "modified" : false
|
||||
fileStore.req ? fileStore.req.sorting.by === "modified" : false,
|
||||
);
|
||||
|
||||
const ascOrdered = computed(() =>
|
||||
fileStore.req ? fileStore.req.sorting.asc : false
|
||||
fileStore.req ? fileStore.req.sorting.asc : false,
|
||||
);
|
||||
|
||||
const dirs = computed(() => items.value.dirs.slice(0, showLimit.value));
|
||||
|
@ -412,6 +410,7 @@ const headerButtons = computed(() => {
|
|||
share: fileStore.selectedCount === 1 && authStore.user?.perm.share,
|
||||
move: fileStore.selectedCount > 0 && authStore.user?.perm.rename,
|
||||
copy: fileStore.selectedCount > 0 && authStore.user?.perm.create,
|
||||
fetch: authStore.user?.perm.create,
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -644,7 +643,7 @@ const colunmsResize = () => {
|
|||
if (items_ === null) return;
|
||||
|
||||
let columns = Math.floor(
|
||||
(document.querySelector("main")?.offsetWidth ?? 0) / columnWidth.value
|
||||
(document.querySelector("main")?.offsetWidth ?? 0) / columnWidth.value,
|
||||
);
|
||||
if (columns === 0) columns = 1;
|
||||
items_.style.width = `calc(${100 / columns}% - 1em)`;
|
||||
|
@ -854,6 +853,16 @@ const windowsResize = throttle(() => {
|
|||
fillWindow();
|
||||
}, 100);
|
||||
|
||||
const fetch = () => {
|
||||
layoutStore.showHover({
|
||||
prompt: "fetch",
|
||||
confirm: (format: any) => {
|
||||
layoutStore.closeHovers();
|
||||
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const download = () => {
|
||||
if (fileStore.req === null) return;
|
||||
|
||||
|
@ -942,7 +951,7 @@ const fillWindow = (fit = false) => {
|
|||
|
||||
// Quantity of items needed to fill 2x of the window height
|
||||
const showQuantity = Math.ceil(
|
||||
(windowHeight + windowHeight * 2) / itemWeight.value
|
||||
(windowHeight + windowHeight * 2) / itemWeight.value,
|
||||
);
|
||||
|
||||
// Less items to display than current
|
||||
|
|
Loading…
Reference in New Issue