This commit is contained in:
parent
23c4585bc1
commit
7bc6d002a9
90 changed files with 13401 additions and 1 deletions
20
src/screens/error.tsx
Normal file
20
src/screens/error.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import Link from "../link";
|
||||
import CreateScreen, { StorageDestoryer } from "./util";
|
||||
|
||||
/**
|
||||
* Executes Screen.
|
||||
* @param props Parameter props.
|
||||
* @returns unknown.
|
||||
*/
|
||||
export default function Screen(props: { error: string; description: string }) {
|
||||
return (
|
||||
<CreateScreen>
|
||||
<p className="text-base font-semibold">{props.error}</p>
|
||||
<p className="pt-4 text-sm w-1/2 text-center text-muted-foreground pb-8">
|
||||
{props.description}
|
||||
</p>
|
||||
<Link label="status.methanium.net" link="https://status.methanium.net" />
|
||||
<StorageDestoryer />
|
||||
</CreateScreen>
|
||||
);
|
||||
}
|
||||
102
src/screens/loading.tsx
Normal file
102
src/screens/loading.tsx
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import { useRef, useState, useEffect } from "react";
|
||||
import CreateScreen, { StorageDestoryer } from "./util";
|
||||
|
||||
const DELAY = 250;
|
||||
|
||||
type ScreenProps = {
|
||||
title?: string;
|
||||
description?: string;
|
||||
fullscreen?: boolean;
|
||||
} & (
|
||||
| { noProgress: true; progress?: never }
|
||||
| { noProgress?: false; progress: number }
|
||||
);
|
||||
|
||||
/**
|
||||
* Executes Screen.
|
||||
* @param props Parameter props.
|
||||
* @returns unknown.
|
||||
*/
|
||||
export default function Screen(props: ScreenProps) {
|
||||
const [displayProgress, setDisplayProgress] = useState(0);
|
||||
const displayProgressRef = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
displayProgressRef.current = displayProgress;
|
||||
}, [displayProgress]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.noProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = props.progress;
|
||||
const start = displayProgressRef.current;
|
||||
const delta = target - start;
|
||||
|
||||
if (delta === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const duration = DELAY;
|
||||
const startTime = performance.now();
|
||||
let frameId = 0;
|
||||
|
||||
/**
|
||||
* Executes animate.
|
||||
* @param now Parameter now.
|
||||
* @returns unknown.
|
||||
*/
|
||||
function animate(now: number) {
|
||||
const elapsed = now - startTime;
|
||||
const t = Math.min(elapsed / duration, 1);
|
||||
const eased = 1 - Math.pow(1 - t, 3);
|
||||
setDisplayProgress(start + delta * eased);
|
||||
|
||||
if (t < 1) {
|
||||
frameId = requestAnimationFrame(animate);
|
||||
}
|
||||
}
|
||||
|
||||
frameId = requestAnimationFrame(animate);
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(frameId);
|
||||
};
|
||||
}, [props.noProgress, props.progress]);
|
||||
|
||||
return (
|
||||
<CreateScreen>
|
||||
<h2 className="text-base font-semibold text-foreground">
|
||||
{props.title ?? "Loading"}
|
||||
</h2>
|
||||
{props.description ? (
|
||||
<p className="text-sm text-muted-foreground mt-1 mb-5">
|
||||
{props.description}
|
||||
</p>
|
||||
) : (
|
||||
<div className="mb-5" />
|
||||
)}
|
||||
<div
|
||||
className="w-64 h-1.5 bg-secondary rounded-full overflow-hidden relative"
|
||||
role={props.noProgress ? "status" : "progressbar"}
|
||||
aria-label="Loading"
|
||||
aria-valuemin={props.noProgress ? undefined : 0}
|
||||
aria-valuemax={props.noProgress ? undefined : 100}
|
||||
aria-valuenow={props.noProgress ? undefined : displayProgress}
|
||||
>
|
||||
{props.noProgress ? (
|
||||
<div className="loading-line-wobble h-full bg-primary rounded-full" />
|
||||
) : (
|
||||
<div
|
||||
className="h-full bg-primary absolute left-0 top-0"
|
||||
style={{
|
||||
width: `${displayProgress}%`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<StorageDestoryer />
|
||||
</CreateScreen>
|
||||
);
|
||||
}
|
||||
98
src/screens/util.tsx
Normal file
98
src/screens/util.tsx
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import { cn } from "../lib/utils";
|
||||
import Controls from "../windowControls";
|
||||
import { isTauri } from "@tauri-apps/api/core";
|
||||
|
||||
export default function CreateScreen({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"bg-background w-screen h-screen flex flex-col justify-center items-center",
|
||||
className,
|
||||
)}
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<>
|
||||
<Controls className="fixed top-0 right-0 m-1.75 mr-[7.6px]! ring-foreground/15" />
|
||||
{children}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
import { Button } from "../cmp/button";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "../cmp/alert-dialog";
|
||||
import { useIsMobile } from "../lib/utils";
|
||||
|
||||
export function ClearStorageButton({ className }: { className?: string }) {
|
||||
const isMobile = useIsMobile();
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger
|
||||
render={
|
||||
<Button
|
||||
variant="destructive"
|
||||
className={cn(isMobile ? "w-full" : "", className)}
|
||||
>
|
||||
Clear storage
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
Are you sure you want to clear storage?
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This action cannot be undone. It will clear all your settings and
|
||||
credentials.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogAction
|
||||
onClick={async () => {
|
||||
localStorage.clear();
|
||||
for (const db of await indexedDB.databases()) {
|
||||
indexedDB.deleteDatabase(db.name!);
|
||||
}
|
||||
location.reload();
|
||||
}}
|
||||
>
|
||||
Continue
|
||||
</AlertDialogAction>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
|
||||
export function StorageDestoryer() {
|
||||
const isMobile = useIsMobile();
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"fixed p-5 bottom-0",
|
||||
isMobile ? "w-full" : "right-0",
|
||||
isTauri() ? "py-10" : "",
|
||||
)}
|
||||
>
|
||||
<ClearStorageButton />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue