diff options
author | Sebastian <sebasjm@gmail.com> | 2021-11-01 16:10:49 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2021-11-01 16:10:55 -0300 |
commit | 88d142d2098ad87613222e9a0c6df478a78f6528 (patch) | |
tree | c5552e43a4641edb233fc858670d50c41d2c7c9b /packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx | |
parent | ea2acd1d3caa21f23127687214045a49d8fea0ad (diff) | |
download | wallet-core-88d142d2098ad87613222e9a0c6df478a78f6528.tar.xz |
more styling
added placeholders for inputs
import declaration for png
next button now has tooltip providing info about whats missing
a lot more of examples for UI testing
added qr dependency for totp rendering
added email and field input types
added all auth method setup screens
added modal when there is not auth provider
merge continent and country into location section
others improvements as well...
Diffstat (limited to 'packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx')
-rw-r--r-- | packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx | 199 |
1 files changed, 138 insertions, 61 deletions
diff --git a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx index e9ffccbac..f4d2aee58 100644 --- a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx @@ -1,19 +1,19 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { AuthMethod, ReducerStateBackup } from "anastasis-core"; -import { h, VNode } from "preact"; +import { AuthMethod } from "anastasis-core"; +import { ComponentChildren, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { useAnastasisContext } from "../../context/anastasis"; -import { AnastasisReducerApi } from "../../hooks/use-anastasis-reducer"; -import { AuthMethodEmailSetup } from "./AuthMethodEmailSetup"; -import { AuthMethodPostSetup } from "./AuthMethodPostSetup"; -import { AuthMethodQuestionSetup } from "./AuthMethodQuestionSetup"; -import { AuthMethodSmsSetup } from "./AuthMethodSmsSetup"; +import { authMethods, KnownAuthMethods } from "./authMethodSetup"; import { AnastasisClientFrame } from "./index"; + + +const getKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T> + export function AuthenticationEditorScreen(): VNode { - const [selectedMethod, setSelectedMethod] = useState<string | undefined>( - undefined - ); + const [noProvidersAck, setNoProvidersAck] = useState(false) + const [selectedMethod, setSelectedMethod] = useState<KnownAuthMethods | undefined>(undefined); + const reducer = useAnastasisContext() if (!reducer) { return <div>no reducer in context</div> @@ -21,7 +21,29 @@ export function AuthenticationEditorScreen(): VNode { if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) { return <div>invalid state</div> } + 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, + }) + } + + 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) + 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]; @@ -31,79 +53,106 @@ export function AuthenticationEditorScreen(): VNode { } } } + if (selectedMethod) { const cancel = (): void => setSelectedMethod(undefined); const addMethod = (args: any): void => { reducer.transition("add_authentication", args); setSelectedMethod(undefined); }; - const methodMap: Record< - string, (props: AuthMethodSetupProps) => h.JSX.Element - > = { - sms: AuthMethodSmsSetup, - question: AuthMethodQuestionSetup, - email: AuthMethodEmailSetup, - post: AuthMethodPostSetup, - }; - const AuthSetup = methodMap[selectedMethod] ?? AuthMethodNotImplemented; + + const AuthSetup = authMethods[selectedMethod].screen ?? AuthMethodNotImplemented; return ( <AuthSetup cancel={cancel} + configured={camByType[selectedMethod] || []} addAuthMethod={addMethod} method={selectedMethod} /> ); } - function MethodButton(props: { method: string; label: string }): VNode { + function MethodButton(props: { method: KnownAuthMethods }): VNode { return ( - <button - disabled={!authAvailableSet.has(props.method)} - onClick={() => { - setSelectedMethod(props.method); - if (reducer) reducer.dismissError(); - }} - > - {props.label} - </button> + <div class="block"> + <button + style={{ justifyContent: 'space-between' }} + class="button is-fullwidth" + onClick={() => { + if (!authAvailableSet.has(props.method)) { + //open add sms dialog + } else { + setSelectedMethod(props.method); + } + if (reducer) reducer.dismissError(); + }} + > + <div style={{ display: 'flex' }}> + <span class="icon "> + {authMethods[props.method].icon} + </span> + <span> + {authMethods[props.method].label} + </span> + </div> + {!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> + } + </button> + </div> ); } - const configuredAuthMethods: AuthMethod[] = reducer.currentReducerState.authentication_methods ?? []; - const haveMethodsConfigured = configuredAuthMethods.length; + const errors = !haveMethodsConfigured ? "There is not enough authentication methods." : undefined; return ( - <AnastasisClientFrame title="Backup: Configure Authentication Methods"> - <div> - <MethodButton method="sms" label="SMS" /> - <MethodButton method="email" label="Email" /> - <MethodButton method="question" label="Question" /> - <MethodButton method="post" label="Physical Mail" /> - <MethodButton method="totp" label="TOTP" /> - <MethodButton method="iban" label="IBAN" /> - </div> - <h2>Configured authentication methods</h2> - {haveMethodsConfigured ? ( - configuredAuthMethods.map((x, i) => { - return ( - <p key={i}> - {x.type} ({x.instructions}){" "} - <button - onClick={() => reducer.transition("delete_authentication", { - authentication_method: i, - })} - > - Delete - </button> + <AnastasisClientFrame title="Backup: Configure Authentication Methods" hideNext={errors}> + <div class="columns"> + <div class="column is-half"> + <div> + {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"> + 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> - ); - }) - ) : ( - <p>No authentication methods configured yet.</p> - )} + </ConfirmModal>} + + {/* {haveMethodsConfigured && ( + configuredAuthMethods.map((x, i) => { + return ( + <p key={i}> + {x.type} ({x.instructions}){" "} + <button class="button is-danger is-small" + onClick={() => reducer.transition("delete_authentication", { + authentication_method: i, + })} + > + Remove + </button> + </p> + ); + }) + )} */} + </div> + <div class="column is-half"> + When recovering your wallet, you will be asked to verify your identity via the methods you configure here. + </div> + </div> </AnastasisClientFrame> ); } +type AuthMethodWithRemove = AuthMethod & { remove: () => void } export interface AuthMethodSetupProps { method: string; addAuthMethod: (x: any) => void; + configured: AuthMethodWithRemove[]; cancel: () => void; } @@ -116,8 +165,36 @@ function AuthMethodNotImplemented(props: AuthMethodSetupProps): VNode { ); } -interface AuthenticationEditorProps { - reducer: AnastasisReducerApi; - backupState: ReducerStateBackup; + +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> + </div> + <button class="modal-close is-large " aria-label="close" onClick={onCancel} /> + </div> } +interface Props { + active?: boolean; + description?: string; + onCancel?: () => void; + onConfirm?: () => void; + label?: string; + children?: ComponentChildren; + danger?: boolean; + disabled?: boolean; +} |