diff options
Diffstat (limited to 'packages')
20 files changed, 437 insertions, 40 deletions
| diff --git a/packages/anastasis-webui/clean_and_build.sh b/packages/anastasis-webui/clean_and_build.sh index 300ed8930..2fa3ec777 100755 --- a/packages/anastasis-webui/clean_and_build.sh +++ b/packages/anastasis-webui/clean_and_build.sh @@ -10,16 +10,51 @@ cp \  	src/scss/fonts/materialdesignicons-webfont-4.9.95.woff2 \  	dist/fonts -echo css -pnpm exec sass -I . ./src/scss/main.scss dist/main.css & -echo js -pnpm exec esbuild --log-level=error --bundle src/main.ts --outdir=dist --target=es6 --loader:.svg=dataurl --format=iife --sourcemap --jsx-factory=h --jsx-fragment=Fragment --platform=browser & +function build_css() { +	pnpm exec sass -I . ./src/scss/main.scss dist/main.css +} +function build_js() { +	pnpm exec esbuild --log-level=error --bundle $1 --outdir=dist --target=es6 --loader:.svg=dataurl --format=iife --sourcemap --jsx-factory=h --jsx-fragment=Fragment --platform=browser +} + +function bundle() { +	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... " + kill -- -$$ + exit 1 +} +trap cleanup SIGHUP SIGINT SIGTERM SIGQUIT + + +echo compile +build_css & +build_js src/main.ts & +build_js src/main.test.ts &  wait -n  wait -n +wait -n +pnpm run --silent test -- -R dot  echo html -cat ui.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/index.html -echo done +bundle ui +bundle ui-dev + + +if [ "WATCH" == "$1" ]; then + +  echo watch mode +  inotifywait -e close_write -r src -q -m | while read line; do +    DATE=$(date) +    echo $DATE $line +    build_js src/main.ts +    bundle ui-dev +    ./watch/send_reload.sh +  done; +fi diff --git a/packages/anastasis-webui/dev.mjs b/packages/anastasis-webui/dev.mjs index d6b6bf10d..3f4915ffc 100755 --- a/packages/anastasis-webui/dev.mjs +++ b/packages/anastasis-webui/dev.mjs @@ -1,4 +1,19 @@  #!/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'; @@ -41,14 +56,17 @@ const watcher = chokidar      broadcast(file, { type: "RELOAD" });    }); - -fs.writeFileSync("dist/stories.html", fs.readFileSync("stories.html")) +/** + * 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/stories.tsx'], +  entryPoints: ['src/main.ts', 'src/stories.tsx'],    bundle: true,    outdir: 'dist',    minify: false, @@ -75,7 +93,6 @@ const server = await esbuild    });  console.log(`Dev server is ready at http://localhost:${server.port}/. -http://localhost:${server.port}/stories.html for the components stories.  The server is running a using websocket at ${devServerPort} to notify code change and live reload.  `); diff --git a/packages/anastasis-webui/stories.html b/packages/anastasis-webui/html/stories.html index 9f41fdeaf..9f41fdeaf 100644 --- a/packages/anastasis-webui/stories.html +++ b/packages/anastasis-webui/html/stories.html diff --git a/packages/anastasis-webui/html/ui-dev.html b/packages/anastasis-webui/html/ui-dev.html new file mode 100644 index 000000000..4cc36268e --- /dev/null +++ b/packages/anastasis-webui/html/ui-dev.html @@ -0,0 +1,50 @@ +<!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 type="text/css"> +      /* <![CDATA[ */ +      ANASTASIS_STYLE_CONTENT +      /* <![CDATA[ */ +    </style> +  </head> + +  <body> +    <div id="container" class="anastasis-container"></div> +    <script type="application/javascript"> +      ANASTASIS_SCRIPT_CONTENT; +    </script> +    <script type="application/javascript"> +      function setupLiveReload(port) { +        const socketPath = `ws://localhost:${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") { +            window.location.reload(); +          } +        }; +        ws.onerror = (error) => { +          console.error(error); +        }; +      } +      setupLiveReload(8003); +    </script> +  </body> +</html> diff --git a/packages/anastasis-webui/ui.html b/packages/anastasis-webui/html/ui.html index 17c48e904..17c48e904 100644 --- a/packages/anastasis-webui/ui.html +++ b/packages/anastasis-webui/html/ui.html diff --git a/packages/anastasis-webui/package.json b/packages/anastasis-webui/package.json index 95b85585d..7c57160ce 100644 --- a/packages/anastasis-webui/package.json +++ b/packages/anastasis-webui/package.json @@ -9,7 +9,7 @@      "dev": "./dev.mjs",      "prepare": "pnpm compile",      "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'", -    "test": "echo no tests", +    "test": "mocha --enable-source-maps 'dist/**/*.test.js'",      "pretty": "prettier --write src"    },    "dependencies": { @@ -18,6 +18,7 @@      "date-fns": "2.28.0",      "jed": "1.1.1",      "preact": "^10.5.15", +    "preact-render-to-string": "^5.1.19",      "preact-router": "^3.2.1",      "qrcode-generator": "^1.4.4"    }, @@ -34,6 +35,7 @@    },    "devDependencies": {      "@creativebulma/bulma-tooltip": "^1.2.0", +    "@types/mocha": "^9.0.0",      "bulma": "^0.9.3",      "bulma-checkbox": "^1.1.1",      "bulma-radio": "^1.1.1", diff --git a/packages/anastasis-webui/src/main.test.ts b/packages/anastasis-webui/src/main.test.ts new file mode 100644 index 000000000..e65cca454 --- /dev/null +++ b/packages/anastasis-webui/src/main.test.ts @@ -0,0 +1,48 @@ +/* + 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 { setupI18n } from "@gnu-taler/taler-util"; +import { renderNodeOrBrowser } from "./test-utils.js"; +import * as pages from "./pages/home/index.storiesNo.js"; + +setupI18n("en", { en: {} }); + +function testThisStory(st: any): any { +  describe(`render examples for ${(st as any).default.title}`, () => { +    Object.keys(st).forEach((k) => { +      const Component = (st as any)[k]; +      if (k === "default" || !Component) return; + +      it(`example: ${k}`, () => { +        renderNodeOrBrowser(Component, Component.args); +      }); +    }); +  }); +} + +describe("render every storybook example", () => { +  [pages].forEach(function testAll(st: any) { +    if (Array.isArray(st.default)) { +      st.default.forEach(testAll); +    } else { +      testThisStory(st); +    } +  }); +}); diff --git a/packages/anastasis-webui/src/pages/home/AddingProviderScreen.tsx b/packages/anastasis-webui/src/pages/home/AddingProviderScreen.tsx index feeac274d..d4675f9da 100644 --- a/packages/anastasis-webui/src/pages/home/AddingProviderScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/AddingProviderScreen.tsx @@ -223,7 +223,7 @@ function TableRow({    onDelete: (s: string) => void;    url: string;    info: AuthenticationProviderStatusOk; -}) { +}): VNode {    const [status, setStatus] = useState("checking");    useEffect(function () {      testProvider(url.endsWith("/") ? url.substring(0, url.length - 1) : url) diff --git a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx index 43d865b48..534f9136d 100644 --- a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx @@ -125,13 +125,6 @@ export function ContinentSelectionScreen(): VNode {                </div>              </div>            </div> - -          {/* {theCountry && <div class="field"> -            <label class="label">Available currencies:</label> -            <div class="control"> -              <input class="input is-small" type="text" readonly value={theCountry.currency} /> -            </div> -          </div>} */}          </div>          <div class="column is-two-third">            <p> @@ -151,18 +144,11 @@ export function ContinentSelectionScreen(): VNode {              }}            >              <p> -              If you just want to try out Anastasis, we recomment that you +              If you just want to try out Anastasis, we recommend that you                choose <b>Testcontinent</b> with <b>Demoland</b>. For this special                country, you will be asked for a simple number and not real,                personal identifiable information.              </p> -            {/*              -            <p> -              Because of the diversity of personally identifying information in -              different countries and cultures, we do not support all countries -              yet. If you want to improve the supported countries,{" "} -              <a href="mailto:contact@anastasis.lu">contact us</a>. -            </p> */}            </div>          </div>        </div> diff --git a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx index f9b292d94..0285c87e1 100644 --- a/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/authMethod/AuthMethodTotpSetup.tsx @@ -36,6 +36,7 @@ export function AuthMethodTotpSetup({    const [test, setTest] = useState("");    const secretKey = useMemo(() => {      const array = new Uint8Array(32); +    if (typeof window === "undefined") return array;      return window.crypto.getRandomValues(array);    }, []); diff --git a/packages/anastasis-webui/src/stories.tsx b/packages/anastasis-webui/src/stories.tsx index a51dfb20f..351d6f37b 100644 --- a/packages/anastasis-webui/src/stories.tsx +++ b/packages/anastasis-webui/src/stories.tsx @@ -143,21 +143,24 @@ function ExampleList({                {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}> -                    <a -                      href={`#${eId}`} -                      onClick={(e) => { -                        e.preventDefault(); -                        location.hash = `#${eId}`; -                        onSelectStory(r, eId); -                      }} -                    > +                  <dd +                    id={eId} +                    key={r.name} +                    data-selected={isSelected} +                    onClick={doSelection} +                  > +                    <a href={`#${eId}`} onClick={doSelection}>                        {r.name}                      </a>                    </dd> diff --git a/packages/anastasis-webui/src/test-utils.ts b/packages/anastasis-webui/src/test-utils.ts new file mode 100644 index 000000000..1fcc753ee --- /dev/null +++ b/packages/anastasis-webui/src/test-utils.ts @@ -0,0 +1,201 @@ +/* + 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 { +  ComponentChildren, +  Fragment, +  FunctionalComponent, +  h as create, +  options, +  render as renderIntoDom, +  VNode, +} from "preact"; +import { render as renderToString } from "preact-render-to-string"; + +// When doing tests we want the requestAnimationFrame to be as fast as possible. +// without this option the RAF will timeout after 100ms making the tests slower +options.requestAnimationFrame = (fn: () => void) => { +  // console.log("RAF called") +  return fn(); +}; + +export function createExample<Props>( +  Component: FunctionalComponent<Props>, +  props: Partial<Props> | (() => Partial<Props>), +): ComponentChildren { +  //FIXME: props are evaluated on build time +  // in some cases we want to evaluated the props on render time so we can get some relative timestamp +  // 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; +} + +export function createExampleWithCustomContext<Props, ContextProps>( +  Component: FunctionalComponent<Props>, +  props: Partial<Props> | (() => Partial<Props>), +  ContextProvider: FunctionalComponent<ContextProps>, +  contextProps: Partial<ContextProps>, +): ComponentChildren { +  const evaluatedProps = typeof props === "function" ? props() : props; +  const Render = (args: any): VNode => create(Component, args); +  const WithContext = (args: any): VNode => +    create(ContextProvider, { +      ...contextProps, +      children: [Render(args)], +    } as any); +  WithContext.args = evaluatedProps; +  return WithContext; +} + +export function NullLink({ +  children, +}: { +  children?: ComponentChildren; +}): VNode { +  return create("a", { children, href: "javascript:void(0);" }); +} + +export function renderNodeOrBrowser(Component: any, args: any): void { +  const vdom = create(Component, args); +  if (typeof window === "undefined") { +    renderToString(vdom); +  } else { +    const div = document.createElement("div"); +    document.body.appendChild(div); +    renderIntoDom(vdom, div); +    renderIntoDom(null, div); +    document.body.removeChild(div); +  } +} + +interface Mounted<T> { +  unmount: () => void; +  getLastResultOrThrow: () => T; +  assertNoPendingUpdate: () => void; +  waitNextUpdate: (s?: string) => Promise<void>; +} + +const isNode = typeof window === "undefined"; + +export function mountHook<T>( +  callback: () => T, +  Context?: ({ children }: { children: any }) => VNode, +): Mounted<T> { +  // const result: { current: T | null } = { +  //   current: null +  // } +  let lastResult: T | Error | null = null; + +  const listener: Array<() => void> = []; + +  // component that's going to hold the hook +  function Component(): VNode { +    try { +      lastResult = callback(); +    } catch (e) { +      if (e instanceof Error) { +        lastResult = e; +      } else { +        lastResult = new Error(`mounting the hook throw an exception: ${e}`); +      } +    } + +    // notify to everyone waiting for an update and clean the queue +    listener.splice(0, listener.length).forEach((cb) => cb()); +    return create(Fragment, {}); +  } + +  // create the vdom with context if required +  const vdom = !Context +    ? create(Component, {}) +    : create(Context, { children: [create(Component, {})] }); + +  // waiter callback +  async function waitNextUpdate(_label = ""): Promise<void> { +    if (_label) _label = `. label: "${_label}"`; +    await new Promise((res, rej) => { +      const tid = setTimeout(() => { +        rej( +          Error(`waiting for an update but the hook didn't make one${_label}`), +        ); +      }, 100); + +      listener.push(() => { +        clearTimeout(tid); +        res(undefined); +      }); +    }); +  } + +  const customElement = {} as Element; +  const parentElement = isNode ? customElement : document.createElement("div"); +  if (!isNode) { +    document.body.appendChild(parentElement); +  } + +  renderIntoDom(vdom, parentElement); + +  // clean up callback +  function unmount(): void { +    if (!isNode) { +      document.body.removeChild(parentElement); +    } +  } + +  function getLastResult(): T | Error | null { +    const copy = lastResult; +    lastResult = null; +    return copy; +  } + +  function getLastResultOrThrow(): T { +    const r = getLastResult(); +    if (r instanceof Error) throw r; +    if (!r) throw Error("there was no last result"); +    return r; +  } + +  async function assertNoPendingUpdate(): Promise<void> { +    await new Promise((res, rej) => { +      const tid = setTimeout(() => { +        res(undefined); +      }, 10); + +      listener.push(() => { +        clearTimeout(tid); +        rej( +          Error(`Expecting no pending result but the hook got updated.  +        If the update was not intended you need to check the hook dependencies  +        (or dependencies of the internal state) but otherwise make  +        sure to consume the result before ending the test.`), +        ); +      }); +    }); + +    const r = getLastResult(); +    if (r) +      throw Error(`There are still pending results. +    This may happen because the hook did a new update but the test didn't consume the result using getLastResult`); +  } +  return { +    unmount, +    getLastResultOrThrow, +    waitNextUpdate, +    assertNoPendingUpdate, +  }; +} diff --git a/packages/anastasis-webui/src/utils/index.tsx b/packages/anastasis-webui/src/utils/index.tsx index ce21071f7..204c48d18 100644 --- a/packages/anastasis-webui/src/utils/index.tsx +++ b/packages/anastasis-webui/src/utils/index.tsx @@ -209,10 +209,12 @@ export const reducerStatesExample = {    initial: undefined,    recoverySelectCountry: {      ...base, +    reducer_type: "recovery",      recovery_state: RecoveryStates.CountrySelecting,    } as ReducerState,    recoverySelectContinent: {      ...base, +    reducer_type: "recovery",      recovery_state: RecoveryStates.ContinentSelecting,    } as ReducerState,    secretSelection: { @@ -222,10 +224,12 @@ export const reducerStatesExample = {    } as ReducerState,    recoveryFinished: {      ...base, +    reducer_type: "recovery",      recovery_state: RecoveryStates.RecoveryFinished,    } as ReducerState,    challengeSelecting: {      ...base, +    reducer_type: "recovery",      recovery_state: RecoveryStates.ChallengeSelecting,    } as ReducerState,    challengeSolving: { @@ -235,34 +239,42 @@ export const reducerStatesExample = {    } as ReducerStateRecovery,    challengePaying: {      ...base, +    reducer_type: "recovery",      recovery_state: RecoveryStates.ChallengePaying,    } as ReducerState,    recoveryAttributeEditing: {      ...base, +    reducer_type: "recovery",      recovery_state: RecoveryStates.UserAttributesCollecting,    } as ReducerState,    backupSelectCountry: {      ...base, +    reducer_type: "backup",      backup_state: BackupStates.CountrySelecting,    } as ReducerState,    backupSelectContinent: {      ...base, +    reducer_type: "backup",      backup_state: BackupStates.ContinentSelecting,    } as ReducerState,    secretEdition: {      ...base, +    reducer_type: "backup",      backup_state: BackupStates.SecretEditing,    } as ReducerState,    policyReview: {      ...base, +    reducer_type: "backup",      backup_state: BackupStates.PoliciesReviewing,    } as ReducerState,    policyPay: {      ...base, +    reducer_type: "backup",      backup_state: BackupStates.PoliciesPaying,    } as ReducerState,    backupFinished: {      ...base, +    reducer_type: "backup",      backup_state: BackupStates.BackupFinished,    } as ReducerState,    authEditing: { @@ -272,10 +284,12 @@ export const reducerStatesExample = {    } as ReducerState,    backupAttributeEditing: {      ...base, +    reducer_type: "backup",      backup_state: BackupStates.UserAttributesCollecting,    } as ReducerState,    truthsPaying: {      ...base, +    reducer_type: "backup",      backup_state: BackupStates.TruthsPaying,    } as ReducerState,  }; diff --git a/packages/anastasis-webui/watch/reply.sh b/packages/anastasis-webui/watch/reply.sh new file mode 100755 index 000000000..1b42aa4e3 --- /dev/null +++ b/packages/anastasis-webui/watch/reply.sh @@ -0,0 +1,17 @@ +#!/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' + +socat UNIX-RECV:./send_signal STDOUT diff --git a/packages/anastasis-webui/watch/send_reload.sh b/packages/anastasis-webui/watch/send_reload.sh new file mode 100755 index 000000000..87eef7d54 --- /dev/null +++ b/packages/anastasis-webui/watch/send_reload.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +COMMAND='{"type":"RELOAD"}' +LEN=$(printf '%x\n' ${#COMMAND}) +OPCODE=81 +cat <(echo -n $OPCODE$LEN | xxd -r -p) <(echo -n $COMMAND) | socat - UNIX-SEND:./send_signal + diff --git a/packages/anastasis-webui/watch/serve.sh b/packages/anastasis-webui/watch/serve.sh new file mode 100755 index 000000000..cf2737416 --- /dev/null +++ b/packages/anastasis-webui/watch/serve.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +socat TCP-LISTEN:8003,fork EXEC:"./watch/reply.sh" + diff --git a/packages/anastasis-webui/watch/web_socket_client.request b/packages/anastasis-webui/watch/web_socket_client.request new file mode 100644 index 000000000..e7077b0cb --- /dev/null +++ b/packages/anastasis-webui/watch/web_socket_client.request @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..b4e0db001 --- /dev/null +++ b/packages/anastasis-webui/watch/web_socket_server.reply @@ -0,0 +1,5 @@ +HTTP/1.1 101 Switching Protocols +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Accept: $WS_ACCEPT + diff --git a/packages/taler-wallet-webextension/clean_and_build.sh b/packages/taler-wallet-webextension/clean_and_build.sh index 2662f8b2d..fb53124ef 100755 --- a/packages/taler-wallet-webextension/clean_and_build.sh +++ b/packages/taler-wallet-webextension/clean_and_build.sh @@ -2,7 +2,7 @@  set -e -#rm -rf dist lib tsconfig.tsbuildinfo .linaria-cache +rm -rf dist lib tsconfig.tsbuildinfo .linaria-cache  echo typecheck and bundle...  node build-fast-with-linaria.mjs & diff --git a/packages/taler-wallet-webextension/package.json b/packages/taler-wallet-webextension/package.json index 88baeb261..d9940776c 100644 --- a/packages/taler-wallet-webextension/package.json +++ b/packages/taler-wallet-webextension/package.json @@ -44,6 +44,7 @@    },    "devDependencies": {      "@babel/core": "7.13.16", +    "@babel/plugin-transform-modules-commonjs": "^7.18.2",      "@babel/plugin-transform-react-jsx-source": "^7.12.13",      "@babel/preset-typescript": "^7.13.0",      "@babel/runtime": "^7.17.8", | 
