anastasis-webui: hotfix behavior of back button on country selection screen

This commit is contained in:
Florian Dold 2021-11-08 16:10:22 +01:00
parent 8da58bd494
commit 16662b194d
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
2 changed files with 119 additions and 71 deletions

View File

@ -1,58 +1,81 @@
/* eslint-disable @typescript-eslint/camelcase */ /* eslint-disable @typescript-eslint/camelcase */
import { BackupStates, RecoveryStates } from "anastasis-core";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { useAnastasisContext } from "../../context/anastasis"; import { useAnastasisContext } from "../../context/anastasis";
import { AnastasisClientFrame, withProcessLabel } from "./index"; import { AnastasisClientFrame, withProcessLabel } from "./index";
export function ContinentSelectionScreen(): VNode { export function ContinentSelectionScreen(): VNode {
const reducer = useAnastasisContext() const reducer = useAnastasisContext();
//FIXME: remove this when #7056 is fixed // FIXME: remove this when #7056 is fixed
const countryFromReducer = (reducer?.currentReducerState as any).selected_country || "" const countryFromReducer =
const [countryCode, setCountryCode] = useState( countryFromReducer ) (reducer?.currentReducerState as any).selected_country || "";
const [countryCode, setCountryCode] = useState(countryFromReducer);
if (!reducer || !reducer.currentReducerState || !("continents" in reducer.currentReducerState)) { if (
return <div /> !reducer ||
!reducer.currentReducerState ||
!("continents" in reducer.currentReducerState)
) {
return <div />;
} }
const selectContinent = (continent: string): void => { const selectContinent = (continent: string): void => {
reducer.transition("select_continent", { continent }) reducer.transition("select_continent", { continent });
}; };
const selectCountry = (country: string): void => { const selectCountry = (country: string): void => {
setCountryCode(country) setCountryCode(country);
}; };
const continentList = reducer.currentReducerState.continents || []; const continentList = reducer.currentReducerState.continents || [];
const countryList = reducer.currentReducerState.countries || []; const countryList = reducer.currentReducerState.countries || [];
const theContinent = reducer.currentReducerState.selected_continent || "" const theContinent = reducer.currentReducerState.selected_continent || "";
// const cc = reducer.currentReducerState.selected_country || ""; // const cc = reducer.currentReducerState.selected_country || "";
const theCountry = countryList.find(c => c.code === countryCode) const theCountry = countryList.find((c) => c.code === countryCode);
const selectCountryAction = () => { const selectCountryAction = () => {
//selection should be when the select box changes it value //selection should be when the select box changes it value
if (!theCountry) return; if (!theCountry) return;
reducer.transition("select_country", { reducer.transition("select_country", {
country_code: countryCode, country_code: countryCode,
currencies: [theCountry.currency], currencies: [theCountry.currency],
}) });
} };
// const step1 = reducer.currentReducerState.backup_state === BackupStates.ContinentSelecting || // const step1 = reducer.currentReducerState.backup_state === BackupStates.ContinentSelecting ||
// reducer.currentReducerState.recovery_state === RecoveryStates.ContinentSelecting; // reducer.currentReducerState.recovery_state === RecoveryStates.ContinentSelecting;
const errors = !theCountry ? "Select a country" : undefined const errors = !theCountry ? "Select a country" : undefined;
const handleBack = async () => {
// We want to go to the start, even if we already selected
// a country.
// FIXME: What if we don't want to lose all information here?
// Can we do some kind of soft reset?
reducer.reset();
};
return ( return (
<AnastasisClientFrame hideNext={errors} title={withProcessLabel(reducer, "Where do you live?")} onNext={selectCountryAction}> <AnastasisClientFrame
hideNext={errors}
<div class="columns" > title={withProcessLabel(reducer, "Where do you live?")}
onNext={selectCountryAction}
onBack={handleBack}
>
<div class="columns">
<div class="column is-one-third"> <div class="column is-one-third">
<div class="field"> <div class="field">
<label class="label">Continent</label> <label class="label">Continent</label>
<div class="control is-expanded has-icons-left"> <div class="control is-expanded has-icons-left">
<div class="select is-fullwidth" > <div class="select is-fullwidth">
<select onChange={(e) => selectContinent(e.currentTarget.value)} value={theContinent} > <select
<option key="none" disabled selected value=""> Choose a continent </option> onChange={(e) => selectContinent(e.currentTarget.value)}
{continentList.map(prov => ( value={theContinent}
>
<option key="none" disabled selected value="">
{" "}
Choose a continent{" "}
</option>
{continentList.map((prov) => (
<option key={prov.name} value={prov.name}> <option key={prov.name} value={prov.name}>
{prov.name} {prov.name}
</option> </option>
@ -68,10 +91,17 @@ export function ContinentSelectionScreen(): VNode {
<div class="field"> <div class="field">
<label class="label">Country</label> <label class="label">Country</label>
<div class="control is-expanded has-icons-left"> <div class="control is-expanded has-icons-left">
<div class="select is-fullwidth" > <div class="select is-fullwidth">
<select onChange={(e) => selectCountry((e.target as any).value)} disabled={!theContinent} value={theCountry?.code || ""}> <select
<option key="none" disabled selected value=""> Choose a country </option> onChange={(e) => selectCountry((e.target as any).value)}
{countryList.map(prov => ( disabled={!theContinent}
value={theCountry?.code || ""}
>
<option key="none" disabled selected value="">
{" "}
Choose a country{" "}
</option>
{countryList.map((prov) => (
<option key={prov.name} value={prov.code}> <option key={prov.name} value={prov.code}>
{prov.name} {prov.name}
</option> </option>
@ -93,12 +123,15 @@ export function ContinentSelectionScreen(): VNode {
</div> </div>
<div class="column is-two-third"> <div class="column is-two-third">
<p> <p>
Your location will help us to determine which personal information Your location will help us to determine which personal information
ask you for the next step. to ask you for the next step.
</p>
<p>
You should choose the country that issued most of your long-term
legal documents or personal identifiers.
</p> </p>
</div> </div>
</div> </div>
</AnastasisClientFrame> </AnastasisClientFrame>
); );
} }

View File

@ -1,23 +1,22 @@
import { BackupStates, RecoveryStates } from "anastasis-core";
import { import {
BackupStates, ComponentChildren,
RecoveryStates Fragment,
} from "anastasis-core";
import {
ComponentChildren, Fragment,
FunctionalComponent, FunctionalComponent,
h, h,
VNode VNode,
} from "preact"; } from "preact";
import { import { useErrorBoundary } from "preact/hooks";
useErrorBoundary
} from "preact/hooks";
import { AsyncButton } from "../../components/AsyncButton"; import { AsyncButton } from "../../components/AsyncButton";
import { Menu } from "../../components/menu"; import { Menu } from "../../components/menu";
import { Notifications } from "../../components/Notifications"; import { Notifications } from "../../components/Notifications";
import { AnastasisProvider, useAnastasisContext } from "../../context/anastasis"; import {
AnastasisProvider,
useAnastasisContext,
} from "../../context/anastasis";
import { import {
AnastasisReducerApi, AnastasisReducerApi,
useAnastasisReducer useAnastasisReducer,
} from "../../hooks/use-anastasis-reducer"; } from "../../hooks/use-anastasis-reducer";
import { AttributeEntryScreen } from "./AttributeEntryScreen"; import { AttributeEntryScreen } from "./AttributeEntryScreen";
import { AuthenticationEditorScreen } from "./AuthenticationEditorScreen"; import { AuthenticationEditorScreen } from "./AuthenticationEditorScreen";
@ -50,6 +49,10 @@ export function withProcessLabel(
interface AnastasisClientFrameProps { interface AnastasisClientFrameProps {
onNext?(): void; onNext?(): void;
/**
* Override for the "back" functionality.
*/
onBack?(): Promise<void>;
title: string; title: string;
children: ComponentChildren; children: ComponentChildren;
/** /**
@ -116,9 +119,27 @@ export function AnastasisClientFrame(props: AnastasisClientFrameProps): VNode {
<section class="section is-main-section"> <section class="section is-main-section">
{props.children} {props.children}
{!props.hideNav ? ( {!props.hideNav ? (
<div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}> <div
<button class="button" onClick={() => reducer.back()}>Back</button> style={{
<AsyncButton class="button is-info" data-tooltip={props.hideNext} onClick={next} disabled={props.hideNext !== undefined}>Next</AsyncButton> marginTop: "2em",
display: "flex",
justifyContent: "space-between",
}}
>
<button
class="button"
onClick={() => (props.onBack ?? reducer.back)()}
>
Back
</button>
<AsyncButton
class="button is-info"
data-tooltip={props.hideNext}
onClick={next}
disabled={props.hideNext !== undefined}
>
Next
</AsyncButton>
</div> </div>
) : null} ) : null}
</section> </section>
@ -139,7 +160,7 @@ const AnastasisClient: FunctionalComponent = () => {
}; };
function AnastasisClientImpl(): VNode { function AnastasisClientImpl(): VNode {
const reducer = useAnastasisContext() const reducer = useAnastasisContext();
if (!reducer) { if (!reducer) {
return <p>Fatal: Reducer must be in context.</p>; return <p>Fatal: Reducer must be in context.</p>;
} }
@ -155,27 +176,19 @@ function AnastasisClientImpl(): VNode {
state.backup_state === BackupStates.CountrySelecting || state.backup_state === BackupStates.CountrySelecting ||
state.recovery_state === RecoveryStates.CountrySelecting state.recovery_state === RecoveryStates.CountrySelecting
) { ) {
return ( return <ContinentSelectionScreen />;
<ContinentSelectionScreen />
);
} }
if ( if (
state.backup_state === BackupStates.UserAttributesCollecting || state.backup_state === BackupStates.UserAttributesCollecting ||
state.recovery_state === RecoveryStates.UserAttributesCollecting state.recovery_state === RecoveryStates.UserAttributesCollecting
) { ) {
return ( return <AttributeEntryScreen />;
<AttributeEntryScreen />
);
} }
if (state.backup_state === BackupStates.AuthenticationsEditing) { if (state.backup_state === BackupStates.AuthenticationsEditing) {
return ( return <AuthenticationEditorScreen />;
<AuthenticationEditorScreen />
);
} }
if (state.backup_state === BackupStates.PoliciesReviewing) { if (state.backup_state === BackupStates.PoliciesReviewing) {
return ( return <ReviewPoliciesScreen />;
<ReviewPoliciesScreen />
);
} }
if (state.backup_state === BackupStates.SecretEditing) { if (state.backup_state === BackupStates.SecretEditing) {
return <SecretEditorScreen />; return <SecretEditorScreen />;
@ -194,15 +207,11 @@ function AnastasisClientImpl(): VNode {
} }
if (state.recovery_state === RecoveryStates.SecretSelecting) { if (state.recovery_state === RecoveryStates.SecretSelecting) {
return ( return <SecretSelectionScreen />;
<SecretSelectionScreen />
);
} }
if (state.recovery_state === RecoveryStates.ChallengeSelecting) { if (state.recovery_state === RecoveryStates.ChallengeSelecting) {
return ( return <ChallengeOverviewScreen />;
<ChallengeOverviewScreen />
);
} }
if (state.recovery_state === RecoveryStates.ChallengeSolving) { if (state.recovery_state === RecoveryStates.ChallengeSolving) {
@ -210,9 +219,7 @@ function AnastasisClientImpl(): VNode {
} }
if (state.recovery_state === RecoveryStates.RecoveryFinished) { if (state.recovery_state === RecoveryStates.RecoveryFinished) {
return ( return <RecoveryFinishedScreen />;
<RecoveryFinishedScreen />
);
} }
if (state.recovery_state === RecoveryStates.ChallengePaying) { if (state.recovery_state === RecoveryStates.ChallengePaying) {
return <ChallengePayingScreen />; return <ChallengePayingScreen />;
@ -222,7 +229,9 @@ function AnastasisClientImpl(): VNode {
<AnastasisClientFrame hideNav title="Bug"> <AnastasisClientFrame hideNav title="Bug">
<p>Bug: Unknown state.</p> <p>Bug: Unknown state.</p>
<div class="buttons is-right"> <div class="buttons is-right">
<button class="button" onClick={() => reducer.reset()}>Reset</button> <button class="button" onClick={() => reducer.reset()}>
Reset
</button>
</div> </div>
</AnastasisClientFrame> </AnastasisClientFrame>
); );
@ -234,11 +243,17 @@ function AnastasisClientImpl(): VNode {
function ErrorBanner(): VNode | null { function ErrorBanner(): VNode | null {
const reducer = useAnastasisContext(); const reducer = useAnastasisContext();
if (!reducer || !reducer.currentError) return null; if (!reducer || !reducer.currentError) return null;
return (<Notifications removeNotification={reducer.dismissError} notifications={[{ return (
type: "ERROR", <Notifications
message: `Error code: ${reducer.currentError.code}`, removeNotification={reducer.dismissError}
description: reducer.currentError.hint notifications={[
}]} /> {
type: "ERROR",
message: `Error code: ${reducer.currentError.code}`,
description: reducer.currentError.hint,
},
]}
/>
); );
} }