aboutsummaryrefslogtreecommitdiff
path: root/packages/challenger-ui/src/pages/AskChallenge.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/challenger-ui/src/pages/AskChallenge.tsx')
-rw-r--r--packages/challenger-ui/src/pages/AskChallenge.tsx138
1 files changed, 44 insertions, 94 deletions
diff --git a/packages/challenger-ui/src/pages/AskChallenge.tsx b/packages/challenger-ui/src/pages/AskChallenge.tsx
index 71f45dde3..675e2b869 100644
--- a/packages/challenger-ui/src/pages/AskChallenge.tsx
+++ b/packages/challenger-ui/src/pages/AskChallenge.tsx
@@ -14,24 +14,20 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import {
+ HttpStatusCode
+} from "@gnu-taler/taler-util";
+import {
Attention,
Button,
- Loading,
LocalNotificationBanner,
+ RouteDefinition,
ShowInputErrorLabel,
useChallengerApiContext,
useLocalNotificationHandler,
- useTranslationContext,
+ useTranslationContext
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
-import { useChallengeSession } from "../hooks/challenge.js";
-import {
- ChallengerApi,
- HttpStatusCode,
- TalerError,
- assertUnreachable,
-} from "@gnu-taler/taler-util";
import { useSessionState } from "../hooks/session.js";
type Form = {
@@ -42,33 +38,29 @@ export const EMAIL_REGEX = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/;
type Props = {
nonce: string;
onSendSuccesful: () => void;
+ routeSolveChallenge: RouteDefinition<{nonce:string}>,
};
-function ChallengeForm({
- nonce,
- status,
- onSendSuccesful,
-}: {
- nonce: string;
- status: ChallengerApi.ChallengeStatus;
- onSendSuccesful: () => void;
-}): VNode {
- const prevEmail = !status.last_address
- ? undefined
- : ((status.last_address as any)["email"] as string);
- const regexEmail = !status.restrictions
- ? undefined
- : ((status.restrictions as any)["email"] as {
- regex?: string;
- hint?: string;
- hint_i18n?: string;
- });
+export function AskChallenge({ nonce, onSendSuccesful,routeSolveChallenge }: Props): VNode {
+ const { state, accepted, completed } = useSessionState();
+ const status = state?.challengeStatus;
+ const prevEmail =
+ !status || !status.last_address
+ ? undefined
+ : ((status.last_address as any)["email"] as string);
+ const regexEmail =
+ !status || !status.restrictions
+ ? undefined
+ : ((status.restrictions as any)["email"] as {
+ regex?: string;
+ hint?: string;
+ hint_i18n?: string;
+ });
const { lib } = useChallengerApiContext();
const { i18n } = useTranslationContext();
const [notification, withErrorHandler] = useLocalNotificationHandler();
const [email, setEmail] = useState<string | undefined>(prevEmail);
- const { accepted, completed } = useSessionState();
const [repeat, setRepeat] = useState<string | undefined>();
const errors = undefinedIfEmpty({
@@ -82,10 +74,12 @@ function ChallengeForm({
: undefined
: !EMAIL_REGEX.test(email)
? i18n.str`invalid email`
- : email !== repeat
- ? i18n.str`emails don't match`
- : undefined,
- repeat: !repeat ? i18n.str`required` : undefined,
+ : undefined,
+ repeat: !repeat
+ ? i18n.str`required`
+ : email !== repeat
+ ? i18n.str`emails doesn't match`
+ : undefined,
});
const onSend = withErrorHandler(
@@ -120,6 +114,10 @@ function ChallengeForm({
},
);
+ if (!status) {
+ return <div>no status loaded</div>;
+ }
+
return (
<Fragment>
<LocalNotificationBanner notification={notification} />
@@ -136,6 +134,15 @@ function ChallengeForm({
</i18n.Translate>
</p>
</div>
+ {state.lastTry && (
+ <Fragment>
+ <Attention title={i18n.str`A code has been sent to ${state.email}`}>
+ <i18n.Translate>
+ You can change the destination or <a href={routeSolveChallenge.url({nonce })}><i18n.Translate>complete the challenge here</i18n.Translate></a>.
+ </i18n.Translate>
+ </Attention>
+ </Fragment>
+ )}
<form
method="POST"
class="mx-auto mt-16 max-w-xl sm:mt-20"
@@ -191,6 +198,10 @@ function ChallengeForm({
autocomplete="email"
class="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
+ <ShowInputErrorLabel
+ message={errors?.repeat}
+ isDirty={repeat !== undefined}
+ />
</div>
</div>
@@ -217,67 +228,6 @@ function ChallengeForm({
);
}
-export function AskChallenge({ nonce, onSendSuccesful }: Props): VNode {
- const { i18n } = useTranslationContext();
- const { state } = useSessionState();
-
- const result = useChallengeSession(nonce, state);
-
- if (!result) {
- return <Loading />;
- }
- if (result instanceof TalerError) {
- return <div />;
- }
-
- if (result.type === "fail") {
- switch (result.case) {
- case HttpStatusCode.BadRequest: {
- return (
- <Attention type="danger" title={i18n.str`Bad request`}>
- <i18n.Translate>
- Could not start the challenge, check configuration.
- </i18n.Translate>
- </Attention>
- );
- }
- case HttpStatusCode.NotFound: {
- return (
- <Attention type="danger" title={i18n.str`Not found`}>
- <i18n.Translate>Nonce not found</i18n.Translate>
- </Attention>
- );
- }
- case HttpStatusCode.NotAcceptable: {
- return (
- <Attention type="danger" title={i18n.str`Not acceptable`}>
- <i18n.Translate>
- Server has wrong template configuration
- </i18n.Translate>
- </Attention>
- );
- }
- case HttpStatusCode.InternalServerError: {
- return (
- <Attention type="danger" title={i18n.str`Internal error`}>
- <i18n.Translate>Check logs</i18n.Translate>
- </Attention>
- );
- }
- default:
- assertUnreachable(result);
- }
- }
-
- return (
- <ChallengeForm
- nonce={nonce}
- status={result.body}
- onSendSuccesful={onSendSuccesful}
- />
- );
-}
-
export function undefinedIfEmpty<T extends object>(obj: T): T | undefined {
return Object.keys(obj).some(
(k) => (obj as Record<string, T>)[k] !== undefined,