summaryrefslogtreecommitdiff
path: root/packages/anastasis-webui/src/hooks
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-10-13 10:48:25 +0200
committerFlorian Dold <florian@dold.me>2021-10-13 10:49:20 +0200
commit0f1ef7eca1f1ab3c5a1787b19a6caec13fb30dec (patch)
treeae634d8a89388169d5093807fb9bdebf704ff67e /packages/anastasis-webui/src/hooks
parentb8d03b6b2aef630c0fafd7f6ab0fe317abfe1d93 (diff)
downloadwallet-core-0f1ef7eca1f1ab3c5a1787b19a6caec13fb30dec.tar.gz
wallet-core-0f1ef7eca1f1ab3c5a1787b19a6caec13fb30dec.tar.bz2
wallet-core-0f1ef7eca1f1ab3c5a1787b19a6caec13fb30dec.zip
anastasis-webui: finish backup flow
Diffstat (limited to 'packages/anastasis-webui/src/hooks')
-rw-r--r--packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts185
1 files changed, 165 insertions, 20 deletions
diff --git a/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts b/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts
index 30bab96d1..d578d1418 100644
--- a/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts
+++ b/packages/anastasis-webui/src/hooks/use-anastasis-reducer.ts
@@ -1,6 +1,58 @@
import { useState } from "preact/hooks";
-type ReducerState = any;
+export type ReducerState =
+ | ReducerStateBackup
+ | ReducerStateRecovery
+ | ReducerStateError;
+
+export interface ReducerStateBackup {
+ recovery_state: undefined;
+ backup_state: BackupStates;
+ code: undefined;
+ continents: any;
+ countries: any;
+ authentication_providers: any;
+ authentication_methods?: AuthMethod[];
+ required_attributes: any;
+ secret_name?: string;
+ policies?: {
+ methods: {
+ authentication_method: number;
+ provider: string;
+ }[];
+ }[];
+ success_details: {
+ [provider_url: string]: {
+ policy_version: number;
+ };
+ };
+ payments?: string[];
+ policy_payment_requests?: {
+ payto: string;
+ provider: string;
+ }[];
+}
+
+export interface AuthMethod {
+ type: string;
+ instructions: string;
+ challenge: string;
+}
+
+export interface ReducerStateRecovery {
+ backup_state: undefined;
+ recovery_state: RecoveryStates;
+ code: undefined;
+
+ continents: any;
+ countries: any;
+}
+
+export interface ReducerStateError {
+ backup_state: undefined;
+ recovery_state: undefined;
+ code: number;
+}
interface AnastasisState {
reducerState: ReducerState | undefined;
@@ -10,6 +62,13 @@ interface AnastasisState {
export enum BackupStates {
ContinentSelecting = "CONTINENT_SELECTING",
CountrySelecting = "COUNTRY_SELECTING",
+ UserAttributesCollecting = "USER_ATTRIBUTES_COLLECTING",
+ AuthenticationsEditing = "AUTHENTICATIONS_EDITING",
+ PoliciesReviewing = "POLICIES_REVIEWING",
+ SecretEditing = "SECRET_EDITING",
+ TruthsPaying = "TRUTHS_PAYING",
+ PoliciesPaying = "POLICIES_PAYING",
+ BackupFinished = "BACKUP_FINISHED",
}
export enum RecoveryStates {
@@ -49,20 +108,62 @@ async function reduceState(
return resp.json();
}
+export interface ReducerTransactionHandle {
+ transactionState: ReducerState;
+ transition(action: string, args: any): Promise<ReducerState>;
+}
+
export interface AnastasisReducerApi {
- currentReducerState: ReducerState;
+ currentReducerState: ReducerState | undefined;
currentError: any;
+ dismissError: () => void;
startBackup: () => void;
startRecover: () => void;
+ reset: () => void;
back: () => void;
transition(action: string, args: any): void;
+ /**
+ * Run multiple reducer steps in a transaction without
+ * affecting the UI-visible transition state in-between.
+ */
+ runTransaction(f: (h: ReducerTransactionHandle) => Promise<void>): void;
+}
+
+function restoreState(): any {
+ let state: any;
+ try {
+ let s = localStorage.getItem("anastasisReducerState");
+ if (s === "undefined") {
+ state = undefined;
+ } else if (s) {
+ console.log("restoring state from", s);
+ state = JSON.parse(s);
+ }
+ } catch (e) {
+ console.log(e);
+ }
+ return state ?? undefined;
}
export function useAnastasisReducer(): AnastasisReducerApi {
- const [anastasisState, setAnastasisState] = useState<AnastasisState>({
- reducerState: undefined,
- currentError: undefined,
- });
+ const [anastasisState, setAnastasisStateInternal] = useState<AnastasisState>(
+ () => ({
+ reducerState: restoreState(),
+ currentError: undefined,
+ }),
+ );
+
+ const setAnastasisState = (newState: AnastasisState) => {
+ try {
+ localStorage.setItem(
+ "anastasisReducerState",
+ JSON.stringify(newState.reducerState),
+ );
+ } catch (e) {
+ console.log(e);
+ }
+ setAnastasisStateInternal(newState);
+ };
async function doTransition(action: string, args: any) {
console.log("reducing with", action, args);
@@ -102,30 +203,74 @@ export function useAnastasisReducer(): AnastasisReducerApi {
doTransition(action, args);
},
back() {
+ const reducerState = anastasisState.reducerState;
+ if (!reducerState) {
+ return;
+ }
if (
- anastasisState.reducerState.backup_state ===
- BackupStates.ContinentSelecting ||
- anastasisState.reducerState.recovery_state ===
- RecoveryStates.ContinentSelecting
+ reducerState.backup_state === BackupStates.ContinentSelecting ||
+ reducerState.recovery_state === RecoveryStates.ContinentSelecting
) {
setAnastasisState({
...anastasisState,
currentError: undefined,
reducerState: undefined,
});
- } else if (
- anastasisState.reducerState.backup_state ===
- BackupStates.CountrySelecting
- ) {
- doTransition("unselect_continent", {});
- } else if (
- anastasisState.reducerState.recovery_state ===
- RecoveryStates.CountrySelecting
- ) {
- doTransition("unselect_continent", {});
} else {
doTransition("back", {});
}
},
+ dismissError() {
+ setAnastasisState({ ...anastasisState, currentError: undefined });
+ },
+ reset() {
+ setAnastasisState({
+ ...anastasisState,
+ currentError: undefined,
+ reducerState: undefined,
+ });
+ },
+ runTransaction(f) {
+ async function run() {
+ const txHandle = new ReducerTxImpl(anastasisState.reducerState!);
+ try {
+ await f(txHandle);
+ } catch (e) {
+ console.log("exception during reducer transaction", e);
+ }
+ const s = txHandle.transactionState;
+ console.log("transaction finished, new state", s);
+ if (s.code !== undefined) {
+ setAnastasisState({
+ ...anastasisState,
+ currentError: txHandle.transactionState,
+ });
+ } else {
+ setAnastasisState({
+ ...anastasisState,
+ reducerState: txHandle.transactionState,
+ currentError: undefined,
+ });
+ }
+ }
+ run();
+ },
};
}
+
+class ReducerTxImpl implements ReducerTransactionHandle {
+ constructor(public transactionState: ReducerState) {}
+ async transition(action: string, args: any): Promise<ReducerState> {
+ console.log("making transition in transaction", action);
+ this.transactionState = await reduceState(
+ this.transactionState,
+ action,
+ args,
+ );
+ // Abort transaction as soon as we transition into an error state.
+ if (this.transactionState.code !== undefined) {
+ throw Error("transition resulted in error");
+ }
+ return this.transactionState;
+ }
+}