client/utilities/utils.ts (55 lines of code) (raw):

import * as Sentry from '@sentry/browser'; import { trackEvent } from './analytics'; // babel doesn't support 'flatten', but this function can be used with flatMap export function flattenEquivalent<T>(x: T): T { return x; } /* * implementation of the Fisher–Yates Shuffle algorithm * example here: https://bost.ocks.org/mike/shuffle/ */ export const shuffleArray = (array: unknown[]) => { let currentIndex = array.length; // While there remain elements to shuffle... while (currentIndex != 0) { // Pick a remaining element... const randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; // And swap it with the current element. [array[currentIndex], array[randomIndex]] = [ array[randomIndex], array[currentIndex], ]; } return array; }; export function formatAmount(amount: number) { return Number.isInteger(amount) ? amount : amount.toFixed(2); } export const processResponse = <T>(resp: Response): Promise<T | null> => { const locationHeader = resp.headers.get('Location'); const allResponsesAreOK = [resp].filter((res) => !res.ok).length === 0; // handle unauthorized if (resp.status === 401 && locationHeader && window !== undefined) { window.location.replace(locationHeader); return Promise.resolve(null); // handle success } else if (allResponsesAreOK) { return resp.json(); } // handle error const error = new Error(`${resp.status} (${resp.statusText})`); trackEvent({ eventCategory: 'fetch', eventAction: 'error', eventLabel: error ? error.toString() : undefined, }); Sentry.captureException(error); throw error; }; // https://stackoverflow.com/a/61511955 export function waitForElement(selector: string): Promise<Element | null> { return new Promise((resolve) => { if (document.querySelector(selector)) { return resolve(document.querySelector(selector)); } const observer = new MutationObserver((_) => { if (document.querySelector(selector)) { observer.disconnect(); resolve(document.querySelector(selector)); } }); observer.observe(document.body, { childList: true, subtree: true, }); }); }