mirror of
https://github.com/wukko/cobalt.git
synced 2025-05-06 16:54:27 +02:00
web/DownloadButton: extract api interaction logic into a lib
download button state is now stored, well, in a state
This commit is contained in:
parent
1c34d2daff
commit
91f5d63b93
@ -11,6 +11,7 @@
|
|||||||
import dialogs from "$lib/state/dialogs";
|
import dialogs from "$lib/state/dialogs";
|
||||||
import { link } from "$lib/state/omnibox";
|
import { link } from "$lib/state/omnibox";
|
||||||
import { updateSetting } from "$lib/state/settings";
|
import { updateSetting } from "$lib/state/settings";
|
||||||
|
import { savingHandler } from "$lib/api/saving-handler";
|
||||||
import { pasteLinkFromClipboard } from "$lib/clipboard";
|
import { pasteLinkFromClipboard } from "$lib/clipboard";
|
||||||
import { turnstileEnabled, turnstileSolved } from "$lib/state/turnstile";
|
import { turnstileEnabled, turnstileSolved } from "$lib/state/turnstile";
|
||||||
|
|
||||||
@ -75,7 +76,7 @@
|
|||||||
|
|
||||||
if (!isBotCheckOngoing) {
|
if (!isBotCheckOngoing) {
|
||||||
await tick(); // wait for button to render
|
await tick(); // wait for button to render
|
||||||
downloadButton.download($link);
|
savingHandler($link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -94,7 +95,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (e.key === "Enter" && validLink($link) && isFocused) {
|
if (e.key === "Enter" && validLink($link) && isFocused) {
|
||||||
downloadButton.download($link);
|
savingHandler($link);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (["Escape", "Clear"].includes(e.key) && isFocused) {
|
if (["Escape", "Clear"].includes(e.key) && isFocused) {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import "@fontsource-variable/noto-sans-mono";
|
import "@fontsource-variable/noto-sans-mono";
|
||||||
|
|
||||||
import API from "$lib/api/api";
|
import { onDestroy } from "svelte";
|
||||||
import { t } from "$lib/i18n/translations";
|
import { t } from "$lib/i18n/translations";
|
||||||
import { createDialog } from "$lib/state/dialogs";
|
import { savingHandler } from "$lib/api/saving-handler";
|
||||||
import { downloadFile } from "$lib/download";
|
import { downloadButtonState } from "$lib/state/omnibox";
|
||||||
|
|
||||||
import type { DialogInfo } from "$lib/types/dialog";
|
import type { CobaltDownloadButtonState } from "$lib/types/omnibox";
|
||||||
|
|
||||||
export let url: string;
|
export let url: string;
|
||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
@ -15,22 +15,9 @@
|
|||||||
$: buttonText = ">>";
|
$: buttonText = ">>";
|
||||||
$: buttonAltText = $t("a11y.save.download");
|
$: buttonAltText = $t("a11y.save.download");
|
||||||
|
|
||||||
let defaultErrorPopup: DialogInfo = {
|
|
||||||
id: "save-error",
|
|
||||||
type: "small",
|
|
||||||
meowbalt: "error",
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: $t("button.gotit"),
|
|
||||||
main: true,
|
|
||||||
action: () => {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
type DownloadButtonState = "idle" | "think" | "check" | "done" | "error";
|
type DownloadButtonState = "idle" | "think" | "check" | "done" | "error";
|
||||||
|
|
||||||
const changeDownloadButton = (state: DownloadButtonState) => {
|
const unsubscribe = downloadButtonState.subscribe((state: CobaltDownloadButtonState) => {
|
||||||
disabled = state !== "idle";
|
disabled = state !== "idle";
|
||||||
loading = state === "think" || state === "check";
|
loading = state === "think" || state === "check";
|
||||||
|
|
||||||
@ -56,107 +43,17 @@
|
|||||||
// transition back to idle after some period of time.
|
// transition back to idle after some period of time.
|
||||||
const final: DownloadButtonState[] = ["done", "error"];
|
const final: DownloadButtonState[] = ["done", "error"];
|
||||||
if (final.includes(state)) {
|
if (final.includes(state)) {
|
||||||
setTimeout(() => changeDownloadButton("idle"), 1500);
|
setTimeout(() => downloadButtonState.set("idle"), 1500);
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
export const download = async (link: string) => {
|
onDestroy(() => unsubscribe());
|
||||||
changeDownloadButton("think");
|
|
||||||
|
|
||||||
const response = await API.request(link);
|
|
||||||
|
|
||||||
if (!response) {
|
|
||||||
changeDownloadButton("error");
|
|
||||||
|
|
||||||
return createDialog({
|
|
||||||
...defaultErrorPopup,
|
|
||||||
bodyText: $t("error.api.unreachable"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.status === "error") {
|
|
||||||
changeDownloadButton("error");
|
|
||||||
|
|
||||||
return createDialog({
|
|
||||||
...defaultErrorPopup,
|
|
||||||
bodyText: $t(response.error.code, response?.error?.context),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.status === "redirect") {
|
|
||||||
changeDownloadButton("done");
|
|
||||||
|
|
||||||
return downloadFile({
|
|
||||||
url: response.url,
|
|
||||||
urlType: "redirect",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.status === "tunnel") {
|
|
||||||
changeDownloadButton("check");
|
|
||||||
|
|
||||||
const probeResult = await API.probeCobaltTunnel(response.url);
|
|
||||||
|
|
||||||
if (probeResult === 200) {
|
|
||||||
changeDownloadButton("done");
|
|
||||||
|
|
||||||
return downloadFile({
|
|
||||||
url: response.url,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
changeDownloadButton("error");
|
|
||||||
|
|
||||||
return createDialog({
|
|
||||||
...defaultErrorPopup,
|
|
||||||
bodyText: $t("error.tunnel.probe"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.status === "picker") {
|
|
||||||
changeDownloadButton("done");
|
|
||||||
const buttons = [
|
|
||||||
{
|
|
||||||
text: $t("button.done"),
|
|
||||||
main: true,
|
|
||||||
action: () => {},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
if (response.audio) {
|
|
||||||
const pickerAudio = response.audio;
|
|
||||||
buttons.unshift({
|
|
||||||
text: $t("button.download.audio"),
|
|
||||||
main: false,
|
|
||||||
action: () => {
|
|
||||||
downloadFile({
|
|
||||||
url: pickerAudio,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return createDialog({
|
|
||||||
id: "download-picker",
|
|
||||||
type: "picker",
|
|
||||||
items: response.picker,
|
|
||||||
buttons,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
changeDownloadButton("error");
|
|
||||||
|
|
||||||
return createDialog({
|
|
||||||
...defaultErrorPopup,
|
|
||||||
bodyText: $t("error.api.unknown_response"),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
id="download-button"
|
id="download-button"
|
||||||
{disabled}
|
{disabled}
|
||||||
on:click={() => download(url)}
|
on:click={() => savingHandler(url)}
|
||||||
aria-label={buttonAltText}
|
aria-label={buttonAltText}
|
||||||
>
|
>
|
||||||
<span id="download-state">{buttonText}</span>
|
<span id="download-state">{buttonText}</span>
|
||||||
|
123
web/src/lib/api/saving-handler.ts
Normal file
123
web/src/lib/api/saving-handler.ts
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import API from "$lib/api/api";
|
||||||
|
|
||||||
|
import { get } from "svelte/store";
|
||||||
|
import { t } from "$lib/i18n/translations";
|
||||||
|
import { downloadFile } from "$lib/download";
|
||||||
|
import { createDialog } from "$lib/state/dialogs";
|
||||||
|
import { downloadButtonState } from "$lib/state/omnibox";
|
||||||
|
|
||||||
|
import type { DialogInfo } from "$lib/types/dialog";
|
||||||
|
|
||||||
|
const defaultErrorPopup: DialogInfo = {
|
||||||
|
id: "save-error",
|
||||||
|
type: "small",
|
||||||
|
meowbalt: "error",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const savingHandler = async (link: string) => {
|
||||||
|
downloadButtonState.set("think");
|
||||||
|
|
||||||
|
const errorButtons = [
|
||||||
|
{
|
||||||
|
text: get(t)("button.gotit"),
|
||||||
|
main: true,
|
||||||
|
action: () => { },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const response = await API.request(link);
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
downloadButtonState.set("error");
|
||||||
|
|
||||||
|
return createDialog({
|
||||||
|
...defaultErrorPopup,
|
||||||
|
buttons: errorButtons,
|
||||||
|
bodyText: get(t)("error.api.unreachable"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status === "error") {
|
||||||
|
downloadButtonState.set("error");
|
||||||
|
|
||||||
|
return createDialog({
|
||||||
|
...defaultErrorPopup,
|
||||||
|
buttons: errorButtons,
|
||||||
|
bodyText: get(t)(response.error.code, response?.error?.context),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status === "redirect") {
|
||||||
|
downloadButtonState.set("done");
|
||||||
|
|
||||||
|
return downloadFile({
|
||||||
|
url: response.url,
|
||||||
|
urlType: "redirect",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status === "tunnel") {
|
||||||
|
downloadButtonState.set("check");
|
||||||
|
|
||||||
|
const probeResult = await API.probeCobaltTunnel(response.url);
|
||||||
|
|
||||||
|
if (probeResult === 200) {
|
||||||
|
downloadButtonState.set("done");
|
||||||
|
|
||||||
|
return downloadFile({
|
||||||
|
url: response.url,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
downloadButtonState.set("error");
|
||||||
|
|
||||||
|
return createDialog({
|
||||||
|
...defaultErrorPopup,
|
||||||
|
buttons: errorButtons,
|
||||||
|
bodyText: get(t)("error.tunnel.probe"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status === "local-processing") {
|
||||||
|
// TODO: actual implementation
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status === "picker") {
|
||||||
|
downloadButtonState.set("done");
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
text: get(t)("button.done"),
|
||||||
|
main: true,
|
||||||
|
action: () => { },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (response.audio) {
|
||||||
|
const pickerAudio = response.audio;
|
||||||
|
buttons.unshift({
|
||||||
|
text: get(t)("button.download.audio"),
|
||||||
|
main: false,
|
||||||
|
action: () => {
|
||||||
|
downloadFile({
|
||||||
|
url: pickerAudio,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return createDialog({
|
||||||
|
id: "download-picker",
|
||||||
|
type: "picker",
|
||||||
|
items: response.picker,
|
||||||
|
buttons,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadButtonState.set("error");
|
||||||
|
return createDialog({
|
||||||
|
...defaultErrorPopup,
|
||||||
|
buttons: errorButtons,
|
||||||
|
bodyText: get(t)("error.api.unknown_response"),
|
||||||
|
});
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
|
import type { CobaltDownloadButtonState } from "$lib/types/omnibox";
|
||||||
|
|
||||||
export const link = writable("");
|
export const link = writable("");
|
||||||
|
export const downloadButtonState = writable<CobaltDownloadButtonState>("idle");
|
||||||
|
1
web/src/lib/types/omnibox.ts
Normal file
1
web/src/lib/types/omnibox.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type CobaltDownloadButtonState = "idle" | "think" | "check" | "done" | "error";
|
Loading…
x
Reference in New Issue
Block a user