error notifications
This commit is contained in:
parent
ae0a35df2b
commit
9ba0e8597d
@ -26,7 +26,7 @@ import { useAsync } from "../hooks/async";
|
||||
|
||||
type Props = {
|
||||
children: ComponentChildren;
|
||||
disabled: boolean;
|
||||
disabled?: boolean;
|
||||
onClick?: () => Promise<void>;
|
||||
[rest: string]: any;
|
||||
};
|
||||
|
59
packages/anastasis-webui/src/components/Notifications.tsx
Normal file
59
packages/anastasis-webui/src/components/Notifications.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
(C) 2021 Taler Systems S.A.
|
||||
|
||||
GNU Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Javier Marchano (sebasjm)
|
||||
*/
|
||||
|
||||
import { h, VNode } from "preact";
|
||||
|
||||
export interface Notification {
|
||||
message: string;
|
||||
description?: string | VNode;
|
||||
type: MessageType;
|
||||
}
|
||||
|
||||
export type MessageType = 'INFO' | 'WARN' | 'ERROR' | 'SUCCESS'
|
||||
|
||||
interface Props {
|
||||
notifications: Notification[];
|
||||
removeNotification?: (n: Notification) => void;
|
||||
}
|
||||
|
||||
function messageStyle(type: MessageType): string {
|
||||
switch (type) {
|
||||
case "INFO": return "message is-info";
|
||||
case "WARN": return "message is-warning";
|
||||
case "ERROR": return "message is-danger";
|
||||
case "SUCCESS": return "message is-success";
|
||||
default: return "message"
|
||||
}
|
||||
}
|
||||
|
||||
export function Notifications({ notifications, removeNotification }: Props): VNode {
|
||||
return <div class="block">
|
||||
{notifications.map((n,i) => <article key={i} class={messageStyle(n.type)}>
|
||||
<div class="message-header">
|
||||
<p>{n.message}</p>
|
||||
<button class="delete" onClick={() => removeNotification && removeNotification(n)} />
|
||||
</div>
|
||||
{n.description && <div class="message-body">
|
||||
{n.description}
|
||||
</div>}
|
||||
</article>)}
|
||||
</div>
|
||||
}
|
@ -15,15 +15,18 @@ export function RecoveryFinishedScreen(): VNode {
|
||||
if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) {
|
||||
return <div>invalid state</div>
|
||||
}
|
||||
const encodedSecret = reducer.currentReducerState.core_secret?.value
|
||||
const encodedSecret = reducer.currentReducerState.core_secret
|
||||
if (!encodedSecret) {
|
||||
return <AnastasisClientFrame title="Recovery Problem" hideNav>
|
||||
<p>
|
||||
Secret not found
|
||||
</p>
|
||||
<div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}>
|
||||
<button class="button" onClick={() => reducer.back()}>Back</button>
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
}
|
||||
const secret = bytesToString(decodeCrock(encodedSecret))
|
||||
const secret = bytesToString(decodeCrock(encodedSecret.value))
|
||||
return (
|
||||
<AnastasisClientFrame title="Recovery Finished" hideNav>
|
||||
<p>
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
ChallengeFeedbackStatus,
|
||||
ChallengeInfo,
|
||||
} from "../../../../anastasis-core/lib";
|
||||
import { AsyncButton } from "../../components/AsyncButton";
|
||||
import { TextInput } from "../../components/fields/TextInput";
|
||||
import { useAnastasisContext } from "../../context/anastasis";
|
||||
|
||||
@ -106,8 +107,8 @@ export function SolveScreen(): VNode {
|
||||
? SolveUndefinedEntry
|
||||
: dialogMap[selectedChallenge.type] ?? SolveUnsupportedEntry;
|
||||
|
||||
function onNext(): void {
|
||||
reducer?.transition("solve_challenge", { answer });
|
||||
async function onNext(): Promise<void> {
|
||||
return reducer?.transition("solve_challenge", { answer });
|
||||
}
|
||||
function onCancel(): void {
|
||||
reducer?.back();
|
||||
@ -136,9 +137,9 @@ export function SolveScreen(): VNode {
|
||||
<button class="button" onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
<button class="button is-info" onClick={onNext}>
|
||||
<AsyncButton onClick={onNext} disabled={false}>
|
||||
Confirm
|
||||
</button>
|
||||
</AsyncButton>
|
||||
</div>
|
||||
</AnastasisClientFrame>
|
||||
);
|
||||
|
@ -25,10 +25,10 @@ export function StartScreen(): VNode {
|
||||
<span>Recover a secret</span>
|
||||
</button>
|
||||
|
||||
<button class="button">
|
||||
{/* <button class="button">
|
||||
<div class="icon"><i class="mdi mdi-file" /></div>
|
||||
<span>Restore a session</span>
|
||||
</button>
|
||||
</button> */}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
} from "preact/hooks";
|
||||
import { AsyncButton } from "../../components/AsyncButton";
|
||||
import { Menu } from "../../components/menu";
|
||||
import { Notifications } from "../../components/Notifications";
|
||||
import { AnastasisProvider, useAnastasisContext } from "../../context/anastasis";
|
||||
import {
|
||||
AnastasisReducerApi,
|
||||
@ -96,18 +97,11 @@ export function AnastasisClientFrame(props: AnastasisClientFrameProps): VNode {
|
||||
return <p>Fatal: Reducer must be in context.</p>;
|
||||
}
|
||||
const next = async (): Promise<void> => {
|
||||
return new Promise(async (res, rej) => {
|
||||
try {
|
||||
if (props.onNext) {
|
||||
await props.onNext();
|
||||
} else {
|
||||
await reducer.transition("next", {});
|
||||
}
|
||||
res()
|
||||
} catch {
|
||||
rej()
|
||||
}
|
||||
})
|
||||
if (props.onNext) {
|
||||
await props.onNext();
|
||||
} else {
|
||||
await reducer.transition("next", {});
|
||||
}
|
||||
};
|
||||
const handleKeyPress = (
|
||||
e: h.JSX.TargetedKeyboardEvent<HTMLDivElement>,
|
||||
@ -120,8 +114,8 @@ export function AnastasisClientFrame(props: AnastasisClientFrameProps): VNode {
|
||||
<Menu title="Anastasis" />
|
||||
<div class="home" onKeyPress={(e) => handleKeyPress(e)}>
|
||||
<h1 class="title">{props.title}</h1>
|
||||
<ErrorBanner />
|
||||
<section class="section is-main-section">
|
||||
<ErrorBanner />
|
||||
{props.children}
|
||||
{!props.hideNav ? (
|
||||
<div style={{ marginTop: '2em', display: 'flex', justifyContent: 'space-between' }}>
|
||||
@ -242,13 +236,11 @@ const AnastasisClientImpl: FunctionalComponent = () => {
|
||||
function ErrorBanner(): VNode | null {
|
||||
const reducer = useAnastasisContext();
|
||||
if (!reducer || !reducer.currentError) return null;
|
||||
return (
|
||||
<div id="error">
|
||||
<p>Error: {JSON.stringify(reducer.currentError)}</p>
|
||||
<button onClick={() => reducer.dismissError()}>
|
||||
Dismiss Error
|
||||
</button>
|
||||
</div>
|
||||
return (<Notifications removeNotification={reducer.dismissError} notifications={[{
|
||||
type: "ERROR",
|
||||
message: `Error code: ${reducer.currentError.code}`,
|
||||
description: reducer.currentError.hint
|
||||
}]} />
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user