export function capitalise0(name: undefined): undefined export function capitalise0(name: string): string export function capitalise0(name?: string) { return name && name[0].toUpperCase() + name.slice(1) } export const debounce = void>( waitFor: number, fn: F ) => { let timeoutId: ReturnType return (...args: any[]) => { clearTimeout(timeoutId) timeoutId = setTimeout(() => fn(...args), waitFor) } } export const fetcher = (url: RequestInfo) => fetch(url).then(r => r.json()) export function inIframe(): boolean { try { return window.self !== window.top } catch (e) { return true } } // We use this variable to hide some features in production while keeping them // in feature-branches. In case we do A/B testing with several branches served // in production, we should add the public faced branch names in the test below. // This is different from the process.env.NODE_ENV in that a feature branch may // be build in production mode (with the NODE_ENV) but we may still want to show // or hide some features. export const productionMode = ['master', 'next'].includes(process.env.HEAD) export function softCatch( fn: (arg: ArgType) => ReturnType ): (arg: ArgType) => ReturnType | null { return function(...args) { try { return fn(...args) } catch (e) { // eslint-disable-next-line no-console console.warn(e) return null } } } export function mapOrApply(fn: (a: A) => B, x: A): B export function mapOrApply(fn: (a: A) => B, x: Array): Array export function mapOrApply(fn: (a: A) => B, x: A | Array) { return Array.isArray(x) ? x.map(fn) : fn(x) } export function coerceArray(x: A | Array): Array { return Array.isArray(x) ? x : [x] } export function getSessionStorage() { // In some browsers like Brave, even just reading the variable sessionStorage // is throwing an error in the iframe, so we can't do things if sessionStorage !== undefined // and we need to wrap it in a try { } catch { } logic try { return window.sessionStorage } catch (e) { return undefined } }