using web-utils in demobank
This commit is contained in:
parent
d3a6544bc5
commit
5969a44391
@ -18,9 +18,9 @@
|
||||
import esbuild from "esbuild";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import crypto from "crypto";
|
||||
import { sassPlugin } from "esbuild-sass-plugin";
|
||||
import sass from "sass";
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const BASE = process.cwd();
|
||||
|
||||
const preact = path.join(
|
||||
@ -44,14 +44,16 @@ const preactCompatPlugin = {
|
||||
},
|
||||
};
|
||||
|
||||
const entryPoints = ["src/index.tsx"];
|
||||
const entryPoints = ["src/index.tsx", "src/stories.tsx"];
|
||||
|
||||
let GIT_ROOT = BASE;
|
||||
while (!fs.existsSync(path.join(GIT_ROOT, ".git")) && GIT_ROOT !== "/") {
|
||||
GIT_ROOT = path.join(GIT_ROOT, "../");
|
||||
}
|
||||
if (GIT_ROOT === "/") {
|
||||
// eslint-disable-next-line no-undef
|
||||
console.log("not found");
|
||||
// eslint-disable-next-line no-undef
|
||||
process.exit(1);
|
||||
}
|
||||
const GIT_HASH = GIT_ROOT === "/" ? undefined : git_hash();
|
||||
@ -86,6 +88,26 @@ function copyFilesPlugin(options) {
|
||||
};
|
||||
}
|
||||
|
||||
const DEFAULT_SASS_FILTER = /\.(s[ac]ss|css)$/
|
||||
|
||||
const buildSassPlugin = {
|
||||
name: "custom-build-sass",
|
||||
setup(build) {
|
||||
|
||||
build.onLoad({ filter: DEFAULT_SASS_FILTER }, ({ path: file }) => {
|
||||
const resolveDir = path.dirname(file)
|
||||
const { css: contents } = sass.compile(file, { loadPaths: ["./"] })
|
||||
|
||||
return {
|
||||
resolveDir,
|
||||
loader: 'css',
|
||||
contents
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
export const buildConfig = {
|
||||
entryPoints: [...entryPoints],
|
||||
bundle: true,
|
||||
@ -95,6 +117,10 @@ export const buildConfig = {
|
||||
".svg": "file",
|
||||
".png": "dataurl",
|
||||
".jpeg": "dataurl",
|
||||
'.ttf': 'file',
|
||||
'.woff': 'file',
|
||||
'.woff2': 'file',
|
||||
'.eot': 'file',
|
||||
},
|
||||
target: ["es6"],
|
||||
format: "esm",
|
||||
@ -108,17 +134,14 @@ export const buildConfig = {
|
||||
},
|
||||
plugins: [
|
||||
preactCompatPlugin,
|
||||
sassPlugin(),
|
||||
copyFilesPlugin([
|
||||
{
|
||||
src: "static/index.html",
|
||||
dest: "dist/index.html",
|
||||
src: "./src/index.html",
|
||||
dest: "./dist/index.html",
|
||||
},
|
||||
]),
|
||||
buildSassPlugin
|
||||
],
|
||||
};
|
||||
|
||||
esbuild.build(buildConfig).catch((e) => {
|
||||
console.log(e);
|
||||
process.exit(1);
|
||||
});
|
||||
await esbuild.build(buildConfig)
|
||||
|
30
packages/demobank-ui/dev.mjs
Executable file
30
packages/demobank-ui/dev.mjs
Executable file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env node
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { serve } from "@gnu-taler/web-util/lib/index.node";
|
||||
import esbuild from "esbuild";
|
||||
import { buildConfig } from "./build.mjs";
|
||||
|
||||
buildConfig.inject = ['./node_modules/@gnu-taler/web-util/lib/live-reload.mjs']
|
||||
|
||||
serve({
|
||||
folder: './dist',
|
||||
port: 8080,
|
||||
source: './src',
|
||||
development: true,
|
||||
onUpdate: async () => esbuild.build(buildConfig)
|
||||
})
|
@ -11,13 +11,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@gnu-taler/taler-util": "workspace:*",
|
||||
"@gnu-taler/web-util": "workspace:*",
|
||||
"date-fns": "2.29.3",
|
||||
"history": "4.10.1",
|
||||
"jed": "1.1.1",
|
||||
"preact": "10.6.5",
|
||||
"preact": "10.11.3",
|
||||
"preact-router": "3.2.1",
|
||||
"qrcode-generator": "^1.4.4",
|
||||
"react": "npm:@preact/compat@^17.1.2",
|
||||
"swr": "1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -30,11 +30,10 @@
|
||||
"bulma-checkbox": "^1.1.1",
|
||||
"bulma-radio": "^1.1.1",
|
||||
"esbuild": "^0.15.12",
|
||||
"esbuild-sass-plugin": "^2.4.0",
|
||||
"eslint": "^8.26.0",
|
||||
"eslint-config-preact": "^1.2.0",
|
||||
"po2json": "^0.4.5",
|
||||
"sass": "1.32.13",
|
||||
"sass": "1.56.1",
|
||||
"typescript": "^4.4.4"
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import { h, VNode } from "preact";
|
||||
import { Translate } from "../../i18n";
|
||||
import { useTranslationContext } from "../../context/translation.js";
|
||||
|
||||
interface Props {
|
||||
mobile?: boolean;
|
||||
@ -31,6 +31,7 @@ export function Sidebar({ mobile }: Props): VNode {
|
||||
const config = { version: "none" };
|
||||
// FIXME: add replacement for __VERSION__ with the current version
|
||||
const process = { env: { __VERSION__: "0.0.0" } };
|
||||
const { i18n } = useTranslationContext();
|
||||
|
||||
return (
|
||||
<aside class="aside is-placed-left is-expanded">
|
||||
@ -49,20 +50,20 @@ export function Sidebar({ mobile }: Props): VNode {
|
||||
</div>
|
||||
<div class="menu is-menu-main">
|
||||
<p class="menu-label">
|
||||
<Translate>Bank menu</Translate>
|
||||
<i18n.Translate>Bank menu</i18n.Translate>
|
||||
</p>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
<div class="ml-4">
|
||||
<span class="menu-item-label">
|
||||
<Translate>Select option1</Translate>
|
||||
<i18n.Translate>Select option1</i18n.Translate>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="ml-4">
|
||||
<span class="menu-item-label">
|
||||
<Translate>Select option2</Translate>
|
||||
<i18n.Translate>Select option2</i18n.Translate>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
import { h, VNode } from "preact";
|
||||
import { useState } from "preact/hooks";
|
||||
import { useTranslator } from "../../i18n";
|
||||
import { useTranslationContext } from "../../context/translation.js";
|
||||
import "../../scss/DurationPicker.scss";
|
||||
|
||||
export interface Props {
|
||||
@ -46,13 +46,13 @@ export function DurationPicker({
|
||||
const ms = ss * 60;
|
||||
const hs = ms * 60;
|
||||
const ds = hs * 24;
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
|
||||
return (
|
||||
<div class="rdp-picker">
|
||||
{days && (
|
||||
<DurationColumn
|
||||
unit={i18n`days`}
|
||||
unit={i18n.str`days`}
|
||||
max={99}
|
||||
value={Math.floor(value / ds)}
|
||||
onDecrease={value >= ds ? () => onChange(value - ds) : undefined}
|
||||
@ -62,7 +62,7 @@ export function DurationPicker({
|
||||
)}
|
||||
{hours && (
|
||||
<DurationColumn
|
||||
unit={i18n`hours`}
|
||||
unit={i18n.str`hours`}
|
||||
max={23}
|
||||
min={1}
|
||||
value={Math.floor(value / hs) % 24}
|
||||
@ -73,7 +73,7 @@ export function DurationPicker({
|
||||
)}
|
||||
{minutes && (
|
||||
<DurationColumn
|
||||
unit={i18n`minutes`}
|
||||
unit={i18n.str`minutes`}
|
||||
max={59}
|
||||
min={1}
|
||||
value={Math.floor(value / ms) % 60}
|
||||
@ -84,7 +84,7 @@ export function DurationPicker({
|
||||
)}
|
||||
{seconds && (
|
||||
<DurationColumn
|
||||
unit={i18n`seconds`}
|
||||
unit={i18n.str`seconds`}
|
||||
max={59}
|
||||
value={Math.floor(value / ss) % 60}
|
||||
onDecrease={value >= ss ? () => onChange(value - ss) : undefined}
|
||||
|
@ -19,27 +19,42 @@
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { i18n, setupI18n } from "@gnu-taler/taler-util";
|
||||
import { createContext, h, VNode } from "preact";
|
||||
import { useContext, useEffect } from "preact/hooks";
|
||||
import { useLang } from "../hooks/index.js";
|
||||
import * as jedLib from "jed";
|
||||
import { useLang } from "../hooks/useLang.js";
|
||||
import { strings } from "../i18n/strings.js";
|
||||
|
||||
interface Type {
|
||||
lang: string;
|
||||
handler: any;
|
||||
supportedLang: { [id in keyof typeof supportedLang]: string };
|
||||
changeLanguage: (l: string) => void;
|
||||
i18n: typeof i18n;
|
||||
isSaved: boolean;
|
||||
}
|
||||
|
||||
const supportedLang = {
|
||||
es: "Español [es]",
|
||||
ja: "日本語 [ja]",
|
||||
en: "English [en]",
|
||||
fr: "Français [fr]",
|
||||
de: "Deutsch [de]",
|
||||
sv: "Svenska [sv]",
|
||||
it: "Italiano [it]",
|
||||
// ko: "한국어 [ko]",
|
||||
// ru: "Ру́сский язы́к [ru]",
|
||||
tr: "Türk [tr]",
|
||||
navigator: "Defined by navigator",
|
||||
};
|
||||
|
||||
const initial = {
|
||||
lang: "en",
|
||||
handler: null,
|
||||
supportedLang,
|
||||
changeLanguage: () => {
|
||||
/**
|
||||
* This function will be replaced by one with
|
||||
* the same signature _but_ coming from the state.
|
||||
* FIXME: clarify this design.
|
||||
*/
|
||||
// do not change anything
|
||||
},
|
||||
i18n,
|
||||
isSaved: false,
|
||||
};
|
||||
const Context = createContext<Type>(initial);
|
||||
|
||||
@ -55,14 +70,23 @@ export const TranslationProvider = ({
|
||||
children,
|
||||
forceLang,
|
||||
}: Props): VNode => {
|
||||
const [lang, changeLanguage] = useLang(initial);
|
||||
const [lang, changeLanguage, isSaved] = useLang(initial);
|
||||
useEffect(() => {
|
||||
if (forceLang) changeLanguage(forceLang);
|
||||
if (forceLang) {
|
||||
changeLanguage(forceLang);
|
||||
}
|
||||
});
|
||||
console.log("lang store", strings);
|
||||
const handler = new jedLib.Jed(strings[lang] || strings["en"]);
|
||||
useEffect(() => {
|
||||
setupI18n(lang, strings);
|
||||
}, [lang]);
|
||||
if (forceLang) {
|
||||
setupI18n(forceLang, strings);
|
||||
} else {
|
||||
setupI18n(lang, strings);
|
||||
}
|
||||
|
||||
return h(Context.Provider, {
|
||||
value: { lang, handler, changeLanguage },
|
||||
value: { lang, changeLanguage, supportedLang, i18n, isSaved },
|
||||
children,
|
||||
});
|
||||
};
|
||||
|
@ -19,7 +19,8 @@
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { StateUpdater, useState } from "preact/hooks";
|
||||
import { StateUpdater } from "preact/hooks";
|
||||
import { useLocalStorage, useNotNullLocalStorage } from "./useLocalStorage.js";
|
||||
export type ValueOrFunction<T> = T | ((p: T) => T);
|
||||
|
||||
const calculateRootPath = () => {
|
||||
@ -68,79 +69,3 @@ export function useBackendInstanceToken(
|
||||
|
||||
return [token, setToken];
|
||||
}
|
||||
|
||||
export function useLang(initial?: string): [string, StateUpdater<string>] {
|
||||
const browserLang =
|
||||
typeof window !== "undefined"
|
||||
? navigator.language || (navigator as any).userLanguage
|
||||
: undefined;
|
||||
const defaultLang = (browserLang || initial || "en").substring(0, 2);
|
||||
const [value, setValue] = useNotNullLocalStorage(
|
||||
"lang-preference",
|
||||
defaultLang,
|
||||
);
|
||||
function updateValue(newValue: string | ((v: string) => string)) {
|
||||
if (document.body.parentElement) {
|
||||
const htmlElement = document.body.parentElement;
|
||||
if (typeof newValue === "string") {
|
||||
htmlElement.lang = newValue;
|
||||
setValue(newValue);
|
||||
} else if (typeof newValue === "function")
|
||||
setValue((old) => {
|
||||
const nv = newValue(old);
|
||||
htmlElement.lang = nv;
|
||||
return nv;
|
||||
});
|
||||
} else setValue(newValue);
|
||||
}
|
||||
return [value, updateValue];
|
||||
}
|
||||
|
||||
export function useLocalStorage(
|
||||
key: string,
|
||||
initialValue?: string,
|
||||
): [string | undefined, StateUpdater<string | undefined>] {
|
||||
const [storedValue, setStoredValue] = useState<string | undefined>(
|
||||
(): string | undefined => {
|
||||
return typeof window !== "undefined"
|
||||
? window.localStorage.getItem(key) || initialValue
|
||||
: initialValue;
|
||||
},
|
||||
);
|
||||
|
||||
const setValue = (
|
||||
value?: string | ((val?: string) => string | undefined),
|
||||
) => {
|
||||
setStoredValue((p) => {
|
||||
const toStore = value instanceof Function ? value(p) : value;
|
||||
if (typeof window !== "undefined")
|
||||
if (!toStore) window.localStorage.removeItem(key);
|
||||
else window.localStorage.setItem(key, toStore);
|
||||
|
||||
return toStore;
|
||||
});
|
||||
};
|
||||
|
||||
return [storedValue, setValue];
|
||||
}
|
||||
|
||||
export function useNotNullLocalStorage(
|
||||
key: string,
|
||||
initialValue: string,
|
||||
): [string, StateUpdater<string>] {
|
||||
const [storedValue, setStoredValue] = useState<string>((): string => {
|
||||
return typeof window !== "undefined"
|
||||
? window.localStorage.getItem(key) || initialValue
|
||||
: initialValue;
|
||||
});
|
||||
|
||||
const setValue = (value: string | ((val: string) => string)) => {
|
||||
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
||||
setStoredValue(valueToStore);
|
||||
if (typeof window !== "undefined")
|
||||
if (!valueToStore) window.localStorage.removeItem(key);
|
||||
else window.localStorage.setItem(key, valueToStore);
|
||||
};
|
||||
|
||||
return [storedValue, setValue];
|
||||
}
|
||||
|
30
packages/demobank-ui/src/hooks/useLang.ts
Normal file
30
packages/demobank-ui/src/hooks/useLang.ts
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { useNotNullLocalStorage } from "./useLocalStorage.js";
|
||||
|
||||
function getBrowserLang(): string | undefined {
|
||||
if (window.navigator.languages) return window.navigator.languages[0];
|
||||
if (window.navigator.language) return window.navigator.language;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function useLang(
|
||||
initial?: string,
|
||||
): [string, (s: string) => void, boolean] {
|
||||
const defaultLang = (getBrowserLang() || initial || "en").substring(0, 2);
|
||||
return useNotNullLocalStorage("lang-preference", defaultLang);
|
||||
}
|
80
packages/demobank-ui/src/hooks/useLocalStorage.ts
Normal file
80
packages/demobank-ui/src/hooks/useLocalStorage.ts
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { StateUpdater, useState } from "preact/hooks";
|
||||
|
||||
export function useLocalStorage(
|
||||
key: string,
|
||||
initialValue?: string,
|
||||
): [string | undefined, StateUpdater<string | undefined>] {
|
||||
const [storedValue, setStoredValue] = useState<string | undefined>(
|
||||
(): string | undefined => {
|
||||
return typeof window !== "undefined"
|
||||
? window.localStorage.getItem(key) || initialValue
|
||||
: initialValue;
|
||||
},
|
||||
);
|
||||
|
||||
const setValue = (
|
||||
value?: string | ((val?: string) => string | undefined),
|
||||
): void => {
|
||||
setStoredValue((p) => {
|
||||
const toStore = value instanceof Function ? value(p) : value;
|
||||
if (typeof window !== "undefined") {
|
||||
if (!toStore) {
|
||||
window.localStorage.removeItem(key);
|
||||
} else {
|
||||
window.localStorage.setItem(key, toStore);
|
||||
}
|
||||
}
|
||||
return toStore;
|
||||
});
|
||||
};
|
||||
|
||||
return [storedValue, setValue];
|
||||
}
|
||||
|
||||
//TODO: merge with the above function
|
||||
export function useNotNullLocalStorage(
|
||||
key: string,
|
||||
initialValue: string,
|
||||
): [string, StateUpdater<string>, boolean] {
|
||||
const [storedValue, setStoredValue] = useState<string>((): string => {
|
||||
return typeof window !== "undefined"
|
||||
? window.localStorage.getItem(key) || initialValue
|
||||
: initialValue;
|
||||
});
|
||||
|
||||
const setValue = (value: string | ((val: string) => string)): void => {
|
||||
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
||||
setStoredValue(valueToStore);
|
||||
if (typeof window !== "undefined") {
|
||||
if (!valueToStore) {
|
||||
window.localStorage.removeItem(key);
|
||||
} else {
|
||||
window.localStorage.setItem(key, valueToStore);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const isSaved = window.localStorage.getItem(key) !== null;
|
||||
return [storedValue, setValue, isSaved];
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Translation helpers for React components and template literals.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports
|
||||
*/
|
||||
import { ComponentChild, ComponentChildren, h, Fragment, VNode } from "preact";
|
||||
|
||||
import { useTranslationContext } from "../context/translation";
|
||||
|
||||
export function useTranslator() {
|
||||
const ctx = useTranslationContext();
|
||||
const jed = ctx.handler;
|
||||
return function str(
|
||||
stringSeq: TemplateStringsArray,
|
||||
...values: any[]
|
||||
): string {
|
||||
const s = toI18nString(stringSeq);
|
||||
if (!s) return s;
|
||||
const tr = jed
|
||||
.translate(s)
|
||||
.ifPlural(1, s)
|
||||
.fetch(...values);
|
||||
return tr;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert template strings to a msgid
|
||||
*/
|
||||
function toI18nString(stringSeq: ReadonlyArray<string>): string {
|
||||
let s = "";
|
||||
for (let i = 0; i < stringSeq.length; i++) {
|
||||
s += stringSeq[i];
|
||||
if (i < stringSeq.length - 1) s += `%${i + 1}$s`;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
interface TranslateSwitchProps {
|
||||
target: number;
|
||||
children: ComponentChildren;
|
||||
}
|
||||
|
||||
function stringifyChildren(children: ComponentChildren): string {
|
||||
let n = 1;
|
||||
const ss = (children instanceof Array ? children : [children]).map((c) => {
|
||||
if (typeof c === "string") return c;
|
||||
|
||||
return `%${n++}$s`;
|
||||
});
|
||||
const s = ss.join("").replace(/ +/g, " ").trim();
|
||||
return s;
|
||||
}
|
||||
|
||||
interface TranslateProps {
|
||||
children: ComponentChildren;
|
||||
/**
|
||||
* Component that the translated element should be wrapped in.
|
||||
* Defaults to "div".
|
||||
*/
|
||||
wrap?: any;
|
||||
|
||||
/**
|
||||
* Props to give to the wrapped component.
|
||||
*/
|
||||
wrapProps?: any;
|
||||
}
|
||||
|
||||
function getTranslatedChildren(
|
||||
translation: string,
|
||||
children: ComponentChildren,
|
||||
): ComponentChild[] {
|
||||
const tr = translation.split(/%(\d+)\$s/);
|
||||
const childArray = children instanceof Array ? children : [children];
|
||||
// Merge consecutive string children.
|
||||
const placeholderChildren = Array<ComponentChild>();
|
||||
for (let i = 0; i < childArray.length; i++) {
|
||||
const x = childArray[i];
|
||||
if (x === undefined) continue;
|
||||
else if (typeof x === "string") continue;
|
||||
else placeholderChildren.push(x);
|
||||
}
|
||||
const result = Array<ComponentChild>();
|
||||
for (let i = 0; i < tr.length; i++)
|
||||
if (i % 2 == 0)
|
||||
// Text
|
||||
result.push(tr[i]);
|
||||
else {
|
||||
const childIdx = Number.parseInt(tr[i], 10) - 1;
|
||||
result.push(placeholderChildren[childIdx]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate text node children of this component.
|
||||
* If a child component might produce a text node, it must be wrapped
|
||||
* in a another non-text element.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* <Translate>
|
||||
* Hello. Your score is <span><PlayerScore player={player} /></span>
|
||||
* </Translate>
|
||||
* ```
|
||||
*/
|
||||
export function Translate({ children }: TranslateProps): VNode {
|
||||
const s = stringifyChildren(children);
|
||||
const ctx = useTranslationContext();
|
||||
const translation: string = ctx.handler.ngettext(s, s, 1);
|
||||
const result = getTranslatedChildren(translation, children);
|
||||
return <Fragment>{result}</Fragment>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch translation based on singular or plural based on the target prop.
|
||||
* Should only contain TranslateSingular and TransplatePlural as children.
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* <TranslateSwitch target={n}>
|
||||
* <TranslateSingular>I have {n} apple.</TranslateSingular>
|
||||
* <TranslatePlural>I have {n} apples.</TranslatePlural>
|
||||
* </TranslateSwitch>
|
||||
* ```
|
||||
*/
|
||||
export function TranslateSwitch({ children, target }: TranslateSwitchProps) {
|
||||
let singular: VNode<TranslationPluralProps> | undefined;
|
||||
let plural: VNode<TranslationPluralProps> | undefined;
|
||||
// const children = this.props.children;
|
||||
if (children)
|
||||
(children instanceof Array ? children : [children]).forEach(
|
||||
(child: any) => {
|
||||
if (child.type === TranslatePlural) plural = child;
|
||||
|
||||
if (child.type === TranslateSingular) singular = child;
|
||||
},
|
||||
);
|
||||
|
||||
if (!singular || !plural) {
|
||||
console.error("translation not found");
|
||||
return h("span", {}, ["translation not found"]);
|
||||
}
|
||||
singular.props.target = target;
|
||||
plural.props.target = target;
|
||||
// We're looking up the translation based on the
|
||||
// singular, even if we must use the plural form.
|
||||
return singular;
|
||||
}
|
||||
|
||||
interface TranslationPluralProps {
|
||||
children: ComponentChildren;
|
||||
target: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* See [[TranslateSwitch]].
|
||||
*/
|
||||
export function TranslatePlural({
|
||||
children,
|
||||
target,
|
||||
}: TranslationPluralProps): VNode {
|
||||
const s = stringifyChildren(children);
|
||||
const ctx = useTranslationContext();
|
||||
const translation = ctx.handler.ngettext(s, s, 1);
|
||||
const result = getTranslatedChildren(translation, children);
|
||||
return <Fragment>{result}</Fragment>;
|
||||
}
|
||||
|
||||
/**
|
||||
* See [[TranslateSwitch]].
|
||||
*/
|
||||
export function TranslateSingular({
|
||||
children,
|
||||
target,
|
||||
}: TranslationPluralProps): VNode {
|
||||
const s = stringifyChildren(children);
|
||||
const ctx = useTranslationContext();
|
||||
const translation = ctx.handler.ngettext(s, s, target);
|
||||
const result = getTranslatedChildren(translation, children);
|
||||
return <Fragment>{result}</Fragment>;
|
||||
}
|
@ -16,25 +16,26 @@
|
||||
@author Sebastian Javier Marchano
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html
|
||||
lang="en"
|
||||
class="has-aside-left has-aside-mobile-transition has-navbar-fixed-top has-aside-expanded"
|
||||
>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
|
||||
<link
|
||||
rel="icon"
|
||||
href="data:;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAD///////////////////////////////////////////////////////////////////////////////////////////////////7//v38//78/P/+/fz//vz7///+/v/+/f3//vz7///+/v/+/fz//v38///////////////////////+/v3///7+/////////////////////////////////////////////////////////v3//v79///////+/v3///////r28v/ct5//06SG/9Gffv/Xqo7/7N/V/9e2nf/bsJb/6uDW/9Sskf/euKH/+/j2///////+/v3//////+3azv+/eE3/2rWd/9Kkhv/Vr5T/48i2/8J+VP/Qn3//3ryn/795Tf/WrpP/2LCW/8B6T//w4Nb///////Pn4P+/d0v/9u3n/+7d0v/EhV7//v///+HDr//fxLD/zph2/+TJt//8/Pv/woBX//Lm3f/y5dz/v3hN//bu6f/JjGn/4sW0///////Df1j/8OLZ//v6+P+/elH/+vj1//jy7f+/elL//////+zYzP/Eg13//////967p//MlHT/wn5X///////v4Nb/yY1s///////jw7H/06KG////////////z5t9/+fNvf//////x4pn//Pp4v/8+vn/w39X/8WEX///////5s/A/9CbfP//////27Oc/9y2n////////////9itlf/gu6f//////86Vdf/r2Mz//////8SCXP/Df1j//////+7d0v/KkG7//////+HBrf/VpYr////////////RnoH/5sq6///////Ii2n/8ubf//39/P/Cf1j/xohk/+bNvv//////wn5W//Tq4//58/D/wHxV//7+/f/59fH/v3xU//39/P/w4Nf/xIFb///////hw7H/yo9t/+/f1f/AeU3/+/n2/+nSxP/FhmD//////9qzm//Upon/4MSx/96+qf//////xINc/+3bz//48e3/v3hN//Pn3///////6M+//752S//gw6//06aK/8J+VP/kzLr/zZd1/8OCWv/q18r/17KZ/9Ooi//fv6r/v3dK/+vWyP///////v39///////27un/1aeK/9Opjv/m1cf/1KCC/9a0nP/n08T/0Jx8/82YdP/QnHz/16yR//jx7P///////v39///////+/f3///7+///////+//7//v7+///////+/v7//v/+/////////////////////////v7//v79///////////////////+/v/+/Pv//v39///+/v/+/Pv///7+//7+/f/+/Pv//v39//79/P/+/Pv///7+////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
||||
/>
|
||||
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
|
||||
<title>Demobank</title>
|
||||
<!-- Optional customization script. -->
|
||||
<script src="demobank-ui-settings.js"></script>
|
||||
<!-- Entry point for the demobank SPA. -->
|
||||
<script type="module" src="index.js"></script>
|
||||
<link rel="stylesheet" href="index.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import App from "./components/app.js";
|
||||
export default App;
|
||||
import { render, h } from "preact";
|
||||
import "./scss/main.scss";
|
||||
|
||||
const app = document.getElementById("app");
|
||||
|
||||
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { QrCodeSection } from "./QrCodeSection.js";
|
||||
|
||||
export default {
|
||||
title: "Qr Code Selection",
|
||||
};
|
||||
|
||||
export const SimpleExample = {
|
||||
component: QrCodeSection,
|
||||
props: {
|
||||
talerWithdrawUri: "taler://withdraw/asdasdasd",
|
||||
},
|
||||
};
|
55
packages/demobank-ui/src/pages/home/QrCodeSection.tsx
Normal file
55
packages/demobank-ui/src/pages/home/QrCodeSection.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { h, VNode } from "preact";
|
||||
import { useEffect } from "preact/hooks";
|
||||
import { QR } from "../../components/QR.js";
|
||||
import { useTranslationContext } from "../../context/translation.js";
|
||||
|
||||
export function QrCodeSection({
|
||||
talerWithdrawUri,
|
||||
abortButton,
|
||||
}: {
|
||||
talerWithdrawUri: string;
|
||||
abortButton: h.JSX.Element;
|
||||
}): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
useEffect(() => {
|
||||
//Taler Wallet WebExtension is listening to headers response and tab updates.
|
||||
//In the SPA there is no header response with the Taler URI so
|
||||
//this hack manually triggers the tab update after the QR is in the DOM.
|
||||
window.location.hash = `/account/${new Date().getTime()}`;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section id="main" class="content">
|
||||
<h1 class="nav">{i18n.str`Transfer to Taler Wallet`}</h1>
|
||||
<article>
|
||||
<div class="qr-div">
|
||||
<p>{i18n.str`Use this QR code to withdraw to your mobile wallet:`}</p>
|
||||
{QR({ text: talerWithdrawUri })}
|
||||
<p>
|
||||
Click{" "}
|
||||
<a id="linkqr" href={talerWithdrawUri}>{i18n.str`this link`}</a> to
|
||||
open your Taler wallet!
|
||||
</p>
|
||||
<br />
|
||||
{abortButton}
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
);
|
||||
}
|
1
packages/demobank-ui/src/pages/home/index.stories.tsx
Normal file
1
packages/demobank-ui/src/pages/home/index.stories.tsx
Normal file
@ -0,0 +1 @@
|
||||
export * as qr from "./QrCodeSection.stories.js";
|
@ -27,13 +27,16 @@ import {
|
||||
} from "preact/hooks";
|
||||
import talerLogo from "../../assets/logo-white.svg";
|
||||
import { LangSelectorLikePy as LangSelector } from "../../components/menu/LangSelector.js";
|
||||
import { QR } from "../../components/QR.js";
|
||||
import { useLocalStorage, useNotNullLocalStorage } from "../../hooks/index.js";
|
||||
import { Translate, useTranslator } from "../../i18n/index.js";
|
||||
import "../../scss/main.scss";
|
||||
import {
|
||||
useLocalStorage,
|
||||
useNotNullLocalStorage,
|
||||
} from "../../hooks/useLocalStorage.js";
|
||||
// import { Translate, useTranslator } from "../../i18n/index.js";
|
||||
import { useTranslationContext } from "../../context/translation.js";
|
||||
import { Amounts, HttpStatusCode, parsePaytoUri } from "@gnu-taler/taler-util";
|
||||
import { createHashHistory } from "history";
|
||||
import Router, { Route, route } from "preact-router";
|
||||
import { QrCodeSection } from "./QrCodeSection.js";
|
||||
|
||||
interface BankUiSettings {
|
||||
allowRegistrations: boolean;
|
||||
@ -987,7 +990,7 @@ async function registrationCall(
|
||||
|
||||
function ErrorBanner(Props: any): VNode | null {
|
||||
const [pageState, pageStateSetter] = Props.pageState;
|
||||
// const i18n = useTranslator();
|
||||
// const { i18n } = useTranslationContext();
|
||||
if (!pageState.error) return null;
|
||||
|
||||
const rval = (
|
||||
@ -1041,7 +1044,7 @@ function StatusBanner(Props: any): VNode | null {
|
||||
}
|
||||
|
||||
function BankFrame(Props: any): VNode {
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
const [pageState, pageStateSetter] = useContext(PageContext);
|
||||
console.log("BankFrame state", pageState);
|
||||
const logOut = (
|
||||
@ -1062,7 +1065,7 @@ function BankFrame(Props: any): VNode {
|
||||
};
|
||||
});
|
||||
}}
|
||||
>{i18n`Logout`}</a>
|
||||
>{i18n.str`Logout`}</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1080,7 +1083,7 @@ function BankFrame(Props: any): VNode {
|
||||
class="demobar"
|
||||
style="display: flex; flex-direction: row; justify-content: space-between;"
|
||||
>
|
||||
<a href="#main" class="skip">{i18n`Skip to main content`}</a>
|
||||
<a href="#main" class="skip">{i18n.str`Skip to main content`}</a>
|
||||
<div style="max-width: 50em; margin-left: 2em;">
|
||||
<h1>
|
||||
<span class="it">
|
||||
@ -1089,7 +1092,7 @@ function BankFrame(Props: any): VNode {
|
||||
</h1>
|
||||
{maybeDemoContent(
|
||||
<p>
|
||||
<Translate>
|
||||
<i18n.Translate>
|
||||
This part of the demo shows how a bank that supports Taler
|
||||
directly would work. In addition to using your own bank account,
|
||||
you can also see the transaction history of some{" "}
|
||||
@ -1100,14 +1103,14 @@ function BankFrame(Props: any): VNode {
|
||||
Public Accounts
|
||||
</a>
|
||||
.
|
||||
</Translate>
|
||||
</i18n.Translate>
|
||||
</p>,
|
||||
)}
|
||||
</div>
|
||||
<a href="https://taler.net/">
|
||||
<img
|
||||
src={talerLogo}
|
||||
alt={i18n`Taler logo`}
|
||||
alt={i18n.str`Taler logo`}
|
||||
height="100"
|
||||
width="224"
|
||||
style="margin: 2em 2em"
|
||||
@ -1168,7 +1171,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
const [rawPaytoInput, rawPaytoInputSetter] = useState<string | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
const { focus, backendState } = Props;
|
||||
const amountRegex = "^[0-9]+(.[0-9]+)?$";
|
||||
const ibanRegex = "^[A-Z][A-Z][0-9]+$";
|
||||
@ -1193,17 +1196,17 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
? undefined
|
||||
: undefinedIfEmpty({
|
||||
iban: !submitData.iban
|
||||
? i18n`Missing IBAN`
|
||||
? i18n.str`Missing IBAN`
|
||||
: !/^[A-Z0-9]*$/.test(submitData.iban)
|
||||
? i18n`IBAN should have just uppercased letters and numbers`
|
||||
? i18n.str`IBAN should have just uppercased letters and numbers`
|
||||
: undefined,
|
||||
subject: !submitData.subject ? i18n`Missing subject` : undefined,
|
||||
subject: !submitData.subject ? i18n.str`Missing subject` : undefined,
|
||||
amount: !submitData.amount
|
||||
? i18n`Missing amount`
|
||||
? i18n.str`Missing amount`
|
||||
: !(parsedAmount = Amounts.parse(`${currency}:${submitData.amount}`))
|
||||
? i18n`Amount is not valid`
|
||||
? i18n.str`Amount is not valid`
|
||||
: Amounts.isZero(parsedAmount)
|
||||
? i18n`Should be greater than 0`
|
||||
? i18n.str`Should be greater than 0`
|
||||
: undefined,
|
||||
});
|
||||
|
||||
@ -1212,7 +1215,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
<div>
|
||||
<div class="pure-form" name="wire-transfer-form">
|
||||
<p>
|
||||
<label for="iban">{i18n`Receiver IBAN:`}</label>
|
||||
<label for="iban">{i18n.str`Receiver IBAN:`}</label>
|
||||
<input
|
||||
ref={ref}
|
||||
type="text"
|
||||
@ -1235,7 +1238,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
isDirty={submitData?.iban !== undefined}
|
||||
/>
|
||||
<br />
|
||||
<label for="subject">{i18n`Transfer subject:`}</label>
|
||||
<label for="subject">{i18n.str`Transfer subject:`}</label>
|
||||
<input
|
||||
type="text"
|
||||
name="subject"
|
||||
@ -1256,7 +1259,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
isDirty={submitData?.subject !== undefined}
|
||||
/>
|
||||
<br />
|
||||
<label for="amount">{i18n`Amount:`}</label>
|
||||
<label for="amount">{i18n.str`Amount:`}</label>
|
||||
<input
|
||||
type="number"
|
||||
name="amount"
|
||||
@ -1309,7 +1312,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
...prevState,
|
||||
|
||||
error: {
|
||||
title: i18n`Field(s) missing.`,
|
||||
title: i18n.str`Field(s) missing.`,
|
||||
},
|
||||
}));
|
||||
return;
|
||||
@ -1358,7 +1361,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
}));
|
||||
}}
|
||||
>
|
||||
{i18n`Want to try the raw payto://-format?`}
|
||||
{i18n.str`Want to try the raw payto://-format?`}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
@ -1366,18 +1369,18 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
|
||||
const errorsPayto = undefinedIfEmpty({
|
||||
rawPaytoInput: !rawPaytoInput
|
||||
? i18n`Missing payto address`
|
||||
? i18n.str`Missing payto address`
|
||||
: !parsePaytoUri(rawPaytoInput)
|
||||
? i18n`Payto does not follow the pattern`
|
||||
? i18n.str`Payto does not follow the pattern`
|
||||
: undefined,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>{i18n`Transfer money to account identified by payto:// URI:`}</p>
|
||||
<p>{i18n.str`Transfer money to account identified by payto:// URI:`}</p>
|
||||
<div class="pure-form" name="payto-form">
|
||||
<p>
|
||||
<label for="address">{i18n`payto URI:`}</label>
|
||||
<label for="address">{i18n.str`payto URI:`}</label>
|
||||
<input
|
||||
name="address"
|
||||
type="text"
|
||||
@ -1386,7 +1389,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
id="address"
|
||||
value={rawPaytoInput ?? ""}
|
||||
required
|
||||
placeholder={i18n`payto address`}
|
||||
placeholder={i18n.str`payto address`}
|
||||
// pattern={`payto://iban/[A-Z][A-Z][0-9]+?message=[a-zA-Z0-9 ]+&amount=${currency}:[0-9]+(.[0-9]+)?`}
|
||||
onInput={(e): void => {
|
||||
rawPaytoInputSetter(e.currentTarget.value);
|
||||
@ -1410,7 +1413,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
class="pure-button pure-button-primary"
|
||||
type="submit"
|
||||
disabled={!!errorsPayto}
|
||||
value={i18n`Send`}
|
||||
value={i18n.str`Send`}
|
||||
onClick={async () => {
|
||||
// empty string evaluates to false.
|
||||
if (!rawPaytoInput) {
|
||||
@ -1444,7 +1447,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
}));
|
||||
}}
|
||||
>
|
||||
{i18n`Use wire-transfer form?`}
|
||||
{i18n.str`Use wire-transfer form?`}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
@ -1459,7 +1462,7 @@ function PaytoWireTransfer(Props: any): VNode {
|
||||
function TalerWithdrawalConfirmationQuestion(Props: any): VNode {
|
||||
const [pageState, pageStateSetter] = useContext(PageContext);
|
||||
const { backendState } = Props;
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
const captchaNumbers = {
|
||||
a: Math.floor(Math.random() * 10),
|
||||
b: Math.floor(Math.random() * 10),
|
||||
@ -1468,15 +1471,15 @@ function TalerWithdrawalConfirmationQuestion(Props: any): VNode {
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<h1 class="nav">{i18n`Confirm Withdrawal`}</h1>
|
||||
<h1 class="nav">{i18n.str`Confirm Withdrawal`}</h1>
|
||||
<article>
|
||||
<div class="challenge-div">
|
||||
<form class="challenge-form">
|
||||
<div class="pure-form" id="captcha" name="capcha-form">
|
||||
<h2>{i18n`Authorize withdrawal by solving challenge`}</h2>
|
||||
<h2>{i18n.str`Authorize withdrawal by solving challenge`}</h2>
|
||||
<p>
|
||||
<label for="answer">
|
||||
{i18n`What is`}
|
||||
{i18n.str`What is`}
|
||||
<em>
|
||||
{captchaNumbers.a} + {captchaNumbers.b}
|
||||
</em>
|
||||
@ -1514,12 +1517,12 @@ function TalerWithdrawalConfirmationQuestion(Props: any): VNode {
|
||||
...prevState,
|
||||
|
||||
error: {
|
||||
title: i18n`Answer is wrong.`,
|
||||
title: i18n.str`Answer is wrong.`,
|
||||
},
|
||||
}));
|
||||
}}
|
||||
>
|
||||
{i18n`Confirm`}
|
||||
{i18n.str`Confirm`}
|
||||
</button>
|
||||
|
||||
<button
|
||||
@ -1532,18 +1535,18 @@ function TalerWithdrawalConfirmationQuestion(Props: any): VNode {
|
||||
)
|
||||
}
|
||||
>
|
||||
{i18n`Cancel`}
|
||||
{i18n.str`Cancel`}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
<div class="hint">
|
||||
<p>
|
||||
<Translate>
|
||||
<i18n.Translate>
|
||||
A this point, a <b>real</b> bank would ask for an additional
|
||||
authentication proof (PIN/TAN, one time password, ..), instead
|
||||
of a simple calculation.
|
||||
</Translate>
|
||||
</i18n.Translate>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -1552,40 +1555,6 @@ function TalerWithdrawalConfirmationQuestion(Props: any): VNode {
|
||||
);
|
||||
}
|
||||
|
||||
function QrCodeSection({
|
||||
talerWithdrawUri,
|
||||
abortButton,
|
||||
}: {
|
||||
talerWithdrawUri: string;
|
||||
abortButton: h.JSX.Element;
|
||||
}): VNode {
|
||||
const i18n = useTranslator();
|
||||
useEffect(() => {
|
||||
//Taler Wallet WebExtension is listening to headers response and tab updates.
|
||||
//In the SPA there is no header response with the Taler URI so
|
||||
//this hack manually triggers the tab update after the QR is in the DOM.
|
||||
window.location.hash = `/account/${new Date().getTime()}`;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section id="main" class="content">
|
||||
<h1 class="nav">{i18n`Transfer to Taler Wallet`}</h1>
|
||||
<article>
|
||||
<div class="qr-div">
|
||||
<p>{i18n`Use this QR code to withdraw to your mobile wallet:`}</p>
|
||||
{QR({ text: talerWithdrawUri })}
|
||||
<p>
|
||||
Click <a id="linkqr" href={talerWithdrawUri}>{i18n`this link`}</a>{" "}
|
||||
to open your Taler wallet!
|
||||
</p>
|
||||
<br />
|
||||
{abortButton}
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offer the QR code (and a clickable taler://-link) to
|
||||
* permit the passing of exchange and reserve details to
|
||||
@ -1595,7 +1564,7 @@ function TalerWithdrawalQRCode(Props: any): VNode {
|
||||
// turns true when the wallet POSTed the reserve details:
|
||||
const [pageState, pageStateSetter] = useContext(PageContext);
|
||||
const { withdrawalId, talerWithdrawUri, accountLabel, backendState } = Props;
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
const abortButton = (
|
||||
<a
|
||||
class="pure-button btn-cancel"
|
||||
@ -1609,7 +1578,7 @@ function TalerWithdrawalQRCode(Props: any): VNode {
|
||||
};
|
||||
});
|
||||
}}
|
||||
>{i18n`Abort`}</a>
|
||||
>{i18n.str`Abort`}</a>
|
||||
);
|
||||
|
||||
console.log(`Showing withdraw URI: ${talerWithdrawUri}`);
|
||||
@ -1629,7 +1598,7 @@ function TalerWithdrawalQRCode(Props: any): VNode {
|
||||
...prevState,
|
||||
|
||||
error: {
|
||||
title: i18n`withdrawal (${withdrawalId}) was never (correctly) created at the bank...`,
|
||||
title: i18n.str`withdrawal (${withdrawalId}) was never (correctly) created at the bank...`,
|
||||
},
|
||||
}));
|
||||
return (
|
||||
@ -1643,7 +1612,7 @@ function TalerWithdrawalQRCode(Props: any): VNode {
|
||||
|
||||
// data didn't arrive yet and wallet didn't communicate:
|
||||
if (typeof data === "undefined")
|
||||
return <p>{i18n`Waiting the bank to create the operation...`}</p>;
|
||||
return <p>{i18n.str`Waiting the bank to create the operation...`}</p>;
|
||||
|
||||
/**
|
||||
* Wallet didn't communicate withdrawal details yet:
|
||||
@ -1657,7 +1626,7 @@ function TalerWithdrawalQRCode(Props: any): VNode {
|
||||
withdrawalInProgress: false,
|
||||
|
||||
error: {
|
||||
title: i18n`This withdrawal was aborted!`,
|
||||
title: i18n.str`This withdrawal was aborted!`,
|
||||
},
|
||||
};
|
||||
});
|
||||
@ -1680,7 +1649,7 @@ function TalerWithdrawalQRCode(Props: any): VNode {
|
||||
function WalletWithdraw(Props: any): VNode {
|
||||
const { backendState, pageStateSetter, focus } = Props;
|
||||
const currency = useContext(CurrencyContext);
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
let submitAmount = "5.00";
|
||||
const amountRegex = "^[0-9]+(.[0-9]+)?$";
|
||||
|
||||
@ -1691,7 +1660,8 @@ function WalletWithdraw(Props: any): VNode {
|
||||
return (
|
||||
<div id="reserve-form" class="pure-form" name="tform">
|
||||
<p>
|
||||
<label for="withdraw-amount">{i18n`Amount to withdraw:`}</label>
|
||||
<label for="withdraw-amount">{i18n.str`Amount to withdraw:`}</label>
|
||||
|
||||
<input
|
||||
type="number"
|
||||
ref={ref}
|
||||
@ -1724,7 +1694,7 @@ function WalletWithdraw(Props: any): VNode {
|
||||
id="select-exchange"
|
||||
class="pure-button pure-button-primary"
|
||||
type="submit"
|
||||
value={i18n`Withdraw`}
|
||||
value={i18n.str`Withdraw`}
|
||||
onClick={() => {
|
||||
submitAmount = validateAmount(submitAmount);
|
||||
/**
|
||||
@ -1753,7 +1723,7 @@ function WalletWithdraw(Props: any): VNode {
|
||||
function PaymentOptions(Props: any): VNode {
|
||||
const { backendState, pageStateSetter, focus } = Props;
|
||||
const currency = useContext(CurrencyContext);
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
|
||||
const [tab, setTab] = useState<"charge-wallet" | "wire-transfer">(
|
||||
"charge-wallet",
|
||||
@ -1769,7 +1739,7 @@ function PaymentOptions(Props: any): VNode {
|
||||
setTab("charge-wallet");
|
||||
}}
|
||||
>
|
||||
{i18n`Obtain digital cash`}
|
||||
{i18n.str`Obtain digital cash`}
|
||||
</button>
|
||||
<button
|
||||
class={tab === "wire-transfer" ? "tablinks active" : "tablinks"}
|
||||
@ -1777,12 +1747,12 @@ function PaymentOptions(Props: any): VNode {
|
||||
setTab("wire-transfer");
|
||||
}}
|
||||
>
|
||||
{i18n`Transfer to bank account`}
|
||||
{i18n.str`Transfer to bank account`}
|
||||
</button>
|
||||
</div>
|
||||
{tab === "charge-wallet" && (
|
||||
<div id="charge-wallet" class="tabcontent active">
|
||||
<h3>{i18n`Obtain digital cash`}</h3>
|
||||
<h3>{i18n.str`Obtain digital cash`}</h3>
|
||||
<WalletWithdraw
|
||||
backendState={backendState}
|
||||
focus
|
||||
@ -1792,7 +1762,7 @@ function PaymentOptions(Props: any): VNode {
|
||||
)}
|
||||
{tab === "wire-transfer" && (
|
||||
<div id="wire-transfer" class="tabcontent active">
|
||||
<h3>{i18n`Transfer to bank account`}</h3>
|
||||
<h3>{i18n.str`Transfer to bank account`}</h3>
|
||||
<PaytoWireTransfer
|
||||
backendState={backendState}
|
||||
focus
|
||||
@ -1807,7 +1777,7 @@ function PaymentOptions(Props: any): VNode {
|
||||
|
||||
function RegistrationButton(Props: any): VNode {
|
||||
const { backendStateSetter, pageStateSetter } = Props;
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
if (bankUiSettings.allowRegistrations)
|
||||
return (
|
||||
<button
|
||||
@ -1816,7 +1786,7 @@ function RegistrationButton(Props: any): VNode {
|
||||
route("/register");
|
||||
}}
|
||||
>
|
||||
{i18n`Register`}
|
||||
{i18n.str`Register`}
|
||||
</button>
|
||||
);
|
||||
|
||||
@ -1834,7 +1804,7 @@ function undefinedIfEmpty<T extends object>(obj: T): T | undefined {
|
||||
function LoginForm(Props: any): VNode {
|
||||
const { backendStateSetter, pageStateSetter } = Props;
|
||||
const [submitData, submitDataSetter] = useCredentialsRequestType();
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
useEffect(() => {
|
||||
ref.current?.focus();
|
||||
@ -1843,17 +1813,17 @@ function LoginForm(Props: any): VNode {
|
||||
const errors = !submitData
|
||||
? undefined
|
||||
: undefinedIfEmpty({
|
||||
username: !submitData.username ? i18n`Missing username` : undefined,
|
||||
password: !submitData.password ? i18n`Missing password` : undefined,
|
||||
username: !submitData.username ? i18n.str`Missing username` : undefined,
|
||||
password: !submitData.password ? i18n.str`Missing password` : undefined,
|
||||
});
|
||||
|
||||
return (
|
||||
<div class="login-div">
|
||||
<form action="javascript:void(0);" class="login-form">
|
||||
<div class="pure-form">
|
||||
<h2>{i18n`Please login!`}</h2>
|
||||
<h2>{i18n.str`Please login!`}</h2>
|
||||
<p class="unameFieldLabel loginFieldLabel formFieldLabel">
|
||||
<label for="username">{i18n`Username:`}</label>
|
||||
<label for="username">{i18n.str`Username:`}</label>
|
||||
</p>
|
||||
<input
|
||||
ref={ref}
|
||||
@ -1872,7 +1842,7 @@ function LoginForm(Props: any): VNode {
|
||||
}}
|
||||
/>
|
||||
<p class="passFieldLabel loginFieldLabel formFieldLabel">
|
||||
<label for="password">{i18n`Password:`}</label>
|
||||
<label for="password">{i18n.str`Password:`}</label>
|
||||
</p>
|
||||
<input
|
||||
type="password"
|
||||
@ -1919,7 +1889,7 @@ function LoginForm(Props: any): VNode {
|
||||
});
|
||||
}}
|
||||
>
|
||||
{i18n`Login`}
|
||||
{i18n.str`Login`}
|
||||
</button>
|
||||
{RegistrationButton(Props)}
|
||||
</div>
|
||||
@ -1935,30 +1905,30 @@ function RegistrationForm(Props: any): VNode {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [pageState, pageStateSetter] = useContext(PageContext);
|
||||
const [submitData, submitDataSetter] = useCredentialsRequestType();
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
|
||||
const errors = !submitData
|
||||
? undefined
|
||||
: undefinedIfEmpty({
|
||||
username: !submitData.username ? i18n`Missing username` : undefined,
|
||||
password: !submitData.password ? i18n`Missing password` : undefined,
|
||||
username: !submitData.username ? i18n.str`Missing username` : undefined,
|
||||
password: !submitData.password ? i18n.str`Missing password` : undefined,
|
||||
repeatPassword: !submitData.repeatPassword
|
||||
? i18n`Missing password`
|
||||
? i18n.str`Missing password`
|
||||
: submitData.repeatPassword !== submitData.password
|
||||
? i18n`Password don't match`
|
||||
? i18n.str`Password don't match`
|
||||
: undefined,
|
||||
});
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<h1 class="nav">{i18n`Welcome to ${bankUiSettings.bankName}!`}</h1>
|
||||
<h1 class="nav">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h1>
|
||||
<article>
|
||||
<div class="register-div">
|
||||
<form action="javascript:void(0);" class="register-form">
|
||||
<div class="pure-form">
|
||||
<h2>{i18n`Please register!`}</h2>
|
||||
<h2>{i18n.str`Please register!`}</h2>
|
||||
<p class="unameFieldLabel registerFieldLabel formFieldLabel">
|
||||
<label for="register-un">{i18n`Username:`}</label>
|
||||
<label for="register-un">{i18n.str`Username:`}</label>
|
||||
</p>
|
||||
<input
|
||||
id="register-un"
|
||||
@ -1976,7 +1946,7 @@ function RegistrationForm(Props: any): VNode {
|
||||
/>
|
||||
<br />
|
||||
<p class="unameFieldLabel registerFieldLabel formFieldLabel">
|
||||
<label for="register-pw">{i18n`Password:`}</label>
|
||||
<label for="register-pw">{i18n.str`Password:`}</label>
|
||||
</p>
|
||||
<input
|
||||
type="password"
|
||||
@ -1993,7 +1963,7 @@ function RegistrationForm(Props: any): VNode {
|
||||
}}
|
||||
/>
|
||||
<p class="unameFieldLabel registerFieldLabel formFieldLabel">
|
||||
<label for="register-repeat">{i18n`Repeat Password:`}</label>
|
||||
<label for="register-repeat">{i18n.str`Repeat Password:`}</label>
|
||||
</p>
|
||||
<input
|
||||
type="password"
|
||||
@ -2012,7 +1982,7 @@ function RegistrationForm(Props: any): VNode {
|
||||
/>
|
||||
<br />
|
||||
{/*
|
||||
<label for="phone">{i18n`Phone number:`}</label>
|
||||
<label for="phone">{i18n.str`Phone number:`}</label>
|
||||
// FIXME: add input validation (must start with +, otherwise only numbers)
|
||||
<input
|
||||
name="phone"
|
||||
@ -2054,7 +2024,7 @@ function RegistrationForm(Props: any): VNode {
|
||||
});
|
||||
}}
|
||||
>
|
||||
{i18n`Register`}
|
||||
{i18n.str`Register`}
|
||||
</button>
|
||||
{/* FIXME: should use a different color */}
|
||||
<button
|
||||
@ -2068,7 +2038,7 @@ function RegistrationForm(Props: any): VNode {
|
||||
route("/account");
|
||||
}}
|
||||
>
|
||||
{i18n`Cancel`}
|
||||
{i18n.str`Cancel`}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@ -2083,7 +2053,7 @@ function RegistrationForm(Props: any): VNode {
|
||||
*/
|
||||
function Transactions(Props: any): VNode {
|
||||
const { pageNumber, accountLabel, balanceValue } = Props;
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
const { data, error, mutate } = useSWR(
|
||||
`access-api/accounts/${accountLabel}/transactions?page=${pageNumber}`,
|
||||
);
|
||||
@ -2114,10 +2084,10 @@ function Transactions(Props: any): VNode {
|
||||
<table class="pure-table pure-table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{i18n`Date`}</th>
|
||||
<th>{i18n`Amount`}</th>
|
||||
<th>{i18n`Counterpart`}</th>
|
||||
<th>{i18n`Subject`}</th>
|
||||
<th>{i18n.str`Date`}</th>
|
||||
<th>{i18n.str`Amount`}</th>
|
||||
<th>{i18n.str`Counterpart`}</th>
|
||||
<th>{i18n.str`Subject`}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -2178,7 +2148,7 @@ function Account(Props: any): VNode {
|
||||
talerWithdrawUri,
|
||||
timestamp,
|
||||
} = pageState;
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
useEffect(() => {
|
||||
mutate();
|
||||
}, [timestamp]);
|
||||
@ -2206,7 +2176,7 @@ function Account(Props: any): VNode {
|
||||
|
||||
isLoggedIn: false,
|
||||
error: {
|
||||
title: i18n`Username or account label '${accountLabel}' not found. Won't login.`,
|
||||
title: i18n.str`Username or account label '${accountLabel}' not found. Won't login.`,
|
||||
},
|
||||
}));
|
||||
|
||||
@ -2233,7 +2203,7 @@ function Account(Props: any): VNode {
|
||||
|
||||
isLoggedIn: false,
|
||||
error: {
|
||||
title: i18n`Wrong credentials given.`,
|
||||
title: i18n.str`Wrong credentials given.`,
|
||||
},
|
||||
}));
|
||||
return <p>Wrong credentials...</p>;
|
||||
@ -2244,7 +2214,7 @@ function Account(Props: any): VNode {
|
||||
|
||||
isLoggedIn: false,
|
||||
error: {
|
||||
title: i18n`Account information could not be retrieved.`,
|
||||
title: i18n.str`Account information could not be retrieved.`,
|
||||
debug: JSON.stringify(error),
|
||||
},
|
||||
}));
|
||||
@ -2287,14 +2257,14 @@ function Account(Props: any): VNode {
|
||||
<BankFrame>
|
||||
<div>
|
||||
<h1 class="nav welcome-text">
|
||||
<Translate>
|
||||
<i18n.Translate>
|
||||
Welcome, {accountLabel} ({getIbanFromPayto(data.paytoUri)})!
|
||||
</Translate>
|
||||
</i18n.Translate>
|
||||
</h1>
|
||||
</div>
|
||||
<section id="assets">
|
||||
<div class="asset-summary">
|
||||
<h2>{i18n`Bank account balance`}</h2>
|
||||
<h2>{i18n.str`Bank account balance`}</h2>
|
||||
<div class="large-amount amount">
|
||||
{data.balance.credit_debit_indicator == "debit" ? <b>-</b> : null}
|
||||
<span class="value">{`${balanceValue}`}</span>
|
||||
@ -2304,7 +2274,7 @@ function Account(Props: any): VNode {
|
||||
</section>
|
||||
<section id="payments">
|
||||
<div class="payments">
|
||||
<h2>{i18n`Payments`}</h2>
|
||||
<h2>{i18n.str`Payments`}</h2>
|
||||
{/* FIXME: turn into button! */}
|
||||
<CurrencyContext.Provider value={balance.currency}>
|
||||
{Props.children}
|
||||
@ -2317,7 +2287,7 @@ function Account(Props: any): VNode {
|
||||
</section>
|
||||
<section id="main">
|
||||
<article>
|
||||
<h2>{i18n`Latest transactions:`}</h2>
|
||||
<h2>{i18n.str`Latest transactions:`}</h2>
|
||||
<Transactions
|
||||
balanceValue={balanceValue}
|
||||
pageNumber="0"
|
||||
@ -2379,7 +2349,7 @@ function SWRWithoutCredentials(Props: any): VNode {
|
||||
function PublicHistories(Props: any): VNode {
|
||||
const [showAccount, setShowAccount] = useShowPublicAccount();
|
||||
const { data, error } = useSWR("access-api/public-accounts");
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
|
||||
if (typeof error !== "undefined") {
|
||||
console.log("account error", error);
|
||||
@ -2391,7 +2361,7 @@ function PublicHistories(Props: any): VNode {
|
||||
|
||||
showPublicHistories: false,
|
||||
error: {
|
||||
title: i18n`List of public accounts was not found.`,
|
||||
title: i18n.str`List of public accounts was not found.`,
|
||||
debug: JSON.stringify(error),
|
||||
},
|
||||
}));
|
||||
@ -2403,7 +2373,7 @@ function PublicHistories(Props: any): VNode {
|
||||
|
||||
showPublicHistories: false,
|
||||
error: {
|
||||
title: i18n`List of public accounts could not be retrieved.`,
|
||||
title: i18n.str`List of public accounts could not be retrieved.`,
|
||||
debug: JSON.stringify(error),
|
||||
},
|
||||
}));
|
||||
@ -2450,7 +2420,7 @@ function PublicHistories(Props: any): VNode {
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<h1 class="nav">{i18n`History of public accounts`}</h1>
|
||||
<h1 class="nav">{i18n.str`History of public accounts`}</h1>
|
||||
<section id="main">
|
||||
<article>
|
||||
<div class="pure-menu pure-menu-horizontal" name="accountMenu">
|
||||
@ -2471,7 +2441,7 @@ function PublicHistories(Props: any): VNode {
|
||||
function PublicHistoriesPage(): VNode {
|
||||
// const [backendState, backendStateSetter] = useBackendState();
|
||||
const [pageState, pageStateSetter] = usePageState();
|
||||
// const i18n = useTranslator();
|
||||
// const { i18n } = useTranslationContext();
|
||||
return (
|
||||
<SWRWithoutCredentials baseUrl={getBankBackendBaseUrl()}>
|
||||
<PageContext.Provider value={[pageState, pageStateSetter]}>
|
||||
@ -2499,12 +2469,12 @@ function PublicHistoriesPage(): VNode {
|
||||
function RegistrationPage(): VNode {
|
||||
const [backendState, backendStateSetter] = useBackendState();
|
||||
const [pageState, pageStateSetter] = usePageState();
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
if (!bankUiSettings.allowRegistrations) {
|
||||
return (
|
||||
<PageContext.Provider value={[pageState, pageStateSetter]}>
|
||||
<BankFrame>
|
||||
<p>{i18n`Currently, the bank is not accepting new registrations!`}</p>
|
||||
<p>{i18n.str`Currently, the bank is not accepting new registrations!`}</p>
|
||||
</BankFrame>
|
||||
</PageContext.Provider>
|
||||
);
|
||||
@ -2521,13 +2491,13 @@ function RegistrationPage(): VNode {
|
||||
function AccountPage(): VNode {
|
||||
const [backendState, backendStateSetter] = useBackendState();
|
||||
const [pageState, pageStateSetter] = usePageState();
|
||||
const i18n = useTranslator();
|
||||
const { i18n } = useTranslationContext();
|
||||
|
||||
if (!pageState.isLoggedIn) {
|
||||
return (
|
||||
<PageContext.Provider value={[pageState, pageStateSetter]}>
|
||||
<BankFrame>
|
||||
<h1 class="nav">{i18n`Welcome to ${bankUiSettings.bankName}!`}</h1>
|
||||
<h1 class="nav">{i18n.str`Welcome to ${bankUiSettings.bankName}!`}</h1>
|
||||
<LoginForm
|
||||
pageStateSetter={pageStateSetter}
|
||||
backendStateSetter={backendStateSetter}
|
||||
@ -2543,7 +2513,7 @@ function AccountPage(): VNode {
|
||||
|
||||
isLoggedIn: false,
|
||||
error: {
|
||||
title: i18n`Page has a problem: logged in but backend state is lost.`,
|
||||
title: i18n.str`Page has a problem: logged in but backend state is lost.`,
|
||||
},
|
||||
}));
|
||||
return <p>Error: waiting for details...</p>;
|
||||
|
@ -1,4 +1,4 @@
|
||||
@import "pure";
|
||||
@import "bank";
|
||||
@import "demo";
|
||||
@import "colors-bank";
|
||||
@use "pure";
|
||||
@use "bank";
|
||||
@use "demo";
|
||||
@use "colors-bank";
|
||||
|
46
packages/demobank-ui/src/stories.tsx
Normal file
46
packages/demobank-ui/src/stories.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
import { strings } from "./i18n/strings.js";
|
||||
|
||||
import * as pages from "./pages/home/index.stories.js";
|
||||
|
||||
import { renderStories } from "@gnu-taler/web-util/lib/index.browser";
|
||||
|
||||
import "./scss/main.scss";
|
||||
|
||||
function SortStories(a: any, b: any): number {
|
||||
return (a?.order ?? 0) - (b?.order ?? 0);
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
renderStories(
|
||||
{ pages },
|
||||
{
|
||||
strings,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", main);
|
||||
} else {
|
||||
main();
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<title>Demobank</title>
|
||||
<!-- Optional customization script. -->
|
||||
<script src="demobank-ui-settings.js"></script>
|
||||
<!-- Entry point for the demobank SPA. -->
|
||||
<script type="module" src="index.js"></script>
|
||||
<link rel="stylesheet" href="index.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user