/* This file is part of GNU Taler (C) 2022 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ import { createElement, VNode } from "preact"; import { useCallback, useMemo } from "preact/hooks"; function getJsonIfOk(r: Response): Promise { if (r.ok) { return r.json(); } if (r.status >= 400 && r.status < 500) { throw new Error(`URL may not be right: (${r.status}) ${r.statusText}`); } throw new Error( `Try another server: (${r.status}) ${r.statusText || "internal server error" }`, ); } export async function queryToSlashConfig(url: string): Promise { return fetch(new URL("config", url).href) .catch(() => { throw new Error(`Network error`); }) .then(getJsonIfOk); } function timeout(ms: number, promise: Promise): Promise { return new Promise((resolve, reject) => { const timer = setTimeout(() => { reject( new Error( `Timeout: the query took longer than ${Math.floor(ms / 1000)} secs`, ), ); }, ms); promise .then((value) => { clearTimeout(timer); resolve(value); }) .catch((reason) => { clearTimeout(timer); reject(reason); }); }); } export async function queryToSlashKeys(url: string): Promise { const endpoint = new URL("keys", url); const query = fetch(endpoint.href) .catch(() => { throw new Error(`Network error`); }) .then(getJsonIfOk); return timeout(3000, query); } export type StateFunc = (p: S) => VNode | null; export type StateViewMap = { [S in StateType as S["status"]]: StateFunc; }; export type RecursiveState = S | (() => RecursiveState); export function compose( name: string, hook: (p: PType) => RecursiveState, viewMap: StateViewMap, ): (p: PType) => VNode { function withHook(stateHook: () => RecursiveState): () => VNode { function TheComponent(): VNode { //if the function is the same, do not compute const state = stateHook(); if (typeof state === "function") { const subComponent = withHook(state); return createElement(subComponent, {}); } const statusName = state.status as unknown as SType["status"]; const viewComponent = viewMap[statusName] as unknown as StateFunc; return createElement(viewComponent, state); } // TheComponent.name = `${name}`; return useMemo(() => { return TheComponent }, [stateHook]); } return (p: PType) => { const h = withHook(() => hook(p)); return h(); }; } export function assertUnreachable(x: never): never { throw new Error("Didn't expect to get here"); }