From 4232c3437bf1292b470ac583fe20582e879dfd2b Mon Sep 17 00:00:00 2001 From: wukko <me@wukko.me> Date: Sat, 13 Jul 2024 19:15:43 +0600 Subject: [PATCH] web: dialog system & basic small dialog --- web/i18n/en/general.json | 3 +- web/src/components/dialog/DialogHolder.svelte | 28 ++++++++++ web/src/components/dialog/SmallDialog.svelte | 55 +++++++++++++++++++ .../save/buttons/DownloadButton.svelte | 31 ++++++++++- web/src/lib/dialogs.ts | 23 ++++++++ web/src/lib/types/dialog.ts | 14 +++++ web/src/routes/+layout.svelte | 11 ++++ 7 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 web/src/components/dialog/DialogHolder.svelte create mode 100644 web/src/components/dialog/SmallDialog.svelte create mode 100644 web/src/lib/dialogs.ts create mode 100644 web/src/lib/types/dialog.ts diff --git a/web/i18n/en/general.json b/web/i18n/en/general.json index 6ea5e8a0..5a371ff8 100644 --- a/web/i18n/en/general.json +++ b/web/i18n/en/general.json @@ -1,3 +1,4 @@ { - "cobalt": "cobalt" + "cobalt": "cobalt", + "gotit": "got it" } diff --git a/web/src/components/dialog/DialogHolder.svelte b/web/src/components/dialog/DialogHolder.svelte new file mode 100644 index 00000000..b5496d65 --- /dev/null +++ b/web/src/components/dialog/DialogHolder.svelte @@ -0,0 +1,28 @@ +<script lang="ts"> + import SmallDialog from "./SmallDialog.svelte"; + import dialogs from "$lib/dialogs"; +</script> + +<div id="dialog-holder" aria-hidden="true"> + {#each $dialogs as dialog} + {#if dialog.type === "small"} + <SmallDialog + id={dialog.id} + title={dialog.title} + bodyText={dialog.bodyText} + bodySubText={dialog.bodySubText} + buttons={dialog.buttons} + /> + {/if} + {/each} +</div> + +<style> + #dialog-holder { + position: absolute; + padding-top: env(safe-area-inset-bottom); + height: calc(100%); + width: 100%; + pointer-events: none; + } +</style> diff --git a/web/src/components/dialog/SmallDialog.svelte b/web/src/components/dialog/SmallDialog.svelte new file mode 100644 index 00000000..8ee30862 --- /dev/null +++ b/web/src/components/dialog/SmallDialog.svelte @@ -0,0 +1,55 @@ +<script lang="ts"> + import { killDialog } from "$lib/dialogs"; + import type { DialogButton } from "$lib/types/dialog"; + + export let id: string; + export let title: string = ""; + export let bodyText: string = ""; + export let bodySubText: string = ""; + export let buttons: DialogButton[]; + + let dialogParent: HTMLDialogElement; + + const close = () => { + if (dialogParent) { + dialogParent.close(); + killDialog(); + } + } + + $: if (dialogParent) { + dialogParent.showModal(); + } +</script> + +<dialog id="dialog-{id}" bind:this={dialogParent} class="small-dialog"> + <div class="popup-header"> + <h2>{title}</h2> + </div> + <div class="popup-body"> + {bodyText} + {#if bodySubText} + <div class="subtext">{bodySubText}</div> + {/if} + </div> + <div class="popup-buttons"> + {#each buttons as button} + <button + on:click={ + (async() => { + await button.action(); + close(); + }) + } + > + {button.text} + </button> + {/each} + </div> +</dialog> + +<style> + .small-dialog { + max-width: 375px; + } +</style> diff --git a/web/src/components/save/buttons/DownloadButton.svelte b/web/src/components/save/buttons/DownloadButton.svelte index 96510462..718a9041 100644 --- a/web/src/components/save/buttons/DownloadButton.svelte +++ b/web/src/components/save/buttons/DownloadButton.svelte @@ -3,14 +3,30 @@ import API from "$lib/api"; import { device } from "$lib/device"; + import { t } from "$lib/i18n/translations"; + import { createDialog } from "$lib/dialogs"; + import type { DialogInfo } from "$lib/types/dialog"; + export let url: string; $: buttonText = ">>"; $: buttonAltText = $t('a11y.save.download'); $: isDisabled = false; + let defaultErrorPopup = { + id: "save-error", + type: "small", + title: "", + bodySubText: "", + buttons: [{ + text: $t("general.gotit"), + color: "gray", + action: () => {}, + }] + } + const changeDownloadButton = (state: string) => { isDisabled = true; switch (state) { @@ -59,14 +75,20 @@ changeDownloadButton("error"); restoreDownloadButton(); - return alert("couldn't access the api"); + return createDialog({ + ...defaultErrorPopup as DialogInfo, + bodyText: "couldn't access the api" + }) } if (response.status === "error" || response.status === "rate-limit") { changeDownloadButton("error"); restoreDownloadButton(); - return alert(`error from api: ${response.text}`); + return createDialog({ + ...defaultErrorPopup as DialogInfo, + bodyText: response.text + }) } if (response.status === "redirect") { @@ -90,7 +112,10 @@ changeDownloadButton("error"); restoreDownloadButton(); - return alert("couldn't probe the stream"); + return createDialog({ + ...defaultErrorPopup as DialogInfo, + bodyText: "couldn't probe the stream" + }) } } }; diff --git a/web/src/lib/dialogs.ts b/web/src/lib/dialogs.ts new file mode 100644 index 00000000..a186be04 --- /dev/null +++ b/web/src/lib/dialogs.ts @@ -0,0 +1,23 @@ +import { readable, type Updater } from "svelte/store"; +import type { DialogInfo } from "$lib/types/dialog"; + +let update: (_: Updater<DialogInfo[]>) => void; + +export default readable<DialogInfo[]>( + [], + (_, _update) => { update = _update } +); + +export function createDialog(newData: DialogInfo) { + update((popups) => { + popups.push(newData); + return popups; + }); +} + +export function killDialog() { + update((popups) => { + popups.pop() + return popups; + }); +} diff --git a/web/src/lib/types/dialog.ts b/web/src/lib/types/dialog.ts new file mode 100644 index 00000000..b473acd1 --- /dev/null +++ b/web/src/lib/types/dialog.ts @@ -0,0 +1,14 @@ +export type DialogButton = { + text: string, + color: string, + action: () => unknown | Promise<unknown> +} + +export type DialogInfo = { + id: string, + type: "small", + title: string, + bodyText: string, + bodySubText: string, + buttons: DialogButton[] +} diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index 2ba680c5..35306b81 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -8,6 +8,7 @@ import Sidebar from "$components/sidebar/Sidebar.svelte"; import NotchSticker from "$components/misc/NotchSticker.svelte"; + import DialogHolder from "$components/dialog/DialogHolder.svelte"; $: reduceMotion = $settings.appearance.reduceMotion @@ -33,6 +34,7 @@ {#if device.is.iPhone && app.is.installed} <NotchSticker /> {/if} + <DialogHolder /> <Sidebar /> <div id="content"> <slot></slot> @@ -302,6 +304,15 @@ font-size: 11px; } + :global(dialog) { + max-height: 100%; + max-width: 100%; + padding: var(--padding); + border-radius: var(--border-radius); + border: none; + pointer-events: all; + } + :global(.subtext) { font-size: 12.5px; font-weight: 500;