anastasis-webui: ui tweaks
This commit is contained in:
parent
6a0c5263bb
commit
4dd5b75cfa
@ -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} />
|
||||
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>}
|
||||
|
||||
</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;
|
||||
|
@ -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
|
||||
|
@ -48,7 +48,7 @@ export function withProcessLabel(
|
||||
}
|
||||
|
||||
interface AnastasisClientFrameProps {
|
||||
onNext?(): void;
|
||||
onNext?(): Promise<void>;
|
||||
/**
|
||||
* Override for the "back" functionality.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user