Improved loading UI/UX on nise-replay-viewer
This commit is contained in:
parent
15dc0e90bc
commit
f6db550edb
@ -5,6 +5,7 @@ import {Helper} from "./composites/helper";
|
||||
import {useEffect} from "react";
|
||||
import {OsuRenderer} from "@/osu/OsuRenderer";
|
||||
import {Stats} from "@/interface/composites/stats";
|
||||
import {LoadingDialog} from "@/interface/composites/loading-dialog";
|
||||
|
||||
export function App() {
|
||||
|
||||
@ -38,6 +39,7 @@ export function App() {
|
||||
<>
|
||||
<Navbar/>
|
||||
<AboutDialog/>
|
||||
<LoadingDialog/>
|
||||
<SongSlider/>
|
||||
<Stats/>
|
||||
<Helper/>
|
||||
|
||||
@ -29,8 +29,8 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
|
||||
|
||||
const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & { hideCloseButton?: boolean } // Add hideCloseButton prop
|
||||
>(({ className, children, hideCloseButton, ...props }, ref) => (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
@ -42,14 +42,17 @@ const DialogContent = React.forwardRef<
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{!hideCloseButton && ( // Conditionally render based on hideCloseButton
|
||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
))
|
||||
DialogContent.displayName = DialogPrimitive.Content.displayName
|
||||
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
||||
|
||||
|
||||
const DialogHeader = ({
|
||||
className,
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
import { Dialog, DialogContent } from "@/interface/components/ui/dialog";
|
||||
import { state } from "@/utils";
|
||||
|
||||
export function LoadingDialog() {
|
||||
const { beatmap, replay} = state();
|
||||
|
||||
if (beatmap && replay) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={true}
|
||||
>
|
||||
<DialogContent className="sm:max-w-[425px]" hideCloseButton={true}>
|
||||
<div className="bottom-0 left-0 ">
|
||||
<h3 className="scroll-m-20 text-2xl font-semibold tracking-tight flex items-center gap-3">
|
||||
Loading replay...
|
||||
</h3>
|
||||
<div className="opacity-75">
|
||||
<h3 className="text-sm">Your replay is being loaded</h3>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@ -38,10 +38,18 @@ export class Drawer {
|
||||
}
|
||||
|
||||
static async loadDefaultImages() {
|
||||
for (const imageName of Object.keys(Drawer.images)) {
|
||||
Drawer.images[imageName as keyof typeof Drawer.images] =
|
||||
await loadImageAsync(`/${imageName}.png`);
|
||||
const imageLoadPromises = Object.keys(Drawer.images).map(imageName =>
|
||||
loadImageAsync(`/${imageName}.png`).then(
|
||||
image => {
|
||||
Drawer.images[imageName as keyof typeof Drawer.images] = image;
|
||||
},
|
||||
error => {
|
||||
console.error(`Failed to load image ${imageName}:`, error);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return Promise.allSettled(imageLoadPromises)
|
||||
}
|
||||
|
||||
static setImages(images: typeof this.images) {
|
||||
@ -97,7 +105,7 @@ export class Drawer {
|
||||
// Drawer.p.strokeWeight(2);
|
||||
// Drawer.p.line(barX + barWidth / 2, barY - 5, barX + barWidth / 2, barY + barHeight + 5);
|
||||
|
||||
Drawer.p.pop();
|
||||
// Drawer.p.pop();
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
|
||||
@ -12,7 +12,7 @@ import {
|
||||
} from "osu-standard-stable";
|
||||
import {Drawer} from "./Drawer";
|
||||
import {Vec2} from "@osujs/math";
|
||||
import {clamp, getBeatmap, getReplay} from "@/utils";
|
||||
import {clamp, getBeatmap, getReplay, state} from "@/utils";
|
||||
import EventEmitter from "eventemitter3";
|
||||
import {toast} from "sonner";
|
||||
import p5 from "p5";
|
||||
@ -303,6 +303,11 @@ export class OsuRenderer {
|
||||
const i_beatmap = await getBeatmap(beatmap, i_replay);
|
||||
|
||||
OsuRenderer.setOptions(i_beatmap, i_replay, judgements);
|
||||
state.setState({
|
||||
beatmap: i_beatmap,
|
||||
replay: i_replay,
|
||||
mods: i_replay.info.mods?.all,
|
||||
});
|
||||
this.event.emit(OsuRendererEvents.LOAD);
|
||||
}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ export class Renderer {
|
||||
Renderer.registerEvents();
|
||||
Drawer.setP(p);
|
||||
|
||||
await Drawer.loadDefaultImages().then(
|
||||
Drawer.loadDefaultImages().then(
|
||||
() => {
|
||||
Renderer.areImagesLoaded = true;
|
||||
OsuRenderer.setPlaying(true);
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
font-weight: 400;
|
||||
src: url('https://replay.nise.moe/ia-quattro-400-normal.woff2') format('woff2');
|
||||
src: url('/ia-quattro-400-normal.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@ -11,7 +11,7 @@
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
font-weight: 700;
|
||||
src: url('https://replay.nise.moe/ia-quattro-700-normal.woff2') format('woff2');
|
||||
src: url('/ia-quattro-700-normal.woff2') format('woff2');
|
||||
}
|
||||
|
||||
#app {
|
||||
|
||||
@ -3,7 +3,7 @@ import { ScoreDecoder } from "../osu-parsers";
|
||||
import { StandardRuleset, StandardBeatmap } from "osu-standard-stable";
|
||||
|
||||
import { IMod, Score } from "osu-classes";
|
||||
import p5, { Image } from "p5";
|
||||
import p5 from "p5";
|
||||
import { create } from "zustand";
|
||||
|
||||
const ruleset = new StandardRuleset();
|
||||
@ -27,11 +27,17 @@ export async function getBeatmap(mapText: string, scoreBase: Score) {
|
||||
return ruleset.applyToBeatmap(map);
|
||||
}
|
||||
|
||||
export async function loadImageAsync(image: string): Promise<Image> {
|
||||
return new Promise((res) => {
|
||||
p.loadImage(image, (img) => {
|
||||
res(img);
|
||||
});
|
||||
export async function loadImageAsync(image: string): Promise<p5.Image> {
|
||||
return new Promise((resolve, reject) => {
|
||||
p.loadImage(
|
||||
image,
|
||||
(img) => {
|
||||
resolve(img);
|
||||
},
|
||||
(err) => {
|
||||
reject(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user