diff options
author | Florian Dold <florian@dold.me> | 2021-10-21 13:11:17 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2021-10-21 13:11:33 +0200 |
commit | 0ee669f52341a8331394a1e9892264c0ef0bb7d7 (patch) | |
tree | 5a4d1a02ad6acd0dd04edde2dc032160c299700f /packages/anastasis-webui | |
parent | cf25f5698e9f3a3b36930e545f7cce9829fb08f6 (diff) |
reducer WIP, user error boundaries in UI
Diffstat (limited to 'packages/anastasis-webui')
4 files changed, 110 insertions, 44 deletions
diff --git a/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts b/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts index 4a242a2e5..72594749d 100644 --- a/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts +++ b/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts @@ -164,10 +164,12 @@ export function useAnastasisReducer(): AnastasisReducerApi { } else { s = await reduceAction(anastasisState.reducerState!, action, args); } - console.log("got new state from reducer", s); + console.log("got response from reducer", s); if (s.code) { + console.log("response is an error"); setAnastasisState({ ...anastasisState, currentError: s }); } else { + console.log("response is a new state"); setAnastasisState({ ...anastasisState, currentError: undefined, diff --git a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx index bbdcf8c2e..7cb7fdf20 100644 --- a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx @@ -57,7 +57,7 @@ export function SecretSelectionScreen(props: RecoveryReducerProps): VNode { <AnastasisClientFrame title="Recovery: Select secret"> <p>Provider: {recoveryDocument.provider_url}</p> <p>Secret version: {recoveryDocument.version}</p> - <p>Secret name: {recoveryDocument.version}</p> + <p>Secret name: {recoveryDocument.secret_name}</p> <button onClick={() => setSelectingVersion(true)}> Select different secret </button> diff --git a/packages/anastasis-webui/src/pages/home/index.tsx b/packages/anastasis-webui/src/pages/home/index.tsx index 6e9ea07fc..5001d1ee4 100644 --- a/packages/anastasis-webui/src/pages/home/index.tsx +++ b/packages/anastasis-webui/src/pages/home/index.tsx @@ -1,17 +1,28 @@ import { - ComponentChildren, createContext, - Fragment, FunctionalComponent, h, VNode + Component, + ComponentChildren, + createContext, + Fragment, + FunctionalComponent, + h, + VNode, } from "preact"; -import { useContext, useLayoutEffect, useRef } from "preact/hooks"; +import { + useContext, + useErrorBoundary, + useLayoutEffect, + useRef, +} from "preact/hooks"; import { Menu } from "../../components/menu"; import { - BackupStates, RecoveryStates, + BackupStates, + RecoveryStates, ReducerStateBackup, ReducerStateRecovery, } from "anastasis-core"; import { AnastasisReducerApi, - useAnastasisReducer + useAnastasisReducer, } from "../../hooks/use-anastasis-reducer"; import { AttributeEntryScreen } from "./AttributeEntryScreen"; import { AuthenticationEditorScreen } from "./AuthenticationEditorScreen"; @@ -27,7 +38,7 @@ import { SecretSelectionScreen } from "./SecretSelectionScreen"; import { SolveScreen } from "./SolveScreen"; import { StartScreen } from "./StartScreen"; import { TruthsPayingScreen } from "./TruthsPayingScreen"; -import "./../home/style" +import "./../home/style"; const WithReducer = createContext<AnastasisReducerApi | undefined>(undefined); @@ -40,7 +51,10 @@ export interface CommonReducerProps { reducerState: ReducerStateBackup | ReducerStateRecovery; } -export function withProcessLabel(reducer: AnastasisReducerApi, text: string): string { +export function withProcessLabel( + reducer: AnastasisReducerApi, + text: string, +): string { if (isBackup(reducer)) { return `Backup: ${text}`; } @@ -71,6 +85,33 @@ interface AnastasisClientFrameProps { hideNext?: boolean; } +function ErrorBoundary(props: { + reducer: AnastasisReducerApi; + children: ComponentChildren; +}) { + const [error, resetError] = useErrorBoundary((error) => + console.log("got error", error), + ); + if (error) { + return ( + <div> + <button + onClick={() => { + props.reducer.reset(); + resetError(); + }} + > + Reset + </button> + <p> + Error: <pre>{error.stack}</pre> + </p> + </div> + ); + } + return <div>{props.children}</div>; +} + export function AnastasisClientFrame(props: AnastasisClientFrameProps): VNode { const reducer = useContext(WithReducer); if (!reducer) { @@ -83,29 +124,30 @@ export function AnastasisClientFrame(props: AnastasisClientFrameProps): VNode { reducer.transition("next", {}); } }; - const handleKeyPress = (e: h.JSX.TargetedKeyboardEvent<HTMLDivElement>): void => { + const handleKeyPress = ( + e: h.JSX.TargetedKeyboardEvent<HTMLDivElement>, + ): void => { console.log("Got key press", e.key); // FIXME: By default, "next" action should be executed here }; - return (<Fragment> - <Menu title="Anastasis" /> - <section class="section"> - <div class="home" onKeyPress={(e) => handleKeyPress(e)}> - <button onClick={() => reducer.reset()}>Reset session</button> - <h1>{props.title}</h1> - <ErrorBanner reducer={reducer} /> - {props.children} - {!props.hideNav ? ( - <div> - <button onClick={() => reducer.back()}>Back</button> - {!props.hideNext ? ( - <button onClick={next}>Next</button> - ) : null} - </div> - ) : null} + return ( + <Fragment> + <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} /> + {props.children} + {!props.hideNav ? ( + <div> + <button onClick={() => reducer.back()}>Back</button> + {!props.hideNext ? <button onClick={next}>Next</button> : null} + </div> + ) : null} + </div> </div> - </section> - </Fragment> + </Fragment> ); } @@ -113,7 +155,9 @@ const AnastasisClient: FunctionalComponent = () => { const reducer = useAnastasisReducer(); return ( <WithReducer.Provider value={reducer}> - <AnastasisClientImpl /> + <ErrorBoundary reducer={reducer}> + <AnastasisClientImpl /> + </ErrorBoundary> </WithReducer.Provider> ); }; @@ -130,27 +174,38 @@ const AnastasisClientImpl: FunctionalComponent = () => { reducerState.backup_state === BackupStates.ContinentSelecting || reducerState.recovery_state === RecoveryStates.ContinentSelecting ) { - return <ContinentSelectionScreen reducer={reducer} reducerState={reducerState} />; + return ( + <ContinentSelectionScreen reducer={reducer} reducerState={reducerState} /> + ); } if ( reducerState.backup_state === BackupStates.CountrySelecting || reducerState.recovery_state === RecoveryStates.CountrySelecting ) { - return <CountrySelectionScreen reducer={reducer} reducerState={reducerState} />; + return ( + <CountrySelectionScreen reducer={reducer} reducerState={reducerState} /> + ); } if ( reducerState.backup_state === BackupStates.UserAttributesCollecting || reducerState.recovery_state === RecoveryStates.UserAttributesCollecting ) { - return <AttributeEntryScreen reducer={reducer} reducerState={reducerState} />; + return ( + <AttributeEntryScreen reducer={reducer} reducerState={reducerState} /> + ); } if (reducerState.backup_state === BackupStates.AuthenticationsEditing) { return ( - <AuthenticationEditorScreen backupState={reducerState} reducer={reducer} /> + <AuthenticationEditorScreen + backupState={reducerState} + reducer={reducer} + /> ); } if (reducerState.backup_state === BackupStates.PoliciesReviewing) { - return <ReviewPoliciesScreen reducer={reducer} backupState={reducerState} />; + return ( + <ReviewPoliciesScreen reducer={reducer} backupState={reducerState} /> + ); } if (reducerState.backup_state === BackupStates.SecretEditing) { return <SecretEditorScreen reducer={reducer} backupState={reducerState} />; @@ -162,29 +217,34 @@ const AnastasisClientImpl: FunctionalComponent = () => { } if (reducerState.backup_state === BackupStates.TruthsPaying) { - return <TruthsPayingScreen reducer={reducer} backupState={reducerState} /> - + return <TruthsPayingScreen reducer={reducer} backupState={reducerState} />; } if (reducerState.backup_state === BackupStates.PoliciesPaying) { const backupState: ReducerStateBackup = reducerState; - return <PoliciesPayingScreen reducer={reducer} backupState={backupState} /> + return <PoliciesPayingScreen reducer={reducer} backupState={backupState} />; } if (reducerState.recovery_state === RecoveryStates.SecretSelecting) { - return <SecretSelectionScreen reducer={reducer} recoveryState={reducerState} />; + return ( + <SecretSelectionScreen reducer={reducer} recoveryState={reducerState} /> + ); } if (reducerState.recovery_state === RecoveryStates.ChallengeSelecting) { - return <ChallengeOverviewScreen reducer={reducer} recoveryState={reducerState} />; + return ( + <ChallengeOverviewScreen reducer={reducer} recoveryState={reducerState} /> + ); } if (reducerState.recovery_state === RecoveryStates.ChallengeSolving) { - return <SolveScreen reducer={reducer} recoveryState={reducerState} /> + return <SolveScreen reducer={reducer} recoveryState={reducerState} />; } if (reducerState.recovery_state === RecoveryStates.RecoveryFinished) { - return <RecoveryFinishedScreen reducer={reducer} recoveryState={reducerState} /> + return ( + <RecoveryFinishedScreen reducer={reducer} recoveryState={reducerState} /> + ); } console.log("unknown state", reducer.currentReducerState); @@ -196,7 +256,6 @@ const AnastasisClientImpl: FunctionalComponent = () => { ); }; - interface LabeledInputProps { label: string; grabFocus?: boolean; @@ -223,7 +282,6 @@ export function LabeledInput(props: LabeledInputProps): VNode { ); } - interface ErrorBannerProps { reducer: AnastasisReducerApi; } @@ -235,7 +293,7 @@ function ErrorBanner(props: ErrorBannerProps): VNode | null { const currentError = props.reducer.currentError; if (currentError) { return ( - <div id="error"> + <div id="error"> <p>Error: {JSON.stringify(currentError)}</p> <button onClick={() => props.reducer.dismissError()}> Dismiss Error diff --git a/packages/anastasis-webui/src/scss/main.scss b/packages/anastasis-webui/src/scss/main.scss index b22557618..2e60bf6f9 100644 --- a/packages/anastasis-webui/src/scss/main.scss +++ b/packages/anastasis-webui/src/scss/main.scss @@ -226,4 +226,10 @@ div[data-tooltip]::before { .notfound { padding: 0 5%; margin: 100px 0; +} + +h1 { + font-size: 1.5em; + margin-top: 0.8em; + margin-bottom: 0.8em; }
\ No newline at end of file |