Something went wrong. Try again.
Web frontend and supporting services for lance.blue
Something went wrong. Try again.
2.5 kB · 72 lines
TypeScript
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273/** * The whole site: sign in with an ATProto account, design a camo, read what * this is. * * The entry point only. It fills in the navigation a screen cannot import, * finds the masthead the layout rendered, catches anything that escapes, and * hands over to the router. */
import { ApiError } from "./api";import { collectNavLinks } from "./chrome";import { render } from "./dom";import { nav } from "./nav";import { markEntered, onHashChange, start } from "./router";import { challengeScreen } from "./screens/challenge";import { errorScreen } from "./screens/error";import { matchesScreen } from "./screens/matches";
// The screens a screen can send the player to that it cannot import. See nav.ts.nav.start = start;nav.matches = matchesScreen;nav.markShown = markEntered;// Re-entering without a fresh Mode choice (waiting.ts's Back button) gets// the least restrictive pool, Scenario, rather than silently narrowing to// Duel's.nav.challenge = (session) => challengeScreen(session, "scenario");
/** * Until start() renders, the page is the CSS "Loading…" on #app[aria-busy]. An * exception on the way there would leave that on screen for good, so both * channels an escaping error can take get turned into a screen with a way out. * * One shot: if the error screen's own render is what threw, a second pass would * only loop. */let trapped = false;
function trap(channel: string, reason: unknown): void { if (trapped) return; trapped = true; // Names the channel, and says the trap - not the app - put that screen up. // The browser's own logging of the error is untouched. console.error(`trap: an error escaped the app (${channel})`, reason); render( ...errorScreen( // ApiError messages are written to be read; anything else is a stack // trace's worth of noise the player cannot act on. reason instanceof ApiError ? reason.message : "An unexpected error interrupted the page.", () => { window.location.reload(); }, ), );}
window.addEventListener("error", (event) => { // Failed subresource loads fire here too, with the element as the target. A // missing image is no reason to throw away the whole page. if (event.target !== window) return; trap("error", event.error);});
window.addEventListener("unhandledrejection", (event) => { trap("unhandledrejection", event.reason);});
window.addEventListener("hashchange", onHashChange);
collectNavLinks();void start();