diff --git a/packages/anastasis-webui/src/components/fields/DateInput.tsx b/packages/anastasis-webui/src/components/fields/DateInput.tsx index c45acc6d2..e1c354f7b 100644 --- a/packages/anastasis-webui/src/components/fields/DateInput.tsx +++ b/packages/anastasis-webui/src/components/fields/DateInput.tsx @@ -8,6 +8,7 @@ export interface DateInputProps { grabFocus?: boolean; tooltip?: string; error?: string; + years?: Array; bind: [string, (x: string) => void]; } @@ -19,7 +20,7 @@ export function DateInput(props: DateInputProps): VNode { } }, [props.grabFocus]); const [opened, setOpened2] = useState(false) - function setOpened(v: boolean) { + function setOpened(v: boolean): void { console.log('dale', v) setOpened2(v) } @@ -50,6 +51,7 @@ export function DateInput(props: DateInputProps): VNode { {showError &&

{props.error}

} setOpened(false)} dateReceiver={(d) => { setDirty(true) diff --git a/packages/anastasis-webui/src/components/fields/NumberInput.tsx b/packages/anastasis-webui/src/components/fields/NumberInput.tsx new file mode 100644 index 000000000..af9bbe66b --- /dev/null +++ b/packages/anastasis-webui/src/components/fields/NumberInput.tsx @@ -0,0 +1,41 @@ +import { h, VNode } from "preact"; +import { useLayoutEffect, useRef, useState } from "preact/hooks"; + +export interface TextInputProps { + label: string; + grabFocus?: boolean; + error?: string; + tooltip?: string; + bind: [string, (x: string) => void]; +} + +export function NumberInput(props: TextInputProps): VNode { + const inputRef = useRef(null); + useLayoutEffect(() => { + if (props.grabFocus) { + inputRef.current?.focus(); + } + }, [props.grabFocus]); + const value = props.bind[0]; + const [dirty, setDirty] = useState(false) + const showError = dirty && props.error + return (
+ +
+ {setDirty(true); props.bind[1]((e.target as HTMLInputElement).value)}} + ref={inputRef} + style={{ display: "block" }} /> +
+ {showError &&

{props.error}

} +
+ ); +} diff --git a/packages/anastasis-webui/src/components/fields/LabeledInput.tsx b/packages/anastasis-webui/src/components/fields/TextInput.tsx similarity index 91% rename from packages/anastasis-webui/src/components/fields/LabeledInput.tsx rename to packages/anastasis-webui/src/components/fields/TextInput.tsx index 96d634a4f..fa6fd9792 100644 --- a/packages/anastasis-webui/src/components/fields/LabeledInput.tsx +++ b/packages/anastasis-webui/src/components/fields/TextInput.tsx @@ -1,7 +1,7 @@ import { h, VNode } from "preact"; import { useLayoutEffect, useRef, useState } from "preact/hooks"; -export interface LabeledInputProps { +export interface TextInputProps { label: string; grabFocus?: boolean; error?: string; @@ -9,7 +9,7 @@ export interface LabeledInputProps { bind: [string, (x: string) => void]; } -export function LabeledInput(props: LabeledInputProps): VNode { +export function TextInput(props: TextInputProps): VNode { const inputRef = useRef(null); useLayoutEffect(() => { if (props.grabFocus) { diff --git a/packages/anastasis-webui/src/components/menu/SideBar.tsx b/packages/anastasis-webui/src/components/menu/SideBar.tsx index 12223d473..87e771009 100644 --- a/packages/anastasis-webui/src/components/menu/SideBar.tsx +++ b/packages/anastasis-webui/src/components/menu/SideBar.tsx @@ -64,9 +64,8 @@ export function Sidebar({ mobile }: Props): VNode { } {reducer.currentReducerState && reducer.currentReducerState.backup_state ? -
  • +
  • Location & Currency
    @@ -79,73 +78,65 @@ export function Sidebar({ mobile }: Props): VNode {
  • - Auth methods + Authorization methods
  • - PoliciesReviewing + Policies reviewing
  • - SecretEditing + Secret input
  • - PoliciesPaying + Payment (optional)
  • - BackupFinished + Backup completed
  • - TruthsPaying + Truth Paying
  • : (reducer.currentReducerState && reducer.currentReducerState?.recovery_state && -
  • +
  • - ContinentSelecting -
    -
  • -
  • -
    - CountrySelecting + Location & Currency
  • - UserAttributesCollecting + Personal information
  • - SecretSelecting + Secret selection
  • -
  • +
  • - ChallengeSelecting -
    -
  • -
  • -
    - ChallengeSolving + Solve Challenges
  • - RecoveryFinished + Secret recovered
  • )} diff --git a/packages/anastasis-webui/src/components/picker/DatePicker.tsx b/packages/anastasis-webui/src/components/picker/DatePicker.tsx index e51b3db68..5b33fa8be 100644 --- a/packages/anastasis-webui/src/components/picker/DatePicker.tsx +++ b/packages/anastasis-webui/src/components/picker/DatePicker.tsx @@ -24,6 +24,7 @@ import { h, Component } from "preact"; interface Props { closeFunction?: () => void; dateReceiver?: (d: Date) => void; + years?: Array; opened?: boolean; } interface State { @@ -207,9 +208,9 @@ export class DatePicker extends Component { } componentDidUpdate() { - if (this.state.selectYearMode) { - document.getElementsByClassName('selected')[0].scrollIntoView(); // works in every browser incl. IE, replace with scrollIntoViewIfNeeded when browsers support it - } + // if (this.state.selectYearMode) { + // document.getElementsByClassName('selected')[0].scrollIntoView(); // works in every browser incl. IE, replace with scrollIntoViewIfNeeded when browsers support it + // } } constructor() { @@ -296,8 +297,7 @@ export class DatePicker extends Component { } {selectYearMode &&
    - - {yearArr.map(year => ( + {(this.props.years || yearArr).map(year => ( {year} diff --git a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx index d9be48fb4..32d7817e3 100644 --- a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx @@ -40,21 +40,21 @@ export default { export const Backup = createExample(TestedComponent, { ...reducerStatesExample.backupAttributeEditing, required_attributes: [{ - name: 'first', + name: 'first name', label: 'first', - type: 'type', + type: 'string', uuid: 'asdasdsa1', widget: 'wid', }, { - name: 'pepe', + name: 'last name', label: 'second', - type: 'type', + type: 'string', uuid: 'asdasdsa2', widget: 'wid', }, { - name: 'pepe2', + name: 'date', label: 'third', - type: 'type', + type: 'date', uuid: 'asdasdsa3', widget: 'calendar', }] @@ -65,19 +65,19 @@ export const Recovery = createExample(TestedComponent, { required_attributes: [{ name: 'first', label: 'first', - type: 'type', + type: 'string', uuid: 'asdasdsa1', widget: 'wid', }, { name: 'pepe', label: 'second', - type: 'type', + type: 'string', uuid: 'asdasdsa2', widget: 'wid', }, { name: 'pepe2', label: 'third', - type: 'type', + type: 'date', uuid: 'asdasdsa3', widget: 'calendar', }] @@ -110,12 +110,20 @@ const allWidgets = [ "anastasis_gtk_xx_square", ] +function typeForWidget(name: string): string { + if (["anastasis_gtk_xx_prime", + "anastasis_gtk_xx_square", + ].includes(name)) return "number"; + if (["anastasis_gtk_ia_birthdate"].includes(name)) return "date" + return "string"; +} + export const WithAllPosibleWidget = createExample(TestedComponent, { ...reducerStatesExample.backupAttributeEditing, required_attributes: allWidgets.map(w => ({ name: w, label: `widget: ${w}`, - type: 'type', + type: typeForWidget(w), uuid: `uuid-${w}`, widget: w })) diff --git a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx index 3b39cf9c4..f74dcefba 100644 --- a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx @@ -4,8 +4,9 @@ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame, withProcessLabel } from "./index"; -import { LabeledInput } from "../../components/fields/LabeledInput"; +import { TextInput } from "../../components/fields/TextInput"; import { DateInput } from "../../components/fields/DateInput"; +import { NumberInput } from "../../components/fields/NumberInput"; export function AttributeEntryScreen(): VNode { const reducer = useAnastasisContext() @@ -65,6 +66,7 @@ export function AttributeEntryScreen(): VNode {
    +

    This personal information will help to locate your secret in the first place

    This stay private

    The information you have entered here:

    @@ -92,20 +94,33 @@ interface AttributeEntryFieldProps { spec: UserAttributeSpec; isValid: () => string | undefined; } - +const possibleBirthdayYear: Array = [] +for (let i = 0; i < 100; i++ ) { + possibleBirthdayYear.push(2020 - i) +} function AttributeEntryField(props: AttributeEntryFieldProps): VNode { const errorMessage = props.isValid() return (
    - {props.spec.type === 'date' ? + {props.spec.type === 'date' && } + {props.spec.type === 'number' && + : - + } + {props.spec.type === 'string' && +
    - diff --git a/packages/anastasis-webui/src/pages/home/AuthMethodPostSetup.tsx b/packages/anastasis-webui/src/pages/home/AuthMethodPostSetup.tsx index 1c2a9a92e..c4ddeff91 100644 --- a/packages/anastasis-webui/src/pages/home/AuthMethodPostSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/AuthMethodPostSetup.tsx @@ -6,7 +6,7 @@ import { import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { AuthMethodSetupProps } from "./AuthenticationEditorScreen"; -import { LabeledInput } from "../../components/fields/LabeledInput"; +import { TextInput } from "../../components/fields/TextInput"; export function AuthMethodPostSetup(props: AuthMethodSetupProps): VNode { const [fullName, setFullName] = useState(""); @@ -42,22 +42,22 @@ export function AuthMethodPostSetup(props: AuthMethodSetupProps): VNode { code that you will receive in a letter to that address.

    -
    - +
    - +
    - +
    - +
    diff --git a/packages/anastasis-webui/src/pages/home/AuthMethodQuestionSetup.tsx b/packages/anastasis-webui/src/pages/home/AuthMethodQuestionSetup.tsx index c2bd24ef9..f1bab94ab 100644 --- a/packages/anastasis-webui/src/pages/home/AuthMethodQuestionSetup.tsx +++ b/packages/anastasis-webui/src/pages/home/AuthMethodQuestionSetup.tsx @@ -7,7 +7,7 @@ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; import { AuthMethodSetupProps } from "./AuthenticationEditorScreen"; import { AnastasisClientFrame } from "./index"; -import { LabeledInput } from "../../components/fields/LabeledInput"; +import { TextInput } from "../../components/fields/TextInput"; export function AuthMethodQuestionSetup(props: AuthMethodSetupProps): VNode { const [questionText, setQuestionText] = useState(""); @@ -29,13 +29,13 @@ export function AuthMethodQuestionSetup(props: AuthMethodSetupProps): VNode { here.

    -
    - +
    diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx index def44c5a6..758963574 100644 --- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx @@ -37,7 +37,7 @@ export default { }, }; -export const OneChallenge = createExample(TestedComponent, { +export const OneUnsolvedPolicy = createExample(TestedComponent, { ...reducerStatesExample.challengeSelecting, recovery_information: { policies: [[{ uuid: '1' }]], @@ -50,7 +50,7 @@ export const OneChallenge = createExample(TestedComponent, { }, } as ReducerState); -export const MoreChallenges = createExample(TestedComponent, { +export const SomePoliciesOneSolved = createExample(TestedComponent, { ...reducerStatesExample.challengeSelecting, recovery_information: { policies: [[{ uuid: '1' }, { uuid: '2' }], [{ uuid: 'uuid-3' }]], @@ -75,13 +75,13 @@ export const MoreChallenges = createExample(TestedComponent, { 'uuid-3': { state: 'solved' } - } + }, } as ReducerState); export const OneBadConfiguredPolicy = createExample(TestedComponent, { ...reducerStatesExample.challengeSelecting, recovery_information: { - policies: [[{ uuid: '2' }]], + policies: [[{ uuid: '1' }, { uuid: '2' }]], challenges: [{ cost: 'USD:1', instructions: 'just go for it', @@ -91,4 +91,130 @@ export const OneBadConfiguredPolicy = createExample(TestedComponent, { }, } as ReducerState); +export const OnePolicyWithAllTheChallenges = createExample(TestedComponent, { + ...reducerStatesExample.challengeSelecting, + recovery_information: { + policies: [[ + { uuid: '1' }, + { uuid: '2' }, + { uuid: '3' }, + { uuid: '4' }, + { uuid: '5' }, + { uuid: '6' }, + ]], + challenges: [{ + cost: 'USD:1', + instructions: 'answer the a question correctly', + type: 'question', + uuid: '1', + },{ + cost: 'USD:1', + instructions: 'enter a text received by a sms', + type: 'sms', + uuid: '2', + },{ + cost: 'USD:1', + instructions: 'enter a text received by a email', + type: 'email', + uuid: '3', + },{ + cost: 'USD:1', + instructions: 'enter a code based on a time-based one-time password', + type: 'totp', + uuid: '4', + },{ + cost: 'USD:1', + instructions: 'send a wire transfer to an account', + type: 'iban', + uuid: '5', + },{ + cost: 'USD:1', + instructions: 'just go for it', + type: 'new-type-of-challenge', + uuid: '6', + }], + }, +} as ReducerState); + + +export const OnePolicyWithAllTheChallengesInDifferentState = createExample(TestedComponent, { + ...reducerStatesExample.challengeSelecting, + recovery_information: { + policies: [[ + { uuid: '1' }, + { uuid: '2' }, + { uuid: '3' }, + { uuid: '4' }, + { uuid: '5' }, + { uuid: '6' }, + { uuid: '7' }, + { uuid: '8' }, + { uuid: '9' }, + { uuid: '10' }, + ]], + challenges: [{ + cost: 'USD:1', + instructions: 'answer the a question correctly', + type: 'question', + uuid: '1', + },{ + cost: 'USD:1', + instructions: 'answer the a question correctly', + type: 'question', + uuid: '2', + },{ + cost: 'USD:1', + instructions: 'answer the a question correctly', + type: 'question', + uuid: '3', + },{ + cost: 'USD:1', + instructions: 'answer the a question correctly', + type: 'question', + uuid: '4', + },{ + cost: 'USD:1', + instructions: 'answer the a question correctly', + type: 'question', + uuid: '5', + },{ + cost: 'USD:1', + instructions: 'answer the a question correctly', + type: 'question', + uuid: '6', + },{ + cost: 'USD:1', + instructions: 'answer the a question correctly', + type: 'question', + uuid: '7', + },{ + cost: 'USD:1', + instructions: 'answer the a question correctly', + type: 'question', + uuid: '8', + },{ + cost: 'USD:1', + instructions: 'answer the a question correctly', + type: 'question', + uuid: '9', + },{ + cost: 'USD:1', + instructions: 'answer the a question correctly', + type: 'question', + uuid: '10', + }], + }, + challenge_feedback: { + 1: { state: 'solved' }, + 2: { state: 'hint' }, + 3: { state: 'details' }, + 4: { state: 'body' }, + 5: { state: 'redirect' }, + 6: { state: 'server-failure' }, + 7: { state: 'truth-unknown' }, + 8: { state: 'rate-limit-exceeded' }, + 9: { state: 'authentication-timeout' }, + 10: { state: 'external-instructions' }, + } +} 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 c9b52e91b..3bb3fb837 100644 --- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx @@ -1,3 +1,4 @@ +import { ChallengeFeedback } from "anastasis-core"; import { h, VNode } from "preact"; import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame } from "./index"; @@ -13,65 +14,94 @@ export function ChallengeOverviewScreen(): VNode { } const policies = reducer.currentReducerState.recovery_information?.policies ?? []; - const chArr = reducer.currentReducerState.recovery_information?.challenges ?? []; - const challengeFeedback = reducer.currentReducerState?.challenge_feedback; + const knownChallengesArray = reducer.currentReducerState.recovery_information?.challenges ?? []; + const challengeFeedback = reducer.currentReducerState?.challenge_feedback ?? {}; - const challenges: { + const knownChallengesMap: { [uuid: string]: { type: string; instructions: string; cost: string; + feedback: ChallengeFeedback | undefined; }; } = {}; - for (const ch of chArr) { - challenges[ch.uuid] = { + for (const ch of knownChallengesArray) { + knownChallengesMap[ch.uuid] = { type: ch.type, cost: ch.cost, instructions: ch.instructions, + feedback: challengeFeedback[ch.uuid] }; } + const policiesWithInfo = policies.map(row => { + let isPolicySolved = true + const challenges = row.map(({ uuid }) => { + const info = knownChallengesMap[uuid]; + const isChallengeSolved = info?.feedback?.state === 'solved' + isPolicySolved = isPolicySolved && isChallengeSolved + return { info, uuid, isChallengeSolved } + }).filter(ch => ch.info !== undefined) + + return { isPolicySolved, challenges } + }) + + const atLeastThereIsOnePolicySolved = policiesWithInfo.find(p => p.isPolicySolved) !== undefined + return ( - -

    Policies

    - {!policies.length &&

    - No policies found -

    } - {policies.map((row, i) => { + + {!policies.length ?

    + No policies found, try with another version of the secret +

    : (policies.length === 1 ?

    + One policy found for this secret. You need to solve all the challenges in order to recover your secret. +

    :

    + We have found {policies.length} polices. You need to solve all the challenges from one policy in order + to recover your secret. +

    )} + {policiesWithInfo.map((row, i) => { + const tableBody = row.challenges.map(({ info, uuid }) => { + return ( + + {info.type} + + {info.instructions} + + {info.feedback?.state ?? "unknown"} + {info.cost} + + {info.feedback?.state !== "solved" ? ( + reducer.transition("select_challenge", { uuid })}> + Solve + + ) : null} + + + ); + }) return (
    -

    Policy #{i + 1}

    - {row.map(column => { - const ch = challenges[column.uuid]; - if (!ch) return
    - There is no challenge for this policy -
    - const feedback = challengeFeedback?.[column.uuid]; - return ( -
    -

    - {ch.type} ({ch.instructions}) -

    -

    Status: {feedback?.state ?? "unknown"}

    - {feedback?.state !== "solved" ? ( - - ) : null} -
    - ); - })} + Policy #{i + 1} + {row.challenges.length === 0 &&

    + This policy doesn't have challenges +

    } + {row.challenges.length === 1 &&

    + This policy just have one challenge to be solved +

    } + {row.challenges.length > 1 &&

    + This policy have {row.challenges.length} challenges +

    } + + + + + + + + + + + {tableBody} + +
    Challenge typeDescriptionStatusCost
    ); })} diff --git a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx index 8744a2b79..2186eb42d 100644 --- a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx @@ -36,4 +36,5 @@ export default { }; export const Backup = createExample(TestedComponent, reducerStatesExample.backupSelectContinent); + export const Recovery = createExample(TestedComponent, reducerStatesExample.recoverySelectContinent); diff --git a/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx index b5933db17..0d2ebb778 100644 --- a/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx +++ b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx @@ -37,7 +37,7 @@ export default { }, }; -export const NormalEnding = createExample(TestedComponent, { +export const GoodEnding = createExample(TestedComponent, { ...reducerStatesExample.recoveryFinished, core_secret: { mime: 'text/plain', value: 'hello' } } as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx index f5fd7c0d1..79a46761c 100644 --- a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx @@ -5,7 +5,7 @@ import { useState } from "preact/hooks"; import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame} from "./index"; -import { LabeledInput } from "../../components/fields/LabeledInput"; +import { TextInput } from "../../components/fields/TextInput"; export function SecretEditorScreen(): VNode { const reducer = useAnastasisContext() @@ -47,14 +47,14 @@ export function SecretEditorScreen(): VNode { onNext={() => secretNext()} >
    -
    - diff --git a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx index 903f57868..5d67ee472 100644 --- a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx @@ -29,15 +29,31 @@ export function SecretSelectionScreen(): VNode { version: n, provider_url: p, }); - setSelectingVersion(false); }); + setSelectingVersion(false); } + const providerList = Object.keys(reducer.currentReducerState.authentication_providers ?? {}) const recoveryDocument = reducer.currentReducerState.recovery_document if (!recoveryDocument) { return ( - -

    No recovery document found

    + +

    No recovery document found, try with another provider

    + + + + + +
    Provider + +
    ) } @@ -45,43 +61,75 @@ export function SecretSelectionScreen(): VNode { return (

    Select a different version of the secret

    - -
    - setOtherVersion(Number((e.target as HTMLInputElement).value))} - type="number" /> - -
    -
    - -
    -
    - + + + + + + + + + + +
    Provider + +
    Version + setOtherVersion(Number((e.target as HTMLInputElement).value))} + type="number" /> + + setOtherVersion(0)}>set to latest version +
    +
    + +
    + ); } return ( -

    Provider: {recoveryDocument.provider_url}

    -

    Secret version: {recoveryDocument.version}

    -

    Secret name: {recoveryDocument.secret_name}

    - +

    Secret found, you can select another version or continue to the challenges solving

    + + + + + + + + + + + + + + + + +
    + Provider + + + + {recoveryDocument.provider_url} setSelectingVersion(true)}>use another provider
    + Secret version + + + + {recoveryDocument.version} setSelectingVersion(true)}>use another version
    + Secret name + + + + {recoveryDocument.secret_name}
    ); } diff --git a/packages/anastasis-webui/src/pages/home/SolveEmailEntry.tsx b/packages/anastasis-webui/src/pages/home/SolveEmailEntry.tsx deleted file mode 100644 index 0d70405e5..000000000 --- a/packages/anastasis-webui/src/pages/home/SolveEmailEntry.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { h, VNode } from "preact"; -import { useState } from "preact/hooks"; -import { useAnastasisContext } from "../../context/anastasis"; -import { AnastasisClientFrame } from "./index"; -import { LabeledInput } from "../../components/fields/LabeledInput"; -import { SolveEntryProps } from "./SolveScreen"; - -export function SolveEmailEntry({ challenge, feedback }: SolveEntryProps): VNode { - const [answer, setAnswer] = useState(""); - const reducer = useAnastasisContext() - const next = (): void => { - if (reducer) reducer.transition("solve_challenge", { - answer, - }) - }; - return ( - next()} - > -

    Feedback: {JSON.stringify(feedback)}

    -

    {challenge.instructions}

    - -
    - ); -} diff --git a/packages/anastasis-webui/src/pages/home/SolvePostEntry.tsx b/packages/anastasis-webui/src/pages/home/SolvePostEntry.tsx deleted file mode 100644 index 22b8d470b..000000000 --- a/packages/anastasis-webui/src/pages/home/SolvePostEntry.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { h, VNode } from "preact"; -import { useState } from "preact/hooks"; -import { useAnastasisContext } from "../../context/anastasis"; -import { AnastasisClientFrame } from "./index"; -import { LabeledInput } from "../../components/fields/LabeledInput"; -import { SolveEntryProps } from "./SolveScreen"; - -export function SolvePostEntry({ challenge, feedback }: SolveEntryProps): VNode { - const [answer, setAnswer] = useState(""); - const reducer = useAnastasisContext() - const next = (): void => { - if (reducer) reducer.transition("solve_challenge", { answer }) - }; - return ( - next()} - > -

    Feedback: {JSON.stringify(feedback)}

    -

    {challenge.instructions}

    - -
    - ); -} diff --git a/packages/anastasis-webui/src/pages/home/SolveQuestionEntry.tsx b/packages/anastasis-webui/src/pages/home/SolveQuestionEntry.tsx deleted file mode 100644 index 319289381..000000000 --- a/packages/anastasis-webui/src/pages/home/SolveQuestionEntry.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { h, VNode } from "preact"; -import { useState } from "preact/hooks"; -import { useAnastasisContext } from "../../context/anastasis"; -import { AnastasisClientFrame } from "./index"; -import { LabeledInput } from "../../components/fields/LabeledInput"; -import { SolveEntryProps } from "./SolveScreen"; - -export function SolveQuestionEntry({ challenge, feedback }: SolveEntryProps): VNode { - const [answer, setAnswer] = useState(""); - const reducer = useAnastasisContext() - const next = (): void => { - if (reducer) reducer.transition("solve_challenge", { answer }) - }; - return ( - next()} - > -

    Feedback: {JSON.stringify(feedback)}

    -

    Question: {challenge.instructions}

    - -
    - ); -} diff --git a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx index 05ae50b48..077726e02 100644 --- a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx @@ -1,28 +1,36 @@ -import { h, VNode } from "preact"; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { AnastasisClientFrame } from "."; import { ChallengeFeedback, ChallengeInfo } from "../../../../anastasis-core/lib"; +import { TextInput } from "../../components/fields/TextInput"; 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"; export function SolveScreen(): VNode { const reducer = useAnastasisContext() - + const [answer, setAnswer] = useState(""); + if (!reducer) { - return
    no reducer in context
    + return +
    no reducer in context
    +
    } if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) { - return
    invalid state
    + return +
    invalid state
    +
    } if (!reducer.currentReducerState.recovery_information) { - return
    no recovery information found
    + return +
    no recovery information found
    +
    } if (!reducer.currentReducerState.selected_challenge_uuid) { - return
    no selected uuid
    + return +
    no selected uuid
    +
    } + const chArr = reducer.currentReducerState.recovery_information.challenges; const challengeFeedback = reducer.currentReducerState.challenge_feedback ?? {}; const selectedUuid = reducer.currentReducerState.selected_challenge_uuid; @@ -39,16 +47,99 @@ export function SolveScreen(): VNode { email: SolveEmailEntry, post: SolvePostEntry, }; - const SolveDialog = dialogMap[selectedChallenge?.type] ?? SolveUnsupportedEntry; + const SolveDialog = selectedChallenge === undefined ? SolveUndefinedEntry : dialogMap[selectedChallenge.type] ?? SolveUnsupportedEntry; + + function onNext(): void { + reducer?.transition("solve_challenge", { answer }) + } + function onCancel(): void { + reducer?.back() + } + + return ( - + + + +
    + + +
    +
    ); } export interface SolveEntryProps { + id: string; challenge: ChallengeInfo; feedback?: ChallengeFeedback; + answer: string; + setAnswer: (s:string) => void; } +function SolveSmsEntry({ challenge, answer, setAnswer }: SolveEntryProps): VNode { + return ( +

    An sms has been sent to "{challenge.instructions}". Type the code below

    + +
    + ); +} +function SolveQuestionEntry({ challenge, answer, setAnswer }: SolveEntryProps): VNode { + return ( + +

    Type the answer to the following question:

    +
    +        {challenge.instructions}
    +      
    + +
    + ); +} + +function SolvePostEntry({ challenge, answer, setAnswer }: SolveEntryProps): VNode { + return ( + +

    instruction for post type challenge "{challenge.instructions}"

    + +
    + ); +} + +function SolveEmailEntry({ challenge, answer, setAnswer }: SolveEntryProps): VNode { + return ( + +

    An email has been sent to "{challenge.instructions}". Type the code below

    + +
    + ); +} + +function SolveUnsupportedEntry(props: SolveEntryProps): VNode { + return ( + +

    + The challenge selected is not supported for this UI. Please update this version or try using another policy. +

    +

    + Challenge type: {props.challenge.type} +

    +
    + ); +} +function SolveUndefinedEntry(props: SolveEntryProps): VNode { + return ( + +

    + There is no challenge information for id "{props.id}". Try resetting the recovery session. +

    +
    + ); +} diff --git a/packages/anastasis-webui/src/pages/home/SolveSmsEntry.tsx b/packages/anastasis-webui/src/pages/home/SolveSmsEntry.tsx deleted file mode 100644 index c4cf3a680..000000000 --- a/packages/anastasis-webui/src/pages/home/SolveSmsEntry.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { h, VNode } from "preact"; -import { useState } from "preact/hooks"; -import { useAnastasisContext } from "../../context/anastasis"; -import { AnastasisClientFrame } from "./index"; -import { LabeledInput } from "../../components/fields/LabeledInput"; -import { SolveEntryProps } from "./SolveScreen"; - -export function SolveSmsEntry({ challenge, feedback }: SolveEntryProps): VNode { - const [answer, setAnswer] = useState(""); - const reducer = useAnastasisContext() - const next = (): void => { - if (reducer) reducer.transition("solve_challenge", { - answer, - }) - }; - return ( - next()} - > -

    Feedback: {JSON.stringify(feedback)}

    -

    {challenge.instructions}

    - -
    - ); -} diff --git a/packages/anastasis-webui/src/pages/home/SolveUnsupportedEntry.tsx b/packages/anastasis-webui/src/pages/home/SolveUnsupportedEntry.tsx deleted file mode 100644 index 7f538d249..000000000 --- a/packages/anastasis-webui/src/pages/home/SolveUnsupportedEntry.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { h, VNode } from "preact"; -import { AnastasisClientFrame } from "./index"; -import { SolveEntryProps } from "./SolveScreen"; - -export function SolveUnsupportedEntry(props: SolveEntryProps): VNode { - return ( - -

    {JSON.stringify(props.challenge)}

    -

    Challenge not supported.

    -
    - ); -} diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx index 8a97ad50c..cf41efb59 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx @@ -14,17 +14,17 @@ TALER; see the file COPYING. If not, see */ -import { AmountJson, AmountLike, Amounts, i18n, Transaction, TransactionType } from "@gnu-taler/taler-util"; +import { AmountLike, Amounts, i18n, Transaction, TransactionType } from "@gnu-taler/taler-util"; import { format } from "date-fns"; -import { Fragment, JSX, VNode, h } from "preact"; +import { JSX, VNode } from "preact"; import { route } from 'preact-router'; import { useEffect, useState } from "preact/hooks"; -import * as wxApi from "../wxApi"; -import { Pages } from "../NavigationBar"; -import emptyImg from "../../static/img/empty.png" -import { Button, ButtonBox, ButtonBoxDestructive, ButtonDestructive, ButtonPrimary, ExtraLargeText, FontIcon, LargeText, ListOfProducts, PopupBox, Row, RowBorderGray, SmallLightText, WalletBox, WarningBox } from "../components/styled"; +import emptyImg from "../../static/img/empty.png"; import { ErrorMessage } from "../components/ErrorMessage"; import { Part } from "../components/Part"; +import { ButtonBox, ButtonBoxDestructive, ButtonPrimary, FontIcon, ListOfProducts, RowBorderGray, SmallLightText, WalletBox, WarningBox } from "../components/styled"; +import { Pages } from "../NavigationBar"; +import * as wxApi from "../wxApi"; export function TransactionPage({ tid }: { tid: string; }): JSX.Element { const [transaction, setTransaction] = useState< @@ -42,7 +42,7 @@ export function TransactionPage({ tid }: { tid: string; }): JSX.Element { } }; fetchData(); - }, []); + }, [tid]); if (!transaction) { return
    Loading ...
    ;