Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | 520x 520x 520x 520x 520x 520x 520x 520x 520x 520x 520x 1235x 1235x 520x 389x 389x 43x 346x 520x 389x 389x 389x 39x 389x 29x 389x 1235x | import { ComponentType } from "react";
import { UserGroup } from "shared/types/models/user";
import { isAdmin, isAdminOrHelper } from "client/utils/checkUserGroup";
import { lazyWithRetry } from "client/utils/lazyWithRetry";
// The views kept out of the main bundle: the About tabs carry the bulk of the
// Markdown content, and the admin and helper tools are reachable by a handful
// of users. They render inside the Suspense boundary that wraps the routes.
//
// The prefetch list at the bottom must stay in step with these - a view split
// without a matching entry keeps its loading spinner on every visit
type ViewImport = () => Promise<{ default: ComponentType }>;
const importAboutView: ViewImport = async () => ({
default: (await import("client/views/about/AboutView")).AboutView,
});
const importFaqView: ViewImport = async () => ({
default: (await import("client/views/about/FaqView")).FaqView,
});
const importInstructionsView: ViewImport = async () => ({
default: (await import("client/views/about/InstructionsView"))
.InstructionsView,
});
const importAdminView: ViewImport = async () => ({
default: (await import("client/views/admin/AdminView")).AdminView,
});
const importHelperView: ViewImport = async () => ({
default: (await import("client/views/helper/HelperView")).HelperView,
});
export const AboutView = lazyWithRetry(importAboutView);
export const FaqView = lazyWithRetry(importFaqView);
export const InstructionsView = lazyWithRetry(importInstructionsView);
export const AdminView = lazyWithRetry(importAdminView);
export const HelperView = lazyWithRetry(importHelperView);
const prefetchView = async (importView: ViewImport): Promise<void> => {
try {
await importView();
} catch {
// Nothing useful to do here, but note the cost: the browser caches a
// failed dynamic import for the rest of the session, so this chunk is now
// poisoned and opening the view will reject instantly and reload the page
// rather than refetch. That is why the caller only prefetches after a data
// load has succeeded and the connection looks usable
}
};
// Some attendees are on metered or barely-working mobile data, where warming
// chunks they may never open is a bad trade
const connectionIsTooPoor = (): boolean => {
const connection = (
navigator as Navigator & {
connection?: { saveData?: boolean; effectiveType?: string };
}
).connection;
if (!connection) {
return false;
}
return (
connection.saveData === true ||
connection.effectiveType === "slow-2g" ||
connection.effectiveType === "2g"
);
};
// Warms the chunks above so opening one of these views doesn't wait on a
// download. The caller decides when, because this must not compete with the
// requests that put content on screen. The staff tools are fetched only for
// the roles that can reach them, to keep the transfer off everyone else's
// connection
export const prefetchLazyViews = (userGroup: UserGroup): void => {
Iif (connectionIsTooPoor()) {
return;
}
const imports = [importAboutView, importFaqView, importInstructionsView];
if (isAdminOrHelper(userGroup)) {
imports.push(importHelperView);
}
if (isAdmin(userGroup)) {
imports.push(importAdminView);
}
for (const importView of imports) {
void prefetchView(importView);
}
};
|