From 32318a80f48bf52ca7823a0c055164f43bdaf1d6 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 27 Oct 2021 15:13:35 -0300 Subject: working version with improved ui --- .../src/components/fields/DateInput.tsx | 4 +- .../src/components/fields/LabeledInput.tsx | 40 ------ .../src/components/fields/NumberInput.tsx | 41 +++++++ .../src/components/fields/TextInput.tsx | 40 ++++++ .../src/components/menu/SideBar.tsx | 43 +++---- .../src/components/picker/DatePicker.tsx | 10 +- .../pages/home/AttributeEntryScreen.stories.tsx | 28 +++-- .../src/pages/home/AttributeEntryScreen.tsx | 25 +++- .../src/pages/home/AuthMethodEmailSetup.tsx | 4 +- .../src/pages/home/AuthMethodPostSetup.tsx | 12 +- .../src/pages/home/AuthMethodQuestionSetup.tsx | 6 +- .../pages/home/ChallengeOverviewScreen.stories.tsx | 134 ++++++++++++++++++++- .../src/pages/home/ChallengeOverviewScreen.tsx | 118 +++++++++++------- .../home/ContinentSelectionScreen.stories.tsx | 1 + .../pages/home/RecoveryFinishedScreen.stories.tsx | 2 +- .../src/pages/home/SecretEditorScreen.tsx | 6 +- .../src/pages/home/SecretSelectionScreen.tsx | 116 ++++++++++++------ .../src/pages/home/SolveEmailEntry.tsx | 26 ---- .../src/pages/home/SolvePostEntry.tsx | 24 ---- .../src/pages/home/SolveQuestionEntry.tsx | 24 ---- .../anastasis-webui/src/pages/home/SolveScreen.tsx | 121 ++++++++++++++++--- .../src/pages/home/SolveSmsEntry.tsx | 26 ---- .../src/pages/home/SolveUnsupportedEntry.tsx | 12 -- 23 files changed, 552 insertions(+), 311 deletions(-) delete mode 100644 packages/anastasis-webui/src/components/fields/LabeledInput.tsx create mode 100644 packages/anastasis-webui/src/components/fields/NumberInput.tsx create mode 100644 packages/anastasis-webui/src/components/fields/TextInput.tsx delete mode 100644 packages/anastasis-webui/src/pages/home/SolveEmailEntry.tsx delete mode 100644 packages/anastasis-webui/src/pages/home/SolvePostEntry.tsx delete mode 100644 packages/anastasis-webui/src/pages/home/SolveQuestionEntry.tsx delete mode 100644 packages/anastasis-webui/src/pages/home/SolveSmsEntry.tsx delete mode 100644 packages/anastasis-webui/src/pages/home/SolveUnsupportedEntry.tsx (limited to 'packages/anastasis-webui/src') 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/LabeledInput.tsx b/packages/anastasis-webui/src/components/fields/LabeledInput.tsx deleted file mode 100644 index 96d634a4f..000000000 --- a/packages/anastasis-webui/src/components/fields/LabeledInput.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { h, VNode } from "preact"; -import { useLayoutEffect, useRef, useState } from "preact/hooks"; - -export interface LabeledInputProps { - label: string; - grabFocus?: boolean; - error?: string; - tooltip?: string; - bind: [string, (x: string) => void]; -} - -export function LabeledInput(props: LabeledInputProps): 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/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/TextInput.tsx b/packages/anastasis-webui/src/components/fields/TextInput.tsx new file mode 100644 index 000000000..fa6fd9792 --- /dev/null +++ b/packages/anastasis-webui/src/components/fields/TextInput.tsx @@ -0,0 +1,40 @@ +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 TextInput(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/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 -
    -
  • -
  • -
    - ChallengeSelecting + Secret selection
  • -
  • +
  • - 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.

    -
    - ); -} -- cgit v1.2.3