summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/stories.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-webextension/src/stories.tsx')
-rw-r--r--packages/taler-wallet-webextension/src/stories.tsx499
1 files changed, 24 insertions, 475 deletions
diff --git a/packages/taler-wallet-webextension/src/stories.tsx b/packages/taler-wallet-webextension/src/stories.tsx
index 02cc15393..8834b8084 100644
--- a/packages/taler-wallet-webextension/src/stories.tsx
+++ b/packages/taler-wallet-webextension/src/stories.tsx
@@ -18,516 +18,65 @@
*
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { setupI18n } from "@gnu-taler/taler-util";
-import { styled } from "@linaria/react";
-import {
- ComponentChild,
- ComponentChildren,
- Fragment,
- FunctionComponent,
- h,
- render,
- VNode,
-} from "preact";
-import { useEffect, useErrorBoundary, useState } from "preact/hooks";
+import { Fragment, FunctionComponent, h } from "preact";
import { LogoHeader } from "./components/LogoHeader.js";
import { PopupBox, WalletBox } from "./components/styled/index.js";
-import * as mui from "./mui/index.stories.js";
+import { strings } from "./i18n/strings.js";
import { PopupNavBar, WalletNavBar } from "./NavigationBar.js";
+
+import * as components from "./components/index.stories.js";
+import * as cta from "./cta/index.stories.js";
+import * as mui from "./mui/index.stories.js";
import * as popup from "./popup/index.stories.js";
import * as wallet from "./wallet/index.stories.js";
-import * as cta from "./cta/index.stories.js";
-import * as components from "./components/index.stories.js";
-import { strings } from "./i18n/strings.js";
-import { setupPlatform } from "./platform/api.js";
-import chromeAPI from "./platform/chrome.js";
-import firefoxAPI from "./platform/firefox.js";
-const url = new URL(window.location.href);
-const lang = url.searchParams.get("lang") || "en";
+import { renderStories } from "@gnu-taler/web-util/lib/index.browser";
-setupI18n(lang, strings);
-
-const Page = styled.div`
- * {
- font-family: Arial, Helvetica, sans-serif;
- }
- p:not([class]) {
- margin-bottom: 1em;
- margin-top: 1em;
- }
- width: 100%;
- display: flex;
- flex-direction: row;
-`;
-
-const SideBar = styled.div`
- min-width: var(--with-size);
- height: calc(100vh - 20px);
- overflow-y: visible;
- overflow-x: hidden;
- scroll-behavior: smooth;
-
- * {
- margin: 0px;
- padding: 0px;
- }
-
- & > {
- ol {
- padding: 4px;
- div:first-child {
- background-color: lightcoral;
- cursor: pointer;
- }
- div[data-hide="true"] {
- display: none;
- }
- dd {
- margin-left: 1em;
- padding: 4px;
- cursor: pointer;
- border-radius: 4px;
- margin-bottom: 4px;
- }
- dd:nth-child(even) {
- background-color: lightgray;
- }
- dd:nth-child(odd) {
- background-color: lightblue;
- }
- a {
- color: black;
- }
- dd[data-selected] {
- background-color: green;
- }
- }
- }
-`;
-
-const ResizeHandleDiv = styled.div`
- width: 10px;
- background: #ddd;
- cursor: ew-resize;
-`;
-
-function ResizeHandle({ onUpdate }: { onUpdate: (x: number) => void }): VNode {
- const [start, setStart] = useState<number | undefined>(undefined);
- return (
- <ResizeHandleDiv
- onMouseDown={(e: any) => {
- setStart(e.pageX);
- console.log("active", e.pageX);
- return false;
- }}
- onMouseMove={(e: any) => {
- if (start !== undefined) {
- onUpdate(e.pageX - start);
- }
- return false;
- }}
- onMouseUp={() => {
- setStart(undefined);
- return false;
- }}
- />
- );
-}
-
-const Content = styled.div`
- width: 100%;
- padding: 20px;
-`;
-
-function parseExampleImport(group: string, im: any): ComponentItem {
- const component = im.default.title;
- return {
- name: component,
- examples: Object.entries(im)
- .filter(([k]) => k !== "default")
- .map(
- ([name, render]) =>
- ({
- group,
- component,
- name,
- render,
- } as ExampleItem),
- ),
- };
-}
-
-const allExamples = Object.entries({ popup, wallet, cta, mui, components }).map(
- ([title, value]) => ({
- title,
- list: value.default.map((s) => parseExampleImport(title, s)),
- }),
-);
-
-interface ComponentItem {
- name: string;
- examples: ExampleItem[];
-}
-
-interface ExampleItem {
- group: string;
- component: string;
- name: string;
- render: {
- (args: any): VNode;
- args: any;
- };
-}
-
-function findByGroupComponentName(
- group: string,
- component: string,
- name: string,
-): ExampleItem | undefined {
- const gl = allExamples.filter((e) => e.title === group);
- if (gl.length === 0) {
- return undefined;
- }
- const cl = gl[0].list.filter((l) => l.name === component);
- if (cl.length === 0) {
- return undefined;
- }
- const el = cl[0].examples.filter((c) => c.name === name);
- if (el.length === 0) {
- return undefined;
- }
- return el[0];
-}
-
-function getContentForExample(item: ExampleItem | undefined): () => VNode {
- if (!item)
- return function SelectExampleMessage() {
- return <div>select example from the list on the left</div>;
- };
- const example = findByGroupComponentName(
- item.group,
- item.component,
- item.name,
- );
- if (!example) {
- return function ExampleNotFoundMessage() {
- return <div>example not found</div>;
- };
- }
- return () => example.render(example.render.args);
-}
-
-function ExampleList({
- name,
- list,
- selected,
- onSelectStory,
-}: {
- name: string;
- list: {
- name: string;
- examples: ExampleItem[];
- }[];
- selected: ExampleItem | undefined;
- onSelectStory: (i: ExampleItem, id: string) => void;
-}): VNode {
- const [isOpen, setOpen] = useState(selected && selected.group === name);
- return (
- <ol>
- <div onClick={() => setOpen(!isOpen)}>{name}</div>
- <div data-hide={!isOpen}>
- {list.map((k) => (
- <li key={k.name}>
- <dl>
- <dt>{k.name.substring(k.name.indexOf("/") + 1)}</dt>
- {k.examples.map((r) => {
- const e = encodeURIComponent;
- const eId = `${e(r.group)}-${e(r.component)}-${e(r.name)}`;
- const isSelected =
- selected &&
- selected.component === r.component &&
- selected.group === r.group &&
- selected.name === r.name;
- return (
- <dd id={eId} key={r.name} data-selected={isSelected}>
- <a
- href={`#${eId}`}
- onClick={(e) => {
- e.preventDefault();
- location.hash = `#${eId}`;
- onSelectStory(r, eId);
- history.pushState({}, "", `#${eId}`);
- }}
- >
- {r.name}
- </a>
- </dd>
- );
- })}
- </dl>
- </li>
- ))}
- </div>
- </ol>
+function main(): void {
+ renderStories(
+ { popup, wallet, cta, mui, components },
+ {
+ strings,
+ getWrapperForGroup,
+ },
);
}
-/**
- * Prevents the UI from redirecting and inform the dev
- * where the <a /> should have redirected
- * @returns
- */
-function PreventLinkNavigation({
- children,
-}: {
- children: ComponentChildren;
-}): VNode {
- return (
- <div
- onClick={(e) => {
- let t: any = e.target;
- do {
- if (t.localName === "a" && t.getAttribute("href")) {
- alert(`should navigate to: ${t.attributes.href.value}`);
- e.stopImmediatePropagation();
- e.stopPropagation();
- e.preventDefault();
- return false;
- }
- } while ((t = t.parentNode));
- }}
- >
- {children}
- </div>
- );
+if (document.readyState === "loading") {
+ document.addEventListener("DOMContentLoaded", main);
+} else {
+ main();
}
-
function getWrapperForGroup(group: string): FunctionComponent {
switch (group) {
case "popup":
return function PopupWrapper({ children }: any) {
return (
- <PreventLinkNavigation>
+ <Fragment>
<PopupNavBar />
<PopupBox>{children}</PopupBox>
- </PreventLinkNavigation>
+ </Fragment>
);
};
case "wallet":
return function WalletWrapper({ children }: any) {
return (
- <PreventLinkNavigation>
+ <Fragment>
<LogoHeader />
<WalletNavBar />
<WalletBox>{children}</WalletBox>
- </PreventLinkNavigation>
+ </Fragment>
);
};
case "cta":
return function WalletWrapper({ children }: any) {
return (
- <PreventLinkNavigation>
+ <Fragment>
<WalletBox>{children}</WalletBox>
- </PreventLinkNavigation>
+ </Fragment>
);
};
default:
return Fragment;
}
}
-
-function ErrorReport({
- children,
- selected,
-}: {
- children: ComponentChild;
- selected: ExampleItem | undefined;
-}): VNode {
- const [error, resetError] = useErrorBoundary();
- //if there is an error, reset when unloading this component
- useEffect(() => (error ? resetError : undefined));
- if (error) {
- return (
- <div>
- <p>Error was thrown trying to render</p>
- {selected && (
- <ul>
- <li>
- <b>group</b>: {selected.group}
- </li>
- <li>
- <b>component</b>: {selected.component}
- </li>
- <li>
- <b>example</b>: {selected.name}
- </li>
- <li>
- <b>args</b>:{" "}
- <pre>{JSON.stringify(selected.render.args, undefined, 2)}</pre>
- </li>
- </ul>
- )}
- <p>{error.message}</p>
- <pre>{error.stack}</pre>
- </div>
- );
- }
- return <Fragment>{children}</Fragment>;
-}
-
-function getSelectionFromLocationHash(hash: string): ExampleItem | undefined {
- if (!hash) return undefined;
- const parts = hash.substring(1).split("-");
- if (parts.length < 3) return undefined;
- return findByGroupComponentName(
- decodeURIComponent(parts[0]),
- decodeURIComponent(parts[1]),
- decodeURIComponent(parts[2]),
- );
-}
-
-function Application(): VNode {
- const initialSelection = getSelectionFromLocationHash(location.hash);
- const [selected, updateSelected] = useState<ExampleItem | undefined>(
- initialSelection,
- );
- useEffect(() => {
- if (location.hash) {
- const hash = location.hash.substring(1);
- const found = document.getElementById(hash);
- if (found) {
- setTimeout(() => {
- found.scrollIntoView({
- block: "center",
- });
- }, 10);
- }
- }
- }, []);
-
- const ExampleContent = getContentForExample(selected);
-
- const GroupWrapper = getWrapperForGroup(selected?.group || "default");
- const [sidebarWidth, setSidebarWidth] = useState(200);
-
- return (
- <Page>
- <LiveReload />
- <SideBar style={{ "--with-size": `${sidebarWidth}px` }}>
- {allExamples.map((e) => (
- <ExampleList
- key={e.title}
- name={e.title}
- list={e.list}
- selected={selected}
- onSelectStory={(item, htmlId) => {
- document.getElementById(htmlId)?.scrollIntoView({
- block: "center",
- });
- updateSelected(item);
- }}
- />
- ))}
- <hr />
- </SideBar>
- <ResizeHandle
- onUpdate={(x) => {
- setSidebarWidth((s) => s + x);
- }}
- />
- <Content>
- <ErrorReport selected={selected}>
- <GroupWrapper>
- <ExampleContent />
- </GroupWrapper>
- </ErrorReport>
- </Content>
- </Page>
- );
-}
-
-if (document.readyState === "loading") {
- document.addEventListener("DOMContentLoaded", main);
-} else {
- main();
-}
-function main(): void {
- try {
- const container = document.getElementById("container");
- if (!container) {
- throw Error("container not found, can't mount page contents");
- }
- render(<Application />, container);
- } catch (e) {
- console.error("got error", e);
- if (e instanceof Error) {
- document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`;
- }
- }
-}
-
-let liveReloadMounted = false;
-function LiveReload({ port = 8002 }: { port?: number }): VNode {
- const [isReloading, setIsReloading] = useState(false);
- useEffect(() => {
- if (!liveReloadMounted) {
- setupLiveReload(port, () => {
- setIsReloading(true);
- window.location.reload();
- });
- liveReloadMounted = true;
- }
- });
-
- if (isReloading) {
- return (
- <div
- style={{
- position: "absolute",
- width: "100%",
- height: "100%",
- backgroundColor: "rgba(0,0,0,0.5)",
- color: "white",
- display: "flex",
- justifyContent: "center",
- }}
- >
- <h1 style={{ margin: "auto" }}>reloading...</h1>
- </div>
- );
- }
- return <Fragment />;
-}
-
-function setupLiveReload(port: number, onReload: () => void): void {
- const protocol = location.protocol === "https:" ? "wss:" : "ws:";
- const host = location.hostname;
- const socketPath = `${protocol}//${host}:${port}/socket`;
-
- const ws = new WebSocket(socketPath);
- ws.onmessage = (message) => {
- const event = JSON.parse(message.data);
- if (event.type === "LOG") {
- console.log(event.message);
- }
- if (event.type === "RELOAD") {
- onReload();
- }
- };
- ws.onerror = (error) => {
- console.error(error);
- };
-}
-
-const isFirefox = typeof (window as any)["InstallTrigger"] !== "undefined";
-
-//FIXME: create different entry point for any platform instead of
-//switching in runtime
-if (isFirefox) {
- console.log("Wallet setup for Firefox API");
- setupPlatform(firefoxAPI);
-} else {
- console.log("Wallet setup for Chrome API");
- setupPlatform(chromeAPI);
-}