aboutsummaryrefslogtreecommitdiff
path: root/packages/anastasis-webui
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-10-21 13:11:17 +0200
committerFlorian Dold <florian@dold.me>2021-10-21 13:11:33 +0200
commit0ee669f52341a8331394a1e9892264c0ef0bb7d7 (patch)
tree5a4d1a02ad6acd0dd04edde2dc032160c299700f /packages/anastasis-webui
parentcf25f5698e9f3a3b36930e545f7cce9829fb08f6 (diff)
reducer WIP, user error boundaries in UI
Diffstat (limited to 'packages/anastasis-webui')
-rw-r--r--packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts4
-rw-r--r--packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx2
-rw-r--r--packages/anastasis-webui/src/pages/home/index.tsx142
-rw-r--r--packages/anastasis-webui/src/scss/main.scss6
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