148 lines
4.2 KiB
Svelte
148 lines
4.2 KiB
Svelte
<script lang="ts">
|
|
import {
|
|
AlertCircle,
|
|
Download,
|
|
LoaderCircle,
|
|
PackageCheck,
|
|
Sparkles,
|
|
X,
|
|
} from "@lucide/svelte";
|
|
|
|
type UpdateToastState = "available" | "downloading" | "installed" | "error";
|
|
|
|
interface Props {
|
|
state: UpdateToastState;
|
|
version: string;
|
|
currentVersion: string;
|
|
progress: number;
|
|
error: string;
|
|
onInstall: () => void;
|
|
onLater: () => void;
|
|
onDismiss: () => void;
|
|
}
|
|
|
|
let {
|
|
state,
|
|
version,
|
|
currentVersion = "",
|
|
progress = 0,
|
|
error = "",
|
|
onInstall,
|
|
onLater,
|
|
onDismiss,
|
|
}: Props = $props();
|
|
|
|
const clampedProgress = $derived(Math.max(0, Math.min(100, Math.round(progress))));
|
|
const title = $derived(
|
|
state === "installed"
|
|
? "Update installed"
|
|
: state === "downloading"
|
|
? "Installing update"
|
|
: state === "error"
|
|
? "Update failed"
|
|
: "Update available",
|
|
);
|
|
const description = $derived(
|
|
state === "installed"
|
|
? "Restart GitLite to use the new version."
|
|
: state === "downloading"
|
|
? "Download and installation are running in the background."
|
|
: state === "error"
|
|
? (error || "The update could not be installed.")
|
|
: "Do you want to install this version now?",
|
|
);
|
|
const versionLabel = $derived(
|
|
version && currentVersion
|
|
? `${currentVersion} -> ${version}`
|
|
: version
|
|
? `Version ${version}`
|
|
: "New version",
|
|
);
|
|
const showProgress = $derived(state === "downloading" || state === "installed");
|
|
const isBusy = $derived(state === "downloading");
|
|
</script>
|
|
|
|
<section
|
|
class="update-toast"
|
|
class:error={state === "error"}
|
|
class:installed={state === "installed"}
|
|
role={state === "error" ? "alert" : "status"}
|
|
aria-live={state === "error" ? "assertive" : "polite"}
|
|
>
|
|
<div class="update-toast-glow" aria-hidden="true"></div>
|
|
|
|
<div class="update-toast-icon" class:busy={isBusy}>
|
|
{#if state === "downloading"}
|
|
<LoaderCircle class="spin" size={21} aria-hidden="true" />
|
|
{:else if state === "installed"}
|
|
<PackageCheck size={21} aria-hidden="true" />
|
|
{:else if state === "error"}
|
|
<AlertCircle size={21} aria-hidden="true" />
|
|
{:else}
|
|
<Sparkles size={21} aria-hidden="true" />
|
|
{/if}
|
|
</div>
|
|
|
|
<div class="update-toast-content">
|
|
<div class="update-toast-top">
|
|
<div class="update-toast-copy">
|
|
<span class="update-toast-kicker">{versionLabel}</span>
|
|
<h2>{title}</h2>
|
|
</div>
|
|
|
|
{#if !isBusy}
|
|
<button class="update-toast-close" type="button" onclick={onDismiss} title="Close" aria-label="Close">
|
|
<X size={15} aria-hidden="true" />
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
|
|
<p>{description}</p>
|
|
|
|
{#if showProgress}
|
|
<div
|
|
class="update-progress"
|
|
class:indeterminate={state === "downloading" && clampedProgress === 0}
|
|
role="progressbar"
|
|
aria-valuemin="0"
|
|
aria-valuemax="100"
|
|
aria-valuenow={clampedProgress}
|
|
>
|
|
<span style={`width: ${clampedProgress}%`}></span>
|
|
</div>
|
|
<div class="update-progress-label">
|
|
{state === "installed" ? "100% complete" : clampedProgress > 0 ? `${clampedProgress}%` : "Waiting for download size"}
|
|
</div>
|
|
{/if}
|
|
|
|
<div class="update-toast-actions">
|
|
{#if state === "installed"}
|
|
<button class="update-toast-secondary" type="button" onclick={onDismiss}>
|
|
Got it
|
|
</button>
|
|
{:else if state === "error"}
|
|
<button class="update-toast-secondary" type="button" onclick={onDismiss}>
|
|
Close
|
|
</button>
|
|
<button class="update-toast-primary" type="button" onclick={onInstall}>
|
|
<Download size={14} aria-hidden="true" />
|
|
Try again
|
|
</button>
|
|
{:else}
|
|
<button class="update-toast-secondary" type="button" onclick={onLater} disabled={isBusy}>
|
|
Later
|
|
</button>
|
|
<button class="update-toast-primary" type="button" onclick={onInstall} disabled={isBusy}>
|
|
{#if isBusy}
|
|
<LoaderCircle class="spin" size={14} aria-hidden="true" />
|
|
Installing
|
|
{:else}
|
|
<Download size={14} aria-hidden="true" />
|
|
Install
|
|
{/if}
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</section>
|