/*
This file is part of GNU Anastasis
(C) 2021-2022 Anastasis SARL
GNU Anastasis is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Anastasis 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with
GNU Anastasis; see the file COPYING. If not, see
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { setupI18n } from "@gnu-taler/taler-util";
import { ComponentChild, Fragment, h, render, VNode } from "preact";
import { useEffect, useErrorBoundary, useState } from "preact/hooks";
import { strings } from "./i18n/strings.js";
import * as pages from "./pages/home/index.storiesNo.js";
const url = new URL(window.location.href);
const lang = url.searchParams.get("lang") || "en";
setupI18n(lang, strings);
const Page = ({ children }: any) =>
{children}
;
const SideBar = ({ children }: any) => ;
const Content = ({ children }: any) => {children}
;
function parseExampleImport(
group: string,
im: any,
name?: string,
): ComponentItem {
const component = name || im.default.title;
const order: number = im.default.args?.order || 0;
return {
name: component,
order,
examples: Object.entries(im)
.filter(([k]) => k !== "default")
.map(
([name, render]) =>
({
group,
component,
name,
render,
} as ExampleItem),
),
};
}
function SortStories(a: any, b: any): number {
return (a?.order ?? 0) - (b?.order ?? 0);
}
const allExamples = Object.entries({ pages }).map(([title, value]) => {
return {
title,
list: Object.entries(value)
.filter(([name]) => name != "default")
.map(([name, value]) => parseExampleImport(title, value, name))
.sort(SortStories),
};
});
interface ComponentItem {
name: string;
order: number;
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 select example from the list on the left
;
};
const example = findByGroupComponentName(
item.group,
item.component,
item.name,
);
if (!example)
return function ExampleNotFoundMessage() {
return example not found
;
};
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 (
setOpen(!isOpen)}>{name}
{list.map((k) => (
-
- {k.name}
{k.examples.map((r) => {
const e = encodeURIComponent;
const eId = `${e(r.group)}-${e(r.component)}-${e(r.name)}`;
function doSelection(e: any): void {
e.preventDefault();
location.hash = `#${eId}`;
onSelectStory(r, eId);
}
const isSelected =
selected &&
selected.component === r.component &&
selected.group === r.group &&
selected.name === r.name;
return (
-
{r.name}
);
})}
))}
);
}
// function getWrapperForGroup(group: string): FunctionComponent {
// switch (group) {
// case "popup":
// return function PopupWrapper({ children }: any) {
// return (
//
//
// {children}
//
// );
// };
// case "wallet":
// return function WalletWrapper({ children }: any) {
// return (
//
//
//
// {children}
//
// );
// };
// case "cta":
// return function WalletWrapper({ children }: any) {
// return (
//
// {children}
//
// );
// };
// default:
// return Fragment;
// }
// }
function ErrorReport({
children,
selected,
}: {
children: ComponentChild;
selected: ExampleItem | undefined;
}): VNode {
const [error] = useErrorBoundary();
if (error) {
return (
Error was thrown trying to render
{selected && (
)}
{error.message}
{error.stack}
);
}
return {children};
}
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(
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");
return (
{allExamples.map((e) => (
{
document.getElementById(htmlId)?.scrollIntoView({
block: "center",
});
updateSelected(item);
}}
/>
))}
{/* */}
{/* */}
);
}
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(, 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 (
reloading...
);
}
return ;
}
function setupLiveReload(port: number, onReload: () => void): void {
const socketPath = `ws://localhost:8003/socket`;
// 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);
};
ws.onclose = (e) => {
console.log("disconnected", e);
};
}