summaryrefslogtreecommitdiff
path: root/packages/anastasis-webui/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/anastasis-webui/src')
-rw-r--r--packages/anastasis-webui/src/components/menu/SideBar.tsx164
-rw-r--r--packages/anastasis-webui/src/context/anastasis.ts41
-rw-r--r--packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx63
-rw-r--r--packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx25
-rw-r--r--packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx35
-rw-r--r--packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx19
-rw-r--r--packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx60
-rw-r--r--packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx26
-rw-r--r--packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx83
-rw-r--r--packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx39
-rw-r--r--packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx36
-rw-r--r--packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx17
-rw-r--r--packages/anastasis-webui/src/pages/home/CountrySelectionScreen.stories.tsx36
-rw-r--r--packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx20
-rw-r--r--packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx47
-rw-r--r--packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.tsx18
-rw-r--r--packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx42
-rw-r--r--packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.tsx24
-rw-r--r--packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx81
-rw-r--r--packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx42
-rw-r--r--packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx44
-rw-r--r--packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx25
-rw-r--r--packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx50
-rw-r--r--packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx37
-rw-r--r--packages/anastasis-webui/src/pages/home/SolveEmailEntry.tsx13
-rw-r--r--packages/anastasis-webui/src/pages/home/SolvePostEntry.tsx11
-rw-r--r--packages/anastasis-webui/src/pages/home/SolveQuestionEntry.tsx11
-rw-r--r--packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx121
-rw-r--r--packages/anastasis-webui/src/pages/home/SolveScreen.tsx32
-rw-r--r--packages/anastasis-webui/src/pages/home/SolveSmsEntry.tsx13
-rw-r--r--packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx35
-rw-r--r--packages/anastasis-webui/src/pages/home/StartScreen.tsx32
-rw-r--r--packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx40
-rw-r--r--packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx16
-rw-r--r--packages/anastasis-webui/src/pages/home/index.tsx162
-rw-r--r--packages/anastasis-webui/src/utils/index.tsx161
36 files changed, 1452 insertions, 269 deletions
diff --git a/packages/anastasis-webui/src/components/menu/SideBar.tsx b/packages/anastasis-webui/src/components/menu/SideBar.tsx
index 628adb571..df582a5d0 100644
--- a/packages/anastasis-webui/src/components/menu/SideBar.tsx
+++ b/packages/anastasis-webui/src/components/menu/SideBar.tsx
@@ -20,7 +20,9 @@
*/
-import { h, VNode } from 'preact';
+import { Fragment, h, VNode } from 'preact';
+import { BackupStates, RecoveryStates } from '../../../../anastasis-core/lib';
+import { useAnastasisContext } from '../../context/anastasis';
import { Translate } from '../../i18n';
import { LangSelector } from './LangSelector';
@@ -31,8 +33,9 @@ interface Props {
export function Sidebar({ mobile }: Props): VNode {
// const config = useConfigContext();
const config = { version: 'none' }
- const process = { env : { __VERSION__: '0.0.0'}}
-
+ const process = { env: { __VERSION__: '0.0.0' } }
+ const reducer = useAnastasisContext()!
+
return (
<aside class="aside is-placed-left is-expanded">
{mobile && <div class="footer" onClick={(e) => { return e.stopImmediatePropagation() }}>
@@ -47,52 +50,117 @@ export function Sidebar({ mobile }: Props): VNode {
</div>
</div>
<div class="menu is-menu-main">
- <p class="menu-label">
- <Translate>Back up a secret</Translate>
- </p>
+ {!reducer.currentReducerState &&
+ <p class="menu-label">
+ <Translate>Backup or Recorver</Translate>
+ </p>
+ }
<ul class="menu-list">
- <li>
- <div class="has-icon">
- <span class="icon"><i class="mdi mdi-square-edit-outline" /></span>
- <span class="menu-item-label"><Translate>Location &amp; Currency</Translate></span>
- </div>
- </li>
- <li class="is-active">
- <div class="has-icon">
- <span class="icon"><i class="mdi mdi-cash-register" /></span>
- <span class="menu-item-label"><Translate>Personal information</Translate></span>
- </div>
- </li>
- <li>
- <div class="has-icon">
- <span class="icon"><i class="mdi mdi-shopping" /></span>
- <span class="menu-item-label"><Translate>Authorization methods</Translate></span>
- </div>
- </li>
- <li>
- <div class="has-icon">
- <span class="icon"><i class="mdi mdi-bank" /></span>
- <span class="menu-item-label"><Translate>Recovery policies</Translate></span>
- </div>
- </li>
- <li>
- <div class="has-icon">
- <span class="icon"><i class="mdi mdi-bank" /></span>
- <span class="menu-item-label"><Translate>Enter secrets</Translate></span>
- </div>
- </li>
- <li>
- <div class="has-icon">
- <span class="icon"><i class="mdi mdi-bank" /></span>
- <span class="menu-item-label"><Translate>Payment (optional)</Translate></span>
- </div>
- </li>
- <li>
- <div class="has-icon">
- <span class="icon"><i class="mdi mdi-cash" /></span>
- <span class="menu-item-label">Backup completed</span>
- </div>
- </li>
+ {!reducer.currentReducerState &&
+ <li>
+ <div class="ml-4">
+ <span class="menu-item-label"><Translate>Start one options</Translate></span>
+ </div>
+ </li>
+ }
+ {reducer.currentReducerState && reducer.currentReducerState.backup_state ? <Fragment>
+ <li class={reducer.currentReducerState.backup_state === BackupStates.ContinentSelecting ? 'is-active' : ''}>
+ <div class="ml-4">
+ <span class="menu-item-label"><Translate>Continent selection</Translate></span>
+ </div>
+ </li>
+ <li class={reducer.currentReducerState.backup_state === BackupStates.CountrySelecting ? 'is-active' : ''}>
+ <div class="ml-4">
+ <span class="menu-item-label"><Translate>Country selection</Translate></span>
+ </div>
+ </li>
+ <li class={reducer.currentReducerState.backup_state === BackupStates.UserAttributesCollecting ? 'is-active' : ''}>
+ <div class="ml-4">
+
+ <span class="menu-item-label"><Translate>User attributes</Translate></span>
+ </div>
+ </li>
+ <li class={reducer.currentReducerState.backup_state === BackupStates.AuthenticationsEditing ? 'is-active' : ''}>
+ <div class="ml-4">
+
+ <span class="menu-item-label"><Translate>Auth methods</Translate></span>
+ </div>
+ </li>
+ <li class={reducer.currentReducerState.backup_state === BackupStates.PoliciesReviewing ? 'is-active' : ''}>
+ <div class="ml-4">
+
+ <span class="menu-item-label"><Translate>PoliciesReviewing</Translate></span>
+ </div>
+ </li>
+ <li class={reducer.currentReducerState.backup_state === BackupStates.SecretEditing ? 'is-active' : ''}>
+ <div class="ml-4">
+
+ <span class="menu-item-label"><Translate>SecretEditing</Translate></span>
+ </div>
+ </li>
+ <li class={reducer.currentReducerState.backup_state === BackupStates.PoliciesPaying ? 'is-active' : ''}>
+ <div class="ml-4">
+
+ <span class="menu-item-label"><Translate>PoliciesPaying</Translate></span>
+ </div>
+ </li>
+ <li class={reducer.currentReducerState.backup_state === BackupStates.BackupFinished ? 'is-active' : ''}>
+ <div class="ml-4">
+
+ <span class="menu-item-label"><Translate>BackupFinished</Translate></span>
+ </div>
+ </li>
+ <li class={reducer.currentReducerState.backup_state === BackupStates.TruthsPaying ? 'is-active' : ''}>
+ <div class="ml-4">
+
+ <span class="menu-item-label"><Translate>TruthsPaying</Translate></span>
+ </div>
+ </li>
+ </Fragment> : (reducer.currentReducerState && reducer.currentReducerState?.recovery_state && <Fragment>
+ <li class={reducer.currentReducerState.recovery_state === RecoveryStates.ContinentSelecting ? 'is-active' : ''}>
+ <div class="ml-4">
+ <span class="menu-item-label"><Translate>TruthsPaying</Translate></span>
+ </div>
+ </li>
+ <li class={reducer.currentReducerState.recovery_state === RecoveryStates.CountrySelecting ? 'is-active' : ''}>
+ <div class="ml-4">
+ <span class="menu-item-label"><Translate>CountrySelecting</Translate></span>
+ </div>
+ </li>
+ <li class={reducer.currentReducerState.recovery_state === RecoveryStates.UserAttributesCollecting ? 'is-active' : ''}>
+ <div class="ml-4">
+ <span class="menu-item-label"><Translate>UserAttributesCollecting</Translate></span>
+ </div>
+ </li>
+ <li class={reducer.currentReducerState.recovery_state === RecoveryStates.SecretSelecting ? 'is-active' : ''}>
+ <div class="ml-4">
+ <span class="menu-item-label"><Translate>SecretSelecting</Translate></span>
+ </div>
+ </li>
+ <li class={reducer.currentReducerState.recovery_state === RecoveryStates.ChallengeSelecting ? 'is-active' : ''}>
+ <div class="ml-4">
+ <span class="menu-item-label"><Translate>ChallengeSelecting</Translate></span>
+ </div>
+ </li>
+ <li class={reducer.currentReducerState.recovery_state === RecoveryStates.ChallengeSolving ? 'is-active' : ''}>
+ <div class="ml-4">
+ <span class="menu-item-label"><Translate>ChallengeSolving</Translate></span>
+ </div>
+ </li>
+ <li class={reducer.currentReducerState.recovery_state === RecoveryStates.RecoveryFinished ? 'is-active' : ''}>
+ <div class="ml-4">
+ <span class="menu-item-label"><Translate>RecoveryFinished</Translate></span>
+ </div>
+ </li>
+ </Fragment>)}
+ {reducer.currentReducerState &&
+ <li>
+ <div class="buttons ml-4">
+ <button class="button is-danger is-right" onClick={() => reducer.reset()}>Reset session</button>
+ </div>
+ </li>
+ }
+
</ul>
</div>
</aside>
diff --git a/packages/anastasis-webui/src/context/anastasis.ts b/packages/anastasis-webui/src/context/anastasis.ts
new file mode 100644
index 000000000..e7f93ed43
--- /dev/null
+++ b/packages/anastasis-webui/src/context/anastasis.ts
@@ -0,0 +1,41 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { createContext, h, VNode } from 'preact';
+import { useContext } from 'preact/hooks';
+import { AnastasisReducerApi } from '../hooks/use-anastasis-reducer';
+
+type Type = AnastasisReducerApi | undefined;
+
+const initial = undefined
+
+const Context = createContext<Type>(initial)
+
+interface Props {
+ value: AnastasisReducerApi;
+ children: any;
+}
+
+export const AnastasisProvider = ({ value, children }: Props): VNode => {
+ return h(Context.Provider, { value, children });
+}
+
+export const useAnastasisContext = (): Type => useContext(Context); \ No newline at end of file
diff --git a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx
new file mode 100644
index 000000000..d28a6df43
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx
@@ -0,0 +1,63 @@
+/* eslint-disable @typescript-eslint/camelcase */
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { ReducerState } from 'anastasis-core';
+import { createExample, reducerStatesExample } from '../../utils';
+import { AttributeEntryScreen as TestedComponent } from './AttributeEntryScreen';
+
+
+export default {
+ title: 'Pages/AttributeEntryScreen',
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: 'onUpdate' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+export const WithSomeAttributes = createExample(TestedComponent, {
+ ...reducerStatesExample.attributeEditing,
+ required_attributes: [{
+ name: 'first',
+ label: 'first',
+ type: 'type',
+ uuid: 'asdasdsa1',
+ widget: 'wid',
+ }, {
+ name: 'pepe',
+ label: 'second',
+ type: 'type',
+ uuid: 'asdasdsa2',
+ widget: 'wid',
+ }, {
+ name: 'pepe2',
+ label: 'third',
+ type: 'type',
+ uuid: 'asdasdsa3',
+ widget: 'calendar',
+ }]
+} as ReducerState);
+
+export const Empty = createExample(TestedComponent, {
+ ...reducerStatesExample.attributeEditing,
+ required_attributes: undefined
+} as ReducerState);
diff --git a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx
index 6f87a3358..2f804f940 100644
--- a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx
@@ -1,15 +1,24 @@
/* eslint-disable @typescript-eslint/camelcase */
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
-import { ReducerStateRecovery, ReducerStateBackup } from "../../../../anastasis-core/lib";
+import { ReducerStateRecovery, ReducerStateBackup, UserAttributeSpec } from "anastasis-core/lib";
+import { useAnastasisContext } from "../../context/anastasis";
import { AnastasisReducerApi } from "../../hooks/use-anastasis-reducer";
import { AnastasisClientFrame, withProcessLabel, LabeledInput } from "./index";
-export function AttributeEntryScreen(props: AttributeEntryProps): VNode {
- const { reducer, reducerState: backupState } = props;
- const [attrs, setAttrs] = useState<Record<string, string>>(
- props.reducerState.identity_attributes ?? {}
- );
+export function AttributeEntryScreen(): VNode {
+ const reducer = useAnastasisContext()
+ const state = reducer?.currentReducerState
+ const currentIdentityAttributes = state && "identity_attributes" in state ? (state.identity_attributes || {}) : {}
+ const [attrs, setAttrs] = useState<Record<string, string>>(currentIdentityAttributes);
+
+ if (!reducer) {
+ return <div>no reducer in context</div>
+ }
+ if (!reducer.currentReducerState || !("required_attributes" in reducer.currentReducerState)) {
+ return <div>invalid state</div>
+ }
+
return (
<AnastasisClientFrame
title={withProcessLabel(reducer, "Select Country")}
@@ -17,7 +26,7 @@ export function AttributeEntryScreen(props: AttributeEntryProps): VNode {
identity_attributes: attrs,
})}
>
- {backupState.required_attributes.map((x: any, i: number) => {
+ {reducer.currentReducerState.required_attributes?.map((x, i: number) => {
return (
<AttributeEntryField
key={i}
@@ -40,7 +49,7 @@ export interface AttributeEntryFieldProps {
isFirst: boolean;
value: string;
setValue: (newValue: string) => void;
- spec: any;
+ spec: UserAttributeSpec;
}
export function AttributeEntryField(props: AttributeEntryFieldProps): VNode {
diff --git a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx
new file mode 100644
index 000000000..44d3795b2
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx
@@ -0,0 +1,35 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { createExample, reducerStatesExample } from '../../utils';
+import { AuthenticationEditorScreen as TestedComponent } from './AuthenticationEditorScreen';
+
+
+export default {
+ title: 'Pages/AuthenticationEditorScreen',
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: 'onUpdate' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+export const Example = createExample(TestedComponent, reducerStatesExample.authEditing);
diff --git a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx
index fc28942aa..e9ffccbac 100644
--- a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx
@@ -1,7 +1,8 @@
/* eslint-disable @typescript-eslint/camelcase */
+import { AuthMethod, ReducerStateBackup } from "anastasis-core";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
-import { AuthMethod, ReducerStateBackup } from "anastasis-core";
+import { useAnastasisContext } from "../../context/anastasis";
import { AnastasisReducerApi } from "../../hooks/use-anastasis-reducer";
import { AuthMethodEmailSetup } from "./AuthMethodEmailSetup";
import { AuthMethodPostSetup } from "./AuthMethodPostSetup";
@@ -9,12 +10,18 @@ import { AuthMethodQuestionSetup } from "./AuthMethodQuestionSetup";
import { AuthMethodSmsSetup } from "./AuthMethodSmsSetup";
import { AnastasisClientFrame } from "./index";
-export function AuthenticationEditorScreen(props: AuthenticationEditorProps): VNode {
+export function AuthenticationEditorScreen(): VNode {
const [selectedMethod, setSelectedMethod] = useState<string | undefined>(
undefined
);
- const { reducer, backupState } = props;
- const providers = backupState.authentication_providers!;
+ const reducer = useAnastasisContext()
+ if (!reducer) {
+ return <div>no reducer in context</div>
+ }
+ if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) {
+ return <div>invalid state</div>
+ }
+ const providers = reducer.currentReducerState.authentication_providers!;
const authAvailableSet = new Set<string>();
for (const provKey of Object.keys(providers)) {
const p = providers[provKey];
@@ -52,14 +59,14 @@ export function AuthenticationEditorScreen(props: AuthenticationEditorProps): VN
disabled={!authAvailableSet.has(props.method)}
onClick={() => {
setSelectedMethod(props.method);
- reducer.dismissError();
+ if (reducer) reducer.dismissError();
}}
>
{props.label}
</button>
);
}
- const configuredAuthMethods: AuthMethod[] = backupState.authentication_methods ?? [];
+ const configuredAuthMethods: AuthMethod[] = reducer.currentReducerState.authentication_methods ?? [];
const haveMethodsConfigured = configuredAuthMethods.length;
return (
<AnastasisClientFrame title="Backup: Configure Authentication Methods">
diff --git a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx
new file mode 100644
index 000000000..65a2b7e13
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx
@@ -0,0 +1,60 @@
+/* eslint-disable @typescript-eslint/camelcase */
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { ReducerState } from 'anastasis-core';
+import { createExample, reducerStatesExample } from '../../utils';
+import { BackupFinishedScreen as TestedComponent } from './BackupFinishedScreen';
+
+
+export default {
+ title: 'Pages/BackupFinishedScreen',
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: 'onUpdate' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+export const Simple = createExample(TestedComponent, reducerStatesExample.backupFinished);
+
+export const WithName = createExample(TestedComponent, {...reducerStatesExample.backupFinished,
+ secret_name: 'super_secret',
+} as ReducerState);
+
+export const WithDetails = createExample(TestedComponent, {
+ ...reducerStatesExample.backupFinished,
+ secret_name: 'super_secret',
+ success_details: {
+ 'http://anastasis.net': {
+ policy_expiration: {
+ t_ms: 'never'
+ },
+ policy_version: 0
+ },
+ 'http://taler.net': {
+ policy_expiration: {
+ t_ms: new Date().getTime() + 60*60*24*1000
+ },
+ policy_version: 1
+ },
+ }
+} as ReducerState);
diff --git a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx
index 6c2770947..218f1d1fd 100644
--- a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx
@@ -1,23 +1,33 @@
import { h, VNode } from "preact";
-import { BackupReducerProps, AnastasisClientFrame } from "./index";
+import { useAnastasisContext } from "../../context/anastasis";
+import { AnastasisClientFrame } from "./index";
-export function BackupFinishedScreen(props: BackupReducerProps): VNode {
+export function BackupFinishedScreen(): VNode {
+ const reducer = useAnastasisContext()
+ if (!reducer) {
+ return <div>no reducer in context</div>
+ }
+ if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) {
+ return <div>invalid state</div>
+ }
+ const details = reducer.currentReducerState.success_details
return (<AnastasisClientFrame hideNext title="Backup finished">
<p>
- Your backup of secret "{props.backupState.secret_name ?? "??"}" was
+ Your backup of secret "{reducer.currentReducerState.secret_name ?? "??"}" was
successful.
</p>
<p>The backup is stored by the following providers:</p>
- <ul>
- {Object.keys(props.backupState.success_details!).map((x, i) => {
- const sd = props.backupState.success_details![x];
+
+ {details && <ul>
+ {Object.keys(details).map((x, i) => {
+ const sd = details[x];
return (
<li key={i}>
{x} (Policy version {sd.policy_version})
</li>
);
})}
- </ul>
- <button onClick={() => props.reducer.reset()}>Back to start</button>
+ </ul>}
+ <button onClick={() => reducer.reset()}>Back to start</button>
</AnastasisClientFrame>);
}
diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx
new file mode 100644
index 000000000..4f186c031
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx
@@ -0,0 +1,83 @@
+/* eslint-disable @typescript-eslint/camelcase */
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { ReducerState } from 'anastasis-core';
+import { createExample, reducerStatesExample } from '../../utils';
+import { ChallengeOverviewScreen as TestedComponent } from './ChallengeOverviewScreen';
+
+
+export default {
+ title: 'Pages/ChallengeOverviewScreen',
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: 'onUpdate' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+export const OneChallenge = createExample(TestedComponent, {...reducerStatesExample.challengeSelecting,
+ recovery_information: {
+ policies: [[{uuid:'1'}]],
+ challenges: [{
+ cost: 'USD:1',
+ instructions: 'just go for it',
+ type: 'question',
+ uuid: '1',
+ }]
+ },
+} as ReducerState);
+
+export const MoreChallenges = createExample(TestedComponent, {...reducerStatesExample.challengeSelecting,
+ recovery_information: {
+ policies: [[{uuid:'1'}, {uuid:'2'}],[{uuid:'3'}]],
+ challenges: [{
+ cost: 'USD:1',
+ instructions: 'just go for it',
+ type: 'question',
+ uuid: '1',
+ },{
+ cost: 'USD:1',
+ instructions: 'just go for it',
+ type: 'question',
+ uuid: '2',
+ },{
+ cost: 'USD:1',
+ instructions: 'just go for it',
+ type: 'question',
+ uuid: '3',
+ }]
+ },
+} as ReducerState);
+
+export const OneBadConfiguredPolicy = createExample(TestedComponent, {...reducerStatesExample.challengeSelecting,
+ recovery_information: {
+ policies: [[{uuid:'2'}]],
+ challenges: [{
+ cost: 'USD:1',
+ instructions: 'just go for it',
+ type: 'sasd',
+ uuid: '1',
+ }]
+ },
+} as ReducerState);
+
+export const NoPolicies = createExample(TestedComponent, reducerStatesExample.challengeSelecting);
diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx
index 1f108ce6d..c9b52e91b 100644
--- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx
@@ -1,10 +1,21 @@
import { h, VNode } from "preact";
-import { RecoveryReducerProps, AnastasisClientFrame } from "./index";
+import { useAnastasisContext } from "../../context/anastasis";
+import { AnastasisClientFrame } from "./index";
+
+export function ChallengeOverviewScreen(): VNode {
+ const reducer = useAnastasisContext()
+
+ if (!reducer) {
+ return <div>no reducer in context</div>
+ }
+ if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) {
+ return <div>invalid state</div>
+ }
+
+ const policies = reducer.currentReducerState.recovery_information?.policies ?? [];
+ const chArr = reducer.currentReducerState.recovery_information?.challenges ?? [];
+ const challengeFeedback = reducer.currentReducerState?.challenge_feedback;
-export function ChallengeOverviewScreen(props: RecoveryReducerProps): VNode {
- const { recoveryState, reducer } = props;
- const policies = recoveryState.recovery_information!.policies;
- const chArr = recoveryState.recovery_information!.challenges;
const challenges: {
[uuid: string]: {
type: string;
@@ -22,15 +33,21 @@ export function ChallengeOverviewScreen(props: RecoveryReducerProps): VNode {
return (
<AnastasisClientFrame title="Recovery: Solve challenges">
<h2>Policies</h2>
- {policies.map((x, i) => {
+ {!policies.length && <p>
+ No policies found
+ </p>}
+ {policies.map((row, i) => {
return (
<div key={i}>
<h3>Policy #{i + 1}</h3>
- {x.map((x, j) => {
- const ch = challenges[x.uuid];
- const feedback = recoveryState.challenge_feedback?.[x.uuid];
+ {row.map(column => {
+ const ch = challenges[column.uuid];
+ if (!ch) return <div>
+ There is no challenge for this policy
+ </div>
+ const feedback = challengeFeedback?.[column.uuid];
return (
- <div key={j}
+ <div key={column.uuid}
style={{
borderLeft: "2px solid gray",
paddingLeft: "0.5em",
@@ -46,7 +63,7 @@ export function ChallengeOverviewScreen(props: RecoveryReducerProps): VNode {
{feedback?.state !== "solved" ? (
<button
onClick={() => reducer.transition("select_challenge", {
- uuid: x.uuid,
+ uuid: column.uuid,
})}
>
Solve
diff --git a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx
new file mode 100644
index 000000000..aad37cd7f
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx
@@ -0,0 +1,36 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { createExample, reducerStatesExample } from '../../utils';
+import { ContinentSelectionScreen as TestedComponent } from './ContinentSelectionScreen';
+
+
+export default {
+ title: 'Pages/ContinentSelectionScreen',
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: 'onUpdate' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+export const Backup = createExample(TestedComponent, reducerStatesExample.backupSelectCountry);
+export const Recovery = createExample(TestedComponent, reducerStatesExample.recoverySelectCountry);
diff --git a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx
index 2fed23d4e..ad529a4a7 100644
--- a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx
@@ -1,15 +1,16 @@
import { h, VNode } from "preact";
-import { CommonReducerProps, AnastasisClientFrame, withProcessLabel } from "./index";
+import { useAnastasisContext } from "../../context/anastasis";
+import { AnastasisClientFrame, withProcessLabel } from "./index";
-export function ContinentSelectionScreen(props: CommonReducerProps): VNode {
- const { reducer, reducerState } = props;
+export function ContinentSelectionScreen(): VNode {
+ const reducer = useAnastasisContext()
+ if (!reducer || !reducer.currentReducerState || !("continents" in reducer.currentReducerState)) {
+ return <div />
+ }
const sel = (x: string): void => reducer.transition("select_continent", { continent: x });
return (
- <AnastasisClientFrame
- hideNext
- title={withProcessLabel(reducer, "Select Continent")}
- >
- {reducerState.continents.map((x: any) => (
+ <AnastasisClientFrame hideNext title={withProcessLabel(reducer, "Select Continent")}>
+ {reducer.currentReducerState.continents.map((x: any) => (
<button onClick={() => sel(x.name)} key={x.name}>
{x.name}
</button>
diff --git a/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.stories.tsx
new file mode 100644
index 000000000..adf36980f
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.stories.tsx
@@ -0,0 +1,36 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { createExample, reducerStatesExample } from '../../utils';
+import { CountrySelectionScreen as TestedComponent } from './CountrySelectionScreen';
+
+
+export default {
+ title: 'Pages/CountrySelectionScreen',
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: 'onUpdate' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+export const Backup = createExample(TestedComponent, reducerStatesExample.backupSelectCountry);
+export const Recovery = createExample(TestedComponent, reducerStatesExample.recoverySelectCountry);
diff --git a/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx
index dbe4b7616..555622c1d 100644
--- a/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx
@@ -1,19 +1,23 @@
/* eslint-disable @typescript-eslint/camelcase */
import { h, VNode } from "preact";
-import { CommonReducerProps, AnastasisClientFrame, withProcessLabel } from "./index";
+import { useAnastasisContext } from "../../context/anastasis";
+import { AnastasisClientFrame, withProcessLabel } from "./index";
-export function CountrySelectionScreen(props: CommonReducerProps): VNode {
- const { reducer, reducerState } = props;
+export function CountrySelectionScreen(): VNode {
+ const reducer = useAnastasisContext()
+ if (!reducer) {
+ return <div>no reducer in context</div>
+ }
+ if (!reducer.currentReducerState || !("countries" in reducer.currentReducerState)) {
+ return <div>invalid state</div>
+ }
const sel = (x: any): void => reducer.transition("select_country", {
country_code: x.code,
currencies: [x.currency],
});
return (
- <AnastasisClientFrame
- hideNext
- title={withProcessLabel(reducer, "Select Country")}
- >
- {reducerState.countries.map((x: any) => (
+ <AnastasisClientFrame hideNext title={withProcessLabel(reducer, "Select Country")} >
+ {reducer.currentReducerState.countries.map((x: any) => (
<button onClick={() => sel(x)} key={x.name}>
{x.name} ({x.currency})
</button>
diff --git a/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx
new file mode 100644
index 000000000..1a9462b88
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx
@@ -0,0 +1,47 @@
+/* eslint-disable @typescript-eslint/camelcase */
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { ReducerState } from 'anastasis-core';
+import { createExample, reducerStatesExample } from '../../utils';
+import { PoliciesPayingScreen as TestedComponent } from './PoliciesPayingScreen';
+
+
+export default {
+ title: 'Pages/PoliciesPayingScreen',
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: 'onUpdate' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+export const Example = createExample(TestedComponent, reducerStatesExample.policyPay);
+export const WithSomePaymentRequest = createExample(TestedComponent, {
+ ...reducerStatesExample.policyPay,
+ policy_payment_requests: [{
+ payto: 'payto://x-taler-bank/bank.taler/account-a',
+ provider: 'provider1'
+ }, {
+ payto: 'payto://x-taler-bank/bank.taler/account-b',
+ provider: 'provider2'
+ }]
+} as ReducerState);
diff --git a/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.tsx b/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.tsx
index be74729eb..8a39cf0e4 100644
--- a/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.tsx
@@ -1,9 +1,17 @@
import { h, VNode } from "preact";
-import { BackupReducerProps, AnastasisClientFrame } from "./index";
-
-export function PoliciesPayingScreen(props: BackupReducerProps): VNode {
- const payments = props.backupState.policy_payment_requests ?? [];
+import { useAnastasisContext } from "../../context/anastasis";
+import { AnastasisClientFrame } from "./index";
+export function PoliciesPayingScreen(): VNode {
+ const reducer = useAnastasisContext()
+ if (!reducer) {
+ return <div>no reducer in context</div>
+ }
+ if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) {
+ return <div>invalid state</div>
+ }
+ const payments = reducer.currentReducerState.policy_payment_requests ?? [];
+
return (
<AnastasisClientFrame hideNext title="Backup: Recovery Document Payments">
<p>
@@ -19,7 +27,7 @@ export function PoliciesPayingScreen(props: BackupReducerProps): VNode {
);
})}
</ul>
- <button onClick={() => props.reducer.transition("pay", {})}>
+ <button onClick={() => reducer.transition("pay", {})}>
Check payment status now
</button>
</AnastasisClientFrame>
diff --git a/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx
new file mode 100644
index 000000000..0c1842420
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx
@@ -0,0 +1,42 @@
+/* eslint-disable @typescript-eslint/camelcase */
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { ReducerState } from 'anastasis-core';
+import { createExample, reducerStatesExample } from '../../utils';
+import { RecoveryFinishedScreen as TestedComponent } from './RecoveryFinishedScreen';
+
+
+export default {
+ title: 'Pages/RecoveryFinishedScreen',
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: 'onUpdate' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+export const NormalEnding = createExample(TestedComponent, {
+ ...reducerStatesExample.recoveryFinished,
+ core_secret: { mime: 'text/plain', value: 'hello' }
+} as ReducerState);
+
+export const BadEnding = createExample(TestedComponent, reducerStatesExample.recoveryFinished);
diff --git a/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.tsx b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.tsx
index 7ccc511ff..8c8a2c7c8 100644
--- a/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.tsx
@@ -3,13 +3,31 @@ import {
decodeCrock
} from "@gnu-taler/taler-util";
import { h, VNode } from "preact";
-import { RecoveryReducerProps, AnastasisClientFrame } from "./index";
+import { useAnastasisContext } from "../../context/anastasis";
+import { AnastasisClientFrame } from "./index";
-export function RecoveryFinishedScreen(props: RecoveryReducerProps): VNode {
+export function RecoveryFinishedScreen(): VNode {
+ const reducer = useAnastasisContext()
+
+ if (!reducer) {
+ return <div>no reducer in context</div>
+ }
+ if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) {
+ return <div>invalid state</div>
+ }
+ const encodedSecret = reducer.currentReducerState.core_secret?.value
+ if (!encodedSecret) {
+ return <AnastasisClientFrame title="Recovery Problem" hideNext>
+ <p>
+ Secret not found
+ </p>
+ </AnastasisClientFrame>
+ }
+ const secret = bytesToString(decodeCrock(encodedSecret))
return (
<AnastasisClientFrame title="Recovery Finished" hideNext>
<p>
- Secret: {bytesToString(decodeCrock(props.recoveryState.core_secret?.value!))}
+ Secret: {secret}
</p>
</AnastasisClientFrame>
);
diff --git a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx
new file mode 100644
index 000000000..b52699e7b
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx
@@ -0,0 +1,81 @@
+/* eslint-disable @typescript-eslint/camelcase */
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { ReducerState } from 'anastasis-core';
+import { createExample, reducerStatesExample } from '../../utils';
+import { ReviewPoliciesScreen as TestedComponent } from './ReviewPoliciesScreen';
+
+
+export default {
+ title: 'Pages/ReviewPoliciesScreen',
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: 'onUpdate' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+export const HasPoliciesButMethodListIsEmpty = createExample(TestedComponent, {
+ ...reducerStatesExample.policyReview,
+ policies: [{
+ methods: [{
+ authentication_method: 0,
+ provider: 'asd'
+ },{
+ authentication_method: 1,
+ provider: 'asd'
+ }]
+ },{
+ methods: [{
+ authentication_method: 1,
+ provider: 'asd'
+ }]
+ }],
+ authentication_methods: []
+} as ReducerState);
+
+export const SomePoliciesWithMethods = createExample(TestedComponent, {
+ ...reducerStatesExample.policyReview,
+ policies: [{
+ methods: [{
+ authentication_method: 0,
+ provider: 'asd'
+ },{
+ authentication_method: 1,
+ provider: 'asd'
+ }]
+ },{
+ methods: [{
+ authentication_method: 1,
+ provider: 'asd'
+ }]
+ }],
+ authentication_methods: [{
+ challenge: 'asd',
+ instructions: 'ins',
+ type: 'type',
+ },{
+ challenge: 'asd2',
+ instructions: 'ins2',
+ type: 'type2',
+ }]
+} as ReducerState);
diff --git a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx
index 3e20538d2..b360ccaf0 100644
--- a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx
@@ -1,35 +1,49 @@
/* eslint-disable @typescript-eslint/camelcase */
import { h, VNode } from "preact";
-import { BackupReducerProps, AnastasisClientFrame } from "./index";
+import { useAnastasisContext } from "../../context/anastasis";
+import { AnastasisClientFrame } from "./index";
+
+export function ReviewPoliciesScreen(): VNode {
+ const reducer = useAnastasisContext()
+ if (!reducer) {
+ return <div>no reducer in context</div>
+ }
+ if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) {
+ return <div>invalid state</div>
+ }
+ const authMethods = reducer.currentReducerState.authentication_methods ?? [];
+ const policies = reducer.currentReducerState.policies ?? [];
-export function ReviewPoliciesScreen(props: BackupReducerProps): VNode {
- const { reducer, backupState } = props;
- const authMethods = backupState.authentication_methods!;
return (
<AnastasisClientFrame title="Backup: Review Recovery Policies">
- {backupState.policies?.map((p, i) => {
- const policyName = p.methods
- .map((x, i) => authMethods[x.authentication_method].type)
- .join(" + ");
+ {policies.map((p, policy_index) => {
+ const methods = p.methods
+ .map(x => authMethods[x.authentication_method] && ({ ...authMethods[x.authentication_method], provider: x.provider }))
+ .filter(x => !!x)
+
+ const policyName = methods.map(x => x.type).join(" + ");
+
return (
- <div key={i} class="policy">
+ <div key={policy_index} class="policy">
<h3>
- Policy #{i + 1}: {policyName}
+ Policy #{policy_index + 1}: {policyName}
</h3>
Required Authentications:
+ {!methods.length && <p>
+ No auth method found
+ </p>}
<ul>
- {p.methods.map((x, i) => {
- const m = authMethods[x.authentication_method];
+ {methods.map((m, i) => {
return (
<li key={i}>
- {m.type} ({m.instructions}) at provider {x.provider}
+ {m.type} ({m.instructions}) at provider {m.provider}
</li>
);
})}
</ul>
<div>
<button
- onClick={() => reducer.transition("delete_policy", { policy_index: i })}
+ onClick={() => reducer.transition("delete_policy", { policy_index })}
>
Delete Policy
</button>
diff --git a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx
new file mode 100644
index 000000000..18560356a
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx
@@ -0,0 +1,44 @@
+/* eslint-disable @typescript-eslint/camelcase */
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { ReducerState } from 'anastasis-core';
+import { createExample, reducerStatesExample } from '../../utils';
+import { SecretEditorScreen as TestedComponent } from './SecretEditorScreen';
+
+
+export default {
+ title: 'Pages/SecretEditorScreen',
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: 'onUpdate' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+export const WithSecretNamePreselected = createExample(TestedComponent, {
+ ...reducerStatesExample.secretEdition,
+ secret_name: 'someSecretName',
+} as ReducerState);
+
+export const WithoutName = createExample(TestedComponent, {
+ ...reducerStatesExample.secretEdition,
+} as ReducerState);
diff --git a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx
index 086d4921d..a5235d66c 100644
--- a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx
@@ -2,18 +2,29 @@
import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
+import { useAnastasisContext } from "../../context/anastasis";
import {
- BackupReducerProps,
AnastasisClientFrame,
- LabeledInput,
+ LabeledInput
} from "./index";
-export function SecretEditorScreen(props: BackupReducerProps): VNode {
- const { reducer } = props;
- const [secretName, setSecretName] = useState(
- props.backupState.secret_name ?? "",
- );
+export function SecretEditorScreen(): VNode {
+ const reducer = useAnastasisContext()
const [secretValue, setSecretValue] = useState("");
+
+ const currentSecretName = reducer?.currentReducerState
+ && ("secret_name" in reducer.currentReducerState)
+ && reducer.currentReducerState.secret_name;
+
+ const [secretName, setSecretName] = useState(currentSecretName || "");
+
+ if (!reducer) {
+ return <div>no reducer in context</div>
+ }
+ if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) {
+ return <div>invalid state</div>
+ }
+
const secretNext = (): void => {
reducer.runTransaction(async (tx) => {
await tx.transition("enter_secret_name", {
diff --git a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx
new file mode 100644
index 000000000..e9c597023
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx
@@ -0,0 +1,50 @@
+/* eslint-disable @typescript-eslint/camelcase */
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { ReducerState } from 'anastasis-core';
+import { createExample, reducerStatesExample } from '../../utils';
+import { SecretSelectionScreen as TestedComponent } from './SecretSelectionScreen';
+
+
+export default {
+ title: 'Pages/SecretSelectionScreen',
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: 'onUpdate' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+export const Example = createExample(TestedComponent, {
+ ...reducerStatesExample.secretSelection,
+ recovery_document: {
+ provider_url: 'http://anastasis.url/',
+ secret_name: 'secretName',
+ version: 1,
+ },
+} as ReducerState);
+
+
+export const NoRecoveryDocumentFound = createExample(TestedComponent, {
+ ...reducerStatesExample.secretSelection,
+ recovery_document: undefined,
+} as ReducerState);
diff --git a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx
index 7cb7fdf20..903f57868 100644
--- a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx
@@ -1,17 +1,29 @@
/* eslint-disable @typescript-eslint/camelcase */
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
-import { RecoveryReducerProps, AnastasisClientFrame } from "./index";
+import { useAnastasisContext } from "../../context/anastasis";
+import { AnastasisClientFrame } from "./index";
-export function SecretSelectionScreen(props: RecoveryReducerProps): VNode {
- const { reducer, recoveryState } = props;
+export function SecretSelectionScreen(): VNode {
const [selectingVersion, setSelectingVersion] = useState<boolean>(false);
- const [otherVersion, setOtherVersion] = useState<number>(
- recoveryState.recovery_document?.version ?? 0
- );
- const recoveryDocument = recoveryState.recovery_document!;
const [otherProvider, setOtherProvider] = useState<string>("");
+ const reducer = useAnastasisContext()
+
+ const currentVersion = reducer?.currentReducerState
+ && ("recovery_document" in reducer.currentReducerState)
+ && reducer.currentReducerState.recovery_document?.version;
+
+ const [otherVersion, setOtherVersion] = useState<number>(currentVersion || 0);
+
+ if (!reducer) {
+ return <div>no reducer in context</div>
+ }
+ if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) {
+ return <div>invalid state</div>
+ }
+
function selectVersion(p: string, n: number): void {
+ if (!reducer) return;
reducer.runTransaction(async (tx) => {
await tx.transition("change_version", {
version: n,
@@ -20,12 +32,21 @@ export function SecretSelectionScreen(props: RecoveryReducerProps): VNode {
setSelectingVersion(false);
});
}
+
+ const recoveryDocument = reducer.currentReducerState.recovery_document
+ if (!recoveryDocument) {
+ return (
+ <AnastasisClientFrame hideNav title="Recovery: Problem">
+ <p>No recovery document found</p>
+ </AnastasisClientFrame>
+ )
+ }
if (selectingVersion) {
return (
<AnastasisClientFrame hideNav title="Recovery: Select secret">
<p>Select a different version of the secret</p>
<select onChange={(e) => setOtherProvider((e.target as any).value)}>
- {Object.keys(recoveryState.authentication_providers ?? {}).map(
+ {Object.keys(reducer.currentReducerState.authentication_providers ?? {}).map(
(x, i) => (
<option key={i} selected={x === recoveryDocument.provider_url} value={x}>
{x}
diff --git a/packages/anastasis-webui/src/pages/home/SolveEmailEntry.tsx b/packages/anastasis-webui/src/pages/home/SolveEmailEntry.tsx
index 6296dc022..2c27895c2 100644
--- a/packages/anastasis-webui/src/pages/home/SolveEmailEntry.tsx
+++ b/packages/anastasis-webui/src/pages/home/SolveEmailEntry.tsx
@@ -1,14 +1,17 @@
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
+import { useAnastasisContext } from "../../context/anastasis";
import { AnastasisClientFrame, LabeledInput } from "./index";
import { SolveEntryProps } from "./SolveScreen";
-export function SolveEmailEntry(props: SolveEntryProps): VNode {
+export function SolveEmailEntry({ challenge, feedback }: SolveEntryProps): VNode {
const [answer, setAnswer] = useState("");
- const { reducer, challenge, feedback } = props;
- const next = (): void => reducer.transition("solve_challenge", {
- answer,
- });
+ const reducer = useAnastasisContext()
+ const next = (): void => {
+ if (reducer) reducer.transition("solve_challenge", {
+ answer,
+ })
+ };
return (
<AnastasisClientFrame
title="Recovery: Solve challenge"
diff --git a/packages/anastasis-webui/src/pages/home/SolvePostEntry.tsx b/packages/anastasis-webui/src/pages/home/SolvePostEntry.tsx
index b11ceed27..1a824acb8 100644
--- a/packages/anastasis-webui/src/pages/home/SolvePostEntry.tsx
+++ b/packages/anastasis-webui/src/pages/home/SolvePostEntry.tsx
@@ -1,14 +1,15 @@
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
+import { useAnastasisContext } from "../../context/anastasis";
import { AnastasisClientFrame, LabeledInput } from "./index";
import { SolveEntryProps } from "./SolveScreen";
-export function SolvePostEntry(props: SolveEntryProps): VNode {
+export function SolvePostEntry({ challenge, feedback }: SolveEntryProps): VNode {
const [answer, setAnswer] = useState("");
- const { reducer, challenge, feedback } = props;
- const next = (): void => reducer.transition("solve_challenge", {
- answer,
- });
+ const reducer = useAnastasisContext()
+ const next = (): void => {
+ if (reducer) reducer.transition("solve_challenge", { answer })
+ };
return (
<AnastasisClientFrame
title="Recovery: Solve challenge"
diff --git a/packages/anastasis-webui/src/pages/home/SolveQuestionEntry.tsx b/packages/anastasis-webui/src/pages/home/SolveQuestionEntry.tsx
index 6393958b3..72dadbe89 100644
--- a/packages/anastasis-webui/src/pages/home/SolveQuestionEntry.tsx
+++ b/packages/anastasis-webui/src/pages/home/SolveQuestionEntry.tsx
@@ -1,14 +1,15 @@
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
+import { useAnastasisContext } from "../../context/anastasis";
import { AnastasisClientFrame, LabeledInput } from "./index";
import { SolveEntryProps } from "./SolveScreen";
-export function SolveQuestionEntry(props: SolveEntryProps): VNode {
+export function SolveQuestionEntry({ challenge, feedback }: SolveEntryProps): VNode {
const [answer, setAnswer] = useState("");
- const { reducer, challenge, feedback } = props;
- const next = (): void => reducer.transition("solve_challenge", {
- answer,
- });
+ const reducer = useAnastasisContext()
+ const next = (): void => {
+ if (reducer) reducer.transition("solve_challenge", { answer })
+ };
return (
<AnastasisClientFrame
title="Recovery: Solve challenge"
diff --git a/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx
new file mode 100644
index 000000000..69af9be42
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx
@@ -0,0 +1,121 @@
+/* eslint-disable @typescript-eslint/camelcase */
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { ReducerState } from 'anastasis-core';
+import { createExample, reducerStatesExample } from '../../utils';
+import { SolveScreen as TestedComponent } from './SolveScreen';
+
+
+export default {
+ title: 'Pages/SolveScreen',
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: 'onUpdate' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+export const NoInformation = createExample(TestedComponent, reducerStatesExample.challengeSolving);
+
+export const NotSupportedChallenge = createExample(TestedComponent, {
+ ...reducerStatesExample.challengeSolving,
+ recovery_information: {
+ challenges: [{
+ cost: 'USD:1',
+ instructions: 'follow htis instructions',
+ type: 'chall-type',
+ uuid: 'ASDASDSAD!1'
+ }],
+ policies: [],
+ },
+ selected_challenge_uuid: 'ASDASDSAD!1'
+} as ReducerState);
+
+export const MismatchedChallengeId = createExample(TestedComponent, {
+ ...reducerStatesExample.challengeSolving,
+ recovery_information: {
+ challenges: [{
+ cost: 'USD:1',
+ instructions: 'follow htis instructions',
+ type: 'chall-type',
+ uuid: 'ASDASDSAD!1'
+ }],
+ policies: [],
+ },
+ selected_challenge_uuid: 'no-no-no'
+} as ReducerState);
+
+export const SmsChallenge = createExample(TestedComponent, {
+ ...reducerStatesExample.challengeSolving,
+ recovery_information: {
+ challenges: [{
+ cost: 'USD:1',
+ instructions: 'follow htis instructions',
+ type: 'sms',
+ uuid: 'ASDASDSAD!1'
+ }],
+ policies: [],
+ },
+ selected_challenge_uuid: 'ASDASDSAD!1'
+} as ReducerState);
+
+export const QuestionChallenge = createExample(TestedComponent, {
+ ...reducerStatesExample.challengeSolving,
+ recovery_information: {
+ challenges: [{
+ cost: 'USD:1',
+ instructions: 'follow htis instructions',
+ type: 'question',
+ uuid: 'ASDASDSAD!1'
+ }],
+ policies: [],
+ },
+ selected_challenge_uuid: 'ASDASDSAD!1'
+} as ReducerState);
+
+export const EmailChallenge = createExample(TestedComponent, {
+ ...reducerStatesExample.challengeSolving,
+ recovery_information: {
+ challenges: [{
+ cost: 'USD:1',
+ instructions: 'follow htis instructions',
+ type: 'email',
+ uuid: 'ASDASDSAD!1'
+ }],
+ policies: [],
+ },
+ selected_challenge_uuid: 'ASDASDSAD!1'
+} as ReducerState);
+
+export const PostChallenge = createExample(TestedComponent, {
+ ...reducerStatesExample.challengeSolving,
+ recovery_information: {
+ challenges: [{
+ cost: 'USD:1',
+ instructions: 'follow htis instructions',
+ type: 'post',
+ uuid: 'ASDASDSAD!1'
+ }],
+ policies: [],
+ },
+ selected_challenge_uuid: 'ASDASDSAD!1'
+} as ReducerState);
diff --git a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx
index 357a7c2d3..05ae50b48 100644
--- a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx
@@ -1,17 +1,31 @@
import { h, VNode } from "preact";
-import { AnastasisReducerApi } from "../../hooks/use-anastasis-reducer";
+import { ChallengeFeedback, ChallengeInfo } from "../../../../anastasis-core/lib";
+import { useAnastasisContext } from "../../context/anastasis";
import { SolveEmailEntry } from "./SolveEmailEntry";
import { SolvePostEntry } from "./SolvePostEntry";
import { SolveQuestionEntry } from "./SolveQuestionEntry";
import { SolveSmsEntry } from "./SolveSmsEntry";
import { SolveUnsupportedEntry } from "./SolveUnsupportedEntry";
-import { RecoveryReducerProps } from "./index";
-import { ChallengeInfo, ChallengeFeedback } from "../../../../anastasis-core/lib";
-export function SolveScreen(props: RecoveryReducerProps): VNode {
- const chArr = props.recoveryState.recovery_information!.challenges;
- const challengeFeedback = props.recoveryState.challenge_feedback ?? {};
- const selectedUuid = props.recoveryState.selected_challenge_uuid!;
+export function SolveScreen(): VNode {
+ const reducer = useAnastasisContext()
+
+ if (!reducer) {
+ return <div>no reducer in context</div>
+ }
+ if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) {
+ return <div>invalid state</div>
+ }
+
+ if (!reducer.currentReducerState.recovery_information) {
+ return <div>no recovery information found</div>
+ }
+ if (!reducer.currentReducerState.selected_challenge_uuid) {
+ return <div>no selected uuid</div>
+ }
+ const chArr = reducer.currentReducerState.recovery_information.challenges;
+ const challengeFeedback = reducer.currentReducerState.challenge_feedback ?? {};
+ const selectedUuid = reducer.currentReducerState.selected_challenge_uuid;
const challenges: {
[uuid: string]: ChallengeInfo;
} = {};
@@ -25,17 +39,15 @@ export function SolveScreen(props: RecoveryReducerProps): VNode {
email: SolveEmailEntry,
post: SolvePostEntry,
};
- const SolveDialog = dialogMap[selectedChallenge.type] ?? SolveUnsupportedEntry;
+ const SolveDialog = dialogMap[selectedChallenge?.type] ?? SolveUnsupportedEntry;
return (
<SolveDialog
challenge={selectedChallenge}
- reducer={props.reducer}
feedback={challengeFeedback[selectedUuid]} />
);
}
export interface SolveEntryProps {
- reducer: AnastasisReducerApi;
challenge: ChallengeInfo;
feedback?: ChallengeFeedback;
}
diff --git a/packages/anastasis-webui/src/pages/home/SolveSmsEntry.tsx b/packages/anastasis-webui/src/pages/home/SolveSmsEntry.tsx
index d0cd41332..163e0d1f3 100644
--- a/packages/anastasis-webui/src/pages/home/SolveSmsEntry.tsx
+++ b/packages/anastasis-webui/src/pages/home/SolveSmsEntry.tsx
@@ -1,14 +1,17 @@
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
+import { useAnastasisContext } from "../../context/anastasis";
import { AnastasisClientFrame, LabeledInput } from "./index";
import { SolveEntryProps } from "./SolveScreen";
-export function SolveSmsEntry(props: SolveEntryProps): VNode {
+export function SolveSmsEntry({ challenge, feedback }: SolveEntryProps): VNode {
const [answer, setAnswer] = useState("");
- const { reducer, challenge, feedback } = props;
- const next = (): void => reducer.transition("solve_challenge", {
- answer,
- });
+ const reducer = useAnastasisContext()
+ const next = (): void => {
+ if (reducer) reducer.transition("solve_challenge", {
+ answer,
+ })
+ };
return (
<AnastasisClientFrame
title="Recovery: Solve challenge"
diff --git a/packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx
new file mode 100644
index 000000000..ad84cd8f2
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx
@@ -0,0 +1,35 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { createExample, reducerStatesExample } from '../../utils';
+import { StartScreen as TestedComponent } from './StartScreen';
+
+
+export default {
+ title: 'Pages/StartScreen',
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: 'onUpdate' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+export const InitialState = createExample(TestedComponent, reducerStatesExample.initial); \ No newline at end of file
diff --git a/packages/anastasis-webui/src/pages/home/StartScreen.tsx b/packages/anastasis-webui/src/pages/home/StartScreen.tsx
index 38124887c..6625ec5b8 100644
--- a/packages/anastasis-webui/src/pages/home/StartScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/StartScreen.tsx
@@ -1,14 +1,34 @@
+
import { h, VNode } from "preact";
-import { AnastasisReducerApi } from "../../hooks/use-anastasis-reducer";
+import { useAnastasisContext } from "../../context/anastasis";
import { AnastasisClientFrame } from "./index";
-export function StartScreen(props: { reducer: AnastasisReducerApi; }): VNode {
+export function StartScreen(): VNode {
+ const reducer = useAnastasisContext()
+ if (!reducer) {
+ return <div>no reducer in context</div>
+ }
return (
<AnastasisClientFrame hideNav title="Home">
- <button autoFocus onClick={() => props.reducer.startBackup()}>
- Backup
- </button>
- <button onClick={() => props.reducer.startRecover()}>Recover</button>
+ <div>
+ <section class="section is-main-section">
+ <div class="columns">
+ <div class="column" />
+ <div class="column is-four-fifths">
+
+ <div class="buttons is-right">
+ <button class="button is-success" autoFocus onClick={() => reducer.startBackup()}>
+ Backup
+ </button>
+
+ <button class="button is-info" onClick={() => reducer.startRecover()}>Recover</button>
+ </div>
+
+ </div>
+ <div class="column" />
+ </div>
+ </section>
+ </div>
</AnastasisClientFrame>
);
}
diff --git a/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx
new file mode 100644
index 000000000..e2f3d521e
--- /dev/null
+++ b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx
@@ -0,0 +1,40 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+*
+* @author Sebastian Javier Marchano (sebasjm)
+*/
+
+import { ReducerState } from 'anastasis-core';
+import { createExample, reducerStatesExample } from '../../utils';
+import { TruthsPayingScreen as TestedComponent } from './TruthsPayingScreen';
+
+
+export default {
+ title: 'Pages/TruthsPayingScreen',
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: 'onUpdate' },
+ onBack: { action: 'onBack' },
+ },
+};
+
+export const Example = createExample(TestedComponent, reducerStatesExample.truthsPaying);
+export const WithPaytoList = createExample(TestedComponent, {
+ ...reducerStatesExample.truthsPaying,
+ payments: ['payto://x-taler-bank/bank/account']
+} as ReducerState);
diff --git a/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx
index 5b8a835b8..319f590a0 100644
--- a/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx
+++ b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx
@@ -1,8 +1,16 @@
import { h, VNode } from "preact";
-import { BackupReducerProps, AnastasisClientFrame } from "./index";
+import { useAnastasisContext } from "../../context/anastasis";
+import { AnastasisClientFrame } from "./index";
-export function TruthsPayingScreen(props: BackupReducerProps): VNode {
- const payments = props.backupState.payments ?? [];
+export function TruthsPayingScreen(): VNode {
+ const reducer = useAnastasisContext()
+ if (!reducer) {
+ return <div>no reducer in context</div>
+ }
+ if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) {
+ return <div>invalid state</div>
+ }
+ const payments = reducer.currentReducerState.payments ?? [];
return (
<AnastasisClientFrame
hideNext
@@ -17,7 +25,7 @@ export function TruthsPayingScreen(props: BackupReducerProps): VNode {
return <li key={i}>{x}</li>;
})}
</ul>
- <button onClick={() => props.reducer.transition("pay", {})}>
+ <button onClick={() => reducer.transition("pay", {})}>
Check payment status now
</button>
</AnastasisClientFrame>
diff --git a/packages/anastasis-webui/src/pages/home/index.tsx b/packages/anastasis-webui/src/pages/home/index.tsx
index 5001d1ee4..4cec47ec8 100644
--- a/packages/anastasis-webui/src/pages/home/index.tsx
+++ b/packages/anastasis-webui/src/pages/home/index.tsx
@@ -1,28 +1,25 @@
import {
- Component,
- ComponentChildren,
- createContext,
- Fragment,
+ BackupStates,
+ RecoveryStates,
+ ReducerStateBackup,
+ ReducerStateRecovery
+} from "anastasis-core";
+import {
+ ComponentChildren, Fragment,
FunctionalComponent,
h,
- VNode,
+ VNode
} from "preact";
import {
- useContext,
useErrorBoundary,
useLayoutEffect,
- useRef,
+ useRef
} from "preact/hooks";
import { Menu } from "../../components/menu";
-import {
- BackupStates,
- RecoveryStates,
- ReducerStateBackup,
- ReducerStateRecovery,
-} from "anastasis-core";
+import { AnastasisProvider, useAnastasisContext } from "../../context/anastasis";
import {
AnastasisReducerApi,
- useAnastasisReducer,
+ useAnastasisReducer
} from "../../hooks/use-anastasis-reducer";
import { AttributeEntryScreen } from "./AttributeEntryScreen";
import { AuthenticationEditorScreen } from "./AuthenticationEditorScreen";
@@ -38,19 +35,11 @@ import { SecretSelectionScreen } from "./SecretSelectionScreen";
import { SolveScreen } from "./SolveScreen";
import { StartScreen } from "./StartScreen";
import { TruthsPayingScreen } from "./TruthsPayingScreen";
-import "./../home/style";
-
-const WithReducer = createContext<AnastasisReducerApi | undefined>(undefined);
function isBackup(reducer: AnastasisReducerApi): boolean {
return !!reducer.currentReducerState?.backup_state;
}
-export interface CommonReducerProps {
- reducer: AnastasisReducerApi;
- reducerState: ReducerStateBackup | ReducerStateRecovery;
-}
-
export function withProcessLabel(
reducer: AnastasisReducerApi,
text: string,
@@ -61,16 +50,6 @@ export function withProcessLabel(
return `Recovery: ${text}`;
}
-export interface BackupReducerProps {
- reducer: AnastasisReducerApi;
- backupState: ReducerStateBackup;
-}
-
-export interface RecoveryReducerProps {
- reducer: AnastasisReducerApi;
- recoveryState: ReducerStateRecovery;
-}
-
interface AnastasisClientFrameProps {
onNext?(): void;
title: string;
@@ -88,7 +67,7 @@ interface AnastasisClientFrameProps {
function ErrorBoundary(props: {
reducer: AnastasisReducerApi;
children: ComponentChildren;
-}) {
+}): VNode {
const [error, resetError] = useErrorBoundary((error) =>
console.log("got error", error),
);
@@ -113,7 +92,7 @@ function ErrorBoundary(props: {
}
export function AnastasisClientFrame(props: AnastasisClientFrameProps): VNode {
- const reducer = useContext(WithReducer);
+ const reducer = useAnastasisContext();
if (!reducer) {
return <p>Fatal: Reducer must be in context.</p>;
}
@@ -135,9 +114,8 @@ export function AnastasisClientFrame(props: AnastasisClientFrameProps): VNode {
<Menu title="Anastasis" />
<div>
<div class="home" onKeyPress={(e) => handleKeyPress(e)}>
- <button onClick={() => reducer.reset()}>Reset session</button>
<h1>{props.title}</h1>
- <ErrorBanner reducer={reducer} />
+ <ErrorBanner />
{props.children}
{!props.hideNav ? (
<div>
@@ -154,96 +132,94 @@ export function AnastasisClientFrame(props: AnastasisClientFrameProps): VNode {
const AnastasisClient: FunctionalComponent = () => {
const reducer = useAnastasisReducer();
return (
- <WithReducer.Provider value={reducer}>
+ <AnastasisProvider value={reducer}>
<ErrorBoundary reducer={reducer}>
<AnastasisClientImpl />
</ErrorBoundary>
- </WithReducer.Provider>
+ </AnastasisProvider>
);
};
const AnastasisClientImpl: FunctionalComponent = () => {
- const reducer = useContext(WithReducer)!;
- const reducerState = reducer.currentReducerState;
- if (!reducerState) {
- return <StartScreen reducer={reducer} />;
+ const reducer = useAnastasisContext()
+ if (!reducer) {
+ return <p>Fatal: Reducer must be in context.</p>;
+ }
+ const state = reducer.currentReducerState;
+ if (!state) {
+ return <StartScreen />;
}
console.log("state", reducer.currentReducerState);
if (
- reducerState.backup_state === BackupStates.ContinentSelecting ||
- reducerState.recovery_state === RecoveryStates.ContinentSelecting
+ state.backup_state === BackupStates.ContinentSelecting ||
+ state.recovery_state === RecoveryStates.ContinentSelecting
) {
return (
- <ContinentSelectionScreen reducer={reducer} reducerState={reducerState} />
+ <ContinentSelectionScreen />
);
}
if (
- reducerState.backup_state === BackupStates.CountrySelecting ||
- reducerState.recovery_state === RecoveryStates.CountrySelecting
+ state.backup_state === BackupStates.CountrySelecting ||
+ state.recovery_state === RecoveryStates.CountrySelecting
) {
return (
- <CountrySelectionScreen reducer={reducer} reducerState={reducerState} />
+ <CountrySelectionScreen />
);
}
if (
- reducerState.backup_state === BackupStates.UserAttributesCollecting ||
- reducerState.recovery_state === RecoveryStates.UserAttributesCollecting
+ state.backup_state === BackupStates.UserAttributesCollecting ||
+ state.recovery_state === RecoveryStates.UserAttributesCollecting
) {
return (
- <AttributeEntryScreen reducer={reducer} reducerState={reducerState} />
+ <AttributeEntryScreen />
);
}
- if (reducerState.backup_state === BackupStates.AuthenticationsEditing) {
+ if (state.backup_state === BackupStates.AuthenticationsEditing) {
return (
- <AuthenticationEditorScreen
- backupState={reducerState}
- reducer={reducer}
- />
+ <AuthenticationEditorScreen />
);
}
- if (reducerState.backup_state === BackupStates.PoliciesReviewing) {
+ if (state.backup_state === BackupStates.PoliciesReviewing) {
return (
- <ReviewPoliciesScreen reducer={reducer} backupState={reducerState} />
+ <ReviewPoliciesScreen />
);
}
- if (reducerState.backup_state === BackupStates.SecretEditing) {
- return <SecretEditorScreen reducer={reducer} backupState={reducerState} />;
+ if (state.backup_state === BackupStates.SecretEditing) {
+ return <SecretEditorScreen />;
}
- if (reducerState.backup_state === BackupStates.BackupFinished) {
- const backupState: ReducerStateBackup = reducerState;
- return <BackupFinishedScreen reducer={reducer} backupState={backupState} />;
+ if (state.backup_state === BackupStates.BackupFinished) {
+ return <BackupFinishedScreen />;
}
- if (reducerState.backup_state === BackupStates.TruthsPaying) {
- return <TruthsPayingScreen reducer={reducer} backupState={reducerState} />;
+ if (state.backup_state === BackupStates.TruthsPaying) {
+ return <TruthsPayingScreen />;
}
- if (reducerState.backup_state === BackupStates.PoliciesPaying) {
- const backupState: ReducerStateBackup = reducerState;
- return <PoliciesPayingScreen reducer={reducer} backupState={backupState} />;
+ if (state.backup_state === BackupStates.PoliciesPaying) {
+ return <PoliciesPayingScreen />;
}
- if (reducerState.recovery_state === RecoveryStates.SecretSelecting) {
+ if (state.recovery_state === RecoveryStates.SecretSelecting) {
return (
- <SecretSelectionScreen reducer={reducer} recoveryState={reducerState} />
+ <SecretSelectionScreen />
);
}
- if (reducerState.recovery_state === RecoveryStates.ChallengeSelecting) {
+ if (state.recovery_state === RecoveryStates.ChallengeSelecting) {
return (
- <ChallengeOverviewScreen reducer={reducer} recoveryState={reducerState} />
+ <ChallengeOverviewScreen />
);
}
- if (reducerState.recovery_state === RecoveryStates.ChallengeSolving) {
- return <SolveScreen reducer={reducer} recoveryState={reducerState} />;
+ if (state.recovery_state === RecoveryStates.ChallengeSolving) {
+ return <SolveScreen />;
}
- if (reducerState.recovery_state === RecoveryStates.RecoveryFinished) {
+ if (state.recovery_state === RecoveryStates.RecoveryFinished) {
return (
- <RecoveryFinishedScreen reducer={reducer} recoveryState={reducerState} />
+ <RecoveryFinishedScreen />
);
}
@@ -251,7 +227,9 @@ const AnastasisClientImpl: FunctionalComponent = () => {
return (
<AnastasisClientFrame hideNav title="Bug">
<p>Bug: Unknown state.</p>
- <button onClick={() => reducer.reset()}>Reset</button>
+ <div class="buttons is-right">
+ <button class="button" onClick={() => reducer.reset()}>Reset</button>
+ </div>
</AnastasisClientFrame>
);
};
@@ -282,26 +260,20 @@ export function LabeledInput(props: LabeledInputProps): VNode {
);
}
-interface ErrorBannerProps {
- reducer: AnastasisReducerApi;
-}
-
/**
- * Show a dismissable error banner if there is a current error.
+ * Show a dismissible error banner if there is a current error.
*/
-function ErrorBanner(props: ErrorBannerProps): VNode | null {
- const currentError = props.reducer.currentError;
- if (currentError) {
- return (
- <div id="error">
- <p>Error: {JSON.stringify(currentError)}</p>
- <button onClick={() => props.reducer.dismissError()}>
- Dismiss Error
- </button>
- </div>
- );
- }
- return null;
+function ErrorBanner(): VNode | null {
+ const reducer = useAnastasisContext();
+ if (!reducer || !reducer.currentError) return null;
+ return (
+ <div id="error">
+ <p>Error: {JSON.stringify(reducer.currentError)}</p>
+ <button onClick={() => reducer.dismissError()}>
+ Dismiss Error
+ </button>
+ </div>
+ );
}
export default AnastasisClient;
diff --git a/packages/anastasis-webui/src/utils/index.tsx b/packages/anastasis-webui/src/utils/index.tsx
new file mode 100644
index 000000000..d1d861469
--- /dev/null
+++ b/packages/anastasis-webui/src/utils/index.tsx
@@ -0,0 +1,161 @@
+/* eslint-disable @typescript-eslint/camelcase */
+import { BackupStates, RecoveryStates, ReducerState } from 'anastasis-core';
+import { FunctionalComponent, h, VNode } from 'preact';
+import { AnastasisProvider } from '../context/anastasis';
+
+export function createExample<Props>(Component: FunctionalComponent<Props>, currentReducerState?: ReducerState, props?: Partial<Props>): { (args: Props): VNode } {
+ const r = (args: Props): VNode => {
+ return <AnastasisProvider value={{
+ currentReducerState,
+ currentError: undefined,
+ back: () => { null },
+ dismissError: () => { null },
+ reset: () => { null },
+ runTransaction: () => { null },
+ startBackup: () => { null },
+ startRecover: () => { null },
+ transition: () => { null },
+ }}>
+ <Component {...args} />
+ </AnastasisProvider>
+ }
+ r.args = props
+ return r
+}
+
+const base = {
+ continents: [
+ {
+ name: "Europe"
+ },
+ {
+ name: "India"
+ },
+ {
+ name: "Asia"
+ },
+ {
+ name: "North America"
+ },
+ {
+ name: "Testcontinent"
+ }
+ ],
+ countries: [
+ {
+ code: "xx",
+ name: "Testland",
+ continent: "Testcontinent",
+ continent_i18n: {
+ de_DE: "Testkontinent"
+ },
+ name_i18n: {
+ de_DE: "Testlandt",
+ de_CH: "Testlandi",
+ fr_FR: "Testpais",
+ en_UK: "Testland"
+ },
+ currency: "TESTKUDOS",
+ call_code: "+00"
+ },
+ {
+ code: "xy",
+ name: "Demoland",
+ continent: "Testcontinent",
+ continent_i18n: {
+ de_DE: "Testkontinent"
+ },
+ name_i18n: {
+ de_DE: "Demolandt",
+ de_CH: "Demolandi",
+ fr_FR: "Demopais",
+ en_UK: "Demoland"
+ },
+ currency: "KUDOS",
+ call_code: "+01"
+ }
+ ],
+ authentication_providers: {
+ "http://localhost:8086/": {
+ http_status: 200,
+ annual_fee: "COL:0",
+ business_name: "ana",
+ currency: "COL",
+ liability_limit: "COL:10",
+ methods: [
+ {
+ type: "question",
+ usage_fee: "COL:0"
+ }
+ ],
+ salt: "WBMDD76BR1E90YQ5AHBMKPH7GW",
+ storage_limit_in_megabytes: 16,
+ truth_upload_fee: "COL:0"
+ },
+ "http://localhost:8087/": {
+ code: 8414,
+ hint: "request to provider failed"
+ },
+ "http://localhost:8088/": {
+ code: 8414,
+ hint: "request to provider failed"
+ },
+ "http://localhost:8089/": {
+ code: 8414,
+ hint: "request to provider failed"
+ }
+ },
+ // expiration: {
+ // d_ms: 1792525051855 // check t_ms
+ // },
+} as Partial<ReducerState>
+
+export const reducerStatesExample = {
+ initial: undefined,
+ recoverySelectCountry: {...base,
+ recovery_state: RecoveryStates.CountrySelecting
+ } as ReducerState,
+ backupSelectCountry: {...base,
+ backup_state: BackupStates.CountrySelecting
+ } as ReducerState,
+ recoverySelectContinent: {...base,
+ recovery_state: RecoveryStates.ContinentSelecting,
+ } as ReducerState,
+ backupSelectContinent: {...base,
+ backup_state: BackupStates.ContinentSelecting,
+ } as ReducerState,
+ secretSelection: {...base,
+ recovery_state: RecoveryStates.SecretSelecting,
+ } as ReducerState,
+ recoveryFinished: {...base,
+ recovery_state: RecoveryStates.RecoveryFinished,
+ } as ReducerState,
+ challengeSelecting: {...base,
+ recovery_state: RecoveryStates.ChallengeSelecting,
+ } as ReducerState,
+ challengeSolving: {...base,
+ recovery_state: RecoveryStates.ChallengeSolving,
+ } as ReducerState,
+ secretEdition: {...base,
+ backup_state: BackupStates.SecretEditing,
+ } as ReducerState,
+ policyReview: {...base,
+ backup_state: BackupStates.PoliciesReviewing,
+ } as ReducerState,
+ policyPay: {...base,
+ backup_state: BackupStates.PoliciesPaying,
+ } as ReducerState,
+ backupFinished: {...base,
+ backup_state: BackupStates.BackupFinished,
+ } as ReducerState,
+ authEditing: {...base,
+ backup_state: BackupStates.AuthenticationsEditing
+ } as ReducerState,
+ attributeEditing: {...base,
+ backup_state: BackupStates.UserAttributesCollecting
+ } as ReducerState,
+ truthsPaying: {...base,
+ backup_state: BackupStates.TruthsPaying
+ } as ReducerState,
+
+}