aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xpackages/anastasis-webui/build.mjs147
-rwxr-xr-xpackages/anastasis-webui/clean_and_build.sh72
-rwxr-xr-xpackages/anastasis-webui/dev.mjs91
-rw-r--r--packages/anastasis-webui/html/stories.html72
-rw-r--r--packages/anastasis-webui/html/ui-dev.html65
-rw-r--r--packages/anastasis-webui/package.json12
-rw-r--r--packages/anastasis-webui/src/components/menu/SideBar.tsx33
-rw-r--r--packages/anastasis-webui/src/components/picker/DurationPicker.tsx12
-rw-r--r--packages/anastasis-webui/src/context/translation.ts41
-rw-r--r--packages/anastasis-webui/src/hooks/index.ts71
-rw-r--r--packages/anastasis-webui/src/hooks/useLang.ts (renamed from packages/anastasis-webui/src/main.ts)33
-rw-r--r--packages/anastasis-webui/src/hooks/useLocalStorage.ts80
-rw-r--r--packages/anastasis-webui/src/i18n/index.tsx211
-rw-r--r--packages/anastasis-webui/src/index.html (renamed from packages/anastasis-webui/html/ui.html)32
-rw-r--r--packages/anastasis-webui/src/index.test.ts (renamed from packages/anastasis-webui/src/main.test.ts)0
-rw-r--r--packages/anastasis-webui/src/index.ts25
-rw-r--r--packages/anastasis-webui/src/pages/home/AddingProviderScreen/stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/AddingProviderScreen/views.tsx4
-rw-r--r--packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/ChallengePayingScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/EditPoliciesScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx6
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx6
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx6
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.stories.tsx1
-rw-r--r--packages/anastasis-webui/src/scss/_mixins.scss2
-rw-r--r--packages/anastasis-webui/src/stories.tsx365
-rw-r--r--packages/anastasis-webui/src/test-utils.ts12
-rw-r--r--packages/anastasis-webui/src/utils/index.tsx16
-rwxr-xr-xpackages/anastasis-webui/watch/reply.sh18
-rwxr-xr-xpackages/anastasis-webui/watch/send.sh12
-rwxr-xr-xpackages/anastasis-webui/watch/send2.sh14
-rwxr-xr-xpackages/anastasis-webui/watch/serve.sh7
-rw-r--r--packages/anastasis-webui/watch/web_socket_client.request6
-rw-r--r--packages/anastasis-webui/watch/web_socket_server.reply5
58 files changed, 430 insertions, 1074 deletions
diff --git a/packages/anastasis-webui/build.mjs b/packages/anastasis-webui/build.mjs
new file mode 100755
index 000000000..ebe914541
--- /dev/null
+++ b/packages/anastasis-webui/build.mjs
@@ -0,0 +1,147 @@
+#!/usr/bin/env node
+/*
+ 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/>
+ */
+/* eslint-disable no-undef */
+import esbuild from 'esbuild'
+import fs from 'fs';
+import path from "path"
+import sass from "sass";
+
+// eslint-disable-next-line no-undef
+const BASE = process.cwd();
+
+const preact = path.join(
+ BASE,
+ "node_modules",
+ "preact",
+ "compat",
+ "dist",
+ "compat.module.js",
+);
+
+const preactCompatPlugin = {
+ name: "preact-compat",
+ setup(build) {
+ build.onResolve({ filter: /^(react-dom|react)$/ }, (args) => {
+ return {
+ path: preact,
+ };
+ });
+ },
+};
+
+
+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()
+
+
+let _package = JSON.parse(fs.readFileSync(path.join(BASE, 'package.json')));
+
+function git_hash() {
+ const rev = fs.readFileSync(path.join(GIT_ROOT, '.git', 'HEAD')).toString().trim().split(/.*[: ]/).slice(-1)[0];
+ if (rev.indexOf('/') === -1) {
+ return rev;
+ } else {
+ return fs.readFileSync(path.join(GIT_ROOT, '.git', rev)).toString().trim();
+ }
+}
+
+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
+ }
+ });
+
+ },
+};
+
+function copyFilesPlugin(options) {
+ if (!options.basedir) {
+ options.basedir = process.cwd()
+ }
+ return {
+ name: "copy-files",
+ setup(build) {
+ build.onEnd(() => {
+ for (const fop of options) {
+ fs.copyFileSync(path.join(options.basedir, fop.src), path.join(options.basedir, fop.dest));
+ }
+ });
+ },
+ };
+}
+
+export const buildConfig = {
+ entryPoints: ['src/index.ts', 'src/stories.tsx'],
+ bundle: true,
+ outdir: 'dist',
+ minify: false,
+ loader: {
+ '.svg': 'dataurl',
+ '.ttf': 'file',
+ '.woff': 'file',
+ '.woff2': 'file',
+ '.eot': 'file',
+ },
+ target: [
+ 'es6'
+ ],
+ format: 'esm',
+ platform: 'browser',
+ sourcemap: true,
+ jsxFactory: 'h',
+ jsxFragment: 'Fragment',
+ define: {
+ '__VERSION__': `"${_package.version}"`,
+ '__GIT_HASH__': `"${GIT_HASH}"`,
+ },
+ plugins: [
+ preactCompatPlugin,
+ copyFilesPlugin([
+ {
+ src: "./src/index.html",
+ dest: "./dist/index.html",
+ },
+ ]),
+ buildSassPlugin
+ ],
+}
+
+await esbuild.build(buildConfig)
+
+
+
+
diff --git a/packages/anastasis-webui/clean_and_build.sh b/packages/anastasis-webui/clean_and_build.sh
deleted file mode 100755
index 9486848fe..000000000
--- a/packages/anastasis-webui/clean_and_build.sh
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env bash
-
-echo clean
-rm -rf dist
-mkdir -p dist/fonts
-cp \
- src/scss/fonts/XRXV3I6Li01BKofINeaE.ttf \
- src/scss/fonts/materialdesignicons-webfont-4.9.95.ttf \
- src/scss/fonts/materialdesignicons-webfont-4.9.95.woff \
- src/scss/fonts/materialdesignicons-webfont-4.9.95.woff2 \
- dist/fonts
-
-VERSION=$(jq -r .version package.json)
-GIT_HASH=$(git rev-parse --short HEAD)
-
-function build_css() {
- pnpm exec sass -I . ./src/scss/main.scss dist/main.css
-}
-function build_js() {
- pnpm exec esbuild --log-level=error --define:process.env.__VERSION__=\"${VERSION}\" --define:process.env.__GIT_HASH__=\"${GIT_HASH}\" --bundle $1 --outdir=dist --target=es6 --loader:.svg=dataurl --format=iife --sourcemap --jsx-factory=h --jsx-fragment=Fragment --platform=browser --minify
-}
-
-function build_html() {
- cat html/$1.html \
- | sed -e '/ANASTASIS_SCRIPT_CONTENT/ {' -e 'r dist/main.js' -e 'd' -e '}' \
- | sed -e '/ANASTASIS_STYLE_CONTENT/ {' -e 'r dist/main.css' -e 'd' -e '}' \
- >dist/$1.html
-}
-
-function cleanup {
- trap - SIGHUP SIGINT SIGTERM SIGQUIT
- echo -n "Cleaning up... "
- wait
- kill -- -$$
- exit 1
-}
-trap cleanup SIGHUP SIGINT SIGTERM SIGQUIT
-
-set -e
-echo compile
-build_css &
-build_js src/main.ts &
-build_js src/stories.tsx &
-build_js src/main.test.ts &
-for file in $(find src/ -name test.ts); do build_js $file; done &
-wait -n
-wait -n
-wait -n
-wait -n
-wait -n
-pnpm run --silent test -- -R dot
-
-echo html
-build_html ui
-build_html ui-dev
-build_html stories
-
-if [ "WATCH" == "$1" ]; then
-
- echo watch mode
- echo Writing any file in the src directory will trigger a browser reload.
- echo Be sure that the watcher server is running.
- echo ./watch/serve.sh
- inotifywait -e close_write -r src -q -m | while read line; do
- echo $(date) $line
- build_js src/main.ts
- build_html ui-dev
- build_js src/stories.tsx
- build_html stories
- ./watch/send.sh '{"type":"RELOAD"}'
- done;
-fi
diff --git a/packages/anastasis-webui/dev.mjs b/packages/anastasis-webui/dev.mjs
index 3f4915ffc..0446603dc 100755
--- a/packages/anastasis-webui/dev.mjs
+++ b/packages/anastasis-webui/dev.mjs
@@ -14,87 +14,18 @@
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/>
*/
-/* eslint-disable no-undef */
-import esbuild from 'esbuild'
-import fs from 'fs';
-import WebSocket from "ws";
-import chokidar from "chokidar";
-const devServerBroadcastDelay = 500
-const devServerPort = 8002
-const wss = new WebSocket.Server({ port: devServerPort });
-const toWatch = ["./src"]
-
-function broadcast(file, event) {
- setTimeout(() => {
- wss.clients.forEach((client) => {
- if (client.readyState === WebSocket.OPEN) {
- console.log(new Date(), file)
- client.send(JSON.stringify(event));
- }
- });
- }, devServerBroadcastDelay);
-}
-
-const watcher = chokidar
- .watch(toWatch, {
- persistent: true,
- ignoreInitial: true,
- awaitWriteFinish: {
- stabilityThreshold: 100,
- pollInterval: 100,
- },
- })
- .on("error", (error) => console.error(error))
- .on("change", async (file) => {
- broadcast(file, { type: "RELOAD" });
- })
- .on("add", async (file) => {
- broadcast(file, { type: "RELOAD" });
- })
- .on("unlink", async (file) => {
- broadcast(file, { type: "RELOAD" });
- });
-
-/**
- * Just bundling UI Stories.
- * FIXME: add linaria CSS after implementing Material so CSS will be bundled
- */
-fs.writeFileSync("dist/index.html", fs.readFileSync("html/stories.html"))
-fs.writeFileSync("dist/mocha.css", fs.readFileSync("node_modules/mocha/mocha.css"))
-fs.writeFileSync("dist/mocha.js", fs.readFileSync("node_modules/mocha/mocha.js"))
-fs.writeFileSync("dist/mocha.js.map", fs.readFileSync("node_modules/mocha/mocha.js.map"))
-
-export const buildConfig = {
- entryPoints: ['src/main.ts', 'src/stories.tsx'],
- bundle: true,
- outdir: 'dist',
- minify: false,
- loader: {
- '.svg': 'dataurl',
- },
- target: [
- 'es6'
- ],
- format: 'iife',
- platform: 'browser',
- sourcemap: true,
- jsxFactory: 'h',
- jsxFragment: 'Fragment',
-}
-
-const server = await esbuild
- .serve({ servedir: 'dist' }, {
- ...buildConfig, outdir: 'dist'
- })
- .catch((e) => {
- console.log(e)
- process.exit(1)
- });
-
-console.log(`Dev server is ready at http://localhost:${server.port}/.
-The server is running a using websocket at ${devServerPort} to notify code change and live reload.
-`);
+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)
+})
diff --git a/packages/anastasis-webui/html/stories.html b/packages/anastasis-webui/html/stories.html
deleted file mode 100644
index 9f41fdeaf..000000000
--- a/packages/anastasis-webui/html/stories.html
+++ /dev/null
@@ -1,72 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <title>Stories</title>
- <style>
- /* page css */
- div.page {
- margin: 0px;
- padding: 0px;
- font-size: 100%;
- font-family: Arial, Helvetica, sans-serif;
- }
- div.page p:not([class]) {
- margin-bottom: 1em;
- margin-top: 1em;
- }
- div.page {
- width: 100%;
- display: flex;
- flex-direction: row;
- }
- /* sidebar css */
- div.sidebar {
- min-width: 200px;
- height: calc(100vh - 20px);
- overflow-y: visible;
- overflow-x: hidden;
- scroll-behavior: smooth;
- }
- div.sidebar > ol {
- padding: 4px;
- }
- div.sidebar div:first-child {
- background-color: lightcoral;
- cursor: pointer;
- }
- div.sidebar div[data-hide="true"] {
- display: none;
- }
- div.sidebar dd {
- margin-left: 1em;
- padding: 4px;
- cursor: pointer;
- border-radius: 4px;
- margin-bottom: 4px;
- }
- div.sidebar dd:nth-child(even) {
- background-color: lightgray;
- }
- div.sidebar dd:nth-child(odd) {
- background-color: lightblue;
- }
- div.sidebar a {
- color: black;
- }
- div.sidebar dd[data-selected] {
- background-color: green;
- }
-
- /* content css */
- div.content {
- width: 100%;
- padding: 20px;
- }
- </style>
- <script src="./stories.js"></script>
- <link rel="stylesheet" href="./main.css" />
- </head>
- <body>
- <taler-stories id="container"></taler-stories>
- </body>
-</html>
diff --git a/packages/anastasis-webui/html/ui-dev.html b/packages/anastasis-webui/html/ui-dev.html
deleted file mode 100644
index 2790d5678..000000000
--- a/packages/anastasis-webui/html/ui-dev.html
+++ /dev/null
@@ -1,65 +0,0 @@
-<!DOCTYPE html>
-<html
- lang="en"
- class="has-aside-left has-aside-mobile-transition has-navbar-fixed-top has-aside-expanded"
->
- <head>
- <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" />
- <style id="style-id" type="text/css">
- /* <![CDATA[ */
- ANASTASIS_STYLE_CONTENT
- /* <![CDATA[ */
- </style>
- </head>
-
- <body>
- <div id="container" class="anastasis-container"></div>
- <script id="code" type="application/javascript">
- ANASTASIS_SCRIPT_CONTENT;
- </script>
- <script type="application/javascript">
- function setupLiveReload() {
- const socketPath = `ws://localhost:8003/socket`;
- console.log("connecting to ", socketPath);
- 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") {
- window.location.reload();
- }
- if (event.type === "UPDATE") {
- document.body.removeChild(document.getElementById("container"));
- const d = document.createElement("div");
- d.setAttribute("id", "container");
- d.setAttribute("class", "anastasis-container");
- document.body.appendChild(d);
- const s = document.createElement("script");
- s.setAttribute("id", "code");
- s.setAttribute("type", "application/javascript");
- s.textContent = atob(event.content);
- document.body.appendChild(s);
- }
- };
- ws.onerror = (error) => {
- console.error(error);
- };
- ws.onclose = (e) => {
- setTimeout(setupLiveReload, 500);
- };
- }
- setupLiveReload();
- </script>
- </body>
-</html>
diff --git a/packages/anastasis-webui/package.json b/packages/anastasis-webui/package.json
index de70d05fc..c01856243 100644
--- a/packages/anastasis-webui/package.json
+++ b/packages/anastasis-webui/package.json
@@ -15,11 +15,12 @@
"dependencies": {
"@gnu-taler/anastasis-core": "workspace:*",
"@gnu-taler/taler-util": "workspace:*",
+ "@gnu-taler/web-util": "workspace:*",
"@types/chai": "^4.3.0",
"chai": "^4.3.6",
"date-fns": "2.29.2",
"jed": "1.1.1",
- "preact": "^10.5.15",
+ "preact": "10.11.3",
"preact-render-to-string": "^5.1.19",
"preact-router": "^3.2.1",
"qrcode-generator": "^1.4.4"
@@ -41,12 +42,9 @@
"bulma": "^0.9.3",
"bulma-checkbox": "^1.1.1",
"bulma-radio": "^1.1.1",
- "chokidar": "^3.5.3",
- "eslint-plugin-header": "^3.1.1",
"jssha": "^3.2.0",
"mocha": "^9.2.0",
- "sass": "1.32.13",
- "typescript": "^4.8.4",
- "ws": "7.4.5"
+ "sass": "1.56.1",
+ "typescript": "^4.8.4"
}
-}
+} \ No newline at end of file
diff --git a/packages/anastasis-webui/src/components/menu/SideBar.tsx b/packages/anastasis-webui/src/components/menu/SideBar.tsx
index 51e854944..3dac73e04 100644
--- a/packages/anastasis-webui/src/components/menu/SideBar.tsx
+++ b/packages/anastasis-webui/src/components/menu/SideBar.tsx
@@ -22,7 +22,7 @@
import { BackupStates, RecoveryStates } from "@gnu-taler/anastasis-core";
import { Fragment, h, VNode } from "preact";
import { useAnastasisContext } from "../../context/anastasis.js";
-import { Translate } from "../../i18n/index.js";
+import { useTranslationContext } from "../../context/translation.js";
interface Props {
mobile?: boolean;
@@ -34,6 +34,7 @@ const VERSION_WITH_HASH = GIT_HASH ? `${VERSION}-${GIT_HASH}` : VERSION;
export function Sidebar({ mobile }: Props): VNode {
const reducer = useAnastasisContext()!;
+ const { i18n } = useTranslationContext();
function saveSession(): void {
const state = reducer.exportState();
@@ -64,7 +65,7 @@ export function Sidebar({ mobile }: Props): VNode {
<div class="menu is-menu-main">
{!reducer.currentReducerState && (
<p class="menu-label">
- <Translate>Backup or Recorver</Translate>
+ <i18n.Translate>Backup or Recorver</i18n.Translate>
</p>
)}
<ul class="menu-list">
@@ -72,7 +73,7 @@ export function Sidebar({ mobile }: Props): VNode {
<li>
<div class="ml-4">
<span class="menu-item-label">
- <Translate>Select one option</Translate>
+ <i18n.Translate>Select one option</i18n.Translate>
</span>
</div>
</li>
@@ -91,7 +92,7 @@ export function Sidebar({ mobile }: Props): VNode {
>
<div class="ml-4">
<span class="menu-item-label">
- <Translate>Location</Translate>
+ <i18n.Translate>Location</i18n.Translate>
</span>
</div>
</li>
@@ -105,7 +106,7 @@ export function Sidebar({ mobile }: Props): VNode {
>
<div class="ml-4">
<span class="menu-item-label">
- <Translate>Personal information</Translate>
+ <i18n.Translate>Personal information</i18n.Translate>
</span>
</div>
</li>
@@ -119,7 +120,7 @@ export function Sidebar({ mobile }: Props): VNode {
>
<div class="ml-4">
<span class="menu-item-label">
- <Translate>Authorization methods</Translate>
+ <i18n.Translate>Authorization methods</i18n.Translate>
</span>
</div>
</li>
@@ -133,7 +134,7 @@ export function Sidebar({ mobile }: Props): VNode {
>
<div class="ml-4">
<span class="menu-item-label">
- <Translate>Policies</Translate>
+ <i18n.Translate>Policies</i18n.Translate>
</span>
</div>
</li>
@@ -147,14 +148,14 @@ export function Sidebar({ mobile }: Props): VNode {
>
<div class="ml-4">
<span class="menu-item-label">
- <Translate>Secret input</Translate>
+ <i18n.Translate>Secret input</i18n.Translate>
</span>
</div>
</li>
{/* <li class={reducer.currentReducerState.backup_state === BackupStates.PoliciesPaying ? 'is-active' : ''}>
<div class="ml-4">
- <span class="menu-item-label"><Translate>Payment (optional)</Translate></span>
+ <span class="menu-item-label"><i18n.Translate>Payment (optional)</i18n.Translate></span>
</div>
</li> */}
<li
@@ -167,14 +168,14 @@ export function Sidebar({ mobile }: Props): VNode {
>
<div class="ml-4">
<span class="menu-item-label">
- <Translate>Backup completed</Translate>
+ <i18n.Translate>Backup completed</i18n.Translate>
</span>
</div>
</li>
{/* <li class={reducer.currentReducerState.backup_state === BackupStates.TruthsPaying ? 'is-active' : ''}>
<div class="ml-4">
- <span class="menu-item-label"><Translate>Truth Paying</Translate></span>
+ <span class="menu-item-label"><i18n.Translate>Truth Paying</i18n.Translate></span>
</div>
</li> */}
{reducer.currentReducerState.backup_state !==
@@ -219,7 +220,7 @@ export function Sidebar({ mobile }: Props): VNode {
>
<div class="ml-4">
<span class="menu-item-label">
- <Translate>Location</Translate>
+ <i18n.Translate>Location</i18n.Translate>
</span>
</div>
</li>
@@ -233,7 +234,7 @@ export function Sidebar({ mobile }: Props): VNode {
>
<div class="ml-4">
<span class="menu-item-label">
- <Translate>Personal information</Translate>
+ <i18n.Translate>Personal information</i18n.Translate>
</span>
</div>
</li>
@@ -247,7 +248,7 @@ export function Sidebar({ mobile }: Props): VNode {
>
<div class="ml-4">
<span class="menu-item-label">
- <Translate>Secret selection</Translate>
+ <i18n.Translate>Secret selection</i18n.Translate>
</span>
</div>
</li>
@@ -263,7 +264,7 @@ export function Sidebar({ mobile }: Props): VNode {
>
<div class="ml-4">
<span class="menu-item-label">
- <Translate>Solve Challenges</Translate>
+ <i18n.Translate>Solve Challenges</i18n.Translate>
</span>
</div>
</li>
@@ -277,7 +278,7 @@ export function Sidebar({ mobile }: Props): VNode {
>
<div class="ml-4">
<span class="menu-item-label">
- <Translate>Secret recovered</Translate>
+ <i18n.Translate>Secret recovered</i18n.Translate>
</span>
</div>
</li>
diff --git a/packages/anastasis-webui/src/components/picker/DurationPicker.tsx b/packages/anastasis-webui/src/components/picker/DurationPicker.tsx
index 12ed158dd..c4caaec9f 100644
--- a/packages/anastasis-webui/src/components/picker/DurationPicker.tsx
+++ b/packages/anastasis-webui/src/components/picker/DurationPicker.tsx
@@ -21,7 +21,7 @@
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
-import { useTranslator } from "../../i18n/index.js";
+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}
diff --git a/packages/anastasis-webui/src/context/translation.ts b/packages/anastasis-webui/src/context/translation.ts
index 87704a13f..44faaa456 100644
--- a/packages/anastasis-webui/src/context/translation.ts
+++ b/packages/anastasis-webui/src/context/translation.ts
@@ -19,23 +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: () => {
// do not change anything
},
+ i18n,
+ isSaved: false,
};
const Context = createContext<Type>(initial);
@@ -50,15 +69,23 @@ export const TranslationProvider = ({
children,
forceLang,
}: Props): VNode => {
- const [lang, changeLanguage] = useLang(initial);
+ const [lang, changeLanguage, isSaved] = useLang(initial);
useEffect(() => {
if (forceLang) {
changeLanguage(forceLang);
}
});
- 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,
});
};
diff --git a/packages/anastasis-webui/src/hooks/index.ts b/packages/anastasis-webui/src/hooks/index.ts
index c03e834d7..2dbf4fa5c 100644
--- a/packages/anastasis-webui/src/hooks/index.ts
+++ b/packages/anastasis-webui/src/hooks/index.ts
@@ -19,7 +19,9 @@
* @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 = () => {
@@ -69,69 +71,4 @@ 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);
- return useNotNullLocalStorage("lang-preference", defaultLang);
-}
-
-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];
-}
+} \ No newline at end of file
diff --git a/packages/anastasis-webui/src/main.ts b/packages/anastasis-webui/src/hooks/useLang.ts
index 72ab257eb..5b02c5255 100644
--- a/packages/anastasis-webui/src/main.ts
+++ b/packages/anastasis-webui/src/hooks/useLang.ts
@@ -13,29 +13,18 @@
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 { setupI18n } from "@gnu-taler/taler-util";
-import { h, render } from "preact";
-import App from "./components/app.js";
-function main(): void {
- try {
- const container = document.getElementById("container");
- if (!container) {
- throw Error("container not found, can't mount page contents");
- }
- render(h(App, {}), 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/.`;
- }
- }
-}
+import { useNotNullLocalStorage } from "./useLocalStorage.js";
-// setupI18n("en", strings);
+function getBrowserLang(): string | undefined {
+ if (window.navigator.languages) return window.navigator.languages[0];
+ if (window.navigator.language) return window.navigator.language;
+ return undefined;
+}
-if (document.readyState === "loading") {
- document.addEventListener("DOMContentLoaded", main);
-} else {
- main();
+export function useLang(
+ initial?: string,
+): [string, (s: string) => void, boolean] {
+ const defaultLang = (getBrowserLang() || initial || "en").substring(0, 2);
+ return useNotNullLocalStorage("lang-preference", defaultLang);
}
diff --git a/packages/anastasis-webui/src/hooks/useLocalStorage.ts b/packages/anastasis-webui/src/hooks/useLocalStorage.ts
new file mode 100644
index 000000000..ed5b491f2
--- /dev/null
+++ b/packages/anastasis-webui/src/hooks/useLocalStorage.ts
@@ -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];
+}
diff --git a/packages/anastasis-webui/src/i18n/index.tsx b/packages/anastasis-webui/src/i18n/index.tsx
deleted file mode 100644
index 01e3cdd3a..000000000
--- a/packages/anastasis-webui/src/i18n/index.tsx
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- 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/>
- */
-
-/**
- * Translation helpers for React components and template literals.
- */
-
-/**
- * Imports
- */
-import { ComponentChild, ComponentChildren, h, Fragment, VNode } from "preact";
-
-import { useTranslationContext } from "../context/translation.js";
-
-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>;
-}
diff --git a/packages/anastasis-webui/html/ui.html b/packages/anastasis-webui/src/index.html
index 6672eba6a..90a795ae3 100644
--- a/packages/anastasis-webui/html/ui.html
+++ b/packages/anastasis-webui/src/index.html
@@ -1,28 +1,42 @@
+<!--
+ This file is part of GNU Taler
+ (C) 2021--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
+-->
<!DOCTYPE html>
<html
lang="en"
class="has-aside-left has-aside-mobile-transition has-navbar-fixed-top has-aside-expanded"
>
<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" />
- <style id="style-id">
- ANASTASIS_STYLE_CONTENT
- </style>
+ <title>Anastasis</title>
+ <!-- Entry point for the demobank SPA. -->
+ <script type="module" src="index.js"></script>
+ <link rel="stylesheet" href="index.css" />
</head>
-
<body>
- <div id="container" class="anastasis-container"></div>
- <script>
- ANASTASIS_SCRIPT_CONTENT;
- </script>
+ <div id="container"></div>
</body>
</html>
diff --git a/packages/anastasis-webui/src/main.test.ts b/packages/anastasis-webui/src/index.test.ts
index 1a87e3857..1a87e3857 100644
--- a/packages/anastasis-webui/src/main.test.ts
+++ b/packages/anastasis-webui/src/index.test.ts
diff --git a/packages/anastasis-webui/src/index.ts b/packages/anastasis-webui/src/index.ts
index e04c44a31..d7b2164ab 100644
--- a/packages/anastasis-webui/src/index.ts
+++ b/packages/anastasis-webui/src/index.ts
@@ -13,7 +13,30 @@
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 { setupI18n } from "@gnu-taler/taler-util";
+import { h, render } from "preact";
import App from "./components/app.js";
import "./scss/main.scss";
-export default App;
+function main(): void {
+ try {
+ const container = document.getElementById("container");
+ if (!container) {
+ throw Error("container not found, can't mount page contents");
+ }
+ render(h(App, {}), 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/.`;
+ }
+ }
+}
+
+// setupI18n("en", strings);
+
+if (document.readyState === "loading") {
+ document.addEventListener("DOMContentLoaded", main);
+} else {
+ main();
+}
diff --git a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/stories.tsx b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/stories.tsx
index dc41d9c1a..268189ed8 100644
--- a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/stories.tsx
@@ -24,6 +24,7 @@ import { createExampleWithoutAnastasis } from "../../../utils/index.jsx";
import { WithoutProviderType, WithProviderType } from "./views.jsx";
export default {
+ title: "Adding Provider Screen",
args: {
order: 1,
},
diff --git a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/views.tsx b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/views.tsx
index e397e0b65..19557a12f 100644
--- a/packages/anastasis-webui/src/pages/home/AddingProviderScreen/views.tsx
+++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen/views.tsx
@@ -23,8 +23,10 @@ import { TextInput } from "../../../components/fields/TextInput.js";
import { Notifications } from "../../../components/Notifications.js";
import { AnastasisClientFrame } from "../index.js";
import { testProvider, WithoutType, WithType } from "./index.js";
+import { useTranslationContext } from "../../../context/translation.js";
export function WithProviderType(props: WithType): VNode {
+ const { i18n } = useTranslationContext();
return (
<AnastasisClientFrame
hideNav
@@ -33,7 +35,7 @@ export function WithProviderType(props: WithType): VNode {
>
<div>
<Notifications notifications={props.notifications} />
- <p>Add a provider url for a {props.providerLabel} service</p>
+ <p>{i18n.str`Add a provider url for a ${props.providerLabel} service`}</p>
<div class="container">
<TextInput
label="Provider URL"
diff --git a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx
index b1569f184..38fc1b56b 100644
--- a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx
@@ -24,6 +24,7 @@ import { createExample, reducerStatesExample } from "../../utils/index.js";
import { AttributeEntryScreen as TestedComponent } from "./AttributeEntryScreen.js";
export default {
+ title: "Attribute Entry Screen",
component: TestedComponent,
args: {
order: 3,
diff --git a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx
index c4901085d..ba48e2d5c 100644
--- a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx
@@ -24,6 +24,7 @@ import { createExample, reducerStatesExample } from "../../utils/index.js";
import { AuthenticationEditorScreen as TestedComponent } from "./AuthenticationEditorScreen.js";
export default {
+ title: "Authentication Editor Screen",
component: TestedComponent,
args: {
order: 4,
diff --git a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx
index f50a72f8a..8aeaec25c 100644
--- a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx
@@ -24,6 +24,7 @@ import { createExample, reducerStatesExample } from "../../utils/index.js";
import { BackupFinishedScreen as TestedComponent } from "./BackupFinishedScreen.js";
export default {
+ title: "Backup finish",
component: TestedComponent,
args: {
order: 8,
diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx
index 552cb069f..d2471755a 100644
--- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx
@@ -28,6 +28,7 @@ import { createExample, reducerStatesExample } from "../../utils/index.js";
import { ChallengeOverviewScreen as TestedComponent } from "./ChallengeOverviewScreen.js";
export default {
+ title: "Challenge overview",
component: TestedComponent,
args: {
order: 5,
diff --git a/packages/anastasis-webui/src/pages/home/ChallengePayingScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ChallengePayingScreen.stories.tsx
index 0d4895a0b..cd41fe03a 100644
--- a/packages/anastasis-webui/src/pages/home/ChallengePayingScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/ChallengePayingScreen.stories.tsx
@@ -23,6 +23,7 @@ import { createExample, reducerStatesExample } from "../../utils/index.js";
import { ChallengePayingScreen as TestedComponent } from "./ChallengePayingScreen.js";
export default {
+ title: "Challenge paying",
component: TestedComponent,
args: {
order: 10,
diff --git a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx
index 3994b7377..12a79c56c 100644
--- a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx
@@ -24,6 +24,7 @@ import { createExample, reducerStatesExample } from "../../utils/index.js";
import { ContinentSelectionScreen as TestedComponent } from "./ContinentSelectionScreen.js";
export default {
+ title: "Continent selection",
component: TestedComponent,
args: {
order: 2,
diff --git a/packages/anastasis-webui/src/pages/home/EditPoliciesScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/EditPoliciesScreen.stories.tsx
index 75619ba05..1e3650300 100644
--- a/packages/anastasis-webui/src/pages/home/EditPoliciesScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/EditPoliciesScreen.stories.tsx
@@ -24,6 +24,7 @@ import { createExample, reducerStatesExample } from "../../utils/index.js";
import { EditPoliciesScreen as TestedComponent } from "./EditPoliciesScreen.js";
export default {
+ title: "Edit policies",
args: {
order: 6,
},
diff --git a/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx
index 54833234d..56c224d34 100644
--- a/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx
@@ -24,6 +24,7 @@ import { createExample, reducerStatesExample } from "../../utils/index.js";
import { PoliciesPayingScreen as TestedComponent } from "./PoliciesPayingScreen.js";
export default {
+ title: "Policies paying",
component: TestedComponent,
args: {
order: 9,
diff --git a/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx
index eda8968b2..1eb2ae50c 100644
--- a/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx
@@ -25,6 +25,7 @@ import { createExample, reducerStatesExample } from "../../utils/index.js";
import { RecoveryFinishedScreen as TestedComponent } from "./RecoveryFinishedScreen.js";
export default {
+ title: "Recovery Finished",
args: {
order: 7,
},
diff --git a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx
index 036455bce..c5003d6a0 100644
--- a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx
@@ -24,6 +24,7 @@ import { createExample, reducerStatesExample } from "../../utils/index.js";
import { ReviewPoliciesScreen as TestedComponent } from "./ReviewPoliciesScreen.js";
export default {
+ title: "Reviewing Policies",
args: {
order: 6,
},
diff --git a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx
index 7a03116e7..dbf8bf128 100644
--- a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx
@@ -24,6 +24,7 @@ import { createExample, reducerStatesExample } from "../../utils/index.js";
import { SecretEditorScreen as TestedComponent } from "./SecretEditorScreen.js";
export default {
+ title: "Secret editor",
component: TestedComponent,
args: {
order: 7,
diff --git a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx
index b457937f8..7669668ee 100644
--- a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx
@@ -27,6 +27,7 @@ import {
} from "./SecretSelectionScreen.js";
export default {
+ title: "Secret selection",
component: SecretSelectionScreen,
args: {
order: 4,
diff --git a/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx
index 5b3a70dd0..1058ae126 100644
--- a/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx
@@ -24,6 +24,7 @@ import { createExample, reducerStatesExample } from "../../utils/index.js";
import { SolveScreen as TestedComponent } from "./SolveScreen.js";
export default {
+ title: "Solve Screen",
component: TestedComponent,
args: {
order: 6,
diff --git a/packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx
index 3d54a9fd6..960426098 100644
--- a/packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx
@@ -23,6 +23,7 @@ import { createExample, reducerStatesExample } from "../../utils/index.js";
import { StartScreen as TestedComponent } from "./StartScreen.js";
export default {
+ title: "Start screen",
component: TestedComponent,
args: {
order: 1,
diff --git a/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx
index 81bab4868..40ed5117c 100644
--- a/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx
@@ -24,6 +24,7 @@ import { createExample, reducerStatesExample } from "../../utils/index.js";
import { TruthsPayingScreen as TestedComponent } from "./TruthsPayingScreen.js";
export default {
+ title: "Truths Paying",
component: TestedComponent,
args: {
order: 10,
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.stories.tsx
index 38391d10d..4a2d76ca3 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSetup.stories.tsx
@@ -23,6 +23,7 @@ import { createExample, reducerStatesExample } from "../../../utils/index.js";
import { authMethods as TestedComponent, KnownAuthMethods } from "./index.js";
export default {
+ title: "Auth method: Email setup",
component: TestedComponent,
args: {
order: 5,
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx
index db9abc86c..cc378d8f6 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.stories.tsx
@@ -27,6 +27,7 @@ import { createExample, reducerStatesExample } from "../../../utils/index.js";
import { authMethods as TestedComponent, KnownAuthMethods } from "./index.js";
export default {
+ title: "Auth method: Email solve",
component: TestedComponent,
args: {
order: 5,
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx
index d4e034a37..6a9595a83 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodEmailSolve.tsx
@@ -23,7 +23,7 @@ import { useState } from "preact/hooks";
import { AsyncButton } from "../../../components/AsyncButton.js";
import { TextInput } from "../../../components/fields/TextInput.js";
import { useAnastasisContext } from "../../../context/anastasis.js";
-import { useTranslator } from "../../../i18n/index.js";
+import { useTranslationContext } from "../../../context/translation.js";
import { AnastasisClientFrame } from "../index.js";
import { SolveOverviewFeedbackDisplay } from "../SolveScreen.js";
import { shouldHideConfirm } from "./helpers.js";
@@ -53,7 +53,7 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode {
_setAnswer(result);
}
const [expanded, setExpanded] = useState(false);
- const i18n = useTranslator();
+ const { i18n } = useTranslationContext();
const reducer = useAnastasisContext();
if (!reducer) {
@@ -124,7 +124,7 @@ export function AuthMethodEmailSolve({ id }: AuthMethodSolveProps): VNode {
const error =
answer.length > 21
- ? i18n`The answer should not be greater than 21 characters.`
+ ? i18n.str`The answer should not be greater than 21 characters.`
: undefined;
return (
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.stories.tsx
index 5f3de47ff..dfe3850f1 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSetup.stories.tsx
@@ -23,6 +23,7 @@ import { createExample, reducerStatesExample } from "../../../utils/index.js";
import { authMethods as TestedComponent, KnownAuthMethods } from "./index.js";
export default {
+ title: "Auth method: IBAN setup",
component: TestedComponent,
args: {
order: 5,
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.stories.tsx
index c06611127..8a9a3f7a0 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodIbanSolve.stories.tsx
@@ -24,6 +24,7 @@ import { createExample, reducerStatesExample } from "../../../utils/index.js";
import { authMethods as TestedComponent, KnownAuthMethods } from "./index.js";
export default {
+ title: "Auth method: IBAN Solve",
component: TestedComponent,
args: {
order: 5,
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.stories.tsx
index 892de6023..8a32c45c1 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSetup.stories.tsx
@@ -23,6 +23,7 @@ import { createExample, reducerStatesExample } from "../../../utils/index.js";
import { authMethods as TestedComponent, KnownAuthMethods } from "./index.js";
export default {
+ title: "Auth method: Post setup",
component: TestedComponent,
args: {
order: 5,
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.stories.tsx
index 8f7dc5ff9..702ba2810 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.stories.tsx
@@ -24,6 +24,7 @@ import { createExample, reducerStatesExample } from "../../../utils/index.js";
import { authMethods as TestedComponent, KnownAuthMethods } from "./index.js";
export default {
+ title: "Auth method: Post solve",
component: TestedComponent,
args: {
order: 5,
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx
index 725382c58..8204ab1cf 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodPostSolve.tsx
@@ -19,7 +19,7 @@ import { useState } from "preact/hooks";
import { AsyncButton } from "../../../components/AsyncButton.js";
import { TextInput } from "../../../components/fields/TextInput.js";
import { useAnastasisContext } from "../../../context/anastasis.js";
-import { useTranslator } from "../../../i18n/index.js";
+import { useTranslationContext } from "../../../context/translation.js";
import { AnastasisClientFrame } from "../index.js";
import { SolveOverviewFeedbackDisplay } from "../SolveScreen.js";
import { shouldHideConfirm } from "./helpers.js";
@@ -48,7 +48,7 @@ export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode {
_setAnswer(result);
}
- const i18n = useTranslator();
+ const { i18n } = useTranslationContext();
const reducer = useAnastasisContext();
if (!reducer) {
@@ -119,7 +119,7 @@ export function AuthMethodPostSolve({ id }: AuthMethodSolveProps): VNode {
const error =
answer.length > 21
- ? i18n`The answer should not be greater than 21 characters.`
+ ? i18n.str`The answer should not be greater than 21 characters.`
: undefined;
return (
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.stories.tsx
index 736e7bfa8..2e108b4e6 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSetup.stories.tsx
@@ -23,6 +23,7 @@ import { createExample, reducerStatesExample } from "../../../utils/index.js";
import { authMethods as TestedComponent, KnownAuthMethods } from "./index.js";
export default {
+ title: "Auth method: Question setup",
component: TestedComponent,
args: {
order: 5,
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx
index 182538775..f7116bf6f 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodQuestionSolve.stories.tsx
@@ -27,6 +27,7 @@ import { createExample, reducerStatesExample } from "../../../utils/index.js";
import { authMethods as TestedComponent, KnownAuthMethods } from "./index.js";
export default {
+ title: "Auth method: Question solve",
component: TestedComponent,
args: {
order: 5,
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.stories.tsx
index 0d58dbdcf..b2c6cb61d 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSetup.stories.tsx
@@ -23,6 +23,7 @@ import { createExample, reducerStatesExample } from "../../../utils/index.js";
import { authMethods as TestedComponent, KnownAuthMethods } from "./index.js";
export default {
+ title: "Auth method: SMS setup",
component: TestedComponent,
args: {
order: 5,
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.stories.tsx
index f1717eff0..2064f12ff 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.stories.tsx
@@ -24,6 +24,7 @@ import { createExample, reducerStatesExample } from "../../../utils/index.js";
import { authMethods as TestedComponent, KnownAuthMethods } from "./index.js";
export default {
+ title: "Auth method: SMS solve",
component: TestedComponent,
args: {
order: 5,
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx
index 965efbe60..58bb53c4f 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodSmsSolve.tsx
@@ -19,7 +19,7 @@ import { useState } from "preact/hooks";
import { AsyncButton } from "../../../components/AsyncButton.js";
import { TextInput } from "../../../components/fields/TextInput.js";
import { useAnastasisContext } from "../../../context/anastasis.js";
-import { useTranslator } from "../../../i18n/index.js";
+import { useTranslationContext } from "../../../context/translation.js";
import { AnastasisClientFrame } from "../index.js";
import { SolveOverviewFeedbackDisplay } from "../SolveScreen.js";
import { shouldHideConfirm } from "./helpers.js";
@@ -48,7 +48,7 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode {
_setAnswer(result);
}
- const i18n = useTranslator();
+ const { i18n } = useTranslationContext();
const [expanded, setExpanded] = useState(false);
const reducer = useAnastasisContext();
@@ -120,7 +120,7 @@ export function AuthMethodSmsSolve({ id }: AuthMethodSolveProps): VNode {
const error =
answer.length > 21
- ? i18n`The answer should not be greater than 21 characters.`
+ ? i18n.str`The answer should not be greater than 21 characters.`
: undefined;
return (
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.stories.tsx
index e22053b96..5582590f7 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.stories.tsx
@@ -23,6 +23,7 @@ import { createExample, reducerStatesExample } from "../../../utils/index.js";
import { authMethods as TestedComponent, KnownAuthMethods } from "./index.js";
export default {
+ title: "Auth method: Totp setup",
component: TestedComponent,
args: {
order: 5,
diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.stories.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.stories.tsx
index 354516d80..20cd7e3c9 100644
--- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.stories.tsx
+++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSolve.stories.tsx
@@ -24,6 +24,7 @@ import { createExample, reducerStatesExample } from "../../../utils/index.js";
import { authMethods as TestedComponent, KnownAuthMethods } from "./index.js";
export default {
+ title: "Auth method: Totp solve",
component: TestedComponent,
args: {
order: 5,
diff --git a/packages/anastasis-webui/src/scss/_mixins.scss b/packages/anastasis-webui/src/scss/_mixins.scss
index 64315785b..a0fe6e93e 100644
--- a/packages/anastasis-webui/src/scss/_mixins.scss
+++ b/packages/anastasis-webui/src/scss/_mixins.scss
@@ -28,7 +28,7 @@
width: $icon-base-width;
&.has-update-mark:after {
- right: ($icon-base-width / 2) - 0.85;
+ right: calc($icon-base-width / 2) - 0.85;
}
}
}
diff --git a/packages/anastasis-webui/src/stories.tsx b/packages/anastasis-webui/src/stories.tsx
index 7d22deece..f345f082d 100644
--- a/packages/anastasis-webui/src/stories.tsx
+++ b/packages/anastasis-webui/src/stories.tsx
@@ -18,302 +18,24 @@
*
* @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";
+import * as pages from "./pages/home/index.storiesNo.js";
-setupI18n(lang, strings);
+import { renderStories } from "@gnu-taler/web-util/lib/index.browser";
-const Page = ({ children }: any) => <div class="page">{children}</div>;
-const SideBar = ({ children }: any) => <div class="sidebar">{children}</div>;
-const Content = ({ children }: any) => <div class="content">{children}</div>;
-
-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),
- ),
- };
-}
+import "./scss/main.scss";
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 <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}</dt>
- {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 (
- <dd
- id={eId}
- key={r.name}
- data-selected={isSelected}
- onClick={doSelection}
- >
- <a href={`#${eId}`} onClick={doSelection}>
- {r.name}
- </a>
- </dd>
- );
- })}
- </dl>
- </li>
- ))}
- </div>
- </ol>
- );
-}
-
-// function getWrapperForGroup(group: string): FunctionComponent {
-// switch (group) {
-// case "popup":
-// return function PopupWrapper({ children }: any) {
-// return (
-// <Fragment>
-// <PopupNavBar />
-// <PopupBox>{children}</PopupBox>
-// </Fragment>
-// );
-// };
-// case "wallet":
-// return function WalletWrapper({ children }: any) {
-// return (
-// <Fragment>
-// <LogoHeader />
-// <WalletNavBar />
-// <WalletBox>{children}</WalletBox>
-// </Fragment>
-// );
-// };
-// case "cta":
-// return function WalletWrapper({ children }: any) {
-// return (
-// <Fragment>
-// <WalletBox>{children}</WalletBox>
-// </Fragment>
-// );
-// };
-// default:
-// return Fragment;
-// }
-// }
-
-function ErrorReport({
- children,
- selected,
-}: {
- children: ComponentChild;
- selected: ExampleItem | undefined;
-}): VNode {
- const [error] = useErrorBoundary();
- if (error) {
- return (
- <div class="error_report">
- <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");
-
- return (
- <Page>
- <LiveReload />
- <SideBar>
- {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>
- <Content>
- <ErrorReport selected={selected}>
- {/* <GroupWrapper> */}
- <ExampleContent />
- {/* </GroupWrapper> */}
- </ErrorReport>
- </Content>
- </Page>
+function main(): void {
+ renderStories(
+ { pages },
+ {
+ strings,
+ },
);
}
@@ -322,72 +44,3 @@ if (document.readyState === "loading") {
} 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 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);
- };
-}
diff --git a/packages/anastasis-webui/src/test-utils.ts b/packages/anastasis-webui/src/test-utils.ts
index 1fcc753ee..f220540f1 100644
--- a/packages/anastasis-webui/src/test-utils.ts
+++ b/packages/anastasis-webui/src/test-utils.ts
@@ -41,8 +41,10 @@ export function createExample<Props>(
// check how we can build evaluatedProps in render time
const evaluatedProps = typeof props === "function" ? props() : props;
const Render = (args: any): VNode => create(Component, args);
- Render.args = evaluatedProps;
- return Render;
+ return {
+ component: Render,
+ props: evaluatedProps
+ };
}
export function createExampleWithCustomContext<Props, ContextProps>(
@@ -58,8 +60,10 @@ export function createExampleWithCustomContext<Props, ContextProps>(
...contextProps,
children: [Render(args)],
} as any);
- WithContext.args = evaluatedProps;
- return WithContext;
+ return {
+ component: WithContext,
+ props: evaluatedProps
+ };
}
export function NullLink({
diff --git a/packages/anastasis-webui/src/utils/index.tsx b/packages/anastasis-webui/src/utils/index.tsx
index 78973e38f..4cf839473 100644
--- a/packages/anastasis-webui/src/utils/index.tsx
+++ b/packages/anastasis-webui/src/utils/index.tsx
@@ -37,16 +37,18 @@ export function createExampleWithoutAnastasis<Props>(
// check how we can build evaluatedProps in render time
const evaluatedProps = typeof props === "function" ? props() : props;
const Render = (args: any): VNode => h(Component, args);
- Render.args = evaluatedProps;
- return Render;
+ return {
+ component: Render,
+ props: evaluatedProps,
+ };
}
export function createExample<Props>(
Component: FunctionalComponent<Props>,
currentReducerState?: ReducerState,
props?: Partial<Props>,
-): { (args: Props): VNode } {
- const r = (args: Props): VNode => {
+): ComponentChildren {
+ const Render = (args: Props): VNode => {
return (
<AnastasisProvider
value={{
@@ -74,8 +76,10 @@ export function createExample<Props>(
</AnastasisProvider>
);
};
- r.args = props;
- return r;
+ return {
+ component: Render,
+ props: props,
+ };
}
const base = {
diff --git a/packages/anastasis-webui/watch/reply.sh b/packages/anastasis-webui/watch/reply.sh
deleted file mode 100755
index 20cbff37e..000000000
--- a/packages/anastasis-webui/watch/reply.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-SERVER_KEY=258EAFA5-E914-47DA-95CA-C5AB0DC85B11
-
-while read line; do
- LINE=$(echo $line | tr -d '\r')
- case $LINE in
- Sec-WebSocket-Key:*)
- CLIENT_KEY="${LINE:19}"
- export WS_ACCEPT=$( echo -n $CLIENT_KEY$SERVER_KEY | sha1sum | xxd -r -p | base64 )
- ;;
- "") break ;;
- esac
-done
-
-cat watch/web_socket_server.reply | sed 's/$'"/`echo \\\r`/" | envsubst '$WS_ACCEPT'
-
-tail -n 0 -F /tmp/send_signal 2> /dev/null
-
diff --git a/packages/anastasis-webui/watch/send.sh b/packages/anastasis-webui/watch/send.sh
deleted file mode 100755
index 184cd2491..000000000
--- a/packages/anastasis-webui/watch/send.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-
-#https://datatracker.ietf.org/doc/html/rfc6455#page-65
-
-COMMAND=$1
-LEN=$(printf '%x\n' ${#COMMAND})
-
-#text command
-OPCODE=81
-
-cat <(echo -n $OPCODE$LEN | xxd -r -p) <(echo -n $COMMAND) >> /tmp/send_signal
-
diff --git a/packages/anastasis-webui/watch/send2.sh b/packages/anastasis-webui/watch/send2.sh
deleted file mode 100755
index 6a2881c19..000000000
--- a/packages/anastasis-webui/watch/send2.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-#https://datatracker.ietf.org/doc/html/rfc6455#page-65
-
-CONTENT=$( cat $1 | base64 -w 0 )
-COMMAND='{"type":"UPDATE","'$CONTENT'"}'
-LEN=$(printf '%0*x\n' 4 ${#COMMAND})
-echo $LEN
-LEN=00000138
-#text command
-OPCODE=81
-
-cat <(echo -n $OPCODE$LEN | xxd -r -p) <(echo -n $COMMAND) >> /tmp/send_signal
-
diff --git a/packages/anastasis-webui/watch/serve.sh b/packages/anastasis-webui/watch/serve.sh
deleted file mode 100755
index f4e9595d5..000000000
--- a/packages/anastasis-webui/watch/serve.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-#clean up
-rm /tmp/send_signal
-
-socat TCP-LISTEN:8003,fork,reuseaddr,keepalive EXEC:"./watch/reply.sh"
-
diff --git a/packages/anastasis-webui/watch/web_socket_client.request b/packages/anastasis-webui/watch/web_socket_client.request
deleted file mode 100644
index e7077b0cb..000000000
--- a/packages/anastasis-webui/watch/web_socket_client.request
+++ /dev/null
@@ -1,6 +0,0 @@
-GET /socket HTTP/1.1
-Connection: Upgrade
-Upgrade: websocket
-Sec-WebSocket-Version: 13
-Sec-WebSocket-Key: aaaaaaaaaaaaaaaaaaaaaa==
-
diff --git a/packages/anastasis-webui/watch/web_socket_server.reply b/packages/anastasis-webui/watch/web_socket_server.reply
deleted file mode 100644
index b4e0db001..000000000
--- a/packages/anastasis-webui/watch/web_socket_server.reply
+++ /dev/null
@@ -1,5 +0,0 @@
-HTTP/1.1 101 Switching Protocols
-Upgrade: websocket
-Connection: Upgrade
-Sec-WebSocket-Accept: $WS_ACCEPT
-