diff options
Diffstat (limited to 'packages/anastasis-webui/src')
3 files changed, 191 insertions, 113 deletions
diff --git a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx index a71220c55..6e3ca2941 100644 --- a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx @@ -1,53 +1,62 @@ -import { AuthMethod } from "anastasis-core"; +import { AuthMethod, ReducerStateBackup } from "anastasis-core"; import { ComponentChildren, Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { useAnastasisContext } from "../../context/anastasis"; -import { authMethods, AuthMethodSetupProps, AuthMethodWithRemove, KnownAuthMethods } from "./authMethod"; +import { + authMethods, + AuthMethodSetupProps, + AuthMethodWithRemove, + KnownAuthMethods, +} from "./authMethod"; import { AnastasisClientFrame } from "./index"; - - -const getKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T> +const getKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>; export function AuthenticationEditorScreen(): VNode { - const [noProvidersAck, setNoProvidersAck] = useState(false) - const [selectedMethod, setSelectedMethod] = useState<KnownAuthMethods | undefined>(undefined); + const [noProvidersAck, setNoProvidersAck] = useState(false); + const [selectedMethod, setSelectedMethod] = useState< + KnownAuthMethods | undefined + >(undefined); + const [tooFewAuths, setTooFewAuths] = useState(false); // const [addingProvider, setAddingProvider] = useState<string | undefined>(undefined) - - const reducer = useAnastasisContext() + const reducer = useAnastasisContext(); if (!reducer) { - return <div>no reducer in context</div> + return <div>no reducer in context</div>; } - if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) { - return <div>invalid state</div> + if ( + !reducer.currentReducerState || + reducer.currentReducerState.backup_state === undefined + ) { + return <div>invalid state</div>; } - const configuredAuthMethods: AuthMethod[] = reducer.currentReducerState.authentication_methods ?? []; + const configuredAuthMethods: AuthMethod[] = + reducer.currentReducerState.authentication_methods ?? []; const haveMethodsConfigured = configuredAuthMethods.length > 0; function removeByIndex(index: number): void { - if (reducer) reducer.transition("delete_authentication", { - authentication_method: index, - }) + if (reducer) + reducer.transition("delete_authentication", { + authentication_method: index, + }); } - const camByType: { [s: string]: AuthMethodWithRemove[] } = {} + const camByType: { [s: string]: AuthMethodWithRemove[] } = {}; for (let index = 0; index < configuredAuthMethods.length; index++) { const cam = { ...configuredAuthMethods[index], - remove: () => removeByIndex(index) - } - const prevValue = camByType[cam.type] || [] - prevValue.push(cam) + remove: () => removeByIndex(index), + }; + const prevValue = camByType[cam.type] || []; + prevValue.push(cam); camByType[cam.type] = prevValue; } - const providers = reducer.currentReducerState.authentication_providers!; const authAvailableSet = new Set<string>(); for (const provKey of Object.keys(providers)) { const p = providers[provKey]; - if ("http_status" in p && (!("error_code" in p)) && p.methods) { + if ("http_status" in p && !("error_code" in p) && p.methods) { for (const meth of p.methods) { authAvailableSet.add(meth.type); } @@ -61,102 +70,147 @@ export function AuthenticationEditorScreen(): VNode { setSelectedMethod(undefined); }; - const AuthSetup = authMethods[selectedMethod].setup ?? AuthMethodNotImplemented; - return (<Fragment> - <AuthSetup - cancel={cancel} - configured={camByType[selectedMethod] || []} - addAuthMethod={addMethod} - method={selectedMethod} /> - - {!authAvailableSet.has(selectedMethod) && <ConfirmModal active - onCancel={cancel} description="No providers founds" label="Add a provider manually" - onConfirm={() => { - null - }} - > - We have found no trusted cloud providers for your recovery secret. You can add a provider manually. - To add a provider you must know the provider URL (e.g. https://provider.com) - <p> - <a>More about cloud providers</a> - </p> - </ConfirmModal>} + const AuthSetup = + authMethods[selectedMethod].setup ?? AuthMethodNotImplemented; + return ( + <Fragment> + <AuthSetup + cancel={cancel} + configured={camByType[selectedMethod] || []} + addAuthMethod={addMethod} + method={selectedMethod} + /> - </Fragment> + {!authAvailableSet.has(selectedMethod) && ( + <ConfirmModal + active + onCancel={cancel} + description="No providers founds" + label="Add a provider manually" + onConfirm={() => { + null; + }} + > + <p> + We have found no Anastasis providers that support this + authentication method. You can add a provider manually. To add a + provider you must know the provider URL (e.g. + https://provider.com) + </p> + <p> + <a>Learn more about Anastasis providers</a> + </p> + </ConfirmModal> + )} + </Fragment> ); } function MethodButton(props: { method: KnownAuthMethods }): VNode { - if (authMethods[props.method].skip) return <div /> - + if (authMethods[props.method].skip) return <div />; + return ( <div class="block"> <button - style={{ justifyContent: 'space-between' }} + style={{ justifyContent: "space-between" }} class="button is-fullwidth" onClick={() => { setSelectedMethod(props.method); }} > - <div style={{ display: 'flex' }}> - <span class="icon "> - {authMethods[props.method].icon} - </span> - {authAvailableSet.has(props.method) ? - <span> - Add a {authMethods[props.method].label} challenge - </span> : - <span> - Add a {authMethods[props.method].label} provider - </span> - } + <div style={{ display: "flex" }}> + <span class="icon ">{authMethods[props.method].icon}</span> + {authAvailableSet.has(props.method) ? ( + <span>Add a {authMethods[props.method].label} challenge</span> + ) : ( + <span>Add a {authMethods[props.method].label} provider</span> + )} </div> - {!authAvailableSet.has(props.method) && - <span class="icon has-text-danger" > + {!authAvailableSet.has(props.method) && ( + <span class="icon has-text-danger"> <i class="mdi mdi-exclamation-thick" /> </span> - } - {camByType[props.method] && - <span class="tag is-info" > - {camByType[props.method].length} - </span> - } + )} + {camByType[props.method] && ( + <span class="tag is-info">{camByType[props.method].length}</span> + )} </button> </div> ); } - const errors = !haveMethodsConfigured ? "There is not enough authentication methods." : undefined; + const errors = !haveMethodsConfigured + ? "There is not enough authentication methods." + : undefined; + const handleNext = async () => { + const st = reducer.currentReducerState as ReducerStateBackup; + if ((st.authentication_methods ?? []).length <= 2) { + setTooFewAuths(true); + } else { + await reducer.transition("next", {}); + } + }; return ( - <AnastasisClientFrame title="Backup: Configure Authentication Methods" hideNext={errors}> + <AnastasisClientFrame + title="Backup: Configure Authentication Methods" + hideNext={errors} + onNext={handleNext} + > <div class="columns"> <div class="column is-half"> <div> - {getKeys(authMethods).map(method => <MethodButton key={method} method={method} />)} + {getKeys(authMethods).map((method) => ( + <MethodButton key={method} method={method} /> + ))} </div> - {authAvailableSet.size === 0 && <ConfirmModal active={!noProvidersAck} - onCancel={() => setNoProvidersAck(true)} description="No providers founds" label="Add a provider manually" - onConfirm={() => { - null - }} - > - We have found no trusted cloud providers for your recovery secret. You can add a provider manually. - To add a provider you must know the provider URL (e.g. https://provider.com) - <p> - <a>More about cloud providers</a> - </p> - </ConfirmModal>} + {tooFewAuths ? ( + <ConfirmModal + active={tooFewAuths} + onCancel={() => setTooFewAuths(false)} + description="Too few auth methods configured" + label="Proceed anyway" + onConfirm={() => reducer.transition("next", {})} + > + You have selected fewer than three authentication methods. We + recommend that you add at least three. + </ConfirmModal> + ) : null} + {authAvailableSet.size === 0 && ( + <ConfirmModal + active={!noProvidersAck} + onCancel={() => setNoProvidersAck(true)} + description="No providers founds" + label="Add a provider manually" + onConfirm={() => { + null; + }} + > + <p> + We have found no Anastasis providers for your chosen country / + currency. You can add a providers manually. To add a provider + you must know the provider URL (e.g. https://provider.com) + </p> + <p> + <a>Learn more about Anastasis providers</a> + </p> + </ConfirmModal> + )} </div> <div class="column is-half"> <p class="block"> - When recovering your wallet, you will be asked to verify your identity via the methods you configure here. - The list of authentication method is defined by the backup provider list. + When recovering your wallet, you will be asked to verify your + identity via the methods you configure here. The list of + authentication method is defined by the backup provider list. </p> <p class="block"> - <button class="button is-info">Manage the backup provider's list</button> + <button class="button is-info"> + Manage the backup provider's list + </button> </p> - {authAvailableSet.size > 0 && <p class="block"> - We couldn't find provider for some of the authentication methods. - </p>} + {authAvailableSet.size > 0 && ( + <p class="block"> + We couldn't find provider for some of the authentication methods. + </p> + )} </div> </div> </AnastasisClientFrame> @@ -172,30 +226,54 @@ function AuthMethodNotImplemented(props: AuthMethodSetupProps): VNode { ); } - -function ConfirmModal({ active, description, onCancel, onConfirm, children, danger, disabled, label = 'Confirm' }: Props): VNode { - return <div class={active ? "modal is-active" : "modal"}> - <div class="modal-background " onClick={onCancel} /> - <div class="modal-card" style={{ maxWidth: 700 }}> - <header class="modal-card-head"> - {!description ? null : <p class="modal-card-title"><b>{description}</b></p>} - <button class="delete " aria-label="close" onClick={onCancel} /> - </header> - <section class="modal-card-body"> - {children} - </section> - <footer class="modal-card-foot"> - <button class="button" onClick={onCancel} >Dismiss</button> - <div class="buttons is-right" style={{ width: '100%' }}> - <button class={danger ? "button is-danger " : "button is-info "} disabled={disabled} onClick={onConfirm} >{label}</button> - </div> - </footer> +function ConfirmModal({ + active, + description, + onCancel, + onConfirm, + children, + danger, + disabled, + label = "Confirm", +}: ConfirmModelProps): VNode { + return ( + <div class={active ? "modal is-active" : "modal"}> + <div class="modal-background " onClick={onCancel} /> + <div class="modal-card" style={{ maxWidth: 700 }}> + <header class="modal-card-head"> + {!description ? null : ( + <p class="modal-card-title"> + <b>{description}</b> + </p> + )} + <button class="delete " aria-label="close" onClick={onCancel} /> + </header> + <section class="modal-card-body">{children}</section> + <footer class="modal-card-foot"> + <button class="button" onClick={onCancel}> + Dismiss + </button> + <div class="buttons is-right" style={{ width: "100%" }}> + <button + class={danger ? "button is-danger " : "button is-info "} + disabled={disabled} + onClick={onConfirm} + > + {label} + </button> + </div> + </footer> + </div> + <button + class="modal-close is-large " + aria-label="close" + onClick={onCancel} + /> </div> - <button class="modal-close is-large " aria-label="close" onClick={onCancel} /> - </div> + ); } -interface Props { +interface ConfirmModelProps { active?: boolean; description?: string; onCancel?: () => void; diff --git a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx index f7731ba00..aafde6e8c 100644 --- a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx @@ -32,7 +32,7 @@ export function ContinentSelectionScreen(): VNode { const theContinent = reducer.currentReducerState.selected_continent || ""; // const cc = reducer.currentReducerState.selected_country || ""; const theCountry = countryList.find((c) => c.code === countryCode); - const selectCountryAction = () => { + const selectCountryAction = async () => { //selection should be when the select box changes it value if (!theCountry) return; reducer.transition("select_country", { @@ -123,8 +123,8 @@ export function ContinentSelectionScreen(): VNode { </div> <div class="column is-two-third"> <p> - Your choice will help us with asking the right information to unique - identify you when you want to recover your backed up secrets. + Your selection will help us ask right information to uniquely + identify you when you want to recover your secret again. </p> <p> Choose the country that issued most of your long-term legal diff --git a/packages/anastasis-webui/src/pages/home/index.tsx b/packages/anastasis-webui/src/pages/home/index.tsx index 74320b6b5..d83442e62 100644 --- a/packages/anastasis-webui/src/pages/home/index.tsx +++ b/packages/anastasis-webui/src/pages/home/index.tsx @@ -48,7 +48,7 @@ export function withProcessLabel( } interface AnastasisClientFrameProps { - onNext?(): void; + onNext?(): Promise<void>; /** * Override for the "back" functionality. */ |