Add panorama previews and repair beta export rendering
This commit is contained in:
122
components/PanoramaPreview.tsx
Normal file
122
components/PanoramaPreview.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
"use client";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Panorama } from "./Panorama";
|
||||
import { api } from "./StartGame";
|
||||
|
||||
export function PanoramaPreview({
|
||||
endpoint,
|
||||
initialId,
|
||||
onClose,
|
||||
}: {
|
||||
endpoint: string;
|
||||
initialId?: string;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const dialog = useRef<HTMLDialogElement>(null);
|
||||
const [locations, setLocations] = useState<string[]>([]),
|
||||
[selected, setSelected] = useState(initialId ?? ""),
|
||||
[urls, setUrls] = useState<string[] | null>(null),
|
||||
[ready, setReady] = useState(false),
|
||||
[error, setError] = useState("");
|
||||
useEffect(() => {
|
||||
dialog.current?.showModal();
|
||||
let disposed = false;
|
||||
api<{ locations: string[] }>(endpoint)
|
||||
.then((d) => {
|
||||
if (disposed) return;
|
||||
setLocations(d.locations);
|
||||
setSelected((s) =>
|
||||
d.locations.includes(s) ? s : (d.locations[0] ?? ""),
|
||||
);
|
||||
})
|
||||
.catch((e) => {
|
||||
if (!disposed) setError(e.message);
|
||||
});
|
||||
return () => {
|
||||
disposed = true;
|
||||
};
|
||||
}, [endpoint]);
|
||||
useEffect(() => {
|
||||
if (!selected) return;
|
||||
let disposed = false;
|
||||
setUrls(null);
|
||||
setReady(false);
|
||||
setError("");
|
||||
api<{ faces: string[] }>(`${endpoint}/${selected}`)
|
||||
.then((d) => {
|
||||
if (!disposed) setUrls(d.faces);
|
||||
})
|
||||
.catch((e) => {
|
||||
if (!disposed) setError(e.message);
|
||||
});
|
||||
return () => {
|
||||
disposed = true;
|
||||
};
|
||||
}, [endpoint, selected]);
|
||||
const index = locations.indexOf(selected);
|
||||
return (
|
||||
<dialog
|
||||
ref={dialog}
|
||||
className="panorama-preview"
|
||||
aria-label="Inspect panorama"
|
||||
onCancel={onClose}
|
||||
>
|
||||
<div className="preview-toolbar">
|
||||
<label>
|
||||
Location{" "}
|
||||
<select
|
||||
aria-label="Preview location"
|
||||
value={selected}
|
||||
onChange={(e) => setSelected(e.target.value)}
|
||||
>
|
||||
{locations.map((p) => (
|
||||
<option key={p}>{p}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<button
|
||||
className="button"
|
||||
disabled={index <= 0}
|
||||
onClick={() => setSelected(locations[index - 1])}
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
<button
|
||||
className="button"
|
||||
disabled={index < 0 || index >= locations.length - 1}
|
||||
onClick={() => setSelected(locations[index + 1])}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
<button className="button" onClick={onClose}>
|
||||
Close preview
|
||||
</button>
|
||||
</div>
|
||||
<div className="preview-viewport">
|
||||
{urls && (
|
||||
<Panorama
|
||||
urls={urls}
|
||||
zoom
|
||||
onReady={() => setReady(true)}
|
||||
onError={setError}
|
||||
/>
|
||||
)}
|
||||
{error ? (
|
||||
<p role="alert" className="preview-status">
|
||||
{error}
|
||||
</p>
|
||||
) : (
|
||||
!ready && (
|
||||
<p role="status" className="preview-status">
|
||||
Loading panorama…
|
||||
</p>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<p className="muted">
|
||||
Drag to look around · Scroll to zoom · Check for blocked views and
|
||||
missing terrain.
|
||||
</p>
|
||||
</dialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user