web: dialog system & basic small dialog

This commit is contained in:
wukko 2024-07-13 19:15:43 +06:00
parent c5fbff560b
commit 4232c3437b
No known key found for this signature in database
GPG key ID: 3E30B3F26C7B4AA2
7 changed files with 161 additions and 4 deletions

View file

@ -1,3 +1,4 @@
{ {
"cobalt": "cobalt" "cobalt": "cobalt",
"gotit": "got it"
} }

View file

@ -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>

View file

@ -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>

View file

@ -3,14 +3,30 @@
import API from "$lib/api"; import API from "$lib/api";
import { device } from "$lib/device"; import { device } from "$lib/device";
import { t } from "$lib/i18n/translations"; import { t } from "$lib/i18n/translations";
import { createDialog } from "$lib/dialogs";
import type { DialogInfo } from "$lib/types/dialog";
export let url: string; export let url: string;
$: buttonText = ">>"; $: buttonText = ">>";
$: buttonAltText = $t('a11y.save.download'); $: buttonAltText = $t('a11y.save.download');
$: isDisabled = false; $: isDisabled = false;
let defaultErrorPopup = {
id: "save-error",
type: "small",
title: "",
bodySubText: "",
buttons: [{
text: $t("general.gotit"),
color: "gray",
action: () => {},
}]
}
const changeDownloadButton = (state: string) => { const changeDownloadButton = (state: string) => {
isDisabled = true; isDisabled = true;
switch (state) { switch (state) {
@ -59,14 +75,20 @@
changeDownloadButton("error"); changeDownloadButton("error");
restoreDownloadButton(); 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") { if (response.status === "error" || response.status === "rate-limit") {
changeDownloadButton("error"); changeDownloadButton("error");
restoreDownloadButton(); restoreDownloadButton();
return alert(`error from api: ${response.text}`); return createDialog({
...defaultErrorPopup as DialogInfo,
bodyText: response.text
})
} }
if (response.status === "redirect") { if (response.status === "redirect") {
@ -90,7 +112,10 @@
changeDownloadButton("error"); changeDownloadButton("error");
restoreDownloadButton(); restoreDownloadButton();
return alert("couldn't probe the stream"); return createDialog({
...defaultErrorPopup as DialogInfo,
bodyText: "couldn't probe the stream"
})
} }
} }
}; };

23
web/src/lib/dialogs.ts Normal file
View file

@ -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;
});
}

View file

@ -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[]
}

View file

@ -8,6 +8,7 @@
import Sidebar from "$components/sidebar/Sidebar.svelte"; import Sidebar from "$components/sidebar/Sidebar.svelte";
import NotchSticker from "$components/misc/NotchSticker.svelte"; import NotchSticker from "$components/misc/NotchSticker.svelte";
import DialogHolder from "$components/dialog/DialogHolder.svelte";
$: reduceMotion = $: reduceMotion =
$settings.appearance.reduceMotion $settings.appearance.reduceMotion
@ -33,6 +34,7 @@
{#if device.is.iPhone && app.is.installed} {#if device.is.iPhone && app.is.installed}
<NotchSticker /> <NotchSticker />
{/if} {/if}
<DialogHolder />
<Sidebar /> <Sidebar />
<div id="content"> <div id="content">
<slot></slot> <slot></slot>
@ -302,6 +304,15 @@
font-size: 11px; 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) { :global(.subtext) {
font-size: 12.5px; font-size: 12.5px;
font-weight: 500; font-weight: 500;